【QT开发笔记-基础篇】| 第五章 绘图QPainter | 5.15 绘制温度曲线

news2025/4/28 0:38:49

本节对应的视频讲解:B_站_视_频

https://www.bilibili.com/video/BV1L24y1Q7hc

前面已经讲解了 QPainter 绘图的基本使用

其中包括:

  • 绘制图形

    点、线、矩形、圆角矩形、椭圆、圆、圆弧、饼图、弦图、多段线、多边形、路径、文本、图片

  • 画笔设置

    线宽、颜色、样式、连接、末端

  • 画刷设置

    颜色、样式

  • 高级选项

    变换、抗锯齿

当我们在项目中需要一些简单的绘制时,比如绘制温度曲线,可以直接使用 QPainter

当然了,如果需要更加复杂的曲线绘制,或柱状图等的绘制时,可以使用如下两个:

  • QChart
  • QCustomPlot

后面会出一个专题:《Qt开发专题-绘制曲线》,专门讲解这两个类的使用

本节使用 Qt 中的 QPainter,实现绘制高低温曲线,效果如下:

image-20221204143901566

本节包含以下技术点:

  • 事件过滤器

  • 画笔颜色、样式

  • 虚线、实线

  • 绘制文本

接下来开始,从新建工程开始讲解


1. 新建工程

首先,新建工程 TempCurve

image-20221222211411798


2. 添加高低温标签

首先,在窗口上拖放两个标签,修改它们的 name 为 lblHigh,lblLow,并设置它们在 widget 中垂直布局

然后,设置 widget 样式表:

QLabel {
	background-color: rgb(63, 106, 138);
}

此时,效果如下:

image-20221222212254698

最后,将 widget 中的空隙都设置为 0

image-20221222212842755

此时,效果如下:

image-20221222213012024


3. 安装事件过滤器

温度曲线绘制到标签上

首先,在 widget 中为标签安装事件过滤器

Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget)
{
    ui->setupUi(this);

    // 安装事件过滤器
    ui->lblHigh->installEventFilter(this);
    ui->lblLow->installEventFilter(this);
}

然后,在 widget.h 中声明 eventFilter

class Widget : public QWidget
{
public:
    bool eventFilter(QObject* watched, QEvent* event);
};

并在 widget.cpp 中实现 eventFilter 函数

bool Widget::eventFilter(QObject* watched, QEvent* event)
{
    if ( event->type() == QEvent::Paint ) {
        if ( watched == ui->lblHigh ) {
            //            paintHigh(); // 后边实现
            qDebug() << "paint lblHigh";
        }
        if ( watched == ui->lblLow ) {
            //            paintLow();	// 后边实现
            qDebug() << "paint lblLow";
        }
    }

    return QWidget::eventFilter(watched, event);
}

4. 实现 paintHigh、paintLow

首先,在 widget.h 中声明这两个函数,并声明两个数组,用来记录高温和低温

class Widget : public QWidget
{
public:
    // 绘制高低温曲线
    void paintHigh();
    void paintLow();

private:
    int mHighTemp[7] = {0};
    int mLowTemp[7] = {0};
};

然后,在 widget.cpp 中实现这两个函数

paintHigh 实现如下:

// 温度曲线相关的宏
#define PADDING       50
#define INCREMENT     8     // 温度曲线像素增量
#define POINT_RADIUS  3     // 曲线描点的大小
#define TEXT_OFFSET_X 12    // 温度文本相对于点的偏移
#define TEXT_OFFSET_Y 10    // 温度文本相对于点的偏移

