GoでGUIアプリを作ろう


GoでGUI作る決定版! ほとんどのプラットフォームで動くGUIネイティブアプリを作れる qtパッケージの紹介。

GitHub: https://github.com/therecipe/qt

クロスプラットフォームなGUIライブラリ

GUIをクロスプラットフォーム対応するのはとても膨大なAPIをカバーするため 多くは限定的な機能提供にとどまるパターンがとても多いです。

その中でQtは20年以上の歴史を持ちかなり広範なプラットフォームをサポートした C++製のGUIフレームワークです。GUI機能そのものがフルスタックな上、 画像処理、ネットワークやオーディオ・ビデオなどのサポートも内包しています。

therecipe/qtはこのQtをGoから使えるようにラップしたパッケージです。

therecipe/qtがサポートするプラットフォームは以下 (本家Qtはもう少し多い)

  • Mac OS X(macOS)
  • Windows
  • Linux/X11
  • MeeGo(Sailfish OS)
  • Android
  • iOS
  • RaspberryPi(1,2,3)

各環境でセットアップする方法はtherecipe/qtのREADME.mdに書いてる通り。 私が動作確認ができたのは今のところ以下の4環境。 ほぼほぼREADME.md通りで問題なく動きました。

  • macOS
  • iOS
  • iOSシミュレータ
  • Linux/X11
  • Android

Linuxだけは少し注意が必要です。 ディストリ配布のQtでは失敗するのと日本語入力関連で ちょっとだけ追加手続きが必要なことです。

therecipe/qtはQtの利点をほぼ継承できています。

  • qtdeployコマンドを使うことでビルドや配布ファイルセット出力がとても簡単です。
  • 操作上はターゲット別に覚えることはほとんどありません。
  • qt-creatorまたはqt-designerでGUIをWYSIWYGデザインすることができる。
  • 比率レイアウトの利用によりスクリーンサイズに依存せずに動作するGUI構築が容易です。
  • iOSビルドではxcodeprojを出力するので追加調整が容易です。

アプリを作る準備

  • GOPATH配下のフォルダにmainパッケージを書くだけ。
  • 凝ったGUIを作りたければQMLファイルをqtquickで作ってそばに置く。

ほんとこれだけであらゆる環境で動くGUIネイティブアプリがビルドできます。

簡易実行はいつものように以下のコマンドで動きます。

go run *.go

同フォルダにて以下のコマンドでdeployフォルダ以下にそれぞれの環境別に成果物が集約されます。

qtdeploy build desktop .
qtdeploy build ios .

iosの場合は成果物の中のxcodeprojを追加で調整が必要です。

  • app-id
  • コード署名
  • アイコン関連

Qtのフォームデザイン

Qtはフォームデザインとレンダリング方法がそれぞれ複数の方式を持っています。 Widgetsデザインでは多くのデスクトップアプリに近い形で デザインは実装コードで記述し、レンダリングはOSネイティブのものを利用します。

後発の手法ではデザイン専用のQMLという記述言語でデザインしレンダラはベクタ方式です。 どちらの手法も挙動実装とは完全に分離したファイルにデザインを保存して使うことができます。 もちろん実装コードでフォーム部品を組み立ててもいいです。 QMLは上位互換でWidgetsもQMLコンポーネントも Windowsで言うところのWPFのような描画記述も書ける見たいです。

Widgetsデザイン、QMLデザインそれぞれフォームデザイナーツールが用意されているので 基本言語仕様について把握する必要はありません。

  • WidgetsデザインはQtDesignerを使い<name>.uiファイルに記録します、
  • QMLデザインはQtCreatorを使い<name>.qmlファイルに記録します。

アプリ実装はこれらを読み込んでフォームを構築します。

  • Widgetsは基本セットが豊富。
  • OSがサポートしてないWidget定義はプラグインを作成する必要がありハードルが高めです。
  • QMLは定義を再利用する仕組みが最初からあり、独自の定義は作りやすい。
  • QMLベースの場合、変わった部品が色々公開されていてそれらを利用することもできます。

実装コードによるデザインは言語縛りができちゃうし大量のデザインを維持するのはなかなかツライ。 デザイン分離手法は大量のウインドウを扱うのもメンテするのも向いているので よほどシンプルなもの(1ソースで完結するようなもの)を除けば 今後のことも考えてQML方式がオススメです。 デザインとアプリ挙動がしっかり分離できるので分業もしやすいです。

