利用QPainter绘制散点图

【1】实例代码

(1)代码目录结构(备注:QtCreator默认步骤新建工程)

(2)工程pro文件

 1 QT       += core gui
 2 
 3 greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
 4 
 5 TARGET = painter
 6 TEMPLATE = app
 7 
 8 
 9 SOURCES += main.cpp\
10         mainwindow.cpp
11 
12 HEADERS  += mainwindow.h
13 
14 FORMS    += mainwindow.ui

(3)头文件

 1 #ifndef MAINWINDOW_H
 2 #define MAINWINDOW_H
 3 
 4 #include <QtGui>
 5 #include <QPaintEvent>
 6 #include <QMainWindow>
 7 
 8 
 9 #include <ctime>
10 #include <cstdlib>
11 
12 namespace Ui
13 {
14     class MainWindow;
15 }
16 
17 class MainWindow : public QMainWindow
18 {
19     Q_OBJECT
20 
21 public:
22     explicit MainWindow(QWidget *parent = 0);
23     ~MainWindow();
24 
25     void Paint();
26 
27 public slots:
28     void onRefresh();
29 protected:
30     void paintEvent(QPaintEvent *);
31 
32 private:
33     Ui::MainWindow *ui;
34     QImage m_image;
35     QPainter *m_painter;
36 };
37 
38 #endif // MAINWINDOW_H

