QT 实现两款自定义的温度计/湿度控件

news2024/11/22 21:21:57

文章目录

  • 0 引入
  • 1、带有标尺的温度/湿度计控件
    • 1.头文件
    • 2.核心代码
  • 2、竖起来的温度/湿度计控件
    • 1.头文件
    • 2.实现
  • 3、引用


在这里插入图片描述

0 引入

QT原生控件没有实现如仪表盘或者温度计的控件,只好自己实现,文章代码部分参考引用的文章。直接上图

图一 带有标尺的温度计、湿度带有标尺的温度计、湿度
图二 温度计、湿度

在这里插入图片描述
控件最核心的部分:在函数paintEvent绘制部分,如果需要动画效果还需要加一个QPropertyAnimation ,这是最主要的,剩下的细节根据需求增加减少即可。


1、带有标尺的温度/湿度计控件

因为只做数据显示用,所以只需要向控件传数据即可。
主要功能:1、可设置显示范围;
2、显示过程中加了动画效果;
3、背景色和前景色以及刻度尺颜色可变;
4、刻度尺间距可变,控件大小随着QWidget适应;

1.头文件

代码如下(示例):

protected:
    void paintEvent(QPaintEvent *);
    void drawBg(QPainter *painter);
    void drawProgress(QPainter *painter);
    void drawRulerTop(QPainter *painter);
    void drawRulerBottom(QPainter *painter);

private:
    QPropertyAnimation *m_valueAnimation;
    double minValue;                //最小值
    double maxValue;                //最大值
    qreal value;                    //当前值
    int longStep;                   //长线条等分步长
    int shortStep;                  //短线条等分步长
    bool rulerTop;                  //刻度线在上面
    bool isAdd;                     //是否为增加,默认为的增加

    QColor bgColor;                 //背景颜色
    QColor lineColor;               //线条颜色
    QColor progressColor;           //进度颜色

public:
    qreal getValue()               const;
    void setrulerTop(bool istop);   //设定刻度线再上还是在下,默认是在上
    void setValue(qreal v);
    void setRange(int minValue, int maxValue);
    void startAnimation();
    void updateValue(double value);
    void setBgColor(const QColor &bgColor);               //设置背景颜色
    void setLineColor(const QColor &lineColor);           //设置线条颜色
    void setProgressColor(const QColor &progressColor);   //设置进度颜色

2.核心代码

绘制部分参考引用2代码,感谢刘典武老师的无私开源,有需要的可去做定制。
代码如下:

void HumidityProgress::startAnimation()
{
    qreal startValue =  value;
    if(isAdd)
    {
        m_valueAnimation->setKeyValueAt(0.5, value+0.5);
        m_valueAnimation->setKeyValueAt(1, value);
        m_valueAnimation->setStartValue(startValue-0.5);
        m_valueAnimation->start();
    }
    else{
        m_valueAnimation->setKeyValueAt(0.5, value-0.5);
        m_valueAnimation->setKeyValueAt(1, value);
        m_valueAnimation->setStartValue(startValue+0.5);
        m_valueAnimation->start();
    }

}

void HumidityProgress::paintEvent(QPaintEvent *)
{
    //绘制准备工作,启用反锯齿
    QPainter painter(this);
    painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);

    //按照绘制顺序
    drawBg(&painter);
    drawProgress(&painter);
    if (rulerTop) {
        drawRulerTop(&painter);
    } else {
        drawRulerBottom(&painter);
    }
}

void HumidityProgress::drawBg(QPainter *painter)
{
    painter->save();
    painter->setPen(lineColor);
    painter->setBrush(bgColor);
    painter->drawRect(rect());
    painter->restore();
}

void HumidityProgress::drawProgress(QPainter *painter)
{
    painter->save();
    painter->setPen(Qt::NoPen);
    painter->setBrush(progressColor);

    double length = width();
    double increment = length / (maxValue - minValue);
    double initX = (value - minValue) * increment;

    QRect rect(0, 0, initX, height());
    painter->drawRect(rect);
    painter->restore();
}

