QT可自定义量程等数据的传感器采集器demo
·
前言
因为项目需要,计划写一个可以自定义量程、自己选择设备、选择通道号、选择传感器电压范围和测量范围等数据的上位机软件,用于对不同型号的传感器数据进行分析处理。
最初设计草稿:


最初的demo_1效果展示:

由上图效果可见,主要演示的是可以配置的选项和效果,以及根据量程范围变化以后实际测量的模拟量数据的变化情况。
优化阶段1效果:添加保存文件功能

代码:
咱们一共需要这么几个类:全局信号类、上位机界面类、弹出对话框类、文件处理类。
源码会分享出来供大家参考和自己修改,如果不在后面会放在文章最后。
全局信号类:
#ifndef GLOBALSIGNAL_H
#define GLOBALSIGNAL_H
#include <QObject>
class GlobalSignal : public QObject
{
Q_OBJECT
public:
//获取单实例
static GlobalSignal* getInstance();
//释放单实例
static void deleteInstance();
signals:
void getDataByChannel(const QString& DevName, int ChannelNum, double currVolt);
void getDataByDevNumChannel(const int DevNum, int ChannelNum, double currVolt);
void getValue(int value);
private:
//单实例对象指针
static GlobalSignal *globalSignal;
//构造、析构、拷贝构造、赋值构造私有
GlobalSignal(){};
~GlobalSignal(){};
GlobalSignal(const GlobalSignal &){};
const GlobalSignal &operator=(const GlobalSignal &){};
};
#endif // GLOBALSIGNAL_H
// 使用方式:
// 1. 引入头文件,在私有对象中添加 GlobalSignal *glbSignal;
//
// 2. 构造函数中创建:glbSignal = GlobalSignal::getInstance();
//
// 3. 触发方式:
// emit glbSignal->getValue(num);
// 4. 接收方式:
// connect(glbSignal,&GlobalSignal::getValue,this,[=](int value){
// QString msg = QString("接收到数据:nn:%1").arg(value);
// ui->textBrowser->append(msg);
// });
#include "globalSignal.h"
//全局信号转发单例类: 饿汉式
GlobalSignal* GlobalSignal::globalSignal = new GlobalSignal();
GlobalSignal *GlobalSignal::getInstance()
{
return globalSignal;
}
void GlobalSignal::deleteInstance()
{
if(globalSignal)
{
delete globalSignal;
globalSignal = nullptr;
}
}
数据结构体
#ifndef ASKDIALOGSTRUCT_H
#define ASKDIALOGSTRUCT_H
#include <QString>
#include <QList>
#include <QVector>
// 询问对话框的结构体,用于存储界面中的信息
typedef struct AskDialogContent
{
QString name; // 名称
QString unit; // 单位
int devNum = 0; // 设备号
QString devDes; // 设备描述
int channelNum = 0; // 通道号
QString channelDes; // 通道描述
int voltRangeNum = 0; // 电压范围编号
QString voltRangeDes; // 电压范围描述
double min = 0.0; // 可测物理量范围最小值
double max = 100.0; // 可测物理量范围最大值
}*pAskDialogContent;
typedef struct RecvDataListStruct
{
int count = 0;
// 序号
QList<int> NumList;
// 时间戳
QList<qint64> TimeStampList;
// 数据
QList<double> DataList;
QList<int>& getNumList() {
return NumList;
};
QList<double>& getDataList(){
return DataList;
};
QList<qint64>& getTimeStampList(){
return TimeStampList;
}
}*pRecvDataList;
#endif // ASKDIALOGSTRUCT_H
传感器自定义量程界面类:
界面:

头文件:
#ifndef SENSORADAPTIVEWIDGET_H
#define SENSORADAPTIVEWIDGET_H
#include <QWidget>
#include <QStandardItemModel>
#include <QItemSelectionModel>
#include <QPushButton>
#include <QStringList>
#include <QTableView>
#include <QStandardItem>
#include <QDateTime>
#include <QHBoxLayout>
#include <QModelIndex>
#include <QTextBrowser>
#include <QVector>
#include <QThread>
#include <QDebug>
#include <QtCore>
#include <QtConcurrent/QtConcurrent>
#include "globalSignal.h"
#include "askContentDialog.h"
#include "askDialogStruct.h"
#include "fileHandleWorker.h"
QT_BEGIN_NAMESPACE
namespace Ui { class SensorAdaptiveWidget; }
QT_END_NAMESPACE
class SensorAdaptiveWidget : public QWidget
{
Q_OBJECT
public:
SensorAdaptiveWidget(QWidget *parent = nullptr);
~SensorAdaptiveWidget();
void initTable();
void initUI();
public slots:
// void parseDataByDevNumChannel(const int& DevNum, int ChannelNum, double currVolt);
void test();
private:
Ui::SensorAdaptiveWidget *ui;
GlobalSignal *glbSignal;
QStandardItemModel *m_pStandardItemModel;
QItemSelectionModel *m_pItemSelectModel;
int m_itemIndex = 0;
bool flag = true;
QThread *fileHandleThread;
FileHandleWorker fileHandleWorker;
int m_fileNum = 0; // 按一下添加就加1,删除不会减去
};
#endif // SENSORADAPTIVEWIDGET_H
cpp文件
#include "sensorAdaptiveWidget.h"
#include "ui_sensorAdaptiveWidget.h"
SensorAdaptiveWidget::SensorAdaptiveWidget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::SensorAdaptiveWidget)
{
ui->setupUi(this);
glbSignal = GlobalSignal::getInstance();
fileHandleThread = new QThread;
fileHandleThread->start();
fileHandleWorker.moveToThread(fileHandleThread);
// connect(glbSignal,&GlobalSignal::getDataByChannel,this,&SensorAdaptiveWidget::parseDataByChannel);
initTable();
initUI();
QtConcurrent::run([=](){ test();}); // 测试用的多线程。
}
SensorAdaptiveWidget::~SensorAdaptiveWidget()
{
flag = false;
fileHandleThread->quit();
delete ui;
}
void SensorAdaptiveWidget::initTable()
{
m_pStandardItemModel = new QStandardItemModel(this);
m_pItemSelectModel = new QItemSelectionModel(m_pStandardItemModel);
ui->tableView->setModel(m_pStandardItemModel);
ui->tableView->setSelectionModel(m_pItemSelectModel);
ui->tableView->setSelectionMode(QAbstractItemView::ExtendedSelection);
ui->tableView->setSelectionBehavior(QAbstractItemView::SelectItems);
ui->tableView->setMinimumSize(50, 25);
// ui->tableView->verticalHeader()->hide();
// ui->tableView->horizontalHeader()->show();
QStringList columnTitles; // 名字,数值,单位,按键
columnTitles << "名字" << "数据" << "单位" << "删除" << "详情" << "导出文件";
m_pStandardItemModel->setHorizontalHeaderLabels(columnTitles);
// connect(m_pStandardItemModel, &QStandardItemModel::dataChanged, this, &SensorAdaptiveWidget::onTableDataChanged);
}
void SensorAdaptiveWidget::initUI()
{
// 添加采集项的按钮事件:
connect(ui->pBtn_addItem,&QPushButton::clicked,this,[=](){
// #弹出对话框,填写信息
AskContentDialog askDialog(this);
if(askDialog.exec() == QDialog::Accepted)
{
// qDebug()<<"ok";
// #获取对话框信息
AskDialogContent content = askDialog.getContent();
// 准备存储:
RecvDataListStruct* recvDataStruct = new RecvDataListStruct;
fileHandleWorker.getRecvDataListVector()->append(recvDataStruct);
m_fileNum++;
// 调试:
// qDebug()<<content.name;
// qDebug()<<content.unit;
// qDebug()<<content.devNum;
// qDebug()<<content.devDes;
// qDebug()<<content.channelNum;
// qDebug()<<content.channelDes;
// qDebug()<<content.min;
// qDebug()<<content.max;
// qDebug()<<"------------------";
// #将tableview增加一行
int currCount = m_pStandardItemModel->rowCount();
m_pStandardItemModel->setItem(currCount,0,new QStandardItem(content.name)); //名称
m_pStandardItemModel->setItem(currCount,1,new QStandardItem("0.0")); //数值
m_pStandardItemModel->setItem(currCount,2,new QStandardItem(content.unit)); //单位
QPersistentModelIndex theIndex(m_pStandardItemModel->index(currCount, 0));
// 绑定数据变动信号:接收到指定设备和通道的信号数据
connect(glbSignal,&GlobalSignal::getDataByDevNumChannel,this, [=](const int DevNum, int ChannelNum, double currVolt){
if(DevNum==content.devNum && ChannelNum==content.channelNum){
// qDebug()<<QString("get signal : DevNum:%1, ChannelNum:%2, currVolt:%3").arg(DevNum).arg(ChannelNum).arg(currVolt);
// qDebug()<<QString("currContent: devNum:%1, channelNum:%2").arg(content.devNum).arg(content.channelNum);
// 换算公式:
double voltRangeMax = 0.0;
switch (content.voltRangeNum) {
case 0:
voltRangeMax = 2.5;
break;
case 1:
voltRangeMax = 5;
break;
case 2:
voltRangeMax = 10;
break;
case 3:
voltRangeMax = 20;
break;
default:
voltRangeMax = 10;
break;
}
//当前实际值 = (当前电压 / 电压量程差)* (传感器量程差) + 传感器量程下限
double currValue = (currVolt/voltRangeMax)*(content.max - content.min)+content.min;
QStandardItem *item = new QStandardItem(QString::number(currValue,'f',2));
// 原始值:
// QStandardItem *item = new QStandardItem(QString::number(currVolt));
m_pStandardItemModel->setItem(theIndex.row(),1, item);
// 存储数据,写数据:
QDateTime currDateTime = QDateTime::currentDateTime();
qint64 currTimeStamp = currDateTime.currentSecsSinceEpoch();
int temp_count = recvDataStruct->count;
recvDataStruct->getNumList().append(temp_count);
recvDataStruct->getDataList().append(QString::number(currValue, 'f', 2).toDouble());
recvDataStruct->getTimeStampList().append(currTimeStamp);
recvDataStruct->count++;
}
});
// #填到tableview中
QPushButton *button1 = new QPushButton("删除");
button1->setProperty("id", m_itemIndex);
QPushButton *button2 = new QPushButton("详情");
button2->setProperty("id", m_itemIndex);
QPushButton *button3 = new QPushButton("导出");
button3->setProperty("id", m_itemIndex);
m_itemIndex++;
// 绑定按钮事件:
connect(button1, &QPushButton::clicked, [=](){
m_pStandardItemModel->removeRow(theIndex.row());
//disconnect();
}); // 删除按钮
connect(button2, &QPushButton::clicked, this, [=](){
AskContentDialog desDialog;
desDialog.loadContentToShow(content);
desDialog.show();
desDialog.exec();
}); // 详情按钮
connect(button3, &QPushButton::clicked, this, [=](){
QMetaObject::invokeMethod(&fileHandleWorker,"writeCsv",Qt::DirectConnection, Q_ARG(QString, QString("D:/%1_dev%2_ch%3.csv").arg(content.name).arg(content.devNum).arg(currCount)),Q_ARG(int, currCount));
}); // 导出按钮
ui->tableView->setIndexWidget(m_pStandardItemModel->index(theIndex.row(),3),button1);
ui->tableView->setIndexWidget(m_pStandardItemModel->index(theIndex.row(),4),button2);
ui->tableView->setIndexWidget(m_pStandardItemModel->index(theIndex.row(),5),button3);
}
});
}
void SensorAdaptiveWidget::test()
{
int num = 6000;
while (flag && num--) {
emit glbSignal->getDataByDevNumChannel(0,num % 16,1.0*(qrand()%10000)/1000.0);
QThread::msleep(20);
}
}
弹出对话框类
界面:

