QT实现固高运动控制卡示波器

news2024/11/24 8:25:04

目录

一、固高示波器

二、基于QCustomPlot实现示波器

三、完整源码


一、固高示波器

        固高运动控制卡自带的软件有一个示波器功能,可以实时显示速度的波形,可辅助分析电机的运行状态。但是我们基于sdk开发了自己的软件,无法再使用该功能,原因是2个软件不能同时与控制卡通信,故此需要我们自己再开发一个示波器。

固高示波器功能展示,功能包括多条曲线的显示、继续/暂停、左移、右移、设置、轴选择等。

二、基于QCustomPlot实现示波器

GCustomPlot简介与使用看官网就可以了

简介, Qt Plotting Widget QCustomPlot - Introduction

下载, Qt Plotting Widget QCustomPlot - Download

需要注意的是需要在pro文件中加入printsuppot模块,源码中使用该模块做pdf打印功能

QT       += printsupport

 参考固高的功能实现速度示波器效果如下

 QCustomPlot初始化设置

    m_customPlot = new QCustomPlot(this);
    m_customPlot->setObjectName(QLatin1String("customPlot"));
    ui->vlyPlot->addWidget(m_customPlot);

    m_customPlot->setBackground(QBrush(QColor("#474848")));
    m_customPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom |
                                  QCP::iSelectPlottables);  /* 可拖拽+可滚轮缩放 */
    m_customPlot->legend->setVisible(true);

添加两条曲线图,addGraph

    m_customPlot->addGraph();
    m_customPlot->graph(0)->setPen(QPen(Qt::green, 3));
    m_customPlot->graph(0)->setName(QStringLiteral("实际速度"));

    m_customPlot->addGraph();
    m_customPlot->graph(1)->setPen(QPen(Qt::yellow, 3));
    m_customPlot->graph(1)->setName(QStringLiteral("规划速度"));

 左右移动,通过设置X轴的范围来改变,再使用replot函数更新视图

m_customPlot->xAxis->setRange(m_customPlot->xAxis->range().lower - ui->spbStep->value(),
                                      m_customPlot->xAxis->range().upper - ui->spbStep->value());
        m_customPlot->replot();

网格线显示与隐藏

        m_customPlot->xAxis->grid()->setVisible(checked);
        m_customPlot->yAxis->grid()->setVisible(checked);
        m_customPlot->replot();

添加数据,示波器有向左自动滚动的效果,也是通过设置范围来实现

void OscilloscopeFrame::addData(double key, double actVel, double prfVel)
{
    m_customPlot->graph(0)->addData(key, actVel);
    m_customPlot->graph(1)->addData(key, prfVel);

    m_customPlot->xAxis->rescale();
    m_customPlot->graph(0)->rescaleValueAxis(false, true);
    m_customPlot->graph(1)->rescaleValueAxis(false, true);

    m_customPlot->xAxis->setRange(m_customPlot->xAxis->range().upper,
                                  m_fixedLength, Qt::AlignRight);
    m_customPlot->replot(QCustomPlot::rpQueuedReplot);  /* 实现重绘 */
}

实时显示数据,通过定时器实现

    m_timer = new QTimer();
    connect(m_timer, &QTimer::timeout, updateData);
    m_timer->start(OSCILLOSCOPE_UPDATE_MSEC);

鼠标放在曲线上显示当前值,关联鼠标移动信号,映射坐标pixelToCoord

    connect(m_customPlot, SIGNAL(mouseMove(QMouseEvent *)), this,
            SLOT(plotMouseMoveEvent(QMouseEvent *)));
void OscilloscopeFrame::plotMouseMoveEvent(QMouseEvent *event)
{
    if ( Qt::ControlModifier == event->modifiers())
    {
        int x_pos = event->pos().x();
        int x_val = m_customPlot->xAxis->pixelToCoord(x_pos);
        float y = m_customPlot->graph(0)->data()->at(x_val)->value;
        QString strToolTip = QString("%1,%2").arg(x_val).arg(y);
        QToolTip::showText(cursor().pos(), strToolTip, m_customPlot);
    }
}

