编程开源技术交流,分享技术与知识

网站首页 > 开源技术 正文

从零开始学Qt(67):高阶!自定义Qt Designer插件

wxchong 2024-10-26 16:18:30 开源技术 29 ℃ 0 评论

本文介绍为UI设计器设计自定义界面组件的Widget插件,直接安装到UI设计器的组件面板里,如同Qt自带的界面设计组件一样使用,在设计时就能看到组件的实际显示效果,只是编译和运行时需要使用到插件的动态链接库(Windows平台上)。

创建 Qt Designer Widget 插件项目

Qt提供两种设计插件的API,可以用于扩展Qt的功能。

高级(high-level) API用于设计插件以扩展Qt的功能,例如定制数据库驱动、图像格式、文本编码、定制样式等,Qt Creator里大量采用了插件,单击Qt Creator的主菜单栏的“Help” 一“About Plugins”菜单项,会显示Qt Creator里已经安装的各种插件。

低级(low-level) API用于创建插件以扩展自己编写应用程序的功能,最常见的就是将自定义Widget组件安装到UI设计器里,用于窗口界面设计。

本节创建一个与QBattery功能一样的类,但是采用创建Qt Designer 插件的方式来创建这个类,并将其安装到UI设计器的组件面板里。

要创建UI设计器插件类,单击Qt Creator的“File” 一 “New File or Project”菜单,在出现的对话框里选择“Other Project”分组的“Qt Custom Designer Widget”项目,会出现一个向导对话框。按照这个向导的操作逐步完成项目创建。

? 第1步:设置插件项目的名称和保存路径

本实例设置项目名称为QBatteryPlugin。

? 第2步:选择项目编译器

可以选择多个编译器,在编译时,再选择具体的编译器。但是实 际上只有MSVC2017 32bit编译器是能用的。

注意使用Qt创建的Widget插件,若要在Qt Creator的UI设计器里正常显示,编译插件的编译器版本必须和编译Qt Creator的版本一致。

Qt 5.14的 Qt Creator 是基于 MSVC2017 32bit 编译器编译的(单击 Qt Creator 的“Help”一“About Qt Creator”菜单,出现的对话框里会显示Qt Creator的版本信息和使用的编译器信息)。所以,为了在Qt Creator里设计窗体时能够正常显示插件,只能使用Qt 5.14 MSVC2017 32bit编译器。

? 第3步:设置自定义QWidget类的名称

只需在左侧的Widget classes列表里设置类名,右侧就会自动设置缺省的文件名,这里添加一个类QBattery。还可以选择一个图标文件作为自定义组件在UI设计器组件面板里的显示图标。

在Description页还可以设置Group、Tooltip和What’s this等信息,Group是自定义组件在组件面板里的分组名称,这里设置为“My Widget”。

? 第4步:显示和设置插件、资源文件名称

本实例缺省的插件名称是qbatteryplugin,资源文件名称为icons.qrc,一般用缺省的即可。

? 第5步:完成设置,生成项目

完成设置后生成的项目的文件组织结构如图所示,这些文件包括以下几个。

  • QBatteryPlugin.pro是插件项目的项目文件,用于实现插件接口。
  • qbatteryplugin.h 和 qbatteryplugin.cpp 是插件的头文件和实现文件。
  • icons.qrc是插件项目的资源文件,存储了图标。
  • qbattery.pri是包含在QBatteryPlugin.pro项目中的一个项目文件(图中选择“Include project”),用于管理自定义组件类。
  • qbattery.h和qbattery.cpp是自定义类QBattery的头文件和实现文件。

插件项目各文件的功能实现

(1)QBatteryPlugin类

qbatteryplugin.h文件中的内容是对插件类QBatteryPlugin的定义,类定义完整代码如下(自动生成,无需修改):

#include <QDesignerCustomWidgetInterface>
class QBatteryPlugin : public QObject, public QDesignerCustomWidgetInterface
{
  Q_OBJECT
  Q_INTERFACES(QDesignerCustomWidgetInterface)
  #if QT_VERSION >= 0x050000
    Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QDesignerCustomWidgetInterface")
  #endif // QT_VERSION >= 0x050000
  public:
    QBatteryPlugin(QObject *parent = 0);
    bool isContainer() const;
    bool isInitialized() const;
    QIcon icon() const;
    QString domXml() const;
    QString group() const;
    QString includeFile() const;
    QString name() const;
    QString toolTip() const;
    QString whatsThis() const;
    QWidget *createWidget(QWidget *parent);
    void initialize(QDesignerFormEditorInterface *core);
  private:
    bool m_initialized;
};

QBatteryPlugin类实现了QDesignerCustomWidgetlnterface接口,这是专门为Qt Designer 设计自定义Widget组件的接口。

在这个类定义里,除了Q_OBJECT宏之外,还用Q_INTERFACES宏声明了实现的接口,用Q_PLUGIN_METADATA声明了元数据名称,这些都无需改动。

public部分的函数都是有关插件信息或功能的一些函数,通过其实现代码可以看出这些函数 的功能。

下面是qbatteryplugin.cpp文件里的实现代码(自动生成,无需修改)。

#include "qbattery.h"
#include "qbatteryplugin.h"
#include <QtPlugin>
  
QBatteryPlugin::QBatteryPlugin(QObject *parent)
: QObject(parent)
{
	m_initialized = false;
}
void QBatteryPlugin::initialize(QDesignerFormEditorInterface * /* core */)
{
  if (m_initialized)
  	return;
  // Add extension registrations, etc. here
  m_initialized = true;
}
bool QBatteryPlugin::isInitialized() const
{//是否初始化
	return m_initialized;
}
QWidget *QBatteryPlugin::createWidget(QWidget *parent)
{//返回自定义Widget组件的实例
	return new QBattery(parent);
}
QString QBatteryPlugin::name() const
{//自定义Widget组件类的名称
	return QLatin1String("QBattery");
}
QString QBatteryPlugin::group() const
{//在组件面板中所属分组名称
	return QLatin1String("My Widget");
}
QIcon QBatteryPlugin::icon() const
{//图标文件名
	return QIcon(QLatin1String(":/battery.jpg"));
}
QString QBatteryPlugin::toolTip() const
{//toolTip 信息
	return QLatin1String("Battery charger indicator");
}
QString QBatteryPlugin::whatsThis() const
{//whatsThis 信息
	return QLatin1String("A battery charger indicator");
}
bool QBatteryPlugin::isContainer() const
{//是否作为容器,false表示该组件上不允许再放其他组件
	return false;
}
QString QBatteryPlugin::domXml() const
{//XML文件描述信息
	return QLatin1String("<widget class=\"QBattery\" name=\"qBattery\">\n</widget>\n");
}
QString QBatteryPlugin::includeFile() const
{//包含文件名
	return QLatin1String("qbattery.h");
}
#if QT_VERSION < 0x050000
	Q_EXPORT_PLUGIN2(qbatteryplugin, QBatteryPlugin)
#endif // QT_VERSION < 0x050000

这些函数的部分内容是根据创建插件向导里设置的内容自动生成的。createWidget()函数创建一个QBattery类的实例,在UI设计器里作为设计实例;name()函数返回组件的类名称;group()函数设置组件安装在面板里的分组名称;icon()设置组件的图标;isContainer()设置组件是否作为容器,false表示不作为容器,不能在这个组件上放置其他组件;domXml()函数用XML设置组件的一些属性,缺省的只设置了类名和实例名。

(2)QBatteryPlugin.pro 的内容

QBatteryPlugin.pro是插件项目的项目管理文件,其内容如下:

CONFIG += plugin debug_and_release
TARGET = $qtLibraryTarget(qbatteryplugin)
TEMPLATE = lib
HEADERS = qbatteryplugin.h
SOURCES = qbatteryplugin.cpp
RESOURCES = icons.qrc
LIBS += -L.
greaterThan(QT_MAJOR_VERSION, 4) {
	QT += designer
} else {
	CONFIG += designer
}
target.path = $[QT_INSTALL_PLUGINS]/designer
INSTALLS += target
include(qbattery.pri)

CONFIG是用于qkame编译设置的,这里配置为:

CONFIG += plugin debug_and_release

其中,plugin表示项目要作为插件,编译后只会产生lib和dll(或.so)文件,debug_and_release 表示项目可以用debug和release模式编译。

