在 Qt6 中,自定义控件可以通过继承现有控件类或组合多个控件来实现,从而满足特定的 UI 需求并提高代码复用性。常见方式包括直接代码实现和在 Qt Designer 中使用“提升”功能。
方式1:Qt Designer 提升法
1.右键项目,添加一个Qt Widgets Designer Form Class

2.新建一个名为SmallWidget的ui类,然后双击ui文件打开Qt Designer进行界面编辑,拖动想要的控件到界面上

3.选中SmallWidget设置布局,然后属性编辑器中调整margin均为0,缩放SmallWidget至想要的大小。

4.第三步完成后其实就可以参考以下代码使用这个控件了,但是局限是不能在Qt Designer中进行拖拽,而且要手动实现布局,开发效率低下。
#include "widget.h"
#include "./ui_widget.h"
#include <QVBoxLayout>
#include "SmallWidget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QVBoxLayout *vbox=new QVBoxLayout(this);
SmallWidget *myWidget=new SmallWidget(this);
vbox->addWidget(myWidget);
}
Widget::~Widget()
{
delete ui;
}
效果:

5.继续我们的提升法,双击打开需要使用SmallWidget控件的ui界面

6.从左侧拖动一个Widget控件到ui上作为一个占位控件,后续这个占位控件会被提升后的控件代替。

7.右键这个widget,点击提升为…

8.基类名称选择QWidget,提升的类名称和头文件与控件类SmallWidget一致,然后点击添加
注意事项:自定义控件SmallWidget的直接父类(基类名称,这里是QWidget)必须和占位控件的类型(这里是QWidget)是 “父子关系”(占位控件可以是基类本身,或基类的父类,如基类是QPushButton,占位控件可以是QWidget),且基类名称(这里是QWidget)要和自定义控件(SmallWidget)的父类(QWidget)完全一致;
最佳实践:自定义控件基类,占位控件类型,基类名称 三者保持一致,绝对不会出错。

9.选中该控件类,点击提升

10.此时这个QWidget类变成了SmallWidget类,提升法成功了

11.点击编译运行,发现出现如下报错,原因是编译器没有找到头文件,需要在工程文件CMakeList.txt中添加下面几行代码。

include_directories(
${CMAKE_SOURCE_DIR} # 如果头文件在根目录,即工程文件同级目录
# ${CMAKE_SOURCE_DIR}/src # 如果头文件在src子目录(根据实际路径调整)
# 如有其他目录,继续添加:${CMAKE_SOURCE_DIR}/controls
)
12.重新编译,运行成功,原来widget的位置变成了自定义的SmallWidget

13.后续可以继续将多个widget拖动至ui界面,然后对多个同时进行提升(之前提升过的控件无需再添加)


14.编译运行,发现4个widget都成功被提升成了SmallWidget

15.此时我们可以编辑SmallWidget类文件,为这个静态的控件添加一些简单的功能,实现slider和spanBox保持数据同步(改变spanBox时slider会同步改变,拖动slider时spanBox也会同步该百年)
#include "SmallWidget.h"
#include "ui_SmallWidget.h"
SmallWidget::SmallWidget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::SmallWidget)
{
ui->setupUi(this);
connect(ui->spinBox,&QSpinBox::valueChanged,ui->horizontalSlider,&QSlider::setSliderPosition);
connect(ui->horizontalSlider,&QSlider::valueChanged,ui->spinBox,&QSpinBox::setValue);
}
SmallWidget::~SmallWidget()
{
delete ui;
}
16.编译运行,效果如下:

17.同时还可以添加一些信号槽可以供外部使用:
//SmallWidget.h
#ifndef SMALLWIDGET_H
#define SMALLWIDGET_H
#include <QWidget>
namespace Ui {
class SmallWidget;
}
class SmallWidget : public QWidget
{
Q_OBJECT
public:
explicit SmallWidget(QWidget *parent = nullptr);
~SmallWidget();
public slots:
void SetNum(int num);
int GetNum();
private:
Ui::SmallWidget *ui;
};
#endif // SMALLWIDGET_H
//SmallWidget.cpp
#include "SmallWidget.h"
#include "ui_SmallWidget.h"
SmallWidget::SmallWidget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::SmallWidget)
{
ui->setupUi(this);
connect(ui->spinBox,&QSpinBox::valueChanged,ui->horizontalSlider,&QSlider::setSliderPosition);
connect(ui->horizontalSlider,&QSlider::valueChanged,ui->spinBox,&QSpinBox::setValue);
}
void SmallWidget::SetNum(int num)
{
ui->spinBox->setValue(num);
}
int SmallWidget::GetNum()
{
return ui->spinBox->value();
}
SmallWidget::~SmallWidget()
{
delete ui;
}
方式2:继承与绘制法
1.右键项目添加新文件,这里选择C++ Class