void HumidityProgress::drawRulerTop(QPainter *painter)
{
    painter->save();
    painter->setPen(lineColor);

    double initX = 0;

    //绘制横向标尺上部分底部线
    double initTopY = 0;
    QPointF lineTopLeftPot = QPointF(initX, initTopY);
    QPointF lineTopRightPot = QPointF(width() - initX, initTopY);
    painter->drawLine(lineTopLeftPot, lineTopRightPot);

    //绘制上部分及下部分横向标尺刻度
    double length = width();
    //计算每一格移动多少
    double increment = length / (maxValue - minValue);
    //长线条短线条长度
    int longLineLen = 15;
    int shortLineLen = 10;

    //根据范围值绘制刻度值及刻度值 长线条需要移动10像素 短线条需要移动5像素
    for (int i = minValue; i <= maxValue; i = i + shortStep) {
        if (i % longStep == 0) {
            QPointF topPot = QPointF(initX, initTopY);
            QPointF bottomPot = QPointF(initX, initTopY + longLineLen);
            painter->drawLine(topPot, bottomPot);

            //第一个值和最后一个值不要绘制
            if (i == minValue || i == maxValue) {
                initX += increment * shortStep;
                continue;
            }

            QString strValue = QString("%1").arg((double)i, 0, 'f', 0);
            double textWidth = fontMetrics().width(strValue);
            double textHeight = fontMetrics().height();

            QPointF textPot = QPointF(initX - textWidth / 2, initTopY + textHeight + longLineLen);
            painter->drawText(textPot, strValue);
        } else {
            if (i % (longStep / 2) == 0) {
                shortLineLen = 10;
            } else {
                shortLineLen = 6;
            }

            QPointF topPot = QPointF(initX, initTopY);
            QPointF bottomPot = QPointF(initX, initTopY + shortLineLen);
            painter->drawLine(topPot, bottomPot);
        }

        initX += increment * shortStep;
    }

    painter->restore();
}

该处使用的url网络请求的数据。


2、竖起来的温度/湿度计控件

因为只做数据显示用,所以只需要向控件传数据即可。
控件是好看,但是大小不能改变,所以这里需要自己实现了,源码作者已经放上。

1.头文件

代码如下(示例):

#ifndef THERMOMETREDLG_H
#define THERMOMETREDLG_H

#include <QWidget>
#include <QPropertyAnimation>
#include <QPainter>
#include <QTimer>

class thermometreDlg : public QWidget
{
    Q_OBJECT
    Q_PROPERTY(qreal value READ getValue WRITE setValue)  //声明属性

public:
    explicit thermometreDlg(QWidget *parent = nullptr);
    qreal getValue();
    void setValue(qreal value);
    void changeValue(qreal value);


protected:
    void paintEvent(QPaintEvent *e);

public slots:
    void startAnimation();

signals:

private:
    qreal m_value;
    qreal curValue;
    int m_width;
    QRectF m_rect;
    int maxValue, minValue;
    qreal m_radius;
    QPropertyAnimation *m_valueAnimation;
    void updateRect();


};

#endif // THERMOMETREDLG_H

2.实现

代码如下(示例):

#include "thermometredlg.h"
#include <QDebug>

thermometreDlg::thermometreDlg(QWidget *parent) : QWidget(parent)
{
    m_width = 20;
    maxValue = 100;
    minValue = 0;
    m_radius = 1.05;
    m_value = 0;
    curValue = m_value;
    QTimer *at = new QTimer(this);
    at->start(1000);
    m_valueAnimation = new QPropertyAnimation(this, "value");
    m_valueAnimation->setDuration(1000);
    m_valueAnimation->setEasingCurve(QEasingCurve::OutCubic);
    m_valueAnimation->setLoopCount(1);
    connect(at, SIGNAL(timeout()), this, SLOT(startAnimation()));

}


void thermometreDlg::updateRect()
{
    m_rect.setX(0);
    m_rect.setY(20 - height()/2);
    m_rect.setWidth(m_width);
    m_rect.setHeight(height() - 40 - m_width* m_radius);
}

