仪表盘在很多汽车和物联网相关的系统中很常用,本文就来介绍一下Qt 仪表盘的实现示例,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下。
一、简述
使用Qt绘制一个仪表盘,用来显示当前的温度,绘制刻度、绘制数字、绘制温度指针。仪表盘全程使用QPainter进行绘制,QPainter是Qt框架中非常重要的一个类,绘制功能的实现离不开它。如果想要使用Qt进行高质量的绘图或UI设计,必须掌握QPainter的使用方法。
二、 设计思路
Qt绘图是通过重写paintEvent(QPaintEvent *event)来实现的,调用这个函数有两种方式:update()和repaint()。绘制的步骤分以下几步:
1、窗体绘制准备
我们都知道,Qt的窗体是方形的,但是我们最终的仪表盘肯定是圆形的,所以在绘制之前我们要将窗体整个设置为透明,然后我们再在上面绘制,看起来的效果就好像控件窗体是圆形的了,其实它还是方形的。
2、绘制表盘
3、绘制刻度
4、绘制文字:我们需要绘制的有两部分:第一部分实时的数值,第二部分是刻度对应的数值。
5、绘制指针
三、效果
四、核心代码
1、头文件
#ifndef DASHBOARDWIDGET_H
#define DASHBOARDWIDGET_H
#include <QWidget>
struct DashBoardWidgetPrivate;
class DashBoardWidget : public QWidget
{
Q_OBJECT
Q_PROPERTY(double value READ value WRITE setValue)
Q_PROPERTY(double min READ min WRITE setMin)
Q_PROPERTY(double max READ max WRITE setmax)
Q_PROPERTY(double startAngle READ startAngle WRITE setStartAngle)
Q_PROPERTY(double endAngle READ endAngle WRITE setEndAngle)
Q_PROPERTY(int scaleMajor READ scaleMajor WRITE setScaleMajor)
Q_PROPERTY(int scaleMinor READ scaleMinor WRITE setScaleMinor)
Q_PROPERTY(QString unit READ unit WRITE setUnit)
Q_PROPERTY(QString text READ text WRITE setText)
Q_PROPERTY(QColor arcColor READ arcColor WRITE setArcColor)
Q_PROPERTY(QColor scaleColor READ scaleColor WRITE setScaleColor)
Q_PROPERTY(QColor pointerColor READ pointerColor WRITE setPointerColor)
Q_PROPERTY(QColor textColor READ textColor WRITE setTextColor)
Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor)
public:
explicit DashBoardWidget(QWidget *parent = nullptr);
~DashBoardWidget();
QSize sizeHint() const override;
QSize minimumSizeHint() const override;
double value() const;
void setValue(const double value);
void setMin(const double min);
double min() const;
void setmax(const double max);
double max() const;
void setStartAngle(const double startAngle);
double startAngle() const;
void setEndAngle(const double endAngle);
double endAngle() const;
void setScaleMajor(const int scale);
int scaleMajor() const;
void setScaleMinor(const int scale);
int scaleMinor() const;
void setUnit(const QString &unit);//设置单位
QString unit() const;
void setText(const QString &text);
QString text() const;
void setArcColor(const QColor &color);
QColor arcColor() const;
void setScaleColor(const QColor &color);
QColor scaleColor() const;
void setPointerColor(const QColor &color);
QColor pointerColor() const;
void setTextColor(const QColor &color);
QColor textColor() const;
void setBackgroundColor(const QColor &color);
QColor backgroundColor() const;
signals:
void valueChanged(const double value);
private slots:
void onStartAnimation(const double value);
protected:
void paintEvent(QPaintEvent *event) override;
private:
void drawArc(QPainter *painter);
void drawScale(QPainter *painter);
void drawScaleNum(QPainter *painter);
void drawPointer(QPainter *painter);
void drawValue(QPainter *painter);
QScopedPointer<DashBoardWidgetPrivate> d;
};
#endif // DASHBOARDWIDGET_H
2、实现代码
#include "dashboardwidget.h"
#include <QPainter>
#include <QPropertyAnimation>
#include <QtMath>
struct DashBoardWidgetPrivate{
int maxValue = 100;
int minValue = 0;
double startAngle = -50;
double endAngle = 230;
int value = minValue;
int scaleMajor = 10;
int scaleMinor = 5;
QString unit = "unit";
QString text = "test";
QColor arcColor = QColor(56, 61, 74);
QColor scaleColor = QColor(4, 168, 173);
QColor pointerColor = QColor(4, 181, 200);
QColor textColor = QColor(144, 133, 116);
QColor backgroundColor = Qt::transparent;
QPropertyAnimation *animation;
};
DashBoardWidget::DashBoardWidget(QWidget *parent)
: QWidget(parent)
, d(new DashBoardWidgetPrivate)
{
d->animation = new QPropertyAnimation(this, "value", this);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
connect(this, &DashBoardWidget::valueChanged, this, &DashBoardWidget::onStartAnimation);
}
DashBoardWidget::~DashBoardWidget()
{
}
QSize DashBoardWidget::sizeHint() const
{
return QSize(300, 300);
}
QSize DashBoardWidget::minimumSizeHint() const
{
return QSize(200, 200);
}
void DashBoardWidget::setMin(const double min)
{
d->minValue = min;
update();
}
double DashBoardWidget::min() const
{
return d->minValue;
}
void DashBoardWidget::setmax(const double max)
{
d->maxValue = max;
update();
}
double DashBoardWidget::max() const
{
return d->maxValue;
}
void DashBoardWidget::setStartAngle(const double startAngle)
{
d->startAngle = startAngle;
update();
}
double DashBoardWidget::startAngle() const
{
return d->startAngle;
}
void DashBoardWidget::setEndAngle(const double endAngle)
{
d->endAngle = endAngle;
update();
}
double DashBoardWidget::endAngle() const
{
return d->endAngle;
}
void DashBoardWidget::setScaleMajor(const int scale)
{
d->scaleMajor = scale;
update();
}
int DashBoardWidget::scaleMajor() const
{
return d->scaleMajor;
}
void DashBoardWidget::setScaleMinor(const int scale)
{
d->scaleMinor = scale;
update();
}
int DashBoardWidget::scaleMinor() const
{
return d->scaleMinor;
}
void DashBoardWidget::setUnit(const QString &unit)
{
d->unit = unit;
update();
}
QString DashBoardWidget::unit() const
{
return d->unit;
}
void DashBoardWidget::setText(const QString &text)
{
d->text = text;
update();
}
QString DashBoardWidget::text() const
{
return d->text;
}
void DashBoardWidget::setArcColor(const QColor &color)
{
d->arcColor = color;
update();
}
QColor DashBoardWidget::arcColor() const
{
return d->arcColor;
}
void DashBoardWidget::setScaleColor(const QColor &color)
{
d->scaleColor = color;
update();
}
QColor DashBoardWidget::scaleColor() const
{
return d->scaleColor;
}
void DashBoardWidget::setPointerColor(const QColor &color)
{
d->pointerColor = color;
update();
}
QColor DashBoardWidget::pointerColor() const
{
return d->pointerColor;
}
void DashBoardWidget::setTextColor(const QColor &color)
{
d->textColor = color;
update();
}
QColor DashBoardWidget::textColor() const
{
return d->textColor;
}
void DashBoardWidget::setBackgroundColor(const QColor &color)
{
d->backgroundColor = color;
update();
}
QColor DashBoardWidget::backgroundColor() const
{
return d->backgroundColor;
}
void DashBoardWidget::paintEvent(QPaintEvent *event)
{
QWidget::paintEvent(event);
QPainter painter(this);
painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
// 背景
if (d->backgroundColor != Qt::transparent) {
painter.setPen(Qt::NoPen);
painter.fillRect(rect(), d->backgroundColor);
}
// 平移中心
painter.translate(width() / 2, height() / 2);
// 圆环
drawArc(&painter);
// 刻度线
drawScale(&painter);
// 刻度值
drawScaleNum(&painter);
// 指示器
drawPointer(&painter);
// 当前值
drawValue(&painter);
}
void DashBoardWidget::onStartAnimation(const double value)
{
if(value < d->minValue
|| value > d->maxValue
|| value == d->value)
return;
int start = d->value;
int end = value;
d->animation->setStartValue(start);
d->animation->setEndValue(end);
d->animation->start();
}
double DashBoardWidget::value() const
{
return d->value;
}
void DashBoardWidget::setValue(const double value)
{
d->value = value;
update();
}
void DashBoardWidget::drawArc(QPainter *painter)
{
double min = qMin(width(), height());
double arcWidth = min / 15.0;
double radius = min / 3 - arcWidth;
QRectF rect = QRectF(-radius, -radius, radius * 2, radius * 2);
QPen pen;
pen.setWidthF(arcWidth);
pen.setCapStyle(Qt::FlatCap);
// 圆弧背景
double angle = d->endAngle - d->startAngle;
pen.setColor(d->arcColor);
painter->setPen(pen);
painter->drawArc(rect, d->startAngle * 16, angle * 16);
}
void DashBoardWidget::drawScale(QPainter *painter)
{
painter->save();
painter->rotate(270 - d->endAngle);
int steps = (d->scaleMajor * d->scaleMinor);
double angleStep = (d->endAngle - d->startAngle) / steps;
double min = qMin(width(), height());
double radius = min / 3;
QPen pen(d->scaleColor);
pen.setCapStyle(Qt::RoundCap);
for (int i = 0; i <= steps; i++) {
if (i % d->scaleMinor == 0) {
pen.setWidthF(1.5);
painter->setPen(pen);
painter->drawLine(0, radius - 8, 0, radius + 5);
} else {
pen.setWidthF(0.5);
painter->setPen(pen);
painter->drawLine(0, radius - 8, 0, radius - 3);
}
painter->rotate(angleStep);
}
painter->restore();
}
void DashBoardWidget::drawScaleNum(QPainter *painter)
{
painter->save();
painter->setPen(d->scaleColor);
double min = qMin(width(), height());
double radius = min / 2.4;
QFont font("Microsoft YaHei", min / 25);
painter->setFont(font);
double startRad = d->endAngle * (M_PI / 180);
double deltaRad = (d->endAngle - d->startAngle) * (M_PI / 180) / d->scaleMajor;
QFontMetrics fontMetrics(font);
for (int i = 0; i <= d->scaleMajor; i++) {
double sina = qSin(startRad - i * deltaRad);
double cosa = qCos(startRad - i * deltaRad);
double value = 1.0 * i * ((d->maxValue - d->minValue) / d->scaleMajor) + d->minValue;
QString strValue = QString("%1").arg(value);
double textWidth = fontMetrics.horizontalAdvance(strValue);
double textHeight = fontMetrics.height();
int x = radius * cosa - textWidth / 2;
int y = -radius * sina + textHeight / 4;
painter->drawText(x, y, strValue);
}
painter->restore();
}
void DashBoardWidget::drawPointer(QPainter *painter)
{
painter->save();
painter->setPen(Qt::NoPen);
painter->setBrush(d->pointerColor);
double radius = qMin(width(), height()) / 3.0;
QPolygonF pts;
pts << QPointF(-5, 0) << QPointF(0, -8)
<< QPointF(5, 0) << QPointF(0, radius);
painter->rotate(270 - d->endAngle);
double degRotate = (d->endAngle - d->startAngle) / (d->maxValue - d->minValue) * (d->value - d->minValue);
painter->rotate(degRotate);
painter->drawConvexPolygon(pts);
painter->restore();
}
void DashBoardWidget::drawValue(QPainter *painter)
{
painter->save();
painter->setPen(d->textColor);
double min = qMin(width(), height());
double radius = min / 2.0 - min / 4.8;
QFont font("Microsoft YaHei", min / 25);
painter->setFont(font);
QString strValue = QString("%1").arg(d->value);
strValue = QString("%1 %2").arg(strValue, d->unit);
QRectF valueRect(-radius, radius / 2.5, radius * 2, radius / 3.5);
painter->drawText(valueRect, Qt::AlignCenter, strValue);
QRectF textRect(-radius, radius / 1.5, radius * 2, radius / 2.5);
//font.setPixelSize(12);
painter->setFont(font);
painter->drawText(textRect, Qt::AlignCenter, d->text);
painter->restore();
}
五、使用示例
以下是一个简单的示例代码,演示了如何在Qt中使用此控件:
#include "mainwindow.h"
#include "dashboardwidget.h"
#include <QtWidgets>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QSlider *slider = new QSlider(this);
slider->setRange(0, 100);
DashBoardWidget *dashBoardWidget = new DashBoardWidget(this);
dashBoardWidget->setUnit("°C");
dashBoardWidget->setText("温度");
connect(slider, &QSlider::valueChanged, dashBoardWidget, &DashBoardWidget::valueChanged);
QWidget *widget = new QWidget(this);
QHBoxLayout *layout = new QHBoxLayout(widget);
layout->addWidget(dashBoardWidget);
layout->addWidget(slider);
setCentralWidget(widget);
resize(500, 400);
}
MainWindow::~MainWindow()
{
}
我们只需要改变当前的值,在其重绘指针时就会根据当前的值重写计算并绘制指针,所以看起来的效果就是我们改变当前的值指针就会移动。
这个示例只是一个基本的实现,实际应用中可能需要更复杂的逻辑来处理动画效果。 望大家看完这篇文章后可以实现仪表盘。
总的来说QT仪表盘的设计思路主要涉及以下几个步骤和考虑因素:
-
窗体绘制准备:首先,需要准备窗体的绘制环境。由于仪表盘通常是圆形的,而Qt的窗体默认是方形的,因此需要设置窗体为透明,以便在上面绘制出圆形仪表盘的效果。
-
绘制表盘:表盘的绘制可以分为几个部分,包括外层圆、内层圆(刻度盘)和中心区域的红色小圆。根据不同的设计方案,可以选择先画外层圆再填充灰色,接着画内层圆填充为黑色,最后补上中心区域的红色小圆,或者采用其他组合方式。
-
绘制刻度:刻度的绘制是仪表盘设计中的重要部分。需要确定刻度的区域和角度,设置基础角度为135°,左右各为135°,总共270°。在刻度盘中画10个大刻度,每个大刻度里再画5个小刻度,总共50个刻度。每个刻度的角度为270°除以50,即5.4°。刻度的绘制包括确定刻度线的起点和终点,以及刻度线的半径。
-
自定义控件的实现:为了实现高度可定制且易于使用的自定义仪表盘控件,需要创建一个新的类,并从QWidget类派生该类。这个类将包含设置仪表盘值、获取值、大小提示等功能,并重写paintEvent函数以自定义绘制逻辑。
-
适应性和可扩展性:设计的仪表盘应具有良好的适应性和可扩展性,能够适应不同尺寸的窗体拉伸,刻度尺和文字能够自动缩放。同时,应支持设置各种颜色、刻度数量、旋转角度等,以及自由拓展各种渐变色和圆的半径。
-
指示器样式:可以设置不同的指示器样式,如圆形指示器、指针指示器、圆角指针指示器和三角形指示器,以及中间圆的宽度,以便使用更大数值。
通过上述步骤和考虑因素,可以设计出一个功能完善、外观美观的Qt仪表盘控件。