TEMPLATE定义项目的类型,这里设置为:

TEMPLATE = lib

这表示项目是一个库,一般的应用程序模板类型是app。

(3)内置项目 qbattery.pri

qbattery.pri是内置于QBatteryPlugin.pro中的项目,qwbattery.pri项目配置文件只有两行,

也就是这个内置项目中包含的头文件和源文件名称。

HEADERS += qbattery.h
SOURCES += qbattery.cpp

(4)组件类QBattery的定义

qbattery.h里的内容是对组件类QBattery的类定义,其功能与前一篇文章中的QBattery类完全一样。这两个类的名称之所以不同,是为了在编译两个实例时不产生冲突。

QBattery类的定义在声明类的时候需要加一个宏QDESIGNER_WIDGET_EXPORT,并且用 Q_PROPERTY宏定义了一个属性powerLevel。

QBattery类的完整定义如下:

#include <QWidget>
#include <QDesignerExportWidget>
class QDESIGNER_WIDGET_EXPORT QBattery : public QWidget
{
  Q_OBJECT
  //自定义属性
  Q_PROPERTY(int powerLevel READ powerLevel WRITE setPowerLevel
  NOTIFY powerLevelChanged DESIGNABLE true)
  private:
    QColor mColorBack=Qt::white; //背景颜色
    QColor mColorBorder=Qt::black; //电池边框颜色
    QColor mColorPower=Qt::green; // 电量柱颜色
    QColor mColorWarning=Qt::red; // 电量短缺时的颜色
    int mPowerLevel=60; // 电量0-100
    int mWarnLevel=20; // 电量低警告阈值
  protected:
  	void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE;
  public:
    explicit QBattery(QWidget *parent = 0);
    void setPowerLevel (int pow); //设置当前电量
    int powerLevel();
    void setWarnLevel(int warn);//设置电量低阈值
    int warnLevel();
    QSize sizeHint();//报告缺省大小
  signals:
  	void powerLevelChanged(int );
  public slots:
};

QDESIGNER_WIDGET_EXPORT宏用于将自定义组件类从插件导出给Qt Designer使用,必须在类名称前使用此宏。

Q_PROPERTY宏用于定义属性,这里定义了一个int类型的属性powerLevel。READ宏声明 了属性的读取函数是powerLevel(); WRITE宏声明了设置属性值的函数是setPowerLevel(); NOTIFY宏声明了其值变化时发射的信号是powerLevelChanged(); DESIGNABLE宏定义属性在 UI设计器里是否可见,缺省为true。

将从QWidget继承的子类QBattery作为插件安装到UI设计器的组件面板里,则在设计期间就可以从属性编辑器里看到这个powerLevel属性并进行设置。

QBattery类的实现代码与之前的实现代码完全相同,不再列出。

插件的编译与安装

使用MSVC2017 32bit编译器,将插件项目在release模式下编译,编译后会生成qbatteryplugin.dll 和 qbatteryplugin.lib 两个文件。

qbatteryplugin.dll是插件的动态链接库文件,需要将此文件复制到Qt Creator的插件目录和 Qt的插件目录下。例如,要是把Qt安装到了C:\Qt\Qt5.14.2目录下,就需要将qbatteryplugin.dll复制到如下两个目录下:

C:\Qt\Qt5.14.2\Tools\QtCreator\bin\plugins\designer
C:\Qt\Qt5.14.2\5.14.2\msvc2017\plugins\designer

重启Qt Creator,使用UI设计器设计窗口时,在左侧的组件面板里会看到增加了一个“My Widget” 分组,里面有一个组件QBattery。

编译和安装Widget插件必须注意以下事项。

  • 要让插件在Qt creator的UI设计器里正常显示,编译插件项目的编译器必须与编译Qt Creator的编译器一致,否则,即使将编译后生成的DLL文件复制到Qt的目录下,Qt Creator 的UI设计器的组件面板里也不会出现自定义的组件。例如,Qt Creator 4.11.1是基于Qt 5.14.2和MSVC2017 32 bit编译器(单击 Qt Creator 的 “Help” 一 “About Qt Creator” 菜单项可以看到这些信息),那么编译插件就必须使用Qt 5.12.2 MSVC2017 32 bit编译器。
  • 用debug和release模式编译的插件也分别只适用于debug和release模式编译的应用程序。在debug模式下编译的插件项目生成的Lib和DLL文件会在文件名最后自动增加一个字母“d”,例如,本项目在debug模式下编译生成的是 qbatteryplugind.dll和 qbatteryplugind.lib,这两个文件应用于使用此插件的应用程序的debug模式。

