Qt绘制动态仪表(模仿汽车仪表指针、故障灯)

news2024/9/23 22:38:42

背景:

项目需要,可能需要做一些仪表显示。此篇除了介绍实现方法,还要说明心路历程。对我而言,重要的是心理,而不是技术。写下来也是自勉。

本人起初心里是比较抵触的,从业20多年了,深知所谓界面,实际上最根本还是代码实现的,操作系统层面多少年来一步步发展更新,是靠不断封装演变来的。所谓的可视化开发,主要还是靠ide提供的插件控件,通常只是应用。如果要按照自己的想法做个什么,那就是绘图机制,把代码变成图不用想就知道麻烦。基于这种心理,就是再说qt可以做得很炫,个人表示一看就够那种,根本看不进去。

终究为了活下去,所以要保住工作,也是对技术的不服输,所以不忙时打开qt示例,看它的文档。其实看过很多遍了,主要是看不进去。终于要善于提炼代码,看它的主要部分,缩小规模,然后研读手册,必要时看看别人怎么说的,逐个尝试,最后形成自己的总结,就学会了。
下面只介绍干货。

用法总结:

所谓绘图机制,肯定用到paintEvent函数,每一个能看到的窗体都是使用操作系统的重绘机制显示的。说人话就是,绘图的过程要写在paintEvent函数里,只要做了更新显示操作,记着调用update函数,它会调用paintEvent函数重绘界面。

qt绘图就一个QPainter,所有事都是它干的。QPen用于设置边框边线,QBrush用于设置填充。
painter画任何东西都是有个draw***函数。

画任何东西都基于坐标系,所以要先操作坐标系,再画。比如想画仪表盘刻度,设定好间隔(循环),转一次坐标系,画一个短线,最后转完一圈,也就画了一圈刻度。

painter就是画画,肯定后画的覆盖先画的,跟图层似的。

因为能旋转坐标系,所以要记住前后的坐标系状态,所以前面调用QPainter::save(),最后调用QPainter::restore()。比如画仪表,开始画背景是正常坐标系,接着通过旋转坐标系画刻度,最后要在正常坐标系下画表轴或者其它东西。这中间,旋转坐标系那部分,就需要暂存painter状态,画完这部分再恢复。

具体下面例子说明。

下面无论我画什么,都不再贴mainwindow代码了,没有必要,每个仪表都是一个类,使用时,在界面画一个label,提升为这个类即可。

手绘仪表盘:

思路是:

先画个黑色矩形当背景;

再旋转坐标系画刻度;

通过一个全局的成员变量值,作为指针旋转角度值,通过旋转坐标系把指针画到相应位置;

最后画一个转轴盖住指针(其实也可以把指针别画那么长,留出圆心位置即可)。

我鸟语不行,注释部分,看官您忽略就好。至于表芯转轴,你们说叫hand heart还是center?要不咱别较真的了,主要看代码。

guage_watertemp.h

/**************************************************************************************************
 ** File name:      guage_watertemp.h (Guage_WaterTemp)
 ** Created by:     Henrick.Nie at 2024-9-8
 ** Used for:       The car water temprature guage.
 **************************************************************************************************/
#ifndef GUAGE_WATERTEMP_H
#define GUAGE_WATERTEMP_H

#include <QLabel>
#include <QPainter>
#include <QDebug>

class Guage_WaterTemp : public QLabel
{
    Q_OBJECT

public:
    explicit Guage_WaterTemp(QWidget *parent = nullptr);

    void f_SetTemp(const qreal &realTemp);

private:
    qreal m_realTemp, m_realMin = 25, m_realMax = 155;

    void paintEvent(QPaintEvent *event);

    void f_Draw(QPainter *p);
};


#endif // GUAGE_WATERTEMP_H

guage_watertemp.cpp

#include "guage_watertemp.h"

