Slide 1

Slide 1 text

地味に便利な Qt の ◯◯ Tasuku Suzuki / Japan Qt Users’ Group 2015/05/26 Qt Japan Summit 2015 @ Akihabara

Slide 2

Slide 2 text

自己紹介 ● 鈴木 佑(Tasuku Suzuki) ○ 1979年10月4日 釧路生まれ、札幌育ち ○ きのこ類と貝類が大好き、焼き魚の脂の匂いが苦手 ○ 2002年に Qt に一目惚れ ○ 2006年に当時の Trolltech 社に入社 ○ 2012年に Nokia Japan をクビになる ○ 2013年からは日本Qtユーザー会の一員として活動 ■ 「Qt 勉強会」を毎月開催しています ○ 2014年に「Qt Champion」に選ばれました!

Slide 3

Slide 3 text

地味に便利な Qt の ◯◯ の内容 ● Qt 5.1 ○ Q_PROPERTY の MEMBER 機能 ● Qt 5.2 ○ コマンドライン引数のパーサー ● Qt 5 の qDebug() のテクニックと進化 ○ Qt 5.2: QLoggingCategory ○ Qt 5.0: qSetMessagePattern ○ Qt 5.5: qInfo

Slide 4

Slide 4 text

Q_PROPERTY の MEMBER 機能

Slide 5

Slide 5 text

QObject の派生クラスの作成 class Object : public QObject { Q_OBJECT public: Object(QObject *parent = Q_NULLPTR) : QObject(parent), m_value(0) {} int value() const { return m_value; } public slots: void setValue(int value) { if (m_value == value) return; m_value = value; emit valueChanged(value); } signals: void valueChanged(int value); private: int m_value; };

Slide 6

Slide 6 text

プロパティとして宣言する class Object : public QObject { Q_OBJECT Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged) public: Object(QObject *parent = Q_NULLPTR) : QObject(parent), m_value(0) {} int value() const { return m_value; } public slots: void setValue(int value) { if (m_value == value) return; m_value = value; emit valueChanged(value); } signals: void valueChanged(int value); private: int m_value; };

Slide 7

Slide 7 text

プロパティ化のメリット ● リフレクション ○ QObject の API で派生クラスの拡張機能が 利用可能になる ● スクリプト環境での API になる ○ JavaScript から QObject のインスタンスを操作 ● C++ だけで書いている時はあまり必要ない ○ ただし、ちゃんとプロパティ化しておいた方が API がわ かりやすくなるのでオススメです。

Slide 8

Slide 8 text

プロパティを作るのはめんどくさい ● いちいちセッターとゲッターを定義/実装するの が面倒 ● C++ の API は必要ないのに… ● プロパティをたくさん作る必要がある… つまり、Qt Quick 向けの API を作る場合

Slide 9

Slide 9 text

メンバー変数だけでプロパティを作る class Object : public QObject { Q_OBJECT Q_PROPERTY(int value MEMBER m_value NOTIFY valueChanged) public: Object(QObject *parent = Q_NULLPTR) : QObject(parent), m_value(0) {} signals: void valueChanged(int value); private: int m_value; };

Slide 10

Slide 10 text

コマンドライン 引数のパーサー

Slide 11

Slide 11 text

QCommandLineParser ● コマンドラインオプションを扱うクラス ● -f とか --force とかに対応できる ● -z 0.1 --zoom 0.1, -z=0.1, -zoom=0.1 のよう な値も取れる ● -h/--help に簡単に対応できる ● 未知のオプションが渡された場合は終了

Slide 12

Slide 12 text

最近の qDebug()

Slide 13

Slide 13 text

qDebug() のおさらい ● (基本的には stderr に)デバッグログを出す ● 色々なレベルがある ○ qDebug(), qWarning(), qCritical(), qFatal() ● Qt 5 では実装が変わって使いやすくなった #define qDebug \ QMessageLogger( \ QT_MESSAGELOG_FILE, \ QT_MESSAGELOG_LINE, \ QT_MESSAGELOG_FUNC).debug

