Ubuntu Weekly Recipe

第351回 GoとQMLとGUIアプリ

この記事を読むのに必要な時間:およそ 5.5 分

Goのテスト

go-qmlを試す前に,Goでビルドできるかどうか試しておきましょう。まず次のような内容で,yahallo.goを作成します。

package main

import "fmt"

func main() {
    fmt.Println("やっはろー")
}

ビルドするだけならbuildコマンドを,ビルドして実行するならrunコマンドを実行します。

$ go run yahallo.go
やっはろー

クロスプラットフォームについて

Goはクロスアーキテクチャだけでなくクロスプラットフォームビルドにも対応しています。たとえば「golang-go-windows-386」パッケージをインストールしておけば,64ビット版のUbuntuで,32ビット版Windows向けバイナリも作成できるのです。

$ sudo apt install golang-go-windows-386
$ GOOS=windows GOARCH=386 go build yahallo.go
$ file yahallo.exe
yahallo.exe: PE32 executable (console) Intel 80386 (stripped to external PDB), for MS Windows

あとはこのyahallo.exeファイルだけをWindowsにコピーして,コマンドプロンプトなどで実行すればちゃんと動きます。ちょっとUbuntu上でWindowsの実行ファイルを作らなくてはいけなくなったときなどに,Goはとても有力な選択肢の1つとなるはずです。

go-qmlそのものはクロスプラットフォーム・クロスアーキテクチャなビルドには対応していません。これはGoとは別に各プラットフォームのQtライブラリを用意しなくてはいけないためです。残念ですが各プラットフォームのバイナリを作成するには,各プラットフォームでビルド環境を構築してください。ただし原則的に,同じソースコードがそのまま動くはずです。

サンプルアプリケーションのビルドと実行

では,実際に何かアプリケーションを作ってみましょう。必要なのは以下の3つの知識です。

Go言語
Goの公式サイトその日本語訳には,Goを学習するためのドキュメントや各種パッケージのリファレンス,言語仕様が掲載されていますので,Goの初心者ならまずはこれらを眺めながら使い方を覚えて行くことになるでしょう。godocコマンドも便利です。またWEB+DB PRESS Vol.82でもGo言語の特集を行っているので,そちらも参考になるでしょう。
QML
UIの部分を担当するQMLは,Qt Projectに各種QMLタイプのリファレンスやサンプルコードが載っています。また日本語の情報としては,Qt QuickではじめるクロスプラットフォームUIプログラミングやその追補版的位置づけのQt Quickを使いこなすクロスプラットフォームUIプログラミングもおすすめです。
qmlパッケージ
qmlパッケージを使う場合,main関数の内外でいくつか初期化処理を行う必要があります。またGo側から各種QMLタイプのメソッドやプロパティ,シグナルにアクセスする方法や逆にQML側にGo側の変数やタイプを公開する方法を把握しておく必要があります。go-qmlのドキュメントを一通り読んだうえで,ソースコードのexampleフォルダーの中身を見ておくと良いでしょう。

Webページを表示する

WebViewを使ったサンプルを作成してみます。WebViewはQMLタイプの1つでWebKitエンジンを使ったWebブラウザー機能をアプリケーションに提供します。次のような内容のファイルをwebview.goという名前で作成してください。

package main

import (
    "fmt"
    "gopkg.in/qml.v1"
    "os"
)

const webview = `
import QtQuick 2.0
import QtWebKit 3.0

WebView {
    width: 1024
    height: 768
}
`

func main() {
    if len(os.Args) != 2 {
        fmt.Fprintf(os.Stderr, "Usage: %s <url>\n", os.Args[0])
        os.Exit(1)
    }
    if err := qml.Run(run); err != nil {
        fmt.Fprintf(os.Stderr, "Error: %v\n", err)
        os.Exit(1)
    }
}

func run() error {
    engine := qml.NewEngine()
    component, err := engine.LoadString("webview.qml", webview)
    if err != nil {
        return err
    }
    win := component.CreateWindow(nil)
    root := win.Root()
    root.On("loadingChanged", func() { fmt.Println("Page Loaded") })
    root.Set("url", os.Args[1])
    win.Show()
    win.Wait()
    return nil
}

あとはコマンド上で,ビルドして実行してみます。

$ go run webview.go http://gihyo.jp

図1 WebViewなのでリンクをクリックすればページ遷移もできる

図1 WebViewなのでリンクをクリックすればページ遷移もできる

具体的な処理内容を見ていきましょう。まずはmain()の中で呼ばれているqml.Run()です。これは内部でQGuiApplicationインスタンスを作成し,引数のrun()を呼び出すイベントループをゴルーチンとして立ち上げつつ,QGuiApplication::exec()を呼び出します。go-qmlの(というよりはMac OS Xの)制約により,qml.Run()はmain()と同じゴルーチンの中で呼び出さなければなりません。よって,この処理はどのアプリケーションでも同じように記述すると思っておけば良いでしょう。なおqmlパッケージのさまざまなメソッドは,qml.Run()が呼ばれるまでは処理を停止します。

アプリケーションのメインルーチンとなるのがrun()の内部です。最初に行っているqml.NewEngine()はQQmlEngineを保持する構造体を作成しています。

engine.LoadString()はQMLデータを読み込むメソッドです。go-qmlは2つの方法でQMLデータを読み込めます。1つはサンプルのようにLoadString()を使って文字列から読む方法,もう1つはLoadFile()を使ってファイルから読む方法です。内部的に前者はQQmlComponent::setData()を,後者はQQmlComponent::loadUrl()を呼び出しています。どちらもQObject(QQmlComponent)を返します。今回はあらかじめ文字列定数webviewにQMLを指定してあるのでLoadString()を使っています。

component.CreateWindow()は作成済みのQQmlEngineを使ってQQuickViewのインスタンスを作成し,それを返しています。第一引数にはQMLコンテキストを渡すことが可能です。win.Root()はQQuickView::rootObject()相当の機能で,ここではQMLのルートオブジェクト(つまりWebViewタイプ)が返ることになります。

root.On()ではオブジェクトの「loadingChanged」シグナルを指定した関数にconnect()しています。root.Set()はオブジェクトの「url」プロパティを変更しています。これによりWebViewではコマンドの引数に指定したURLがロードされ,OnLoadingChangedが呼ばれた結果,端末に「Page Loaded」が表示されることになります。

win.Show()はウィンドウの表示で,win.Wait()はウィンドウが閉じられるまで待つメソッドです。ウィンドウが閉じられるとrun()関数から戻り,qml.Run()で立ち上げたゴルーチンが終了し,QGuiApplication::exit()が呼び出されます。

著者プロフィール

柴田充也(しばたみつや)

Ubuntu Japanese Team Member。数年前にLaunchpad上でStellariumの翻訳をしたことがきっかけで,Ubuntuの翻訳にも関わるようになりました。