Guage_WaterTemp::Guage_WaterTemp(QWidget *parent) :
    QLabel(parent)
{
}
void Guage_WaterTemp::paintEvent(QPaintEvent *event)
{
    QLabel::paintEvent(event);
    QPainter painter(this);
    f_Draw(&painter);
}
void Guage_WaterTemp::f_Draw(QPainter *p)
{
    QColor colorHand = QColor(Qt::red);

    //Init the size data.
    int iHeartSize = width() / 5;
    int iX0 = width() / 2, iY0 = height() - iHeartSize;
    int iBackWidth = width(), iBackHeight = width() / 2 + iHeartSize * 1.5 ;
    int iBackLeft = 0 - iBackWidth / 2, iBackTop = 0 - iBackWidth / 2 - iHeartSize / 2;
    int iMarkSize = iBackWidth / 40, iMarklength = iBackWidth / 20;
    int iHandSize = iBackWidth / 25, iHandLeft = iBackLeft + width() / 20, iHandLength = iBackWidth / 2 - iBackWidth / 20;
    int iText30Left = iBackLeft + width() / 28, iText30Top = 0 - width() / 15;
    int iText90Left = 0 - width() / 18, iText90Top = 0 - width() / 4;
    int iText150Left = width() / 3.5, iText150Top = iText30Top;

    //Init the origin.
    p->setRenderHint(QPainter::Antialiasing);
    p->translate(iX0, iY0);

    //Draw the black rect as the background.
    p->setPen(Qt::NoPen);
    p->setBrush(QBrush(Qt::black));
    p->drawRect(iBackLeft, iBackTop, iBackWidth, iBackHeight);

    //Draw the hand heart on the origin.
    p->setPen(QPen(QBrush(colorHand), iHeartSize, Qt::SolidLine, Qt::RoundCap));
    p->drawPoint(0, 0);

    //Draw the mark. (From 30 degrees to 150 degrees, 150-30=120, 120/10=12)
    p->save();
    p->rotate(30);
    for(int i = 30; i <= 150; ++i)
    {
        QBrush brush = (i < 140) ? QBrush(Qt::white) : QBrush(Qt::red);
        p->setPen(QPen(brush, iMarkSize));
        p->drawPoint(iBackLeft, 0);
        p->rotate(1);
    }
    p->restore();

    QList<int> iList_Big = { 30, 90, 150 };
    p->save();
    p->rotate(30);
    for (int i = 30; i <= 150; i += 12)
    {
        int iSize = iList_Big.contains(i) ? iMarkSize * 2 : iMarkSize;
        int iLeft = iList_Big.contains(i) ? iBackLeft + iMarkSize / 2 : iBackLeft;
        int iLength = iList_Big.contains(i) ? iMarklength + 10 : iMarklength;
        QBrush brush = (i < 130) ? QBrush(Qt::white) : QBrush(Qt::red);
        p->setPen(QPen(brush, iSize));
        p->drawLine(iLeft, 0, iLeft + iLength, 0);
        p->rotate(12);
    }
    p->restore();

    //Draw the mark text.
    p->setFont(QFont("", width() / 10));
    p->setPen(QPen(QBrush(Qt::white), width() / 18));
    p->drawText(iText30Left, iText30Top, "30");
    p->drawText(iText90Left, iText90Top, "90");
    p->drawText(iText150Left, iText150Top, "150");

    //Draw hands.
    p->save();
    p->setPen(QPen(QBrush(colorHand), iHandSize, Qt::SolidLine, Qt::RoundCap));
    if (m_realTemp < m_realMin)
    {
        m_realTemp = m_realMin;
    }
    if (m_realTemp > m_realMax)
    {
        m_realTemp = m_realMax;
    }
    p->rotate(m_realTemp);
    p->drawLine(iHandLeft, 0, iHandLeft + iHandLength, 0);
    p->restore();

    p->end();
}
void Guage_WaterTemp::f_SetTemp(const qreal &realTemp)
{
    m_realTemp = realTemp;
    update();
}

效果:

a962b05de3a946b48078be5bef15a3be.jpeg

其实我是仿照大众polo劲情的水温表画的。至于指针,既然是旋转肯定是角度。看到表盘的样式时,要大致估计一下,最左侧和左右侧的刻度大概是坐标轴上的多少度。算一下它的跨度,和实际需要的水温数值要有个转换。比方说,这个水温表的刻度刚好是水温和角度一一对应,所以不用转换。

图片仪表盘:

上面看到效果后会很有成就感,但要把仪表盘画得逼真可不容易,还要考虑缩放和字体,细节太多。因此有了这种方法,从网上找图片素材,自己加工仪表盘。直接先把图片画上去当背景,然后代码只控制表针即可。以后修改背景表盘,photoshop修图即可。

后期又做了小修改,因为图片作为资源文件,每个人安排的路径可能不同,通用起见,需要调用f_Init函数先初始化路径前缀。

先看我找的素材:

d814b1bb900543c890953131dc6a1ac0.jpeg

