SlideShare a Scribd company logo
QtとC++で
GUIプログラミング
@seanchas_t
2013年9月28日
13年9月29日日曜日
自己紹介
• twitter.com/seanchas_t
• [PR] Qtでペイントソフトを制作中
- github.com/iofg2100/PaintField
13年9月29日日曜日
イントロダクション
13年9月29日日曜日
はじめに
• C++でGUIアプリを作ろう
• 今回紹介するのは
Qtを使ったGUIアプリ開発
- コード例、Qtのシステムの概略の紹介
13年9月29日日曜日
目次
• イントロダクション
• 使ってみる (Qt Widgets編)
• 使ってみる (Qt Quick編)
• QObjectについて
• Qtのコンテナライブラリ
• Qtの描画システム
13年9月29日日曜日
Qtについて
• クロスプラットフォームのアプリケーション、UIフ
レームワーク
- WindowsでもMacでもLinuxでも
同じコードが動く
- Android、iOSにもQt 5.1から実験的に対応
- 今回はデスクトップに限った話
13年9月29日日曜日
Getting Started
• qt-project.org/downloads
からダウンロード
- もしくはお好きなパッケージマネージャで
• 統合開発環境はQt Creator
• C++11を使う場合は、
プロジェクトファイル (*.pro) に
“CONFIG += c++11” を追加
13年9月29日日曜日
使ってみる (Qt Widgets編)
13年9月29日日曜日
Qt Widgetsとは
• Qtに昔からある、GUIの記述方法
• C++で各UI部品を作って配置
13年9月29日日曜日
• ボタンを押したら数字が1増える
シンプルなカウンタを作ろう
こんな感じ
ボタン
ラベル
13年9月29日日曜日
シンプルなカウンタを作ろう
カウンタ
イメージ図
ボタン ラベル
クリック
カウントアップ 値が変化
文字列を更新 ↓UI
13年9月29日日曜日
シンプルなカウンタを作ろう
• Qtでは「シグナル」と「スロット」
を使ってこの図と同じようにコードを組む
13年9月29日日曜日
カウンタ
class Counter : public QObject
{
Q_OBJECT
public:
explicit Counter(QObject *parent = 0) : QObject(parent) {}
signals:
void countChanged(int count);
public slots:
void increment()
{
++m_count;
emit countChanged(m_count);
}
private:
int m_count = 0;
};
13年9月29日日曜日
カウンタ
class Counter : public QObject
{
Q_OBJECT
public:
explicit Counter(QObject *parent = 0) : QObject(parent) {}
signals:
void countChanged(int count);
public slots:
void increment()
{
++m_count;
emit countChanged(m_count);
}
private:
int m_count = 0;
};
このオブジェクトが
発することのできる「シグナル」
(値が変化したことを知らせる)
13年9月29日日曜日
カウンタ
class Counter : public QObject
{
Q_OBJECT
public:
explicit Counter(QObject *parent = 0) : QObject(parent) {}
signals:
void countChanged(int count);
public slots:
void increment()
{
++m_count;
emit countChanged(m_count);
}
private:
int m_count = 0;
};
シグナルに呼応して
実行される「スロット」
(カウントアップ)
13年9月29日日曜日
トップレベルのウィジェット
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(Counter *counter, QWidget *parent = 0) : QWidget(parent)
{
auto layout = new QVBoxLayout();
auto label = new QLabel("0");
connect(counter, SIGNAL(countChanged(int)), label, SLOT(setNum(int)));
layout->addWidget(label);
auto button = new QPushButton("Increment");
connect(button, SIGNAL(pressed()), counter, SLOT(increment()));
layout->addWidget(button);
setLayout(layout);
}
};
13年9月29日日曜日
トップレベルのウィジェット
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(Counter *counter, QWidget *parent = 0) : QWidget(parent)
{
auto layout = new QVBoxLayout();
auto label = new QLabel("0");
connect(counter, SIGNAL(countChanged(int)), label, SLOT(setNum(int)));
layout->addWidget(label);
auto button = new QPushButton("Increment");
connect(button, SIGNAL(pressed()), counter, SLOT(increment()));
layout->addWidget(button);
setLayout(layout);
}
};
レイアウトで
ボタンやラベルを配置
13年9月29日日曜日
トップレベルのウィジェット
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(Counter *counter, QWidget *parent = 0) : QWidget(parent)
{
auto layout = new QVBoxLayout();
auto label = new QLabel("0");
connect(counter, SIGNAL(countChanged(int)), label, SLOT(setNum(int)));
layout->addWidget(label);
auto button = new QPushButton("Increment");
connect(button, SIGNAL(pressed()), counter, SLOT(increment()));
layout->addWidget(button);
setLayout(layout);
}
};
先ほどのCounterと、
ボタン・ラベルのシグナル/スロットを接続
13年9月29日日曜日
main関数
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Counter counter;
Widget widget(&counter);
widget.show();
return app.exec();
}
結局こんな感じになる
(先ほどと同じ)
13年9月29日日曜日
使ってみる (Qt Quick編)
13年9月29日日曜日
Qt Quickとは
• QMLという言語を使ってUIを記述
• QMLは動的にロードされる
• CSSっぽくオブジェクトを配置、
プロパティを設定
• JavaScriptでロジックも記述可能
- JavaScriptでは無理なことも
• ローカルファイルへのアクセスとか
13年9月29日日曜日
Qt Quickとは
• C++との連携も可能
- QMLからC++を呼び出す
- C++からQMLを呼び出す
- Qt QuickのUIアイテムをC++で実装する
• C++だとQQuickItem
13年9月29日日曜日
カウンタ
class Counter : public QObject
{
Q_OBJECT
public:
explicit Counter(QObject *parent = 0) : QObject(parent) {}
Q_PROPERTY(int count READ count WRITE setCount NOTIFY countChanged)
int count() const { return m_count; }
void setCount(int count) { m_count = count; }
signals:
void countChanged(int count);
public slots:
void increment()
{
++m_count;
emit countChanged(m_count);
}
private:
int m_count = 0;
};
13年9月29日日曜日
カウンタ
class Counter : public QObject
{
Q_OBJECT
public:
explicit Counter(QObject *parent = 0) : QObject(parent) {}
Q_PROPERTY(int count READ count WRITE setCount NOTIFY countChanged)
int count() const { return m_count; }
void setCount(int count) { m_count = count; }
signals:
void countChanged(int count);
public slots:
void increment()
{
++m_count;
emit countChanged(m_count);
}
private:
int m_count = 0;
};
setter、getter、変更時のsignalを登録して
プロパティを宣言
13年9月29日日曜日
QML
import QtQuick 2.1
import QtQuick.Controls 1.0
ApplicationWindow {
title: "Window"
Column {
anchors.fill: parent
anchors.margins: 10
spacing: 10
Label {
text: counter.count
}
Button {
text: "Button"
onClicked: {
counter.increment()
}
}
}
}
13年9月29日日曜日
QML
import QtQuick 2.1
import QtQuick.Controls 1.0
ApplicationWindow {
title: "Window"
Column {
anchors.fill: parent
anchors.margins: 10
spacing: 10
Label {
text: counter.count
}
Button {
text: "Button"
onClicked: {
counter.increment()
}
}
}
}
Qt Quick Controls
以前のQt Quickにはなかったボタン、メニューなどの
よく使うGUI部品などが使えるようになった
(5.1から)
13年9月29日日曜日
QML
import QtQuick 2.1
import QtQuick.Controls 1.0
ApplicationWindow {
title: "Window"
Column {
anchors.fill: parent
anchors.margins: 10
spacing: 10
Label {
text: counter.count
}
Button {
text: "Button"
onClicked: {
counter.increment()
}
}
}
}
C++で書いたCounterオブジェクトを使用可能
13年9月29日日曜日
QML
import QtQuick 2.1
import QtQuick.Controls 1.0
ApplicationWindow {
title: "Window"
Column {
anchors.fill: parent
anchors.margins: 10
spacing: 10
Label {
text: counter.count
}
Button {
text: "Button"
onClicked: {
counter.increment()
}
}
}
}
プロパティバインディング
13年9月29日日曜日
プロパティバインディング
• プロパティに他のオブジェクトの
プロパティを使った式を使うと、
自動的にその変化に追従させられる
• Reactive Programming
- Excelっぽい
• QMLの大きな特徴
13年9月29日日曜日
QML
import QtQuick 2.1
import QtQuick.Controls 1.0
ApplicationWindow {
title: "Window"
Column {
anchors.fill: parent
anchors.margins: 10
spacing: 10
Label {
text: counter.count
}
Button {
text: "Button"
onClicked: {
counter.increment()
}
}
}
}
プロパティバインディング
textが、counterのcountプロパティ
(先ほど宣言した)に追従
13年9月29日日曜日
QML
import QtQuick 2.1
import QtQuick.Controls 1.0
ApplicationWindow {
title: "Window"
Column {
anchors.fill: parent
anchors.margins: 10
spacing: 10
Label {
text: counter.count
}
Button {
text: "Button"
onClicked: {
counter.increment()
}
}
}
}
JavaScriptで処理を書ける
(カウンタのインクリメント)
13年9月29日日曜日
main関数
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QQmlApplicationEngine engine(QUrl("qrc:/main.qml"));
Counter counter;
engine.rootContext()->setContextProperty("counter", &counter);
auto window = qobject_cast<QQuickWindow *>(engine.rootObjects().first());
window->show();
return a.exec();
}
13年9月29日日曜日
main関数
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QQmlApplicationEngine engine(QUrl("qrc:/main.qml"));
Counter counter;
engine.rootContext()->setContextProperty("counter", &counter);
auto window = qobject_cast<QQuickWindow *>(engine.rootObjects().first());
window->show();
return a.exec();
}
counterをQML内で使えるようにする
13年9月29日日曜日
main関数
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QQmlApplicationEngine engine(QUrl("qrc:/main.qml"));
Counter counter;
engine.rootContext()->setContextProperty("counter", &counter);
auto window = qobject_cast<QQuickWindow *>(engine.rootObjects().first());
window->show();
return a.exec();
} ApplicationWindow(C++ではQQuickWindowに相当)
を参照して表示させる
13年9月29日日曜日
QObject
13年9月29日日曜日
QObjectとは
• 普通のC++にはないQtの様々な機能
- signal・slot
- QMLからの
メソッドの動的な呼び出し
QObjectの機能
13年9月29日日曜日
QObjectとは
• QWidget系のオブジェクトも
QMLのオブジェクトも
全部QObjectから派生している
• 先ほど作ったCounterクラスもQObjectから派生
13年9月29日日曜日
QObjectの例 (さっきの)
class Counter : public QObject
{
Q_OBJECT
public:
explicit Counter(QObject *parent = 0) : QObject(parent) {}
Q_PROPERTY(int count READ count WRITE setCount NOTIFY countChanged)
int count() const { return m_count; }
void setCount(int count) { m_count = count; }
signals:
void countChanged(int count);
public slots:
void increment()
{
++m_count;
emit countChanged(m_count);
}
private:
int m_count = 0;
};
signal・slot
プロパティ
13年9月29日日曜日
QObjectの機能
• 親子構造
• シグナルとスロット
• プロパティ
• リフレクション
- 動的なメソッドの呼び出し
- 動的なプロパティの書き換え
• など
13年9月29日日曜日
QObjectの機能
• なぜこんなことができるのか?
- Meta-Object Compiler (MOC) の活躍
- ビルド時にヘッダファイルを読み込んで
各クラスごとに特殊なコードを生成
- Q_OBJECTマクロが必要
13年9月29日日曜日
QObjectの親子構造
• QObjectは親子構造を持つことができる
• 親がdeleteされると子もdelete
- わざわざ手動でdeleteする必要がない
• QObjectのメモリ管理は
この親子関係で行われる
13年9月29日日曜日
QObjectの親子構造
auto parent = new QObject();
auto child1 = new QObject(parent);
auto child2 = new QObject();
child2->setParent(parent);
delete parent;
child1, child2も自動的にdeleteされる
13年9月29日日曜日
シグナルとスロット
• signalを発すると、そのsignalに接続された
すべてのslotが呼び出される
• QObject::connectで接続
• 類似のライブラリ: Boost.Signals2
• スレッド間でも使える
(イベントとしてキューされる)
13年9月29日日曜日
シグナルとスロット
auto lineEdit = new QLineEdit();
auto label = new QLabel();
connect(lineEdit, SIGNAL(textChanged(QString)), label, SLOT(setText(QString)));
connect(lineEdit, &QLineEdit::textChanged, label, &QLabel::setText);
connect(lineEdit, &QLineEdit::textChanged, [](const QString &text){
qDebug() << "text changed to" << text;
});
従来の方法。SIGNALとSLOTは実は文字列を返すマクロ
メンバ関数ポインタで接続 (Qt5から)
任意の関数オブジェクトを接続 (Qt5から)
13年9月29日日曜日
動的なメソッドの呼び出し
• signalや、Q_INVOKABLEを付けたメソッドは
動的に呼び出すことができる
• QMLからでも呼び出せるようになる
13年9月29日日曜日
動的なメソッドの呼び出し
class Object : public QObject
{
Q_OBJECT
public:
explicit Object(QObject *parent = 0);
void method1();
Q_INVOKABLE void method2();
public slots:
void slot1();
};
NG
OK
13年9月29日日曜日
Qtのコンテナライブラリ
13年9月29日日曜日
Qtのコンテナライブラリ
• Qtには、様々な独自のコンテナライブラリが
用意されている
- QString (文字列)
- QList (リスト)
- QHash (辞書)
- など
13年9月29日日曜日
Qtのコンテナライブラリ
• STLとの違い
- メソッドが豊富
- コピーオンライトである
• コンテナをコピーしても、変更が加わるまでは
同じ内部データを共有する
• shared_ptrとかmove semanticsとか
考えなくてもいい
- 総じてSTLより易しい
13年9月29日日曜日
Qtのコンテナライブラリ
• STLとの互換性
- begin()、end()などは用意されている
• range-based forでも使える
• STLのアルゴリズムが使える
- QList、QHashではinitializer_listも使える
13年9月29日日曜日
Qtの描画システム
13年9月29日日曜日
描画システム (Qt Widgets)
• 描画イベントが発生すると
QWidget::paintEventが呼ばれる
• QWidget::paintEvent内で実際に描画
class Widget : public QWidget
{
...
protected:
void paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.setBrush(Qt::red);
painter.drawEllipse(0, 0, 100, 100);
}
};
13年9月29日日曜日
描画システム (Qt Quick)
• scene graph
(描画する図形や変形などを木構造で表したグラフ)
を構築して描画が行われる
• scene graphの更新時、各Itemごとに、
QQuickItem::updatePaintNodeが呼ばれる
• scene graphはメインとは別のスレッドで描画される
• (QQuickPaintedItemを継承して
QWidget風の描画方法も一応可能)
13年9月29日日曜日
描画システム (Qt Quick)
class Item : public QQuickItem
{
Q_OBJECT
public:
explicit Item(QQuickItem *parent = 0) : QQuickItem(parent)
{
this->setFlag(ItemHasContents, true);
}
protected:
QSGNode *updatePaintNode(QSGNode *old, UpdatePaintNodeData *)
{
auto node = static_cast<QSGSimpleRectNode *>(old);
if (!node) {
node = new QSGSimpleRectNode();
node->setColor(Qt::red);
}
node->setRect(this->boundingRect());
return node;
}
};
13年9月29日日曜日
まとめ
• Qtの簡単な使用例と概要について紹介
• C++の応用の参考の1つになれば幸い
13年9月29日日曜日
以上です
13年9月29日日曜日

More Related Content

QtとC++でGUIプログラミング