void Widget::paintHigh()
{
    QPainter painter(ui->lblHigh);
    painter.setRenderHint(QPainter::Antialiasing, true);    // 抗锯齿

    // 1. 计算 x 轴坐标
    int pointX[7] = {0};
    for ( int i = 0; i < 7; i++ ) {
        pointX[i] = ui->lblHigh->pos().x() + PADDING + (ui->lblHigh->width() - PADDING * 2) / 6 * i;
    }

    // 2. 计算 y 轴坐标
    // 2.1 计算平均值
    int tempSum     = 0;
    int tempAverage = 0;

    for ( int i = 0; i < 7; i++ ) {
        tempSum += mHighTemp[i];
    }

    tempAverage = tempSum / 7;    // 最高温平均值

    // 2.2 计算 y 轴坐标
    int pointY[7] = {0};
    int yCenter   = ui->lblHigh->height() / 2;
    int increment = ui->lblHigh->height() / 20;
    for ( int i = 0; i < 7; i++ ) {
        pointY[i] = yCenter - ((mHighTemp[i] - tempAverage) * increment);
    }

    // 3. 开始绘制
    // 3.1 初始化画笔
    QPen pen = painter.pen();
    pen.setWidth(1);                      //设置画笔宽度为1
    pen.setColor(QColor(255, 170, 0));    //设置颜色

    painter.setPen(pen);
    painter.setBrush(QColor(255, 170, 0));    //设置画刷颜色
    painter.setFont(QFont("Microsoft YaHei", 14));

    // 3.2 画点、写文本
    for ( int i = 0; i < 7; i++ ) {
        painter.drawEllipse(QPoint(pointX[i], pointY[i]), POINT_RADIUS, POINT_RADIUS);
        painter.drawText(QPoint(pointX[i] - TEXT_OFFSET_X, pointY[i] - TEXT_OFFSET_Y), QString::number(mHighTemp[i]) + "°");
    }

    // 3.3 绘制曲线
    for ( int i = 0; i < 6; i++ ) {
        if ( i == 0 ) {
            pen.setStyle(Qt::DotLine);    //虚线
            painter.setPen(pen);
        } else {
            pen.setStyle(Qt::SolidLine);    // 实线
            painter.setPen(pen);
        }
        painter.drawLine(pointX[i], pointY[i], pointX[i + 1], pointY[i + 1]);
    }
}

paintLow 实现如下:

void Widget::paintLowCurve()
{
    QPainter painter(ui->lblLowCurve);
    painter.setRenderHint(QPainter::Antialiasing, true);  // 抗锯齿

    // 1. 计算 x 轴坐标
    int pointX[7] = {0};
    for ( int i = 0; i < 7; i++ ) {
        pointX[i] = ui->lblLowCurve->pos().x() + PADDING + (ui->lblLowCurve->width() - PADDING * 2) / 6 * i;
    }

    // 2. 计算 y 轴坐标
    // 2.1 计算平均值
    int tempSum = 0;
    int tempAverage = 0;

    for ( int i = 0; i < 7; i++ ) {
        tempSum += mLowTemperature[i];
    }

    tempAverage = tempSum / 7;  // 最高温平均值

    // 2.2 计算 y 轴坐标
    int pointY[7] = {0};
    int yCenter = ui->lblLowCurve->height() / 2;
    int increment = ui->lblLowCurve->height() / 20;
    for ( int i = 0; i < 7; i++ ) {
        pointY[i] = yCenter - ((mLowTemperature[i] - tempAverage) * increment);
    }

    // 3. 开始绘制
    // 3.1 初始化画笔
    QPen pen = painter.pen();
    pen.setWidth(1);                    // 设置画笔宽度为1
    pen.setColor(QColor(0, 255, 255));  // 设置颜色

    painter.setPen(pen);
    painter.setBrush(QColor(0, 255, 255));  //设置画刷颜色
    painter.setFont(QFont("Microsoft YaHei", 14));

    // 3.2 画点、写文本
    for ( int i = 0; i < 7; i++ ) {
        painter.drawEllipse(QPoint(pointX[i], pointY[i]), POINT_RADIUS, POINT_RADIUS);
        painter.drawText(QPoint(pointX[i] - TEXT_OFFSET_X, pointY[i] - TEXT_OFFSET_Y), QString::number(mLowTemperature[i]) + "°");
    }

    // 3.3 绘制曲线
    for ( int i = 0; i < 6; i++ ) {
        if ( i == 0 ) {
            pen.setStyle(Qt::DotLine);  //虚线
            painter.setPen(pen);

        } else {
            pen.setStyle(Qt::SolidLine);  // 实线
            painter.setPen(pen);
        }
        painter.drawLine(pointX[i], pointY[i], pointX[i + 1], pointY[i + 1]);
    }
}

此时,由于数组 mHighTemp 和 mLowTemp 数组的初始值都是 0 ,因此绘制出来的是7条水平直线的连接,如下:

image-20221222221555120


5. 产生随机温度

接下来,随机生成高温数组、低温数组

首先,在 widget.h 中声明一个 updateTemp 的函数

class Widget : public QWidget
{
public:
    // 更新高低温
    void updateTemp();
};

然后,在 widget.cpp 中实现 updateTemp

#include <QRandomGenerator64>

void Widget::updateTemp()
{
    for ( int i = 0; i < 7; i++ ) {
        mHighTemp[i] = 20 + QRandomGenerator::global()->generate() % 10;
        mLowTemp[i]  = -5 + QRandomGenerator::global()->generate() % 10;
    }

    ui->lblHigh->update();
    ui->lblLow->update();
}