再看我修好的效果:

f45cc417af744fc0a094cea34aceea04.jpeg4b797a398adc43fe92b78c8c647bf3b3.jpeg5931d8ffe67c407ba81f61899e8e73b8.jpegd7a770143027477db7d06663705ecf2e.jpeg

上代码:

polo_base.h

/**************************************************************************************************
 ** File name:      polo_baes.h (Polo_Base)
 ** Created by:     Henrick.Nie at 2024-9-9
 ** Used for:       The base class of the polo guage.
 ** Using method:
 **
 **     Firstly, a image resource path is required.
 **
 **         obj->f_Init(":/images/");
 **
 **************************************************************************************************/

#ifndef POLO_BASE_H
#define POLO_BASE_H

#include <QLabel>
#include <QPainter>
#include <QDebug>

class Polo_Base : public QLabel
{
    Q_OBJECT
public:
    explicit Polo_Base(QWidget *parent = nullptr);

    inline virtual void f_Init(const QString &sRcPathPrefix) = 0;

protected:
    inline void f_SetBack(const QString &sFileName) { m_sPicPath = sFileName; }

    virtual void f_SetValue(const qreal &realValue);

private:
    QString m_sPicPath;
    qreal m_realDegree, m_realMin = 40, m_realMax = 140;

    void paintEvent(QPaintEvent *event);

    void f_Draw(QPainter *p);
};

#endif // POLO_BASE_H

polo_base.cpp

#include "polo_base.h"

Polo_Base::Polo_Base(QWidget *parent) :
    QLabel(parent)
{

}
void Polo_Base::paintEvent(QPaintEvent *event)
{
    QLabel::paintEvent(event);
    QPainter painter(this);
    f_Draw(&painter);
}
void Polo_Base::f_Draw(QPainter *p)
{
    QColor colorHand = QColor(Qt::red);

    //Init the origin.
    int iX0 = width() / 2, iY0 = height() - width() / 4;
    p->setRenderHint(QPainter::Antialiasing);
    p->translate(iX0, iY0);

    //Draw the background picture.
    QPixmap pic(m_sPicPath);
    int iPicWidth = width(), iPicHeight = width() * pic.height() / pic.width();
    int iLeft = 0 - iPicWidth / 2, iTop = 0 - iPicHeight + width() / 4;
    p->drawPixmap(iLeft, iTop, width(), width() * pic.height() / pic.width(), pic);

    //Draw hands.
    int iHandSize = width() / 25, iHandLeft = iLeft - width() / 15, iHandLength = width() / 2 - width() / 20;
    p->save();
    p->setPen(QPen(QBrush(colorHand), iHandSize, Qt::SolidLine, Qt::RoundCap));
    if (m_realDegree < m_realMin)
    {
        m_realDegree = m_realMin;
    }
    if (m_realDegree > m_realMax)
    {
        m_realDegree = m_realMax;
    }
    p->rotate(m_realDegree);
    p->drawLine(iHandLeft, 0, iHandLeft + iHandLength, 0);
    p->restore();

    //Draw the hand heart on the origin.
    int iHeartSize = width() / 4;
    p->setPen(QPen(QBrush(QColor(Qt::darkGray)), iHeartSize, Qt::SolidLine, Qt::RoundCap));
    p->drawPoint(0, 0);

    p->end();
}
void Polo_Base::f_SetValue(const qreal &realValue)
{
    m_realDegree = realValue;
    update();
}


 polo_watertemp.h

/**************************************************************************************************
 ** File name:      polo_watertemp.h (Polo_WaterTemp)
 ** Created by:     Henrick.Nie at 2024-9-9
 ** Used for:       The water temprature guage of polo.
 ** Using method:
 **
 **     Firstly, a image resource path is required.
 **
 **         obj->f_Init(":/images/");
 **
 **************************************************************************************************/

#ifndef POLO_WATERTEMP_H
#define POLO_WATERTEMP_H

#include "polo_base.h"

class Polo_WaterTemp : public Polo_Base
{
    Q_OBJECT
public:
    explicit Polo_WaterTemp(QWidget *parent = nullptr);

    void f_Init(const QString &sRcPathPrefix) override;
    void f_SetValue(const qreal &realValue) override;
};

#endif // POLO_WATERTEMP_H

polo_watertemp.cpp

#include "polo_watertemp.h"

