タスクメニュー拡張

Qt Widgets Designer 用のカスタムウィジェットプラグインを作成し、プラグインに関連付けられたカスタムタスクメニューエントリを提供します。

Task Menu Extension の例では、Qt Widgets Designer 用のカスタムウィジェットプラグインの作成方法と、QDesignerTaskMenuExtension クラスを使用してプラグインに関連付けられたカスタムタスクメニューエントリを提供する方法を示します。

Qt Widgets Designerで使用できるカスタムウィジェットを提供するには、自己完結型の実装を提供する必要があります。この例では、タスクメニュー拡張機能を表示するように設計されたカスタムウィジェットを使用します:TicTacToe ウィジェットです。

拡張機能とは、Qt Widgets Designerの動作を変更するオブジェクトです。QDesignerTaskMenuExtension は、この拡張機能を持つウィジェットが選択されたときに、カスタムタスクメニューエントリを提供できます。

Qt Widgets Designerには、4種類の拡張機能があります:

  • QDesignerContainerExtension Qt Widgets Designer のマルチページ・コンテナ・プラグインにページを追加(および削除)できる拡張機能。
  • QDesignerMemberSheetExtension Qt Widgets Designerのシグナルとスロットの編集モードを使用して接続を構成するときに表示されるウィジェットのメンバ関数を操作できる拡張機能。
  • QDesignerPropertySheetExtension Qt Widgets Designer のプロパティエディタで表示されるウィジェットのプロパティを操作できる拡張機能を提供します。
  • QDesignerTaskMenuExtension Qt Widgets Designerのタスクメニューにカスタムメニューエントリを追加できる拡張機能。

この例と同じパターンに従って、それぞれの拡張機能ベースクラスを置き換えるだけで、すべての拡張機能を使用できます。詳細については、Qt Widgets Designer C++ Classes を参照してください。

タスクメニュー・エクステンションの例は、5つのクラスで構成されています:

  • TicTacToe は、ユーザーに三目並べゲームをプレイさせるカスタムウィジェットです。
  • TicTacToePlugin TicTacToe クラスを Qt Widgets Designer に公開します。
  • TicTacToeTaskMenuFactory TicTacToeTaskMenu オブジェクトを作成します。
  • TicTacToeTaskMenu タスクメニューの拡張、つまりプラグインに関連するタスクメニューのエントリを提供します。
  • TicTacToeDialog Qt Widgets DesignerにロードされたTic-Tac-Toeプラグインの状態を変更します。

カスタムウィジェットプラグインのプロジェクトファイルには、Qt Widgets Designer内で確実に動作するための追加情報が必要です。例えば、カスタムウィジェットプラグインはQt Widgets Designerで提供されるコンポーネントに依存しており、使用するプロジェクトファイルでこれを指定する必要があります。まず、プラグインのプロジェクトファイルを見てみましょう。

続いて、TicTacToePlugin クラスを確認し、TicTacToeTaskMenuFactoryTicTacToeTaskMenu クラスを見ていきます。最後に、TicTacToe ウィジェットのクラス定義を簡単に見る前に、TicTacToeDialog クラスを確認します。

プロジェクト・ファイル

CMake

プロジェクトファイルには、Qt Widgets Designer ライブラリにリンクするプラグインをビルドすることを記述する必要があります:

find_package(Qt6 REQUIRED COMPONENTS Core Designer Gui Widgets)

qt_add_plugin(taskmenuextension)

target_link_libraries(taskmenuextension PUBLIC
    Qt::Core
    Qt::Designer
    Qt::Gui
    Qt::Widgets
)

次の例では、ウィジェットのヘッダーファイルとソースファイルを追加する方法を示します:

target_sources(taskmenuextension PRIVATE
    tictactoe.cpp tictactoe.h
    tictactoedialog.cpp tictactoedialog.h
    tictactoeplugin.cpp tictactoeplugin.h
    tictactoetaskmenu.cpp tictactoetaskmenu.h
)

Qt Widgets Designerがカスタムウィジェットを使用できるように、プラグイン・インターフェースの実装を提供します。この例では、タスクメニュー拡張と拡張ファクトリ、ダイアログの実装も提供します。

