当前位置: 首页 > news >正文

QT学习从零开始,开发一个串口调试助手

1、QT环境搭建

  • 有魔法直接官网下载,没有用迅雷直接下载,参考文章QT下载

  • 官网注册QT账号

  • 安装时一路下一步,仅在勾选组件时勾选上MinGWAndroidQt Vitual KeyboardQt Script,Tools里的 MinGW

  • 测试,新建一个C++项目,编译

2、C++基础

(1).什么是C++:C的升级版,是一种高级语言

(2).什么是面向对象,什么是面向过程

c语言就是面向过程的,C++就是面向对象的。
举例: a+b:
直接计算a+b就是面向过程。
面向对象就是给a+b穿上了一层衣服。不是直接计算a+b。封装了再计算a+b,更加方便使用

(3).C++的灵魂,c++的类

C语言结构体的升级版,类的成员变量不仅可以是变量,还可以是函数

(4).如何定义一个类

class student
{
public:char name[64];char age;
};

(5).什么是对象

对象是类的实例化。通过类定义的变量就是对象

(6).如何定义一个对象。

直接定义:

student my;

在堆里面定义:

student *my = new student;

删除堆定义的对象

delete my; //目的是释放堆里面的内存

(7).怎么访问类的成员。

访问方法和C语言一样,普通变量通过.访问,指针通过->访问

int main()
{student my;student *my1=new student;my.age=18;my1->age=19;cout << my.age<< endl;cout << my1->age<< endl;cout << "Hello World!" << endl;return 0;
}

(8).类的函数成员。

因为类里面的成员不仅可以是变量,也可以是函数。
第一步:在类里面声明
第二步:实现这个函数。我们可以直接在类里面写,也可以写在类的外面直接写在类的里面

//实现在类里面
class student
{
public:char name[64];int age;void test(){cout<<123<<endl;}
};
//实现在类外面
class student
{
public:char name[64];int age;void test();
};
//student::表示test属于类里面的函数,不加则是普通函数
void student::test(){cout<<123<<endl;
}

访问函数和访问变量是一样的。

(9).类的访问修饰符

类的访问修饰符就是对类的成员进行权限管理。
public 表示函数和变量是公开的,任何人都可以访问。
private 表示函数和变量只能在自己的类里面自己访问自己,不能通过对象来访问。能不能强行访问?可以的,通过类的函数成员间接访问
protected 表示函数和变量只能在自己的类里面自己访问自己,但是可以被派生类来访问的。

(10).类函数的重载特性

类函数的重载特性就是说我们可以在类里面定义同名的函数,但是参数不同的函数。

class student
{
public:char name[64];int age;void test();void test(char a);int test(int a);
private:int qq=123;
};

重载函数在调用的时候,会根据参数的类型,然后去匹配相应的函数进行调用。

(11).构造函数和析构函数

析构函数:假如定义了析构函数,当对象被删除或者生命周期结束的时候,就会触发析构函数。
构造函数:假如定义了构造函数,对象创建时就会触发这个构造函数。

怎么定义析构函数和构造函数?
  • 析构函数和构造函数的名字必须和类名一模一样。
  • 析构函数要在前面加上一个~
class student
{
public:~student(); //析构函数student(); //构造函数
};
student::student(){cout<<"hello"<<endl;
}
student::~student(){cout<<"bye"<<endl;
}

构造函数是可以被重载的。
析构函数不能被重载。

class student
{
public:~student(); //析构函数student(); //构造函数student(int a); //构造函数
};student::student(){cout<<"hello"<<endl;
}
student::student(int a){cout<<"hellohello"<<endl;
}
student::~student(){cout<<"bye"<<endl;
}
int main()
{student my;student *my1=new student;student *my2=new student(2);return 0;
}

结果

hello
hello
hellohello
bye

(12).类的继承

类的继承允许我们在新的类里面继承父类的public还有protected部分,private是不能被继承的。
当我们觉得这个类不好的时候,可以使用类的继承,添加我们需要的功能。
格式:

