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

  • 13,515 views
Uploaded on

C++勉強会 @ tkb #2 での発表資料

C++勉強会 @ tkb #2 での発表資料

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
13,515
On Slideshare
0
From Embeds
0
Number of Embeds
8

Actions

Shares
Downloads
40
Comments
0
Likes
20

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. QtとC++で GUIプログラミング @seanchas_t 2013年9月28日 13年9月29日日曜日
  • 2. 自己紹介 • twitter.com/seanchas_t • [PR] Qtでペイントソフトを制作中 - github.com/iofg2100/PaintField 13年9月29日日曜日
  • 3. イントロダクション 13年9月29日日曜日
  • 4. はじめに • C++でGUIアプリを作ろう • 今回紹介するのは Qtを使ったGUIアプリ開発 - コード例、Qtのシステムの概略の紹介 13年9月29日日曜日
  • 5. 目次 • イントロダクション • 使ってみる (Qt Widgets編) • 使ってみる (Qt Quick編) • QObjectについて • Qtのコンテナライブラリ • Qtの描画システム 13年9月29日日曜日
  • 6. Qtについて • クロスプラットフォームのアプリケーション、UIフ レームワーク - WindowsでもMacでもLinuxでも 同じコードが動く - Android、iOSにもQt 5.1から実験的に対応 - 今回はデスクトップに限った話 13年9月29日日曜日
  • 7. Getting Started • qt-project.org/downloads からダウンロード - もしくはお好きなパッケージマネージャで • 統合開発環境はQt Creator • C++11を使う場合は、 プロジェクトファイル (*.pro) に “CONFIG += c++11” を追加 13年9月29日日曜日
  • 8. 使ってみる (Qt Widgets編) 13年9月29日日曜日
  • 9. Qt Widgetsとは • Qtに昔からある、GUIの記述方法 • C++で各UI部品を作って配置 13年9月29日日曜日
  • 10. • ボタンを押したら数字が1増える シンプルなカウンタを作ろう こんな感じ ボタン ラベル 13年9月29日日曜日
  • 11. シンプルなカウンタを作ろう カウンタ イメージ図 ボタン ラベル クリック カウントアップ 値が変化 文字列を更新 ↓UI 13年9月29日日曜日
  • 12. シンプルなカウンタを作ろう • Qtでは「シグナル」と「スロット」 を使ってこの図と同じようにコードを組む 13年9月29日日曜日
  • 13. カウンタ 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日日曜日
  • 14. カウンタ 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日日曜日
  • 15. カウンタ 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日日曜日
  • 16. トップレベルのウィジェット 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日日曜日
  • 17. トップレベルのウィジェット 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日日曜日
  • 18. トップレベルのウィジェット 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日日曜日
  • 19. main関数 int main(int argc, char *argv[]) { QApplication app(argc, argv); Counter counter; Widget widget(&counter); widget.show(); return app.exec(); } 結局こんな感じになる (先ほどと同じ) 13年9月29日日曜日
  • 20. 使ってみる (Qt Quick編) 13年9月29日日曜日
  • 21. Qt Quickとは • QMLという言語を使ってUIを記述 • QMLは動的にロードされる • CSSっぽくオブジェクトを配置、 プロパティを設定 • JavaScriptでロジックも記述可能 - JavaScriptでは無理なことも • ローカルファイルへのアクセスとか 13年9月29日日曜日
  • 22. Qt Quickとは • C++との連携も可能 - QMLからC++を呼び出す - C++からQMLを呼び出す - Qt QuickのUIアイテムをC++で実装する • C++だとQQuickItem 13年9月29日日曜日
  • 23. カウンタ 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日日曜日
  • 24. カウンタ 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日日曜日
  • 25. 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日日曜日
  • 26. 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日日曜日
  • 27. 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日日曜日
  • 28. 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日日曜日
  • 29. プロパティバインディング • プロパティに他のオブジェクトの プロパティを使った式を使うと、 自動的にその変化に追従させられる • Reactive Programming - Excelっぽい • QMLの大きな特徴 13年9月29日日曜日
  • 30. 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日日曜日
  • 31. 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日日曜日
  • 32. 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日日曜日
  • 33. 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日日曜日
  • 34. 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日日曜日
  • 35. QObject 13年9月29日日曜日
  • 36. QObjectとは • 普通のC++にはないQtの様々な機能 - signal・slot - QMLからの メソッドの動的な呼び出し QObjectの機能 13年9月29日日曜日
  • 37. QObjectとは • QWidget系のオブジェクトも QMLのオブジェクトも 全部QObjectから派生している • 先ほど作ったCounterクラスもQObjectから派生 13年9月29日日曜日
  • 38. 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日日曜日
  • 39. QObjectの機能 • 親子構造 • シグナルとスロット • プロパティ • リフレクション - 動的なメソッドの呼び出し - 動的なプロパティの書き換え • など 13年9月29日日曜日
  • 40. QObjectの機能 • なぜこんなことができるのか? - Meta-Object Compiler (MOC) の活躍 - ビルド時にヘッダファイルを読み込んで 各クラスごとに特殊なコードを生成 - Q_OBJECTマクロが必要 13年9月29日日曜日
  • 41. QObjectの親子構造 • QObjectは親子構造を持つことができる • 親がdeleteされると子もdelete - わざわざ手動でdeleteする必要がない • QObjectのメモリ管理は この親子関係で行われる 13年9月29日日曜日
  • 42. 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日日曜日
  • 43. シグナルとスロット • signalを発すると、そのsignalに接続された すべてのslotが呼び出される • QObject::connectで接続 • 類似のライブラリ: Boost.Signals2 • スレッド間でも使える (イベントとしてキューされる) 13年9月29日日曜日
  • 44. シグナルとスロット 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日日曜日
  • 45. 動的なメソッドの呼び出し • signalや、Q_INVOKABLEを付けたメソッドは 動的に呼び出すことができる • QMLからでも呼び出せるようになる 13年9月29日日曜日
  • 46. 動的なメソッドの呼び出し 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日日曜日
  • 47. Qtのコンテナライブラリ 13年9月29日日曜日
  • 48. Qtのコンテナライブラリ • Qtには、様々な独自のコンテナライブラリが 用意されている - QString (文字列) - QList (リスト) - QHash (辞書) - など 13年9月29日日曜日
  • 49. Qtのコンテナライブラリ • STLとの違い - メソッドが豊富 - コピーオンライトである • コンテナをコピーしても、変更が加わるまでは 同じ内部データを共有する • shared_ptrとかmove semanticsとか 考えなくてもいい - 総じてSTLより易しい 13年9月29日日曜日
  • 50. Qtのコンテナライブラリ • STLとの互換性 - begin()、end()などは用意されている • range-based forでも使える • STLのアルゴリズムが使える - QList、QHashではinitializer_listも使える 13年9月29日日曜日
  • 51. Qtの描画システム 13年9月29日日曜日
  • 52. 描画システム (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日日曜日
  • 53. 描画システム (Qt Quick) • scene graph (描画する図形や変形などを木構造で表したグラフ) を構築して描画が行われる • scene graphの更新時、各Itemごとに、 QQuickItem::updatePaintNodeが呼ばれる • scene graphはメインとは別のスレッドで描画される • (QQuickPaintedItemを継承して QWidget風の描画方法も一応可能) 13年9月29日日曜日
  • 54. 描画システム (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日日曜日
  • 55. まとめ • Qtの簡単な使用例と概要について紹介 • C++の応用の参考の1つになれば幸い 13年9月29日日曜日
  • 56. 以上です 13年9月29日日曜日