Qt Widgets Designerが検索できる場所にプラグインをインストールすることが重要です。プロジェクトのターゲットパスを指定し、インストールするアイテムのリストに追加することでこれを行います:

   set(INSTALL_EXAMPLEDIR "${QT6_INSTALL_PREFIX}/${QT6_INSTALL_PLUGINS}/designer")
install(TARGETS taskmenuextension
    RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
    BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
    LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)

タスクメニュー拡張はライブラリとして作成されます。タスクメニュー拡張機能はライブラリとして作成され、プロジェクトのインストール時に他の Qt Widgets Designer プラグインと一緒にインストールされます(ninja install または同等のインストール手順を使用)。

プラグインの詳細については、How to Create Qt Pluginsドキュメントを参照してください。

qmake

次の例は、プラグインを Qt Widgets Designer ライブラリにリンクする方法を示しています:

TEMPLATE = lib
CONFIG  += plugin

QT      += widgets designer

次の例では、ウィジェットのヘッダーファイルとソースファイルを追加する方法を示します:

HEADERS += tictactoe.h \
           tictactoedialog.h \
           tictactoeplugin.h \
           tictactoetaskmenu.h
SOURCES += tictactoe.cpp \
           tictactoedialog.cpp \
           tictactoeplugin.cpp \
           tictactoetaskmenu.cpp
OTHER_FILES += tictactoe.json

次の例は、プラグインを Qt Widgets Designer のプラグインパスにインストールする方法を示しています:

target.path = $$[QT_INSTALL_PLUGINS]/designer
INSTALLS += target

TicTacToePluginクラスの定義

TicTacToePlugin クラスは、the TicTacToe クラスを Qt Widgets Designer に公開します。その定義はCustom Widget Pluginの例のプラグインクラスと同じです。クラス定義のうち、この特定のカスタムウィジェットに固有の部分はクラス名だけです。

Qt がウィジェットをプラグインとして認識するように、Q_PLUGIN_METADATA() マクロを追加して、ウィジェットに関する関連情報をエクスポートします:

#ifndef TICTACTOEPLUGIN_H
#define TICTACTOEPLUGIN_H

#include <QtUiPlugin/QDesignerCustomWidgetInterface>

class QIcon;
class QWidget;

class TicTacToePlugin : public QObject, public QDesignerCustomWidgetInterface
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QDesignerCustomWidgetInterface")
    Q_INTERFACES(QDesignerCustomWidgetInterface)

public:
    explicit TicTacToePlugin(QObject *parent = nullptr);

    QString name() const override;
    QString group() const override;
    QString toolTip() const override;
    QString whatsThis() const override;
    QString includeFile() const override;
    QIcon icon() const override;
    bool isContainer() const override;
    QWidget *createWidget(QWidget *parent) override;
    bool isInitialized() const override;
    void initialize(QDesignerFormEditorInterface *formEditor) override;
    QString domXml() const override;

private:
    bool initialized = false;
};

#endif

プラグインクラスは、クラス名やインクルードファイルなど、プラグインの基本情報を Qt Widgets Designer に提供します。さらに、TicTacToe ウィジェットのインスタンスを作成する方法も知っています。TicTacToePluginは、プラグインがQt Widgets Designerにロードされた後に呼び出されるinitialize ()関数も定義します。この関数のQDesignerFormEditorInterface パラメータは、Qt Widgets Designer のすべての API へのゲートウェイをプラグインに提供します。

TicTacToePlugin クラスは、QObjectQDesignerCustomWidgetInterface の両方を継承します。多重継承を使用する場合、Q_INTERFACES() マクロを使用して、すべてのインターフェイス(つまり、Q_OBJECT を継承しないクラス)をメタオブジェクトシステムに知らせることが重要です。これにより、Qt Widgets Designerは、qobject_cast()を使用して、QObject ポインタだけを使用して、サポートされているインターフェイスを問い合わせることができます。

TicTacToePluginクラスの実装

TicTacToePluginクラスの実装は、ほとんどの部分でCustom Widget Pluginサンプルのプラグインクラスと同等です:

