• Qt利用QPainter自绘实现热感应图效果


    相关资料:

    https://blog.csdn.net/gongjianbo1992/article/details/104566768?utm_medium=distribute.pc_relevant_bbs_down.none-task--2~all~first_rank_v2~rank_v29-13.nonecase&depth_1-utm_source=distribute.pc_relevant_bbs_down.none-task--2~all~first_rank_v2~rank_v29-13.nonecase

    https://github.com/gongjianbo/MyTestCode/tree/master/Qt/MyHeatMap  代码下载

    .pro

     1 QT       += core gui
     2 
     3 greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
     4 
     5 CONFIG += c++11
     6 
     7 # The following define makes your compiler emit warnings if you use
     8 # any Qt feature that has been marked deprecated (the exact warnings
     9 # depend on your compiler). Please consult the documentation of the
    10 # deprecated API in order to know how to port your code away from it.
    11 DEFINES += QT_DEPRECATED_WARNINGS
    12 
    13 # You can also make your code fail to compile if it uses deprecated APIs.
    14 # In order to do so, uncomment the following line.
    15 # You can also select to disable deprecated APIs only up to a certain version of Qt.
    16 #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
    17 
    18 SOURCES += 
    19     main.cpp 
    20     mainwindow.cpp
    21 
    22 HEADERS += 
    23     mainwindow.h
    24 
    25 FORMS += 
    26     mainwindow.ui
    27 
    28 # Default rules for deployment.
    29 qnx: target.path = /tmp/$${TARGET}/bin
    30 else: unix:!android: target.path = /opt/$${TARGET}/bin
    31 !isEmpty(target.path): INSTALLS += target
    View Code

    main.cpp

     1 #include "mainwindow.h"
     2 
     3 #include <QApplication>
     4 
     5 int main(int argc, char *argv[])
     6 {
     7     QApplication a(argc, argv);
     8     MainWindow w;
     9     w.show();
    10     return a.exec();
    11 }
    View Code

    mainwindow.h

     1 #ifndef MAINWINDOW_H
     2 #define MAINWINDOW_H
     3 
     4 #include <QMainWindow>
     5 
     6 #include <QPainter>
     7 #include <QPaintEvent>
     8 #include <QMouseEvent>
     9 
    10 #include <QLinearGradient>
    11 #include <QRadialGradient>
    12 
    13 QT_BEGIN_NAMESPACE
    14 namespace Ui { class MainWindow; }
    15 QT_END_NAMESPACE
    16 
    17 //参照JS版:https://blog.csdn.net/HiddlestonCloud/article/details/83743449
    18 //参照Qt版:https://blog.csdn.net/pbe_sedm/article/details/8982357
    19 class MainWindow : public QMainWindow
    20 {
    21     Q_OBJECT
    22     struct Point
    23     {
    24         int posX;
    25         int posY;
    26         int radius;
    27         int count;
    28     };
    29 public:
    30     MainWindow(QWidget *parent = nullptr);
    31     ~MainWindow();
    32 
    33 protected:
    34     void paintEvent(QPaintEvent *event) override;
    35     void mousePressEvent(QMouseEvent *event) override;
    36 
    37     //添加点,返回该点权重,如果是一次性生成的点可以单独优化
    38     int appendPoint(const Point &pt);
    39     //最大权重改变之后重新绘制dataimg
    40     void drawDataImg();
    41     //叠加绘制dataimg
    42     void drawDataPoint(const Point &pt);
    43     //根据dataimg绘制热力图
    44     void drawHeatImg();
    45 
    46     //清空
    47     void clear();
    48 
    49 private:
    50     Ui::MainWindow *ui;
    51 
    52     //整体的透明度[0-255]
    53     static constexpr int HeatAlpha=200;
    54     //固定宽高的演示
    55     static constexpr int ImgWidth=1000;
    56     static constexpr int ImgHeight=700;
    57     //最大权重
    58     int _maxCount=1;
    59     //权重统计表(把权重单独拿出来,就可以不用遍历数据点来计算了)
    60     //加()初始化为0
    61     int *_countTable=new int[ImgWidth*ImgHeight]();
    62 
    63     //数据点
    64     QList<Point> _posList;
    65     //绘制透明度
    66     QImage _dataImg;
    67     //最终热力图
    68     QImage _heatImg;
    69     //颜色表,透明度为0
    70     QRgb _colorList[256];
    71 };
    72 #endif // MAINWINDOW_H
    View Code

    mainwindow.cpp

      1 #include "mainwindow.h"
      2 #include "ui_mainwindow.h"
      3 
      4 #include <QDebug>
      5 
      6 MainWindow::MainWindow(QWidget *parent)
      7     : QMainWindow(parent)
      8     , ui(new Ui::MainWindow)
      9 {
     10     ui->setupUi(this);
     11 
     12     //这是固定宽高的演示
     13     setFixedSize(ImgWidth,ImgHeight);
     14 
     15     //data用alpha叠加
     16     _dataImg=QImage(ImgWidth,ImgHeight,QImage::Format_Alpha8);
     17     _dataImg.fill(Qt::transparent);
     18     //热力图通过alpha值查表
     19     _heatImg=QImage(ImgWidth,ImgHeight,QImage::Format_ARGB32);
     20     _heatImg.fill(Qt::transparent);
     21 
     22     //根据线性渐变色条得到颜色表
     23     QLinearGradient linear=QLinearGradient(QPoint(0,0),QPoint(255,0));
     24     linear.setColorAt(0, Qt::blue);
     25     linear.setColorAt(0.4, Qt::blue);
     26     linear.setColorAt(0.5, Qt::cyan);
     27     linear.setColorAt(0.6, Qt::green);
     28     linear.setColorAt(0.8, Qt::yellow);
     29     linear.setColorAt(0.95, Qt::red);
     30 
     31     //把渐变色绘制到Img方便取颜色
     32     QImage img(256,1,QImage::Format_ARGB32);
     33     QPainter painter(&img);
     34     painter.fillRect(img.rect(),linear);
     35 
     36     //HeatAlpha为热力图整体透明度
     37     quint32 alpha=0;
     38     for(quint32 i=0;i<256;i++){
     39         //根据热力图透明度来计算颜色表的透明度
     40         alpha=HeatAlpha/255.0*i;
     41         //typedef unsigned int QRgb: format #AARRGGBB
     42         //颜色+透明度
     43         _colorList[i]=img.pixel(i,0)&0x00FFFFFF|(alpha<<24);
     44     }
     45 
     46     //清空图像
     47     connect(ui->btnClear,&QPushButton::clicked,this,[=](){
     48         this->clear();
     49     });
     50     //保存图像
     51     connect(ui->btnSave,&QPushButton::clicked,this,[=](){
     52         _dataImg.save("save.png","PNG");
     53     });
     54 }
     55 
     56 MainWindow::~MainWindow()
     57 {
     58     delete []_countTable;
     59     delete ui;
     60 }
     61 
     62 void MainWindow::paintEvent(QPaintEvent *event)
     63 {
     64     QPainter p(this);
     65 
     66     //绘制一个黑色网格,便于查看渐变色
     67     p.setPen(QPen(Qt::black,2));
     68     for(int i=0;i<width();i+=50){
     69         p.drawLine(i,0,i,height());
     70     }
     71     for(int i=0;i<height();i+=50){
     72         p.drawLine(0,i,width(),i);
     73     }
     74     //绘制热力图
     75     p.drawImage(0,0,_heatImg);
     76     QMainWindow::paintEvent(event);
     77 }
     78 
     79 void MainWindow::mousePressEvent(QMouseEvent *event)
     80 {
     81     event->accept();
     82     const QPoint pos=event->pos();
     83     const int radius=ui->spinBox->value();
     84 
     85     //次数默认为1
     86     const Point pt{pos.x(),pos.y(),radius,1};
     87     const int pos_count=appendPoint(pt);
     88     //目前是根据最大次数来计算的,或许也可以根据次数来分段
     89     //为什么不直接if(pos_count>_maxCount)才重新绘制?
     90     //而是有两个点叠加if(pos_count>1)就重新绘制?
     91     //因为单纯的叠加目前还没有带入权重来计算,如果最大权重更大,那么这个叠加的点颜色就走样了
     92     if(pos_count>1){
     93         if(pos_count>_maxCount)
     94             _maxCount=pos_count;
     95         drawDataImg();
     96     }else{
     97         drawDataPoint(pt);
     98     }
     99     drawHeatImg();
    100     update();
    101 }
    102 
    103 int MainWindow::appendPoint(const Point &pt)
    104 {
    105     //无效的数据
    106     if(pt.posX<0||pt.posY<0||pt.radius<0||
    107             pt.posX>=ImgWidth||pt.posY>=ImgHeight)
    108         return 0;
    109     //根据权重表获知是否已有该点
    110     if(_countTable[pt.posX+pt.posY*ImgWidth]>0){
    111         for(Point &the_pos:_posList)
    112         {
    113             if(the_pos.posX==pt.posX&&the_pos.posY==pt.posY){
    114                 //对已有点叠加权重值
    115                 the_pos.count+=pt.count;
    116                 break;
    117             }
    118         }
    119     }else{
    120         _posList.push_back(pt);
    121     }
    122     _countTable[pt.posX+pt.posY*ImgWidth]+=pt.count;
    123     return _countTable[pt.posX+pt.posY*ImgWidth];
    124 }
    125 
    126 void MainWindow::drawDataImg()
    127 {
    128     //重新绘制先清空
    129     _dataImg.fill(Qt::transparent);
    130     QPainter painter(&_dataImg);
    131     painter.setPen(Qt::transparent);
    132     //绘制点的部分可以调用drawpoint
    133     const double max_count=_maxCount;
    134     for(int i=0;i<_posList.count();i++)
    135     {
    136         const Point &pt=_posList.at(i);
    137         //以最大次数来计算该点的权重
    138         const uchar alpha=uchar(_countTable[pt.posX+pt.posY*ImgWidth]/max_count*255);
    139         QRadialGradient gradient(pt.posX,pt.posY,pt.radius);
    140         gradient.setColorAt(0,QColor(0,0,0,alpha));
    141         gradient.setColorAt(1,QColor(0,0,0,0));
    142         painter.setBrush(gradient);
    143         painter.drawEllipse(QPointF(pt.posX,pt.posY),pt.radius,pt.radius);
    144     }
    145 }
    146 
    147 void MainWindow::drawDataPoint(const Point &pt)
    148 {
    149     QPainter painter(&_dataImg);
    150     painter.setPen(Qt::transparent);
    151 
    152     //以最大次数来计算该点的权重
    153     const uchar alpha=uchar(_countTable[pt.posX+pt.posY*ImgWidth]/(double)_maxCount*255);
    154 
    155     QRadialGradient gradient(pt.posX,pt.posY,pt.radius);
    156     gradient.setColorAt(0,QColor(0,0,0,alpha));
    157     gradient.setColorAt(1,QColor(0,0,0,0));
    158     painter.setBrush(gradient);
    159     painter.drawEllipse(QPointF(pt.posX,pt.posY),pt.radius,pt.radius);
    160 }
    161 
    162 void MainWindow::drawHeatImg()
    163 {
    164     //把alpha值转为颜色值
    165     for(int row=0;row<_dataImg.height();row++)
    166     {
    167         //dataimg QImage::Format_Alpha8,一个点1个字节
    168         const uchar *line_data=_dataImg.scanLine(row);
    169         //heatimg QImage::Format_ARGB32,一个点4个字节
    170         QRgb *line_heat=reinterpret_cast<QRgb*>(_heatImg.scanLine(row));
    171         for(int col=0;col<_dataImg.width();col++)
    172         {
    173             //根据alpha透明度从颜色表取颜色
    174             line_heat[col]=_colorList[line_data[col]];
    175         }
    176     }
    177 }
    178 
    179 void MainWindow::clear()
    180 {
    181     _dataImg.fill(Qt::transparent);
    182     _heatImg.fill(Qt::transparent);
    183     _posList.clear();
    184     _maxCount=1;
    185     memset(_countTable,0,ImgWidth*ImgHeight*sizeof(int));
    186     update();
    187 }
    View Code

    mainwindow.ui

      1 <?xml version="1.0" encoding="UTF-8"?>
      2 <ui version="4.0">
      3  <class>MainWindow</class>
      4  <widget class="QMainWindow" name="MainWindow">
      5   <property name="geometry">
      6    <rect>
      7     <x>0</x>
      8     <y>0</y>
      9     <width>667</width>
     10     <height>452</height>
     11    </rect>
     12   </property>
     13   <property name="windowTitle">
     14    <string>MainWindow</string>
     15   </property>
     16   <widget class="QWidget" name="centralwidget">
     17    <layout class="QVBoxLayout" name="verticalLayout">
     18     <property name="leftMargin">
     19      <number>0</number>
     20     </property>
     21     <property name="topMargin">
     22      <number>0</number>
     23     </property>
     24     <property name="rightMargin">
     25      <number>0</number>
     26     </property>
     27     <property name="bottomMargin">
     28      <number>0</number>
     29     </property>
     30     <item>
     31      <layout class="QHBoxLayout" name="horizontalLayout">
     32       <item>
     33        <widget class="QLabel" name="label">
     34         <property name="text">
     35          <string>Radius</string>
     36         </property>
     37        </widget>
     38       </item>
     39       <item>
     40        <widget class="QSpinBox" name="spinBox">
     41         <property name="minimum">
     42          <number>10</number>
     43         </property>
     44         <property name="maximum">
     45          <number>200</number>
     46         </property>
     47         <property name="value">
     48          <number>50</number>
     49         </property>
     50        </widget>
     51       </item>
     52       <item>
     53        <spacer name="horizontalSpacer">
     54         <property name="orientation">
     55          <enum>Qt::Horizontal</enum>
     56         </property>
     57         <property name="sizeHint" stdset="0">
     58          <size>
     59           <width>40</width>
     60           <height>20</height>
     61          </size>
     62         </property>
     63        </spacer>
     64       </item>
     65       <item>
     66        <widget class="QPushButton" name="btnClear">
     67         <property name="text">
     68          <string>clear</string>
     69         </property>
     70        </widget>
     71       </item>
     72       <item>
     73        <widget class="QPushButton" name="btnSave">
     74         <property name="text">
     75          <string>save</string>
     76         </property>
     77        </widget>
     78       </item>
     79      </layout>
     80     </item>
     81     <item>
     82      <spacer name="verticalSpacer">
     83       <property name="orientation">
     84        <enum>Qt::Vertical</enum>
     85       </property>
     86       <property name="sizeHint" stdset="0">
     87        <size>
     88         <width>20</width>
     89         <height>373</height>
     90        </size>
     91       </property>
     92      </spacer>
     93     </item>
     94    </layout>
     95   </widget>
     96   <widget class="QMenuBar" name="menubar">
     97    <property name="geometry">
     98     <rect>
     99      <x>0</x>
    100      <y>0</y>
    101      <width>667</width>
    102      <height>23</height>
    103     </rect>
    104    </property>
    105   </widget>
    106   <widget class="QStatusBar" name="statusbar"/>
    107  </widget>
    108  <resources/>
    109  <connections/>
    110 </ui>
    View Code
    作者:疯狂Delphi
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.

    欢迎关注我,一起进步!扫描下方二维码即可加我

  • 相关阅读:
    C# MVC 自定义ActionResult实现EXCEL下载
    如何让WEBAPI 能够进行跨越访问
    C#进阶系列——WebApi 接口返回值不困惑:返回值类型详解
    sC#进阶系列——WebApi 接口参数不再困惑:传参详解
    mybatis.net insert 返回主键
    [转]MySQL中timestamp数据类型的特点
    [转]java List和数组相互转换方法
    [转]Mybatis foreach 批量操作
    [转]让iframe自适应高度-真正解决
    [转]decorators.xml的用法
  • 原文地址:https://www.cnblogs.com/FKdelphi/p/14659082.html
Copyright © 2020-2023  润新知