class 儿子:public 爸爸{
public:.......
protected:
}

例子

class student
{
public:char name[64];int age;void test();void test(char a);
private:int qq=123;
};
class mystudent: public student{
public:int grade;
}
如何在子类里面去访问父类的成员?

也是通过.->来访问的。

(13).虚函数和纯虚函数

虚函数:有实际定义的,允许派生类对他进行覆盖式的替换,virtual来修饰。
纯虚函数:没有实际定义的虚函数就是纯虚函数。(函数里面没有代码)
举例:

class student
{
public:char name[64];int age;virtual void test(); //虚函数virtual void testa(){} //纯虚函数
private:int qq=123;
};
void student::test(){cout<<123<<endl;
}

怎么定义一个虚函数?
virtual来修饰,虚函数是用在类的继承上的。

class student
{
public:char name[64];int age;virtual void test(); //虚函数virtual void testa(){} //纯虚函数
private:int qq=123;
};
void student::test(){cout<<123<<endl;
}
class mystudent: public student{
public:int grade;void test(){cout<<"test"<<endl;}
};
虚函数的优点?

可以预留接口,实现分工合作。

3、QT基础

qt的移植性非常的强,一套代码不用改太多,直接通用所有的平台,qt4和qt5选择看电脑性能。

(1).创建一个基本Qt页面 :QQ登录页面

创建Qt工程
 Qt项目工程文件.pro

点击forms,然后双击ui文件,就可以进入ui编辑器
ui编辑器面板介绍:

设计一个QQ登录界面用到的组件:

放图片,放文本,放gif图的组件就是qlabel
放账号和密码的对话框我们用的组件是qlinedit(不管多长的输入都在一行)
按钮我们使用的组件是qpushbutton,将密码组件的echoMode回显模式设置为密码模式,输入密码不显示.
编译

(2).Qt信号和槽

1.给控件改名字

为了分析代码方便,给控件改名字。要通俗易懂。

2.什么是信号和槽

信号:指控件发出的特定的信号。

比如按钮的信号:

:槽就是槽函数的意思,我们可以把槽函数绑定在某一个控件的某一个信号上。

3.怎么关联信号和槽
  • 自动关联
    手动选择相应的控件,然后右键->转到槽。
    第一个部分: 自动关联会给我们的工程添加以下内容:

    槽函数只能声明到 private slots或者public slots下面。

  • 手动关联
    手动关联使用connect这个函数。
    仍然需要在widget.h中声明槽函数,widget.cpp实现槽函数

connect(ui->logoBt,SIGNAL(clicked()),this,SLOT(logoBt_clicked_slots()));
connect(A,SIGNAL(B),C,SLOT(D));
//当对象A发出B信号时候,就会触发对象C的槽函数D
//谁发出信号,发出什么信号;谁处理信号,怎么处理信号

QT官方命名槽函数的方法是 on_对象名_信号(),这样QT会自动将信号和槽连接起来,如果此时手动关联时使用这种命名,还使用了connect函数,信号就会触发两次槽函数

(3).QQ登录界面实战

1、Qt工程添加图片

添加图片资源文件、引用这个图片

2、Qt布局

水平布局、垂直布局、栅格布局

3、切换页面
  • 创建新的页面

  • 切换页面+账户密码匹配

Qt的三驾马车
1、Qt下的串口编程
2、Qt下的网络编程:TCP、UDP
3、Qt下操作GPIO

4、上位机开发之串口助手

1、UI界面
2、逻辑功能

(1)、UI界面

需要用到的Qt组件:

接收框 Plain Text Edit
发送框 Line Edit
属性框 Combo Box
按钮 Push Button
文本 Lable

  • 为接收框 、发送框、属性框 、按钮改名
  • 属性框显示默认值设置
