【Qt开发】建立自己的Qt基本类、函数库 包括图表、多线程、串口等
文章目录
- 前言
- QtCharts绘图
- 继承QObject的QThread多线程
- QSerialPort串口配置、发送、接收回调函数
- 附录:C语言到C++的入门知识点(主要适用于C语言精通到Qt的C++开发入门)
- C语言与C++的不同
- C++中写C语言代码
- C语言到C++的知识点
- Qt开发中需要了解的C++基础知识
- namespace
- 输入输出
- 字符串类型
- class类
- 构造函数和析构函数(解析函数)
- 类的继承
前言
为了使开发更方便 对使用到的模块进行了总结 最终封装成了一个类、函数库
使用typedef、class类、回调函数等来建立 方便打包和开发
开源地址:
gitee地址
QtCharts绘图
【Qt开发】QtCharts图表 在ui上添加QChartView控件并进行绘图配置
可以实现一键配置
#ifndef MY_QT_DEF_H
#define MY_QT_DEF_H
#include <QValueAxis>
#include <QList>
#include <QSplineSeries>
#include <QString>
#include <QChart>
#include <QChartView>
QT_CHARTS_USE_NAMESPACE
typedef struct
{
float min;
float max;
QString tittle;
QString format;
Qt::Alignment alignment;
QValueAxis *axis;
}MY_QChartView_Float_Axis_Struct;
//splineSeries曲线实例化(折线用QLineSeries)
typedef struct
{
int maxSize;
QList<float> data;
QString tittle;
MY_QChartView_Float_Axis_Struct X;
MY_QChartView_Float_Axis_Struct Y;
QPainter::RenderHint renderHint;
//上面是必配置的参数 下面是指针类型
QSplineSeries *splineSeries;
QChart *chart;
QChartView *chartView;
}MY_QChartView_Float_Struct;
void Init_MY_QChartView_Float_Axis_Struct(MY_QChartView_Float_Axis_Struct *Stu,QChart *chart);
void Init_MY_QChartView_Float_Struct(MY_QChartView_Float_Struct *Stu);
void Add_MY_QChartView_Float_Value(MY_QChartView_Float_Struct *Stu,float value);
#endif // MY_QT_DEF_H
#include "MY_QT_DEF.h"
void Init_MY_QChartView_Float_Axis_Struct(MY_QChartView_Float_Axis_Struct *Stu,QChart *chart)
{
Stu->axis->setLabelFormat(Stu->format);
Stu->axis->setTitleText(Stu->tittle);
chart->addAxis(Stu->axis, Stu->alignment);
Stu->axis->setRange(Stu->min,Stu->max);
}
void Init_MY_QChartView_Float_Struct(MY_QChartView_Float_Struct *Stu)
{
Stu->chart=new QChart();
Stu->splineSeries = new QSplineSeries();
Stu->X.axis=new QValueAxis();
Stu->Y.axis=new QValueAxis();
Stu->chart->legend()->hide();
Stu->chart->setTitle(Stu->tittle);
Stu->chart->addSeries(Stu->splineSeries);
Init_MY_QChartView_Float_Axis_Struct(&Stu->X,Stu->chart);
Init_MY_QChartView_Float_Axis_Struct(&Stu->Y,Stu->chart);
Stu->splineSeries->attachAxis(Stu->X.axis);
Stu->splineSeries->attachAxis(Stu->Y.axis);
Stu->chartView->setChart(Stu->chart);
Stu->chartView->setRenderHint(Stu->renderHint);
}
//添加数据自动移动函数
void Add_MY_QChartView_Float_Value(MY_QChartView_Float_Struct *Stu,float value)
{
Stu->data.append(value);
while (Stu->data.size() > Stu->maxSize)
{
Stu->data.removeFirst();
}
Stu->splineSeries->clear();
float xSpace = (Stu->X.max-Stu->X.min) / (Stu->maxSize - 1);
for (int i = 0; i < Stu->data.size(); ++i)
{
Stu->splineSeries->append(xSpace * i, Stu->data.at(i));
}
}
继承QObject的QThread多线程
采用moveToThread方法执行多线程
对整体进行了封装
可以自我调用
可以通过注册回调函数来进行不同的工作
【Qt开发】多线程QThread(通过QObject::moveToThread)和QMutex互斥锁的配置和多线程类的封装及回调函数
//QObject的多线程示例
class MY_Thread_Worker;
class MY_Thread_Worker : public QObject
{
Q_OBJECT
private:
QMutex lock;
bool isCanRun;
bool isCanStart;
MY_QT_CALLBACK pfCallback;
public slots:
void Judg_doWork(void)
{
this->isCanRun=true;
//执行
while(this->isCanRun)
{
QMutexLocker locker(&this->lock);
pfCallback(this,0,NULL);
}
isCanStart=true;
}
signals:
void startWork(void);
public:
void * pArg;
QThread *workerThread;
void stopWork(bool Wait_Flag)
{
this->isCanRun = false;
if(Wait_Flag)
{
QMutexLocker locker(&this->lock);
}
}
bool startThread(void)
{
if(!isCanRun && isCanStart)
{
isCanStart=false;
workerThread->start();
emit this->startWork();
return true;
}
return false;
}
bool stopThread(bool Wait_Flag)
{
if(workerThread->isRunning())
{
stopWork(Wait_Flag);
return true;
}
return false;
}
void closeThread(void)
{
stopThread(false);
this->workerThread->quit();
}
MY_Thread_Worker(MY_QT_CALLBACK const pfunc,QThread * worker_Thread = nullptr)
{
pfCallback=pfunc;
pArg=nullptr;
if(!worker_Thread)
{
this->workerThread = new QThread;
}
else
{
this->workerThread = worker_Thread;
}
this->moveToThread(workerThread);
this->stopWork(false);
isCanStart=true;
connect(workerThread, SIGNAL(finished()),this, SLOT(deleteLater()));
connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater()));
connect(this, SIGNAL(startWork()), this, SLOT(Judg_doWork()));
// connect(this, SIGNAL(startWork(QString)), worker, SLOT(Judg_doWork(QString))); 主窗口发送函数
// connect(worker, SIGNAL(resultReady(QString)),this, SLOT(handleResults(QString))); 主窗口接收函数
}
~MY_Thread_Worker()
{
closeThread();
}
};
QSerialPort串口配置、发送、接收回调函数
包装的类具有 单线程/多线程 回调函数/虚函数四种配置方案
同样采用回调函数方便配置和修改
【Qt开发】QSerialPort串口配置、发送、接收回调函数 多线程接收的串口类封装
class MY_SerialPort_Thread;
class MY_SerialPort_Thread : public QObject
{
Q_OBJECT
private:
QMutex lock;
bool isCanRun;
bool isCanStart;
bool Thread_Flag;
MY_QT_CALLBACK pfCallback;
public slots:
void SerialPort_RX(void)
{
SerialPort_RX_Ready_Callback(this);
}
void Callback_SerialPort_RX(void)
{
pfCallback(this,0,NULL);
}
void RX_Thread(void)
{
this->isCanRun=true;
//执行
while(isCanRun)
{
QMutexLocker locker(&this->lock);
if(SerialPort.waitForReadyRead(1))
{
SerialPort_RX_Thread_Callback(this);
}
}
isCanStart=true;
}
void Callback_RX_Thread(void)
{
this->isCanRun=true;
//执行
while(isCanRun)
{
QMutexLocker locker(&this->lock);
if(SerialPort.waitForReadyRead(1))
{
pfCallback(this,1,NULL);
}
}
isCanStart=true;
}
signals:
void Start_RX_Thread(void);
public:
QSerialPort SerialPort;
QList<QString> SerialPort_List;
QString SerialPort_Name;
uint32_t BaudRate;
uint8_t DataBits;
uint8_t StopBits;
QSerialPort::Parity Parity;
QSerialPort::FlowControl FlowControl;
bool Open_Flag;
uint8_t RX_Data;
uint8_t *RX_Buf;
uint32_t RX_Buf_Size;
uint32_t RX_Flag;
QThread workerThread;
void Stop_Port(uint8_t TX_RX_All)
{
switch(TX_RX_All)
{
case 0:SerialPort.clear(QSerialPort::Output);break;
case 1:SerialPort.clear(QSerialPort::Input);break;
default:SerialPort.clear();SerialPort.flush();break;
}
}
void Scan_SerialPort(void)
{
SerialPort_List.clear();
foreach (const QSerialPortInfo &info,QSerialPortInfo::availablePorts())
{
SerialPort_List.append(info.portName());
}
}
void Clean_RX(void)
{
RX_Flag=0;
RX_Data=0;
memset(RX_Buf,0,RX_Buf_Size);
}
void Reset_SerialPort(void)
{
Scan_SerialPort();
BaudRate=115200;
DataBits=8;
StopBits=1;
Parity=QSerialPort::NoParity;
FlowControl = QSerialPort::NoFlowControl;
if(Open_Flag)
{
Stop_Port(2);
}
Clean_RX();
}
bool Ctrl_SerialPort(bool OpenNotClose)
{
if(OpenNotClose)
{
SerialPort.setPortName(SerialPort_Name);
SerialPort.setBaudRate(BaudRate);
SerialPort.setFlowControl(FlowControl);
switch(DataBits)
{
case 5:SerialPort.setDataBits(QSerialPort::Data5);break;
case 6:SerialPort.setDataBits(QSerialPort::Data6);break;
case 7:SerialPort.setDataBits(QSerialPort::Data7);break;
case 8:SerialPort.setDataBits(QSerialPort::Data8);break;
default:break;
}
SerialPort.setParity(Parity);
switch(StopBits)
{
case 1:SerialPort.setStopBits(QSerialPort::OneStop);break;
case 2:SerialPort.setStopBits(QSerialPort::TwoStop);break;
case 3:SerialPort.setStopBits(QSerialPort::OneAndHalfStop);break;
default:break;
}
if (!SerialPort.open(QIODevice::ReadWrite))
{
return false;
}
Open_Flag=true;
if(this->Thread_Flag)
{
startThread();
}
return true;
}
stopThread(true);
Open_Flag=false;
SerialPort.close();
return true;
}
uint32_t SerialPort_TX_Sequence(uint8_t *buf,uint32_t size)
{
if(Open_Flag)
{
return SerialPort.write((char *)buf,size);
}
return 0;
}
uint32_t SerialPort_TX(uint8_t *buf,uint32_t size)
{
if(Open_Flag)
{
SerialPort.clear(QSerialPort::Output);
return SerialPort.write((char *)buf,size);
}
return 0;
}
virtual void SerialPort_RX_Ready_Callback(MY_SerialPort_Thread *port)
{
Q_UNUSED(port);
foreach (const uint8_t i,port->SerialPort.readAll())
{
port->RX_Data=i;
SerialPort_RX_Callback(port);
}
}
virtual void SerialPort_RX_Thread_Callback(MY_SerialPort_Thread *port)
{
Q_UNUSED(port);
foreach (const uint8_t i,port->SerialPort.readAll())
{
port->RX_Data=i;
SerialPort_RX_Callback(port);
}
}
virtual void SerialPort_RX_Callback(MY_SerialPort_Thread *port)
{
Q_UNUSED(port);
qDebug()<<port->RX_Data;
}
void stopWork(bool Wait_Flag)
{
this->isCanRun = false;
if(Wait_Flag)
{
QMutexLocker locker(&this->lock);
}
}
bool startThread(void)
{
if(!isCanRun && isCanStart)
{
isCanStart=false;
workerThread.start();
emit this->Start_RX_Thread();
return true;
}
return false;
}
bool stopThread(bool Wait_Flag)
{
if(workerThread.isRunning())
{
stopWork(Wait_Flag);
return true;
}
return false;
}
void closeThread(void)
{
stopThread(false);
this->workerThread.quit();
}
MY_SerialPort_Thread(uint32_t Buf_Size=256,bool ThreadNotNormal=false,MY_QT_CALLBACK const pfunc=nullptr)
{
pfCallback=pfunc;
SerialPort_Name="";
Open_Flag=false;
RX_Buf_Size = Buf_Size;
RX_Buf = new uint8_t[RX_Buf_Size];
SerialPort.setReadBufferSize(RX_Buf_Size);
this->Thread_Flag = ThreadNotNormal;
Reset_SerialPort();
if(Thread_Flag)
{
this->moveToThread(&workerThread);
this->stopWork(false);
isCanStart=true;
connect(&workerThread, SIGNAL(finished()),this, SLOT(deleteLater()));
connect(&workerThread, SIGNAL(finished()), &workerThread, SLOT(deleteLater()));
if(pfCallback)
{
connect(this, SIGNAL(Start_RX_Thread()), this, SLOT(Callback_RX_Thread()));
}
else
{
connect(this, SIGNAL(Start_RX_Thread()), this, SLOT(RX_Thread()));
}
}
else
{
if(pfCallback)
{
connect(&this->SerialPort, SIGNAL(readyRead()),this, SLOT(Callback_SerialPort_RX()));
}
else
{
connect(&this->SerialPort, SIGNAL(readyRead()),this, SLOT(SerialPort_RX()));
}
}
}
~MY_SerialPort_Thread()
{
Open_Flag=false;
SerialPort.close();
delete RX_Buf;
closeThread();
}
};
附录:C语言到C++的入门知识点(主要适用于C语言精通到Qt的C++开发入门)
C语言与C++的不同
C语言是一门主要是面向工程的语言
C++则是面向对象
C语言中 某些功能实现起来较为繁琐
比如结构体定义:
一般写作:
typedef struct stu_A
{
}A;
也可以写作:
typedef struct
{
}A;
但 大括号后面的名称是不可省去的
不过 C++的写法就比较简单
除了支持上述写法外
也支持直接声明
typedef struct A
{
}
另外 C++是完全支持C语言库和语法的
不过C++里面的库也有些很方便的高级功能用法 只不过实现起来可能不如C的速度快
再者 C语言与C++的编译流程不一样
C语言没有函数重载 所以给编译器传参就是直接传函数名称
但是C++除了传函数名称外 还会穿函数的参数、类型等等 以实现函数重载
C++中写C语言代码
上文提到 C++可以完全兼容C的写法
但是编译流程也还是不一样
所以如果在编译层面进行C语言代码编译 则通常用以下方法:
extern "C"
{
...
}
表面大括号内的内容用C的方法进行编译
另外 如果还是用C++的编译器 但要实现C语言函数 则需要用到C语言的库
在C语言中 我们一般用如下方法导入库
#include <stdio.h>
此方法同样适用于C++ 但是C++可以更方便的写成去掉.h的方式
比如:
#include <iostream>
在C++中 为了调用C语言的库 可以采用在原库名称前加一个"c"的方式导入
如:
#include <cstdio>
这样就可以使用printf等函数了 甚至比C++的std方法更快
C语言到C++的知识点
Qt开发中需要了解的C++基础知识
namespace
C++面向对象的特性下诞生的一个名称
表示某个函数、变量在某个集合下 用作namespace
比如 <iostream>
库中的关键字cin在std下 则写作std::cin
std就是namespace
::表示某空间下的某某
前面是空间名称 后面是变量、函数名称
用using namespace
可以告诉编译器以下都用xx名称空间
比如:
using namespace std;
cout<<"a";
如果没有告诉编译器所使用的空间名称 则要写成:
std::cout<<"a";
同样 可以自定义某一段代码属于哪个空间:
namespace xx
{
...
}
输入输出
在C++中 用iostream作为输入输出流的库
#include <iostream>
用cin和cout关键字进行输入和输出
如:
using namespace std;
int a=0;
cin>>a; //输入到a
cout<<a; //输出a
类比scanf和printf
同样 还有一个关键字endl表示换行
cout和cin的传参是不固定的
由编译器自行裁定
字符串类型
在C语言中 常用char *表示字符串
但是在C++中 可以直接用string类型
比如:
char * s="456";
string str="123";
由于cout的特性 这两种字符串都可以直接打印
但如果使用C语言中printf的打印方式时 采用%s方式打印字符串 则不能传入string类型
class类
C++的核心就是class
同Python等支持面向对象的语言一样
可以理解成一个支持函数、继承、自动初始化、销毁的结构体
在class类中 有private
私有、public
公有变量
前者只能内部访问 后者可以外部调用使用
如:
class A
{
public:
int a;
private:
int b;
}
a可以用A.a的方式方位 b则外部无法访问
构造函数和析构函数(解析函数)
构造函数可以理解成对类的初始化 反之析构函数则是退出时进行销毁前的函数
两者需要与类的名称相同 析构函数则在前面加一个~表示非
如:
class A
{
public:
int a;
A();
~A();
private:
int b;
}
A::A()
{
...
}
A::~A()
{
...
}
构造函数可以定义传参 析构函数则不行
类的继承
如果有两个类A和B 想让A里面包含B 则可以写作继承的写法
继承后 A类的变量可以直接调用B下面的成员
如:
class B
{
int b;
}
class A: public B
{
int a;
}
在定义A后 可以访问到B的成员b 当然 继承也可以私有