三、完整源码

OscilloscopeFrame.h

#ifndef OSCILLOSCOPEFRAME_H
#define OSCILLOSCOPEFRAME_H

#include <QFrame>
#include "oscilloscopelib_global.h"

namespace Ui
{
    class OscilloscopeFrame;
}

class QCustomPlot;

class AbstractRobot;

class OSCILLOSCOPELIBSHARED_EXPORT OscilloscopeFrame : public QFrame
{
    Q_OBJECT

public:
    explicit OscilloscopeFrame(QWidget *parent = 0);
    ~OscilloscopeFrame();
    void setFixedLength(double length);
    void addData(double key, double actVel, double prfVel);
    void installController(AbstractRobot *controller);

private slots:
    void plotMouseMoveEvent(QMouseEvent *event);

private:
    Ui::OscilloscopeFrame *ui;
    QCustomPlot *m_customPlot = nullptr;
    QTimer *m_timer = nullptr;
    double m_fixedLength;
    AbstractRobot *m_controller = nullptr;
    double m_actPos = 0;
    double m_count = 0;
};

#endif // OSCILLOSCOPEFRAME_H

OscilloscopeFrame.cpp

#include "OscilloscopeFrame.h"
#include "qcustomplot.h"
#include "ui_OscilloscopeFrame.h"
#include <QtMath>
#include <QDebug>
#include <device/AbstractRobot.h>

#define OSCILLOSCOPE_UPDATE_MSEC  60

OscilloscopeFrame::OscilloscopeFrame(QWidget *parent) :
    QFrame(parent),
    ui(new Ui::OscilloscopeFrame)
{
    ui->setupUi(this);
    ui->spbStep->setValue(1);
    for(int i = 1; i <= 8; ++i)
    {
        ui->cmbAxisId->addItem(QString::number(i));
    }

    m_customPlot = new QCustomPlot(this);
    m_customPlot->setObjectName(QLatin1String("customPlot"));
    ui->vlyPlot->addWidget(m_customPlot);

    m_customPlot->setBackground(QBrush(QColor("#474848")));
    m_customPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom |
                                  QCP::iSelectPlottables);  /* 可拖拽+可滚轮缩放 */
    m_customPlot->legend->setVisible(true);

    m_customPlot->addGraph();
    m_customPlot->graph(0)->setPen(QPen(Qt::green, 3));
    m_customPlot->graph(0)->setName(QStringLiteral("实际速度"));

    m_customPlot->addGraph();
    m_customPlot->graph(1)->setPen(QPen(Qt::yellow, 3));
    m_customPlot->graph(1)->setName(QStringLiteral("规划速度"));

    m_customPlot->axisRect()->setupFullAxesBox();
    m_customPlot->yAxis->setRange(0, 3.3);

    ui->btnPause2Continue->setCheckable(true);
    setFixedLength(100);

    /* 暂停/继续 */
    connect(ui->btnPause2Continue, &QPushButton::clicked, this, [ = ]()
    {
        if(!m_timer)
        {
            return;
        }
        bool isCheckable = ui->btnPause2Continue->isCheckable();
        if(isCheckable)
        {
            m_timer->stop();
            ui->btnPause2Continue->setText(QStringLiteral("继续"));
        }
        else
        {
            m_timer->start(OSCILLOSCOPE_UPDATE_MSEC);
            ui->btnPause2Continue->setText(QStringLiteral("暂停"));
        }
        ui->btnPause2Continue->setCheckable(!isCheckable);
    });

    /* 左移 */
    connect(ui->btnLeftMove, &QPushButton::clicked, this, [ = ]()
    {
        m_customPlot->xAxis->setRange(m_customPlot->xAxis->range().lower - ui->spbStep->value(),
                                      m_customPlot->xAxis->range().upper - ui->spbStep->value());
        m_customPlot->replot();
    });

    /* 右移 */
    connect(ui->btnRightMove, &QPushButton::clicked, this, [ = ]()
    {
        m_customPlot->xAxis->setRange(m_customPlot->xAxis->range().lower  + ui->spbStep->value(),
                                      m_customPlot->xAxis->range().upper  + ui->spbStep->value());
        m_customPlot->replot();
    });

    /* 显示表格 */
    connect(ui->chkGrid, &QCheckBox::toggled, this, [ = ](bool checked)
    {
        m_customPlot->xAxis->grid()->setVisible(checked);
        m_customPlot->yAxis->grid()->setVisible(checked);
        m_customPlot->replot();
    });

    auto updateData = [ & ]()
    {
        if(m_controller)
        {
            int axis = ui->cmbAxisId->currentText().toInt();
            T_AxisStatus status;
            int ec = m_controller->readAxisStatus(axis, &status);
            if(0 == ec)
            {
                ui->lblActVel->setText(QString::number(status.dEncVel));
                ui->lblPrfVel->setText(QString::number(status.dPrfVel));
                ui->lblActPos->setText(QString::number(status.dEncPos));
                ui->lblPrfPos->setText(QString::number(status.dPrfPos));
                if(m_actPos != status.dEncPos)
                {
                    m_actPos = status.dEncPos;
                    addData(m_count, status.dEncVel, status.dPrfVel);
                    m_count ++;
                }
            }
            else
            {
                ui->lblActVel->setText("error " + QString::number(ec));
                ui->lblPrfVel->setText("error " + QString::number(ec));
                ui->lblActPos->setText("error " + QString::number(ec));
                ui->lblPrfPos->setText("error " + QString::number(ec));
            }
        }
    };

    m_timer = new QTimer();
    connect(m_timer, &QTimer::timeout, updateData);
    m_timer->start(OSCILLOSCOPE_UPDATE_MSEC);

    connect(m_customPlot, SIGNAL(mouseMove(QMouseEvent *)), this,
            SLOT(plotMouseMoveEvent(QMouseEvent *)));

    for(int i = 0; i < 500; i++)
    {
        double x = qDegreesToRadians((double)i);
        addData(i, sin(x), cos(x));
    }
}

