##Qtとは
Qtとはクロスプラットフォームフレームワークです。
様々なライブラリが用意され、プラットフォームに依存しない形でアプリケーションを作成できます。対応しているプラットフォームは豊富で、デスクトップアプリケーションだけでなく、組み込みでも利用がされています。C++とPythonでのアプリケーション開発が行えます。
####採用事例
- Photoshop Elements
- Skype
- Google Earth
※Qtで作られたアプリケーションを調べてみるも参照ください。
###Qtのバージョン
###Qtの開発で重要な要素
以降の内容のソースコードはこちらからCloneできます。
https://github.com/ora1027/QiitaApplication.git
#####プロパティバインディング
プロパティバインディングとは、プロパティを別のプロパティと連動させる時に使用します。
例えば、ボタンの幅を、ボタンをクリックするごとに1.2倍にする時には下記のように書けます。
Button {
id: button
width: root.width / 2
height: button.width * 0.75
anchors.centerIn: parent
highlighted: true
Text {
id: button_text
anchors.centerIn: parent
text: qsTr("Button")
font.pointSize: 20
}
onClicked: {
if (button.font.pointSize <= 20 * 1.2 * 1.2)
button.font.pointSize *= 1.2
else
button.font.pointSize = 20
}
}
#####StackView
先程作成したPropertyBidingの要素をPropertyBinding.qmlファイルに移します。
MainPage.qmlを作成し、MainPageからPropertyBindingページに画面遷移するようにしましょう。
ApplicationWindow {
id: root
visible: true
width: 540
height: 960
StackView {
id: stackView
anchors.fill: parent
initialItem: MainPage {}
}
}
anchors.fill: parentに設定することを忘れがちになります。
MainPage.qmlも作成します。
Page {
id: root
header: ToolBar {
ToolButton {
id: menuButton
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
}
Label {
anchors.centerIn: parent
text: qsTr("Qiita Application")
font.pixelSize: 16
elide: Label.ElideRight
}
}
Column {
spacing: 10
Button {
text: qsTr("Property Binding")
onClicked: root.StackView.view.push("PropertyBindingPage.qml")
}
// ページが増えるとボタンを追加していく。
}
}
#####レイアウト
LayoutPage.qmlファイルを作成します。
import QtQuick 2.9
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.3
Page {
id: root
header: MyHeader{
toolButtonIcon: "qrc:/images/arrow_back-24px.svg"
onToolButtonClicked: root.StackView.view.pop()
}
GridLayout {
id: grid
anchors.fill: parent
columns: 2
rowSpacing: 5
columnSpacing: 5
anchors.margins: 5
// example models
property var titles: [ "title1", "title2", "title3", "title4", "title5" ]
property var values: [ "value1", "value2", "value3", "value4", "value5" ]
Repeater {
model: grid.titles
Label {
Layout.row: index
Layout.column: 0
Layout.fillWidth: true
Layout.fillHeight: true
text: modelData
}
}
Repeater {
model: grid.values
TextArea {
Layout.row: index
Layout.column: 1
Layout.fillWidth: true
Layout.fillHeight: true
text: modelData
}
}
}
}
#####カスタムコンポーネント
このアプリで使用するボタンとヘッダーを共通のコンポーネントとして作成しましょう。
MyButton.qml
import QtQuick 2.9
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.2
Button {
id: control
width: 270
height: 100
text: qsTr("Button")
property color buttonColor: "lightblue"
style: ButtonStyle {
background: Rectangle {
border.width: control.activeFocus ? 2 : 1
border.color: "#888"
radius: 4
gradient: Gradient {
GradientStop { position: 0 ; color: control.pressed ? Qt.darker(buttonColor, 1.5) : buttonColor }
GradientStop { position: 1 ; color: control.pressed ? Qt.darker(buttonColor, 3.0) : Qt.darker(buttonColor, 1.0) }
}
}
label: Text {
renderType: Text.NativeRendering
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
font.family: "Helvetica"
font.pointSize: 20
color: "blue"
text: control.text
}
}
}
MyHeader.qml
signal toolButtonClickedを登録することで、各ページごとにToolButtonが押された時の処理を切り替えることができます。
import QtQuick 2.9
import QtQuick.Controls 2.3
ToolBar {
property string toolButtonIcon: toolButton.icon.source
signal toolButtonClicked
ToolButton {
id: toolButton
icon.source: toolButtonIcon
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
onClicked: toolButtonClicked()
}
Label {
anchors.centerIn: parent
text: qsTr("Qiita Application")
font.pixelSize: 16
elide: Label.ElideRight
}
}
これによって全てのページにヘッダーの詳細を書くことが必要なくなる。共通部分はまとめてMyHeader.qmlに書きます。
アイコンやクリックされた時の処理はページごとに切り替えることができます。
// header: ToolBar {
// ToolButton {
// id: menuButton
// anchors.left: parent.left
// anchors.verticalCenter: parent.verticalCenter
// }
// Label {
// anchors.centerIn: parent
// text: qsTr("Qiita Application")
// font.pixelSize: 16
// elide: Label.ElideRight
// }
// }
header: MyHeader{
toolButtonIcon: "qrc:/images/arrow_back-24px.svg"
onToolButtonClicked: root.StackView.view.pop()
}
#####リソースファイル
Resources上で右クリックを押す。Add Newを選択肢、Resourceファイルを追加します。
image.qrcファイルが作成されます。
① プロジェクトフォルダにimagesフォルダを作成し、その中に必要なファイルを追加します。
② Add > Add prefix => /
③ Add > Add Files
リソースファイルにアイコンなどの画像を追加しないとアプリケーション内で使用できません。
#####アニメーション
AnimationPage.qmlを作成します。
import QtQuick 2.9
import QtQuick.Controls 2.3
Page {
id: root
header: MyHeader{
toolButtonIcon: "qrc:/images/arrow_back-24px.svg"
onToolButtonClicked: root.StackView.view.pop()
}
property string text: "This is Animation Page !!"
property bool animated: true
focus: true
Keys.onPressed: {
if (event.key == Qt.Key_Delete || event.key == Qt.Key_Backspace)
root.remove()
else if (event.text != "") {
root.append(event.text)
}
}
function append(text) {
root.animated = false
var lastLetter = root.children[root.children.length - 1]
var newLetter = letterComponent.createObject(root)
newLetter.text = text
newLetter.follow = lastLetter
root.animated = true
}
function remove() {
if (root.children.length)
root.children[root.children.length - 1].destroy()
}
function doLayout() {
var follow = null
for (var i = 0; i < root.text.length; ++i) {
var newLetter = letterComponent.createObject(root)
newLetter.text = root.text[i]
newLetter.follow = follow
follow = newLetter
}
}
Component {
id: letterComponent
Text {
id: letter
property variant follow
x: follow ? follow.x + follow.width : root.width / 6
y: follow ? follow.y : root.height / 2
font.pixelSize: 40;
font.bold: true
color: "grey";
styleColor: "#222222";
style: Text.Outline
MouseArea {
anchors.fill: parent
drag.target: letter; drag.axis: Drag.XAndYAxis
onPressed: letter.color = "#dddddd"
onReleased: letter.color = "#999999"
}
Behavior on x { enabled: root.animated; SpringAnimation { spring: 3; damping: 0.3; mass: 1.0 } }
Behavior on y { enabled: root.animated; SpringAnimation { spring: 3; damping: 0.3; mass: 1.0 } }
}
}
Component.onCompleted: doLayout()
}
#####スプラッシュ
SplashShowクラスを作成します。
ヘッダーファイル
#ifndef SPLASHSHOW_H
#define SPLASHSHOW_H
#include <QObject>
#include <QQuickView>
class SplashShow : public QObject
{
Q_OBJECT
public:
SplashShow(QQuickView* viewer);
void start();
Q_INVOKABLE end();
public slots:
void init();
private:
QQuickView* viewer_;
};
#endif // SPLASHSHOW_H
ソースファイル
#include "SplashShow.h"
#include <QCoreApplication>
#include <QtQml/QQmlContext>
#include <QQmlApplicationEngine>
#include <QDir>
#include <QThread>
#include <QDebug>
SplashShow::SplashShow(QQuickView* viewer)
: viewer_(viewer)
{}
void SplashShow::start()
{
qDebug() << "SplashShow::start() is called";
QDir directory(QCoreApplication::applicationDirPath());
QString path = directory.absoluteFilePath("../../SplashPage.qml");
qDebug() << "SplashShow::init() path = " << path;
viewer_->engine()->rootContext()->setContextProperty("SplashShow", this);
viewer_->setSource(QUrl::fromLocalFile(path));
viewer_->show();
}
void SplashShow::init()
{
qDebug() << "SplashShow::init() is called";
QThread::msleep(1000);
}
void SplashShow::end()
{
qDebug() << "SplashShow::end() is called";
viewer_->close();
}
import QtQuick 2.0
Item {
id: root
width: 540
height: 960
/*
スプラッシュ時に表示する画像などを記述する
:
:
*/
Loader {
id: main
anchors.fill: parent
asynchronous: true
visible: status == Loader.Ready
}
PauseAnimation {
id: fakeLoadingDelay
duration: 50
onRunningChanged: {
if ( !running ) {
// Call the init() function of SplashShow
SplashShow.init();
console.log("Inside Puase Animation")
// When the init() returns, load main.qml
main.source = "main.qml"
}
}
}
Component.onCompleted: fakeLoadingDelay.start()
}
#####Model
MainPageから遷移するページが増える度にButtonのコンポーネントを追加したくないです。
そこで、ModelとRepeaterの機能を使用して、ModelからLabelとqmlファイルを取得し、Repeaterで表示するように変更しましょう。
現状
Button {
text: qsTr("Property Binding")
onClicked: root.StackView.view.push("PropertyBindingPage.qml")
}
Button {
text: qsTr("Layout")
onClicked: root.StackView.view.push("LayoutPage.qml")
}
Button {
text: qsTr("Animation")
onClicked: root.StackView.view.push("AnimationPage.qml")
}
ScreenTransitionModel.qmlを作成します。
import QtQuick 2.9
ListModel {
ListElement {label: "Property Binding"; path: "PropertyBindingPage.qml"}
ListElement {label: "Layout"; path: "LayoutPage.qml"}
ListElement {label: "Animation"; path: "AnimationPage.qml"}
ListElement {label: "End"; path: ""}
}
最終的なMainPage.qml
import QtQuick 2.9
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.3
Page {
id: root
header: MyHeader {
id: header
toolButtonIcon: "qrc:/images/headline-24px.svg"
onToolButtonClicked: drawer.open()
}
Drawer {
id: drawer
width: Math.min(root.width, root.height) / 3
height: root.height
ListView {
focus: true
currentIndex: -1
anchors.fill: parent
delegate: ItemDelegate {
width: parent.width
text: model.text
highlighted: ListView.isCurrentItem
onClicked: {
drawer.close()
model.triggered()
}
}
model: ListModel {
ListElement {
text: qsTr("Open...")
}
ListElement {
text: qsTr("About...")
}
}
ScrollIndicator.vertical: ScrollIndicator { }
}
}
ColumnLayout {
anchors.fill: parent
spacing: 10
Image {
width: 400
height: 400
Layout.alignment: Qt.AlignHCenter
fillMode: Image.PreserveAspectFit
clip: true
source: "qrc:/images/DotPicture.png"
}
Repeater {
model: ScreenTransitionModel {}
Component.onCompleted: console.log("inside Repeater delegate. model = ", model)
delegate: MyButton {
text: model.label
Layout.alignment: Qt.AlignHCenter
onClicked: model.label === "End" ? SplashShow.end() : root.StackView.view.push(model.path)
Component.onCompleted: console.log("MyButton width = ", width + ", MyButton height = ", height)
}
}
}
}
##参考
####QtのExample
Qtのサンプルは豊富で実はいい情報がたくさん詰まっています。慣れてくると非常に勉強になります。
- Chat Tutorial
- animation
下のページからQMLについて豊富な情報がたくさん仕入れられます。
Qt5 Cadaques
最後まで読んで下さりありがとうございました。
今後もWebView, SQL, Bluetooth接続, Camera, OpenGLなどの機能を追加していきます。