Polo_WaterTemp::Polo_WaterTemp(QWidget *parent) :
    Polo_Base(parent)
{

}
void Polo_WaterTemp::f_Init(const QString &sRcPathPrefix)
{
    f_SetBack(sRcPathPrefix + "polo_watertemp.jpg");
}
void Polo_WaterTemp::f_SetValue(const qreal &realValue)
{
    //The given water temprature value, it equals the degrees.
    Polo_Base::f_SetValue(realValue);
}

polo_fuel.h

/**************************************************************************************************
 ** File name:      polo_fuel.h (Polo_Fuel)
 ** Created by:     Henrick.Nie at 2024-9-9
 ** Used for:       The fuel guage of polo.
 ** Using method:
 **
 **     Firstly, a image resource path is required.
 **
 **         obj->f_Init(":/images/");
 **
 **************************************************************************************************/

#ifndef POLO_FUEL_H
#define POLO_FUEL_H

#include "polo_base.h"

class Polo_Fuel : public Polo_Base
{
    Q_OBJECT
public:
    explicit Polo_Fuel(QWidget *parent = nullptr);

    void f_Init(const QString &sRcPathPrefix) override;
    void f_SetValue(const qreal &realValue) override;
};

#endif // POLO_FUEL_H

polo_fuel.cpp

#include "polo_fuel.h"

Polo_Fuel::Polo_Fuel(QWidget *parent) :
    Polo_Base(parent)
{

}
void Polo_Fuel::f_Init(const QString &sRcPathPrefix)
{
    f_SetBack(sRcPathPrefix + "polo_fuel.jpg");
}
void Polo_Fuel::f_SetValue(const qreal &realValue)
{
    Polo_Base::f_SetValue(40 + realValue * 2.2);
}

燃料表这个要注意数值换算了。下面转速表也是。

tachometer.h

/**************************************************************************************************
 ** File name:      tachometer.h (TachoMeter)
 ** Created by:     Henrick.Nie at 2024-9-9
 ** Using method:
 **
 **     Firstly, a image resource path is required.
 **
 **         obj->f_Init(":/images/");
 **
 **************************************************************************************************/

#ifndef TACHOMETER_H
#define TACHOMETER_H

#include <QLabel>
#include <QPainter>
#include <QDebug>

class TachoMeter : public QLabel
{
    Q_OBJECT
public:
    explicit TachoMeter(QWidget *parent = nullptr);

    inline void f_Init(const QString &sRcPathPrefix) { m_sRcPathPrefix = sRcPathPrefix; }
    void f_SetValue(const qreal &realValue);

private:
    QString m_sRcPathPrefix;
    qreal m_realValue = -45, m_realMin = -45, m_realMax = 225;

    void paintEvent(QPaintEvent *event);
    void f_Draw(QPainter* p);
};

#endif // TACHOMETER_H

tachometer.cpp

#include "tachometer.h"

TachoMeter::TachoMeter(QWidget *parent) :
    QLabel(parent)
{

}
void TachoMeter::paintEvent(QPaintEvent *event)
{
    QLabel::paintEvent(event);
    QPainter painter(this);
    f_Draw(&painter);
}
void TachoMeter::f_Draw(QPainter *p)
{
    //Init the size data.
    int iSideSize = qMin(width(), height());
    int iX0 = width() / 2, iY0 = height() / 2;

    //Init the origin.
    p->setRenderHint(QPainter::Antialiasing);
    p->translate(iX0, iY0);

    //Draw the background.
    p->setPen(Qt::NoPen);
    p->setBrush(QBrush(Qt::black));
    p->drawRect(0 - width() / 2, 0 - height() / 2, width(), height());

    QPixmap pic(m_sRcPathPrefix + "tacho1.jpg");
    int iLeft = 0 - iSideSize / 2, iTop = 0 - iSideSize / 2;
    p->drawPixmap(iLeft, iTop, iSideSize, iSideSize, pic);

    //Draw the hand.
    p->save();
    p->setPen(QPen(QBrush(Qt::red), iSideSize / 40, Qt::SolidLine, Qt::RoundCap));
    if (m_realValue < m_realMin)
    {
        m_realValue = m_realMin;
    }
    if (m_realValue > m_realMax)
    {
        m_realValue = m_realMax;
    }
    p->rotate(m_realValue);
    p->drawLine(0 - iSideSize / 2 + iSideSize / 20, 0, 0, 0);
    p->restore();

    //center
    p->setPen(Qt::NoPen);
    p->setBrush(QBrush(QColor(Qt::darkGray)));
    p->drawEllipse(QPoint(0, 0), iSideSize / 20, iSideSize / 20);

    p->end();
}
void TachoMeter::f_SetValue(const qreal &realValue)
{
    //value: 0 to 800
    //degree: -45 to 225 (225-(-45))=270
    qreal realScale = 270.0 / 800.0; //per 10 rpm
    m_realValue = 0 - 45 + realValue * realScale;
    update();
}

