任务菜单扩展
为Qt Widgets Designer 创建自定义小部件插件,并提供与该插件关联的自定义任务菜单条目。
“任务菜单扩展”示例演示了如何为 Qt Widgets Designer,并演示如何使用QDesignerTaskMenuExtension 类提供与该插件关联的自定义任务菜单条目。
Qt Widgets Designer 右键菜单中自定义“编辑状态...”操作的屏幕截图。" src="images/taskmenuextension-example.webp" title="Qt Widgets Designer 右键菜单中自定义“编辑状态...”操作的屏幕截图。"/>
要提供一个可与Qt Widgets Designer 配合使用的自定义小部件,我们需要提供一个自包含的实现。在本示例中,我们使用了一个旨在展示任务菜单扩展功能的自定义小部件:TicTacToe小部件。
扩展(extension)是一种可修改 `Qt Widgets Designer` 行为的对象。当选中带有此扩展的小部件时,QDesignerTaskMenuExtension 可以提供自定义任务菜单条目。
Qt Widgets Designer 中提供了四种扩展类型:
- QDesignerContainerExtension 提供一种扩展,允许您向Qt Widgets Designer 中的多页容器插件添加(和删除)页面。
- QDesignerMemberSheetExtension 提供一种扩展,允许您操作小部件的成员函数,这些函数在通过Qt Widgets Designer 的信号和槽编辑模式配置连接时显示。
- QDesignerPropertySheetExtension 提供了一个扩展,允许您操作小部件的属性,该小部件显示在Qt Widgets Designer 的属性编辑器中。
- QDesignerTaskMenuExtension 提供了一个扩展,允许您向Qt Widgets Designer 的任务菜单中添加自定义菜单项。
您可以按照本示例中的相同模式使用所有扩展,只需替换相应的扩展基类即可。有关更多信息,请参阅 Qt Widgets Designer C++ Classes。
“任务菜单扩展”示例由五个类组成:
TicTacToe是一个自定义控件,允许用户玩井字棋游戏。TicTacToePlugin将 `TicTacToe` 类暴露给 `Qt Widgets Designer`。TicTacToeTaskMenuFactory用于创建一个TicTacToeTaskMenu对象。TicTacToeTaskMenu提供任务菜单扩展,即插件相关的任务菜单条目。TicTacToeDialog允许用户修改已加载到Qt Widgets Designer 中的井字棋插件的状态。
自定义小部件插件的项目文件需要一些额外信息,以确保它们能在Qt Widgets Designer 中正常运行。例如,自定义小部件插件依赖于Qt Widgets Designer 提供的组件,这必须在我们使用的项目文件中进行指定。我们将首先查看该插件的项目文件。
随后,我们将继续审视TicTacToePlugin 类,并了解TicTacToeTaskMenuFactory 和TicTacToeTaskMenu 这两个类。最后,在简要查看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}"
)任务菜单扩展以库的形式创建。当项目被安装时(使用ninja install 或等效的安装流程),它将与其他Qt Widgets Designer 插件一同安装。
有关插件的更多信息,请参阅《如何创建 Qt 插件》文档。
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 += targetTicTacToePlugin 类定义
TicTacToePlugin 类将the 的TicTacToe类暴露给Qt Widgets Designer 。其定义与“自定义控件插件”示例中的插件类等同,该示例中对此有详细说明。类定义中唯一特定于此特定自定义控件的部分是类名。
为了确保 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还定义了initialize()函数,该函数在插件加载到Qt Widgets Designer 后会被调用。该函数的QDesignerFormEditorInterface 参数为插件提供了访问Qt Widgets Designer 所有API的入口。
TicTacToePlugin 类同时继承自QObject 和QDesignerCustomWidgetInterface 。需要注意的是,在使用多重继承时,必须确保所有接口(即未继承Q_OBJECT 的类)都通过Q_INTERFACES()宏告知元对象系统。这使得Qt Widgets Designer 能够仅凭一个QObject 指针,使用qobject_cast()查询支持的接口。
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;
}唯一存在显著差异的是 initialize() 函数:
void TicTacToePlugin::initialize(QDesignerFormEditorInterface *formEditor)
{initialize() 函数以QDesignerFormEditorInterface 对象作为参数。QDesignerFormEditorInterface 类提供了对Qt Widgets Designer 组件的访问权限。
在Qt Widgets Designer 中,您可以创建两种类型的插件:自定义控件插件和工具插件。QDesignerFormEditorInterface 提供了访问Qt Widgets Designer 中所有组件的接口,这些组件通常是创建工具插件所必需的:扩展管理器、对象检查器、属性编辑器和控件框。自定义控件插件也可以访问这些组件。
if (initialized)
return;
auto *manager = formEditor->extensionManager();
Q_ASSERT(manager != nullptr);在创建与自定义控件插件相关的扩展时,我们需要访问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 ` 对象,并使用 `Qt Widgets Designer` 当前的 `extension manager `(从 `QDesignerFormEditorInterface ` 参数中获取)将其注册。第一个参数是新创建的工厂,第二个参数是扩展标识符(字符串)。宏 `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的插件处理类配合使用:该宏确保Qt Widgets Designer 能够访问并构造该自定义小部件。如果没有此宏,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() 中首先要做的是检查请求的扩展是否为任务菜单扩展。如果是,且请求它的控件是TicTacToe 控件,我们就创建并返回一个TicTacToeTaskMenu 对象。否则,我们直接返回一个空指针,允许Qt Widgets Designer 的扩展管理器继续遍历已注册的扩展工厂。
TicTacToeTaskMenu 类定义

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::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();
}每当用户在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 类定义

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::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()函数来设置state属性。
accept();
}最后,我们调用QEvent::accept()函数来设置事件对象的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() 函数。
我们需要将TicTacToe 小部件的状态声明为属性,以便Qt Widgets Designer 能够访问它;这样Qt Widgets Designer 就可以像管理TicTacToe 小部件从QWidget 和QObject 继承的属性那样管理它,例如使用属性编辑器。
© 2026 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.