2.创建一个CircleButton类,继承自基类QWidget

3.重写绘图事件,添加并完成相关信号槽功能
#ifndef CIRCLEBUTTON_H
#define CIRCLEBUTTON_H
#include <QWidget>
#include <QMouseEvent>
#include <QPainter>
class CircleButton : public QWidget
{
Q_OBJECT
public:
explicit CircleButton(QWidget *parent = nullptr);
// 自定义属性:按钮文字
void setText(const QString &text);
QString text() const;
signals:
void clicked();
protected:
// 核心:重写绘图事件(必须)
void paintEvent(QPaintEvent *event) override;
// 可选:重写鼠标事件,实现交互
void mousePressEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
private:
QString m_text; // 按钮文字
bool m_isPressed; // 标记按钮是否被按下
};
#endif // CIRCLEBUTTON_H
#include "CircleButton.h"
CircleButton::CircleButton(QWidget *parent)
: QWidget{parent},
m_isPressed(false)
{
// 设置控件最小尺寸,避免显示异常
setMinimumSize(100, 100);
// 允许接收鼠标事件
setMouseTracking(true);
}
void CircleButton::setText(const QString &text)
{
m_text = text;
update(); // 触发重绘,更新显示
}
QString CircleButton::text() const
{
return m_text;
}
// 核心:重写paintEvent实现自定义绘制
void CircleButton::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event); // 忽略未使用的参数
// 1. 创建画笔(必须在paintEvent内创建,且关联当前控件)
QPainter painter(this);
// 抗锯齿:让绘制的图形边缘更平滑(可选,但推荐)
painter.setRenderHint(QPainter::Antialiasing);
// 2. 绘制按钮背景(圆形)
QPen pen; // 钢笔:控制轮廓
if (m_isPressed) {
// 按下状态:红色轮廓
pen.setColor(Qt::red);
} else {
// 正常状态:黑色轮廓
pen.setColor(Qt::black);
}
pen.setWidth(2); // 轮廓宽度
painter.setPen(pen);
// 绘制圆形:圆心在控件中心,半径为控件最小边长的一半减2(留边距)
int radius = qMin(width(), height()) / 2 – 2;
painter.drawEllipse(rect().center(), radius, radius);
// 3. 绘制按钮文字(居中)
painter.setPen(Qt::white); // 文字颜色
painter.setFont(QFont("Microsoft YaHei", 12)); // 字体
// 文字居中显示
painter.drawText(rect(), Qt::AlignCenter, m_text);
}
// 重写鼠标按下事件
void CircleButton::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
m_isPressed = true;
update(); // 触发重绘,显示按下状态
}
QWidget::mousePressEvent(event); // 调用父类事件,保证事件传递
}
// 重写鼠标释放事件
void CircleButton::mouseReleaseEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
m_isPressed = false;
update(); // 触发重绘,恢复正常状态
emit clicked(); // 发送自定义点击信号(如需)
}
QWidget::mouseReleaseEvent(event);
}
4.在需要使用CircleButton的类中引入头文件,然后通过new CircleButton(this)添加到布局即可
#include "widget.h"
#include "./ui_widget.h"
#include "CircleButton.h"
#include <QVBoxLayout>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QVBoxLayout *vbox=new QVBoxLayout(this);
CircleButton *myButton=new CircleButton(this);
vbox->addWidget(myButton);
}
Widget::~Widget()
{
delete ui;
}
5.编译运行,实现效果如下:

6.还可以结合提升法在 Designer 中使用。
网硕互联帮助中心



评论前必须登录!
注册