串口助手
搜索端口号
QSerialPortInfo::availablePorts():这个函数返回一个QList<QSerialPortInfo>,其中包含了系统上所有可用的串口信息。
每个QSerialPortInfo对象包含了关于一个特定串口的信息,比如串口名称、制造商、串口描述等。foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts()):这是一个Qt特有的循环语法,用于遍历
availablePorts()返回的列表。对于列表中的每一个QSerialPortInfo对象,都会创建一个名为info的常量引用,然后在循环体内
对其进行操作。ui->serialCb->addItem(info.portName()):这行代码的作用是将当前遍历到的串口名称添加到GUI中的下拉列表(ComboBox)
控件中。
ui->serialCb是访问UI中名为serialCb的下拉列表控件的方式,addItem是一个成员函数,用于在下拉列表中添加一个新的条目
info.portName()则是获取当前遍历到的串口名称。

在这里插入图片描述

(2)、逻辑功能

初始化:打开和关闭串口

给打开的button添加槽函数,完成串口初始化

#include "widget.h"
#include "ui_widget.h"
#include <QSerialPortInfo> 
#include <QMessageBox> # 消息提示类
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);serialPort=new QSerialPort(this); # 创建一个serialPort串口对象//搜索端口//QSerialPortInfo::availablePorts() 自动搜索当前可用端口foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts()){ui->serialCb->addItem(info.portName()); //在serialCb中显示可用串口}}Widget::~Widget()
{delete ui;
}#
void Widget::on_openBt_clicked()
{QSerialPort::BaudRate baudrate;QSerialPort::DataBits databits;QSerialPort::StopBits stopbits;QSerialPort::Parity parity;//设置波特率、数据位长度、停止位、校验位、端口号//1、先获取设置的值if(ui->baundrateCb->currentText()=="4800"){baudrate=QSerialPort::Baud4800;}else if(ui->baundrateCb->currentText()=="9600"){baudrate=QSerialPort::Baud9600;}else if(ui->baundrateCb->currentText()=="115200"){baudrate=QSerialPort::Baud115200;}if(ui->dataCb->currentText()=="5"){databits=QSerialPort::Data5;}else if(ui->dataCb->currentText()=="6"){databits=QSerialPort::Data6;}else if(ui->dataCb->currentText()=="7"){databits=QSerialPort::Data7;}else if(ui->dataCb->currentText()=="8"){databits=QSerialPort::Data8;}if(ui->stopCb->currentText()=="1"){stopbits=QSerialPort::OneStop;}else if(ui->stopCb->currentText()=="1.5"){stopbits=QSerialPort::OneAndHalfStop;}else if(ui->stopCb->currentText()=="2"){stopbits=QSerialPort::TwoStop;}if(ui->stopCb->currentText()=="none"){parity=QSerialPort::NoParity;}//2、初始化串口相关属性值serialPort->setPortName(ui->serialCb->currentText());serialPort->setBaudRate(baudrate);serialPort->setParity(parity);serialPort->setDataBits(databits);serialPort->setStopBits(stopbits);//3、打开串口if(serialPort->open(QIODevice::ReadWrite)==true){QMessageBox::information(this,"提示","成功");}else{QMessageBox::critical(this,"提示","失败");}
}
#
void Widget::on_closeBt_clicked()
{serialPort->close();
}
接收和发送数据

主要注意收到数据时的信号是什么,然后为他编写槽函数即可

查看帮助手册QSerialPort类的信号,有以下信号,但和接收数据的信号无关。

Signals
void baudRateChanged(qint32 baudRate, QSerialPort::Directions directions)
void breakEnabledChanged(bool set)
void dataBitsChanged(QSerialPort::DataBits dataBits)
void dataTerminalReadyChanged(bool set)
void errorOccurred(QSerialPort::SerialPortError error)
void flowControlChanged(QSerialPort::FlowControl flow)
void parityChanged(QSerialPort::Parity parity)
void requestToSendChanged(bool set)
void stopBitsChanged(QSerialPort::StopBits stopBits)

QSerialPort继承自QIODevice,因此查看看父类信号

QSerialPort ClassHeader: #include <QSerialPort> 
qmake: QT += serialport
Since: Qt 5.1
Inherits: QIODevice. 

QIODevice有以下信号

