NimのGUIフレームワーク、nimxに入門してみる

タイトル通りNimのGUIフレームワーク、nimxに入門した話です。
この記事は Nim Advent Calendar 201820日目の記事として登録させていただいています。 初アドベントカレンダーですね。

nimxとは

nimxとは、Nimのクロスプラットフォーム対応のGUIフレームワークです。
GitHub: https://github.com/yglukhov/nimx

nakeを用いてiOS, Android, MacOS, Windows, Linux, JS, Asm.jsに対応したビルドが可能です。(webの知識があれなのでJSに関しては公式ドキュメントの参照を推奨します)

この記事について

さて、この記事ではnimxの導入から実際にビルドしてみる手順までをまとめようかと思いますが、僕の実行した環境は以下です。

macOS Mojave
Nim 0.19.0
nimx 0.1

導入

nimxのインストールなどは基本的にGitHubリポジトリのドキュメントに書いてあることを実行すればokなのですが、念のためコマンドを示しておきます。

インストールはnimbleから

$ nimble install nimx


もしnimbleが入っていないならば

$ git clone https://github.com/nim-lang/nimble.git
$ cd nimble
$ nim c src/nimble
$ src/nimble install

を実行してから上のコマンドを実行してください。*1

僕は大丈夫だったのですが、どうやらインストールでエラーがでたみたいな記事も拝見したので、もしインストールで何かつっかかるようでしたらこちらの記事なんかが参考になるかもしれません

実行してみる

nimxをインストールしたら実際にhello world appを実行してみました。

ドキュメントに書いてある通りにhelloworld.nimを作成し、$ nim c -r --threads:on helloworldで実行できるはずです。
helloworld.nimのコードは以下です。

import nimx / [ window, layout, button, text_field ]

runApplication:
    let w = newWindow(newRect(50, 50, 500, 150))
    w.makeLayout: # DSL follows
        - Label as greetingLabel: # Add a view of type Label to the window. Create a local reference to it named greetingLabel.
            center == super # center point of the label should be equal to center point of superview
            width == 300 # width should be 300 points
            height == 15 # well, this should be obvious now
            text: "Press the Greet button to see the greeting" # property "text" should be set to whatever the label should display
        - Button: # Add a view of type Button. We're not referring to it so it's anonymous.
            centerX == super # center horizontally
            top == prev.bottom + 5 # the button should be lower than the label by 5 points
            width == 100
            height == 25
            title: "Greet"
            onAction:
                greetingLabel.text = "Hello, world!"

macでしたら以下のようなウインドウが表示されるのではないでしょうか。(画像はnimx/doc/hello-world-app.pngより) hoge

コードと表示されたウインドウとを見比べてもらえればコンテンツのwidthheighttextの設定についてはわかると思いますが、superprevについては以下のような意味を持っています*2

  • super -> そのviewを持っているview (= 親view)
  • prev -> 直前に置かれた、同じ親をもつview

nakeでビルド

nimxでつくったGUIを実際に様々なプラットフォーム向けにビルドするにはnakeを使います。

nakeはNimのビルド時のタスクを記述するためのビルドツールです。
導入が済んでいない場合はこれもnimbleからインストールできます。

$ nimble update
$ nimble install nake

試しに先ほどのhello world appをnakeを使ってビルドしてみましょう。 まず、適当に空のディレクトリを作成してそこに先ほどのhelloworld.nimを移動し、新たにnakefile.nimを作成してください。

今回はnakefile.nimの中身は

import nimx/naketools

の一行のみで大丈夫です。 これによって実行されるファイルがmain.nimに指定されるので、helloworld.nimmain.nimに名前を変更しておきます。

ここまで済んだら$ nakeを実行するだけなのですが、僕の環境ではここでエラーが。。

Hint: xmltree [Processing]
Hint: xmlparser [Processing]
Hint: parsexml [Processing]
../../.nimble/pkgs/nimx-0.1/nimx/naketools.nim(423, 17) Error: undeclared identifier: 'stripLineEnd'

ふむ?ということでundeclaredですよ〜とのことのstripLineEndについて調べてみると、これはNimの標準ライブラリに含まれているプロシージャのはずなのですが、なぜかエラーが出ている。
ということでNimのstrutilsのコードを見てみるとstripLineEndはまだmasterにマージされていない様子でした。

そこで、自分の~/.nimble/pkgs/nimx-0.1/nimx/naketools.nimの423行目のstripLineEnd(result)を、Nimの適当なブランチ*3stripLineEnd()の実装内容を踏まえて、

if result.len > 0:
    case result[^1]
        of '\n':
          if result.len > 1 and result[^2] == '\r':
            result.setLen result.len-2
          else:
            result.setLen result.len-1
        of '\r', '\v', '\f':
            result.setLen result.len-1
        else:
            discard

のように変更したところ、$ nim c -r --threads:on helloworldと同じように実行できました。

各プラットフォーム向けのビルドでは、nakeの後ろに-ios-droid-jsなどと指定することでビルドができます。
何も指定しなかった場合、現在実行中のプラットフォーム向けのビルドが行われます。 詳しくは公式を参照してください。

ちなみに、Android向けのビルドにはAndroid NDKが必要なようです。

最後に

Nim本体でmasterで実装されていないプロシージャを参照しているという僕としては初めてのタイプのエラーに遭遇し、バージョンが1未満の言語を実感しました(?)

普段はAndroidを触っているということもあり本当はモバイル向けにUIを色々弄ってみたかったのですが入門記事みたくなってしまいました。*4
これはこれで誰かの導入の一助になれればと思います。

*1:詳細はnimbleのドキュメントを参照

*2:https://github.com/yglukhov/nimx/blob/master/doc/layout-dsl.mdより

*3:僕はdevelブランチを見ました

*4:絶対加筆or別記事でやるぞ2018冬