QPaint绘制自定义坐标轴组件00

最终效果

1.创建一个ui页面,修改背景颜色

鼠标右键->改变样式表->添加颜色->background-color->选择合适的颜色->ok->Apply->ok

重新运行就可以看到widget的背景颜色已经改好

2.创建一个自定义的widget窗口小部件类,class MyChart : public QWidget

mychart.h

#ifndef MYCHART_H
#define MYCHART_H

#include <QWidget>
#include <QPainter>
#include <QString>

struct DataNode
{
    int value;
    QString key;
};

// MyChart继承自QWidget类,是一个窗口小部件。
class MyChart : public QWidget
{
    Q_OBJECT
public:
    // `explicit` 是 C++ 中的一个关键字,用于修饰类的构造函数,表示该构造函数只能用于显式地创建对象,不能被隐式地调用。
    // 只能通过MyChart painter = MyChart(parent)的方式显式地创建一个 `MyChart` 对象:
    // `parent` 参数的默认值为 `nullptr`,这表示如果没有提供父部件的指针,那么 `MyChart` 就没有父部件,即它是一个独立的窗口部件。
    explicit MyChart(QWidget *parent = nullptr);
    void updateValue(const DataNode &node);

protected:
    // `paintEvent(QPaintEvent *event)` 是一个事件处理函数,
    // 在 Qt 框架中,当需要重绘窗口部件时就会自动触发 `paintEvent(QPaintEvent *event)` 函数,
    // 以便开发者可以实现窗口部件的绘制逻辑,从而更新窗口的显示内容。
    // 在窗口需要进行重绘时,Qt 框架会自动调用 `MyChart` 对象的 `paintEvent(QPaintEvent *event)` 函数,从而实现图表的绘制更新。
    // 由于 `paintEvent` 函数是在需要重绘窗口部件时自动调用的,因此我们不需要手动调用它。
    // 当然,如果需要手动更新窗口部件的显示内容,
    // 也可以使用 `QWidget` 类中提供的 `update()` 函数或 `repaint()` 函数来触发 `paintEvent` 函数的调用,
    // 从而实现窗口的重绘。但通常情况下,Qt 框架会自动处理窗口部件的刷新和重绘。
    // `paintEvent` 函数是在 `QWidget` 类中定义的虚函数,
    // 它被设计为在窗口部件需要重新绘制时自动调用,以便让程序员有机会对窗口的内容进行绘制修改。
    // 在 `QWidget` 子类中,如果需要修改默认的绘制行为,则可以重写 `paintEvent` 函数来实现。
    void paintEvent(QPaintEvent *event);

private:
    int yMaxValue = 10;
    int maxNodeNum = 110;
    QList<DataNode> listDataNode;
};

#endif // MYCHART_H

 mychart.cpp

#include "mychart.h"

MyChart::MyChart(QWidget *parent) : QWidget(parent)
{

}

// 数据刷新
void MyChart::updateValue(const DataNode &node)
{
    // 如果当前列表中的数据节点数量已经达到了最大值 `maxNodeNum`,
    // 先删除队列头部的元素,即最早加入的元素(使用 `removeFirst()` 函数)。
    if(listDataNode.size() >= maxNodeNum) {
        listDataNode.removeFirst();
    }
    // 然后,将数据节点 `node` 添加到当前列表的末尾,使用 `append()` 函数实现。
    listDataNode.append(node);
    // 最后,将整个图表更新,调用 `update()` 函数。
    // `update()` 函数是用来触发 `paintEvent()` 函数的信号的。
    // 当窗口或控件需要更新或重绘自己时,它们会同时发射一个 `update()` 信号。
    // 这个信号会被 Qt 的事件循环机制捕获,最终调用 `paintEvent()` 函数进行绘图。
    // 因此,如果不调用 `update()` 函数,`paintEvent()` 函数就不会被调用,也就不会更新图表的显示内容。
    update();
}

