You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

446 lines
19 KiB

1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
  1. #include "widgetplot2d.h"
  2. #include <QDebug>
  3. #include "ui_widgetplot2d.h"
  4. WidgetPlot2D::WidgetPlot2D(QWidget* parent) : QWidget(parent), ui(new Ui::WidgetPlot2D), time(QTime::currentTime()) {
  5. ui->setupUi(this);
  6. // QCP绘图初始化
  7. initQCP();
  8. // 初始化控件
  9. initWidget();
  10. // 上下分裂器
  11. ui->splitter->setStretchFactor(0, 20);
  12. ui->splitter->setStretchFactor(1, 1);
  13. // 左右分裂器
  14. ui->splitter1->setStretchFactor(0, 10);
  15. ui->splitter1->setStretchFactor(1, 3);
  16. // 滚动条设置
  17. ui->horizontalScrollBar->setRange(0, 0);
  18. // 拖动水平滚动条,图幅跟随变化
  19. connect(ui->horizontalScrollBar, SIGNAL(valueChanged(int)), this, SLOT(horzScrollBarChanged(int)));
  20. // 默认坐标范围
  21. ui->customPlot->yAxis->setRange(-20, 20);
  22. ui->customPlot->replot();
  23. }
  24. WidgetPlot2D::~WidgetPlot2D() { delete ui; }
  25. /* 初始化曲线名称 */
  26. void WidgetPlot2D::initGraphName(QStringList name) {
  27. //----------------------------------------------------------------------------------------//
  28. // 设置表头
  29. ui->treeWidget->setHeaderLabels(QStringList() << "名称"
  30. << ""
  31. << "颜色");
  32. ui->treeWidget->header()->setVisible(true);
  33. // 表头文字中间对齐
  34. QTreeWidgetItem* header = ui->treeWidget->headerItem();
  35. header->setTextAlignment(0, Qt::AlignLeft);
  36. header->setTextAlignment(1, Qt::AlignLeft);
  37. header->setTextAlignment(2, Qt::AlignLeft);
  38. // 设置列宽
  39. ui->treeWidget->setColumnWidth(0, 100);
  40. ui->treeWidget->setColumnWidth(1, 90);
  41. ui->treeWidget->setColumnWidth(2, 30);
  42. // 表头顺序是否可以拖动改变
  43. ui->treeWidget->header()->setCascadingSectionResizes(true);
  44. // 被选部分是否高亮显示
  45. ui->treeWidget->header()->setHighlightSections(false);
  46. // 最后一个区域是否占满表格余下的所有部分
  47. ui->treeWidget->header()->setStretchLastSection(true);
  48. // 列表的列数
  49. ui->treeWidget->setColumnCount(3);
  50. //----------------------------------------------------------------------------------------//
  51. for (int i = 0; i < name.length(); i++) {
  52. // 添加曲线列表项
  53. QTreeWidgetItem* item = new QTreeWidgetItem;
  54. ui->treeWidget->addTopLevelItem(item);
  55. //----------------------------------------------------------------------------------------//
  56. // 向曲线列表项第一列添加曲线是否可见复选框
  57. QCheckBox* isShowCheckBox = new QCheckBox(name.at(i));
  58. isShowCheckBox->setChecked(true);
  59. connect(isShowCheckBox, SIGNAL(stateChanged(int)), this, SLOT(changeGraphVisible()));
  60. ui->treeWidget->setItemWidget(item, 0, isShowCheckBox);
  61. isShowCheckBoxVector.append(isShowCheckBox);
  62. //----------------------------------------------------------------------------------------//
  63. // 向曲线列表项第二列添加曲线数值标签
  64. QLabel* valueLabel = new QLabel("0");
  65. QFont font("Courier");
  66. valueLabel->setFont(font);
  67. // valueLabel->setAlignment(Qt::AlignHCenter);
  68. ui->treeWidget->setItemWidget(item, 1, valueLabel);
  69. valueLabelVector.append(valueLabel);
  70. //----------------------------------------------------------------------------------------//
  71. // 向曲线列表项第三列添加曲线颜色按钮,点击该按钮弹出颜色选择框,选择曲线颜色
  72. QPushButton* GraphColorPushButton = new QPushButton("");
  73. // 随机分配初始按钮背景色
  74. static int r = 0, g = 0, b = 0;
  75. r += 150;
  76. g += 50;
  77. b += 100;
  78. if (r > 255) r -= 255;
  79. if (g > 255) g -= 255;
  80. if (b > 255) b -= 255;
  81. QPalette pal;
  82. pal.setColor(QPalette::Button, QColor(r, g, b));
  83. GraphColorPushButton->setPalette(pal);
  84. GraphColorPushButton->setAutoFillBackground(true); // 该句不能缺少,否则背景颜色无法改变
  85. GraphColorPushButton->setFlat(true);
  86. // 固定颜色按钮大小
  87. GraphColorPushButton->setMinimumHeight(18); // 固定高度
  88. GraphColorPushButton->setMaximumHeight(18);
  89. GraphColorPushButton->setMinimumWidth(30); // 固定宽度
  90. GraphColorPushButton->setMaximumWidth(30);
  91. connect(GraphColorPushButton, SIGNAL(clicked()), this, SLOT(changeGraphColor()));
  92. ui->treeWidget->setItemWidget(item, 2, GraphColorPushButton);
  93. GraphColorPushButtonVector.append(GraphColorPushButton);
  94. //----------------------------------------------------------------------------------------//
  95. // 添加曲线
  96. ui->customPlot->addGraph();
  97. ui->customPlot->graph(ui->customPlot->graphCount() - 1)->setPen(QPen(QColor(r, g, b)));
  98. ui->customPlot->graph(ui->customPlot->graphCount() - 1)->setVisible(true);
  99. // 将曲线名称与曲线序号一一对应,之后添加数据就可以一一对应
  100. nameToGraphMap[name.at(i)] = i;
  101. // 初始化数据容器的值
  102. for (int t = 0; t < name.length(); t++) {
  103. valueVector.append(0);
  104. }
  105. }
  106. }
  107. /* 添加数据 */
  108. void WidgetPlot2D::addData(QString name, double value, int offms) {
  109. // 如果点击了“暂停”按钮,则不绘制图形
  110. if (ui->pausePBtn->text() == "开始") return;
  111. // 系统当前时间 = 系统运行初始时间 + 系统运行时间
  112. static double start = time.hour() * 60 * 60 + time.minute() * 60 + time.second() + time.msec() / 1000.0;
  113. double key = start + time.elapsed() / 1000.0 + offms / 1000;
  114. // double start = 0;
  115. // static int key = 0;
  116. // key++;
  117. // qDebug() << "start" << start;
  118. // qDebug() << "key" << key;
  119. // 子网格显示
  120. ui->customPlot->xAxis->grid()->setSubGridVisible(ui->subGridCheck->isChecked());
  121. ui->customPlot->yAxis->grid()->setSubGridVisible(ui->subGridCheck->isChecked());
  122. // 自适应量程
  123. if (ui->autoRangeCheck->isChecked()) {
  124. ui->customPlot->rescaleAxes();
  125. }
  126. // 设置时间轴
  127. int timeAxis = ui->timeAxisSpin->value();
  128. ui->customPlot->xAxis->setRange(key, timeAxis, Qt::AlignRight);
  129. // x轴和y轴全程显示
  130. if (ui->fullShowCheck->isChecked()) {
  131. ui->customPlot->rescaleAxes();
  132. }
  133. // 刷新绘图水平滚动条
  134. ui->horizontalScrollBar->setRange(int(start), int(key)); // 刷新滚动条的范围
  135. ui->horizontalScrollBar->setPageStep(1); // 设置翻页步长为 1s 的宽度
  136. ui->horizontalScrollBar->setValue(int(key)); // 调整滑块位置到最右边
  137. // 更新曲线绘图
  138. ui->customPlot->graph(nameToGraphMap[name])->addData(key, value);
  139. // ui->customPlot->replot();
  140. // 使用rpQueuedReplot参数可以加快绘图速度,避免不必要的重复绘制
  141. ui->customPlot->replot(QCustomPlot::rpQueuedReplot);
  142. // 存储曲线的当前值
  143. valueVector[nameToGraphMap[name]] = value;
  144. //----------------------------------------------------------------------------------------//
  145. // 计算每秒的帧率
  146. static double lastFpsKey = key;
  147. static int frameCount = 0;
  148. frameCount++;
  149. // 每2秒显示一次帧率
  150. if (key - lastFpsKey > 0.5) {
  151. uint64_t sum = 0;
  152. for (int i = 0; i < ui->customPlot->plottableCount(); i++) {
  153. sum += uint64_t(ui->customPlot->graph(i)->data()->size());
  154. }
  155. ui->statusLabel->setText(QString("%1 FPS, Total Data points: %2").arg(frameCount / (key - lastFpsKey), 0, 'f', 0).arg(sum));
  156. lastFpsKey = key;
  157. frameCount = 0;
  158. // 更新数据标签
  159. for (int t = 0; t < ui->customPlot->plottableCount(); t++) {
  160. valueLabelVector[t]->setText(QString::number(valueVector[t]));
  161. }
  162. }
  163. //----------------------------------------------------------------------------------------//
  164. }
  165. /* 曲线是否显示 */
  166. void WidgetPlot2D::changeGraphVisible() {
  167. for (int i = 0; i < isShowCheckBoxVector.length(); i++) {
  168. ui->customPlot->graph(i)->setVisible(isShowCheckBoxVector[i]->isChecked());
  169. }
  170. }
  171. /* 改变曲线颜色 */
  172. void WidgetPlot2D::changeGraphColor() {
  173. /* 在槽(SLOT)中sender()函数会返回一个指向 QObject 的指针来指向信号的发送者。
  174. * C++ RTTI(Run-Time Type Identification)dynamic_cast运算符
  175. * sender()QPushButton类sender()QObject指针转换为QPushButton指针
  176. * if中的语句就会执行sender()QPushButton类型的指针dynamic_cast就会返回0if中的语句就不会执行了
  177. * */
  178. if (QPushButton* btn = dynamic_cast<QPushButton*>(sender())) {
  179. QColor color = QColorDialog::getColor(Qt::white, this);
  180. // 用户取消颜色对话框
  181. if (color.isValid() == false) return;
  182. // 设置按钮背景色
  183. QPalette pal; // = btn->palette();
  184. pal.setColor(QPalette::Button, color);
  185. btn->setPalette(pal);
  186. btn->setAutoFillBackground(true); // 该句不能缺少,否则背景颜色无法改变
  187. btn->setFlat(true); // 该句不能缺少,否则背景颜色无法改变
  188. }
  189. // 改变对应的曲线颜色
  190. for (int i = 0; i < GraphColorPushButtonVector.length(); i++) {
  191. // 获取按钮的颜色
  192. QPalette pal = GraphColorPushButtonVector[i]->palette();
  193. QColor color = pal.color(QPalette::Button);
  194. // 曲线颜色对应按钮颜色
  195. ui->customPlot->graph(i)->setPen(QPen(color));
  196. }
  197. ui->customPlot->replot();
  198. }
  199. /* QCP绘图初始化 */
  200. void WidgetPlot2D::initQCP() {
  201. // 刻度显示
  202. ui->customPlot->xAxis->setTicks(true);
  203. ui->customPlot->yAxis->setTicks(true);
  204. // 刻度值显示
  205. ui->customPlot->xAxis->setTickLabels(true);
  206. ui->customPlot->yAxis->setTickLabels(true);
  207. // 网格显示
  208. ui->customPlot->xAxis->grid()->setVisible(true);
  209. ui->customPlot->yAxis->grid()->setVisible(true);
  210. // 子网格显示
  211. ui->customPlot->xAxis->grid()->setSubGridVisible(false);
  212. ui->customPlot->yAxis->grid()->setSubGridVisible(false);
  213. // 右和上坐标轴、刻度值显示
  214. ui->customPlot->xAxis2->setVisible(true);
  215. ui->customPlot->yAxis2->setVisible(true);
  216. ui->customPlot->yAxis2->setTicks(true);
  217. ui->customPlot->yAxis2->setTickLabels(true);
  218. // make top right axes clones of bottom left axes. Looks prettier:
  219. // ui->customPlot->axisRect()->setupFullAxesBox();
  220. // make left and bottom axes always transfer their ranges to right and top axes:
  221. connect(ui->customPlot->xAxis, SIGNAL(rangeChanged(QCPRange)), ui->customPlot->xAxis2, SLOT(setRange(QCPRange)));
  222. connect(ui->customPlot->yAxis, SIGNAL(rangeChanged(QCPRange)), ui->customPlot->yAxis2, SLOT(setRange(QCPRange)));
  223. // 暗色主题
  224. // setPlotTheme(Qt::white, Qt::black);
  225. // 亮色主题
  226. setTheme(Qt::black, Qt::white);
  227. // 可放大缩小和移动
  228. ui->customPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom);
  229. // x轴以时间形式显示
  230. QSharedPointer<QCPAxisTickerTime> timeTicker(new QCPAxisTickerTime);
  231. timeTicker->setTimeFormat("%h:%m:%s");
  232. ui->customPlot->xAxis->setTicker(timeTicker);
  233. ui->customPlot->axisRect()->setupFullAxesBox();
  234. ui->customPlot->replot();
  235. }
  236. /* 设置绘图主题 */
  237. void WidgetPlot2D::setTheme(QColor axis, QColor background) {
  238. // 坐标颜色按钮
  239. QPalette pal = ui->axisColorPBtn->palette();
  240. pal.setColor(QPalette::Button, axis);
  241. ui->axisColorPBtn->setPalette(pal);
  242. ui->axisColorPBtn->setAutoFillBackground(true); // 该句不能缺少,否则背景颜色无法改变
  243. ui->axisColorPBtn->setFlat(true); // 该句不能缺少,否则背景颜色无法改变
  244. // 背景颜色按钮
  245. pal = ui->backgroundColorPBtn->palette();
  246. pal.setColor(QPalette::Button, background);
  247. ui->backgroundColorPBtn->setPalette(pal);
  248. ui->backgroundColorPBtn->setAutoFillBackground(true); // 该句不能缺少,否则背景颜色无法改变
  249. ui->backgroundColorPBtn->setFlat(true); // 该句不能缺少,否则背景颜色无法改变
  250. //----------------------------------------------------------------------------------------//
  251. // 坐标标注颜色
  252. ui->customPlot->xAxis->setLabelColor(axis);
  253. ui->customPlot->yAxis->setLabelColor(axis);
  254. // 坐标刻度值颜色
  255. ui->customPlot->xAxis->setTickLabelColor(axis);
  256. ui->customPlot->yAxis->setTickLabelColor(axis);
  257. // 坐标基线颜色和宽度
  258. ui->customPlot->xAxis->setBasePen(QPen(axis, 1));
  259. ui->customPlot->yAxis->setBasePen(QPen(axis, 1));
  260. // 坐标主刻度颜色和宽度
  261. ui->customPlot->xAxis->setTickPen(QPen(axis, 1));
  262. ui->customPlot->yAxis->setTickPen(QPen(axis, 1));
  263. // 坐标子刻度颜色和宽度
  264. ui->customPlot->xAxis->setSubTickPen(QPen(axis, 1));
  265. ui->customPlot->yAxis->setSubTickPen(QPen(axis, 1));
  266. // 坐标标注颜色
  267. ui->customPlot->xAxis2->setLabelColor(axis);
  268. ui->customPlot->yAxis2->setLabelColor(axis);
  269. // 坐标刻度值颜色
  270. ui->customPlot->xAxis2->setTickLabelColor(axis);
  271. ui->customPlot->yAxis2->setTickLabelColor(axis);
  272. // 坐标基线颜色和宽度
  273. ui->customPlot->xAxis2->setBasePen(QPen(axis, 1));
  274. ui->customPlot->yAxis2->setBasePen(QPen(axis, 1));
  275. // 坐标主刻度颜色和宽度
  276. ui->customPlot->xAxis2->setTickPen(QPen(axis, 1));
  277. ui->customPlot->yAxis2->setTickPen(QPen(axis, 1));
  278. // 坐标子刻度颜色和宽度
  279. ui->customPlot->xAxis2->setSubTickPen(QPen(axis, 1));
  280. ui->customPlot->yAxis2->setSubTickPen(QPen(axis, 1));
  281. // 整个画布背景色
  282. ui->customPlot->setBackground(background);
  283. // 绘图区域背景色
  284. ui->customPlot->axisRect()->setBackground(background);
  285. // 刷新绘图
  286. ui->customPlot->replot();
  287. }
  288. /* 初始化控件 */
  289. void WidgetPlot2D::initWidget() {
  290. // 时间轴默认时间宽度
  291. ui->timeAxisSpin->setValue(20);
  292. // 主题
  293. QStringList theme;
  294. theme << "亮色"
  295. << "暗色"
  296. << "自定义";
  297. ui->themeCombo->addItems(theme);
  298. connect(ui->themeCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(changePlotTheme()));
  299. //----------------------------------------------------------------------------------------//
  300. // 坐标颜色按钮
  301. QPalette pal = ui->axisColorPBtn->palette();
  302. pal.setColor(QPalette::Button, Qt::black);
  303. ui->axisColorPBtn->setPalette(pal);
  304. ui->axisColorPBtn->setAutoFillBackground(true); // 该句不能缺少,否则背景颜色无法改变
  305. ui->axisColorPBtn->setFlat(true); // 该句不能缺少,否则背景颜色无法改变
  306. connect(ui->axisColorPBtn, SIGNAL(clicked()), this, SLOT(changePlotTheme()));
  307. // 背景颜色按钮
  308. pal = ui->backgroundColorPBtn->palette();
  309. pal.setColor(QPalette::Button, Qt::white);
  310. ui->backgroundColorPBtn->setPalette(pal);
  311. ui->backgroundColorPBtn->setAutoFillBackground(true); // 该句不能缺少,否则背景颜色无法改变
  312. ui->backgroundColorPBtn->setFlat(true); // 该句不能缺少,否则背景颜色无法改变
  313. connect(ui->backgroundColorPBtn, SIGNAL(clicked()), this, SLOT(changePlotTheme()));
  314. // 操作按钮
  315. connect(ui->clearPBtn, SIGNAL(clicked()), this, SLOT(plotOperation()));
  316. connect(ui->fullShowPBtn, SIGNAL(clicked()), this, SLOT(plotOperation()));
  317. connect(ui->savePBtn, SIGNAL(clicked()), this, SLOT(plotOperation()));
  318. connect(ui->pausePBtn, SIGNAL(clicked()), this, SLOT(plotOperation()));
  319. //----------------------------------------------------------------------------------------//
  320. }
  321. /* 改变画图主题 */
  322. void WidgetPlot2D::changePlotTheme() {
  323. // 主题选择
  324. if (QComboBox* combo = dynamic_cast<QComboBox*>(sender())) {
  325. if (combo->currentText() == "亮色") {
  326. setTheme(Qt::black, Qt::white); // 亮色主题
  327. } else if (combo->currentText() == "暗色") {
  328. setTheme(Qt::white, Qt::black); // 暗色主题
  329. } else if (combo->currentText() == "自定义") {
  330. // 绘图坐标颜色
  331. QPalette axisPal = ui->axisColorPBtn->palette();
  332. QColor axisColor = axisPal.color(QPalette::Button);
  333. // 绘图背景颜色
  334. QPalette backgroundPal = ui->backgroundColorPBtn->palette();
  335. QColor backgroundColor = backgroundPal.color(QPalette::Button);
  336. setTheme(axisColor, backgroundColor);
  337. }
  338. }
  339. // 用户自定义主题
  340. if (QPushButton* btn = dynamic_cast<QPushButton*>(sender())) {
  341. QColor color = QColorDialog::getColor(Qt::white, this);
  342. // 用户取消颜色对话框
  343. if (color.isValid() == false) return;
  344. // 设置按钮背景色
  345. QPalette pal = btn->palette();
  346. pal.setColor(QPalette::Button, color);
  347. btn->setPalette(pal);
  348. btn->setAutoFillBackground(true); // 该句不能缺少,否则背景颜色无法改变
  349. btn->setFlat(true); // 该句不能缺少,否则背景颜色无法改变
  350. // 绘图坐标颜色
  351. QPalette axisPal = ui->axisColorPBtn->palette();
  352. QColor axisColor = axisPal.color(QPalette::Button);
  353. // 绘图背景颜色
  354. QPalette backgroundPal = ui->backgroundColorPBtn->palette();
  355. QColor backgroundColor = backgroundPal.color(QPalette::Button);
  356. // 改变主题颜色
  357. setTheme(axisColor, backgroundColor);
  358. ui->themeCombo->setCurrentText("自定义");
  359. }
  360. }
  361. /* 绘图操作 */
  362. void WidgetPlot2D::plotOperation() {
  363. if (QPushButton* btn = dynamic_cast<QPushButton*>(sender())) {
  364. if (btn->text() == "清除") {
  365. for (int i = 0; i < ui->customPlot->graphCount(); i++) {
  366. // 先获得每条曲线的数据指针,然后删除数据
  367. ui->customPlot->graph(i)->data()->clear();
  368. ui->customPlot->replot();
  369. time = QTime::currentTime();
  370. }
  371. }
  372. if (btn->text() == "整图") {
  373. ui->customPlot->rescaleAxes();
  374. ui->customPlot->replot();
  375. }
  376. if (btn->text() == "保存") {
  377. savePlotPng();
  378. }
  379. if (btn->text() == "暂停") {
  380. btn->setText("开始");
  381. QIcon icon(":/image/player.png");
  382. btn->setIcon(icon);
  383. } else if (btn->text() == "开始") {
  384. btn->setText("暂停");
  385. QIcon icon(":/image/pause.png");
  386. btn->setIcon(icon);
  387. }
  388. }
  389. }
  390. /* 判断路径是否存在,不存在则新建,只能创建一级子目录,必须保证上级目录存在 */
  391. bool WidgetPlot2D::isDirExist(QString fullPath) {
  392. QDir dir(fullPath);
  393. if (dir.exists()) {
  394. return true;
  395. } else {
  396. // 创建一级子目录,必须保证上级目录存在
  397. bool ok = dir.mkdir(fullPath);
  398. return ok;
  399. }
  400. }
  401. /* 保存绘图成图片 */
  402. void WidgetPlot2D::savePlotPng() {
  403. // 获取程序运行路径
  404. QString savePath = QCoreApplication::applicationDirPath() + "/output";
  405. // 判断文件路径
  406. if (isDirExist(savePath)) {
  407. } else {
  408. qDebug() << "保存文件目录未找到!";
  409. QMessageBox::warning(this, "warning", "保存文件目录未找到!", QMessageBox::Yes, QMessageBox::Yes);
  410. }
  411. QString fileName = QFileDialog::getSaveFileName(this,
  412. "保存波形数据", // 对话框的标题
  413. savePath, // 保存的默认路径为程序运行路径
  414. "Save Picture (*.png *jpg)"); // 打开文件的类型,;隔开
  415. // 如果用户点击了“取消”按钮
  416. if (fileName.isNull()) return;
  417. // 保存图片
  418. ui->customPlot->savePng(fileName, 1280, 800, 1.0, -1, 255);
  419. }
  420. /* 水平滚动条移动 */
  421. void WidgetPlot2D::horzScrollBarChanged(int value) {
  422. // 若全程显示,则不移动绘图坐标
  423. if (ui->fullShowCheck->isChecked()) return;
  424. int timeAxis = ui->timeAxisSpin->value();
  425. ui->customPlot->xAxis->setRange(value, timeAxis, Qt::AlignRight);
  426. ui->customPlot->replot();
  427. }