绘图事件

通过下面一个例子来讲解绘图事件:

窗口刚打开时上面和下面两个标签会显示高温和低温曲线

然后每次双击时,会随机生成几个高温值和低温值,并且把新生成的温度重新绘制上去,从而产生双击曲线变化的效果

绘图事件

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

对应的事件类型是QEvent::Paint

需要重写的函数是

void paintEvent(QPaintEvent* ev) override

什么时候需要重绘呢,常见的比如:

1.窗口初始化和显示:比如窗口首次创建,或者窗口被覆盖,最小化后再恢复时,会重绘

2.部件大小或位置变化:比如窗口/控件的大小或位置发生变化时,需要重绘来适应新的大小或位置

3.强制重绘:调用窗口/控件的update()或repaint()函数时,对应调用者会被重绘

等等

举例

通过自定义1个控件

该控件有上下两个标签

上面的标签显示高温曲线(只显示7个温度)

下面的标签显示低温曲线(只显示7个温度)

通过事件过滤器的方式来捕获绘图事件,从而将曲线绘制到上下两个标签中

双击标签时,会随机产生7个高温值和低温值,然后调用2个标签的update()方法强制重绘,触发绘图事件,在绘图事件中将新的温度曲线绘制进去

#ifndef PAINTEVENT_H
#define PAINTEVENT_H

#include <QWidget>
#include<QLabel>
#include<QVBoxLayout>
#include<QEvent>
#include<QPainter>
#include<QRandomGenerator64>


class PaintEvent : public QWidget
{
    Q_OBJECT
private:
    QLabel* top_label;//在这个标签中绘制高温曲线
    QLabel* btm_label;//在这个标签中绘制低温曲线

    int high_tempera[7]{0};//存储7个高温的温度值
    int low_tempera[7]{0};//存储7个低温的温度值
public:
    explicit PaintEvent(QWidget *parent = nullptr):QWidget{parent}
    {
        //构造函数
        QVBoxLayout* layout=new QVBoxLayout(this);
        layout->setSpacing(0);
        layout->setContentsMargins(0,0,0,0);

        top_label=new QLabel(this);
        top_label->setFrameShape(QFrame::Box);
        layout->addWidget(top_label);

        btm_label=new QLabel(this);
        btm_label->setFrameShape(QFrame::Box);
        layout->addWidget(btm_label);


        //使用事件过滤器,来自定义这两个标签的绘图事件
        //在重写的eventFilter函数中,对这两个标签的绘图事件进行处理
        top_label->installEventFilter(this);
        btm_label->installEventFilter(this);

        //在这个函数中随机生成7个高温和低温值
        //并调用2个标签的update方法,触发绘图事件,将随机生成的温度值绘制上去
        updateTempurature();
    }

protected:
    bool eventFilter(QObject* watched,QEvent* ev) override
    {
        //捕捉绘图事件
        if(ev->type()==QEvent::Paint)
        {
            if(watched==top_label)
            {
                //在这个函数中将高温曲线绘制到上面的标签中
                paintHigh();
            }
            if(watched==btm_label)
            {
                //在这个函数中将低温曲线绘制到下面的标签中
                paintLow();
            }

        }
        //捕捉鼠标双击事件
        else if(ev->type()==QEvent::MouseButtonDblClick)
        {
            //鼠标双击时在这个函数中随机生成7个高温和低温值
            //并调用2个标签的update方法,触发绘图事件,将随机生成的温度值绘制上去
            //实现双击曲线值更新的效果
            updateTempurature();
        }

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

#define PADDING 50
#define POINT_RADIUS 4//温度圆点的半径
#define TEXT_OFFSET_X 12//温度文本相对于点在X轴的偏移
#define TEXT_OFFSET_Y 10//温度文本相对于点在Y轴的偏移

    void paintHigh()//绘制高温曲线
    {
        //painter将曲线绘制到top_label标签上面
        QPainter painter(top_label);

        //1.计算X轴坐标
        int pointX[7]{0};//绘制7个点,整个曲线左右两边离标签两边留一定的距离PADDING,比如50px
        for(int i=0;i<7;++i)
        {
            //把整个标签的宽度减去两边的PADDING后平均分成6份,表示x坐标增量
            //每一个点的x坐标递增这个增量
            pointX[i]=0+PADDING+(top_label->width()-PADDING*2)/6*i;
        }


        //2.计算y轴坐标
        int sum=0;//计算7个温度的温度总和
        int average=0;//计算7个温度的平均值
        for(int i=0;i<7;++i)
        {
            sum+=high_tempera[i];
        }
        average=sum/7;

        int pointY[7]{0};

        //整个标签的高度/2来表示平均温度所在的y坐标
        //然后这些温度的y坐标就在平均温度上下波动
        int y_center=top_label->height()/2;

        //把整个标签的高度平均分成20份,来表示一个y坐标的变化系数,你也可以不是20份,也可以是30,也可以10
        //份数越小,那么这些温度的y坐标波动的幅度就越小,份数越大,那么幅度就越大,最好配合前面的图片理解
        int increment=top_label->height()/20;

        for(int i=0;i<7;++i)
        {
            //那么每一个温度的y坐标就是平均温度所在的坐标-(温度和平均温度的差值)*变化系数
            //qt坐标系是向右x正,向下y正,因此温度越大,那么y应该往上,y就越小,因此要减去(温度和平均温度的差值)*变化系数
            pointY[i]=y_center-(high_tempera[i]-average)*increment;
        }

        //3.开始绘制
        QPen pen=painter.pen();
        pen.setWidth(2);
        pen.setColor(QColor(255,0,0));
        painter.setPen(pen);
        painter.setBrush(QColor(255,0,0));
        painter.setFont(QFont("Microsoft YaHei",14));
        //4.绘制点、文本
        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(high_tempera[i])+"°");
        }