效果:

06057548e3274a0897c4917e90f396a0.jpeg

效果解析:

上面效果图中,我在界面上放置了滑动条,实际操作时,可以改变滑块位置,直接看到仪表动作。

警告灯:

在上述基础上,还可以使用label的setmovie函数,播放一些动态的gif图片。以实现警告灯的效果。

 label_movie.h

/**************************************************************************************************
 ** File name:      label_movie.h (Label_Movie)
 ** Created by:     Henrick.Nie at 2024-9-10
 ** Using method:
 **
 **     Firstly, a image resource path is required.
 **
 **         obj->f_Init(":/images/");
 **
 **************************************************************************************************/

#ifndef LABEL_MOVIE_H
#define LABEL_MOVIE_H

#include <QLabel>
#include <QPainter>
#include <QMovie>
#include <QDebug>

class Label_Movie : public QLabel
{
    Q_OBJECT
public:
    explicit Label_Movie(QWidget *parent = nullptr);

    inline void f_Init(const QString &sRcPathPrefix) { m_sRcPathPrefix = sRcPathPrefix; }
    void f_SetJpg(const QString &sFileName);
    void f_SetGif(const QString &sFileName);

private:
    QString m_sRcPathPrefix;
    QMovie m_movie;
};

#endif // LABEL_MOVIE_H

label_movie.cpp

#include "label_movie.h"

Label_Movie::Label_Movie(QWidget *parent) :
    QLabel(parent)
{

}
void Label_Movie::f_SetJpg(const QString &sFileName)
{
    this->setPixmap(QPixmap(m_sRcPathPrefix + sFileName));
}
void Label_Movie::f_SetGif(const QString &sFileName)
{
    m_movie.stop();
    m_movie.setFileName(m_sRcPathPrefix + sFileName);
    this->setMovie(&m_movie);
    m_movie.start();
}

之所以要包装一个label,因为qt手册提到ownership,也许qt这样处理有其它用意,但对于本次情况,每次单独处理不方便。

 [slot] void QLabel::setMovie(QMovie *movie)
Sets the label contents to movie. Any previous content is cleared. The label does NOT take ownership of the movie.
The buddy shortcut, if any, is disabled.

效果如下:

72c76309eae54449bba63d82be13a2fe.jpeg

当水温和油量到达警戒值时,自动亮起故障灯。

故障灯整合面板:

guage_warning.h

/**************************************************************************************************
 ** File name:      guage_warning.h (Guage_Warning)
 ** Created by:     Henrick.Nie at 2024-9-11
 ** Used for:       The warning light of the engine.
 ** Using method:
 **
 **     Firstly, a image resource path is required.
 **
 **         obj->f_Init(":/images/");
 **
 **************************************************************************************************/

#ifndef GUAGE_WARNING_H
#define GUAGE_WARNING_H

#include <QWidget>

namespace Ui {
class Guage_Warning;
}

class Guage_Warning : public QWidget
{
    Q_OBJECT

public:
    explicit Guage_Warning(QWidget *parent = nullptr);
    ~Guage_Warning();

    enum EWarningType { eNone, eWater, eOil, eFuel, eEngine, eBattery, eBrake };

    void f_Init(const QString &sRcPathPrefix);
    void f_SetStatus(const EWarningType &eType, const bool &bIsWarning);

private:
    Ui::Guage_Warning *ui;
    QString m_sRcPathPrefix;
    EWarningType m_eType = eNone;

};

#endif // GUAGE_WARNING_H

guage_warning.cpp

#include "guage_warning.h"
#include "ui_guage_warning.h"

Guage_Warning::Guage_Warning(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Guage_Warning)
{
    ui->setupUi(this);
    this->setStyleSheet("background: black;");
    ui->gridLayout->setSpacing(0);
}

Guage_Warning::~Guage_Warning()
{
    delete ui;
}

