【QT开发笔记-基础篇】| 第四章 事件QEvent | 4.8 绘图事件

news2024/11/15 19:53:25

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

【QT开发笔记-基础篇】 第4章 事件 4.8 绘图事件(1)


本章要实现的整体效果如下:

整体效果

QEvent::Paint

​ 当窗口/控件需要重绘时,触发该事件,它对应的子类是 QPaintEvent

本节通过一个向 QLabel 上绘制高低温曲线的案例,来讲解绘图事件。

有两种实现方式:

  • 自定义标签控件,并重写 paintEvent()
  • 事件过滤器,直接绘制到标准 QLabel

本节采用事件过滤器的方法来实现。


1. 界面放置高低温标签

首先,在 paint-widget.h 中声明两个标签:

#include <QLabel>

class PaintWidget : public QWidget
{
private:
    QLabel* lblHigh;
    QLabel* lblLow;
};

然后,在 paint-widget.cpp 的构造中显示两个标签:

PaintWidget::PaintWidget(QWidget* parent) : QWidget{parent}
{
    QVBoxLayout* verticalLayout = new QVBoxLayout(this);
    verticalLayout->setSpacing(0);
    verticalLayout->setContentsMargins(0, 0, 0, 0);

    //添加一个Label,用于绘制高温曲线
    lblHigh = new QLabel(this);
    lblHigh->setText("");
    lblHigh->setFrameShape(QFrame::Box);
    verticalLayout->addWidget(lblHigh);

    //添加一个Label,用于绘制低温曲线
    lblLow = new QLabel(this);
    lblLow->setText("");
    lblLow->setFrameShape(QFrame::Box);
    verticalLayout->addWidget(lblLow);
}

此时,运行效果如下:

运行效果


2. 安装事件过滤器

在当前窗口中拦截两个标签的事件,就需要拦截发给标签的事件

首先,在 paint_widget.cpp 中为标签安装事件过滤器:

PaintWidget::PaintWidget(QWidget* parent) : QWidget{parent}
{
	// ...

    lblHigh->installEventFilter(this);
    lblLow->installEventFilter(this);
}

然后,在 paint_widget.h 中声明 eventFilter() 函数:

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

最后,在 paint_widget.cpp 中实现 eventFilter() 函数

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

具体流程:

  • 窗口刚出现时两个标签需要重绘,框架发送 QEvent::Paint 事件给标签
  • 事件被当前窗口拦截,进而调用其 eventFilter() 方法
  • eventFilter()中,判断事件类型以及目标控件,来调用绘制高低温曲线的函数

3. 实现 paintHigh、paintLow

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

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

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

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

paintHigh() 实现如下:

#include <QPainter>

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