QIODevice Class
Signalsvoid aboutToClose()
void bytesWritten(qint64 bytes)
void channelBytesWritten(int channel, qint64 bytes)
void channelReadyRead(int channel)
void readChannelFinished()
void readyRead()

[signal] void QIODevice::readyRead()
This signal is emitted once every time new data is available for reading from the device’s current read channel. It will only be emitted again once new data is available, such as when a new payload of network data has arrived on your network socket, or when a new block of data has been appended to your device.
每次从设备当前读取通道读取新数据时,都会发出一次该信号。只有在有新数据可用时,如网络套接字上有新的网络数据有效载荷或有新的数据块被添加到设备上时,才会再次发出该信号。
readyRead() is not emitted recursively; if you reenter the event loop or call waitForReadyRead() inside a slot connected to the readyRead() signal, the signal will not be reemitted (although waitForReadyRead() may still return true).
Note for developers implementing classes derived from QIODevice: you should always emit readyRead() when new data has arrived (do not emit it only because there’s data still to be read in your buffers). Do not emit readyRead() in other conditions.
See also bytesWritten().

#include "widget.h"
#include "ui_widget.h"
#include <QSerialPortInfo>
#include <QMessageBox>
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);serialPort=new QSerialPort(this);connect(serialPort,SIGNAL(readyRead()),SLOT(serialPortReadyRead_SLOT()));//接收数据//搜索端口//QSerialPortInfo::availablePorts() 自动搜索当前可用端口foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts()){ui->serialCb->addItem(info.portName()); //在serialCb中显示可用串口}}Widget::~Widget()
{delete ui;
}
//接收数据并显示到receiveEdit上
void Widget::serialPortReadyRead_SLOT()
{QString Buf;Buf=QString(serialPort->readAll());ui->receiveEdit->appendPlainText(Buf);
}
//发送数据
void Widget::on_sendBt_clicked()
{serialPort->write(ui->sendEdit->text().toLocal8Bit().data());//字符串转
}
//清空发送区内容
void Widget::on_clearBt_clicked()
{ui->sendEdit->clear();
}void Widget::on_openBt_clicked()
{QSerialPort::BaudRate baudrate;QSerialPort::DataBits databits;QSerialPort::StopBits stopbits;QSerialPort::Parity parity;//设置波特率、数据位长度、停止位、校验位、端口号if(ui->baundrateCb->currentText()=="4800"){baudrate=QSerialPort::Baud4800;}else if(ui->baundrateCb->currentText()=="9600"){baudrate=QSerialPort::Baud9600;}else if(ui->baundrateCb->currentText()=="115200"){baudrate=QSerialPort::Baud115200;}if(ui->dataCb->currentText()=="5"){databits=QSerialPort::Data5;}else if(ui->dataCb->currentText()=="6"){databits=QSerialPort::Data6;}else if(ui->dataCb->currentText()=="7"){databits=QSerialPort::Data7;}else if(ui->dataCb->currentText()=="8"){databits=QSerialPort::Data8;}if(ui->stopCb->currentText()=="1"){stopbits=QSerialPort::OneStop;}else if(ui->stopCb->currentText()=="1.5"){stopbits=QSerialPort::OneAndHalfStop;}else if(ui->stopCb->currentText()=="2"){stopbits=QSerialPort::TwoStop;}if(ui->stopCb->currentText()=="none"){parity=QSerialPort::NoParity;}serialPort->setPortName(ui->serialCb->currentText());serialPort->setBaudRate(baudrate);serialPort->setParity(parity);serialPort->setDataBits(databits);serialPort->setStopBits(stopbits);if(serialPort->open(QIODevice::ReadWrite)==true){QMessageBox::information(this,"提示","成功");}else{QMessageBox::critical(this,"提示","失败");}
}void Widget::on_closeBt_clicked()
{serialPort->close();
}

5、Qt程序打包成Windows软件

问题一:什么是打包和部署?

因为我们要把写好的程序发给用户来用,写好的源码也不是随便给别人的。

问题二:怎么打包和部署?