ただし、QtCreatorはIDEになっててプロジェクト管理機能がありますが、 Goに対応していないのでデザイン機能だけを利用します。

サンプルのありか

cd $GOPATH/src/github.com/therecipe/qt/internal/examples

ミニマムブラウザ

コードで組み立ててみるサンプル

package main

import (
	"os"

	"github.com/therecipe/qt/core"
	"github.com/therecipe/qt/webengine"
	"github.com/therecipe/qt/widgets"
)

func main() {
	widgets.NewQApplication(len(os.Args), os.Args)
	var view = webengine.NewQWebEngineView(nil)
	view.SetUrl(core.NewQUrl3("http://www.qt.io", 0))
	view.Show()
	widgets.QApplication_Exec()
}

QMLで組み立ててみるサンプル

package main

import (
	"os"

	"github.com/therecipe/qt/core"
	"github.com/therecipe/qt/gui"
	"github.com/therecipe/qt/qml"
	_ "github.com/therecipe/qt/webengine"
)

func main() {
	gui.NewQGuiApplication(len(os.Args), os.Args)
	app := qml.NewQQmlApplicationEngine(nil)
	app.Load(core.NewQUrl3("qrc:main.qml", 0))
	gui.QGuiApplication_Exec()
}

トラブルシュート

「qrc:」って?

Qtリソースアクセス用のスキームです。 実装やコンポーネントプロパティで参照する指定をすると、 バイナリの中に対象のリソースが埋め込まれます。 配付形態での実行時はそのリソースにアクセスできるようになります。 簡易実行ではqrcにはアクセスできないのでローカルファイルアクセスに 切り替えやすいようラップするといいかもしれません。

linux上のアプリで日本語入力できない

go run時に入力可能にする方法はsetup時にインストールしたqt5($QT_DIR)の中にある $QT_DIR/plugins/platforminputcontexts/libfcitxplatforminputcontextplugin.soを入れて置くだけです。

こうして置くと、デプロイ時のファイル群にも含められるようになり、 デプロイバイナリでも日本語入力が可能です。

ただしLinuxのディストリごとにこのプラグインファイルの入手方法が微妙に違ったりします。

arch-linux:

sudo pacman -S fcitx-qt5 # arch-linux
cp /usr/lib/qt/plugins/platforminputcontexts/libfcitxplatforminputcontextplugin.so $QT_DIR/plugins/platforminputcontexts/

ubuntu/xenial:

sudo apt install fcitx-frontend-qt5 # ubuntu/xenial
cp /usr/lib/x86_64-linux-gnu/qt5/plugins/platforminputcontexts/libfcitxplatforminputcontextplugin.so  $QT_DIR/plugins/platforminputcontexts/

QWebEngineViewをQMLに書いたらエラーで落ちる

拡張されたWidgetは該当パッケージを取り込んだ時に初期化され利用可能になる。

なので以下の記述を追加すればOK

import _ "github.com/therecipe/qt/webengine"

クロス開発について

  • クロスターゲットビルドはなんでもOKではありません。
  • デスクトップOSと同じターゲットは問題なくビルド・デプロイできます。
  • macOSからモバイル系はビルド・デプロイ可能。
  • WindowsまたはlinuxからAndroid、SailfishOSはビルド・デプロイ可能。
  • RaspberryPi向けの環境作りはちと苦労する。
  • これ以外の組み合わせではクロスターゲットのgccやクロス用のQtをビルドする必要があり大変です。

まとめ

Go言語からみたメリット

  • 一つのGoソースから一通りのメジャープラットフォームにデプロイできる。
  • Goには無いメディア周りがクロス環境でサポートされてるのは大きい。
  • 以下のQtのメリットのほとんどを得ることができる。

QtSDKそのもののメリット

  • Qt本体は長く鍛えられ高い品質を十分に備えてるので安心して使える。
  • デプロイファイルセットは環境依存を切り離してあるのでそのセットを丸ごと配布で動作する。
  • デザイナーは十分な機能がありかなり凝ったフォームやたくさんのフォームもつくれる。
  • 比率を利用したレイアウトエンジンなのでスクリーンサイズの差で悪影響が出にくい。
  • Qt自体はネット、メディア、その他諸々の機能も内包していて環境違いの挙動差はだいぶ抑えてある。