void thermometreDlg::setValue(qreal value)
{
    m_value = value;
    update();
}

void thermometreDlg::changeValue(qreal value)
{
    if(value > maxValue)
        value = maxValue;
    if(value < minValue)
        value = minValue;
    curValue = value;
}

qreal thermometreDlg::getValue()
{
    return m_value;
}


void thermometreDlg::paintEvent(QPaintEvent *e)
{
    updateRect();
    QPainter painter(this);
    QPen pen(Qt::black);
    painter.translate(this->width()/2, this->height()/2);  //坐标轴移动到中心点
    painter.setRenderHints(QPainter::TextAntialiasing | QPainter::Antialiasing);  // 启用反锯齿
    painter.save();
    //绘制上方的柱状
    painter.fillRect(m_rect, QColor(168,200, 225));

    //绘制底部的圆
    QRectF tmpRect = QRectF(m_rect.bottomLeft(), QPointF(m_width, height()/2- m_width*m_radius));
    painter.fillRect(tmpRect, QColor(255, 0, 0));
    painter.setPen(Qt::NoPen);
    painter.setBrush(QColor(255, 0, 0));
    painter.drawEllipse(tmpRect.bottomLeft()+QPointF(tmpRect.width()/2, 0),m_width*m_radius, m_width*m_radius);
    painter.restore();

    //绘制刻度以及刻度值
    painter.save();
    painter.setPen(QColor(Qt::black));
    int nYCount = (maxValue - minValue)/10+1;
    qreal perHeight = (m_rect.height())/nYCount;
    for (int i=0; i<nYCount; ++i) {
        QPointF basePoint = m_rect.bottomLeft() - QPointF(0, perHeight/2) - QPointF(0, perHeight*i);
        //左侧大刻度
        painter.drawLine(basePoint, basePoint+QPointF(-10, 0));
        for (int j=1; j<10; ++j) {
            if(i == nYCount -1)
                continue;
            painter.drawLine(basePoint-QPointF(0, perHeight/10*j),basePoint-QPointF(5, perHeight/10*j));
        }
        painter.drawText(basePoint+QPointF(-28, 4), QString("%1").arg(minValue+i*10));
        //右侧大刻度
        basePoint = m_rect.bottomRight() - QPointF(0, perHeight/2) - QPointF(0, perHeight*i);
        painter.drawLine(basePoint, basePoint+QPointF(10, 0));
        for (int j=1; j<10; ++j) {
            if(i == nYCount -1)
                continue;
            painter.drawLine(basePoint-QPointF(0, perHeight/10*j),basePoint-QPointF(-5, perHeight/10*j));
        }
    }
    painter.restore();

    //根据值填充m_rect
    qreal h = (m_value-minValue)/(maxValue-minValue)*(m_rect.height()-perHeight);
    if(h<0)
        h = 0;
    if(h > m_rect.height())
        h = m_rect.height();
    painter.fillRect(m_rect.adjusted(0, m_rect.height()-h-perHeight/2-1 , 0, 0), QColor(255, 0, 0));

    QWidget::paintEvent(e);
}

void thermometreDlg::startAnimation()
{
    qreal startValue = getValue();
    m_valueAnimation->setKeyValueAt(0, startValue-1);
    m_valueAnimation->setKeyValueAt(0.5, curValue+1);
    m_valueAnimation->setKeyValueAt(1, curValue);
    m_valueAnimation->setStartValue(startValue-2);
    m_valueAnimation->start();
}


3、引用

1、用qt实现一个温度计控件
2、Qt编写自定义控件2-进度条标尺
3、Qt动画框架:QPropertyAnimation(属性动画)


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

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

相关文章

unity打AB包,AssetBundle预制体与图集(二)

第二步&#xff1a;加载AB包的资源&#xff0c;用于显示 using System.Collections; using System.Collections.Generic; using System.IO; using UnityEngine; using UnityEngine.Networking; using UnityEngine.U2D; using UnityEngine.UI;public class GameLaunch : MonoBe…

