Qt
QtDay 14

QWidgetの階層構造を可視化するデバッグ支援ツール

More than 1 year has passed since last update.


Qt Advent Calendar 2016 14日目の記事です。


QWidgetの階層構造を可視化するデバッグ支援ツールWidgetBrowserを作りました。


はじめに

大規模なアプリケーションの開発では、画面上に見えているウィジェットがどこで実装されているのかわからないということがしばしばあります。表示されている文字列で検索することもできますが、クラス名が分かればもっと効率よく該当ソースコードを探すことができます。

Qtでは、宣言中にQ_OBJECTマクロを含むクラスであればクラス名を知ることができるので、例えばカーソル下にあるウィジェットのクラス名を調べるということが可能です。

そこで、ウィジェットの階層構造を辿ってクラス名を調べられるツールWidgetBrowserを開発しました。

image


インストール

基本的にアプリケーションに組み込んで使います。

WidgetBrowsergit cloneしてsrc/wbディレクトリをアプリケーション内にコピーし、ビルド対象に含めます。

起動するにはwb::WbDialog::exec()を呼ぶだけでOKです。WbDialogクラスが自動的にQApplicationオブジェクトから情報を集めて表示します。


example

#include <wb/dialog.h>


wb::WbDialog dialog;
dialog.exec();

また、イベントフィルター WbOpenListenerQApplicationにインストールすることでショートカットキーで起動できます。


example

#include <QApplication>

#include <QKeySequence>
#include <wb/listener.h>

WbOpenListener listener;
listener.setKeySequenceToOpen(QKeySequence("Meta+F2"));
qApp->installEventfilter(&listener);

アプリケーションのソースコードを変更できない場合、WidgetBrowserを組み込んだQtのDLLを

ビルドして差し替えるという方法が使えます(対象がLGPL版Qtを使っていれば)。


使い方

起動すると、その時点のウィジェットの階層構造がキャプチャーされ、左側のツリーに表示されます。

トップレベルの要素は以下の5つです。


  • Active Modal Widget

    アクティブなモーダルウィジェット(QApplication::activeModalWidget()の値)

  • Active Popup Widget
    アクティブなポップアップウィジェット(QApplication::activePopupWidget()の値)

  • Focus Widget
    フォーカスを持つウィジェット(QApplication::focusWidget()の値)

  • Widget at Cursor
    カーソル下のウィジェット(QApplication::widgetAt(QCursor::pos())の値)

  • Top Level Widgets
    トップレベルウィジェット(QApplication::topLevelWidgets()の値)

これらは起動した瞬間の値であり、更新されません。

image

ツリー上でウィジェットを選択すると右側にウィジェットのスクリーンショットが表示されます。

ツリー上ではコンテキストメニューからウィジェットのクラス名をコピーできます。

スクリーンショット上では画像を保存できます。

ダイアログを閉じるとアプリケーションに処理を返します。


作り方

ツリービューは、ウィジェットの階層構造を表すアイテムモデルを作成して使っています。

スクリーンショットは、QWidget::renderを呼ぶことで画像を取得しているため副作用があります。また、一度もshow()されていないウィジェットは正しくない画像になることがあります。

QWidget* widget;

QLabel* label;

const QRect rectangle(QPoint(), widget->size());
QPixmap pixmap(rectangle.size());
widget->render(&pixmap, QPoint(), QRegion(rectangle));

label->setPixmap(pixmap);

クラス名をコピーする機能は以下のように実装しています。

cpp

void copyClassName(QWidget* widget) {

QApplication::clipboard()->setText(widget->metaObject()->className());

}


欲しい機能

週末にハッカソンで作ったツールなので現時点での機能は少ないです。

QtDesignerのプロパティエディタ的な機能を付けたい。

image


まとめ

大きなプロジェクトでは検索のキーワードとしてクラス名を知りたいことがあるのでツールを作りました(おまけでスクリーンショットも付けてみました)。