        //5.绘制曲线,把个点连起来
        for(int i=0;i<7;++i)
        {
            if(i==6)
            {
                continue;
            }
            if(i==0)
            {
                //第一个点连接第二个点时画虚线
                pen.setStyle(Qt::DotLine);//虚线
            }
            else
            {
                pen.setStyle(Qt::SolidLine);
            }
            painter.setPen(pen);
            painter.drawLine(QPoint(pointX[i],pointY[i]),QPoint(pointX[i+1],pointY[i+1]));
        }
    }

    void paintLow()//绘制低温曲线,同理
    {
        QPainter painter(btm_label);//painter将曲线绘制到btm_label标签上面
        //1.计算X轴坐标
        int pointX[7]{0};//绘制7个点,整个曲线左右两边离标签两边留一定的距离,比如50px
        for(int i=0;i<7;++i)
        {
            pointX[i]=btm_label->pos().x()+PADDING+(btm_label->width()-PADDING*2)/6*i;
        }


        //2.计算y轴坐标
        int sum=0;//计算7个温度的温度总和
        int average=0;//计算7个温度的平均值
        for(int i=0;i<7;++i)
        {
            sum+=low_tempera[i];
        }
        average=sum/7;

        int pointY[7]{0};
        int y_center=btm_label->height()/2;
        int increment=btm_label->height()/20;
        for(int i=0;i<7;++i)
        {
            pointY[i]=y_center-(low_tempera[i]-average)*increment;
        }

        //3.开始绘制
        QPen pen=painter.pen();
        pen.setWidth(2);
        pen.setColor(QColor(0,0,255));
        painter.setPen(pen);
        painter.setBrush(QColor(0,0,255));
        painter.setFont(QFont("Microsoft YaHei",14));
        //4.绘制点、文本
        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(low_tempera[i])+"°");
        }


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

    //更新温度
    void updateTempurature()
    {

        for(int i=0;i<7;i++)
        {
            //随机生成7个温度
            high_tempera[i]=20+QRandomGenerator::global()->generate()%10;
            low_tempera[i]=-5+QRandomGenerator::global()->generate()%10;
        }

        //然后强制触发上下两个标签的绘图事件
        top_label->update();
        btm_label->update();
    }

};

#endif // PAINTEVENT_H

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值