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

33,223 views

Published on

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

Published in: Technology
0 Comments
51 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
33,223
On SlideShare
0
From Embeds
0
Number of Embeds
1,615
Actions
Shares
0
Downloads
83
Comments
0
Likes
51
Embeds 0
No embeds

No notes for slide

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

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

×