Qtアプリのサンプル

Windowsを使っていた頃に、MangaMeeyaと言うアプリを使っていました。
非常に良くできたアプリだったのですが、現在は開発を中止しているようで、本家サイトもなくなっています。

Macを使うようになってから、同じようなアプリが欲しいな〜と思っていろいろ試してみたのですが、なかなか気に入るものがありません。

そこで、Qtを使ったアプリケーション作成のサンプルとして、非常に簡単なものを作ってみました。

https://github.com/false-git/ComicsViewer

* 自炊したマンガを読むためのアプリ
* 対応しているファイルはzipファイルのみ
* zipファイルの中のjpgファイルのみを表示する
* zipファイル内のファイル名の文字コードはSJISを想定
* zipファイルのネストには未対応
* カーソルの上下か、画面左右のマウスクリックでページ移動
* カーソルの左右でもページ移動するが、1ページずつ
* 画像は、日本のマンガを想定しているので、右開きのみ

Mac OS XとWindows 7(Qtはmingw32版)でのみ動作確認しています。(このままではVC++ではビルドできません)

この記事を書いた目的は、アプリの紹介ではなく、あくまでQtでアプリケーションを作成/配布するときのエッセンスの紹介です。(配布しませんが)

# zipファイルを読む
Javaを使っていると、zipファイルは標準ライブラリでアクセスすることができます。
Qtも簡単にできるだろうと思っていたのですが、標準のライブラリではできないようです。
調べてみた結果、[quazip](http://quazip.sourceforge.net/)と言うものを見つけました。
この記事を書いたときの最新は0.7.1です。
これを使って、zipファイルにアクセスすることにしました。

しかし、OS Xでquazipをそのままビルドしてインストールすると、前回の記事に書いた install name が適切に設定されません。
これは、Qtのqmakeで使うproject ファイルで指定する`TEMPLATE`が`lib`のときに、そのままでは適切なinstall nameが設定されないためのようです。

少し調べた結果、`QMAKE_LFLAGS_SONAME`に、`-Wl,-install_name,/usr/local/lib/`のように記述すれば良いことがわかりました。
後述しますが、OS X用のアプリケーションを配布するときには、このinstall nameを書き換えるのですが、この時点で正しいinstall nameが入っていないと、書き換え時のファイルコピーに失敗します。

上記修正をするためのパッチを、[gist](https://gist.github.com/false-git/b83f4ab8c3fc6759d684)に置きました。

(Windowsのmingw32でビルドするための修正も含んでいます)

# ソースを書く
この部分は、説明を省きます。
Qt Creatorを使って、QMLとC++で書いていきます。
ソース中には qdoc形式のコメントを書いてあります。

# 国際化する
自分だけで使うことを考えると不要なのですが、マニュアルを読んでもわかりにくいところだったのでここだけ少し説明します。
Qt には、国際化のための仕組みがあって、少し気をつけてソースを書いて、リソースを翻訳してあげると、ほぼ自動で国際化してくれます。
詳しくは、[Internationalization with Qt](http://doc.qt.io/qt-5/internationalization.html) とか [Qt Linguist Manual](http://doc.qt.io/qt-5/qtlinguist-index.html) とかを見てください。

## 国際化に備えたソースの書き方
基本的なルールは、以下です。
* QMLでは、国際化対象の文字列は qsTr()で囲む
* C++では、tr()で囲む
他にも、日時や通貨の扱いの問題がありますが、基本はこれだけです。
このアプリでは、メニューの文字列だけを翻訳対象としています。

## tsファイルを用意する
projectファイルに、以下を追加します。

```mf
CODECFORSRC = UTF-8
TRANSLATIONS = i18n/ComicsViewer_ja.ts
lupdate_only {
    SOURCES += *.qml
}
```
`CODECFORSRC` は、ソースファイルの文字コードです。
`TRANSLATIONS`に翻訳する言語分のファイルを並べます。(ここでは日本語だけ)
`lupdate_only` は、この後使うlupdateコマンドを実行するときだけ、SOURCESにファイルを追加すると言う指定です。(qmlファイルは翻訳対象ですが、C++のコンパイル対象ではないため)

この状態で、lupdateを実行するとソースの中からqsTr()やtr()を探して、tsファイルを作ってくれますが、ディレクトリがないと怒られるので、i18nディレクトリはあらかじめ作っておきます。

lupdateの実行は、Qt Creatorを使っている場合は、`ツール->外部->Linguist->翻訳を更新 (lupdate)`を選びます。
すると、i18nディレクトリにComicsViewer_ja.tsと言うファイルができます。

## 翻訳する
このファイルはXMLですが、手で修正するのではなくLinguistと言うツールを使います。
Macの場合はLinguist.appを起動して、今作成したtsファイルを読み込みます。
後は、一つ一つ翻訳して、最後に保存します。

## 翻訳結果を反映する
Linguistでの作業が終わったら、今度はlreleaseと言うコマンドを実行します。再び Qt Creatorに戻って、`ツール->外部->Linguist->翻訳をリリース (lrelease)`を選びます。
すると、i18/ComicsViewer_ja.qm と言うファイルができあがります。
このqmファイルが実行時に必要です。

## qmファイルをアプリケーションに取り込む
できあがったqmファイルは、アプリケーションが実行時に読み込む必要があります。
実行ファイルと一緒に配布しても良いのですが、実行ファイル内に取り込んでしまいましょう。
以下のファイルを作成します。

```i18n.qrc
<RCC>
    <qresource prefix="/">
        <file>i18n/ComicsViewer_ja.qm</file>
    </qresource>
</RCC>
```
そして、ComicsViewer.pro の RESOURCES に追加します。

```ComicsViewer.pro(抜粋)
RESOURCES += qml.qrc \
    i18n.qrc
```
これで、実行ファイルにqmファイルの内容が一緒にリンクされます。
後は、プログラムからqmファイルを読み込んでやります。

```c++:main.cpp
    QTranslator myappTranslator;
    myappTranslator.load(":/i18n/ComicsViewer_" + QLocale::system().name());
    app.installTranslator(&myappTranslator);
```
app は QApplicationです。
ファイル名を:で始めると、リソースから読んでくれます。

# アイコンをつける
これも、自分だけで使うなら不要なのですが、一応やり方の説明と言うことで。
アイコンをつける方法は、[Setting the Application Icon](http://doc.qt.io/qt-5/appicon.html)に説明があります。
ここの説明は特にわかりにくくないと思います。

最初、フリー素材を使って、CC BY 3.0の説明とかを書こうと思っていたのですが、良いものが見つけられなかったので、自分で作成してみました。
[Inkscape](https://inkscape.org/ja/)と言うアプリを使って、1024x1024のアイコンを作ります。
デザインセンスのかけらも持ち合わせていないので、丸に文字と言うアイコンを作りました。
Inkscapeで作るのは、SVGと言うベクターデータなので、これを必要な解像度分のpngファイルにします。

## Mac OS X
OS Xの場合は、これらのpngファイルを決められた名前でiconsetと言うディレクトリに置きます。

続いて、iconutilと言うコマンドでicnsファイルを作ります。

```shell-session
% iconutil -c icns ComicsViewer.iconset
```
ファイルのサンプルは、ソースのiconディレクトリに置いてあります。

icnsファイルができたら、プロジェクトファイルに以下を追加します。

```mf:ComicsViewer.pro
macx: ICON = ComicsViewer.icns
```

## Windows
Windowsの場合は、icoファイルを作ります。
いろいろなやり方がありますが、ImageMagickのconvertコマンドで作りました。

```shell-session
% convert ComicsViewer.iconset/icon_16x16.png ComicsViewer.iconset/icon_32x32.png ComicsViewer.iconset/icon_256x256.png ComicsViewer.ico
```

icoファイルができたら、rcファイルを作ります。

```ComicsViewer.rc
IDI_ICON1 ICON DISCARDABLE "ComicsViewer.ico"
```
続いて、プロジェクトファイルに以下を追加します。

```mf:ComicsViewer.pro
win32: RC_FILE = ComicsViewer.rc
```

# Mac用の配布バイナリを作る
ここまでで、ビルド環境ではそのまま実行することができます。
しかし、Qtやquazipがインストールされていない環境では実行することができません。
OS Xの.appファイル(ディレクトリ)は、中にFrameworksと言うディレクトリを作って、そこに依存する動的ライブラリを入れることで、環境に依存しない形にすることができます。
これを行うのが、Qtに含まれているmacdeployqtコマンドです。

アプリケーションをビルドしてComicsViewer.appができたら、以下のコマンドでFrameworksを組み込みます。

```shell-session
% macdeployqt ComicsViewer.app -qmldir=~/qt/Qt5.4.0/5.4/clang_64/qml
ERROR: no file at "/opt/local/lib/libz.1.dylib"
ERROR: no file at "/opt/local/lib/libz.1.dylib"
ERROR: no file at "/opt/local/lib/libz.1.dylib"
ERROR: no file at "/opt/local/lib/libz.1.dylib"
```
`-qmldir`オプションには、~~`Qtのインストールディレクトリ/qml`~~を指定します。
QtQuick(QML)を使っている場合はこのオプションが必須です。使っていない場合は不要です。

**2015/3/24追記 ここから**
macdeployqtの-qmlオプションの説明が間違っていました。
ソース上の、qmlファイルがある場所を指定します。

```shell-session
% macdeployqt ComicsViewer.app -qmldir=.
ERROR: no file at "/opt/local/lib/libz.1.dylib"
ERROR: no file at "/opt/local/lib/libz.1.dylib"
ERROR: no file at "/opt/local/lib/libz.1.dylib"
ERROR: no file at "/opt/local/lib/libz.1.dylib"
```
**2015/3/24追記 ここまで**

さらに、`-dmg`オプションをつけると、.appを含んだdmgファイルが生成されます。
見慣れた 「アプリケーションにドラッグ＆ドロップしてね♪」と言う画面が出てくることを期待したのですが、残念ながら単に.appが含まれるだけのdmgでした。

Windowsにもwindeployqtと言うコマンドがあるようですが、こちらについては試していません。

以上、駆け足ですが、Qtを使ってアプリケーションを作成して、配布するところまでを簡単に説明してみました。
ソースを見てもらうと、C++とQMLでたったの800行余りしかないことがわかります。
Qtを使うと、GUIのプログラムが簡単に作れますので、是非使ってみてください。(宣伝)