B - Little Tiger vs. Deep Monkey

思路&#xff1a; &#xff08;1&#xff09;条件&#xff1a;n道单选题&#xff0c;分值不一定相同&#xff0c;选对或者错&#xff0c;A,B分别做题&#xff0c;得分多者胜&#xff1b; &#xff08;2&#xff09;问题&#xff1a;A至少做对几道题才能保证获胜概率达到50%&a…

web前端js基础------制作滚动图片

1&#xff0c;要求 通过定时器使其出现滚动的效果 可以通过按键控制图片滚动的方向&#xff08;设置两个按钮绑定点击事件&#xff09; 当鼠标悬停时图片停止&#xff0c;鼠标离开时图片继续向前滚动&#xff08;可以设置鼠标的悬停和离开事件&#xff09; 参考如下 conten…

【后端速成 Vue】初识指令(下)

前言&#xff1a; 上一篇初识指令(上)文章中&#xff0c;一共讲解了 v-html&#xff0c;v-show&#xff0c;v-if&#xff0c;v-else&#xff0c;v-else-if&#xff0c;v-on&#xff0c;v-bind 这些指令&#xff0c;当然&#xff0c;还剩不少的指令没有讲解&#xff0c;本问将会…

DBeaver Ultimate forMac/Win中文版:掌控数据宇宙的强大工具

在当今的数字化世界中&#xff0c;数据库管理软件在企业和个人的数据处理中扮演着至关重要的角色。在这篇文章中&#xff0c;我们将介绍一款备受赞誉的数据库管理软件——DBeaver Ultimate&#xff0c;它被广泛应用于各种行业和场景&#xff0c;帮助用户高效地管理和利用他们的…

“Redis与Spring整合及缓存优化“

文章目录 引言1. Spring整合Redis1.1. 为什么选择Redis作为缓存解决方案&#xff1f;Redis的特点和优势Redis与传统关系数据库的对比 1.2. Spring与Redis整合的基本步骤 2. Redis注解式缓存2.1. Spring提供的缓存注解介绍2.2. 使用注解实现方法级别的缓存 3. Redis的击穿、穿透…

系统有同类资源m个,供n个进程共享,若每个进程对资源的最大需求量为k,试问:当m,n,k的值分别为下列情况时(见下表),是否会发生死锁?

一.系统有同类资源m个&#xff0c;供n个进程共享&#xff0c;若每个进程对资源的最大需求量为k&#xff0c;试问&#xff1a;当m,n,k的值分别为下列情况时&#xff08;见下表&#xff09;&#xff0c;是否会发生死锁&#xff1f; &#xff08;1&#xff09;m6;n3;k3 &#xf…

shopee买家通系统一款全自动化操作的软件

Shopee买家通系统可以批量注册虾皮买家号、自动加购加心愿单、根据关键词及产品编号搜索下单。 想要注册虾皮买家号&#xff0c;准备好相应国家的手机号及ip即可&#xff0c;准备好之后按照软件所需格式添加后即可运行自动化注册&#xff0c;注册时可以自动输入手机号、自动接…

linux服务器国内源安装nvm,又快又方便

国内安装nvm的话&#xff0c;如果你的服务器不能访问github&#xff0c;那么使用gitee快速安装还是很方便的&#xff1a; # 能方位github的话&#xff0c;使用这条命令 curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.2/install.sh | bash# 不能访问github的话…

延长千倍!美国阿贡实验室量子比特相干时间新纪录

两个具有长相干时间并强耦合的量子比特&#xff08;图片来源&#xff1a;网络&#xff09; 在书面语、口语以及信息处理领域&#xff0c;连贯性均是实现有效沟通的关键要素&#xff0c;这同样适用于量子比特。 美国能源部&#xff08;DOE&#xff09;下属的阿贡国家实验室所领…

计算机毕业设计java+springboot+vue的在线考试系统