void Guage_Warning::f_Init(const QString &sRcPathPrefix)
{
    m_sRcPathPrefix = sRcPathPrefix;

    ui->labWater  ->setPixmap(QPixmap(m_sRcPathPrefix + "warning_water.jpg"));
    ui->labOil    ->setPixmap(QPixmap(m_sRcPathPrefix + "warning_oil.jpg"));
    ui->labFuel   ->setPixmap(QPixmap(m_sRcPathPrefix + "warning_fuel.jpg"));
    ui->labEngine ->setPixmap(QPixmap(m_sRcPathPrefix + "warning_engine.jpg"));
    ui->labBattery->setPixmap(QPixmap(m_sRcPathPrefix + "warning_battery.jpg"));
    ui->labBrake  ->setPixmap(QPixmap(m_sRcPathPrefix + "warning_brake.jpg"));
}
void Guage_Warning::f_SetStatus(const EWarningType &eType, const bool &bIsWarning)
{
    Label_Movie *lab;
    QString sJpg, sGif;
    switch (eType) {
    case eWater:
        lab = ui->labWater;
        sJpg = "warning_water.jpg";
        sGif = "warning_water.gif";
        break;
    case eOil:
        lab = ui->labOil;
        sJpg = "warning_oil.jpg";
        sGif = "warning_oil.gif";
        break;
    case eFuel:
        lab = ui->labFuel;
        sJpg = "warning_fuel.jpg";
        sGif = "warning_fuel.gif";
        break;
    case eEngine:
        lab = ui->labEngine;
        sJpg = "warning_engine.jpg";
        sGif = "warning_engine.gif";
        break;
    case eBattery:
        lab = ui->labBattery;
        sJpg = "warning_battery.jpg";
        sGif = "warning_battery.gif";
        break;
    case eBrake:
        lab = ui->labBrake;
        sJpg = "warning_brake.jpg";
        sGif = "warning_brake.gif";
        break;
    default:
        lab = nullptr;
        break;
    }

    if (nullptr == lab)
    {
        return;
    }

    lab->f_Init(m_sRcPathPrefix);
    void (Label_Movie::*f_ptr)(const QString &sFileName) = bIsWarning ? &Label_Movie::f_SetGif : &Label_Movie::f_SetJpg;
    QString sPic = bIsWarning ? sGif : sJpg;
    (lab->*f_ptr)(sPic);
}

guage_warning.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Guage_Warning</class>
 <widget class="QWidget" name="Guage_Warning">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>316</width>
    <height>134</height>
   </rect>
  </property>
  <property name="minimumSize">
   <size>
    <width>129</width>
    <height>66</height>
   </size>
  </property>
  <property name="windowTitle">
   <string>Form</string>
  </property>
  <layout class="QGridLayout" name="gridLayout">
   <item row="0" column="0">
    <widget class="Label_Movie" name="labWater">
     <property name="text">
      <string>TextLabel</string>
     </property>
     <property name="alignment">
      <set>Qt::AlignCenter</set>
     </property>
    </widget>
   </item>
   <item row="0" column="1">
    <widget class="Label_Movie" name="labOil">
     <property name="text">
      <string>TextLabel</string>
     </property>
     <property name="alignment">
      <set>Qt::AlignCenter</set>
     </property>
    </widget>
   </item>
   <item row="0" column="2">
    <widget class="Label_Movie" name="labFuel">
     <property name="text">
      <string>TextLabel</string>
     </property>
     <property name="alignment">
      <set>Qt::AlignCenter</set>
     </property>
    </widget>
   </item>
   <item row="1" column="0">
    <widget class="Label_Movie" name="labEngine">
     <property name="text">
      <string>TextLabel</string>
     </property>
     <property name="alignment">
      <set>Qt::AlignCenter</set>
     </property>
    </widget>
   </item>
   <item row="1" column="1">
    <widget class="Label_Movie" name="labBattery">
     <property name="text">
      <string>TextLabel</string>
     </property>
     <property name="alignment">
      <set>Qt::AlignCenter</set>
     </property>
    </widget>
   </item>
   <item row="1" column="2">
    <widget class="Label_Movie" name="labBrake">
     <property name="text">
      <string>TextLabel</string>
     </property>
     <property name="alignment">
      <set>Qt::AlignCenter</set>
     </property>
    </widget>
   </item>
  </layout>
 </widget>
 <customwidgets>
  <customwidget>
   <class>Label_Movie</class>
   <extends>QLabel</extends>
   <header>label_movie.h</header>
  </customwidget>
 </customwidgets>
 <resources/>
 <connections/>