// 图标绘制
// `paintEvent` 函数中的调用实际上是在继承关系中向上查找到的 `QWidget::paintEvent()` 函数的实现,
// 它在需要绘制更新时被自动触发。
// 在默认情况下,这个函数为空实现,因此需要我们手动重写它并自己实现绘图功能。
void MyChart::paintEvent(QPaintEvent *event)
{
    (void)event;
    // `QPainter` 是 Qt 中的一个绘图工具类,它封装了各种绘制函数和处理设备上下文的能力。
    // 通过使用 `QPainter` 类可以在窗口、部件和其它设备上上进行绘图操作。
    // 通过调用 `painter` 的各种绘制函数可以在空白的窗口部件上一步步画出你需要的复杂图形,包括直线、圆弧、多边形、文本等等。
    // `this` 关键字是指向当前对象的指针,即指向调用成员函数的对象的指针。
    // `this` 关键字可以用来访问对象的成员变量和成员函数,区分局部变量和成员变量。
    // `this` 指的是当前 `MyChart` 类型的对象,也就是指示当前需要绘制图表的部件对象。
    // 在这个函数中,我们通过将对象指针传给 `QPainter` 构造函数,来创建一个绘制器,使用它进行绘图操作。
    // 需要注意的是,`this` 关键字指向的是对象的指针,而不是类本身。
    // 所以说,`this` 不是用来区分类和对象的关键字,而是用来访问对象内部成员的工具。
    QPainter painter(this);
    // 启用抗锯齿功能,即让绘制的线条、边缘等对锯齿进行平滑处理,让图像更加平滑和自然。
    painter.setRenderHint(QPainter::Antialiasing);
    // `QPen` 是 Qt 中的一个画笔类,用于控制绘图时线条的样式、颜色和粗细等参数,通常与 `QPainter` 类一起使用。
    // 在默认情况下,`QPen` 对象的颜色为黑色,线宽为0,样式为实线。
    // 可以通过 `setBrush()`、`setColor()`、`setWidth()`、`setStyle()` 等函数来设置画笔的各个属性。
    QPen pen;
    pen.setWidth(2);
    pen.setColor(QColor(100, 200, 100));
    // setPen(pen)将创建的 `QPen` 画笔对象传入painter,就可以使用该笔刷来绘制线条、形状、文本等各种图形元素了。
    painter.setPen(pen);

    //坐标轴
    // 高度
    int yLength = this->height() * 0.9;
    // 长度
    int xLength = this->width();
    // `QPoint` 类是 Qt 中的一个点类,用于表示二维平面坐标系中的一个点,其具体坐标值由 `x()` 和 `y()` 成员函数获取。
    // `zero` 是一个 `QPoint` 类型的点,由横坐标`this->width() * 0.03`纵坐标`this->height() * 0.95` 两个数值组成,
    // 它代表了坐标系中的原点或者起始点,用来确定坐标轴的位置。
    QPoint zero(this->width() * 0.03, this->height() * 0.95);
    // 以下两行代码通常表示绘制一个基础的坐标系,绘制坐标系通常是绘制图表的第一步,是各种图表展示中的基础步骤之一。
    // 从 `zero` 点开始,向上绘制一条长度为 `yLength` 的水平线段表示y轴,并向右绘制一条长度为 `xLength` 的垂直线段表示x轴。
    // 这里使用了 `QPoint` 类型的构造函数创建起始点和结束点的对象。
    // y轴,原点zero,终点QPoint(zero.x() + xLength, zero.y())
    painter.drawLine(zero, QPoint(zero.x(), zero.y() - yLength));
    // x轴,原点zero,终点QPoint(zero.x() + xLength, zero.y())
    painter.drawLine(zero, QPoint(zero.x() + xLength, zero.y()));
    // 刻度间隔数
    int durationX = 100;
    int durationY = 10;
    // 每个刻度之间间隔的长度
    int xPeriod = xLength / durationX - 1;
    int yPeriod = yLength / durationY - 1;
    // 绘制坐标轴上的刻度和数字,用以标示坐标轴上每个刻度对应的数值
    // 遍历 y 轴的每个刻度位置,从起点 `zero` 开始向上连续绘制 `durationY` 个横向线段用于表示刻度。
    for (int i = 0; i <= durationY; ++i) {
        // 绘制表示y轴刻度的水平线段
        painter.drawLine(QPoint(zero.x() - 1, zero.y() - i * yPeriod), QPoint(zero.x() + 5, zero.y() - i * yPeriod));
        QString value = QString::number(i * 2);
        // 绘制刻度数值
        painter.drawText(QPoint(zero.x() - 25, zero.y() - i * yPeriod + 5), value);
    }
    for (int i = 0; i < durationX; ++i) {
        // 绘制表示x轴刻度的垂直线段
        painter.drawLine(QPoint(zero.x() + i * xPeriod, zero.y() + 3), QPoint(zero.x() + i * xPeriod, zero.y() - 5));
    }
    // 更新数据
    QList<QPoint> pointList;
    for(int i = 0; i < listDataNode.size(); i++) {
        DataNode node = listDataNode.at(i);
        QString key = node.key;
        int value = node.value;
        // 当前数据在x轴位置对应的刻度值
        int xOffset = zero.x() + i * xPeriod;
        // 当前数据在y轴位置对应的刻度值
        int yOffset = value * yLength / yMaxValue;
        // 像数据列表中添加数据转换后对应的坐标点
        pointList << QPoint(xOffset, zero.y() - yOffset);
        // 使用 `QTransform` 类对绘制坐标文本的位置和方向进行变换
        QTransform transform;
        // `translate()` 函数将文本的绘制起点平移 (`xOffset + 5`, `zero.y() - 7`) 的位置,
        // 即向右偏移5个像素,向上偏移7个像素,这是调试后比较合适的显示位置
        transform.translate(xOffset + 5, zero.y() - 7);
        // `rotate(-45)` 函数将文本沿顺时针方向旋转 45 度。
        transform.rotate(-45);
        // `setTransform()` 函数将 transform 对象设置为画笔对象 painter 的当前变换矩阵。
        painter.setTransform(transform);
        // `drawText()` 函数在变换后的位置绘制文本。
        painter.drawText(0, 5, key);
        // `resetTransform()` 函数将画笔对象的变换矩阵重置为原始状态。
        // 这个步骤很重要,如果不重置的话,下次绘制的文本会沿之前的变换矩阵进行绘制。
        painter.resetTransform();
    }
    // 折线线条宽度
    pen.setWidth(3);
    // 折现线条颜色
    pen.setColor(Qt::red);
    painter.setPen(pen);
    // 遍历并连接个数据节点,绘制折线
    for(int i = 0; i < pointList.size(); i++) {
        if((i+1) < pointList.size()) {
            // 连接个数据点,绘制折线
            painter.drawLine(pointList.at(i), pointList.at(i+1));
        }
    }
}

