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

本节对应的视频讲解: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);
}

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

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大轮明王讲QT

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值