OscilloscopeFrame::~OscilloscopeFrame()
{
    m_timer->stop();
    delete m_timer;
    m_timer = nullptr;
    delete ui;
}

void OscilloscopeFrame::setFixedLength(double length)
{
    /* 显示固定长度 */
    m_fixedLength = length;
}

void OscilloscopeFrame::addData(double key, double actVel, double prfVel)
{
    m_customPlot->graph(0)->addData(key, actVel);
    m_customPlot->graph(1)->addData(key, prfVel);

    m_customPlot->xAxis->rescale();
    m_customPlot->graph(0)->rescaleValueAxis(false, true);
    m_customPlot->graph(1)->rescaleValueAxis(false, true);

    m_customPlot->xAxis->setRange(m_customPlot->xAxis->range().upper,
                                  m_fixedLength, Qt::AlignRight);
    m_customPlot->replot(QCustomPlot::rpQueuedReplot);  /* 实现重绘 */
}

void OscilloscopeFrame::installController(AbstractRobot *controller)
{
    m_controller = controller;
}

void OscilloscopeFrame::plotMouseMoveEvent(QMouseEvent *event)
{
    if ( Qt::ControlModifier == event->modifiers())
    {
        int x_pos = event->pos().x();
        int x_val = m_customPlot->xAxis->pixelToCoord(x_pos);
        float y = m_customPlot->graph(0)->data()->at(x_val)->value;
        QString strToolTip = QString("%1,%2").arg(x_val).arg(y);
        QToolTip::showText(cursor().pos(), strToolTip, m_customPlot);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/457568.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

深度学习技巧应用8-各种数据类型的加载与处理,并输入神经网络进行训练

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下深度学习技巧应用8-各种数据类型的加载与处理&#xff0c;并输入神经网络进行训练。在模型训练中&#xff0c;大家往往对各种的数据类型比较难下手&#xff0c;对于非结构化数据已经复杂的数据的要进行特殊处理&…

听我一句劝,别去外包,干了三年,废了....

先说一下自己的情况&#xff0c;大专生&#xff0c;18年通过校招进入湖南某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试…

2.黑马SpringbBoot运维篇笔记自己修改

SpringBoot运维实用篇 ​ 基础篇发布以后&#xff0c;看到了很多小伙伴在网上的留言&#xff0c;也帮助超过100位小伙伴解决了一些遇到的问题&#xff0c;并且已经发现了部分问题具有典型性&#xff0c;预计将有些问题在后面篇章的合适位置添加到本套课程中&#xff0c;作为解…

[社区图书馆】《PyTorch高级机器学习实战》书评

《PyTorch高级机器学习实战》是一本非常实用的机器学习书籍&#xff0c;作者为阿里云智能首席AI专家赵健。这本书的目标读者是具有一定Python编程基础并对深度学习有兴趣的开发者和研究者。 在书中&#xff0c;作者从最基础的线性回归、逻辑回归、卷积神经网络&#xff08;CNN…

前端食堂技术周刊第 80 期:Vite 4.3、Node.js 20、TS 5.1 Beta、Windi CSS 即将落幕

美味值&#xff1a;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f; 口味&#xff1a;东坡肉 食堂技术周刊仓库地址&#xff1a;https://github.com/Geekhyt/weekly 本期摘要 Vite 4.3Node.js 20TypeScript 5.1 BetaWindi CSS 即将落幕Pretty TypeScri…

springboot项目的jar文件以打包成docker镜像的方式部署

清单&#xff1a; 安装有docker的Linuxspringboot打包的jar文件&#xff08;该项目只有一个返回"hello world"接口&#xff09; Linux的IP地址&#xff1a;192.168.221.129 springboot项目的接口&#xff1a; 1、上传jar文件至Linux 我上传的位置为&#xff1a;/…

4.2——派生类的构造函数和析构函数

派生类继承了基类的成员&#xff0c;实现了原有代码的重用&#xff0c;但是基类的构造函数和析构函数不能被继承&#xff0c;在派生类中&#xff0c;如果对派生类新增的成员进行初始化&#xff0c;就需要加入派生类的构造函数。与此同时&#xff0c;对所有从基类继承下来的成员…

SpringMVC-学习修改尚硅谷最新教程笔记

三、SpringMVC 1、SpringMVC简介 1.1、什么是MVC MVC是一种软件架构的思想&#xff0c;将软件按照模型、视图、控制器来划分 M&#xff1a;Model&#xff0c;模型层&#xff0c;指工程中的JavaBean&#xff0c;作用是处理数据 JavaBean分为两类&#xff1a; **一类称为实…

【JAVA-模块五 数组】

JAVA-模块五 数组 一、数组&#xff08;一维&#xff09;1.1数组是什么&#xff1f;1.2java中数组静态初始化&#xff1a;&#xff08;存&#xff09;两种定义格式&#xff1a;数组初始化格式&#xff1a;静态初始化后&#xff0c;打印数组名&#xff1a; 1.3 数组元素访问&…

win11 环境下streamlit使用pycharm debug

目录 1. pycharm中配置run 脚本2. streamlit3. 开始debug调试 1. pycharm中配置run 脚本 &#xff08;一&#xff09;点击 Edit Configurations,按图操作. 2. streamlit 1.streamlit 安装在 anaconda 的 base 环境&#xff08;随意哈&#xff0c;安装哪里都可以&#xff0c…

Zookeeper 面试题总结

Zookeeper 1、工作中 Zookeeper 有什么用途吗2、zookeeper 数据模型是什么样的3、那你知道 znode 有几种类型呢4、你知道 znode 节点里面存储什么吗5、每个节点数据最大不能超过多少呢6、你知道 znode 节点上监听机制嘛7、那你讲下 Zookeeper 特性吧8、你刚提到顺序一致性&…

LRU缓存淘汰策略——面试高频

⭐️前言⭐️ 本文主要介绍在面试中常见的高频手撕算法题目&#xff0c;LRU算法&#xff0c; &#x1f349;欢迎点赞 &#x1f44d; 收藏 ⭐留言评论 &#x1f4dd;私信必回哟&#x1f601; &#x1f349;博主将持续更新学习记录收获&#xff0c;友友们有任何问题可以在评论区…

LEVIR-CD遥感建筑变化检测数据集

LEVIR-CD是一个新的大规模遥感二元变化检测数据集&#xff0c;它将有助于开发新的基于深度学习的遥感图像变化检测算法。 下载地址&#xff1a;https://justchenhao.github.io/LEVIR/ 历史消息 20230311:我们为LEVIR_CD中的每个样本补充了地理空间信息&#xff08;例如&#…

实例分割算法BlendMask

实例分割算法BlendMask 论文地址&#xff1a;https://arxiv.org/abs/2001.00309 github代码&#xff1a;https://github.com/aim-uofa/AdelaiDet 我的个人空间&#xff1a;我的个人空间 密集实例分割 ​ 密集实例分割主要分为自上而下top-down与自下而上bottom-up两类方法…

Node.js代码实例:简单Web服务端

文章目录 前言代码仓库为什么要写一份Node.js简单Web服务端的代码实例&#xff1f;内容目录结构代码server.jsindex.htmlindex.cssindex.jsvalue.html 结果总结参考资料作者的话 前言 Node.js代码实例&#xff1a;简单Web服务端。 代码仓库 yezhening/Programming-examples: …

LVS-DR

系列文章目录 文章目录 系列文章目录一、1.2. 二、实验1.2. 总结 一、 1. 2. 二、实验 1. 先把四台机器都关闭防火墙和安全机制 vim /etc/sysconfig/selinux把selinux都改成disabled 在NFS里面建立两个共享目录 给文件执行权限并写入内容给html 和www vim /etc/expor…

【C++】C++中的类型转化

说起类型转化&#xff0c;我们在C语言之前的学习中可以了解到&#xff0c;类型转换可以分为两种情况&#xff1a;隐式类型转化&#xff1b;显示类型转化。但是为什么在c中还要继续对类型转化做文章呢&#xff1f;我们一起来看&#xff1a; 目录 1. C语言中的类型转换 2. C强制…

【Transformer系列(5)】Transformer代码超详细解读(Pytorch)

前言 前面几篇我们一起读了transformer的论文&#xff0c;更进一步了解了它的模型架构&#xff0c;这一篇呢&#xff0c;我们就来看看它是如何代码实现的&#xff01; &#xff08;建议大家在读这一篇之前&#xff0c;先去看看上一篇模型结构讲解 这样可以理解更深刻噢&…

Python 基础(十二):字典

❤️ 博客主页&#xff1a;水滴技术 &#x1f338; 订阅专栏&#xff1a;Python 入门核心技术 &#x1f680; 支持水滴&#xff1a;点赞&#x1f44d; 收藏⭐ 留言&#x1f4ac; 文章目录 一、声明字典1.1 使用 {} 声明字典1.2 使用 dict 函数声明字典1.3 声明一个空的字典 二…

【数据结构】AVLTree

1.AVL树的概念 二叉搜索树虽可以缩短查找的效率&#xff0c;但如果数据有序或接近有序二叉搜索树将退化为单支树&#xff0c;查找元素相当于在顺序表中搜索元素&#xff0c;效率低下。因此&#xff0c;两位俄罗斯的数G.M.Adelson-Velskii 和E.M.Landis在1962年 发明了一种解决上…