TicTacToePlugin::TicTacToePlugin(QObject *parent)
    : QObject(parent)
{
}

QString TicTacToePlugin::name() const
{
    return u"TicTacToe"_s;
}

QString TicTacToePlugin::group() const
{
    return u"Display Widgets [Examples]"_s;
}

QString TicTacToePlugin::toolTip() const
{
    return u"Tic Tac Toe Example, demonstrating class QDesignerTaskMenuExtension (C++)"_s;
}

QString TicTacToePlugin::whatsThis() const
{
    return {};
}

QString TicTacToePlugin::includeFile() const
{
    return u"tictactoe.h"_s;
}

QIcon TicTacToePlugin::icon() const
{
    return {};
}

bool TicTacToePlugin::isContainer() const
{
    return false;
}

QWidget *TicTacToePlugin::createWidget(QWidget *parent)
{
    auto *ticTacToe = new TicTacToe(parent);
    ticTacToe->setState(u"-X-XO----"_s);
    return ticTacToe;
}

bool TicTacToePlugin::isInitialized() const
{
    return initialized;
}

大きく異なる唯一の関数は、initialize()関数です:

void TicTacToePlugin::initialize(QDesignerFormEditorInterface *formEditor)
{

initialize() 関数はQDesignerFormEditorInterface オブジェクトを引数にとります。QDesignerFormEditorInterface クラスは Qt Widgets Designer のコンポーネントへのアクセスを提供します。

Qt Widgets Designerでは、カスタムウィジェットプラグインとツールプラグインの2種類のプラグインを作成できます。QDesignerFormEditorInterface 、通常ツールプラグインを作成するために必要なQt Widgets Designerのすべてのコンポーネント(拡張機能マネージャ、オブジェクトインスペクタ、プロパティエディタ、ウィジェットボックス)にアクセスできます。カスタムウィジェットプラグインも同じコンポーネントにアクセスできます。

    if (initialized)
        return;

    auto *manager = formEditor->extensionManager();
    Q_ASSERT(manager != nullptr);

カスタムウィジェットプラグインに関連する拡張機能を作成する場合、QDesignerFormEditorInterface パラメータから取得した Qt Widgets Designer の現在の拡張機能マネージャにアクセスする必要があります。

Qt Widgets Designer のQDesignerFormEditorInterface には、Qt Designer のすべてのコンポーネントに関する情報が格納されています:アクションエディタ、オブジェクトインスペクタ、プロパティエディタ、ウィジェットボックス、拡張機能およびフォームウィンドウマネージャです。

QExtensionManager クラスは Qt Widgets Designer の拡張管理機能を提供します。Qt Widgets Designerの現在の拡張機能マネージャを使用すると、指定したオブジェクトの拡張機能を取得できます。また、与えられたオブジェクトの拡張機能を登録したり解除したりすることもできます。拡張機能はQt Widgets Designerの動作を変更するオブジェクトです。

拡張機能を登録する場合、実際に登録されるのは関連する拡張機能ファクトリです。Qt Widgets Designerでは、拡張機能ファクトリは必要に応じて名前の付いた拡張機能を検索し、作成するために使用されます。したがって、この例では、タスクメニューの拡張機能自体は、ユーザーによってタスクメニューが要求されるまで作成されません。

    manager->registerExtensions(new TicTacToeTaskMenuFactory(manager),
                                Q_TYPEID(QDesignerTaskMenuExtension));

    initialized = true;
}

QString TicTacToePlugin::domXml() const
{
    return uR"(
<ui language="c++">
    <widget class="TicTacToe" name="ticTacToe"/>
    <customwidgets>
        <customwidget>
            <class>TicTacToe</class>
            <propertyspecifications>
            <tooltip name="state">Tic Tac Toe state</tooltip>
            <stringpropertyspecification name="state" notr="true" type="singleline"/>
            </propertyspecifications>
        </customwidget>
    </customwidgets>
</ui>
)"_s;
}

QDesignerFormEditorInterface パラメータから取得した Qt Widgets Designer の現在のextension manager を使用して登録するTicTacToeTaskMenuFactory オブジェクトを作成します。最初の引数は新しく作成されたファクトリーで、2番目の引数は文字列である拡張識別子です。Q_TYPEID() マクロは単に文字列をQLatin1String に変換します。

