《Qt/UI美化实战课程》新课首发
(1)无边框窗口(11讲)
(2)图标字体(10讲)
(3)官方图表QChart:曲线、柱状图、饼图(20+讲)
(4)第三方图表QCustomPlot:曲线、柱状图、饼图(20+讲)
(5)监控日志高亮(共 14 讲)
(6)仪表盘(16讲)
(7)天气预报(11+讲)
(8)基础控件(15+讲)
(9)高级控件(12+讲)
(10)精美换肤(15+讲)
详情参见个人主页的置顶视频(明王出品,必属精品)
需要系统跟明王学习的小伙伴,可以直接加明王:coding4096
(1)总课时:超 120+ 讲,每日更新
(2)讲课风格:从零新建项目,从零一行行写代码
(3)提供资料:视频教程+配套源码+详细笔记
本章将会从零实现一个自定义控件-仪表盘
,它支持:
✅ 自定义起始角度、圆弧跨度
✅ 自定义圆环宽度、颜色、样式
✅ 支持背景圆环、当前值圆环
✅ 自定义刻度线的颜色、位置、大小刻度的数目
✅ 自定义刻度值的颜色、位置、最大最小值、精度(小数位数)
✅ 自定义显示标题:温度、湿度、电流、电压等(标题完美地显示在缺口
处)
✅ 支持动画特性:从一个值变为另一个值,指针会平滑旋转
✅ 支持仪表盘自适应大小(仪表盘缩放时,圆环宽度、刻度线、刻度值、标题大小等,自适应地缩放)
本章会从零开始,自定义实现一个仪表盘控件。会实现的2个效果:
本节先创建项目,并完成整体界面布局。
1. 新建项目
创建一个名为 GaugeDemo
的项目,直接继承 QWidget
,取消勾选 "Generate form
",手写布局,如下:
创建完成,项目目录如下:
2. 完成布局
界面整体效果,如下:
整体采用水平布局,左侧和右侧均采用垂直布局
首先,把仪表盘的源码文件 gauge.cpp
、gauge.h
拷贝到项目的根目录下,如下:
右键左侧的项目文件名,选择 "添加现有文件...",把这两个文件,添加到项目当中,如下:
然后,来到 widget.h
中,声明布局相关的成员变量和成员函数,如下:
#include <QLineEdit>
#include <QLabel>
#include <QSlider>
#include <QSpinBox>
#include <QComboBox>
#include <QCheckBox>
#include "gauge.h"
class Widget : public QWidget
{
private:
void initLeft();
void initRight();
private:
QWidget* leftWidget;
QWidget* rightWidget;
// 左侧
Gauge* gauge;
QSlider* slider;
// 右侧
// 基础设置
QLineEdit* leMinValue;
QLineEdit* leMaxValue;
QLineEdit* lePrecision;
QLineEdit* leUnit;
QLineEdit* leTitle;
QLabel* lblTitleColor;
// 圆环
QLineEdit* leStartAngle;
QLineEdit* leSpanAngle;
QSpinBox* sbRingWidth;
QLabel* lblRingColor;
QLabel* lblCurrentRingColor;
QComboBox* cboRingStyle;
// 刻度
QLineEdit* leMajorScale;
QLineEdit* leMinorScale;
QLabel* lblScaleColor;
QLabel* lblScaleValuesColor;
QComboBox* cboScalePos;
// 指针
QLabel* lblPointerColor;
QComboBox* cboPointerStyle;
QCheckBox* chkAnimation;
QLineEdit* leAnimationStep;
};
然后,来到 widget.cpp 构造中,实现布局,如下:
#include <QHBoxLayout>
#include <QVBoxLayout>
Widget::Widget(QWidget* parent) : QWidget(parent)
{
this->setWindowTitle("《Qt/UI美化实战课程》-第五章 自定义仪表盘(总课时超120+讲 VX: coding4096)");
this->setStyleSheet("Font: 18px;");
// 1. 布局
QHBoxLayout* mainLayout = new QHBoxLayout(this);
leftWidget = new QWidget(this);
QVBoxLayout* leftLayout = new QVBoxLayout(leftWidget);
leftWidget->setObjectName("leftWidget");
leftWidget->setStyleSheet("QWidget#leftWidget{background-color: rgb( 58, 67, 90);}");
rightWidget = new QWidget(this);
QVBoxLayout* rightLayout = new QVBoxLayout(rightWidget);
rightWidget->setObjectName("rightWidget");
rightWidget->setStyleSheet("QWidget#rightWidget{background-color: rgb( 150, 180,220);}");
initLeft();
initRight();
mainLayout->addWidget(leftWidget);
mainLayout->addWidget(rightWidget);
mainLayout->setStretchFactor(leftWidget, 3);
mainLayout->setStretchFactor(rightWidget, 1);
}
此时,运行效果如下:
接下来分别填充左侧布局和右侧布局
2.1 左侧布局
左侧整体采用垂直布局,上面一个仪表盘,下边一个滑块用于改变仪表盘的数值
来到 widget.cpp
,实现 initLeft()
函数,如下:
void Widget::initLeft()
{
gauge = new Gauge(this);
gauge->setMinimumWidth(300);
slider = new QSlider(this);
slider->setOrientation(Qt::Horizontal);
slider->setRange(0, 100);
leftWidget->layout()->addWidget(gauge);
leftWidget->layout()->addWidget(slider);
}
此时,运行效果:
2.1 右侧布局
右侧整体采用垂直布局,垂直分布着 4
个 GroupBox
,分别用来基础设置、圆环设置、刻度设置、指针设置
来到 widget.cpp
,实现 initRight()
函数,如下:
#include <QGroupBox>
void Widget::initRight()
{
QVBoxLayout* rightLayout = static_cast<QVBoxLayout*>(rightWidget->layout());
// 1. 基础设置
QGroupBox* basicGroup = new QGroupBox("基础设置", this);
QGridLayout* basicLayout = new QGridLayout(basicGroup);
QLabel* lbl1 = new QLabel("范围");
QLabel* lbl2 = new QLabel("小数精度");
QLabel* lbl3 = new QLabel("单位");
QLabel* lbl4 = new QLabel("标题");
QLabel* lbl5 = new QLabel("颜色");
lbl1->setAlignment(Qt::AlignRight);
lbl2->setAlignment(Qt::AlignRight);
lbl3->setAlignment(Qt::AlignRight);
lbl4->setAlignment(Qt::AlignRight);
lbl5->setAlignment(Qt::AlignRight);
lbl1->setMinimumWidth(100);
basicLayout->addWidget(lbl1, 0, 0);
basicLayout->addWidget(lbl2, 1, 0);
basicLayout->addWidget(lbl3, 2, 0);
basicLayout->addWidget(lbl4, 3, 0);
basicLayout->addWidget(lbl5, 4, 0);
QHBoxLayout* rangeLayout = new QHBoxLayout();
leMinValue = new QLineEdit(this);
QLabel* lbl = new QLabel("-", this);
leMaxValue = new QLineEdit(this);
rangeLayout->addWidget(leMinValue);
rangeLayout->addWidget(lbl);
rangeLayout->addWidget(leMaxValue);
lePrecision = new QLineEdit(this);
leUnit = new QLineEdit(this);
leTitle = new QLineEdit(this);
lblTitleColor = new QLabel(this);
basicLayout->addLayout(rangeLayout, 0, 1);
basicLayout->addWidget(lePrecision, 1, 1);
basicLayout->addWidget(leUnit, 2, 1);
basicLayout->addWidget(leTitle, 3, 1);
basicLayout->addWidget(lblTitleColor, 4, 1);
rightLayout->addWidget(basicGroup);
// 2. 圆环设置
QGroupBox* ringGroup = new QGroupBox("圆环", this);
QGridLayout* ringLayout = new QGridLayout(ringGroup);
QLabel* lbl6 = new QLabel("起始角度");
QLabel* lbl7 = new QLabel("跨越角度");
QLabel* lbl8 = new QLabel("宽度");
QLabel* lbl9 = new QLabel("背景颜色");
QLabel* lbl10 = new QLabel("当前颜色");
QLabel* lbl11 = new QLabel("样式");
lbl6->setAlignment(Qt::AlignRight);
lbl7->setAlignment(Qt::AlignRight);
lbl8->setAlignment(Qt::AlignRight);
lbl9->setAlignment(Qt::AlignRight);
lbl10->setAlignment(Qt::AlignRight);
lbl11->setAlignment(Qt::AlignRight);
lbl6->setMinimumWidth(100);
ringLayout->addWidget(lbl6, 0, 0);
ringLayout->addWidget(lbl7, 1, 0);
ringLayout->addWidget(lbl8, 2, 0);
ringLayout->addWidget(lbl9, 3, 0);
ringLayout->addWidget(lbl10, 4, 0);
ringLayout->addWidget(lbl11, 5, 0);
leStartAngle = new QLineEdit(this);
leSpanAngle = new QLineEdit(this);
sbRingWidth = new QSpinBox(this);
lblRingColor = new QLabel(this);
lblCurrentRingColor = new QLabel(this);
cboRingStyle = new QComboBox(this);
ringLayout->addWidget(leStartAngle, 0, 1);
ringLayout->addWidget(leSpanAngle, 1, 1);
ringLayout->addWidget(sbRingWidth, 2, 1);
ringLayout->addWidget(lblRingColor, 3, 1);
ringLayout->addWidget(lblCurrentRingColor, 4, 1);
ringLayout->addWidget(cboRingStyle, 5, 1);
rightLayout->addWidget(ringGroup);
// 3. 刻度设置
QGroupBox* scaleGroup = new QGroupBox("刻度", this);
QGridLayout* scaleLayout = new QGridLayout(scaleGroup);
QLabel* lbl12 = new QLabel("大刻度");
QLabel* lbl13 = new QLabel("小刻度");
QLabel* lbl14 = new QLabel("刻度线颜色");
QLabel* lbl15 = new QLabel("刻度值颜色");
QLabel* lbl16 = new QLabel("位置");
lbl12->setAlignment(Qt::AlignRight);
lbl13->setAlignment(Qt::AlignRight);
lbl14->setAlignment(Qt::AlignRight);
lbl15->setAlignment(Qt::AlignRight);
lbl16->setAlignment(Qt::AlignRight);
lbl12->setMinimumWidth(100);
scaleLayout->addWidget(lbl12, 0, 0);
scaleLayout->addWidget(lbl13, 1, 0);
scaleLayout->addWidget(lbl14, 2, 0);
scaleLayout->addWidget(lbl15, 3, 0);
scaleLayout->addWidget(lbl16, 4, 0);
leMajorScale = new QLineEdit(this);
leMinorScale = new QLineEdit(this);
lblScaleColor = new QLabel(this);
lblScaleValuesColor = new QLabel(this);
cboScalePos = new QComboBox(this);
scaleLayout->addWidget(leMajorScale, 0, 1);
scaleLayout->addWidget(leMinorScale, 1, 1);
scaleLayout->addWidget(lblScaleColor, 2, 1);
scaleLayout->addWidget(lblScaleValuesColor, 3, 1);
scaleLayout->addWidget(cboScalePos, 4, 1);
rightLayout->addWidget(scaleGroup);
// 4. 指针设置
QGroupBox* pointerGroup = new QGroupBox("指针", this);
QGridLayout* pointerLayout = new QGridLayout(pointerGroup);
QLabel* lbl17 = new QLabel("颜色");
QLabel* lbl18 = new QLabel("样式");
QLabel* lbl19 = new QLabel("动画");
QLabel* lbl20 = new QLabel("动画步长");
lbl17->setAlignment(Qt::AlignRight);
lbl18->setAlignment(Qt::AlignRight);
lbl19->setAlignment(Qt::AlignRight);
lbl20->setAlignment(Qt::AlignRight);
lbl17->setMinimumWidth(100);
pointerLayout->addWidget(lbl17, 0, 0);
pointerLayout->addWidget(lbl18, 1, 0);
pointerLayout->addWidget(lbl19, 2, 0);
pointerLayout->addWidget(lbl20, 3, 0);
lblPointerColor = new QLabel(this);
cboPointerStyle = new QComboBox(this);
chkAnimation = new QCheckBox(this);
leAnimationStep = new QLineEdit(this);
pointerLayout->addWidget(lblPointerColor, 0, 1);
pointerLayout->addWidget(cboPointerStyle, 1, 1);
pointerLayout->addWidget(chkAnimation, 2, 1);
pointerLayout->addWidget(leAnimationStep, 3, 1);
rightLayout->addWidget(pointerGroup);
// 5. 弹簧
QSpacerItem* item = new QSpacerItem(1, 1, QSizePolicy::Fixed, QSizePolicy::Expanding);
rightLayout->addItem(item);
}
此时,运行效果: