Qt6 QML Book 第一章
サンプルコード
https://qmlbook.github.io/
ここにソースコードがありますので、ここを見てください。
こう書かれています。
『Qt5 Cadaquesオンラインブックへようこそ!なぜQt5なのか?それはQt5が素晴らしいからです!なぜCadaquesなのか?それは、著者の一人がスペイン北東部のこの岩の多い海岸で素晴らしい休日を過ごしたからです。
Qt5プログラミングに関する全章は、ユルゲン・ボックラゲ=リアンネルとヨハン・テリンによって書かれ、ここで公開されています。すべての書籍コンテンツはクリエイティブ・コモンズ 表示 - 非営利 - 継承 4.0ライセンスの下でライセンスされています。また、例はBSDライセンスの下でライセンスされています。』
クリエイティブ・コモンズ 表示 - 非営利 - 継承 4.0ライセンス
ですが、このページは2020年の12月7日以降更新されていないようです。そして、Qt6ではなく、内容がQt5になっています。私もソースコードをダウンロードして見ましたが、いくつか気になる点がありました。
・基本的に動かないコードがある
・この本の内容と違うコードがある
・全ての章のコードがあるわけではない
私の見落としかもしれませんが、この3つが原因で、噛み合わない、抜け落ち、バージョン違いが散見されます。
再度確認ですが、この本はQt6.7をベースに、Qt6 QMLの内容を見ていきます。
上記のような事情もあり、筋を通すのがなかなか困難ではありましたが、出来るところまではやってみたいと思います。
この本は、著者(私)が、『Qt Company Qt6 QML』(原著とします。)を読破する際、つまずいたことを書き留めた本になります。 内容は、英語の全訳ではなく、日本語に訳したうえで、更にかみ砕いた内容になっています。英語の訳が載せられているだけの場合もあります。本書の内容に従っていけば、英文を読んだことにもなり、+αの知識が身につくはずです。ですから、再度原著を振り返って読まなくても、内容が吸収されることになります。しかし、ソースコードが問題なく動く場合とか、単なる和訳でも十分な場合は、特に何も追加していません。
私個人の意見の前には、必ず※を置いています。それ以外は訳文だと思ってください。
全てが説明通りにいくとは限りません。例えば、私はMaterialスタイルの適用時、原著のイメージ通り橙色が出てくると思っていた箇所で青色が出てきたりすることがありました。この本はあまりハマりすぎるとよくないと思っています。全てが全て100%完璧に出来ているというわけではないです。今でも、この文書を改善してくれる人を募っています。
私がざっと読んでみて感じたことは、例えば、EffectsのShaderの項目のコードは動きません。まだ、QtQuick3Dのソースコード自体が存在していないようです。
pdf版はお勧めしません。こちらのURLの、ウェブページを読むことをお勧めします。pdfはダウンロードができ、印刷などもできるのでしょうが、コードの抜けが目立ちます。
https://www.qt.io/product/qt6/qml-book
/*
* Copyright (c) 2013, Juergen Bocklage-Ryannel, Johan Thelin
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the editors nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
著作権の和訳:
* 著作権 (c) 2013, Juergen Bocklage-Ryannel, Johan Thelin * 全著作権所有。
* * ソースおよびバイナリ形式での再配布および使用は、変更の有無にかかわらず、以下の条件が満たされている場合に許可されます:
* * ソースコードの再配布は、上記の著作権表示、この条件リスト、および以下の免責事項を保持している必要があります。
* * バイナリ形式での再配布は、上記の著作権表示、この条件リスト、および以下の免責事項を、配布に付随するドキュメントおよび/またはその他の資料に含める必要があります。
* * 編集者の名前や、その貢献者の名前を、特定の事前書面による許可なしに、このソフトウェアから派生した製品を支持または推奨するために使用することはできません。
* * このソフトウェアは、著作権者および貢献者によって「現状のまま」提供されており、 * 商品性および特定の目的への適合性を含むがこれに限定されない、明示または黙示の保証はすべて否認されます。
* いかなる場合も、<著作権者> は、このソフトウェアの使用に起因する、直接的、間接的、付随的、特別、懲罰的、または結果的な損害(代替品またはサービスの調達、 * 使用の喪失、データまたは利益の損失、または事業の中断を含むがこれに限定されない)について、 たとえそのような損害の可能性について通知されていたとしても、 いかなる理論に基づくかを問わず、一切の責任を負わないものとします。
QMLで書く最初のコード (ここは私の文章です。)
このドキュメントはQML Documentationを補完するために書かれたものなのですが、ドキュメントから読むと、出だしからつまずく可能性があります。
まず、プロジェクトの立ち上げが肝心です。これから、失敗例を載せます。
ついつい、Qt Quick アプリケーションでしょ?と思ってこちら側からスタートすると、痛い目に合います。ここでは、その理由を書きたいと思います。
ドキュメントには最初のサンプルコードとしてこのように示されています。
import QtQuick
Image {
id: root
source: "images/background.png"
}
このコードでいきなりエラーが発生します。
1つ目の理由です。Imageは、単独で表示可能なコンポーネントではない、というのが普通です。Window等のGUIコンポーネントの子として存在することで、初めて表示可能となります。
従って、
import QtQuick
Window
{
id: root
visible: true
Image{
id: background
source: "background.png"
anchors.fill: parent
}
}
という書き方が正しいです。(Window以外でももちろんOK)
次に、初心者あるあるの問題が浮上してきます。画像ファイルのロードができないという問題です。例えば、クラシックなQtを利用する方は、プロジェクトと同じディレクトリに画像ファイルや、フォルダを置けばいいと考えるでしょう。そして、リソースファイルを作って仮想ディレクトリから画像を読み込ませればいいと。
もしあなたが本当の初心者であれば、何のことやらわからないと思いますが、クラシックなQtでできていたリソースのロードが、この方法ではできません。
CMakeLists.txtを書く際、画像ファイルをロードするために設定を行う必要があります。この場合、リソースファイルの設定方法はこのように行うのが正しいみたいです。
CmakeLists.txt(一部)
qt_add_qml_module(appQt6Introduction
URI Qt6Introduction
VERSION 1.0
QML_FILES
Main.qml
RESOURCES //この書き方に注目 ""はなくてもOK
"background.png"
"pinwheel.png"
"pole.png"
)
これは、今のプロジェクトがあるフォルダに、3つのpngファイルが置かれている状態です。RESOURCESと書き、ファイル名をダイレクトに書き込みます。このようにすれば、ファイルをロードすることができます。 クラシックなQtをお使いの方は、qrcファイルを作ってしまいがちですが、どうやらこの方法は使えないようです。(私は絶対にイメージが表示されませんでした。) Qtの適当なサンプルコードを開き、同じようにImageクラスを使って画像ファイルを使っているコードを見てみると、CMakeLists.txtの内容は上記のようになっているものばかりでした。 そして、結果がこれです。
思ったほど画像が大きく出ませんでした。でもうまくいったことがよくわかります。なぜ私が書いたコードの結果と、原著の実行結果とが、全くと言っていいほど手段も結果も食い違ってしまっているのか・・・。
(※ちなみに、この方法でも画像が表示されない場合は、画像ファイルの方が壊れていることを疑ったほうがいいです。私は運悪く、壊れた画像を一生懸命ロードしようとして無駄に時間を使った経験があります。ただ、他のイメージビューアでは、表示可能だったので、そこがまた、混乱の元でした。拡張子が違う、ということも可能性としてありえます。いずれにせよ、コードに問題が無いように見えるのに、なぜか画像が表示されないのであれば、まずは画像を取り換えてみることです。)
結論:プロジェクトの立ち上げから間違えていたからでした!
このサンプルコードをうまく動かすには、
他のプロジェクト⇒Qt Quick UI Prototype⇒選択...から入らなければならないのです。
サンプルコードのページが用意されているのに、ドキュメントを真っ正直に読破しようとすると、良くない結果になります。ですから、皆さんもサンプルコードをダウンロードして、そのサンプルコードを利用するようにしてください。(とはいえ、サンプルコードは基本的にQt5をベースとしているようでしたから・・・。)
Qt Creatorは、2重に起動することもできます。1つは自分が書くコードのため。もう一つはそのサンプルコードを実行したり、読んだりするために使うのがいいかもしれません。
では、これでプロジェクトを立ち上げてみましょう。
プロジェクト名は何でもいいです。
最初に出来上がるファイルは少ないです。3つだけです。
ここに、
3つの画像ファイルを入れます。
そして、
import QtQuick
Image{
id: root
source: "background.png"
}
と書きます。いざ!(F5)を押します。
いかがでしょうか。先ほどと全く違った結果になりましたね。
では、このまま原著を読み進めていきましょう。原著だと、imagesというフォルダを作り、その中に3つの画像ファイルを入れていますね。その場合は、images/という文字列をsourceの値に加えることになります。
Imageオブジェクトがロードしたimage画像のサイズにより、表示される画像のサイズが決まります。だから、いちいちwidth, heightを設定する必要はありません。
idというのは、特別なプロパティです。選択的なプロパティでもあります。つまり、設定しなくてもよいです。一度設定すれば、最後まで変更してはいけません。
rootというのは、QMLプロジェクト全体に渡ってアクセス可能なただ一つのidです。著者が使用した感じだと、定義後は、どの位置からでも、rootはアクセス可能です。
では、もう2つのイメージを組み合わせるようにコードを書きましょう。
import QtQuick
Image{
id: root
source: "background.png"
Image {
id: pole
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
source: "pole.png"
}
Image {
id: wheel
anchors.centerIn: parent
source: "pinwheel.png"
}
}
はい。とっても簡単ですね。
このコードで重要なのは、anchorです。pinwheel.pngと、pole.pngは、どのようにしてこの位置に置かれたのでしょうか。
anchors.centerIn: parent
parentは親です。つまり、Imageのrootを意味しています。QMLは、入れ子関係にすることで、親子関係を表現します。包み込んでいる側が親。包み込まれている方が子や孫・・・つまり直系卑属です。
anchorというのは、日本語で言えば『支点・固定具』という意味だと考えてください。centerIn(centerinという単語は、英語の辞書にはありませんでした。Center in +場所で、場所の中央を指すのだと思います。)ですから、anchors.centerIn: parentで、固定具を、親の中心に設定する、という意味になります。ここで指定されるのは、parent自体です。これは、このコードと同じ意味を持ちます。
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
こちらが、https://doc.qt.io/qt-6/qtquick-positioning-anchors.htmlから借用した画像です。ここには、centerInという文字がありませんけれども、horizontalCenterと、verticalCenterを組み合わせた所を指しています。
そして、こちらがpinwheelの位置です。では、poleはどうなっているかというと、
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
2つ設定されていますね。でも考え方は同じです。poleの水平線の中心を、親の水平線の中心に合わせる。アンカーの底を親の底に合わせる。図を見ると、実際にそうなっているということが確認できるはずです。
verticalは垂直なので、horizontalCenterとverticalCenterは逆なのではないか?と感じてしまいますが、注意しましょう。
anchors.centerInだけは、指定の仕方がちょっと特殊で、parent自体を指定していますね。つまり、どのオブジェクトの中心に置くのか、を指定するエイリアス(horizontalCenterとverticalCenterの組み合わせの別名)の役割を果たしているのでしょう。
anchorsの機能は、親に当たるオブジェクト。あるいは、兄弟に当たるオブジェクト間でしか使えません。poleとwheelからすれば、親はroot。poleとwheelは兄弟です。英語で言えば、兄弟はsiblingと書きます。
この短いサンプルコードで見たように、親の上に子が表示される関係にある、ということがわかります。そして、兄弟関係では、コード上、下の方に書かれることで、結果として上に表示されるということもわかります。ここは非常に重要ですね。
では、次のお話に行きます。それは、この風車をマウスで回転させるというお話です。
import QtQuick
Image{
id: root
source: "background.png"
MouseArea {
id: mouseArea
anchors.fill: parent
onClicked: wheel.rotation += 90
}
...
MouseAreaをrootオブジェクト全体に敷きます。この状態で、風車の羽をクリックすると、色が変化して回転するのがわかりますね。
ここで注目するコードは、onClickedです。MouseAreaは、次のようなシグナルを持ちます。
これは、MoueArea QML Typeから取ってきたスクリーンショットです。この中に、clickedという項目があることがわかりますね。QMLでは、
on<SignalName>:{}
これでシグナルを作る事が出来ます。{}は、複数行のコードを書く場合に書きます。1行の時は省略されることが多いです。
だから、上のコードでは、onClickedとされ、その後にwheel.rotation += 90と書いてあります。wheelは、idがwheelのImageコンポーネントのことです。で、Image QML Typeは、Item QML Typeを継承しており、Item QML Typeがrotationというプロパティを持っていることがわかります。
これを応用すれば、何だって回転させることができちゃうわけですね。
例えば、poleの方を回転させてしまいましょう。
MouseArea {
id: mouseArea
anchors.fill: parent
onClicked: pole.rotation += 90
}
こうなりますが、風車よりもわかりにくいですね。
シグナルについて、もう一つ補足することがあります。それは、このタイプのシグナルです。
on<PropertyName>Changed
先ほどと同じように作り出すことができます。例えば、
Image {
id: pole
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
onAnchorsChanged:{
console.log(anchors)
}
source: "pole.png"
onSourceChanged:
{
console.log(source)
}
onRotationChanged:{
console.log(rotation)
}
}
こういうことができてしまうわけです。anchorsが別の値に置き換わったときに、onAnchorsChangedのコードが実行されます。onSourceChangedも、onRotationChangedも同様、それぞれのプロパティの値が変化したときに{}内のコードが実行されます。
このシグナルは、よく使います。デバッグにも大活躍します。軽い気持ちでさっと作ってさっと削除できます。
さて、風車が90度回転するのはわかったのですが、動きがぎこちない。流れが悪い。壊れた感じです。もっと流れるように回転させましょう。
それには、アニメーションを使います。ここでは、アニメーションの中でも、Behaviorを使います。Behaviorプロパティというのはプロパティが変更されるたびに呼び出されるアニメーションです。実際に見てみましょう。
Defines a default animation for a property change.
あるプロパティの変化にたいする初期設定のアニメーションを定義
Image {
id: root Image
{
id: wheel
Behavior on rotation
{
NumberAnimation {
duration: 250
}
}
}
}
読み解いてみましょう。回転させたいのはpinwheel.pngなので、id: wheelのところにアニメーションをセットします。Behaviorアニメーションは、1つのプロパティが変化するたびに呼ばれるアニメーションなので、Behavior on rotationとします。これで、rotationプロパティについての振る舞いだと指定したわけです。
NumberAnimationというのは、読んで字のごとく、数値(int型)のプロパティが変化するときに呼ばれるアニメーションであり、PropertyAnimationの特殊なタイプです。
durationというのは、プロパティが変化する間隔で、1000が1秒を指します。250は、その4分の1ですから、0.25秒ごとにrotationが90度変化します。
全体のコードはこうなります。
import QtQuick
Image{
id: root
source: "background.png"
MouseArea{
id: mouseArea
anchors.fill: parent
onClicked:{
wheel.rotation += 90
}
}
Image {
id: pole
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
source: "pole.png"
}
Image {
id: wheel
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
source: "pinwheel.png"
Behavior on rotation {
NumberAnimation {
duration: 250
}
}
}
}
絵を載せることはできませんが、QMLドキュメントから拝借しましょうか。
風車は先ほどよりも滑らかに回転します。
ただ、onClickedがシグナルなので、回転が継続しているように見せるためには、何回もクリックする必要がありますし、実際はこのように見えることはないでしょう。実は、この画像は別に用意されているのです。blur.pngです。pinwheel.pngの代わりに、blur.pngを使うと書いてあります。
冒頭にお示ししましたソースコードをダウンロードしていただければと思います。
さて、私はこの実行結果を見て思うのですが、クリックするごとに回転する、というのはやはりまだどこかぎこちない気もします。
そこで、ちょっと遊びで自動で回転するようにしてみましょう。単純にQML ドキュメントの内容を引っ張ってきただけでは、本書の価値もあまりありません。だから、この本では、少しだけコードを追加してみようと思います。
import QtQuick
Image{
id: root
source: "background.png"
MouseArea{
id: mouseArea
anchors.fill: parent
onClicked:{
timer.running = true
}
}
Timer {
id: timer
interval: 50; running: true; repeat: true
onTriggered: wheel.rotation += 90
}
}
このようにコードを書き加えてみてください。0.25秒ごとに90度変化するのは変わりませんが、Timerが走っているので、常にwheel.rotation += 90が実行されます。インターバルは250なので、0.25秒ごとに、90度変更のコードが実行されます。そして、0.25秒で90度回転します。90度変化したと思ったらすぐにTimerが実行される感じですね。クリックすると、タイマーがスタートします。
第1章 おわり
お疲れさまでした。Qt6 QML のドキュメントに書かれている冒頭の内容をここで私なりに解説させていただきました。
原著では、ここから本格的な話が始まるようで、Qt6 SDKをインストールする説明が続いていくようです。SDKというのは、Software Development Kitの略で、開発環境のことを言っています。皆さんは既にQt Creatorをインストールされてらっしゃることでしょう。Qt Creatorは全て揃っているキットなので、SDK=Qt Creatorであるとみなしていいと思います。
Qt Creatorはインストーラを使ってインストールする場合と、Gitからクローンしてインストールする場合があるようです。
https://wiki.qt.io/Building_Qt_6_from_Git
Gitからクローンしてインストールする方法は、こちらを参考にしてください。
Qtの書籍紹介です。 QML関係
この記事が気に入ったらサポートをしてみませんか?