TicTacToeTaskMenuFactory クラスはQExtensionFactory のサブクラスです。ユーザが指定されたタスクメニュー拡張機能を持つウィジェットの上でマウスの右ボタンをクリックしてタスクメニューを要求すると、Qt Widgets Designer の拡張機能マネージャは登録されたすべてのファクトリーを実行し、選択されたウィジェットのタスクメニュー拡張機能を作成できる最初のファクトリーを呼び出します。このファクトリーはTicTacToeTaskMenu オブジェクト(拡張機能)を作成します。

デフォルト値は必要ないので、QDesignerCustomWidgetInterface::domXml() 関数 (Qt Widgets Designer で使用される標準 XML フォーマットのウィジェットのデフォルト設定を含む) の再実装は省略します。

    Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QDesignerCustomWidgetInterface")

最後に、Q_PLUGIN_METADATA()マクロを使用して、Qtのプラグイン処理クラスで使用するためにTicTacToePluginクラスをエクスポートします:このマクロは、Qt Widgets Designerがカスタムウィジェットにアクセスして構築できるようにします。このマクロがないと、Qt Widgets Designerはウィジェットを使用できません。

TicTacToeTaskMenuFactory クラスの定義

TicTacToeTaskMenuFactory クラスは、Qt Widgets Designer の標準拡張ファクトリを提供するQExtensionFactory を継承します。

class TicTacToeTaskMenuFactory : public QExtensionFactory
{
    Q_OBJECT

public:
    explicit TicTacToeTaskMenuFactory(QExtensionManager *parent = nullptr);

protected:
    QObject *createExtension(QObject *object, const QString &iid, QObject *parent) const override;
};

このサブクラスの目的は、QExtensionFactory::createExtension() 関数を再実装し、TicTacToe タスクメニュー拡張機能を作成できるようにすることです。

TicTacToeTaskMenuFactory クラスの実装

クラスのコンストラクタは、QExtensionFactory ベースクラスのコンストラクタを呼び出すだけです:

TicTacToeTaskMenuFactory::TicTacToeTaskMenuFactory(QExtensionManager *parent)
    : QExtensionFactory(parent)
{
}

上述したように、Qt Widgets Designerで指定されたタスクメニュー拡張機能を持つウィジェットの上でマウスの右ボタンをクリックして、ユーザがタスクメニューを要求すると、ファクトリーが呼び出されます。

Qt Widgets Designerの動作は、要求された拡張機能がコンテナ、メンバーシート、プロパティシート、タスクメニューのいずれに関連付けられていても同じです:拡張機能マネージャは、登録されているすべての拡張機能ファクトリを実行し、要求された拡張機能を作成するまでcreateExtension() を呼び出します。

QObject *TicTacToeTaskMenuFactory::createExtension(QObject *object,
                                                   const QString &iid,
                                                   QObject *parent) const
{
    if (iid != Q_TYPEID(QDesignerTaskMenuExtension))
        return nullptr;

    if (auto *tic = qobject_cast<TicTacToe*>(object))
        return new TicTacToeTaskMenu(tic, parent);

    return nullptr;
}

したがって、TicTacToeTaskMenuFactory::createExtension() で最初にすることは、要求された拡張機能がタスクメニュー拡張機能かどうかをチェックすることです。もしそうで、要求しているウィジェットがTicTacToe ウィジェットなら、TicTacToeTaskMenu オブジェクトを作成して返します。そうでない場合は、単に null ポインタを返し、Qt Widgets Designer の拡張機能マネージャが登録されたファクトリの検索を続行できるようにします。

TicTacToeTaskMenu クラスの定義

TicTacToeTaskMenu クラスは、Qt Widgets Designer のタスクメニューにカスタムエントリ(QActions の形式)を追加できるQDesignerTaskMenuExtension を継承しています。

class TicTacToeTaskMenu : public QObject, public QDesignerTaskMenuExtension
{
    Q_OBJECT
    Q_INTERFACES(QDesignerTaskMenuExtension)

public:
    explicit TicTacToeTaskMenu(TicTacToe *tic, QObject *parent);

