由基于qml,c++的串口调试工具浅谈qml与c++混合编程
最近在做一个基于sim900 的串口通信工具,基于qml和c++来实现。
首先,对于串口,qt有自带的QSerialPort,可以实现同步,和异步通信,qt creator也有自带的例子,本例子是从其中一个名为“terminal”的例子学习了qt如何实现异步通信(c++),然后通过qml来写界面,逻辑部分由c++实现。
通过qmlc++混合编程基于QSerialPort的异步通信(记得在pro中加上QT+=serialport),主要步骤包括下面几个:
1.使用setPort()或者setPortName()方指定想要访问的串口设备。
2.以只读或者只写或者读写模式调用open()方法打开串口。(注意:串口都是以互斥的方式访问,这也就是说我们不能打开一个已经打开的串口。)
3.成功打开之后,QSerialPort尝试着获取串口当前的配置并初始化它。你也可以使用setBaudRate(),setDataBits(),setParity(),setStopBits()和setFlowControl()方法重新配置它,
4.如果串口用读写模式打开,你就可以调用read()或者write()方法,可选的还有readline()和readAll()方法。可以使用close()方法来关闭串口和取消I/O操作。
下边叙述本程序: serial.h,serial.cpp为主函数,main.qml为主界面,Settings.qml为串口设置界面:
- /////////////////main.qml///////////////////////////
- import QtQuick 2.1
- import QtQuick.Controls 1.1
- import QtQuick.Layouts 1.1
- Rectangle{
- width: 800
- height: 600
- color: "lightblue"
- Settings{
- id:settingwindow
- visible: false
- }
- Column{
- anchors.fill: parent
- spacing: 50
- Row{
- spacing: 50
- Button{
- width: 60
- text: "Open"
- onClicked: {
- settingwindow.visible=true//使设置窗口可见,通过设置串口的apply按钮触发的serialtest.openAndSetPort函数打开和设置串口
- }
- }
- Button{
- width: 60
- text: "Close"
- onClicked: {
- serialtest.closePort()//关闭串口
- Qt.quit()
- }
- }
- }
- Grid{
- rows:2
- columns:4
- rowSpacing: 20
- columnSpacing: 40
- Label{
- height: 25
- text: "Send Data : "
- verticalAlignment :Text.AlignVCenter
- }
- TextField {
- id: textInput1
- width: 300
- height: 25
- placeholderText: qsTr("Send Data")
- font.pixelSize: 12
- }
- Label{
- height: 25
- text: "Number of Send Data: "+serialtest.sendnumber//显示发送数据计数
- verticalAlignment :Text.AlignVCenter
- }
- Button{
- id:sendData
- width: 60
- text: "Send"
- onClicked: {
- serialtest.sendto(textInput1.text);//触发发送数据函数
- }
- }
- Label{
- height: 25
- text: "Receive Data : "
- verticalAlignment :Text.AlignVCenter
- }
- Rectangle{
- height: 300
- width: 300
- color: "lightgreen"
- radius: 10
- Label{
- anchors.fill: parent
- id: textreceive
- font.pixelSize: 12
- text:serialtest.receivedata
- }
- }
- Label{
- height: 25
- text: "Number of receive Data: "+serialtest.receivenumber//显示接收数据计数
- verticalAlignment :Text.AlignVCenter
- }
- Button{
- width: 60
- text: "Clear"
- onClicked: {//清空接收数据显示,将数据计数清零
- serialtest.receivedata=""
- serialtest.sendnumber="0"
- serialtest.receivenumber="0"
- serialtest.clearnumber();
- }
- }
- }
- }
- }
- /////////////////////Settings.qml/////////////////////////////////////////
- import QtQuick 2.1
- import QtQuick.Controls 1.1
- import QtQuick.Window 2.0
- Window{
- id:setwindow
- width: 300
- height: 300
- Column{
- id: maincolumn
- anchors.fill: parent
- spacing: 10
- Rectangle{
- anchors.horizontalCenter: parent.horizontalCenter
- height: 1
- width: parent.width
- }
- Label{
- anchors.horizontalCenter: parent.horizontalCenter
- text: "Set Serial Port"
- font.pointSize:12
- font.bold: true
- }
- Grid{
- id:selectgrid
- anchors.horizontalCenter: parent.horizontalCenter
- rows:6
- columns: 2
- columnSpacing: 20
- rowSpacing: 10
- Label{
- id:selectlabel
- height: 20
- text: "PortName:"
- font.pointSize:9
- horizontalAlignment : Text.AlignHCenter
- verticalAlignment :Text.AlignVCenter
- }
- ComboBox {
- id :firstcombo
- width: maincolumn.width/2
- currentIndex: 2
- model: [ "COM1", "COM2", "COM3" ,"COM4" ,"COM5" ,"COM6" ]
- }
- Label{
- text: "BaudRate:"
- height: 20
- font.pointSize:selectlabel.font.pointSize
- horizontalAlignment : Text.AlignHCenter
- verticalAlignment :Text.AlignVCenter
- }
- ComboBox {
- id: baudRate
- width:firstcombo.width
- currentIndex: 0
- model: [ "9600", "19200", "38400","115200" ]
- }
- Label{
- text: "Data bits:"
- height: 20
- font.pointSize:selectlabel.font.pointSize
- horizontalAlignment : Text.AlignHCenter
- verticalAlignment :Text.AlignVCenter
- }
- ComboBox {
- id:dataBits
- width:firstcombo.width
- currentIndex: 3
- model: [ "5", "6", "7", "8" ]
- }
- Label{
- text: "Parity:"
- height: 20
- font.pointSize:selectlabel.font.pointSize
- horizontalAlignment : Text.AlignHCenter
- verticalAlignment :Text.AlignVCenter
- }
- ComboBox {
- id:parity
- width:firstcombo.width
- currentIndex: 0
- model: [ "None", "Even", "Odd", "Mark", "Space" ]
- }
- Label{
- height: 20
- text: "Stop bits:"
- font.pointSize:selectlabel.font.pointSize
- horizontalAlignment : Text.AlignHCenter
- verticalAlignment :Text.AlignVCenter
- }
- ComboBox {
- id:stopBits
- width:firstcombo.width
- currentIndex: 0
- model: [ "1", "1.5", "2" ]
- }
- Label{
- height: 20
- text: "Flow control:"
- font.pointSize:selectlabel.font.pointSize
- horizontalAlignment : Text.AlignHCenter
- verticalAlignment :Text.AlignVCenter
- }
- ComboBox {
- id:flowControl
- currentIndex: 0
- width:firstcombo.width
- model: [ "None", "RTS/CTS", "XON/XOFF" ]
- }
- }
- Button{
- width: 60
- text: "Apply"
- anchors.horizontalCenter: parent.horizontalCenter
- onClicked: {
- serialtest.openAndSetPort(firstcombo.currentIndex,baudRate.currentIndex,dataBits.currentIndex
- ,parity.currentIndex,stopBits.currentIndex,flowControl.currentIndex)
- //触发此函数,由combobox控件的currentIndex作为函数变量,(所有combobox的model值和顺序都和serialtest.openAndSetPort一致,这样就可以通过传递index来获取当前设置信息)
- setwindow.visible=false
- }
- }
- }
- }
- <span style="font-family: Arial, Helvetica, sans-serif;"></span><pre name="code" class="html"><span style="font-family: Arial, Helvetica, sans-serif;">////////////////////////////</span><span style="font-family: Arial, Helvetica, sans-serif;">serialset.h/////////////////////////////</span>
#include <QObject>
#include <QtSerialPort/QSerialPort>
class SerialTest : public QSerialPort
{
Q_OBJECT
Q_PROPERTY(QString receivedata READ receivedata WRITE setreceivedata NOTIFY receivedataChanged)//从串口收到的数据
Q_PROPERTY(QString sendnumber READ sendnumber WRITE setsendnumber NOTIFY sendnumberChanged)//发送的数据字节统计
Q_PROPERTY(QString receivenumber READ receivenumber WRITE setreceivenumber NOTIFY receivenumberChanged)//接收的数据字节统计
public:
struct Settings {//端口设定结构体
QString name;
qint32 baudRate;
QSerialPort::DataBits dataBits;
QSerialPort::Parity parity;
QSerialPort::StopBits stopBits;
QSerialPort::FlowControl flowControl;
};
SerialTest(QSerialPort *parent = 0);
QString receivedata(void);
void setreceivedata(QString receivedata);
QString sendnumber();
void setsendnumber(QString sendnumber);
QString receivenumber();
void setreceivenumber(QString receivenumber);
Q_INVOKABLE void openAndSetPort(int PortNameIndex,int BaudRateIndex,int DatabitsIndex,int ParityIndex,int StopbitsIndex,int FlowcontrolIndex);//打开并设定端口;
Q_INVOKABLE void closePort();//关闭端口;
Q_INVOKABLE void sendto(QString sendmessage);//发送数据;
Q_INVOKABLE void clearnumber();//数据统计清零;
signals:
void receivedataChanged();
void receivenumberChanged();
void sendnumberChanged();
public slots:
void receivefrom();//信号(收到数据激发的信号)响应函数
private:
QString m_receivedata;
QString m_sendnumber,m_receivenumber;
};
#endif // SERIALTEST_H
- ///////////////////////////serialset.cpp////////////////////////////
- <span style="font-family: Arial, Helvetica, sans-serif; color: rgb(0, 0, 128);">#include</span><span style="font-family: Arial, Helvetica, sans-serif; color: rgb(0, 128, 0);"><iostream></span>
SerialTest::Settings currentsetting;//定义设定值结构体的结构体变量
QSerialPort serialtest;
qint64 c_sendnumber,c_receivenumber;
SerialTest::SerialTest(QSerialPort *parent):QSerialPort (parent),m_receivedata("Receive Label"),m_receivenumber("0"),m_sendnumber("0")
{
QObject::connect(&serialtest, SIGNAL(readyRead()),this, SLOT(receivefrom()));//将端口收到数据产生的信号绑定receivefrom()函数;
}
//打开端口并设置:函数的参数(……Index由qml中combobox的currentIndex决定),由按钮触发
void SerialTest::openAndSetPort(int PortNameIndex,
int BaudRateIndex,
int DatabitsIndex,
int ParityIndex,
int StopbitsIndex,
int FlowcontrolIndex)
{
////////////////////1.得到当前选择的各项设置//////////////////////////////
//得到当前端口名
QString allname[6]={"COM1","COM2","COM3","COM4","COM5","COM6"};//列举所有的端口名
currentsetting.name=allname[PortNameIndex];//由qml里表示name的combobox的currentIndex来确定当前的name
std::cout<<" ok setPortName to "+ currentsetting.name.toStdString()<< std::endl;//通过输出来验证设定成功
//得到当前波特率
qint32 allbauRate[4]={9600,19200,38400,115200};
currentsetting.baudRate=allbauRate[BaudRateIndex];
//得到当前发送位数
QSerialPort::DataBits allDatabits[4]={QSerialPort::Data5,
QSerialPort::Data6,
QSerialPort::Data7,
QSerialPort::Data8};
currentsetting.dataBits=allDatabits[DatabitsIndex];
//得到当前Parity
QSerialPort::Parity allparity[5]={QSerialPort::NoParity,
QSerialPort::EvenParity,
QSerialPort::OddParity,
QSerialPort::MarkParity,
QSerialPort::SpaceParity};
currentsetting.parity=allparity[ParityIndex];
//得到当前停止位
QSerialPort::StopBits allstopBits[3]={QSerialPort::OneStop,
QSerialPort::OneAndHalfStop,
QSerialPort::TwoStop};
currentsetting.stopBits=allstopBits[StopbitsIndex];
//得到当前FlowControl
QSerialPort::FlowControl allflowControl[3]={QSerialPort::NoFlowControl,
QSerialPort::HardwareControl,
QSerialPort::SoftwareControl};
currentsetting.flowControl=allflowControl[FlowcontrolIndex];
////////////////////2.设定当前端口名//////////////////////////////
serialtest.setPortName(currentsetting.name);
////////////////////3.打开这一端口并按照当前设置信息进行设置//////////////////////////////
if (serialtest.open(QIODevice::ReadWrite))//打开这一端口
{
std::cout<<"open port sucess"<<std::endl;
if(serialtest.setBaudRate(currentsetting.baudRate)//设置各项信息
&& serialtest.setDataBits(currentsetting.dataBits)
&& serialtest.setParity(currentsetting.parity)
&& serialtest.setStopBits(currentsetting.stopBits)
&& serialtest.setFlowControl(currentsetting.flowControl))
{
std::cout<<"set sucess"<<std::endl;
}
}
}
////////////////////4.发送数据//////////////////////////////
void SerialTest::sendto(QString sendmessage)//此函数由qml里的send按钮触发,sendmessage来源于qml文本框的当前文本,
{
QByteArray data = sendmessage.toLocal8Bit()+'\r';//将QString转为QByteArray,并加上'\r'(回车符),因为芯片要求在回车符之后再返回数据
qint64 testwritenumber=serialtest.write(data);//写入数据
m_receivedata=m_receivedata+"\n";//加上换行符便于显示
c_sendnumber=c_sendnumber+testwritenumber-1;//发送数据字节数统计(减去回车符)
setsendnumber(QString ::number(c_sendnumber));//更新发送的数据字节总数
}
void SerialTest::setsendnumber(QString sendnumber)//更新发送的数据字节总数,触发sendnumberChanged()的消息响应函数sendnumber()来更新显示
{
m_sendnumber=sendnumber;
emit sendnumberChanged();
}
QString SerialTest::sendnumber()//响应sendnumberChanged()消息
{
return m_sendnumber;
}
////////////////////4.接收数据//////////////////////////////
void SerialTest::receivefrom()//由readyRead()消息出发(在前边进行绑定),当串口收到数据此消息被激活(对于串口,每发送出去一个字节,都会将此字节返回,触发readyread消息,当芯片有特殊指令时,收到的信息更多,比如对sim900,发送0000,芯片就会受到0000,但是发送AT,会受到 AT OK)
{
QByteArray data = serialtest.readAll();//读取所有收到的数据
QString receivedata=data.data();//将QByteArray转为QString来显示
m_receivedata= m_receivedata+receivedata;//将某次收到的数据进行累加,因为如果不累加的话每次有readyread就会触发此函数,会重置m_receivedata,覆盖之前收到的数据
emit receivedataChanged();//发送消息触发receivedata(),更新当前收到的数据显示receivedata
qint64 testreadnumber=data.length();//接收数据字节数统计
c_receivenumber=c_receivenumber+testreadnumber;
setreceivenumber(QString ::number(c_receivenumber));//更新接收的数据字节总数
}
void SerialTest::setreceivenumber(QString receivenumber)//更新接收的数据字节总数
{
m_receivenumber=receivenumber;
emit receivenumberChanged();;
}
QString SerialTest::receivenumber()//响应receivenumberChanged()消息
{
return m_receivenumber;
}
QString SerialTest::receivedata()//qml读取receivedata值的时候就会触发此函数,或者emit receivedataChanged()更新当前收到的数据显示时触发
{
return m_receivedata;
}
void SerialTest::setreceivedata(QString receivedata)//其任务已被receive from函数完成,但是在清空数据时用到这个函数
{
m_receivedata=receivedata;
emit receivedataChanged();
}
////////////////////5.关闭端口//////////////////////////////
void SerialTest::closePort()//由按钮出发
{
serialtest.close();
std::cout<<"close port sucess"<<std::endl;
}
////////////////////6.清空计数//////////////////////////////
void SerialTest::clearnumber()//由按钮出发
{
c_sendnumber=0;
c_receivenumber=0;
}
首先,打开并设置串口: 由main.qml里的名为“Open”的按钮打开Settings.qml设置界面(即使settings窗口其可见),然后转入settings.qml,设置各个combobox之后,通过点击Apply按钮触发SerialTest::openAndSetPort函数(通过Q_INVOKABLE在serialtest.h中定义使得能够在qml里边访问),函数变量即为当前qml里各个combobox的currentIndex,由有各个combobox的model值和顺序与SerialTest::openAndSetPort函数中每个参数的可选值相同,所以可以由qml中各个combobox的currentIndex得到SerialTest::openAndSetPort函数中每个端口参数的值,然后由得到的设定值name,打开端口,设置其他端口参数。SerialTest::openAndSetPort函数执行完以后,设置Settings.qml对应的设置窗口不可见,回到主窗口。
第二,发送数据:在主窗口以senddata为名的textfield控件中输入要发送的内容,点击send按钮,触发SerialTest::sendto(QString sendmessage)函数(通过Q_INVOKABLE定义在serialtest.h中定义),其中变量来源于用户输入textfield的text内容,需要将其转为qbytearray来发送,注意:转换后加了'\r',这是因为芯片要求在回车符('\r')之后再返回数据,比如对sim900芯片,发送0000,芯片就会收到0000,但是发送AT'\r',会受到 AT'\r'OK。所以加上'\r',然后进行写操作,发送qbytearray数据到串口,并对发送的自己数进行计数,计数由定义的 Q_PROPERTY(QString sendnumber READ sendnumber WRITE setsendnumber NOTIFY sendnumberChanged)来完成, 执行setsendnumber(QString::number(c_sendnumber)),将当前计数的值进行设定,此函数更新m_sendnumber的值为当前计数,并emit sendnumberChanged()发送消息,使qml中text:"NumberofSendData:"+serialtest.sendnumber(56行)更新serialtest.sendnumber值,这时就回来通过读取QStringSerialTest::sendnumber()函数,而返回值m_sendnumber就是当前计数值,这时c++传值到qml的方法,如果要在qml向c++传值,只需在qml里执行SerialTest::setsendnumber(QStringsendnumber)函数,但是前提是在头文件里将此函数设置为Q_INVOKABLE函数或者在public slots:内定义函数,另一种qml向c++传值方法就是在定义一个函数,同样需要设置为Q_INVOKABLE函数或者在publicslots:内定义,然后在qml里使用,将qml的值由此函数送到c++即可,有时候还需要在qml里使用function先做一些处理。
第三,接收数据:对于串口来说,每发送一个字节的数据,就会返回收到这个数据,这时候使用QSerialPort就会产生一个信号:readyRead()。将此信号与函数receivefrom()进行绑定: QObject::connect(&serialtest, SIGNAL(readyRead()),this, SLOT(receivefrom())),就可以在发送完数据后得到readyRead()信号时触发SerialTest::receivefrom()函数,读取数据,转换为QString来显示,这里有一个问题需要注意,就是readyRead()信号可能多次产生,可能收到的数据还没有显示,新的数据又来了,将其覆盖,所以有个方法就是每次send之后收到的所有消息进行字符串累加,这样就可以避免这个问题(搞了好久才搞定的)。此函数后续的emit就不说了,和前面的类似,只不过前边的是显示计数,这里显示接受的数据,而且这里把set函数及Q_PROPERTY的WRITE函数的功能放到receivefrom函数来实现了(主要就是更新m_receivedata值和emit receivedataChanged()消息两个功能)。下边receivenumber和签署sendnumber一样。 最后关闭窗口和清空计数,分别有两个按钮来响应,需要将两个函数设置成Q_INVOKABLE使得qml能够调用。
最后,本文只是个人的程序说明,具体的qml c++混合编程可以参考foruok大神的博客文章(大神要出书了:《qt quick核心编程》大家快去支持)http://blog.csdn.net/foruok/article/details/32698603
最后附上本程序的github源码地址
https://github.com/zing235/TestSerial.git
http://blog.csdn.net/u010423298/article/details/41791799
由基于qml,c++的串口调试工具浅谈qml与c++混合编程的更多相关文章
- 浅谈自底向上的Shell脚本编程及效率优化
作者:沐星晨 出处:http://blog.csdn.net/sosodream/article/details/6276758 浅谈自底向上的Shell脚本编程及效率优化 小论文,大家多批评指导:) ...
- 浅谈Java 8的函数式编程
函数式编程语言是什么? 函数式编程语言的核心是它以处理数据的方式处理代码.这意味着函数应该是第一等级(First-class)的值,并且能够被赋值给变量,传递给函数等等.(转载自http://xz.p ...
- 浅谈Spring AOP 面向切面编程 最通俗易懂的画图理解AOP、AOP通知执行顺序~
简介 我们都知道,Spring 框架作为后端主流框架之一,最有特点的三部分就是IOC控制反转.依赖注入.以及AOP切面.当然AOP作为一个Spring 的重要组成模块,当然IOC是不依赖于Spring ...
- spark浅谈(2):SPARK核心编程
一.SPARK-CORE 1.spark核心模块是整个项目的基础.提供了分布式的任务分发,调度以及基本的IO功能,Spark使用基础的数据结构,叫做RDD(弹性分布式数据集),是一个逻辑的数据分区的集 ...
- 【STL 源码剖析】浅谈 STL 迭代器与 traits 编程技法
大家好,我是小贺. 点赞再看,养成习惯 文章每周持续更新,可以微信搜索「herongwei」第一时间阅读和催更,本文 GitHub : https://github.com/rongweihe/Mor ...
- 浅谈CAS(Compare and Swap) 原理
浅谈CAS原理java并发编程也研究了一段时间了,对CAS的原理总是不太理解,今天再研究了一下,记录一些自己的理解. 说到CAS,再java中的某些情况下,甚至jdk1.5以后的大多数情况,并发 ...
- 基于Modbus的C#串口调试开发
说明:本文主要研究的是使用C# WinForm开发的串口调试软件(其中包含Modbus协议相关操作).Modbus相关协议可以查阅百度文库等,可参考: <http://wenku.baidu.c ...
- 痞子衡嵌入式:串口调试工具Jays-PyCOM诞生记 - 索引
大家好,我是痞子衡,是正经搞技术的痞子.本系列痞子衡给大家介绍的是串口调试工具Jays-PyCOM诞生. 串口调试助手是嵌入式开发里非常常用的小工具,市面上有非常多流行的串口调试工具,比如TeraTe ...
- 痞子衡嵌入式:串口调试工具Jays-PyCOM诞生记(1)- 环境搭建(Python2.7.14 + pySerial3.4 + wxPython4.0.3)
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是串口调试工具Jays-PyCOM诞生之环境搭建. 在写Jays-PyCOM时需要先搭好开发和调试环境,下表列出了开发过程中会用到的所有软 ...
随机推荐
- 转: angularjs学习总结(~~很详细的教程)
1 前言 前端技术的发展是如此之快,各种优秀技术.优秀框架的出现简直让人目不暇接,紧跟时代潮流,学习掌握新知识自然是不敢怠慢. AngularJS是google在维护,其在国外已经十分火热,可是国内的 ...
- APUE学习之---------------进程
离职了,交接期也有足够的时间了,可以在好好的再看一下APUE,想想上次详细的看还是在两年之前,虽然中间也偶尔会翻出来看看,但是由于工作上交集相对比较少一直没有去细读一下.现在正好是一段空挡期可以好好看 ...
- JAVA中List、Map、Set的区别与选用
类层次关系如下: Collection ├List│├LinkedList│├ArrayList│└Vector│ └Stack└SetMap├Hashtable├HashMap └WeakHashM ...
- Xvfb+YSlow+ShowSlow搭建前端性能测试框架 - 前端技术 | TaoBaoUED
Xvfb+YSlow+ShowSlow搭建前端性能测试框架 - 前端技术 | TaoBaoUED Xvfb+YSlow+ShowSlow搭建前端性能测试框架 作者:黑三 | 时间:2010-07-07 ...
- 犯罪团伙利用POS机刷信用卡积分转卖 年获利千万
今年1月20日,广东省公安厅展示去年缴获的盗刷专用POS机. 今年1月20日,广东省公安厅展示了一批缴获的盗刷信用卡工具. 他们是一群靠信用卡谋生的年轻人,平均年龄不超过30岁. 他们将各银行信用 ...
- ZYB's Premutation(有逆序数输出原序列,线段树)
ZYB's Premutation Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Othe ...
- 记载abp中Dbcontext的疑问
q:abp中httpcontext如何在一次请求中保证获取的是相同的实例. 大牛的原话: LifestylePerWebRequest does not works good with async. ...
- Java多线程之synchronized(二)
为了解决“非线程安全”带来的问题,上一节中使用的办法是用关键字synchronized修饰多个线程可能同时访问到的方法,但是这样写是存在一定的弊端的,比如线程A调用一个用synchronized修饰的 ...
- [Swust OJ 801]--Ordered Fractions
题目链接:http://acm.swust.edu.cn/problem/801/ Time limit(ms): 1000 Memory limit(kb): 10000 Description ...
- [C#参考]事件和委托的关系
前面说了委托,接下来就要说一说事件了,同时最后再说一下委托和事件的区别. 事件和委托很相似,事件就好像是被简化的针对特殊用途的委托.看下面的图: 从这张图中能看到,事件是发布者的一个成员,它不是类型. ...