1. 初めに
クリスマスなので?リア充QtのCustom Widget Pluginをばっさり斬ってみたいと思います。
1.1. 開発環境
OSはWindowsとLinuxの2種類を扱います。(macOSは手元に環境がない為、対象外とさせていただきます。ごめんなさい。)
Qt SDK及びQt Creatorは、インストーラ版(MinGW版(Windows)/gcc版(Linux))と自分でコードをビルドした版の2種類。
2×2の組み合わせで4種類を対象とします。
Qt5.9.1とQt5.9.2が元ネタのQt Versionです。
2. Custom Widget Pluginって何?
Custom Widget PluginはQt Designer上において、QWidgetのサブクラスの操作をカスタマイズする機能です。
QWidgetのサブクラスをQt Designerで操作する機能は、Qtに2つ用意されています。
(1)格上げ(Proceed)する機能
(2)Custom Widget Plugin
の2つです。
この内、(1)格上げ(Proceed)する機能については、いろいろなサイトや書籍で扱っていますが、なぜか本稿の対象である(2)Custom Widget Pluginについては、Webサイトで検索してもほとんど情報が出てきません。そこで、本稿で取り上げてみることにしました。
Custom Widget Pluginは、QWidgetのサブクラスに、Extension機能を加えた動的ライブラリです。
このライブラリをQt Creator/Qt Designerが認識する場所(後述)に格納することによって、Qt Creator/Qt Designerが認識します。
Pluginが認識すると、Qt Designerのウィジェットボックスに「Input Widets」や「Display Widgets」と同レベルのグループ(グループ名は自分で設定可能)が表示され、この中に、自分で作成したオリジナルウィジェットが表示されます。もちろん中央部にドラッグできます。
※スクリーンショットが貼れればよかったのですが、環境整えるのが間に合いませんでしたorz
2.1. 特徴を比較する
まずこの2つの機能について、比較してみましょう。
格上げ | Custom Widget Plugin | |
---|---|---|
利用のしやすさ | 簡単 | 難しい。また、Qt Designerで利用できるまでに時間が掛かる |
Qt Designer上の見た目 | QWidgetそのまま変わらない | カスタマイズした見た目が反映される |
誰向け? | QWidgetのサブクラスのコードを持っている人向け。 自分でサブクラスを作って、自分で画面を作る |
QWidgetのサブクラスのコードを持っていない第3者 |
QWidgetのサブクラスを使用する時に必要なファイル | .hファイル及び.cppファイル | .hファイル及び動的ライブラリファイル(.so or .dll) |
2.2. どんな機能があるのか
2.2.1. Extension機能
Creating Custom Widget Extensionsで説明されています。
下記の4つの機能があります。
QDesignerTaskMenuExtension
右クリックで表示されるTaskMenuをカスタマイズするExtensionです。
例)Task Menu Extension ExampleQDesignerContainerExtension
カスタムの複数ページのコンテナを実装する場合に使用するExtensionです。Qt Designerで複数ページのコンテナプラグインのページを追加および削除できる拡張機能が用意されています。
例)Container Extension ExampleQDesignerMemberSheetExtension
オリジナルのシグナル・スロットを追加するExtensionです。
右クリック→「スロットへ移動...」選択時に表示されるシグナル一覧に、自作Qwidgetサブクラスのシグナルが追加で表示されます。QDesignerPropertySheetExtension, QDesignerDynamicPropertySheetExtension
プロパティ一覧の項目をカスタマイズするExtensionです。
2.3. 開発の流れ
Custom Widget Pluginを作成し、利用する流れは下記になります。
- QWidgetのサブクラスを作成(QOpenGLWidgetのようなOpenGL機能付きでも可)
- 「Custom Widget Plugin」をビルド(ライブラリが作成され、任意の場所にライブラリが移動) ※
- 実行すると、対象アプリケーションが指定するよう指示される為、qtcreator.exe(Linuxならqtcreator.shも可)を指定して、別のQt Creatorを起動
- 新しいuiファイル追記ウィジェットプロジェクトを作成
- uiファイルをダブルクリックし、Qt Desginerを起動
- 自作Qwidgetサブクラスを中央部にドラッグして操作
- プロジェクトファイルに「LIBS += -L〇〇 -l〇〇」を追加
- ビルドして実行
※Custom Widget Pluginのライブラリを格納する場所は、qquickwidget.dll(qquickwidget.so)が格納されている場所と同じ場所です。
2つ以上ある場合は、Toolディレクトリを含む方が正解です。
3. Custom Widget Pluginの問題
ここまでの話だと、Custom Widget Pluginっていう機能はおもしろそうじゃないか、オラわくわくしてきたぞ、と思われる方もいらっしゃるかもしれません。
Custom Widget Pluginの存在に気付いた時は当初、私もその一人でした。
しかし、実際に使用しようとすると・・・厳しい現実が待っていました。
3.1. サンプルが動かない
Qt SDKに用意されているCustom Widget Pluginの一番シンプルなサンプルであるWorld Time Clock Plugin ExampleをインストーラでインストールしたQt SDKで動作させようとすると動作しません。(他のサンプルも同様)
それぞれのOSについて見ていきます。
3.1.1. Windowsでの動作
結論から言いますと、Windows(MinGW)インストーラ版では、Custom Widget Pluginは動作しません。
これは、「Qt CreatorをビルドするコンパイラとCustom Widget Pluginをビルドするコンパイラが異なると動作しない」という制約に引っかかってしまう為です。
手元のQt5.9.2だと、Qt CreatorはVisualStudio2015でビルドされていますが、VisualStudioはインストールしていない環境なので、Qt SDKはMigGW版であり、Custom Widget Pluginをビルドする環境もMinGW版です。
解決策としては、
- Qt Creatorのコードをダウンロードして自分でMinGWでビルドする
- VisualStudioコンパイラ版のQt SDKを使用する。
のどちらかにするしか対応方法はないようです。
最初から全てコードを落としてきてビルドした環境であれば問題なく動作します。
3.1.2. Linuxでの動作
Linuxのインストーラ版は、サンプルコードそのままでは動作しませんが、プロジェクトファイルを書き換えれば動作します。
何が問題かというと、Qt独自のビルド環境変数「QT_INSTALL_PLUGINS」の示す先がQt Designerが認識する場所を指していないことが問題です。
自分でQtをビルドした場合は、「QT_INSTALL_PLUGINS」に正しい値がセットされるので問題は発生しません。
※インストーラ版と自分ビルド版では、ディレクトリ構成が異なります。自分ビルド版ではToolディレクトリは存在しません。従って、インストーラ化した時の何かにバグがあるのではないかと予想しています。
また、もう1つ別の観点で問題があります。
プロジェクトファイルの書き方についてです。
ここにあるように
target.path = $$[QT_INSTALL_PLUGINS]/designer
INSTALLS += target
のように記述すればよいとのことですが、これはQMakeをシェル上から実行した場合のみ有効となる記述方法で、Qt Creator上の操作からビルドしても上記の設定は認識してくれません。
そこで、代案として私が考えたのが、「DESTDIR」に直接Custom Widget Pluginライブラリを格納するパスを記述する方法です。
QtのVersionが上がった時に、パスがいちいち変わる可能性があるので、微妙と言えば微妙ですが、Qt Creator上で操作できるという観点において、公式サイトの記述よりましだと思っています。
3.2. プロパティExtensionが微妙すぎる
2.2.1.の説明で、あえてぼかして説明しましたが、QDesignerPropertySheetExtensionはQDesignerMemberSheetExtensionと異なり、プロパティを追加することはできません。
例えば、QPushButtonをQt Designer上に張り付けると、「QObject」、「QWidget」、「QAbstractButton」、「QPushButton」の各クラスのプロパティが全て表示されます。
この動作のイメージで、QWidgetのサブクラスのプロパティExtensionを作成したら、「QObject」、「QWidget」、「QWidgetのサブクラス」の3つのクラスのプロパティが表示されるイメージになると思いますが、残念ながらQDesignerPropertySheetExtensionのサブクラスに、自分で定義したプロパティしか表示できません。「QObject」、「QWidget」のプロパティを表示したかったら、「自分で定義する」しか方法がありません。
※これは、QDesignerPropertySheetExtensionのメンバ関数が全て純粋仮想関数なのが原因かと思います。(要は設計ミス)デフォルト実装はQDesignerPropertySheetExtensionの中でしておく方がよかったのではないと思います。Qt SDK側は恐らくd-pointer化したPrivateクラスでデフォルトの実装を行っているはずで、なぜか公開した側のクラスはかなり締め付けられています。
ちなみに、QObjectの「objectName」プロパティとQWidgetの「geometry」プロパティはQDesignerPropertySheetExtensionのサブクラスで再定義しておく必要があるようです。存在しないとQt Designerで変な挙動を起こします。
他にも必須プロパティがあるのか、Qtの中の人に問い合わせたこともありますが、知らないと回答されました。。。
3.3. Qt5.9.1までは・・・
Qt5.9.1までは、QDesignerMemberSheetExtensionとQDesignerPropertySheetExtensionを1つのPluginに組み込むとQDesignerMemberSheetExtensionが正しく動作しません。
http://bugreports.qt.io/browse/QTBUG-62936/
が原因らしいです。
Qt5.9.2では直っていますが、正直なぜ、こんなド正常の動作にバグがあるのか理解できません。
(他のQt SDKのサンプルはこれでもかという位リッチなのに)Custom Widget Pluginのサンプルは充実していないことから、窓際的な扱いをされているのでしょうか?
4. まとめ
いろいろと大変なCustom Widget Pluginですが、やっぱり、Qt Designer上でペタペタ貼り付けて画面を作るのは楽しいものです。
様々な障害を乗り越えて、「Custom Widget Pluginを使いたい!」と言ってくださる方がいらっしゃいましたら、ぜひコメントください。
Custom Widget Pluginの作り方の記事を書きたくなるかもしれません。
5. 最後に
今年のQt Advent Calendarは、当初なかなか枠が埋まらなくて、「読専」だった私もデビューしてしまいました。
調子に乗って3つも書いてしまいました。
来年も自分に続いてくれるNew Challengerをお待ちしています。
それでは、みなさま良いお年を!