    QAction *preferredEditAction() const override;
    QList<QAction *> taskActions() const override;

private slots:
    void editState();

private:
    QAction *editStateAction;
    TicTacToe *ticTacToe;
};

preferredEditAction()taskActions() 関数を再実装します。親ウィジェットと、タスクメニューが要求されるTicTacToe ウィジェットの2 つの引数を取るコンストラクタを実装します。

さらに、editState() のプライベートスロット、editStateAction のカスタムスロット、TicTacToe ウィジェットへのプライベートポインタを宣言します。

TicTacToeTaskMenuクラスの実装

TicTacToeTaskMenu::TicTacToeTaskMenu(TicTacToe *tic, QObject *parent)
    : QObject(parent)
    , editStateAction(new QAction(tr("Edit State..."), this))
    , ticTacToe(tic)
{
    connect(editStateAction, &QAction::triggered, this, &TicTacToeTaskMenu::editState);
}

コンストラクタでは、まずパラメータとして送られたTicTacToe ウィジェット、つまり変更したい状態のウィジェットへの参照を保存します。これは後でカスタムアクションを呼び出すときに必要になります。また、カスタムeditStateAction を作成し、editState() スロットに接続します。

void TicTacToeTaskMenu::editState()
{
    TicTacToeDialog dialog(ticTacToe);
    dialog.exec();
}

editState() スロットは、ユーザがTicTacToe ウィジェットのタスクメニューでEdit State...オプションを選択するたびに呼び出されます。このスロットは、ウィジェットの現在の状態を表示するTicTacToeDialog を作成し、ユーザがゲームをプレイすることで状態を編集できるようにします。

QAction *TicTacToeTaskMenu::preferredEditAction() const
{
    return editStateAction;
}

preferredEditAction() 関数を再実装して、TicTacToe ウィジェットを選択してF2を押したときに呼び出されるアクションとして、独自のeditStateAction を返します。

QList<QAction *> TicTacToeTaskMenu::taskActions() const
{
    return QList<QAction *>{editStateAction};
}

taskActions() 関数を再実装して、カスタムアクションのリストを返し、TicTacToe ウィジェットのタスクメニューのデフォルトメニューエントリの上に表示します。

TicTacToeDialogクラスの定義

TicTacToeDialog クラスはQDialog を継承しています。このダイアログによって、ユーザーは現在選択されているTic-Tac-Toeプラグインの状態を修正できます。

class TicTacToeDialog : public QDialog
{
    Q_OBJECT

public:
    explicit TicTacToeDialog(TicTacToe *plugin = nullptr, QWidget *parent = nullptr);

    QSize sizeHint() const override;

private slots:
    void resetState();
    void saveState();

private:
    TicTacToe *editor;
    TicTacToe *ticTacToe;
    QDialogButtonBox *buttonBox;
};

sizeHint() 関数を再実装します。また、resetState()saveState() の2つのプライベートスロットを宣言します。ダイアログのボタンとレイアウトに加えて、TicTacToe の2つのポインタを宣言します。1つは、ユーザが対話できるウィジェットへのポインタ、もう1つは、ユーザが状態を編集したい元のカスタムウィジェットプラグインへのポインタです。

TicTacToeDialogクラスの実装

TicTacToeDialog::TicTacToeDialog(TicTacToe *tic, QWidget *parent)
    : QDialog(parent)
    , editor(new TicTacToe)
    , ticTacToe(tic)
    , buttonBox(new QDialogButtonBox(QDialogButtonBox::Ok
                                     | QDialogButtonBox::Cancel
                                     | QDialogButtonBox::Reset))
{
    editor->setState(ticTacToe->state());

    connect(buttonBox->button(QDialogButtonBox::Reset), &QAbstractButton::clicked,
            this, &TicTacToeDialog::resetState);
    connect(buttonBox, &QDialogButtonBox::accepted, this, &TicTacToeDialog::saveState);
    connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);

    auto *mainLayout = new QVBoxLayout(this);
    mainLayout->addWidget(editor);
    mainLayout->addWidget(buttonBox);

    setWindowTitle(tr("Edit State"));
}