</ui>

使用的时候更简洁:



void MainWindow::on_sbWaterTemp_valueChanged(int value)
{
    ui->lab_WaterTemp->f_SetTemp(value);
    ui->lab_WaterTempPolo->f_SetValue(value);
    ui->wWarning->f_SetStatus(Guage_Warning::eWater, (value > 120));
}

void MainWindow::on_sbWaterTemp_2_valueChanged(int value)
{
    on_sbWaterTemp_valueChanged(value);
}

void MainWindow::on_sbFuel_valueChanged(int value)
{
    ui->lab_FuelPolo->f_SetValue(value);
    ui->wWarning->f_SetStatus(Guage_Warning::eFuel, (value < 10));
}

void MainWindow::on_sbTacho_valueChanged(int value)
{
    ui->lab_Tacho->f_SetValue(value);
}

整合故障灯后效果如下:

本文完。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2126511.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

240911-基于Ollama的22行代码实现极简交互式RAG问答

A. 最终效果 B. 环境配置 pip install llama-index-embeddings-ollama pip install llama-index-llms-ollama pip install llama-indexC. 代码程序 from llama_index.core import VectorStoreIndex, Document, SimpleDirectoryReader,Settings from llama_index.llms.ollama …

计算机毕业设计选题推荐-动漫插画分享网站-Java/Python项目实战

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

Linux 工程师:探索开源世界的专业之路

摘要&#xff1a; 本文深入探讨了 Linux 工程师这一职业角色。从 Linux 操作系统的基础特性与架构开始阐述&#xff0c;详细分析了 Linux 工程师在不同领域的工作范畴&#xff0c;包括系统安装与配置、内核开发与优化、网络管理、安全防护等方面。文中还探讨了成为一名优秀 Lin…

vue3数据持久化方案:pinia-plugin-persistedstate源码浅析

概述 Pinia是vue3的官方推荐用于数据共享的库,但是Pinia🍍中的数据是存在于浏览器的内存中,当浏览器刷新后,这些数据就会消失。因此我们需要对数据做持久化存储,这个时候就需要用到pinia-plugin-persistedstate。 pinia-plugin-persistedstate本质上利用浏览器持久化存…

即插即用篇 | YOLOv8 引入高效的直方图Transformer模块 | 突破天气障碍:Histoformer引领高效图像修复新路径“

本改进已同步到YOLO-Magic框架! 摘要:摘要。基于Transformer的恶劣天气图像修复方法取得了显著进展。大多数方法通过沿通道维度或在空间上固定范围的块内使用自注意力,以减少计算负担。然而,这种折中方式在捕获长距离空间特征方面存在局限性。受到恶劣天气导致的退化因素主…

C++职工管理系统(具备增删改查功能 涉及文件操作、指针数组操作、升序降序、多态、虚函数)

目录 &#x1f315;需求分析&#x1f315;创建项目&#x1f315;完整代码&#x1f319;项目结构&#x1f319;include⭐worker.h (它是后面employ&#xff0c;boss&#xff0c;manager的基类)⭐boss.h⭐employee.h⭐manager.h⭐workerManager.h &#x1f319;src⭐boss.cpp⭐em…

0911(绘制事件,qt中的网络通信)

一、实现一个时钟 1)代码 头文件&#xff1a; #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QPainter> #include <QPaintEvent> #include <QTimer> #include <QTime> #include <QTimerEvent>QT_BEGIN_NAMESPACE nam…

如何用Google Trend进行SEO优化?方法与策略

做SEO的都知道&#xff0c;Google Trend是一款免费工具&#xff0c;用户可以查看不同关键词的搜索趋势、兴趣强度和区域分布。通过 Google Trends&#xff0c;你可以获得以下信息&#xff0c;这些数据可以帮助您更好地了解用户需求并优化您的SEO策略&#xff1a; 1、搜索量趋势…

线结构光测量系统标定--导轨

光平面标定原理可查看之前的博文《光平面标定》&#xff0c;光条中心提取可参考线结构光专栏光条中心提取系列的文章&#xff0c;相机标定参考相机标定专栏中的博文。&#xff08;欢迎进Q群交流&#xff1a;874653199&#xff09; 线结构光测量系统(指一个线结构光传感器与一个…

如何检查前端项目中我们没有使用的第三方包