最后,在 widget.cpp 的构造中,手动调用 updateTemp

Widget::Widget(QWidget* parent) : QWidget(parent), ui(new Ui::Widget)
{
    ui->setupUi(this);

    updateTemp();

    // 安装事件过滤器
    ui->lblHigh->installEventFilter(this);
    ui->lblLow->installEventFilter(this);
}

此时,第一次运行后,就可以生成高低温曲线了,如下:

image-20221222222951163


6. 双击更新高低温曲线

在 eventFilter 中,拦截鼠标双击事件,如下:

bool Widget::eventFilter(QObject* watched, QEvent* event)
{
    if ( event->type() == QEvent::Paint ) {
        if ( watched == ui->lblHigh ) {
            paintHigh();
            //            qDebug() << "paint lblHigh";
        }
        if ( watched == ui->lblLow ) {
            paintLow();
            //            qDebug() << "paint lblLow";
        }
    } else if ( event->type() == QEvent::MouseButtonDblClick ) {
        updateTemp();
    }

    return QWidget::eventFilter(watched, event);
}

此时,每次双击鼠标,就可以刷新高低温曲线了!

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

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

相关文章

crawlergo联动xray漏洞挖掘

SRC漏洞挖掘 简介 SRC漏洞平台&#xff1a;安全应急响应中心&#xff08;SRC, Security Response Center&#xff09;&#xff0c;是企业用于对外接收来自用户发现并报告的产品安全漏洞的站点。说白了&#xff0c;就是连接白帽子和企业的平台&#xff0c;你去合法提交漏洞给他…

研讨会回顾 | 中国企业在软件自动化测试方面的实践现状、挑战及趋势探讨

2022年12月6日&#xff0c;龙智与软件测试自动化“领导者”SmartBear联合举办了主题为“如何通过自动化测试实现降本、增效与提质”的在线研讨会。 此次研讨会中&#xff0c;龙智技术总监李毅为大家分享中国企业在质量和测试中面临的挑战&#xff0c;以及自动化测试实践的现状与…

关于 Serverless 应用架构对企业价值的一些思考

作者&#xff1a;寒斜 前言 对于企业方而言&#xff0c;最关心的核心诉求就是如何能获取更多的营收&#xff0c;更高的利润&#xff0c;通俗点说就是如何赚更多的钱&#xff1b;企业赚钱的方式主要是通过出售企业服务&#xff0c;当用户购买更多的企业服务&#xff0c;企业赚…

【Linux】vim的基本操作

这里写目录标题一、vim编辑器1、基本概念2、基本操作二、vim指令集1、命令模式命令集2、底行模式命令集一、vim编辑器 1、基本概念 vi和vim都是多模式的编辑器&#xff0c;vim是vi的升级版本&#xff0c;并且兼容vi的所以指令。 vim有多种模式&#xff0c;本文讲解常用的3种模…

c++入门(命名空间+缺省参数+函数重载)

文章目录1. 命名空间1. c语言的两个域2. 命名空间的使用1.类型问题命名空间A和B的实现2. 变量问题3.三种访问方法1.指定命名空间访问2. 全局展开using namespace std 的含义尽量不使用using namespace std的原因3. 部分展开2. 缺省参数(备胎)1. 概念2.全缺省参数3.半缺省参数错…

2023/1/6 Vue学习笔记-3-生命周期

1 引出生命周期 透明度变化的案例&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport&qu…

HTML实现动态旋转字母背景

