使用QT做虚拟示波器,共16通道,波形是重叠在一起(不同颜色区分),想用写好的TCP传输来让单片机与电脑通信,解刨数据,放入到示波器中进行显示。


准备工作:首先我缺一个绘图的控件,于是在网上找了找,网上给我介绍的有三种。分别是qcustomplot、qwt以及qchart。我第一次使用的是qcustomplot,我发现使用qcustomplot如果不生成静态库再使用的话,运行起来特别特别慢,这就对编程带来了不便。这其实也不能怪人家,它毕竟大小就有1M多,我们一般写代码一个文件才几十K,所以想呀,qcustomplot编译起来肯定很花时间。然后我就使用了QChart类,我听说会玩这个的话,要比另外两种强。qwt我还没碰过。声明一下,我只是谈谈我个人的想法,不喜勿喷!

  1. QChartView是一个窗口,提供显示的地方。而QChart则像是一张图纸,QLineSeries直线或者QSplineSeries平滑线就相当于是画笔了。我使用继承QChartView来做一个方便我对外使用打开通道、关闭通道、添加数据等功能的接口。

    class WaveDisplay : public QChartView
  2. 使用枚举定义16个通道

    //设置通道数
    typedef enum
    {Channel1,  Channel2,  Channel3,  Channel4,Channel5,  Channel6,  Channel7,  Channel8,Channel9,  Channel10, Channel11, Channel12,Channel13, Channel14, Channel15, Channel16
    }Channel;
  3. 曲线选择类型,方便后面更改

    typedef QLineSeries LineType;//QLineSeries 直线     QSplineSeries 平滑线
    
  4. 整个控件需要用到一些共有变量,每一次打开一个通道的新建一只画笔(QSplineSeries),然后这只画笔还得记录下来,为我后续添加数据使用,我在这里使用的是QHash来保存的,个人感觉这个还是挺好用的,推荐!后面三行用于自动控制刻度范围。

    QChart *chart;                                              //图纸
    QValueAxis *axisX, *axisY;                                  //坐标轴
    QHash<Channel, LineType*> seriesHash;                       //将多条线放在图纸上
    QHash<Channel, double> minALL;                              //记录所有曲线的最小值
    QHash<Channel, double> maxALL;                              //记录所有曲线的最大值
    double Xmax = 0, Ymax = -350;
    double Xmin = 0, Ymin = 350;
    double Xrang = 3;
  5. 初始化工作

    WaveDisplay::WaveDisplay(QWidget *parent) : QChartView(parent)
    {mainLayout = new QGridLayout(parent);             //居中显示mainLayout->addWidget(this, 0, 1, 3, 1);chart = new QChart;                               //画图纸chart->legend()->setVisible(false);               //曲线文本提示setChart(chart);                                  //添加图纸axisX = new QValueAxis;                           //设置坐标轴axisY = new QValueAxis;chart->addAxis(axisX, Qt::AlignBottom);           //将坐标轴加到chart上,居下chart->addAxis(axisY, Qt::AlignLeft);             //居左setRenderHint(QPainter::Antialiasing, true);      //反走样/*特效*///setRubberBand(QChartView::RectangleRubberBand); //矩形缩放//chart->setAnimationOptions(QChart::SeriesAnimations); //曲线动画模式,不能启用这一项或是选择这个选项,这个会导致曲线闪烁
    }
    
  6. 打开一个通道

    void WaveDisplay::openChannel(Channel channel)//添加一条平滑线并添加到图纸中
    {minALL.insert(channel, 100000);maxALL.insert(channel, -100000);if(seriesHash.contains(channel))                 //如果重复打开同一个通道就会执行这条语句,主要是防止内存泄漏{chart->addSeries(seriesHash[channel]);chart->setAxisX(axisX, seriesHash[channel]);  //将x和y坐标轴与第一条曲线连接chart->setAxisY(axisY, seriesHash[channel]);return;}LineType *series = new LineType();series->setPen(Color[channel]);chart->addSeries(series);chart->setAxisX(axisX, series);                   //将x和y坐标轴与第一条曲线连接chart->setAxisY(axisY, series);seriesHash.insert(channel, series);               //记录到表中
    }
  7. 颜色如果自己不想配的话可以直接使用我自己配的,配16中人眼看起来完全不同颜色还是挺难受的

    namespace ChannelColor{
    static QColor Color[16] = {QColor(165, 42, 42),    //Channel1ColorQColor (255, 127, 80),  //Channel2ColorQColor (30, 144, 255),  //Channel3ColorQColor (218, 165, 32),  //Channel4ColorQColor (255, 0, 255),   //Channel5ColorQColor (147, 112, 219), //Channel6ColorQColor (0, 255, 255),   //Channel7ColorQColor (0, 0, 128),     //Channel8ColorQColor (128, 0, 128),   //Channel9ColorQColor (46, 139, 87),   //Channel10ColorQColor (152, 251, 152), //Channel11ColorQColor (0, 255, 127),   //Channel12ColorQColor (0, 0, 255),     //Channel13ColorQColor (135, 206, 250), //Channel14ColorQColor (255, 192, 203), //Channel15ColorQColor (127, 255, 0)    //Channel16Color
    };
    }
    
  8. 关闭通道

    void WaveDisplay::closeChannel(Channel channel)
    {if(seriesHash.contains(channel))                //如果先前没有打开这通道,程序就不会执行这条复合语句,主要防止用户点错{chart->removeSeries(seriesHash[channel]);   //先删去图纸上的线minALL.remove(channel);maxALL.remove(channel);}
    }
  9. 可以在设置数据和追加数据的时候添加判断,这样就可以自动刻度了

    void WaveDisplay::setData(Channel channel, const QList<QPointF>& data)
    { if(seriesHash.contains(channel) && chart->series().contains(seriesHash[channel])){seriesHash[channel]->clear();seriesHash[channel]->append(data);Xmin = data[0].x() < Xmin ? data[0].x() : Xmin;Xmax = data[data.count() - 1].x() > Xmax ? data[data.count() - 1].x() : Xmax;for(int i = 0; i < data.count(); i++){maxALL[channel] = data[i].y() > maxALL[channel] ? data[i].y() : maxALL[channel];minALL[channel] = data[i].y() < minALL[channel] ? data[i].y() : minALL[channel];}updateChart();}
    }
    
  10. 追加数据

    void WaveDisplay::appendData(Channel channel, const QPointF& data)
    {if(seriesHash.contains(channel) && chart->series().contains(seriesHash[channel])){seriesHash[channel]->append(data);if(data.x() > Xrang){Xmax = data.x();Xmin = Xmax - Xrang;}maxALL[channel] = data.y() > maxALL[channel] ? data.y() : maxALL[channel];minALL[channel] = data.y() < minALL[channel] ? data.y() : minALL[channel];updateChart();}
    }
    void WaveDisplay::appendData(Channel channel, const QList<QPointF>& data)
    {if(seriesHash.contains(channel) && chart->series().contains(seriesHash[channel])){seriesHash[channel]->append(data);Xmin = data[0].x() < Xmin ? data[0].x() : Xmin;Xmax = data[data.count() - 1].x() > Xmax ? data[data.count() - 1].x() : Xmax;for(int i = 0; i < data.count(); i++){maxALL[channel] = data[i].y() > maxALL[channel] ? data[i].y() : maxALL[channel];minALL[channel] = data[i].y() < minALL[channel] ? data[i].y() : minALL[channel];}updateChart();}
    }
  11. 按键点击事件:ESC——恢复默认显示;TAB——支持鼠标取点

    void WaveDisplay::keyPressEvent(QKeyEvent *event)
    {switch (event->key()){case Qt::Key_Escape:updateChart();break;case Qt::Key_Tab://Tab键被按下showPoint = true;break;default:break;}
    }
  12. 按键释放事件:TAB——不支持鼠标取点

    void WaveDisplay::keyReleaseEvent(QKeyEvent *event)
    {if(event->key() == Qt::Key_Tab)showPoint = false;
    }
  13. 鼠标点击事件:左键——取点;右键——配合曲线移动

    void WaveDisplay::mouseMoveEvent(QMouseEvent *event)//参考:https://blog.csdn.net/qq_31073871/article/details/83019943
    {QPoint temp = event->pos();//获取当前坐标的位置if(showPoint){QPointF f = chart->mapToValue(temp);QString str = '(' + QString::number(f.x()) + ',' + QString::number(f.y()) + ')';QToolTip::showText(event->globalPos(), str);}else{QToolTip::hideText();}//    getPos(event);if(event->buttons() == Qt::RightButton){      // 这里必须使用buttons(),因为鼠标移动过程中会检测所有按下的键,而这个时候button()是无法检测那个按键被按下,所以必须使用buttons()函数,看清楚,是buttons()不是button()QPointF f = chart->mapToValue(temp) - chart->mapToValue(lastPos);axisX->setRange(axisX->min() - f.x(), axisX->max() - f.x());axisY->setRange(axisY->min() - f.y(), axisY->max() - f.y());chart->update();lastPos = temp;update();}
    }
  14. 鼠标点击事件:左键——取点;右键——移动曲线

    void WaveDisplay::mousePressEvent(QMouseEvent *event)
    {if(event->button() == Qt::RightButton){      // 如果是鼠标中键按下QCursor cursor;cursor.setShape(Qt::ClosedHandCursor);QApplication::setOverrideCursor(cursor); // 使鼠标指针暂时改变形状lastPos = event->pos();                  // 获取指针位置和窗口位置的差值,这个globalPos()获取的是鼠标在桌面上的位置,也可以使用pos()函数获取指针在窗口的位置。}else if(event->button() == Qt::LeftButton){getPos(event);}
    }
  15. 鼠标移动事件:如果TAB按下则显示鼠标位置;右键:移动曲线

    void WaveDisplay::mouseMoveEvent(QMouseEvent *event)
    {QPoint temp = event->pos();//获取当前坐标的位置if(showPoint){QPointF f = chart->mapToValue(temp);QString str = '(' + QString::number(f.x()) + ',' + QString::number(f.y()) + ')';QToolTip::showText(event->globalPos(), str);}else{QToolTip::hideText();}if(event->buttons() == Qt::RightButton){      // 这里必须使用buttons(),因为鼠标移动过程中会检测所有按下的键,而这个时候button()是无法检测那个按键被按下,所以必须使用buttons()函数,看清楚,是buttons()不是button()QPointF f = chart->mapToValue(temp) - chart->mapToValue(lastPos);axisX->setRange(axisX->min() - f.x(), axisX->max() - f.x());axisY->setRange(axisY->min() - f.y(), axisY->max() - f.y());chart->update();lastPos = temp;update();}
    }
  16. 鼠标释放事件:配合曲线移动

    void WaveDisplay::mouseReleaseEvent(QMouseEvent *event)
    {Q_UNUSED(event);QApplication::restoreOverrideCursor();         // 恢复鼠标指针形状
    }
  17. 滚轮事件:参考http://blog.csdn.net/smarterr/article/details/80781368

    void WaveDisplay::wheelEvent(QWheelEvent *event)
    {if(event->delta() > 0){                    // 当滚轮远离使用者时chart->zoomIn();                       // 进行放大Xrang /= 1.5;}else{                                     // 当滚轮向使用者方向旋转时chart->zoomOut();                      // 进行缩小Xrang *= 1.5;}
    }
  18. 获取鼠标坐标函数,自己简单写了下思路,欢迎提问

    void WaveDisplay::getPos(QMouseEvent *event)
    {QPoint temp = event->pos();//获取当前坐标的位置if(showPoint){QPointF f = chart->mapToValue(temp);QString str = '(' + QString::number(f.x()) + ',' + QString::number(f.y()) + ')';QToolTip::showText(event->globalPos(), str);QHash<Channel, LineType*>::iterator seriesTemp;for(seriesTemp = seriesHash.begin(); seriesTemp != seriesHash.end(); ++seriesTemp)//遍历每条曲线{QList<QPointF> pointfTemp = seriesTemp.value()->points();//线上所有点对应的屏幕坐标int low = 0;int high = pointfTemp.count() - 1;if(pointfTemp[low].x() > f.x() || pointfTemp[high].x() < f.x()){posList[seriesTemp.key()].clear();continue;}while(low < high - 1)//从某条线找到这个点的附近{int middle = (low + high) / 2;if(pointfTemp[middle].x() < f.x()){low = middle;}if(pointfTemp[middle].x() > f.x()){high = middle;}}if(low == high)posList[seriesTemp.key()] = '(' + QString::number(f.x()) + ',' + QString::number(pointfTemp[high].y()) + ')';else{posList[seriesTemp.key()] = '(' + QString::number(f.x()) + ',' + QString::number\(pointfTemp[low].y() + (pointfTemp[high].y() - pointfTemp[low].y()) / (pointfTemp[high].x() - pointfTemp[low].x()) * (f.x() - pointfTemp[low].x()))\+ ')';}}}else{QToolTip::hideText();}
    }
    
  19. 恢复坐标轴

    void WaveDisplay::updateChart()
    {double num;QHash<Channel, double>::iterator rangTemp;//找最小值if(minALL.size()){rangTemp = minALL.begin();num = rangTemp.value();++rangTemp;for(; rangTemp != minALL.end(); ++rangTemp)//遍历每条曲线{num = rangTemp.value() < num ? rangTemp.value() : num;}Ymin = num;}//找最大值if(maxALL.size()){rangTemp = maxALL.begin();num = rangTemp.value();++rangTemp;for(; rangTemp != maxALL.end(); ++rangTemp)//遍历每条曲线{num = rangTemp.value() > num ? rangTemp.value() : num;}Ymax = num;}axisX->setRange(Xmin, Xmax);//设置图表坐标轴的范围,可以不设置,自动调节的double range = Ymax - Ymin;axisY->setRange(Ymin - range * 0.1, Ymax + range * 0.1);
    }
    

Qt使用QChart制作多路虚拟示波器相关推荐

  1. 使用QT的QChart写自己的示波器_QChart波形显示

    我几个月前最开始学习QT,就是从完成波形显示的功能开始的.之前工作的上位机需要有虚拟示波器功能,也就是波形显示.不曾玩过QT的小白,被安排学习完成一下.当时便选用QChart,根据领导需求也更新迭代过 ...

  2. QT之QCustomPlot绘图实现8通道串口虚拟示波器

    QT之QCustomPlot绘图实现8通道串口虚拟示波器----串口插拔自动检测 QT之QCustomPlot绘图实现8通道串口虚拟示波器----QCustomPlot实时绘图 QT之QCustomP ...

  3. 虚拟示波器OSC802介绍、拆机

    介绍 外壳正面贴的标签明确告知了OSC802示波器的一些参数,2通道,-5V~+5V(此电平的话应该是探头在1x衰减档时刻的,如果配上标配的探头打在10x衰减档的话可以测量-50V~+50V电平),8 ...

  4. STM32使用虚拟示波器

    STM32使用虚拟示波器 在调试过程中.,经常会有需要看到数据实时变化的情况,这时候便需要用到虚拟示波器.如:制作平衡车时,需要了解拟合角度跟随加速度计和陀螺仪的动态变化情况:做电机PWM调速时需要了 ...

  5. Qt实现Qchart的打印和打印预览的几种方法

    实现打印预览和打印,是挺常用的功能.把其他一些内容如QTextBrowser或者QEditText打印和打印预览是容易的,因为它们都自带了print方法,可以直接输出到printer.这里介绍下Qt实 ...

  6. matlab示波器模拟,声卡虚拟示波器-使用matlab DAQ工具箱中API实现

    声卡有两个模拟输入接口,Line In 和麦克风;有一个声音输出 Line Out,即Speeker.两个输入口都可以用作虚拟示波器的输入.但是由于声卡的输入端与内部放大器之间存在一个耦合电容,限制了 ...

  7. 开源虚拟示波器-_一个新的开源数据库,TP-Link路由器上的开源固件以及更多新闻

    开源虚拟示波器- 在本周的开源新闻摘要中,我们介绍了Attic Labs的新开源数据库,TP-Link路由器上的开源固件等. 2016年7月31日至8月6日的开源新闻摘要 Attic Labs宣布新的 ...

  8. USB总线虚拟示波器,用高性能硬件模块,组合成多种仪器

    虚拟示波器HS系列是利用高性能的模块化硬件,结合高效灵活的软件来完成各种测试.测量和自动化的应用,并通过现有的计算机组合成多种测量仪器,完成你想要的各种测试. HS6 虚拟示波器 HS5 虚拟示波器 ...

  9. 虚拟示波器软件 JSCOPE -- 使用 jlink 仿真器来查看变量

    看到一些大佬的公众号文章,才知道有这么一个东西,这东西看起来真不错,所以转载一下 原文链接:<一款超级好用的虚拟示波器软件> 由于链接经常丢失,截图一份,原文是有视频教程的,如果没失效,看 ...

  10. 虚拟示波器-开天斧-STC8H8K64U核心功能实验板测试

    1.  安装keil, 在STC官网下载stc-isp, 并在isp中添加芯片型号和头文件. 2. 在STC官网下载开天斧的代码包,打开文件"19-老刘爱捣鼓示波器-泽文版,使用OLED屏显 ...

最新文章

  1. unix odbc php 连接sqlserver,Ubuntu下通过unixODBC连接MS SqlServer2005
  2. 使用Poco实现插件方式加载动态库
  3. python封装继承多态_浅谈JavaScript的面向对象和它的封装、继承、多态
  4. 虚拟机VM10装Mac OS X 10.9.3
  5. 【转】如何在Qt 4程序中优化布局结构-兼回答网友提问
  6. [概率论与数理统计] 常用定义与公式
  7. 测量学用C语言编程求子午线弧长,GPS数据解析 数据拆分 坐标转换 显示线路图源代码...
  8. Windows放大镜黑屏问题以及快捷键
  9. 海康摄像头音频方案(播放音频文件+语音对讲+语音转发)-支持window/Linux-java版本
  10. 【MPEG】DVB / ATSC / ISDB区别
  11. 【MySQL】SHOW WARNINGS和SHOW ERRORS的作用是什么?
  12. [M1]Daily Scum 9.28
  13. 基于ssm的生活故事分享交流博客系统
  14. ORACLE EBS中附件功能的使用
  15. 登陆动网论坛时报“Microsoft JET Database Engine 错误 '80004005' ”
  16. SMARTFORM A5单据打印(自定义纸张/针式打印机)格式问题
  17. redis.clients.jedis.exceptions.JedisDataException: WRONGTYPE Operation against a key holding the wro
  18. 2022年山东省职业院校技能大赛“网络安全”比赛任务书
  19. 下一个排列,下一字典序
  20. PLSQL批量导出导入存储过程

热门文章

  1. 时差怎么理解_懂的人自然懂,不懂的人再多解释也有时差
  2. 33. Pay Gap for the Brightest Female Graduatea 最聪明的大学女毕业生面临的工资差距
  3. 在网页中插入视频代码大全
  4. 从中序与后序遍历序列构造二叉树
  5. mysql io瓶颈_服务器IO瓶颈对MySQL性能的影响
  6. 公司官网如何快速让百度收录?以及如何做网站流量?
  7. 普加项目管理甘特图使用--安装部署
  8. gopro lrv文件和thm文件
  9. Java程序设计 北京大学 2019冬期末测验
  10. 摄影常用词:光圈、快门、曝光、底片、ISO、焦距、景深