コンストラクタでは、まずパラメータとして送られたTicTacToeウィジェットへの参照を保存します。次に、新しいTicTacToe ウィジェットを作成し、その状態をパラメータ・ウィジェットの状態と同じに設定します。

最後に、ダイアログのボタンとレイアウトを作成します。

QSize TicTacToeDialog::sizeHint() const
{
    return {250, 250};
}

sizeHint() 関数を再実装して、ダイアログが適切なサイズになるようにします。

void TicTacToeDialog::resetState()
{
    editor->clearBoard();
}

resetState() スロットは、ユーザーがリセットボタンを押すたびに呼び出されます。エディタウィジェット、つまりダイアログのコンストラクタで作成したTicTacToe ウィジェットに対してclearBoard() 関数を呼び出すだけです。

void TicTacToeDialog::saveState()
{

saveState() スロットは、ユーザがOKボタンを押すたびに呼び出され、エディタ・ウィジェットの状態を変更したいウィジェットに転送します。状態の変更を Qt Widgets Designer に見えるようにするには、QDesignerFormWindowInterface クラスを使用して後者のウィジェットの state プロパティを設定する必要があります。

QDesignerFormWindowInterface は、関連するフォーム・ウィンドウに関する情報を提供し、そのプロパティを変更できるようにします。このインターフェイスは直接インスタンス化することを意図しておらず、Qt Widgets Designerのフォームウィンドウマネージャによって制御されるQt Widgets Designerの現在のフォームウィンドウへのアクセスを提供します。

特定のウィジェットを含むフォーム・ウィンドウを探す場合は、staticQDesignerFormWindowInterface::findFormWindow() 関数を使用できます:

    if (auto *formWindow = QDesignerFormWindowInterface::findFormWindow(ticTacToe))
        formWindow->cursor()->setProperty("state", editor->state());

変更したいウィジェットのフォーム・ウィンドウを取得した後、QDesignerFormWindowInterface::cursor() 関数を使用してフォーム・ウィンドウのカーソルを取得します。

QDesignerFormWindowCursorInterface クラスは、フォーム・ウィンドウのテキスト・カーソルへのインタフェースを提供します。カーソルを取得したら、最後にQDesignerFormWindowCursorInterface::setProperty ()関数を使ってstateプロパティを設定します。

    accept();
}

最後に、イベント・オブジェクトのacceptフラグを設定するQEvent::accept ()関数を呼び出します。accept パラメータを設定することは、イベント受信者がイベントを望んでいることを示す。不要なイベントは、親ウィジェットに伝搬される可能性があります。

TicTacToe クラスの定義

TicTacToe クラスは、ユーザが三目並べゲームをプレイできるカスタムウィジェットです。

class TicTacToe : public QWidget
{
    Q_OBJECT
    Q_PROPERTY(QString state READ state WRITE setState)

public:
    explicit TicTacToe(QWidget *parent = nullptr);

    QSize minimumSizeHint() const override;
    QSize sizeHint() const override;
    void setState(const QString &newState);
    QString state() const;
    void clearBoard();

protected:
    void mousePressEvent(QMouseEvent *event) override;
    void paintEvent(QPaintEvent *event) override;

private:
    static constexpr char16_t Empty = '-';
    static constexpr char16_t Cross = 'X';
    static constexpr char16_t Nought = 'O';

    QRect cellRect(int position) const;
    int cellWidth() const { return width() / 3; }
    int cellHeight() const { return height() / 3; }

    QString myState;
    int turnNumber = 0;
};

TicTacToe クラス定義の主な内容は、state プロパティの宣言と、state() 関数およびsetState() 関数です。

Qt Widgets Designerから見えるようにするために、TicTacToe ウィジェットの状態をプロパティとして宣言する必要があります。TicTacToe ウィジェットがQWidgetQObject から継承したプロパティと同じように、Qt Widgets Designerが管理できるようにします。

プロジェクト例 @ code.qt.io

©2024 The Qt Company Ltd. 本書に含まれるドキュメントの著作権は、それぞれの所有者に帰属します。 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。