void PaintWidget::paintHigh()
{
    QPainter painter(lblHigh);
    painter.setRenderHint(QPainter::Antialiasing, true);  // 抗锯齿

    // 1. 计算 x 轴坐标
    int pointX[7] = {0};
    for ( int i = 0; i < 7; i++ ) {
        pointX[i] = lblHigh->pos().x() + PADDING + (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 = lblHigh->height() / 2;
    int increment = 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(2);                  //设置画笔宽度为1
    pen.setColor(QColor(255, 0, 0));  //设置颜色

    painter.setPen(pen);
    painter.setBrush(QColor(255, 0, 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 PaintWidget::paintLow()
{
    QPainter painter(lblLow);
    painter.setRenderHint(QPainter::Antialiasing, true);  // 抗锯齿

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

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

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

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

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

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

    painter.setPen(pen);
    painter.setBrush(QColor(0, 0, 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(mLowTemp[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]);
    }
}

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

此时效果


4. 产生随机温度

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

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

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

然后,在 paint_widget.cpp 中实现 updateTemp() 函数:

#include <QRandomGenerator64>

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

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

最后,在 paint_widget.cpp 的构造中,手动调用 updateTemp() 函数:

PaintWidget::PaintWidget(QWidget* parent) : QWidget{parent}
{
    // ...

    updateTemp();

    lblHigh->installEventFilter(this);
    lblLow->installEventFilter(this);
}

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

此时效果


5. 双击更新高低温曲线

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

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

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

最终效果

此时整体调用流程:

  • 拦截标签的双击事件,调用 updateTemp() 函数
  • lblHigh->update() 表示要重绘标签,框架发送 QEvent::Paint 事件给标签
  • 事件被窗口拦截,进而调用其 eventFilter()
  • eventFilter() 中,调用 paintHigh()paintLow() 来真正在标签上绘制曲线

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

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

相关文章

“揭秘!如何通过京东商品详情接口轻松获取海量精准商品信息!“

京东商品详情接口可以通过HTTP GET请求获取商品详情信息。 请求参数包括num_iid&#xff0c;表示JD商品ID。 请求示例&#xff1a; GET /jd/item_get/?num_iid10335871600 HTTP/1.1 Host: api-vx.Taobaoapi2014.cn Connection: close Accept-Encoding: gzip 点击获取…

决策树深度探索: 从基础构建到机器学习应用

机器学习 第六课 决策树 概述决策树的基本概念决策树的应用决策树的基本构建节点 (Node)分支 (Branch)决策树的构造过程 信息增益熵 (Entropy)条件熵 (Conditional Entropy)信息增益 (Information Gain) 信息熵计算计算数据集的熵计算在给定属性条件下的熵计算信增益 (Informat…

深入剖析Java类加载过程:探寻类加载器的奥秘

摘要: 一个java文件从被加载到被卸载这个生命过程&#xff0c;总共要经历4个阶段&#xff1a; 加载->链接&#xff08;验证准备解析&#xff09;->初始化&#xff08;使用前的准备&#xff09;->使用->卸载 其中类加载过程包括加载、验证、准备、解析和初始化五个阶…

如何使用Abaqus import进行预应力跌落仿真

跌落测试除了单次跌落外&#xff0c;根据不同的标准需求&#xff0c;还有多次跌落或者预应力跌落&#xff0c;上次我们进行了单次跌落在Abaqus中的实现过程&#xff0c;今天我们使用Abaqus Import功能&#xff0c;进行卡扣的预应力跌落。 首先进行卡扣的装配仿真&#xff0c;在…

Note——time

time import import datetime import timeDefinition of time from 1970-01-01 00:00:00 UTC Coordinated Universal Time as the float format of ‘seconds’ For example use structured time lists [ year,month,day,hours,minutes,seconds…] 表示从1970-01-01 00:00:…

当苹果铅笔不能工作时,不一定都是苹果铅笔的问题!苹果铅笔不工作时如何修复

你的苹果铅笔没有按预期工作,可能有几个原因;大多数都有相当简单的修复方法。苹果铅笔的故障排除技巧在两代配件中基本相同。 本文中的信息适用于兼容iPad上的苹果铅笔(第二代)和苹果铅笔(第一代)。 检查蓄电池 苹果铅笔的电池必须充电才能工作。要检查iPad上的电池状…

云表:为什么要使用低代码开发?低代码选择指南

随着信息技术的不断发展&#xff0c;我们进入了一个数字化的时代。在这个时代&#xff0c;IT技术已经成为推动全球信息化浪潮的重要力量。然而&#xff0c;随着应用程序开发技术的不断发展&#xff0c;开发效率并没有像摩尔定律一样快速提升&#xff0c;反而成为了瓶颈。因此&a…

Django实现音乐网站 (21)

使用Python Django框架做一个音乐网站&#xff0c; 本篇音乐播放器功能完善及原有功能修改。 目录 播放列表修改 视图修改 删除、清空播放器 设置路由 视图处理 修改加载播放器脚本 模板修改 脚本设置 清空功能实现 删除列表音乐 播放列表无数据处理 视图修改 播放…

uniapp自定义右击菜单

效果图&#xff1a; 代码&#xff1a; 1、需要右击的view: <view class"answer-box" contextmenu.stop.prevent.native"showRightMenu($event, item, content)"> </view>2、右击弹出层&#xff1a; <view v-if"visible" :styl…

001.第一个C语言项目

Visual studio2022的使用 创建第一个C语言项目和源文件 https://blog.csdn.net/qq_45037165/article/details/124520286 第一个C语言项目 #include<stdio.h> int main() {printf("Hello World");return 0; }运行结果&#xff1a; 第一行为库函数&#xff0…

docker容器健康状态健康脚本

代码&#xff1a; #!/bin/bash# 定义要监控的容器名称 container_name"mysql-container"# 使用Docker命令来检查容器的运行状态 container_status$(docker inspect --format"{{.State.Status}}" "$container_name")# 检查容器状态并进行相应操作…

PAM从入门到精通(十五)

接前一篇文章&#xff1a;PAM从入门到精通&#xff08;十四&#xff09; 本文参考&#xff1a; 《The Linux-PAM Application Developers Guide》 先再来重温一下PAM系统架构&#xff1a; 更加形象的形式&#xff1a; 六、整体流程示例 1. 官方文档中的例程及解析 从PAM从入…

数字人引领政务革新,AI智慧政务一体机的未来服务之路

数字人、数字员工&#xff0c;已是当下不可忽视的新型劳动者&#xff0c;它们在众多领域频频出圈&#xff0c;持续打破人们的固有认知。在近期公布的2023数字人实践案例TOP10里&#xff0c;CCTV央视网数字代言人阿央”带领新一代青年紧跟国家大事、关注国家发展&#xff1b;首位…

Java操作Python及数据交互最佳实践

Java操作Python最佳实践 1、Java与Python的互操作性2、Java调用Python脚本及数据交互2.1、准备工作2.2、执行一段Python代码2.3、执行Python文件脚本2.4、执行Python文件中的指定方法2.5、执行含有第三方库的Python文件3、附录(完整代码)1、Java与Python的互操作性 在当今的软…

Mac电脑无法识别移动硬盘怎么办?

很多人都喜欢在Mac电脑上办公、学习&#xff0c;但有时我们将移动硬盘连接Mac电脑时&#xff0c;却会发现电脑无法识别移动硬盘。那么&#xff0c;Mac电脑无法识别移动硬盘怎么办呢&#xff1f; Mac无法识别移动硬盘的原因 导致Mac不识别移动硬盘的原因有很多&#xff0c;你可…

交换机知识汇总大全,值得收藏!

下午好&#xff0c;我的网工朋友。 说到网工相关的网络设备&#xff0c;你的第一反应是啥&#xff1f;答案基本上都是交换机&#xff0c;是吧。 交换机作为网工最常用的设备之一&#xff0c;你懂多少&#xff1f; 今天想带你温顾一番交换机的基础知识&#xff0c;顺便说说&a…

iOS如何实现语音转文字功能?

1.项目中添加权限 Privacy - Speech Recognition Usage Description : 需要语音识别权限才能实现语音转文字功能 2.添加头文件 #import <AVFoundation/AVFoundation.h> #import<Speech/Speech.h> 3.实现语音转文字逻辑: 3.1 根据wav语音文件创建请求 SFSpeechU…

Macos文件列表创建工具:Nifty File Lists for mac

Nifty File Lists Mac是一款功能强大的文件列表工具&#xff0c;它可以帮助用户在Mac电脑上保存、整理和编辑文件列表。该软件支持将文件列表保存为MS Excel、数字和页面友好的CSV&#xff08;逗号分隔值&#xff09;、TSV&#xff08;制表符分隔值&#xff09;甚至HTML格式&am…

并发程序设计

一、进程的创建和回收 (一)进程的概念 1、进程&#xff01;程序 程序是静态的&#xff0c;而进程是动态的&#xff0c;进程是程序执行的一种状态。 2、进程和程序的区别 1&#xff09;进程控制块中包含进程的属性 2&#xff09;程序在磁盘里面&#xff0c;堆栈都是在内存中…