头文件:
#ifndef ASKCONTENTDIALOG_H
#define ASKCONTENTDIALOG_H
#include <QDialog>
#include <QString>
#include "askDialogStruct.h"
namespace Ui {
class AskContentDialog;
}
class AskContentDialog : public QDialog
{
Q_OBJECT
public:
explicit AskContentDialog(QWidget *parent = nullptr);
~AskContentDialog();
AskDialogContent getContent(); // 获取数据结构体
void loadContentToShow(AskDialogContent content);
private:
Ui::AskContentDialog *ui;
};
#endif // ASKCONTENTDIALOG_H
cpp文件:
#include "askContentDialog.h"
#include "ui_askContentDialog.h"
AskContentDialog::AskContentDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::AskContentDialog)
{
ui->setupUi(this);
// 设置对话框标题
setWindowTitle("新采集项对话框");
}
AskContentDialog::~AskContentDialog()
{
delete ui;
}
AskDialogContent AskContentDialog::getContent()
{
AskDialogContent content;
content.name = ui->lineEdit_name->text();
content.unit = ui->lineEdit_unit->text();
content.devNum = ui->comboBox_dev->currentIndex();
content.devDes = ui->comboBox_dev->currentText();
content.channelNum = ui->comboBox_channelNum->currentIndex();
content.channelDes = ui->comboBox_channelNum->currentText();
content.voltRangeNum = ui->comboBox_VoltRange->currentIndex();
content.voltRangeDes = ui->comboBox_VoltRange->currentText();
content.min = ui->doubleSpinBox_min->value();
content.max = ui->doubleSpinBox_max->value();
return content;
}
void AskContentDialog::loadContentToShow(AskDialogContent content)
{
ui->label->setText("配置查看");
ui->buttonBox->setVisible(false);
ui->lineEdit_name->setText(content.name);
ui->lineEdit_unit->setText(content.unit);
ui->comboBox_dev->setCurrentIndex(content.devNum);
ui->comboBox_channelNum->setCurrentIndex(content.channelNum);
ui->comboBox_VoltRange->setCurrentIndex(content.voltRangeNum);
ui->doubleSpinBox_min->setValue(content.min);
ui->doubleSpinBox_max->setValue(content.max);
}
源码:
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)