Qt系列文章036-自定义Qt Designer插件
1 前言 上节讲了如何自定义一个QWidget组件,然后提升为QWidget控件来使用,本节将讲解如何自定义一个插件,直接拖放,而插件时基于组件形成的,所以前面的知识是必须要掌握滴~2 创建Qt Designer Widget插件项目 Qt提供两种设计插件的API,可以用于扩展Qt的功能。高级(high-level) API用于设计插件以扩展Qt的功能,例如 定制数据库驱动、图像格式、文本编码
1 前言
上节讲了如何自定义一个QWidget组件,然后提升为QWidget控件来使用,本节将讲解如何自定义一个插件,直接拖放,而插件时基于组件形成的,所以前面的知识是必须要掌握滴~
公众号:Qt实战,各种开源作品、经验整理、项目实战技巧,专注Qt/C++软件开发,视频监控、物联网、工业控制、嵌入式软件、国产化系统应用软件开发。
公众号:Qt入门和进阶,专门介绍Qt/C++相关知识点学习,帮助Qt开发者更好的深入学习Qt。多位Qt元婴期大神,一步步带你从入门到进阶,走上财务自由之路。
官方店:https://shop114595942.taobao.com//
2 创建Qt Designer Widget插件项目
Qt提供两种设计插件的API,可以用于扩展Qt的功能。高级(high-level) API用于设计插件以扩展Qt的功能,例如 定制数据库驱动、图像格式、文本编码、定制样式 等,Qt Creator 里大量采用了插件,单击Qt Creator的主菜单栏的 Help→About Plugins 菜单项,会显示 Qt Creator里已经安装的各种插件。
低级(low-level) API 用于创建插件以扩展自己编写应用程序的功能,最常见的就是将自定义Widget 组件(上一节的专讲) 安装到UI设计器里,用于窗口界面设计。
本节创建一个与上节的QmyBattery功能一样的类 MyBattery,但是采用创建 Qt Designer 插件 的方式来创建这个类,并将其安装到UI设计器的组件面板里。
要创建UI设计器插件类,单击Qt Creator的 文件(File) → 新建文件或项目(New File or Project) 菜单,在出现的对话框里选择 其他项目(Other Project) 分组的 Qt4设计师自定义控件(Qt Custom Designer Widget) 项目,会出现一个向导对话框。按照这个向导的操作逐步完成项目创建。如下图所示:
选择确定后
第1步是设置插件项目的名称和保存路径,本实例设置项目名称为MyBatteryDesignerPlugin。
第2步是选择项目编译器,可以选择多个编译器,在编译时,再选择具体的编译器。但是实际上只有MSVC系列编译器是能用的。
注意:
使用Qt创建的Widget插件,若要在QtCreator的UI设计器里正常显示,编译插件的编译器版本必须
和编译Qt Creator的版本一致。
Qt5.12.11(后面更新为11最新版本了) 的QtCreator是基于MSVC2019 64bit编译器编译的(单击QtCreator的 帮助(Help)→AboutQtCreator 菜单,出现的对话框里会显示QtCreator的版本信息和使用的编译器信息,见下图)。所以,为了在QtCreator里设计窗体时能够正常显示插件,只能使用Qt5.12.11 MSVC2019 64bit编译器。
第3步是设置自定义QWidget类的名称(见下图),只需在左侧的 控件类(Widget Classes) 列表里设置类名,右侧就会自动设置缺省的文件名,这里添加一个类 MyBattery。还可以选择一个图标文件作为自定义组件在UI设计器组件面板里的显示图标。
在上图的 说明(Description) 页还可以设置 组(Group)、工具提示(Tooltip) 和 这是什么(What’s this) 等信息,组(Group) 是自定义组件在组件面板里的分组名称,这里设置为 My Widget 。如图所示:
第4步是显示和设置插件、资源文件名称。本实例缺省的插件名称是 mybatteryplugin, 资源文件名称为 icons.qrc,一般用缺省的即可。如下图所示:
第5步 完成设置,生成项目。
完成设置后生成的项目的文件组织结构如下图所示
这些文件包括以下几个:
- MyBatteryDesignerPlugin.pro 是插件项目的项目文件,用于实现插件接口。
- mybatteryplugin.h 和 mybatteryplugin.cpp 是插件的头文件和实现文件。
- icons.qrc 是插件项目的资源文件,存储了图标。
- mybattery.pri 是包含在 MyBatteryDesignerPlugin.pro 项目中的一个项目文件(上图自定义列表中选择 包含项目(Include project) ),用于管理自定义组件类。
- mybattery.h 和 mybattery.cpp 是自定义类MyBattery的头文件和实现文件。
3 插件项目各文件的功能实现
3.1 MyBatteryPlugin 类
mybatteryplugin.h 文件中的内容是对插件类 MyBatteryPlugin 的定义,类定义完整代码如下:
#include <QDesignerCustomWidgetInterface>
class MyBatteryPlugin : 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:
MyBatteryPlugin(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;
};
MyBatteryPlugin 类实现了 QDesignerCustomWidgetInterface 接口,这是专门为Qt Designer 设计 自定义 Widget组件 的接口。
在这个类定义里,除了Q_OBJECT 宏之外,还用了 Q_INTERFACES 宏声明了实现的接口,用 Q_PLUGIN_METADATA 声明了元数据名称,这些都无需改动。
public 部分的函数都是有关插件信息或功能的一些函数,通过其实现代码可以看出这些函数的功能。下面是 mybatteryplugin.cpp 文件里的实现代码。
#include "mybattery.h"
#include "mybatteryplugin.h"
#include <QtPlugin>
MyBatteryPlugin::MyBatteryPlugin(QObject *parent)
: QObject(parent)
{
m_initialized = false;
}
void MyBatteryPlugin::initialize(QDesignerFormEditorInterface * /* core */)
{
if (m_initialized)
return;
// Add extension registrations, etc. here
m_initialized = true;
}
bool MyBatteryPlugin::isInitialized() const
{
return m_initialized;
}
QWidget *MyBatteryPlugin::createWidget(QWidget *parent)
{
return new MyBattery(parent);
}
QString MyBatteryPlugin::name() const
{
return QLatin1String("MyBattery");
}
QString MyBatteryPlugin::group() const
{
return QLatin1String("My Widget");
}
QIcon MyBatteryPlugin::icon() const
{
return QIcon(QLatin1String(":/battery.png"));
}
QString MyBatteryPlugin::toolTip() const
{
return QLatin1String("");
}
QString MyBatteryPlugin::whatsThis() const
{
return QLatin1String("");
}
bool MyBatteryPlugin::isContainer() const
{
return false;
}
QString MyBatteryPlugin::domXml() const
{
return QLatin1String("<widget class=\"MyBattery\" name=\"myBattery\">\n</widget>\n");
}
QString MyBatteryPlugin::includeFile() const
{
return QLatin1String("mybattery.h");
}
#if QT_VERSION < 0x050000
Q_EXPORT_PLUGIN2(mybatteryplugin, MyBatteryPlugin)
#endif // QT_VERSION < 0x050000
这些函数的部分内容是根据创建插件向导里设置的内容自动生成的。createWidget() 函数创建一个MyBattery类的实例,在UI设计器里作为设计实例,name() 函数返回组件的类名称, group() 函数设置组件安装在面板里的分组名称, icon() 设置组件的图标, isContainer() 设置组件是否作为容器,false 表示不作为容器,不能在这个组件上放置其他组件, domXml() 函数用 XML 设置组件的一些属性,缺省的只设置了类名和实例名。
3.2 MyBatteryDesignerPlugin.pro 的内容
MyBatteryDesignerPlugin.pro 是插件项目的项目管理文件,其内容如下:
CONFIG += plugin debug_and_release
TARGET = $$qtLibraryTarget(mybatteryplugin)
TEMPLATE = lib
HEADERS = mybatteryplugin.h
SOURCES = mybatteryplugin.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(mybattery.pri)
CONFIG 是用于 qmake 编译设置的,这里配置为:
CONFIG += plugin debug_and_release
其中, plugin 表示项目要作为插件,编译后只会产生 lib和dl (或.so) 文件, debug_and_release 表示项目可以用 debug 和 release 模式编译。
TEMPLATE 定义项目的类型,这里设置为:
TEMPLATE = lib
这表示项目是一个库,一般的应用程序模板类型是 app。
3.3 内置项目mybattery.pri
mybattery.pri 是内置于 MyBatteryDesignerPlugin.pro 中的项目,mybattery.pri 项目配置文件只有两行,也就是这个内置项目中包含的头文件和源文件名称。
HEADERS += mybattery.h
SOURCES += mybattery.cpp
3.4 组件类MyBattery的定义
mybattery.h 里的内容是对组件类 MyBattery 的类定义,其功能与上节(自定义插件和库) 中的 MyBattery 类完全一样。这两个类的名称是一样的,所以在编译两个实例时可能会产生冲突(各位自己可以重新更改一个不一致的名称)。
MyBattery 类的定义与 上节 的定义基本一样,只是在声明类的时候需要加一个宏QDESIGNER_WIDGET_EXPORT ,并且用 Q_PROPERTY 宏定义了一个属性 powerLevel。本节 MyBattery 类的完整定义如下:
class QDESIGNER_WIDGET_EXPORT MyBattery : public QWidget
{
Q_OBJECT
//自定义属性
Q_PROPERTY (int powerLevel READ powerLevel WRITE setPowerLevel NOTIFY powerLevelChanged DESIGNABLE true)
public:
MyBattery(QWidget *parent = 0);
int powerLevel() const;
void setPowerLevel(int newPowerLevel);
void setWarnLevel (int warn); //设置电量低阈值
int warnLevel ();
virtual QSize sizeHint();//缺省大小
signals:
void powerLevelChanged(int);
protected:
void paintEvent(QPaintEvent * event) Q_DECL_OVERRIDE;
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; //电量低警示阈值
};
QDESIGNER_WIDGET_EXPORT 宏用于将自定义组件类从插件导出给 Qt Designer 使用,必须在类名称前使用此宏。
Q_PROPERTY 宏用于定义属性,这里定义了一个int类型的属性 powerLevel ,READ 宏声明 了属性的读取函数是 powerLevel(),WRITE 宏声明了设置属性值的函数是setPowerLevel(), NOTIFY 宏声明了其值变化时发射的信号是powerLevelChanged() , DESIGNABLE 宏定义属性在 UI设计器里是否可见,缺省为true。
将从 QWidget 继承的子类 MyBattery 作为插件安装到UI设计器的组件面板里,则在设计期间
就可以从属性编辑器里看到这个 powerLevel 属性并进行设置。MyBattery类的实现代码与 上节MyBattery 的实现代码完全相同,不再列出。
4 插件的编译与安装
使用 MSVC2019 64bit 编译器,将插件项目在 release 模式下编译,编译后会生成mybatteryplugin.dll 和 mybatteryplugin.lib 两个文件。
mybatteryplugin.dll 是插件的动态链接库文件,需要将此文件复制到 Qt Creator 的插件目录和Qt的插件目录下。例如,要把Qt安装到 D:\Qt\Qt5.12.11 目录下,就需要将 mybatteryplugin.dll 复制到如下两个目录下:
D:\Qt\Qt5.12.11\Tools\QtCreator\bin\plugins\designer
D:\Qt\Qt5.12.11\5.12.11\msvc2017_64\plugins\designer
重启 Qt Creator ,使用UI设计器设计窗口时,在左侧的组件面板里会看到增加了一个 MyWidget
分组,里面有一个组件 MyBattery。
编译和安装 Widget 插件必须注意以下事项。
- 要让插件在Qt Creator的UI设计器里正常显示,编译插件项目的编译器必须与编译QtCreator的编译器一致,否则,即使将编译后生成的DLL文件复制到Qt的目录下,QtCreator的UI设计器的组件面板里也不会出现自定义的组件。例如,QtCreator4.15.0是基于Qt5.15.2和MSVC2019 64bit编译器(单击Qt Creator的Help → About Qt Creator 菜单项可以看到这些信息),那么编译插件就必须使用 MSVC2019 64bit 编译器。
- 用debug和release模式编译的插件也分别只适用于debug和release模式编译的应用程序。在debug模式下编译的插件项目生成的 Lib和DLL 文件会在文件名最后自动增加一个 字母d,例如,本项目在debug模式下编译生成的是 mybatteryplugind.dll和mybatteryplugind.lib, 这两个文件应用于使用此插件的应用程序的debug模式。
5 使用自定义插件
在QtCreator的UI设计器的组件面板里能正常显示自定义的MyBattery组件后,就可以在窗体设计时使用QwBattery组件了。
创建一个基于QWidget 的实例应用程序BatteryUser。 设计窗体时,从组件面板上拖放一个
MyBattery 到窗体.上,窗体的功能与上节实例的窗体相同,但是在设计窗体时,就能直接看到 MyBattery 绘制的电池图形,在属性编辑器里可以编辑MyBattery组件的powerLevel属性(见下图),在其 转到槽(Go to slot) 对话框里会出现自定义的信号powerLevelChanged(int),可以为此信号设计槽函数。
下面的代码实现的是利用滑动条设置battery的当前电量值,在battery的 powerLevelChanged() 信号的槽函数里,将当前电量值显示在标签里,程序运行后就可以实现与上节相同的功能。
void Widget::on_horizontalSlider_valueChanged(int value)
{
ui->battery->setPowerLevel(value);
}
void Widget::on_battery_powerLevelChanged(int arg1)
{
//电量值改变时,在标签中显示
QString str=QString("当前电量: %1 %").arg(arg1);
ui->LabInfo->setText(str) ;
}
注意: 项目BatteryUser只能用MSVC2019 64bit 编译器进行编译,因为使用的Widget 插件类MyBattery 是用MSVC2019 64bit编译的。
要正常编译项目 BatteryUser,还需要做以下设置。
- 在项目的源文件目录下创建一个 include子目录(名称随个人喜好设置),将 MyBattery 类定义
的头文件 mybattery.h、插件的debug和release两种模式编译生成的库文件mybatteryplugin.lib
以及 mybatteryplugind.lib 复制到此目录下,项目在编译链接时需要使用此头文件和库文件。
如图所示:
我是一股脑将dll也复制进来了,其实不用的!
2.在项目管理器中,选中 BatteryUser 项目节点并单击右键,在快捷菜单中单击 添加库(Add Lirar…) ,在出现的向导对话框第一步中, 选择库类型时,将 外部库(External Library) 选中,因为本项目需要使用的是已经编译好的库文件。如下图所示:
- 在向导的第二步(见上图),单击 库文件(Library file) 编辑框后面的按钮 浏览…,选择include目录
下的库文件 mybatteryplugin.lib,会自动填 包含路径(Include path) 编辑框。在平台选择中可以只选择一个 Windows 平台,连接方式选择动态(Dynamic),下方的 为debug版本添加’d’ 作为后缀(Add ‘d’ suffix for debug version) 表示在debug版本的库名称后面添加一个字母’d’,以便编译器自动区分 release和debug 版本的库文件。
完成 添加库(Add Library) 对话框的设置后,Qt Creator会自动修改项目文件 BatteryUser.pro 的内容,在其中添加了以下几行:
win32:CONFIG(release, debug|release): LIBS += -L$$PWD/include/ -lmybatteryplugin
else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/include/ -lmybatteryplugind
INCLUDEPATH += $$PWD/include
DEPENDPATH += $$PWD/include
LIBS用于设置添加的库文件,会判断当前项目是以debug还是release 模式编译,自动加入mybatteryplugin.lib或mybatteryplugind.lib库文件。
INCLUDEPATH 和 DEPENDPATH 用于设置头文件目录和项目依赖项目录,都指向项目路径下的 include目录。
注意:要运行应用程序, 还需要将插件的DLL文件复制到编译后的release或debug版本的可执行文件目录下,在本例中就是qwbattryplugin.dll 文件或 qwbatteryplugind.dlI文件,因为应用程序运行需要相应的DLL文件。在应用程序发布时,也需要将DLL文件随同应用程序发布。
自定义Widget插件的功能使得我们可以扩展QtCreator的组件种类,设计自己需要的组件。也有许多第三方Widget 插件可供直接使用,减少自己编程的工作量,例如 QWT 就是一套非常好的开源Widget插件。
6 使用MSVC编译器输出中文的问题
在QtCreator中使用MSVC编译器编译项目时,若处理不当容易出现中文字符串乱码问题。
例如 BatteryUser 项目中,如果槽函数on_battery_powerLevelChanged() 的代码改写为如下的形式,程序运行时,LabInfo 显示的汉字就会出现乱码。
void Widget::on_battery_powerLevelChanged(int arg1)
{
//电量值改变时,在标签中显示
QString str="当前电量:" + QString("%1 %").arg(arg1);
ui->LabInfo->setText(str) ;
}
这是因为Qt Creator 保存的文件使用的是 UTF-8编码 ( 是任何平台、任何语言都可以使用的跨平台的字符集),MSVC编译器虽然可以正常编译 带BOM的UTF-8 编码的源文件,但是生成的
可执行文件的编码是Windows本地字符集,比如GB2312 。也就是在可执行文件中,字符串当
前电量: 是以 GB2312编码 的,而可执行程序执行到这条语句时,对这个字符串却是以UTF-8解
码的,这样就会出现乱码。
网上找到的解决这个问题有两种方法,一种方法是使用QStringLiteral()宏封装字符串(这种方式其实也会乱码,我测试是一样,没什么效果,还是报错,不报错,好的都会变乱码), 另一种方法是强制MSVC编译器生成的可执行文件使用UTF-8编码。
程序中需要使用QStringLiteral()宏对每个中文字符串进行封装,并且不能再使用tr()函数用于翻译字符串。 所以着重看第二种方法
强制MSVC编译器采用UTF-8 编码生成可执行文件,需要在每个使用到中文字符串的头文件和源程序文件的前部加入如下的语句:
#if _MSC_VER >= 1600 //MSVC2015>1899, MSVC_VER=14.0
#pragma execution_ character_ set("utf-8")
#endif
MSVC2010以后的编译器可以使用此方案,这是强制编译后的执行文件采用UTF-8编码。这
样,即使不再使用QStringLiteral()宏,程序运行时也不会再出现汉字乱码的问题了。而且,也可以
采用tr()函数用于翻译字符串。
7 运行效果图
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)