(4)实现文件

  1 #include "mainwindow.h"
  2 #include "ui_mainwindow.h"
  3 
  4 #define POINTSNUM 10
  5 #define LowerFactor 0.8
  6 #define UpperFactor 1.2
  7 
  8 typedef struct Data
  9 {
 10     double scaleRange;
 11     double maxScale;
 12     double minScale;
 13     QVector<double> vecData;
 14 
 15     Data() : scaleRange(0.0), maxScale(0.0), minScale(0.0)
 16     {}
 17     void init()
 18     {
 19         for (int i = 0; i < POINTSNUM; ++i)
 20         {
 21             if (i % 2)
 22                 vecData.append(rand() % 50 - 10);
 23             else
 24                 vecData.append(rand() % 50 + 10);
 25         }
 26         double maxElement = *(std::max_element(vecData.begin(), vecData.end()));
 27         double minElement = *(std::min_element(vecData.begin(), vecData.end()));
 28         if (maxElement < 0)
 29         {
 30             maxScale = maxElement * LowerFactor;
 31             minScale = minElement * UpperFactor;
 32         }
 33         else if (0 == maxElement)
 34         {
 35             maxScale = 2;
 36             minScale = minElement * UpperFactor;
 37         }
 38         else if (maxElement > 0 && minElement < 0)
 39         {
 40             maxScale = maxElement * UpperFactor;
 41             minScale = minElement * UpperFactor;
 42         }
 43         else if (minElement > 0)
 44         {
 45             maxScale = maxElement * UpperFactor;
 46             minScale = minElement * LowerFactor;
 47         }
 48 
 49         scaleRange = maxScale - minScale;
 50     }
 51     void print()
 52     {
 53         for (int i = 0; i < POINTSNUM; ++i)
 54         {
 55             qDebug () << i << "::" << vecData[i] << " ";
 56         }
 57         qDebug() << endl;
 58     }
 59 }DataInfo;
 60 
 61 MainWindow::MainWindow(QWidget *parent)
 62     : QMainWindow(parent),
 63       ui(new Ui::MainWindow)
 64 {
 65     ui->setupUi(this);
 66     ui->mainToolBar->setVisible(true);
 67 
 68     QAction *pAction = new QAction("refresh", this);
 69     ui->mainToolBar->addAction(pAction);
 70     connect(pAction, &QAction::triggered, this, &MainWindow::onRefresh);
 71 
 72     resize(1000, 730); // 窗体大小 宽度1000 高度730
 73 
 74     m_image = QImage(990, 710, QImage::Format_RGB32);  // 画布的初始化大小设为,使用32位颜色
 75     QColor backColor = qRgb(255, 255, 255);    // 画布初始化背景色使用白色
 76     m_image.fill(backColor); // 对画布进行填充
 77 
 78     m_painter = new QPainter(&m_image);
 79     m_painter->setRenderHint(QPainter::Antialiasing, true); // 设置反锯齿模式
 80 
 81     Paint();
 82 }
 83 
 84 void MainWindow::onRefresh()
 85 {
 86     m_painter->fillRect(10, 10, 980 - 10, 700 - 10, Qt::white);
 87     Paint();
 88     update();
 89 }
 90 
 91 void MainWindow::Paint()
 92 {
 93     // 确定坐标轴起点坐标
 94     int pointx = 80, pointy = 650;
 95 
 96     // 确定坐标轴宽度和高度,上文已定义画布大小,宽高依此而定。
 97     int width = 980 - pointx - 70;  // 宽度 = 画布宽度 - 坐标起点x - 右端间隙
 98     int height = 700 - 2 * 50;      // 高度 = 画布高度 - 上下端的间隙高度
 99 
100     // 绘制视图区域
101     // 即外围的矩形(由左上角与右下角的两个点确定一个矩形)
102     m_painter->drawRect(20, 30, 980 - 10, 700 - 20);
103 
104     // 绘制X、Y1、Y2轴
105     QPointF xStartPoint(pointx, pointy);
106     QPointF xEndPoint(width + pointx, pointy);
107     m_painter->drawLine(xStartPoint, xEndPoint); // 坐标轴x宽度为width
108 
109     QPointF y1StartPoint(pointx, pointy - height);
110     QPointF y1EndPoint(pointx, pointy);
111     m_painter->drawLine(y1StartPoint, y1EndPoint); // 坐标轴y1高度为height
112 
113     QPointF y2StartPoint(pointx + width, pointy - height);
114     QPointF y2EndPoint(pointx + width, pointy);
115     m_painter->drawLine(y2StartPoint, y2EndPoint); // 坐标轴y2高度为height
116 
117     // (2)获得数据并分析最大值与最小值
118     DataInfo vectorX, vectorY1, vectorY2; // 数据储存在容器中,大小为POINTSNUM]
119     // 模拟随机数据
120     srand((int)time(NULL));
121     vectorX.init();
122     vectorY1.init();
123     vectorY2.init();
124 
125     vectorX.print();
126     vectorY1.print();
127     vectorY2.print();
128 
129     double kx = (double)(width / vectorX.scaleRange); // x轴的系数
130     double ky1 = (double)(height / vectorY1.scaleRange);  // y1方向的比例系数
131     double ky2 = (double)(height / vectorY2.scaleRange);  // y2方向的比例系数
132 
133     // (3)绘制点
134     QPen penPointY1, penPointY2;
135     penPointY1.setColor(Qt::blue);
136     penPointY1.setWidth(5);
137 
138     penPointY2.setColor(Qt::red);
139     penPointY2.setWidth(5);
140 
141     for (int i = 0; i < POINTSNUM; ++i)
142     {
143         double dXStart = pointx + kx * (vectorX.vecData[i] - vectorX.minScale);
144         m_painter->setPen(penPointY1); // 蓝色的笔,用于标记Y1各个点
145         m_painter->drawPoint(dXStart, pointy - (vectorY1.vecData[i] - vectorY1.minScale) * ky1);
146 
147         m_painter->setPen(penPointY2); // 红色的笔,用于标记Y2各个点
148         m_painter->drawPoint(dXStart, pointy - (vectorY2.vecData[i] - vectorY2.minScale) * ky2);
149     }
150 
151     // (4) 绘制刻度线
152     QPen penDegree;
153     penDegree.setColor(Qt::black);
154     penDegree.setWidth(2);
155     m_painter->setPen(penDegree);
156 
157     // x轴刻度线和值
158     // x轴 第一个刻度值
159     m_painter->drawText(pointx + 3, pointy + 12, QString::number(vectorX.minScale, 'f', 3));
160     for (int i = 0; i < POINTSNUM - 1; ++i) // 分成10份
161     {
162 //        // 选取合适的坐标,绘制一段长度为4的直线,用于表示刻度
163 //        m_painter->drawLine(pointx + (i + 1) * width/10, pointy,
164 //                         pointx + (i+1)*width/10, pointy + 4);
165 
166         m_painter->drawText(pointx + (i+0.9) * width / POINTSNUM, pointy + 12, // 值的位置信息
167                          QString::number(vectorX.minScale + (i+1) * (vectorX.scaleRange/POINTSNUM), 'f', 3));
168     }
169     // x轴 最后一个刻度值
170     m_painter->drawText(pointx + (POINTSNUM - 1 + 0.8) * width / POINTSNUM, pointy + 12,
171                      QString::number(vectorX.maxScale, 'f', 3));
172 
173     xStartPoint.setX(pointx);
174     xStartPoint.setY(pointy + 20);
175     xEndPoint.setX(pointx + width);
176     xEndPoint.setY(pointy + 20);
177     m_painter->drawLine(xStartPoint, xEndPoint);
178 
179     m_painter->drawText(pointx + width/2, pointy + 35, QString("X"));
180 
181     // y1轴刻度线和值
182     // y1轴 第一个刻度值
183     m_painter->drawText(pointx - 25, pointy - 3, QString::number(vectorY1.minScale, 'f', 3));
184     for (int i = 0; i < POINTSNUM - 1; ++i)
185     {
186         // 代码较长,但是掌握基本原理即可。
187         // 主要就是确定一个位置,然后画一条短短的直线表示刻度。
188 
189 //        m_painter->drawLine(pointx, pointy-(i+1)*height/10,
190 //                         pointx-4, pointy-(i+1)*height/10);
191 
192         m_painter->drawText(pointx - 25, pointy - (i+0.85) * height/POINTSNUM,
193                          QString::number(vectorY1.minScale + (i+1) * (vectorY1.scaleRange/POINTSNUM), 'f', 3));
194     }
195     // y1轴 最后一个刻度值
196     m_painter->drawText(pointx - 25, pointy - (POINTSNUM - 1 + 0.85) * height/POINTSNUM,
197                      QString::number(vectorY1.maxScale, 'f', 3));
198 
199     y1StartPoint.setX(pointx - 35);
200     y1StartPoint.setY(pointy - height);
201 
202     y1EndPoint.setX(pointx - 35);
203     y1EndPoint.setY(pointy);
204     m_painter->drawLine(y1StartPoint, y1EndPoint);
205 
206     m_painter->drawText(pointx - 55, pointy - height/2, QString("Y1"));
207 
208     // y2轴刻度线和值
209     // y2轴 第一个刻度值
210     m_painter->drawText(pointx + width + 10, pointy - 3, QString::number(vectorY2.minScale, 'f', 3));
211     for (int i = 0; i < POINTSNUM - 1; ++i)
212     {
213 //        m_painter->drawLine(pointx + width, pointy-(i+1)*height/10,
214 //                         pointx + width + 4, pointy-(i+1)*height/10);
215 
216         m_painter->drawText(pointx + width + 10, pointy - (i+0.85)*height/POINTSNUM,
217                          QString::number((vectorY2.minScale + (i+1)*(vectorY2.scaleRange/POINTSNUM)), 'f' , 3));
218     }
219     // Y2轴 最后一个刻度值
220     m_painter->drawText(pointx + width + 10, pointy - (POINTSNUM - 1 + 0.85)*height/POINTSNUM,
221                      QString::number(vectorY2.maxScale, 'f', 3));
222 
223     y2StartPoint.setX(pointx + width + 40);
224     y2StartPoint.setY(pointy - height);
225 
226     y2EndPoint.setX(pointx + width + 40);
227     y2EndPoint.setY(pointy);
228     m_painter->drawLine(y2StartPoint, y2EndPoint);
229 
230     m_painter->drawText(pointx + width + 50, pointy - height/2, QString("Y2"));
231 
232     // (5)绘制网格
233     QPen penDotLine;
234     penDotLine.setStyle(Qt::DotLine);
235     m_painter->setPen(penDotLine);
236     for (int i = 0; i < POINTSNUM; ++i)
237     {
238         // 垂直线
239         m_painter->drawLine(pointx + (i+1)* width/POINTSNUM, pointy,
240                          pointx + (i+1)* width/POINTSNUM, pointy - height);
241         // 水平线
242         m_painter->drawLine(pointx, pointy-(i+1)*height/POINTSNUM,
243                          pointx + width, pointy-(i+1)*height/POINTSNUM);
244     }
245 }
246 
247 MainWindow::~MainWindow()
248 {
249     delete ui;
250     delete m_painter;
251 }
252 
253 void MainWindow::paintEvent(QPaintEvent *)
254 {
255     QPainter painter(this);
256     painter.drawImage(0, 0, m_image);
257 }

(5)main文件

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

【2】效果图

运行结果图如下:

Good Good Study, Day Day Up.

顺序 选择 循环 总结

posted @ 2017-05-19 20:08  kaizenly  阅读(5392)  评论(0编辑  收藏  举报
打赏