Slide 14

Slide 14 text

qDebug() のおさらい ● #include ● printf 形式 ○ qDebug("Hello World"); ○ qDebug("Hello %s %d World", "Qt", 5); ● streaming 形式 ← オススメ ○ qDebug() << "Hello" << "Qt" << 5 << "World"; ○ 間にスペースが、最後に改行が自動で入る ○ 文字列や数値だけではなくクラスも出力できる ■ qDebug() << QDateTime::currentDateTime();

Slide 15

Slide 15 text

qDebug() の便利な使い方(1) QString world = QStringLiteral("World"); qDebug() << "Hello" << world; -> Hello “World” qDebug().nospace() << "Hello" << world; -> Hello“World” // Qt 5.4 qDebug().noquote() << "Hello" << world; -> Hello World

Slide 16

Slide 16 text

qDebug() の便利な使い方(2) // 変数に代入して凝った使い方をする QStringList list = {"Hello", "World"}; { QDebug dbg = qDebug(); dbg.nospace().noquote(); dbg << "Contents:"; for (int i = 0; i < list.size(); ++i) dbg << ' ' << i << '/' << list.at(i); } -> Contents: 0/Hello 1/World

Slide 17

Slide 17 text

qDebug() の便利な使い方(3) // QTextStream のマニピュレーターを使用 qDebug().nospace() << "RGB: #" << hex << uppercasedigits << 0x7e << 0xc2 << 0x42; -> RGB: #7EC242

Slide 18

Slide 18 text

qDebug() の便利な使い方(4) // カスタムクラスのデバッグ出力対応 struct Color { uchar r, g, b; }; QDebug operator<<(QDebug dbg, const Color &color) { QDebugStateSaver saver(dbg); // 5.1 dbg.resetFormat(); // 5.4 dbg.nospace() << "Color(#"; dbg << hex << uppercasedigits; dbg << qSetFieldWidth(2) << qSetPadChar('0'); dbg << color.r << color.g << color.b; dbg.resetFormat(); dbg << ")"; return dbg; } qDebug() << Color({126, 194, 66});

Slide 19

Slide 19 text

余談:標準”出力”にメッセージを出す #include static QTextStream &qStdOut() { static QTextStream ret(stdout); return ret; } qStdOut() << "Hello Qt World!" << endl;

Slide 20

Slide 20 text

ログ出力の カテゴリ分け

Slide 21

Slide 21 text

条件によるのログ出力の切り替え(1) // .cpp #ifndef MY_NO_CATEGORY_A_DEBUG qDebug() << "This is for the category A"; #endif # .pro DEFINES += MY_NO_CATEGORY_A_DEBUG

Slide 22

Slide 22 text

条件によるのログ出力の切り替え(2) // .cpp if (qEnvironmentVariableIsEmpty( "MY_NO_CATEGORY_A_DEBUG")) { qDebug() << "This is for the category A"; }

Slide 23

Slide 23 text

デバッグ出力のカテゴリ対応(Qt 5.2) #include QLoggingCategory categoryA("category.a"); qCDebug(categoryA) << "Hello category A"; qCWarning(categoryA) << "Hello!!"; QLoggingCategory categoryB("category.b"); qCDebug(categoryB) << "Hello category B"; qCCritical(categoryB) << "Hello?"; QLoggingCategory categoryC("category.c"); // Qt 5.3 qCDebug(categoryC, "%s", "Hello category C");

Slide 24

Slide 24 text

グローバルスコープにカテゴリを定義 // mydebug.h #ifndef MYDEBUG_H #define MYDEBUG_H #include Q_DECLARE_LOGGING_CATEGORY(categoryD) Q_DECLARE_LOGGING_CATEGORY(categoryE) Q_DECLARE_LOGGING_CATEGORY(categoryF) #endif // MYDEBUG_H // mydebug.cpp #include "mydebug.h" Q_LOGGING_CATEGORY(categoryD, "category.d") Q_LOGGING_CATEGORY(categoryE, "category.e") Q_LOGGING_CATEGORY(categoryF, "category.f")

Slide 25

Slide 25 text

出力カテゴリのフィルタルール *=true category.*=false category.a=true category.b.critical=true ● 上から順番に評価される ● [.]=true|false ● * はカテゴリの最初か最後のみ有効

Slide 26

Slide 26 text

出力カテゴリのフィルタルールの設定 ● コンテンツを直接設定 ○ QLoggingCategory::setFilterRules(...) ○ QT_LOGGING_RULES 環境変数 ● ファイルに記述 ○ QT_LOGGING_CONF の [Rules] セクション ○ QtProject/qtlogging.ini の [Rules] セクション ● 詳細は ドキュメント を参照

Slide 27

Slide 27 text

ログの表示 形式の設定

Slide 28

Slide 28 text

昔の qDebug() は不便な面もあった ● ソースコードのどこで出力しているの?

Slide 29

Slide 29 text

#define myDebug() \ qDebug() << Q_FUNC_INFO << __LINE__ myDebug() << "Hello World"; 関数名と行を出力するマクロを作った

Slide 30

Slide 30 text

qSetMessagePattern ● Qt 5.0 で QMessageLogger と共に導入 ● デバッグメッセージの出力パターンを設定 ○ QT_MESSAGE_PATTERN 環境変数 ○ qSetMessagePattern( "%{function}#%{line}: %{message}"); ○ 様々なプレースホルダーがある ■ %{appname} %{category} %{file} ■ %{function} %{line} %{message} %{pid} ■ %{threadid} %{type} %{time process} ■ %{time [format]} ■ %{backtrace [depth=N], separator=”...”}

Slide 31

Slide 31 text

ログの出力先 カスタマイズ

Slide 32

Slide 32 text

デフォルトのログの出力先 ● Windows Debugger Log ● QNX slogger2 ● journald ● Android message handler ● stderr

Slide 33

Slide 33 text

qInstallMessageHandler で変更可能 logViewer = new QTextBrowser; logViewer->show(); qInstallMessageHandler( [](QtMsgType type , const QMessageLogContext &context , const QString &message) { logViewer->append(qFormatLogMessage(type, context, message)); }); ● スレッドセーフにしてね ● 再帰に対応してね ○ thread_local(c++11) or QThreadStorage

Slide 34

Slide 34 text

qInfo qCInfo Qt 5.5 で追加されました 以上

Slide 35

Slide 35 text

まとめ

Slide 36

Slide 36 text

Q_PROPERTY の MEMBER 機能 ● メンバ変数を直接指定できる ○ セッター/ゲッターがいらなくなる ○ シグナルは必要 ● 手抜きでいい場合は便利 ● C++ の API も必要な場合はちゃんとする ● ドキュメントか朝木さんの記事を参照 ○ doc.qt.io/qt-5/properties.html ○ qt-labs.jp/2013/04/qt-property.html

Slide 37

Slide 37 text

QCommandLineParser ● コマンドライン引数用のクラスができた ● 色々なパターンに対応可能 ● Qt の中のツールも QCLP 化されてきた ● ドキュメントか澤津さんの記事を参照 ○ doc.qt.io/qt-5/qcommandlineparser.html ○ Qt5.2で登場したQCommandLineParserの使い方につ いて

Slide 38

Slide 38 text

最近の qDebug() ● Qt 5.2 でカテゴリ分けに対応 ○ QLoggingCategory でカテゴリを定義 ○ qDebug() を qCDebug(category) に変える ○ ルールは QLoggingCategory::setFilterRules(...) で ● メッセージのフォーマットも変えられる ○ qSetMessagePattern(...)/QT_MESSAGE_PATTERN ○ 様々な情報が出せる %{function} %{line} ● 独自ハンドラも作れる ● qInfo() / qCInfo() が Qt 5.5 から追加

Slide 39

Slide 39 text

Q?