警告

本节包含从 C++ 自动翻译到 Python 的片段,可能包含错误。

自定义小部件插件#

Qt Designer 创建自定义小部件插件。

../_images/customwidgetplugin-example.webp

在本示例中,使用的是基于模拟时钟示例的自定义小部件,并且没有提供任何自定义信号或槽。

准备工作#

要提供一个可以用于 Qt Designer 的自定义小部件,我们需要提供一个自包含的实现并提供一个插件接口。在本示例中,我们为了方便重用了模拟时钟示例。

项目文件#

CMake#

项目文件需要声明构建一个链接到 Qt Designer 库的插件。

find_package(Qt6 REQUIRED COMPONENTS Core Gui UiPlugin Widgets)

qt_add_plugin(customwidgetplugin)

target_link_libraries(customwidgetplugin PUBLIC
    Qt::Core
    Qt::Gui
    Qt::UiPlugin
    Qt::Widgets
)

链接库列表指定了 Qt::UiPlugin。这表示插件仅使用抽象接口 QDesignerCustomWidgetInterfaceQDesignerCustomWidgetCollectionInterface,并且没有链接到 Qt Designer 库。当访问其他有链接的 Qt Designer 接口时,应该使用 Designer;这确保插件动态链接到 Qt Designer 库,并且运行时依赖于它们。

以下示例展示了如何添加小部件的头文件和源文件

target_sources(customwidgetplugin PRIVATE
    analogclock.cpp analogclock.h
    customwidgetplugin.cpp customwidgetplugin.h
)

我们提供了插件接口的实现,以便 Qt Designer 可以使用自定义小部件。

同样重要的是确保插件被安装到 Qt Designer 查找的位置。我们通过指定项目的目标路径并将其添加到安装项目的列表中来实现这一点

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

自定义小部件被创建为一个库。当项目安装(使用 ninja install 或等效的安装程序)时,它将与其他 Qt Designer 插件一起安装。

有关插件的更多信息,请参阅如何创建 Qt 插件的文档。

qmake#

以下示例展示了如何将插件链接到 Qt Designer

CONFIG      += plugin
TEMPLATE    = lib

QT          += widgets uiplugin

变量 QT 包含关键字 uiplugin,它是 Qt::UiPlugin 库的等效项。

以下示例展示了如何添加小部件的头文件和源文件

HEADERS     = analogclock.h \
              customwidgetplugin.h
SOURCES     = analogclock.cpp \
              customwidgetplugin.cpp
OTHER_FILES += analogclock.json

以下示例展示了如何将插件安装到 Qt Designer 的插件路径中

TARGET = $$qtLibraryTarget($$TARGET)

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

模拟时钟类的定义和实现#

AnalogClock 类定义和实现方式与模拟时钟示例中描述的完全相同。由于此类是自包含的,且不需要任何外部配置,因此可以无需修改即可作为自定义小部件在 Qt Designer 中使用。

模拟时钟插件类定义

AnalogClock 类通过 AnalogClockPlugin 类暴露给 Qt Designer。此类继承自 QObject 和 QDesignerCustomWidgetInterface 类,并实现了由 QDesignerCustomWidgetInterface 定义的接口。

为了确保 Qt 识别该小部件作为插件,可以通过添加 Q_PLUGIN_METADATA() 宏来导出有关小部件的相关信息。

class AnalogClockPlugin(QObject, QDesignerCustomWidgetInterface):

    Q_OBJECT
    Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QDesignerCustomWidgetInterface")
    Q_INTERFACES(QDesignerCustomWidgetInterface)
# public
    AnalogClockPlugin = explicit(QObject parent = None)
    bool isContainer() override
    bool isInitialized() override
    QIcon icon() override
    QString domXml() override
    QString group() override
    QString includeFile() override
    QString name() override
    QString toolTip() override
    QString whatsThis() override
    QWidget createWidget(QWidget parent) override
    def initialize(core):
# private
    initialized = False

这些函数提供了有关小部件的信息,Qt Designer 可以使用这些信息在 小部件框 中使用。私有的 initialized 成员变量用于记录小部件是否已被 Qt Designer 初始化。

请注意,此类定义中仅特指的部分是自定义小部件的类名。

模拟时钟插件实现

类的构造函数仅调用 QObject 基类构造函数并将 initialized 变量设置为 false

def __init__(self, parent):
    super().__init__(parent)

当需要插件时,Qt Designer 将通过调用 initialize() 函数来初始化插件。

def initialize(self, */):

    if initialized:
        return
    initialized = True

在此示例中,测试了 initialized 私有变量,并且仅在插件尚未初始化的情况下将其设置为 true。尽管,此插件初始化时不需要执行任何特殊代码,但我们可以在测试初始化后包含这样的代码。

isInitialized() 函数让 Qt Designer 知道插件是否已准备好使用。

def isInitialized(self):

    return initialized

通过 createWidget() 函数提供了自定义小部件的实例。模拟时钟的实现很简单。

QWidget AnalogClockPlugin.createWidget(QWidget parent)

    return AnalogClock(parent)

在这种情况下,自定义小部件只需要指定一个 parent。如果需要向小部件提供其他参数,可以在此处添加。

以下函数为 Qt Designer 提供信息,以便在工具箱中代表该小部件。 name() 函数返回提供自定义小部件的类的名称

def name(self):

    return "AnalogClock"

group() 函数用于描述自定义小部件所属的小部件类型

def group(self):

    return "Display Widgets [Examples]"

小部件插件将在 Qt Designer 的小部件框中放置于由其组名标识的章节中。用于表示小部件的工具箱中的图标由 icon() 函数返回

def icon(self):

    return {}

在这种情况下,我们返回一个空图标来指示我们没有可以使用来表示小部件的图标。

可以为自定义小部件在组件框中的条目提供工具提示和“这是什么?”帮助。函数toolTip()应返回一个描述小部件的简短消息

def toolTip(self):

    return {}

whatsThis()函数可以返回更长的描述

def whatsThis(self):

    return {}

isContainer()函数告知Qt Designer是否将小部件用作其他小部件的容器。如果不是,则Qt Designer不允许用户在其内部放置小部件。

def isContainer(self):

    return False

大多数Qt小部件可以包含子小部件,但在Qt Designer中,使用专用容器小部件来达到此目的更有意义。通过返回false,我们表示自定义小部件不能包含其他小部件;如果返回true,则Qt Designer将允许将其他小部件放置在模拟时钟内部并定义布局。

domXml()函数提供了一种方式,将小部件的默认设置包含在Qt Designer使用的标准XML格式中。在这种情况下,我们只指定小部件的几何形状

def domXml(self):

    return uR"(
<ui language="c++">
  <widget class="AnalogClock" name="analogClock">
)"
R"(
    <property name="geometry">
      <rect>
        <x>0</x>
        <y>0</y>
        <width>100</width>
        <height>100</height>
      </rect>
    </property>
")
R"(
    <property name="toolTip">
      <string>The current time</string>
    </property>
    <property name="whatsThis">
      <string>The analog clock widget displays the current time.</string>
    </property>
  </widget>
</ui>
)"_s

如果小部件提供合理的尺寸提示,则无需在此处定义它。此外,返回空字符串而不是<widget>元素,将通知Qt Designer不要将小部件安装到组件框中。

为了使模拟时钟小部件可供应用程序使用,我们实现了includeFile()函数以返回包含自定义小部件类定义的头文件名

def includeFile(self):

    return "analogclock.h"

示例项目 @ code.qt.io