1 前言
当UI设计器提供的界面组件不满足实际设计需求时,可以从QWidget继承自定义界面组件。有两种方法使用自定义界面组件,一种 是提升法( promotion),例如前面有一个demo中QGraphicsView组件提升为自定义的QWGraphicsView类,提升法用于界面可视化设计时不够直观,不能在界面上即刻显示自定义组件的效果;另一种是为UI设计器设计自定义界面组件的Widget插件,直接安装到UI设计器的组件面板里,如同Qt自带的界面设计组件一样使用,在设计时就能看到组件的实际显示效果,只是编译和运行时需要使用到插件的动态链接库(Windows平台上)。
本节将先介绍这两种自定义Widget组件的设计和使用方法,再介绍Qt编写和使用静态链接库和共享库(Windows 平台上就是动态链接库)的方法。
公众号:Qt实战,各种开源作品、经验整理、项目实战技巧,专注Qt/C++软件开发,视频监控、物联网、工业控制、嵌入式软件、国产化系统应用软件开发。
公众号:Qt入门和进阶,专门介绍Qt/C++相关知识点学习,帮助Qt开发者更好的深入学习Qt。多位Qt元婴期大神,一步步带你从入门到进阶,走上财务自由之路。
官方店:https://shop114595942.taobao.com//
2 自定义Widget组件
2.1 自定义Widget子类MyBattery
Qt的UI设计器提供了很多GUI设计的界面组件,可以满足常见的界面设计需求。但是某些时候需要设计特殊的界面组件,而在UI设计器的组件面板里根本没有合适的组件,这时就需要设计自定义的界面组件。
所有界面组件的基类是QWidget,要设计自定义的界面组件,可以从 QWidget 继承一个自定义的类,重定义其 paintEvent() 事件,利用Qt的绘图功能绘制组件外观,并实现需要的其他功能。
例如,假设需要设计一个如下图所示的电池电量显示组件,用于电池使用或充电时显示其电量,但是在UI设计器的组件面板里是没有这样一个现成的组件的。这就需要设计一个自定义的Widget组件。
为此,设计一个从QWidget继承的类MyBattery。创建C++类,可以单击 Qt Creator 的 File →New File or Project 菜单项,在出现的对话框里选择C++组里的C++ Class,在向导中设置类的名称,并选择基类为QWidget。
定义 MyBattery 类的 MyBattery.h 文件的完整代码如下:
#include <QWidget>
#include <QColor>
QT_BEGIN_NAMESPACE
namespace Ui { class MyBattery; }
QT_END_NAMESPACE
class MyBattery : public QWidget
{
Q_OBJECT
public:
MyBattery(QWidget *parent = nullptr);
~MyBattery();
void setPowerLevel(int pow); //设置当前电量
int powerLevel();
void setWarnLevel (int warn); //设置电量低阈值
int warnLevel ();
virtual QSize sizeHint();//缺省大小
protected:
void paintEvent(QPaintEvent * event) Q_DECL_OVERRIDE;
signals :
void powerLevelChanged(int);
private:
Ui::MyBattery *ui;
QColor mColorBack = Qt::white;//背景颜色
QColor mColorBorder = Qt::black; //电池边框颜色
QColor mColorPower = Qt::green; //电量柱颜色
QColor mColorWarning = Qt::red;//电量短缺时的颜色
int mPowerLevel=60;//电量0-100
int mWarnLevel=20; //电量低警示阈值
};
在 private 部分定义了几个私有变量,主要是各种颜色的定义、当前电量值 mPowerLevel 和电量低阈值 mWarnLevel。
protected 部分重定义了 paintEvent() 事件,QWidget 类的 paintEvent() 事件用于界面绘制,在此事件里,可以使用 QPainter 的各种绘图功能绘制自己需要的界面。
public 部分定义了用于读取和设置当前电量值、电量低阈值的函数,还定义了 sizeHint() 函数,用于返回组件缺省大小。
定义了一个信号 powerLevelChanged(int),在当前电量值改变时发射该信号,使用MyBattery 类时可以设计槽函数对此信号做处理。
下面是MyBattery类的实现代码,复杂一点的部分是 paintEvent() 事件函数里绘制界面的功能实现,这里设置了窗口逻辑坐标,所以,当组件大小变化时,绘制的电池大小也会自动变化。
#include "MyBattery.h"
#include "ui_MyBattery.h"
#include <QPainter>
MyBattery::MyBattery(QWidget *parent)
: QWidget(parent)
, ui(new Ui::MyBattery)
{
ui->setupUi(this);
}
MyBattery::~MyBattery()
{
delete ui;
}
void MyBattery::setPowerLevel(int pow)
{
//设置当前电量值
mPowerLevel=pow;
emit powerLevelChanged(pow) ; //发射信号
repaint ();
}
int MyBattery::powerLevel()
{
//返回当前电量值
return mPowerLevel;
}
void MyBattery::setWarnLevel(int warn)
{
//设置电量低阈值
mWarnLevel =warn;
repaint();
}
int MyBattery::warnLevel()
{
//返回电量低阈值
return mWarnLevel ;
}
QSize MyBattery::sizeHint()
{
//缺省大小,调整比例
int H=this->height();
int W=H*12/5;
QSize size(W,H);
return size;
}
void MyBattery::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
//界面组件绘制
QPainter painter(this);
QRect rect(0,0, width(), height());
painter.setViewport(rect);
painter.setWindow(rect);
painter.setRenderHint(QPainter::Antialiasing);
painter.setRenderHint(QPainter::TextAntialiasing);
//! 绘制电池边框
//设置画笔
QPen pen;
pen.setWidth(2);
pen.setColor(mColorBorder);
pen.setStyle(Qt::SolidLine);
pen.setCapStyle(Qt::FlatCap);
pen.setJoinStyle(Qt::BevelJoin);
painter.setPen(pen);
//画刷
QBrush brush;
brush.setColor(mColorBack);
brush.setStyle(Qt::SolidPattern);
painter.setBrush(brush);
rect.setRect(1,1,109,48);
//绘制电池边框
painter.drawRect(rect);
brush.setColor(mColorBorder);
painter.setBrush(brush);
rect.setRect(110,15,10,20);
//画电池正极头
painter.drawRect(rect);
//画电池柱
if(mPowerLevel > mWarnLevel)
{
//正常颜色电量柱
brush.setColor(mColorPower);
pen.setColor(mColorPower);
}
else
{
//电量低电量柱
brush.setColor(mColorWarning);
pen.setColor(mColorWarning);
}
painter.setBrush(brush);
painter.setPen(pen);
if(mPowerLevel > 0)
{
//画电池柱
rect.setRect(5,5,mPowerLevel, 40);
painter.drawRect(rect);
}
//绘制电量百分比文字
QFontMetrics textsize(this->font());
QString powStr = QString("%1 %%").arg(mPowerLevel);
//得到字符串的矩形
QRect textRect = textsize.boundingRect(powStr);
painter.setFont(this->font());
pen.setColor(mColorBorder);
painter.setPen(pen);
painter.drawText(55-textRect.width()/2,23+textRect.height()/2,powStr);
}
2.2 自定义Widget组件的使用
实现了 MyBattery 类之后,若是用代码创建 MyBattery 类对象,其使用与一般的组件类是一样的;若是在UI设计器中使用QmyBattery,则需要采用提升法(promotion)。
实例程序是一个基于 QWidegt 的应用程序,使用UI设计器设计主窗体时,在窗体上放置一个QWidegt 类组件,然后鼠标右键调出其快捷菜单,单击 提升为(Promote to) 菜单项,会出现如下图所示的对话框。
此对话框里,在基类名称下拉列表框里选择QWidget,将提升后的类名称设置为MyBattery ,头文件名称会自动生成。可以将设置添加到已提升类的列表里,以便重复使用。设置后,单击 提升(Promote) 按钮,就可以将此 QWidget 组件提升为 MyBattery 类。提升后,在Property Editor 里,会看到这个组件的类名称变为了 MyBattery。然后,将其 objectName 更改为 battery。
虽然界面上放置的 QWidget 组件被提升为了 MyBattery 类,但是在这个组件的 跳转到槽(Gotoslot) 对话框里并没有 MyBattery 类的 powerLevelChanged(int) 信号,无法采用可视化方法生成信号的槽函数。
在主窗口上放置一个 QSlider 组件和一个 QLabel 组件。滑动标尺改变数值时,设置为 battery 的当前电量值,其 valueChanged() 信号的槽函数代码如下:
void WidgetUI::on_horizontalSlider_valueChanged(int value)
{
//滑动改变电量值
ui->battery->setPowerLevel(value);
QString str = QStringLiteral("当前电量:%1 %").arg(value);
ui->label_curPowStr->setText(str);
}
实例运行时就可以得到如上图所示的运行界面。battery的各种参数采用其缺省的设置,
battery的当前电量值改变时,内部会调用 paintEvent() 事件代码重新绘制电池显示效果。