使用自定义插件

在Qt Creator的UI设计器的组件面板里能正常显示自定义的QBattery组件后,就可以在窗 体设计时使用QBattery组件了。

创建一个基于QWidget的实例应用程序BatteryUser。设计窗体时,从组件面板上拖放一个 QBattery到窗体上。在设计窗体时,就能直接看到QBattery绘制的电池图形,在属性编辑器里可以编辑QBattery组件的powerLevel属性,在其“Go to slot”对话框里会出现自定义的信号powerLevelChanged(int),可以为此信号设计槽函数。

下面的代码实现的是利用滑动条设置battery的当前电量值,在battery的powerLevelChanged()信号的槽函数里,将当前电量值显示在标签里,程序运行后就可以实现上一篇文章示例中相同的功能。

void Widget::on_horizontalSlider_valueChanged(int value)
{ // 拖动slider改变battery的电量
	ui->battery->setPowerLevel(value);
}
void Widget::on_battery_powerLevelChanged(int arg1)
{ //电量值改变时,在标签中显示
	QString str=QStringLiteral("当前电量:")+QString::asprintf ("%d %%", arg1);
	ui->labInfo->setText(str);
}

注意项目BatteryUser只能用MSVC2017 32bit编译器进行编译,因为使用的Widget插件类QBattery是用MSVC2017 32bit 编译的。

要正常编译项目BatteryUser还需要做以下设置。

  • 在项目的源文件目录下创建一个include子目录(名称随个人喜好设置),将QBattery类定义的头文件qbattery.h、插件的debug和release两种模式编译生成的库文件qbatteryplugin.lib 以及qbatteryplugind.lib复制到此目录下,项目在编译链接时需要使用此头文件和库文件。
  • 在项目管理器中,选中BatteryUser项目节点并单击右键,在快捷菜单中单击“Add Library…”,在出现的向导对话框第一步中,选择库类型时,将外部库“External Library” 选中,因为本项目需要使用的是己经编译好的库文件。
  • 在向导的第二步,单击“Library file”编辑框后面的按钮,选择include目录 下的库文件qwbatteryplugin.lib,会自动填充“Include path”编辑框。在平台选择中可以只选择一个 windows平台,连接方式选择Dynamic,下方的 Add “d” suffix for debug version 表示在debug版本的库名称后面添加一个字母“d”,以便编译器自动区分release和debug 版本的库文件。

完成“Add Library”对话框的设置后,QtCreator会自动修改项目文件BatteryUser.pro的内容,在其中添加了以下几行:

win32:CONFIG(release, debug|release): LIBS += -L$PWD/include/ -lqbatteryplugin
else:win32:CONFIG(debug, debug|release): LIBS += -L$PWD/include/ -lqbatteryplugind
INCLUDEPATH += $PWD/include
DEPENDPATH += $PWD/include

LIBS用于设置添加的库文件,会判断当前项目是以debug还是release模式编译,自动加入 qbatteryplugin.lib 或 qbatteryplugind.lib 库文件。

INCLUDEPATH和DEPENDPATH用于设置头文件目录和项目依赖项目录,都指向项目路径 下的include目录。

这样设置后,项目就可以在release或debug模式下编译了,同样只能使用MSVC2017 32bit编译器。 注意要运行应用程序,还需要将插件的DLL文件复制到编译后的release或debug版本的可执行文件目录下,在本例中就是qbatteryplugin.dll文件或qbatteryplugind.dll文件,因为应用程序运行需要相应的DLL文件。在应用程序发布时,也需要将DLL文件随同应用程序发布。

自定义Widget插件的功能使得我们可以扩展Qt Creator的组件种类,设计自己需要的组件。 也有许多第三方Widget插件可供直接使用,减少自己编程的工作量,例如QWT就是一套非常好的开源Widget插件。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表