Qt 初心者が Qt を使った AGL アプリを作成する手順の記録。Linux GUI に慣れてないので、Qt アプリの作成を Mac OS で行い、クロスビルドから wgt の作成までを Ubuntu 16.04 LTS を使った。
ソースコード: https://github.com/propella/agl-hello
Qt インストール方法
- https://www.qt.io/download-open-source/#section-2 のインストーラで ~/Qt にインストール
簡単なアプリの作成
簡単なアプリのソース
自分が思いついた一番簡単な Qt の GUI のプロジェクトファイルは以下のようになった。
# agl-hello.pro
QT += widgets
TARGET = agl-hello
TEMPLATE = app
SOURCES += main.cpp
CONFIG += debug
pro ファイルは qmake というツールの設定ファイルで、qmake は pro ファイルから Makefile 等を生成する。代入形式で定義する。リストは空白区切り。
-
QT : プログラムで使う Qt Module
- QT にはデフォルトで
core
やgui
等の基本的な値がセットされているため、追加するには+=
を使う。
- QT にはデフォルトで
- TARGET : 出来上がるファイルの名前。デフォルトで project name と同じになる。
- TEMPLATE : アプリを作る時は app, ライブラリの時は lib
- SOURCES : ソースコードを記述する。
- CONFIG : デバッグしたい時 debug を追加
- pro ファイルのデバッグには message が便利。
以下プログラムソースコード。
// main.cpp
# include <QApplication>
# include <QLabel>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QLabel label("Hello, World!");
label.setWindowFlags(Qt::FramelessWindowHint);
label.show();
return a.exec();
}
ここで setWindowFlags(Qt::FramelessWindowHint)
で FramelessWindowHint を指定するのがポイント。これが無いとなぜか AGL HomeScreen から表示出来なかった。
ビルド方法
Qt Creator を使う場合
- File > Open File or Project > agl-hello.pro を選択
- Configure Project でデフォルトのまま Configure Project を押す。いつの間にか qthello.pro.user が出来ている。
- 再生ボタンで実行
-
../build-qthello-Desktop_Qt_5_9_1_clang_64bit-Debug
にビルド用フォルダが出来ている。
Terminal を使う場合
~/Qt/5.9.1/clang_64/bin/qmake
make
クロスビルド用 Docker 環境の作成
AGL SDK Quick Setup に、Docker を使ったクロスビルド環境の作成が紹介されている。Docker 無しで Ubuntu 16.04 の上に直接 SDK をインストールしても動くので、面倒ならこのステップは省略出来る。
この docker image は特定のディレクトリ構成で使うように出来ているので、~/ssd 及び ~/devel を作成する
mkdir ~/ssd ~/devel
chmod a+w ~/ssd ~/devel
すると以下のようにコンテナ内からホストにアクセス出来る。
container | host | 用途 |
---|---|---|
/xdt |
~/ssd/xdt_$ID |
SDK インストール場所 |
/home/devel/mirror |
~/ssd/localmirror_$ID |
作業場所 |
/home/devel/share |
~/devel/docker/share |
ダウンロードファイル置き場 |
docker image をダウンロードして設定
wget https://download.automotivelinux.org/AGL/snapshots/sdk/docker/docker_agl_worker-3.0.tar.xz
sudo docker load -i docker_agl_worker-3.0.tar.xz
docker images
git clone https://git.automotivelinux.org/AGL/docker-worker-generator
cd docker-worker-generator
./contrib/create_container 0 docker.automotivelinux.org/agl/worker:3.0
コンテナにログイン (パスワードは devel) なぜか TERM=screen が無いと、screen 経由で vi が上手く動かない。
TERM=screen ssh -p 2222 devel@localhost
コンテナ内からは uid: 1664 / gid: 1664 の devel というユーザでホストとファイル共有する。以下のように設定するとホストとファイル交換が出来る。(linux 詳しく無いので間違ってるかも)
ホスト
sudo groupadd --gid 1664 devel
sudo useradd --uid 1664 --gid 1664 devel
コンテナ
sudo groupadd --gid 1002 tyamamiya
sudo gpasswd -a devel tyamamiya
SDK のインストール
AGL のターゲット向け SDK はどこからか拾ってくるか、自分で作る場合は AGL の build ディレクトリで agl-demo-platform-crosssdk をビルドする。
bitbake agl-demo-platform-crosssdk
SDK は tmp/deploy/sdk/poky-agl-glibc-x86_64-agl-demo-platform-crosssdk-aarch64-toolchain-3.99.1+snapshot.sh のような場所に出来る。Docker を使っている場合はこれを ~/devel/docker/share に置いてコンテナと共有し、コンテナの install_sdk
でインストールする。
Docker を使わない場合はこのファイルを直接実行すればインストーラが起動する。
host
mv tmp/deploy/sdk/poky-agl-glibc-x86_64-agl-demo-platform-crosssdk-aarch64-toolchain-3.99.1+snapshot.sh ~/devel/docker/share/
container
install_sdk ~/share/poky-agl-glibc-x86_64-agl-demo-platform-crosssdk-aarch64-toolchain-3.99.1+snapshot.sh
実機で動くかテストしてみる。(デバイスのホスト名が device
とする)
$ source /xdt/sdk/environment-setup-aarch64-agl-linux
$ cat <<EOT > hello.c
#include <stdio.h>
int main() { printf("Hello world\n"); return 0; }
EOT
$ $CC -o hello hello.c
$ scp hello root@device:.
$ ssh root@device ./hello
Hello world
アプリを Widget にする
クロスコンパイルしてデバイス上に表示する
サンプルアプリをコンテナの ~/mirror にコピーする。SDK を source すると qmake が使えるようになるので、そのままコンテナでビルドし、デバイス上で動く事を確認する。
$ git clone git@github.com:propella/agl-hello.git
$ cd agl-hello
$ source /xdt/sdk/environment-setup-aarch64-agl-linux
$ qmake
$ make
$ scp agl-hello root@device:.
AGL 起動時に Home Screen が表示される場合、そのままでは普通の Qt アプリが動かないので weston.ini を編集して ivi-shell.so を無効にする。
$ vi /etc/xdg/weston/weston.ini
# shell=ivi-shell.so (shell=ivi-shell.so をコメントアウト)
$ systemctl restart weston
$ ~/agl-hello
AGL デバイスの画面に寂しく Hello World! が表示されれば成功。終わったら weston.ini の設定を戻して systemctl restart weston
する。
Wiget を作成する。
AGL のアプリは W3C Widget 仕様に基づいて作成した wgt ファイルだ。SDK 内に wgt ファイルを作るためのツールがある。パッケージを作るためには以下のファイルが必要。
- config.xml : 今から作ります
- アイコンファイル : なんでも良い
- 実行ファイル
<?xml version="1.0" encoding="UTF-8"?>
<widget xmlns="http://www.w3.org/ns/widgets" id="poi" version="0.1">
<name>Hello World</name>
<icon src="icon.png"/>
<content src="agl-hello" type="application/x-executable"/>
<description>A Simple Hello World</description>
<author>Takashi Yamamiya</author>
<feature name="urn:AGL:widget:required-permission">
<param name="http://tizen.org/privilege/internal/dbus" value="required" />
</feature>
<license>MIT</license>
</widget>
AGL App Framework は widget の id 属性でアプリを識別します。今回何故か poi
という ID にしてみました。本来 ID はアプリを表す名前であるべきですが、現時点 (AGL 3.0) では HomeScreen の実装が不十分で、プリインストールされた決め打ちの ID のアイコンしか表示されません。今回は問題のある HomeScreen を騙すために、poi
という既に存在するアプリの ID を流用します。
これらをまとめて適当なディレクトリに置きます。SDK の wgtpkg-pack コマンドで wgt ファイルを作成し、デバイスに転送します。
mkdir root
cp config.xml root/
cp icon.png root/
cp agl-hello root/
wgtpkg-pack -f -o agl-hello.wgt root
scp agl-hello.wgt root@device:.
デバイス側では afm-util コマンドを使ってアプリの登録削除を行います。
afm-util uninstall poi@0.1 # 既存の poi アプリを削除
afm-util install agl-hello.wgt # 作成した Hello World を poi 名前で登録
これで AGL Home Screen から POINT OF INTEREST のアイコンを押すと Hello World が表示されます。起動中にアプリの入れ替えは出来ないようなので、うまくいかない時はリブートして下さい。
afm-util には便利な機能が沢山あって Quick Tutorial に詳しい紹介があります。私がよく使うのは以下です。
- afm-util uninstall (id@バージョン) # 既存の poi アプリを削除
- afm-util install (wgt ファイル) # 作成した Hello World を poi 名前で登録
- afm-util runners # 起動中のアプリを確認
- afm-util terminate (runid) # runid で起動しているアプリを停止
- systemctl --user restart HomeScreen # HomeScreen 再起動
- アプリの入れ替えには役に立たないが、インストールしていない状態がからインストールした時にこのコマンドでアイコンを表示し直せる。
CMake で Widget ファイルを作成する
折角なのでこれらの作業を自動化するために cmake を使う。https://git.automotivelinux.org/apps/hvac 等の他のアプリを見ると qmake だけでも自動化出来るようだが、たまたま参考にした poi が cmake を使っていたので cmake になってしまった。
# CMakeLists.txt
cmake_minimum_required(VERSION 2.8.11)
project(agl-hello)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fPIC ")
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
find_package(Qt5Widgets REQUIRED)
qt5_add_resources(agl_hello_QRC )
add_executable(agl-hello main.cpp ${agl_hello_SRC} ${agl_hello_QRC})
target_link_libraries(agl-hello Qt5::Core Qt5::Widgets)
install (TARGETS agl-hello DESTINATION bin)
configure_file(config.xml.in config.xml)
add_custom_command(
OUTPUT agl-hello.wgt
DEPENDS agl-hello
COMMAND rm -rf package
COMMAND mkdir -p package/root
COMMAND cp config.xml package/root/
COMMAND cp ${CMAKE_CURRENT_SOURCE_DIR}/icon.png package/root/icon.png
COMMAND cp agl-hello package/root/agl-hello
COMMAND wgtpkg-pack -f -o package/agl-hello.wgt package/root
)
add_custom_target(widget ALL DEPENDS agl-hello.wgt)
qmake から cmake への変換は http://doc.qt.io/qt-5/cmake-manual.html に詳しく書いてあるが、ポイントは以下
- set(CMAKE_INCLUDE_CURRENT_DIR ON) # 良くわからないがとりあえず入れておく。
- set(CMAKE_AUTOMOC ON) # 良くわからないがとりあえず入れておく。
- find_package(Qt5Widgets REQUIRED) # qt5_ 関連のマクロが使えるようになる。
- qt5_add_resources(agl_hello_QRC ) # この例では使ってないが、リソースファイルをビルドしたい時に使う。
- target_link_libraries(agl-hello Qt5::Core Qt5::Widgets) # cmake の QT と同様の内容を記述
これで結局ビルド手順は以下のようになる。
cmake
make
package/agl-hello.wgt に Widget が出来る。
AGL Recipe の書き方
ここまで行くとあとはレシピを書くだけだが、力尽きたので今度書く。
基本 cmake_qt5, pkgconfig, aglwgt を inherit するだけでパッケージのインストールまで行われる。