在 Qt 开发中,自定义 Widget 是实现个性化 UI 组件的核心方式,本文以QmyBattery电池组件为例,详细讲解如何基于QWidget子类化实现具备电量显示、警告阈值、样式自定义的电池控件,涵盖核心绘制逻辑、属性封装、尺寸适配等关键知识点。
一、组件设计思路

QmyBattery组件需实现以下核心功能:
- 可视化展示电池外形(主体 + 正极);
- 按百分比显示电量填充效果,支持正常 / 警告颜色切换;
- 封装电量、警告阈值等属性,支持外部设置 / 获取;
- 自适应控件尺寸,保持电池外观比例协调;
- 发射电量变化信号,支持与其他组件联动。
二、核心代码实现
1. 头文件(QmyBattery.h)
cpp
#ifndef QMYBATTERY_H
#define QMYBATTERY_H
#include <QWidget>
#include <QPainter>
#include <QFontMetrics>
class QmyBattery : public QWidget
{
Q_OBJECT
// 注册属性
Q_PROPERTY(int powerLevel READ powerLevel WRITE setPowerLevel NOTIFY powerLevelChanged)
Q_PROPERTY(int warnLevel READ warnLevel WRITE setWarnLevel)
Q_PROPERTY(QColor colorBorder READ colorBorder WRITE setColorBorder)
Q_PROPERTY(QColor colorPower READ colorPower WRITE setColorPower)
Q_PROPERTY(QColor colorWarning READ colorWarning WRITE setColorWarning)
Q_PROPERTY(QColor colorBack READ colorBack WRITE setColorBack)
public:
explicit QmyBattery(QWidget *parent = nullptr);
// 属性获取接口:
int powerLevel(); // 获取当前电量
int warnLevel(); // 获取警告阈值
QColor colorBorder(); // 获取边框颜色
QColor colorPower(); // 获取正常电量颜色
QColor colorWarning(); // 获取警告颜色
QColor colorBack(); // 获取背景颜色
// 属性设置接口:
void setPowerLevel(int pow); // 设置电量(触发重绘+信号)
void setWarnLevel(int warn); // 设置警告阈值(触发重绘)
void setColorBorder(QColor color); // 修正拼写
void setColorPower(QColor color);
void setColorWarning(QColor color);
void setColorBack(QColor color);
// 重写尺寸提示,适配电池比例
QSize sizeHint()const override;
signals:
void powerLevelChanged(int pow); // 电量变化信号
protected:
// 重写绘制事件,实现电池可视化
void paintEvent(QPaintEvent *event) override;
private:
// 成员变量:
int mPowerLevel = 60; // 默认电量60%
int mWarnLevel = 20; // 默认警告阈值20%
QColor mColorBorder = Qt::black; // 边框颜色
QColor mColorPower = Qt::green; // 正常电量颜色
QColor mColorWarning = Qt::red; // 警告颜色
QColor mColorBack = Qt::white; // 背景颜色
};
#endif // QMYBATTERY_H
2. 源文件(QmyBattery.cpp)
cpp
#include "QmyBattery.h"
QmyBattery::QmyBattery(QWidget *parent) : QWidget(parent)
{
// 初始化无额外逻辑,属性使用默认值
}
// ========== 属性获取接口 ==========
int QmyBattery::powerLevel()
{
return mPowerLevel;
}
int QmyBattery::warnLevel()
{
return mWarnLevel;
}
QColor QmyBattery::colorBorder()
{
return mColorBorder;
}
QColor QmyBattery::colorPower()
{
return mColorPower;
}
QColor QmyBattery::colorWarning()
{
return mColorWarning;
}
QColor QmyBattery::colorBack()
{
return mColorBack;
}
// ========== 属性设置接口 ==========
void QmyBattery::setPowerLevel(int pow)
{
// 限制电量范围0-100,避免绘制异常
mPowerLevel = qBound(0, pow, 100);
emit powerLevelChanged(mPowerLevel); // 发射电量变化信号
repaint(); // 触发重绘,更新显示
}
void QmyBattery::setWarnLevel(int warn)
{
mWarnLevel = qBound(0, warn, 100); // 限制阈值范围0-100
repaint();
}
void QmyBattery::setColorBorder(QColor color)
{
mColorBorder = color;
repaint();
}
void QmyBattery::setColorPower(QColor color)
{
mColorPower = color;
repaint();
}
void QmyBattery::setColorWarning(QColor color)
{
mColorWarning = color;
repaint();
}
void QmyBattery::setColorBack(QColor color)
{
mColorBack = color;
repaint();
}
// ========== 尺寸适配 ==========
QSize QmyBattery::sizeHint() const
{
// 保持电池宽高比12:5,适配任意控件高度
int H = this->height();
int W = H * 12 / 5;
return QSize(W, H);
}
// ========== 核心绘制逻辑 ==========
void QmyBattery::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event); // 标记参数未使用,消除编译器警告
QPainter painter(this);
// 抗锯齿,使绘制边缘更平滑
painter.setRenderHint(QPainter::Antialiasing);
painter.setRenderHint(QPainter::TextAntialiasing);
// 1. 设置绘制坐标系(视口+窗口,固定电池比例)
QRect rect(0, 0, width(), height());
painter.setViewport(rect); // 物理像素区域
painter.setWindow(0, 0, 120, 50); // 逻辑坐标系(120x50)
// 2. 绘制电池背景
QBrush brush;
QPen pen;
pen.setWidth(2);
pen.setColor(mColorBorder); // 修正:mColorBoder → mColorBorder
pen.setStyle(Qt::SolidLine);
painter.setPen(pen);
brush.setColor(mColorBack);
brush.setStyle(Qt::SolidPattern);
painter.setBrush(brush);
// 绘制电池主体背景
QRect batteryRect(1, 1, 109, 48);
painter.drawRect(batteryRect);
// 绘制电池正极(顶部小矩形)
QRect positiveRect(110, 15, 10, 20);
painter.drawRect(positiveRect);
// 3. 绘制电量填充
if (mPowerLevel > mWarnLevel)
{
brush.setColor(mColorPower); // 正常电量颜色
pen.setColor(mColorPower);
}
else
{
brush.setColor(mColorWarning); // 警告颜色
pen.setColor(mColorWarning);
}
painter.setBrush(brush);
painter.setPen(pen);
// 电量>0时绘制填充矩形(宽度对应电量百分比)
if (mPowerLevel > 0)
{
QRect powerRect(5, 5, mPowerLevel, 40);
painter.drawRect(powerRect);
}
// 4. 绘制电量百分比文字(居中显示)
QFontMetrics textSize(this->font());
QString powStr = QString::asprintf("%d%%", mPowerLevel);
QRect textRect = textSize.boundingRect(powStr);
painter.setFont(this->font());
pen.setColor(mColorBorder);
painter.setPen(pen);
// 文字居中计算:水平55-文字宽/2,垂直23+文字高/2
painter.drawText(55 - textRect.width()/2, 23 + textRect.height()/2, powStr);
}
三、核心知识点解析
1. 坐标系适配(setViewport/setWindow)
通过setViewport(绑定控件物理尺寸)和setWindow(固定逻辑尺寸 120x50),实现电池控件无论拉伸 / 缩小时,始终保持 12:5 的宽高比,避免变形。
2. 绘制分层逻辑
组件绘制分为 4 层,层层叠加保证视觉效果:
- 第 1 层:电池背景(白色 / 自定义背景色);
- 第 2 层:电池边框 + 正极(黑色 / 自定义边框色);
- 第 3 层:电量填充(绿色 / 红色,按阈值切换);
- 第 4 层:电量百分比文字(居中显示,边框色保证对比度)。
3. 属性封装与 Qt 属性系统
通过Q_PROPERTY注册属性,使组件支持 Qt 属性系统:
- 可在 UI 设计师中直接编辑电量、颜色等属性;
- 支持信号与槽联动,如
powerLevelChanged信号可绑定到其他组件响应电量变化。
4. 尺寸适配(sizeHint)
重写sizeHint返回按 12:5 比例计算的推荐尺寸,布局管理器(如 QVBoxLayout、QHBoxLayout)会优先使用该尺寸,保证组件布局协调。
四、组件使用示例
实现了QmyBattery类之后,若是用代码创建QmyBattery类对象,其使用与一般的组件类是一样的,若是在UI设计器中使用QmyBattery,则需要采用提升法,在窗体上放置一个QWidget类组件,然后鼠标右键调出菜单"提升为..."