1.我们把工厂切换到release模式,然后编译。
release模式:基本没有调试信息。
debug模式:有很多调试信息。
2.找到release模式构建的文件夹。
3.改exe文件的图标。
先把图标加到原始工程所在文件夹,然后在pro文件里面添加RC_ICONS=serial_iocn.ico
注意:图标的格式必须为.ico这个格式的,其他格式不行。
4.封包操作,需要用到QT的控制台,如下图:
Qt控制台

接着在电脑桌面建立一个文件夹serial,拷贝release文件里的exe文件到新建的文件夹serial,QT控制台切换到serial文件夹
windeployqt工具把库加到我们新建立的文件夹里: windeploypt exe文件名称

打包 ## 6、网络编程:TCP、UDP

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 数学建模学习笔记
  • f({1, 3})与f(C c)和`f(const C c)
  • 海康gige工业相机无驱动取像突破(c#实现,版本更新,你也可以移植到linux下去用)
  • 从零开始的CPP(36)——操作Excel
  • JS【详解】对象的内部属性 vs 内部方法
  • 【Android Studio】修改项目名称can‘t rename root module解决办法
  • 【Python随笔】比PyQt5更先进的pyside6安装和使用方法
  • 【使用Python和ADB过滤与处理Android包名】
  • SAAMDSSA-系统架构师(五十一)
  • Go开发后端和Vue3开发前端的前后端分离框架中自己手戳一个OA流程审批、工作流引擎给新时代一个漂亮便捷的工作流引擎
  • 验证码案例
  • UE中的运行时Mesh - 学习笔记
  • day16
  • Nginx系列-Nginx Location匹配规则
  • 【鸿蒙学习】Stage模型与FA模型的对比与应用选择
  • 【从零开始安装kubernetes-1.7.3】2.flannel、docker以及Harbor的配置以及作用
  • 【刷算法】求1+2+3+...+n
  • 2017前端实习生面试总结
  • Android 架构优化~MVP 架构改造
  • Git学习与使用心得(1)—— 初始化
  • in typeof instanceof ===这些运算符有什么作用
  • IndexedDB
  • JavaScript异步流程控制的前世今生
  • java概述
  • Js基础——数据类型之Null和Undefined
  • Js实现点击查看全文(类似今日头条、知乎日报效果)
  • log4j2输出到kafka
  • nodejs实现webservice问题总结
  • RedisSerializer之JdkSerializationRedisSerializer分析
  • 京东美团研发面经
  • 前端攻城师
  • 职业生涯 一个六年开发经验的女程序员的心声。
  • 自动记录MySQL慢查询快照脚本
  • 1.Ext JS 建立web开发工程
  • LIGO、Virgo第三轮探测告捷,同时探测到一对黑洞合并产生的引力波事件 ...
  • ​​​​​​​sokit v1.3抓手机应用socket数据包: Socket是传输控制层协议,WebSocket是应用层协议。
  • ###51单片机学习(1)-----单片机烧录软件的使用,以及如何建立一个工程项目
  • #《AI中文版》V3 第 1 章 概述
  • (1/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序
  • (附源码)计算机毕业设计SSM基于健身房管理系统
  • (七)MySQL是如何将LRU链表的使用性能优化到极致的?
  • (三) diretfbrc详解
  • (十七)devops持续集成开发——使用jenkins流水线pipeline方式发布一个微服务项目
  • (一)Docker基本介绍
  • (原創) 如何刪除Windows Live Writer留在本機的文章? (Web) (Windows Live Writer)
  • (转)setTimeout 和 setInterval 的区别
  • (转)甲方乙方——赵民谈找工作
  • (转)详解PHP处理密码的几种方式
  • .Net Core 微服务之Consul(二)-集群搭建
  • .Net Core 中间件与过滤器
  • .net core使用EPPlus设置Excel的页眉和页脚
  • .net 按比例显示图片的缩略图
  • .NET/C# 判断某个类是否是泛型类型或泛型接口的子类型
  • .NET基础篇——反射的奥妙
  • .pub是什么文件_Rust 模块和文件 - 「译」