演示 css html, body {background: radial-gradient(#181818, #000000);margin: 0;padding: 0;border: 0;-ms-overflow-style: none;}::-webkit-scrollbar {width: 0.5em;height: 0.5em;background-color: #c7c7c7;}/*定义滚动条轨道 内阴影圆角*/::-webkit-scrollbar-track {…

数据分析之数据相关性分析

相关性分析 作者&#xff1a;学者科技 时间&#xff1a;2022/12/25 应用场景 发现数据之间的关联性 比如 啤酒 和 尿布 删减统计指标 比如 城市里的温度传感器&#xff0c;相关性强的可以去掉以节约成本 挑选回归建模的变量 选择与因变量相关性高的自变量自变量间如果有高度…

数字漫画行业繁花似锦,国漫扎根本土文化“向外生长”?

2022年&#xff0c;一半是寒冰&#xff0c;一半是烈火。这一年&#xff0c;我们遇到了消费互联网的降温&#xff0c;包括互联网大厂降本增效潮到来&#xff0c;电商大促节不再公布销售额。同样&#xff0c;我们也见证了经济迸发的新活力&#xff0c;比如元宇宙、虚拟人掀起的热…

配置Domino解决CORS跨域问题

大家好&#xff0c;才是真的好。 前面我们讲过几篇Web应用开发的简单示例&#xff0c;主要功能是同一个站点的同一个应用里里面查看和搜索文档。如果对数据进行操作&#xff0c;可能多少会遇到网页跨域访问问题。 跨域&#xff0c;简单来说&#xff0c;就是浏览器对javascrip…

【知识图谱导论-浙大】第二章:知识图谱的表示

前文&#xff1a; 【知识图谱导论-浙大】第一章&#xff1a;知识图谱概论 本节内容的视频讲解如下&#xff1a; 【知识图谱理论】&#xff08;浙大2022知识图谱课程&#xff09;第二讲-知识图谱的表示什么是知识表示 简而言之&#xff0c;知识表示&#xff08;Knowledge Rep…

【HTML】纯CSS居然能做出这种效果,一款宝藏网页分享(超详细讲解 | 附源码)

&#x1f482;作者简介&#xff1a; THUNDER王&#xff0c;一名热爱财税和SAP ABAP编程以及热爱分享的博主。目前于江西师范大学会计学专业大二本科在读&#xff0c;同时任汉硕云&#xff08;广东&#xff09;科技有限公司ABAP开发顾问。在学习工作中&#xff0c;我通常使用偏后…

【ASP.NET】家乡网站设计作业「历史」「人文」「自然」「美食」「高中」

文章目录作业描述结果展示代码MasterPage.master(母版页)首页历史页人文页美食页景观页高中页完整资源链接上学期其中ASP.NET网站设计老师布置的作业&#xff0c;在此记录一下&#xff1b;作业描述 为自己的家乡设计一个网站&#xff1a; 要求&#xff1a; 1.网站主要是展示和…

TYPE-C和USB-C接口有什么区别?

USB-C接口全称为USB Type-C&#xff0c;属于USB 3.0下一代接口&#xff0c;其亮点在于更加纤薄的设计、更快的传输速度&#xff08;最高可达10Gbps&#xff09;、更强的电力传输&#xff08;最高100W&#xff09;&#xff0c;此外USB-C接口还支持双面插入&#xff0c;正反面随便…

【NCC】之三:FFT(DFT)加速协方差的计算

FFT加速计算两个图的协方差文章目录<center> FFT加速计算两个图的协方差1. 傅里叶变换和卷积1.1 卷积定理1.2 空域卷积和频域乘积的复杂度2. opencv中的DFT3. FFT用于NCC4. 测试结果部分代码1. 傅里叶变换和卷积 1.1 卷积定理 图片来源 在空域上的卷积就是上面的动图所展…

再学C语言27:输入和输出——缓冲区

I/O函数&#xff1a;输入/输出函数 I/O函数将信息传输至程序并从程序中传出信息&#xff0c;如printf()、scanf()、getchar()、putchar()等函数 getchar()和putchar()每次输入/输出一个字符 示例代码&#xff1a; #include <stdio.h> int main(void) {char c;// 输入回…

Vivado综合设置之-resource_sharing

-​resource_sharing用于对算数运算&#xff08;加法、减法和乘法&#xff09;实现资源共享&#xff0c;以节约LUT资源&#xff0c;有3个值&#xff1a;auto、off和on&#xff0c;默认是auto。 默认情况下&#xff0c;将resource_sharing设置为auto即可。 本文验证-resource_…

[Leetcode] 将二叉搜索树变平衡

将二叉搜索树变平衡&#xff1a;https://leetcode.cn/problems/balance-a-binary-search-tree/给你一棵二叉搜索树&#xff0c;请你返回一棵 平衡后的二叉搜索树&#xff0c;新生成的树应该与原来的树有着相同的节点值。如果有多种构造方法&#xff0c;请你返回任意一种。如果一…

Codeforces Round #842 (Div. 2)-C. Elemental Decompress

题目&#xff1a; 题目大意&#xff1a; 给定一个数列t&#xff0c;你构造两个数列a和b&#xff0c;使得max(a[i],b[i])t[i] 核心思想&#xff1a; 1、先根据给出的数组进行放置&#xff0c;优先放到a数组中&#xff0c;如果这个数已经在a数组中出现了&#xff0c;再去放到b数…

sentinel的使用

一、sentinel控制台的使用1、sentinel控制台jar包地址&#xff1a;Releases alibaba/Sentinel GitHub账号密码都为sentinel控制台访问地址&#xff1a;http://localhost:80802、sentinel的maven坐标<dependency><groupId>com.alibaba.cloud</groupId><a…