3.添加一个用于绘制自定义控件的控件,一般是Qwidget,修改QWidget的类属性,提升为自定义的类

提升类完成后qt designer显示当前组件已经是MyChart类

 重新编译运行后,原来的QWidget子窗口页面变成了自定义的Mychart页面

编写应用代码,应用自己编写的MyChart类实现数据刷新

charttest.h

#ifndef CHARTTEST_H
#define CHARTTEST_H

#include <QWidget>
#include <QTimer>
#include <QTime>
#include "mychart.h"

QT_BEGIN_NAMESPACE
namespace Ui { class ChartTest; }
QT_END_NAMESPACE

class ChartTest : public QWidget
{
    Q_OBJECT

public:
    ChartTest(QWidget *parent = nullptr);
    ~ChartTest();
    void initState();

private:
    Ui::ChartTest *ui;
    int index = 0;
    QTimer timer;   //定时器
};
#endif // CHARTTEST_H

charttest.cpp 

#include "charttest.h"
#include "ui_charttest.h"

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

ChartTest::~ChartTest()
{
    timer.stop();
    delete ui;
}

void ChartTest::initState()
{
    this->resize(1000, 400);
    connect(&timer, &QTimer::timeout, [=]()
    {
        // 模拟数据
        static int y = 1;
        if (y++ >= 9) {
            y = 1;
        }
        static int value = 0;
        DataNode node = {y, "ABC" + QString::number(value++)};
        // 刷新数据
        ui->widget->updateValue(node);
    });
    timer.start(50);
}

