任务菜单扩展
为Qt Widgets Designer 创建自定义 widget 插件,并提供与插件关联的自定义任务菜单条目。
任务菜单扩展示例展示了如何为 Qt Widgets Designer的自定义 widget 插件,以及如何使用QDesignerTaskMenuExtension 类提供与插件关联的自定义任务菜单条目。
要提供可与Qt Widgets Designer 一起使用的自定义 widget,我们需要提供一个独立的实现。在本示例中,我们使用一个自定义 widget 来显示任务菜单扩展功能:TicTacToe widget。
扩展是一个可以修改Qt Widgets Designer 行为的对象。当选择一个带有该扩展的 widget 时,QDesignerTaskMenuExtension 可以提供自定义任务菜单条目。
Qt Widgets Designer 中有四种可用的扩展:
- QDesignerContainerExtension 在 中提供了一个扩展,允许你向多页面容器插件添加(和删除)页面。Qt Widgets Designer
- QDesignerMemberSheetExtension 提供了一个扩展,允许您在使用 的信号和插槽编辑模式配置连接时,对显示的 widget 成员函数进行操作。Qt Widgets Designer
- QDesignerPropertySheetExtension 提供了一个扩展,允许您操作 widget 的属性,显示在 的属性编辑器中。Qt Widgets Designer
- QDesignerTaskMenuExtension 提供了一个扩展,允许您在 的任务菜单中添加自定义菜单项。Qt Widgets Designer
您可以按照本例中的相同模式使用所有扩展,只需替换相应的扩展基类即可。有关详细信息,请参阅 Qt Widgets Designer C++ Classes.
任务菜单扩展示例由五个类组成:
TicTacToe
是一个自定义部件,可让用户玩井字游戏。TicTacToePlugin
将 类公开给 。TicTacToe
Qt Widgets DesignerTicTacToeTaskMenuFactory
创建 对象。TicTacToeTaskMenu
TicTacToeTaskMenu
提供任务菜单扩展,即插件的相关任务菜单条目。TicTacToeDialog
允许用户修改加载到 的井字游戏插件的状态。Qt Widgets Designer
自定义 widget 插件的项目文件需要一些附加信息,以确保它们能在Qt Widgets Designer 中运行。例如,自定义 widget 插件依赖于随Qt Widgets Designer 提供的组件,而这必须在我们使用的项目文件中指定。我们将首先查看插件的项目文件。
然后,我们将继续查看TicTacToePlugin
类,并查看TicTacToeTaskMenuFactory
和TicTacToeTaskMenu
类。最后,在快速查看TicTacToe
widget 的类定义之前,我们将查看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 )
下面的示例展示了如何添加 widget 的头文件和源文件:
target_sources(taskmenuextension PRIVATE tictactoe.cpp tictactoe.h tictactoedialog.cpp tictactoedialog.h tictactoeplugin.cpp tictactoeplugin.h tictactoetaskmenu.cpp tictactoetaskmenu.h )
我们提供了插件接口的实现,以便Qt Widgets Designer 可以使用自定义 widget。在本示例中,我们还提供了任务菜单扩展和扩展工厂的实现,以及对话框的实现。
必须确保插件安装在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}" )
任务菜单扩展作为库创建。在安装项目(使用ninja install
或类似的安装程序)时,它将与其他Qt Widgets Designer 插件一起安装。
有关插件的更多信息,请参阅如何创建 Qt 插件文档。
qmake
下面的示例展示了如何将插件链接到Qt Widgets Designer 库:
TEMPLATE = lib CONFIG += plugin QT += widgets designer
下面的示例演示了如何添加 widget 的头文件和源文件:
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
类向Qt Widgets Designer 公开了the
TicTacToe 类。其定义等同于自定义 Widget 插件示例的插件类,详细解释见该示例。类定义中唯一与该自定义部件相关的部分是类名。
为确保 Qt 将 widget 识别为插件,请通过添加Q_PLUGIN_METADATA()
宏导出 widget 的相关信息:
#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
widget 的实例。TicTacToePlugin 还定义了initialize() 函数,该函数在插件加载到Qt Widgets Designer 后被调用。该函数的QDesignerFormEditorInterface 参数为插件提供了通向所有Qt Widgets Designer API 的网关。
TicTacToePlugin
类继承自QObject 和QDesignerCustomWidgetInterface 。在使用多重继承时,一定要确保使用Q_INTERFACES() 宏向元对象系统提供所有接口(即不继承Q_OBJECT 的类)。这样,Qt Widgets Designer 就可以使用qobject_cast() 查询支持的接口,而只需使用QObject 指针。
TicTacToePlugin 类的实现
TicTacToePlugin 类的大部分实现与自定义小工具插件示例的插件类相同:
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; }
唯一明显不同的是初始化()函数:
void TicTacToePlugin::initialize(QDesignerFormEditorInterface *formEditor) {
initialize()
函数将QDesignerFormEditorInterface 对象作为参数。QDesignerFormEditorInterface 类可访问Qt Widgets Designer 的组件。
在Qt Widgets Designer 中,您可以创建两种插件:自定义 widget 插件和工具插件。QDesignerFormEditorInterface 可访问创建工具插件通常需要的所有Qt Widgets Designer 组件:扩展管理器、对象检查器、属性编辑器和 widget 框。自定义 widget 插件可以访问相同的组件。
if (initialized) return; auto *manager = formEditor->extensionManager(); Q_ASSERT(manager != nullptr);
在创建与自定义 widget 插件相关的扩展时,我们需要访问Qt Widgets Designer 的当前扩展管理器,该管理器可从QDesignerFormEditorInterface 参数中获取。
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; }
我们创建一个TicTacToeTaskMenuFactory
对象,并使用从QDesignerFormEditorInterface 参数获取的Qt Widgets Designer'当前extension manager 注册该对象。第一个参数是新创建的工厂,第二个参数是扩展标识符(字符串)。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() 宏导出 TicTacToePlugin 类,供 Qt XML 的插件处理类使用:该宏确保Qt Widgets Designer 可以访问和构建自定义 widget。如果没有该宏,Qt Widgets Designer 就无法使用该部件。
井字任务菜单工厂(TicTacToeTaskMenuFactory)类定义
TicTacToeTaskMenuFactory
类继承于QExtensionFactory ,后者为Qt Widgets Designer 提供了一个标准扩展工厂。
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()
中做的第一件事就是检查请求的扩展是否是任务菜单扩展。如果是,并且请求扩展的 widget 是TicTacToe
widget,我们就会创建并返回一个TicTacToeTaskMenu
对象。否则,我们只需返回一个空指针,允许Qt Widgets Designer 的扩展管理器继续搜索已注册的工厂。
井字任务菜单类定义
TicTacToeTaskMenu
类继承于QDesignerTaskMenuExtension ,它允许您在Qt Widgets Designer 的任务菜单中添加自定义条目(以 QActions 的形式)。
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
窗口部件。
此外,我们还声明了私有的editState()
插槽、我们自定义的editStateAction
以及指向我们要修改状态的TicTacToe
微件的私有指针。
井字任务菜单类的实现
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
widget 的引用,即我们要修改状态的 widget。稍后调用自定义操作时,我们将需要它。我们还创建了自定义editStateAction
,并将其连接到editState()
插槽。
void TicTacToeTaskMenu::editState() { TicTacToeDialog dialog(ticTacToe); dialog.exec(); }
每当用户在TicTacToe
小部件的任务菜单中选择 "编辑状态..."选项时,就会调用editState()
插槽。该插槽会创建一个TicTacToeDialog
,显示部件的当前状态,并允许用户通过玩游戏来编辑其状态。
QAction *TicTacToeTaskMenu::preferredEditAction() const { return editStateAction; }
我们重新实现了preferredEditAction()
函数,以返回我们自定义的editStateAction
作为选择TicTacToe
部件并按下F2时应调用的操作。
QList<QAction *> TicTacToeTaskMenu::taskActions() const { return QList<QAction *>{editStateAction}; }
我们重新实现了taskActions()
函数,以返回自定义操作列表,使这些操作显示在TicTacToe
小工具任务菜单中默认菜单项的顶部。
井字对话框类定义
TicTacToeDialog
类继承于QDialog 。该对话框允许用户修改当前选定的井字游戏插件的状态。
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()
。除了对话框的按钮和布局外,我们还声明了两个TicTacToe
指针,一个指向用户可以交互的部件,另一个指向用户要编辑状态的原始自定义部件插件。
井字对话框类的实现
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()
槽就会被调用,并将编辑器部件的状态转移到我们要修改状态的部件上。为了让Qt Widgets Designer 看到状态的改变,我们需要使用QDesignerFormWindowInterface 类设置后一个窗口部件的状态属性。
QDesignerFormWindowInterface 该接口为您提供相关表单窗口的信息,并允许您更改其属性。该接口并不是用来直接实例化的,而是用来访问 的当前窗体窗口,由 的窗体窗口管理器控制。Qt Widgets Designer Qt Widgets Designer
如果要查找包含特定窗口部件的窗体窗口,可以使用静态QDesignerFormWindowInterface::findFormWindow() 函数:
if (auto *formWindow = QDesignerFormWindowInterface::findFormWindow(ticTacToe)) formWindow->cursor()->setProperty("state", editor->state());
在获取部件的窗体窗口(我们要修改的状态)后,我们使用QDesignerFormWindowInterface::cursor() 函数来获取窗体窗口的光标。
QDesignerFormWindowCursorInterface 类为窗体窗口的文本光标提供了一个接口。有了光标后,我们就可以使用QDesignerFormWindowCursorInterface::setProperty() 函数设置状态属性了。
accept(); }
最后,我们调用QEvent::accept() 函数来设置事件对象的接受标志。设置accept
参数表示事件接收者想要该事件。不想要的事件可能会传播到父部件。
井字类定义
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()
函数的声明。
我们需要将TicTacToe
widget 的状态声明为一个属性,使其对Qt Widgets Designer 可见;允许Qt Widgets Designer 以管理TicTacToe
widget 从QWidget 和QObject 继承的属性(例如属性编辑器)的相同方式对其进行管理。
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.