问题描述&#xff1a;我们在赶项目或者在做些功能或者效果的时候往往会用到很多的第三方包&#xff0c;那么时间一长&#xff0c;我们有时候会忘记删除这些包到底该怎么办呢&#xff1f;接下来教给大家一个方法。 在我们的项目根目录下面随便起一个.js的文件 代码如下&#x…

算法工程师重生之第四天(两两交换链表中的节点 删除链表的倒数第N个节点 链表相交 环形链表II 总结 )

参考文献 代码随想录 一、两两交换链表中的节点 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节点交换&#xff09;。 示例 1&#xff1a; 输入&am…

掌握python的dataclass,让你的代码更简洁优雅!

"dataclass"是从"Python3.7"版本开始&#xff0c;作为标准库中的模块被引入。 随着"Python"版本的不断更新&#xff0c;"dataclass"也逐步发展和完善&#xff0c;为"Python"开发者提供了更加便捷的数据类创建和管理方式。 …

Element-UI 组件实现面包屑导航栏

Element-UI 组件实现面包屑导航栏 面包屑导航栏是一种辅助导航系统&#xff0c;它显示用户当前位置在网站或应用层次结构中的位置&#xff0c;可以帮助用户了解他们当前页面的位置&#xff0c;并且可以方便地返回到上级页面或首页。 面包屑导航栏的实现原理&#xff1a; 路径…

【网易低代码】第2课,页面表格查询功能

你好&#xff01; 这是一个新课程 CodeWave网易低代码 通过自然语言交互式智能编程&#xff0c;同时利用机器学 习&#xff0c;帮助低代码开发者进一步降低使用门槛、提高应用开发效率 【网易低代码】第2课&#xff0c;页面表格查询功能 1.拖拽表格组件到页面布局中2.服务端逻辑…

FFCD:森林火灾分类数据集(猫脸码客 第184期)

亲爱的读者们&#xff0c;您是否在寻找某个特定的数据集&#xff0c;用于研究或项目实践&#xff1f;欢迎您在评论区留言&#xff0c;或者通过公众号私信告诉我&#xff0c;您想要的数据集的类型主题。小编会竭尽全力为您寻找&#xff0c;并在找到后第一时间与您分享。 fores…

2024/9/11黑马头条跟学笔记(七)

1)今日内容介绍 搜索结果&#xff0c;搜索记录&#xff0c;搜索联想 搭建环境 索引&#xff0c;存储&#xff0c;分词 多条件复合查询 结果高亮处理 索引数据同步&#xff08;文章发布后创建索引 kafka&#xff09; 搭建mongodb&#xff0c;存储链和性能好过mysql 异步保…

56 - I. 数组中数字出现的次数

comments: true difficulty: 中等 edit_url: https://github.com/doocs/leetcode/edit/main/lcof/%E9%9D%A2%E8%AF%95%E9%A2%9856%20-%20I.%20%E6%95%B0%E7%BB%84%E4%B8%AD%E6%95%B0%E5%AD%97%E5%87%BA%E7%8E%B0%E7%9A%84%E6%AC%A1%E6%95%B0/README.md 面试题 56 - I. 数组中数…

【学习笔记】SSL证书密码套件之加密

本篇将介绍密码套件中加密常用的协议并将他们进行比较&#xff0c;包括&#xff1a;CHACHA20、AES-256-GCM、AES-128-GCM、AES-256-CBC、AES-128-CBC、3DES-CBC、RC4-128、DES-CBC 一、概念 &#xff08;选择以上合适协议&#xff09;对称加密算法 目的是保护批量数据传输流密…

linux从0到1 基础完整知识

1. Linux系统概述 Linux是一种开源操作系统&#xff0c;与Windows或macOS等操作系统不同&#xff0c;Linux允许用户自由地查看、修改和分发其源代码。以下是Linux系统的一些显著的优势。 稳定性和可靠性&#xff1a; 内核以其稳定性而闻名&#xff0c;能够持续运行数月甚至数…

Codeforces Round 971 (Div. 4)——C题题解

本题的大意是一个青蛙从原点开始跳格子(0,0)&#xff0c;最终要跳到点(x,y)去&#xff0c;并且每一步的步长不能超过k&#xff0c;问最短几步可以跳到终点 分析&#xff1a; 本题利用贪心思想&#xff0c;肯定是先跳最大的步长这样总体用的步数最长 代码演示&#xff1a; #inc…