利用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.
顺序 选择 循环 总结