在基类名称下选择QWidget,将提升后的类名称设置为QmyBattery,单击Add ,单击Promote,提升后在属性将组件名称修改为qmyBattery


再放置4个按钮用来测试这个电池组件。
cpp
void MainWindow::on_pushButton_clicked()
{
ui->qmyBattery->setPowerLevel(70);
}
void MainWindow::on_pushButton_2_clicked()
{
ui->qmyBattery->setPowerLevel(10);
}
void MainWindow::on_pushButton_3_clicked()
{
ui->qmyBattery->setPowerLevel(100);
}
void MainWindow::on_pushButton_4_clicked()
{
ui->qmyBattery->setPowerLevel(0);
}

默认电量60%

设置电量100%

设置电量70%

设置电量10%

设置电量0
完整的代码下载地址:Qt中自定义QmyBattery电池组件开发代码资源-CSDN下载
总结
QmyBattery组件通过子类化QWidget,重写paintEvent实现核心绘制逻辑,封装属性与信号保证组件的可复用性和交互性。核心要点包括:
- 利用 Qt 绘图框架实现分层绘制,保证视觉层次清晰;
- 通过坐标系适配和
sizeHint实现组件尺寸自适应; - 遵循 Qt 属性系统设计规范,提升组件易用性;
- 核心逻辑解耦(绘制 / 属性 / 信号分离),便于扩展维护。
该组件可直接集成到 Qt 项目中,适用于电量显示、设备状态监控等场景,是 Qt 自定义 Widget 开发的典型实践。