Nimでデスクトップアプリを作る
はじめに
解説を挟みつつ、Nimでデスクトップアプリを作っていきます。
環境
- MacOS Ventura 13.0.1
- Nim1.6.6
GUI
owlkettleというGUIライブラリを使って作っていきます。
サンプルアプリ
owlkettleのgui
マクロを使うと、宣言的UIを構築できます。
試しに、次のようなコードを書いてみます。
import
pkg/owlkettle
viewable App:
counter: int
method view(app: AppState): Widget =
result = gui:
Window:
title = "Counter"
defaultSize = (200, 60)
Box(orient = OrientX, margin = 12, spacing = 6):
Label(text = $app.counter)
Button {.expand: false.}:
text = "+"
style = {ButtonSuggested}
proc clicked() =
app.counter += 1
brew(gui(App()))
実行するとこんなウィンドウが生成されました。
+ボタンを押すと左の数字が増えていく、簡単なアプリケーションです。コードを少しずつ解説します。
viewable
owlkettleでは、最初にviewable
マクロを使って、Viewable
という抽象的なオブジェクトを定義します。この例では、App
が定義されています。Viewable
には、アプリの中で変化する値をフィールドとして格納しておき、ここでは、int型のcounter
を定義しています。
viewable App:
counter: int
view
次にview
メソッドを定義していきます。ここで、view
はApp
型ではなくAppState
型を引数に取ることに注意します。AppState
は、App
を定義したときに同時に定義されているオブジェクトです。
viewable
でとあるX
を定義するとき、自動的にXState
が定義されます。XStateは、X
と同じフィールドをもちます。また、view
の返り値はWidget
型です。
proc view(app: AppState): Widget =
...
gui, Window
guiマクロでは、ウィジェットと呼ばれるオブジェクトを入れ子構造で記述します。各ウィジェットにはフィールドがあり、Widget(field = value)
, もしくはウィジェットのブロック内でfield = value
のように設定できます。まずはウィンドウを表示するために、Window
ウィジェットを配置します。フィールドとしてtitle
とdefaultSize
をもっています。
result = gui:
Window:
title = "Counter"
defaultSize = (200, 60)
Box
次に、数字とボタンを表示しますが、その前にBox
というウィジェットを配置します。通常のウィジェットは入れ子として配置できる子ウィジェットが1つなのに対し、このウィジェットは複数の子ウィジェットをもつことができ、ウィジェットを複数並べて表示するときに使います。orient
フィールドには定数OrientX
とOrientY
が設定でき、前者で左から横に、後者で上から縦に並べて表示します。ここではOrientX
にします。フィールドmargin
は外側、spacing
は内側の余白を設定します。
result = gui:
Window:
...
Box(orient = OrientX, margin = 12, spacing = 6):
...
Label, Button
テキストとボタンを表示します。Label
ウィジェットでテキストを表示できます。text
フィールドに、app
のフィールドcounter
を文字列化したものを設定すると、counter
が更新されたとき勝手に表示も更新されます。
Button
ウィジェットを使いボタンを表示します。Button
に{.expand: false.}
というプラグマがついています。詳しい説明は割愛しますが、これは親ウィジェットであるBox
のAdder
というものの一種で、このプラグマをつけると無駄な余白をなくして表示できます。text
にはボタンに表示する文字を、style
にはButtonStyle
型の組み合わせをset
で設定します。Button
内ブロックに定義されているclicked()
は、イベントと呼ばれるものの一種です。ウィジェットには、フィールドの他に、ユーザーがアクションを起こしたときに発火するイベントという関数を設定できるものがあり、Button
には、そのボタンをクリックしたときに発火するclicked()
が用意されています。ここでは、app.counter
をインクリメントする処理が書かれています。
result = gui:
Window:
...
Box:
Label(text = $app.counter)
Button {.expand: false.}:
text = "+"
style = {ButtonSuggested}
proc clicked() =
app.counter += 1
brew
最後に、定義したApp
とview()
を元にウィンドウを組み立てて表示します。
brew(gui(App()))
作ってみた
owlkettleを使って、パスワードを生成するアプリを作ってみました。
おわりに
Nim製GUIライブラリの選択肢は他にもありますが、その中でも記述しやすく、ドキュメントが比較的充実していることや、クロスプラットフォームに対応している点で優れていると思います。ぜひ使ってみてください!
Discussion