首页 > 其他分享 >Qt实现仪表盘-自定义控件

Qt实现仪表盘-自定义控件

时间:2024-07-18 12:56:30浏览次数:14  
标签:QColor 控件 const Qt 自定义 double void DashBoardWidget painter

        仪表盘在很多汽车和物联网相关的系统中很常用,本文就来介绍一下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仪表盘的设计思路主要涉及以下几个步骤和考虑因素:‌

  1. 窗体绘制准备:‌首先,‌需要准备窗体的绘制环境。‌由于仪表盘通常是圆形的,‌而Qt的窗体默认是方形的,‌因此需要设置窗体为透明,‌以便在上面绘制出圆形仪表盘的效果。‌

  2. 绘制表盘:‌表盘的绘制可以分为几个部分,‌包括外层圆、‌内层圆(‌刻度盘)‌和中心区域的红色小圆。‌根据不同的设计方案,‌可以选择先画外层圆再填充灰色,‌接着画内层圆填充为黑色,‌最后补上中心区域的红色小圆,‌或者采用其他组合方式。‌

  3. 绘制刻度:‌刻度的绘制是仪表盘设计中的重要部分。‌需要确定刻度的区域和角度,‌设置基础角度为135°,‌左右各为135°,‌总共270°。‌在刻度盘中画10个大刻度,‌每个大刻度里再画5个小刻度,‌总共50个刻度。‌每个刻度的角度为270°除以50,‌即5.4°。‌刻度的绘制包括确定刻度线的起点和终点,‌以及刻度线的半径。‌

  4. 自定义控件的实现:‌为了实现高度可定制且易于使用的自定义仪表盘控件,‌需要创建一个新的类,‌并从QWidget类派生该类。‌这个类将包含设置仪表盘值、‌获取值、‌大小提示等功能,‌并重写paintEvent函数以自定义绘制逻辑。‌

  5. 适应性和可扩展性:‌设计的仪表盘应具有良好的适应性和可扩展性,‌能够适应不同尺寸的窗体拉伸,‌刻度尺和文字能够自动缩放。‌同时,‌应支持设置各种颜色、‌刻度数量、‌旋转角度等,‌以及自由拓展各种渐变色和圆的半径。‌

  6. 指示器样式:‌可以设置不同的指示器样式,‌如圆形指示器、‌指针指示器、‌圆角指针指示器和三角形指示器,‌以及中间圆的宽度,‌以便使用更大数值。‌

通过上述步骤和考虑因素,‌可以设计出一个功能完善、‌外观美观的Qt仪表盘控件。

六、源代码下载 

标签:QColor,控件,const,Qt,自定义,double,void,DashBoardWidget,painter
From: https://blog.csdn.net/u012959478/article/details/140384235

相关文章

  • QT利用QPainter实现自定义圆弧进度条组件
               在可视化应用中,弧形进度条应用也比较广泛,本文示例封装了一个可复用、个性化的弧形进度条组件。本文示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下。主要结构就是外围一圈圆角进度,中间加上标题和对应进度的百分比,进度条的起始角......
  • 【Qt】探索Qt框架:开发经典贪吃蛇游戏的全过程与实践
    文章目录引言项目链接:1.Qt框架的使用简介2.贪吃蛇游戏设计2.1游戏规则和玩法介绍2.2游戏界面设计概述3.核心代码解析3.1主界面(GameHall)3.1.1布局和功能介绍3.1.2代码实现分析3.2游戏选择界面(GameSelect)3.2.1功能介绍3.2.2代码实现分析3.3游戏房间(GameRoom......
  • 2024-07-18 给vue项目添加自定义路由守卫
    要配置路由守卫要使用到vue-router,它是Vue.js官方的路由管理器,主要用于帮助开发者构建单页面应用(SinglePageApplication,简称SPA)。步骤一:新建路由文件,文件名随意,建议叫router.ts,规范一点//router.tsimport{createRouter,createWebHashHistory}from"vue-router";i......
  • Wpf和Winform使用devpress控件库导出Excel并调整报表样式
    Wpf和Winform使用devpress控件库导出Excel并调整报表样式背景客户需求经常需要出各种报表,部分客户对报表的样式有要求。包括颜色、字体、分页等等。代码使用Datagridview导出excel调整样式DevExpress.XtraGrid.Views.Grid.GridViewgdv#regionGridView属性设置//行号所......
  • 使用Spring Boot AOP和自定义注解优雅实现操作日志记录
    使用SpringBootAOP和自定义注解优雅实现操作日志记录大家好,今天我们来聊聊如何在SpringBoot项目中,通过AOP(面向切面编程)和自定义注解,优雅地实现操作日志记录。操作日志对于系统的可维护性和安全性至关重要,它能帮助我们追踪用户行为,排查问题。什么是AOP?AOP,全称Aspect-Oriented......
  • 自定义转换器
    我们要自定义转换器就要声明一个类,然后继承父类的BaseConverter需要用正则表达式的需要重写父类的regex代码实现: fromflaskimportFlaskfromwerkzeug.routingimportBaseConverterapp=Flask(__name__)classCustomConverter(BaseConverter):#自定义转换器要继承......
  • 界面控件DevExpress Blazor UI v24.1 - 发布全新TreeList组件
    DevExpress BlazorUI组件使用了C#为BlazorServer和BlazorWebAssembly创建高影响力的用户体验,这个UI自建库提供了一套全面的原生BlazorUI组件(包括PivotGrid、调度程序、图表、数据编辑器和报表等)。DevExpress Blazor控件目前已经升级到v24.1版本了,此版本发布了全新的TreeLi......
  • 【QT开发】串口通信管理QSerialPort类详解及实战应用
    QSerialPort是Qt提供的一个功能强大、简单易用的串口通信类。通过本文的学习,您应该对QSerialPort的基本使用、高级应用技巧及相关注意事项有了全面的理解。在实际项目中,QSerialPort可以帮助实现与外部设备的串口通信,确保数据的可靠传输和接收。希望本文能帮助您更好地......
  • QTimer的一个莫名错误
    该错误很莫名奇妙,暂时也不知道为什么会出错。是这样的,我自定义了一个QThread,在run函数中会循环打开一个资源,这个资源会出现打不开的情况,所以如果打开失败,就需要等待一段时间再次去打开。因为中间有可能手动终端等待,手动触发再次尝试打开资源,所以没有直接使用msleep,搞的比较复杂。......
  • Qt处理中文编码出现错误
    Qt的QString使用起来非常方便,内部封装了很多很好用的函数和功能。其中有个函数是simplified(),这个函数可以除去字符串的首尾和内部的空白(空白包括\t,\n,\v,\f,\r,'')。其中Qt帮助文档中说的是移除的ASCII中的\t,\n,\v,\f,\r,''。这里隐藏着一个坑。如果字符串不是完全的ASCII字符......