地味に便利な Qt の ◯◯

地味に便利な Qt の ◯◯

Qt Japan Summit 2015 の Technical & Features トラックで行ったセッションの資料です。

サンプルコードも公開しています
$ git clone git://git.qt-users.jp/codereview/qtjs15/qtjapansummit2015-tasuku

必要に応じて以下の補足記事も参考にしてください。
【Qt】QMessageLogContextをreleaseモードでも利用する
http://taurano.hatenablog.com/entry/2015/06/26/235853

B696e9347149e244ae6f6fd85e035cca?s=128

Tasuku Suzuki

May 28, 2015
Tweet

Transcript

  1. 地味に便利な Qt の ◯◯ Tasuku Suzuki / Japan Qt Users’

    Group 2015/05/26 Qt Japan Summit 2015 @ Akihabara
  2. 自己紹介 • 鈴木 佑(Tasuku Suzuki) ◦ 1979年10月4日 釧路生まれ、札幌育ち ◦ きのこ類と貝類が大好き、焼き魚の脂の匂いが苦手

    ◦ 2002年に Qt に一目惚れ ◦ 2006年に当時の Trolltech 社に入社 ◦ 2012年に Nokia Japan をクビになる ◦ 2013年からは日本Qtユーザー会の一員として活動 ▪ 「Qt 勉強会」を毎月開催しています ◦ 2014年に「Qt Champion」に選ばれました!
  3. 地味に便利な 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
  4. Q_PROPERTY の MEMBER 機能

  5. 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; };
  6. プロパティとして宣言する 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; };
  7. プロパティ化のメリット • リフレクション ◦ QObject の API で派生クラスの拡張機能が 利用可能になる •

    スクリプト環境での API になる ◦ JavaScript から QObject のインスタンスを操作 • C++ だけで書いている時はあまり必要ない ◦ ただし、ちゃんとプロパティ化しておいた方が API がわ かりやすくなるのでオススメです。
  8. プロパティを作るのはめんどくさい • いちいちセッターとゲッターを定義/実装するの が面倒 • C++ の API は必要ないのに… •

    プロパティをたくさん作る必要がある… つまり、Qt Quick 向けの API を作る場合
  9. メンバー変数だけでプロパティを作る 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; };
  10. コマンドライン 引数のパーサー

  11. QCommandLineParser • コマンドラインオプションを扱うクラス • -f とか --force とかに対応できる • -z

    0.1 --zoom 0.1, -z=0.1, -zoom=0.1 のよう な値も取れる • -h/--help に簡単に対応できる • 未知のオプションが渡された場合は終了
  12. 最近の qDebug()

  13. qDebug() のおさらい • (基本的には stderr に)デバッグログを出す • 色々なレベルがある ◦ qDebug(),

    qWarning(), qCritical(), qFatal() • Qt 5 では実装が変わって使いやすくなった #define qDebug \ QMessageLogger( \ QT_MESSAGELOG_FILE, \ QT_MESSAGELOG_LINE, \ QT_MESSAGELOG_FUNC).debug
  14. qDebug() のおさらい • #include <QtCore/QDebug> • printf 形式 ◦ qDebug("Hello

    World"); ◦ qDebug("Hello %s %d World", "Qt", 5); • streaming 形式 ← オススメ ◦ qDebug() << "Hello" << "Qt" << 5 << "World"; ◦ 間にスペースが、最後に改行が自動で入る ◦ 文字列や数値だけではなくクラスも出力できる ▪ qDebug() << QDateTime::currentDateTime();
  15. 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
  16. 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
  17. qDebug() の便利な使い方(3) // QTextStream のマニピュレーターを使用 qDebug().nospace() << "RGB: #" <<

    hex << uppercasedigits << 0x7e << 0xc2 << 0x42; -> RGB: #7EC242
  18. 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});
  19. 余談:標準”出力”にメッセージを出す #include <QtCore/QTextStream> static QTextStream &qStdOut() { static QTextStream ret(stdout);

    return ret; } qStdOut() << "Hello Qt World!" << endl;
  20. ログ出力の カテゴリ分け

  21. 条件によるのログ出力の切り替え(1) // .cpp #ifndef MY_NO_CATEGORY_A_DEBUG qDebug() << "This is for

    the category A"; #endif # .pro DEFINES += MY_NO_CATEGORY_A_DEBUG
  22. 条件によるのログ出力の切り替え(2) // .cpp if (qEnvironmentVariableIsEmpty( "MY_NO_CATEGORY_A_DEBUG")) { qDebug() << "This

    is for the category A"; }
  23. デバッグ出力のカテゴリ対応(Qt 5.2) #include <QtCore/QLoggingCategory> 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");
  24. グローバルスコープにカテゴリを定義 // mydebug.h #ifndef MYDEBUG_H #define MYDEBUG_H #include <QtCore/QLoggingCategory> 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")
  25. 出力カテゴリのフィルタルール *=true category.*=false category.a=true category.b.critical=true • 上から順番に評価される • <category>[.<type>]=true|false •

    * はカテゴリの最初か最後のみ有効
  26. 出力カテゴリのフィルタルールの設定 • コンテンツを直接設定 ◦ QLoggingCategory::setFilterRules(...) ◦ QT_LOGGING_RULES 環境変数 • ファイルに記述

    ◦ QT_LOGGING_CONF の [Rules] セクション ◦ QtProject/qtlogging.ini の [Rules] セクション • 詳細は ドキュメント を参照
  27. ログの表示 形式の設定

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

  29. #define myDebug() \ qDebug() << Q_FUNC_INFO << __LINE__ myDebug() <<

    "Hello World"; 関数名と行を出力するマクロを作った
  30. 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=”...”}
  31. ログの出力先 カスタマイズ

  32. デフォルトのログの出力先 • Windows Debugger Log • QNX slogger2 • journald

    • Android message handler • stderr
  33. 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
  34. qInfo qCInfo Qt 5.5 で追加されました 以上

  35. まとめ

  36. Q_PROPERTY の MEMBER 機能 • メンバ変数を直接指定できる ◦ セッター/ゲッターがいらなくなる ◦ シグナルは必要

    • 手抜きでいい場合は便利 • C++ の API も必要な場合はちゃんとする • ドキュメントか朝木さんの記事を参照 ◦ doc.qt.io/qt-5/properties.html ◦ qt-labs.jp/2013/04/qt-property.html
  37. QCommandLineParser • コマンドライン引数用のクラスができた • 色々なパターンに対応可能 • Qt の中のツールも QCLP 化されてきた

    • ドキュメントか澤津さんの記事を参照 ◦ doc.qt.io/qt-5/qcommandlineparser.html ◦ Qt5.2で登場したQCommandLineParserの使い方につ いて
  38. 最近の qDebug() • Qt 5.2 でカテゴリ分けに対応 ◦ QLoggingCategory でカテゴリを定義 ◦

    qDebug() を qCDebug(category) に変える ◦ ルールは QLoggingCategory::setFilterRules(...) で • メッセージのフォーマットも変えられる ◦ qSetMessagePattern(...)/QT_MESSAGE_PATTERN ◦ 様々な情報が出せる %{function} %{line} • 独自ハンドラも作れる • qInfo() / qCInfo() が Qt 5.5 から追加
  39. Q?