main.cpp 

#include "charttest.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    ChartTest w;
    w.show();
    return a.exec();
}

ChartTest.pro 

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11

# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
    main.cpp \
    charttest.cpp \
    mychart.cpp

HEADERS += \
    charttest.h \
    mychart.h

FORMS += \
    charttest.ui

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

编写晚代码后运行效果

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

OpenCV-41 使用掩膜的直方图

一、掩膜 掩膜即为与原图大小一致的黑底白框图。 如何生成掩膜&#xff1f; 先生成一个全黑的和原始图片大小一样大的图片。mask np.zeros(img.shape, np.uint8)将想要的区域通过索引方式设置为255.mask[100:200, 200:300] 示例代码如下&#xff1a; import cv2 import ma…

【电路笔记】-LR串联电路

LR串联电路 文章目录 LR串联电路1、概述2、示例1所有线圈、电感器、扼流圈和变压器都会在其周围产生磁场,由电感与电阻串联组成,形成 LR 串联电路。 1、概述 在本节有关电感器的第一个文章中,我们简要介绍了电感器的时间常数,指出流过电感器的电流不会瞬时变化,而是会以恒…

【LeetCode: 107. 二叉树的层序遍历 II + BFS】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

不花一分钱,在 Mac 上跑 Windows(M1/M2 版)

这是在 MacOS M1 上体验最新 Windows11 的效果&#xff1a; VMware Fusion&#xff0c;可以运行 Windows、Linux 系统&#xff0c;个人使用 licence 免费 安装流程见 &#x1f449; https://zhuanlan.zhihu.com/p/452412091 从申请 Fusion licence 到下载镜像&#xff0c;再到…

PPT导出PDF时保持图像高清的方法

问题: 我们经常会发现&#xff0c;在PPT中插入的图片非常高清&#xff0c;但是通过PPT转换为PDF之后&#xff0c;图片就会出现不同程度的失真。 问题产生的原因: 这是因为Acrobat的PDF Maker在将PPT转换为PDF的时候&#xff0c;对PPT中的图片进行了压缩 Solution: 在PPT的…

信息安全技术基础知识

一、考点分布 信息安全基础&#xff08;※※&#xff09;信息加密解密技术&#xff08;※※※&#xff09;密钥管理技术&#xff08;※※&#xff09;访问控制及数字签名技术&#xff08;※※※&#xff09;信息安全的保障体系 二、信息安全基础 信息安全包括5个基本要素&#…

ChatGPT绘图指南:DALL.E3玩法大全(一)

一、 DALLE.3 模型介绍 1、什么是 DALLE.3 模型&#xff1f; DALLE-3模型&#xff0c;是一种由OpenAI研发的技术&#xff0c;它是一种先进的生成模型&#xff0c;可以将文字描述转化为清晰的图片。这种模型的名称"DALLE"实际上是"Deep Auto-regressive Latent …

Qt:Qt3个窗口类的区别、VS与QT项目转换

一、Qt3个窗口类的区别 QMainWindow&#xff1a;包含菜单栏、工具栏、状态栏 QWidget&#xff1a;普通的一个窗口&#xff0c;什么也不包括 QDialog&#xff1a;对话框&#xff0c;常用来做登录窗口、弹出窗口&#xff08;例如设置页面&#xff09; QDialog实现简易登录界面…

致敬新春“不回家”的厨师,李锦记让厨师的年味更有滋味

“新春饭市万家团圆&#xff0c;致敬千万坚守岗位的厨师” 新春团圆饭向来是餐饮行业最为关注的节点&#xff0c;但过去几年&#xff0c;在疫情与后疫情时期&#xff0c;新年团圆饭市不免冷清。而今年餐饮行业真正迎来“龙抬头”&#xff0c;龙年除夕夜的团圆饭市终于重迎来了…

第三十三回 镇三山大闹青州道 霹雳火夜走瓦砾场-python分割字符串

