この記事は Qiita に 2018/01/02 に投稿したものです。2020/3/26 移行しました。
追記 (2018/9/27) Julia 1.0 で動くように修正。PackageCompiler.jl を使った例を追加。
概要
- 人生初のGUIアプリ作成をJuliaでやってみました
- GUIツールキットとしてGtkを使用しています
- GUIプログラミング初心者なので、手始めに下図のようなボタンをクリックしたらクリックした回数が表示されるという簡単なアプリ作成から始めてみました
作成までの流れは 1. 画面レイアウトの作成 2. ボタンを押したときに呼ばれる関数の定義 3. ボタンと関数を連携させる と言った感じです。
環境
OS: ArchLinux Julia 1.0.0 Gtk.jl v0.16.4
インストール
GUIを作るためにGtk.jl
を使っているのでインストールしていない場合インストールしてください。
using Pkg Pkg.add("Gtk.jl")
画面レイアウトの作成
初めに大枠のウィンドウを作り、そこにボタンやらスライダーやら何やらのWidgetを追加していきます。
using Gtk, Gtk.ShortNames win = Window("Count Click") # 大枠のWindowをつくる v = Box(:v) # widgetを並べて配置するためのボックス。 # :v でwidgetを縦に、:h で横並びになる l = Label("You clicked 0 times.") # クリックした回数を表示する部分 b = Button("Click!") # ボタン push!(win, v) # Window に Box を配置する push!(v, l) # Box内に表示部分を配置 push!(v, b) # Box内にボタンを配置 set_gtk_property!(v, :expand, l, true) # Box内の余白をなくして表示部分を広くする showall(win) # レイアウトに反映させる
とりあえず、GUIアプリっぽくなってきました。しかし、このままではボタンを押したときの動作を定義していないのでボタンを押しても何も起こりません。
関数定義とボタンとの連携
次にボタンがクリックされたときに呼ばれる関数(callback function)を定義します。
nclick = 0 function click() global nclick += 1 set_gtk_property!(l, :label, "You clicked $nclick times.") # Label Widgetのラベルを変更する return nothing end signal_connect(x -> click(), b, "clicked") # ボタンと関数をつなぐ
setproperty!
を使うと各 Widget の property を変更することができます。
最後に signal_connect
でボタンと関数をつなげます。上記の場合だとボタンb
から"clicked"
シグナルが出たら callback function を呼ぶという意味になるらしいです。このとき、callback function にはボタン b
が引数として与えられます。今回の場合、引数もらっても使わないので無名関数使って素通りさせてます。
これで目標のGUIアプリを作ることができるはずです。 窓の外の野鳥や車でも見ながらポチポチしてください。
まとめ
Juliaを使って簡単なGUIアプリを作ることができました。
私は他の言語でGUIアプリを作ったことがないので比較は出来ませんが、思っていたよりもGUIアプリって簡単に作れるものなんだなぁといった印象です。 まともな情報が公式ドキュメントくらいしかないのは辛いところですが。
他にも役に立たないものを練習がてら色々作ってみましたが、複雑なものでも 関数定義, setproperty!
, signal_connect
をひたすら繰り返せばそれっぽいものは作れるとわかりました。
ウィジェットが増えてくるとコマンドで画面レイアウトを作るのは大変なので Glade を使いましょう!
ソースコード
#!/usr/bin/env julia using Gtk, Gtk.ShortNames # 画面レイアウトの作成 win = Window("Count Click") v = Box(:v) l = Label("You clicked 0 times.") b = Button("Click!") push!(win, v) push!(v, l) push!(v, b) set_gtk_property!(v, :expand, l, true) showall(win) # ボタンを押したときに呼ばれる関数 nclick = 0 function click() global nclick += 1 set_gtk_property!(l, :label, "You clicked $nclick times.") return nothing end # ボタンと関数をつなぐ signal_connect(x -> click(), b, "clicked") if !isinteractive() c = Condition() signal_connect(win, :destroy) do widget notify(c) end wait(c) end
最後の部分はREPL使わずにコマンドラインからスクリプトとして実行したときのために入れています。
click とでもスクリプト名つけて、実行権限もつけて、PATHの通っているところおけば普通のGUIのアプリの様に使えます。
$ chmod +x click $ ./click
PackageCompiler.jl
PackageCompiler.jl を使うと Julia のプログラムを実行可能ファイルへと変換することが出来ます。 今回使った PackageCompiler.jl のバージョンは 0.5.0 です。
(v1.0) pkg> add PackageCompiler
上記のコードそのままでは動かなかったので多少修正したバージョンがこちらです。
module Hello using Gtk Base.@ccallable function julia_main(ARGS::Vector{String})::Cint win = GtkWindow("Count Click") v = GtkBox(:v) l = GtkLabel("You clicked 0 times.") b = GtkButton("Click!") push!(win, v) push!(v, l) push!(v, b) set_gtk_property!(v, :expand, l, true) # callback function global nclick = 0 function click(x) nclick += 1 set_gtk_property!(l, :label, "You clicked $(nclick) times.") end # connect button and function signal_connect(click, b, "clicked") showall(win) if !isinteractive() c = Condition() signal_connect(win, :destroy) do widget notify(c) end wait(c) end return 0 end end
これを PackageCompiler を使ってコンパイル、その後実行します。
julia> using PackageCompiler julia> build_executable("click.jl", "click") julia> run(`builddir/click`)
機能自体がシンプルなのでレスポンスの違いを体感することはないと思いますが、起動スピードが純粋な Julia のスクリプトを使った場合よりも圧倒的に早くなりました。
参考
- Gtk.jl documentation: Gtk.jlの公式ドキュメントを読めば簡単なアプリの作り方はおおよそ理解できます。
- GTK+ 3 Reference Manual: 各Widgetのシグナルで悩んだらGTK+ 3 Reference Manualを見ましょう。
- yomichi/JuliaBook-Samples: もっと凝ったアプリを作りたい場合 yomichiさんのサンプルコードが参考になります。
追記
もう少し実用的のものを作ってみました。GoogleにもWolfram Alphaにもグラフをプロットさせることが出来ない状況になったら有用かもしれません。 https://github.com/goropikari/GtkFunctionPlot.jl