项目介绍 具体在系统设计上&#xff0c;采用了B/S的结构&#xff0c;同时&#xff0c;也使用Java技术在动态页面上进行了设计&#xff0c;后台上采用Mysql数据库&#xff0c;是一个非常优秀的青少年编程在线考试系统。 开发环境 开发语言&#xff1a;Java 后端框架&#xff…

SpirngBoot做H5支付 JSSDK支付 为uniapp小程序提供接口 详细内容包含支付、退款以及支付退款回调 纯微信支付V2支付 最后附上完整代码

可以先看一下下面的IndexController 先理解一下需要用到哪些参数然后再从头开始准备就不会一头蒙了 完整代码&#xff1a; 链接: https://pan.quark.cn/s/518e02b22e4f 提取码&#xff1a;J6f2 可能遇到的坑&#xff1a; 微信支付这个的文档可以说跟没有是一样的 只写入参回…

【HMS Core】推送热门合集4

【问题描述1】 如果云端通知的category&#xff0c;本地通知不支持&#xff0c;如何处理&#xff1f; 【解决方案】 如果云端通知的category&#xff0c;本地通知不支持&#xff0c;或者云端通知的category的取值在分类标准中是不涉及&#xff0c;那么说明该类型不支持本地通…

工业物联网网关解决方案openwrt二次开发无线路由WiFi模块选型

在现今高科技快速发展的时代&#xff0c;无线通信技术已经成为人们日常生活中不可或缺的一部分。而在众多的无线技术产品中&#xff0c;基于IEEE 802.11系列协议的WiFi技术无疑是其中的主流。随着WiFi技术的广泛应用&#xff0c;市面上涌现出了各种各样的主控平台和WiFi模块。今…

魔众文库系统 v5.5.0 批量快捷上传,文档图标优化,档转换逻辑优化

魔众文库系统基于文档系统知识&#xff0c;建立平台与领域&#xff0c;打造流量、用户、付费和变现的闭环&#xff0c;帮助您更好的搭建文库系统。 魔众文库系统发布v5.5.0版本&#xff0c;新功能和Bug修复累计14项&#xff0c;批量快捷上传&#xff0c;文档图标优化&#xff…

JQ完成模拟QQ好友分组案例(介绍JQ实现原理)

当我们写这个案例之前&#xff0c;需要引入好JQ文件&#xff0c;以防没有效果 这个案例的需求请看以下效果图 不能重复点击&#xff0c;只有删除掉之后才可以继续点击 效果图&#xff1a; 代码介绍&#xff1a; <!DOCTYPE html> <html lang"en"><h…

【每日一题】2586. 统计范围内的元音字符串数-2023.11.7

题目&#xff1a; 2586. 统计范围内的元音字符串数 给你一个下标从 0 开始的字符串数组 words 和两个整数&#xff1a;left 和 right 。 如果字符串以元音字母开头并以元音字母结尾&#xff0c;那么该字符串就是一个 元音字符串 &#xff0c;其中元音字母是 a、e、i、o、u 。…

Python采集数据代码示例

基本的爬虫程序的示例&#xff1a; typescript import * as request from request; // 信息 const proxyHost ; const proxyPort ; // 网站的 URL const url ; // 使用 request 库发起请求 request({ url, method: GET, proxy: { host: proxyHost…

vue递归获取树形菜单

文章目录 前言什么是递归&#xff1f; 一、数据集二、 递归函数三、打印树形结构展示 前言 什么是递归&#xff1f; 程序调用自身的编程技巧称为递归&#xff08; recursion&#xff09;。 递归 粗略的理解为 循环 &#xff0c;只不过 递归 是调用自身。 在实际使用中&#xf…

libwebsockets入门

WebSocket是一种在单个TCP连接上进行全双工通讯的协议&#xff0c;用于在Web客户端和服务器之间建立持久连接&#xff0c;进行实时通信。它是HTML5开始提供的一种通讯方式&#xff0c;通过使用WebSocket连接&#xff0c;web应用程序可以执行实时的交互&#xff0c;而不是以前的…