黄信和刘知寨押解宋江和花荣向青州走&#xff0c;碰到了燕顺等三人来劫囚车&#xff0c;黄信逃走了&#xff0c;刘知寨被抓住&#xff0c;被花荣一刀杀了。 黄信把情况报给青州知府&#xff0c;派来了青州兵马秦统制&#xff0c;人称霹雳火的秦明。秦明与花荣打&#xff0c;花…

Go语言每日一练——链表篇(九)

传送门 牛客面试笔试必刷101题 ----------------链表相加(二) 题目以及解析 题目 解题代码及解析 解析 这一道题主要是要对链表相加的过程进行模拟&#xff0c;虽然思路不难但是细节出比较多&#xff0c;这里博主的思路主要是先将两个链表反转过来然后以Head1为基础来模拟…

JAVA并发编程(八)-无锁-乐观锁(非阻塞)

文章目录 &#x1f436;一、无锁实现&#xff08;CAS&#xff09;1、代码演示2、CAS效率分析3、CAS的特点 &#x1f431;二、原子整数&#x1f42d;三、原子引用1、代码演示2、ABA问题3、AtomicMarkableReference &#x1f439;四、原子数组&#x1f430;五、字段更新器&#x…

多线程的基本原理学习

由一个问题引发的思考 线程的合理使用能够提升程序的处理性能&#xff0c;主要有两个方面&#xff0c;第一个是能够利用多核cpu以及超线程技术来实现线程的并行执行&#xff1b;第二个是线程的异步化执行相比于同步执行来说&#xff0c;异步执行能够很好的优化程序的处理性能提…

EasyRecovery软件有哪些版本?如何下载2024最新版本

EasyRecovery 是一款功能强大的数据恢复软件&#xff0c;适用于电脑上的数据恢复需求。以下是该软件的不同电脑版本及其主要特点&#xff1a; EasyRecovery Home&#xff08;家用版&#xff09;&#xff1a; 主要针对家庭用户&#xff0c;提供常规的数据恢复功能。可以恢复各种…

计算机毕业设计SSM基于的高校学习资源共享系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; vue mybatis Maven mysql5.7或8.0等等组成&#xff0c;B…

项目管理工具软件Maven趣闻

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl Maven这个单词来自于意第绪语&#xff08;Yiddish&#xff09;&#xff0c;这是一种与德语和希伯来语有密切关系的犹太民族语言。在这个语境中&#xff0c;Maven意为“知识的…

typescript环境搭建,及tsc命令优化

typescript typescript. 是一种由微软开发的 开源 、跨平台的编程语言。. 它是 JavaScript 的超集&#xff0c;最终会被编译为JavaScript代码。. TypeScript添加了可选的静态类型系统、很多尚未正式发布的ECMAScript新特性&#xff08;如装饰器 [1] &#xff09;。. 2012年10月…

面试:正确率能很好的评估分类算法吗

正确率&#xff08;accuracy&#xff09; 正确率是我们最常见的评价指标&#xff0c;accuracy (TPTN)/(PN)&#xff0c;正确率是被分对的样本数在所有样本数中的占比&#xff0c;通常来说&#xff0c;正确率越高&#xff0c;分类器越好。 不同算法有不同特点&#xff0c;在不同…

c++类和对象新手保姆级上手教学(上)

前言&#xff1a; c其实顾名思义就是c语言的升级版&#xff0c;很多刚学c的同学第一感觉就是比c语言难学很多&#xff0c;其实没错&#xff0c;c里的知识更加难以理解可以说杂且抽象&#xff0c;光是类和对象&#xff0c;看起来容易&#xff0c;但想完全吃透&#xff0c;真的挺…

开关电源电路主要元器件基础知识详解

在学习电子电路过程中&#xff0c;电源我们无法绕开的一个重要部分&#xff0c;很多时候&#xff0c;故障就出现在电源部分&#xff0c;特别是开关电源。开关电源电路主要是由熔断器、热敏电阻器、互感滤波器、桥式整流电路、滤波电容器、开关振荡集成电路、开关变压器、光耦合…