野火电子论坛

标题: QT5串口上位机——从零开始教你写03 [打印本页]

作者: 沉沦之枫叶    时间: 2021-10-29 22:50
标题: QT5串口上位机——从零开始教你写03
[md]@[TOC](QT5串口上位机——从零开始教你写03 "TOC")

# 简单串口编写

## 3.线程类对象的添加和UI界面的参数设置

### 添加任务类对象和线程类对象

打开工程,选择**mainwidget.h**,添加我们创建的类**Serial**和线程类**QThread**。

内容如下:

```cpp
#ifndef MAINWIDGET_H
#define MAINWIDGET_H

#include <QWidget>
#include "serial.h"  //添加自定义类serial头文件
#include <QThread>   //添加线程类QThread头文件

QT_BEGIN_NAMESPACE
namespace Ui { class MainWidget; }
QT_END_NAMESPACE

class MainWidget : public QWidget
{
    Q_OBJECT

public:
    MainWidget(QWidget *parent = nullptr);
    ~MainWidget();
    QThread *t1;    //1.创建子线程对象
    Serial *m_work; //2.创建任务类对象
  
private:
    Ui::MainWidget *ui;

};
#endif // MAINWIDGET_H

```

在**mainwidget.cpp**中将创建的对象实例化,并将任务函数移入子线程。

内容如下:

```cpp
#include "mainwidget.h"
#include "ui_mainwidget.h"

MainWidget::MainWidget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::MainWidget)
{
    ui->setupUi(this);
  
    //1.创建子线程对象,动态分配空间,指定父对象
    t1 = new QThread(this);
    //2.创建任务类对象,动态分配空间,不能指定父对象
    m_work = new Serial;//防止内存泄漏,关闭窗口时 delete m_work;
    //3.将任务对象移动到某个子线程中
    m_work->moveToThread(t1);
  
}

MainWidget::~MainWidget()
{
    delete ui;
    delete m_work;  //关闭时释放内存空间
}
```

> 注:这里线程操作参考之前的文章[Qt多线程的使用记录](https://blog.csdn.net/qq_39538318/article/details/120398160 "Qt多线程的使用记录")中的方式二。

### 初始化ui界面参数

在开始工作前需要初始化ui界面的参数,如设置QCombobox的下拉框内容。因此需要创建一个**void initUI(void)**;函数。打开**mainwidget.h**,添加我们需要创建的函数,并使用快捷键<kbd>alt</kbd>+<kbd>enter</kbd>,在**mainwidget.cpp**中定义**void initUI(void)**;函数。

设置内容如下:

```cpp
public:
    MainWidget(QWidget *parent = nullptr);
    ~MainWidget();
    QThread *t1;    //1.创建子线程对象
    Serial *m_work; //2.创建任务类对象
    void initUI();  //初始化UI界面参数,如设置QCombobox的item和设置QSS样式。
```

其中 **void initUI();** 函数内容如下。

```cpp
void MainWidget::initUI()
{
     QStringList baudrateList; //设置波特率列表
     baudrateList<<"1200"<<"2400"<<"4800"<<"9600"<<"19200"<<"38400"
                <<"57600"<<"115200";
     ui->comBaudRate->addItems(baudrateList); //将设置的Item加入到列表中
     ui->comBaudRate->setEditable(true); //设置可以手动输入波特率
     //限定波特率手动输入时只能输入数字且范围为(0, 1000000)即最高1M
     isBaudRateRange= new QIntValidator; //防止内存泄漏,关闭窗口时 delete aIntValidator;
     isBaudRateRange->setRange(0, 1000000);
     ui->comBaudRate->setValidator(isBaudRateRange);
     ui->comBaudRate->setCurrentIndex(3);  //设置默认显示第4个即9600(从0开始)

     QStringList databitsList; //设置数据位列表,根据串口中配置顺序设置。
     databitsList<<"5"<<"6"<<"7"<<"8";
     ui->comDataBits->addItems(databitsList); //将设置的Item加入到列表中
     ui->comDataBits->setCurrentIndex(3);  //设置默认显示第4个即8bit(从0开始)

     QStringList parityList; //设置校验位列表,根据串口中配置顺序设置。
     parityList<<"Even"<<"Mark"<<"None"<<"Odd"<<"Space";
     ui->comParity->addItems(parityList); //将设置的Item加入到列表中
     ui->comParity->setCurrentIndex(2);  //设置默认显示第3个即None(从0开始)

     QStringList stopbitsList; //设置停止位列表,根据串口中配置顺序设置。
     stopbitsList<<"1"<<"1.5"<<"2";
     ui->comStopBits->addItems(stopbitsList); //将设置的Item加入到列表中
     ui->comStopBits->setCurrentIndex(0);  //设置默认显示第1个即1bit(从0开始)

     QStringList encodeList; //设置编码格式列表,根据编码格式中配置顺序设置。
     encodeList<<"ASCII"<<"UTF8"<<"UTF16"<<"GBK"<<"Big5"<<"ShiftJIS";
     ui->comEncode->addItems(encodeList); //将设置的Item加入到列表中
     ui->comEncode->setCurrentIndex(0);  //设置默认显示第1个即ASCII(从0开始)

}
```

> 注:
>
> 1. 其中需要在**mainwidget.h**中添加**#include < QStringList >** ;
> 2. 配置的**item**参数需要同之前**switch**设置的参数顺序相同。
> 3. 要在**mainwidget.h**中添加**#include < QIntValidator >**;并创建一个私有成员,**QIntValidator* isBaudRateRange;**

设置完成后在**MainWidget**中运行 **initUI();** 函数

```cpp
MainWidget::MainWidget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::MainWidget)
{
    ui->setupUi(this);

    //0.初始化ui界面;
    initUI();
    //1.创建子线程对象,动态分配空间,指定父对象
    t1 = new QThread(this);
    //2.创建任务类对象,动态分配空间,不能指定父对象
    m_work = new Serial;//防止内存泄漏,关闭窗口时 delete m_work;
    //3.将任务对象移动到某个子线程中
    m_work->moveToThread(t1);

}
```

至此,**QCombobox** 参数初始化完成。(后期仍会在 **initUI()** 中进行其他配置如设置QSS样式等)。

### 利用QSerialPortInfo得到串口号

这里我们会发现并没有设置串口的**Item**参数。主要原因是,电脑再识别设备的串口号时,会任意分配可用的端口号,因此这里我们通过使用**QSerialPortInfo**类来得到可用端口号,并设置到串口的**Item**中。

首先,打开**mainwidget.h**,添加 **#include < QSerialPortInfo>** , 然后创建 **void getportInfo();** 函数,并使用快捷键<kbd>alt</kbd>+<kbd>enter</kbd>,在**mainwidget.cpp** 中定义 **void getportInfo();** 函数。

**实现步骤:**

> 首先检测可用的设备端口号,当存在可用的端口号时,通过遍历的方式,将可用的端口号保存在**portStringList**中。然后设置到串口的**Item**中。无可用端口号时,弹出信号框。

具体内容如下:

```cpp
void MainWidget::getportInfo()
{
    qint32 comCnt=0; //保存当前可用的串口数量
    QStringList portStringList; //保存当前可用的串口列表

    if(portStringList.length()!=0)
    {
      portStringList.clear();//检测portStringList内容当不为空时清除再次调用时使用
    }
    if(ui->comPortName->count()!=0)
    {
        ui->comPortName->clear();//检测comPortName的列表数量当不为空时清除再次调用时使用
    }
    //获取串口设备数量
    comCnt = QSerialPortInfo::availablePorts().length();
    if(comCnt!=0)
    {
       //获取串口信息
       foreach (const QSerialPortInfo &qspinfo, QSerialPortInfo::availablePorts())
       {
            portStringList+=qspinfo.portName();
       }
       ui->comPortName->addItems(portStringList);
    }
    else{   
        message("未检测到串口!");
        }
}
```

> 注:
>
> 1. 其中message();为自己重新封装的信号框函数,具体内容见下。

打开mainwidget.h,添加 #include < QMessageBox > , 然后重新封装函数 void message(const char *str); ,并使用快捷键<kbd>alt</kbd>+<kbd>enter</kbd>,在**mainwidget.cpp** 中定义 void message(const char *str);函数。

具体内容如下:

```cpp
void MainWidget::message(const char *str)
{
    QMessageBox msgBox;
    msgBox.setText(QString::fromLocal8Bit(str));
    msgBox.exec();
}
```

然后在**MainWidget**中运行 **getportInfo();** 函数

这里要注意,在拔插串口设备时,可用的端口号会产生变化,但 **getportInfo();** 函数仅在打开程序时运行一次,因此,这里需要增加更新端口号的方法。

实现方法:

> 1. 添加检测拔插设备的函数,当设备变化时重新运行**getportInfo();**
> 2. 使用定时器,设定每100ms检测一次,当设备变化时重新运行**getportInfo();**
> 3. 增加按钮,当点击按钮时重新运行**getportInfo();**

这里选择增加按钮的方法,主要原因是简单且不占用资源。

![](https://gitee.com/cltzq/picture/ ... 202110282346391.png)

增加按钮的信号与槽,当点击按钮时,重新运行**getportInfo();**

具体内容如下:

```cpp
MainWidget::MainWidget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::MainWidget)
{
    ui->setupUi(this);

    //0.初始化ui界面;配置qss样式
    initUI();
    //1.创建子线程对象,动态分配空间,指定父对象
    t1 = new QThread(this);
    //2.创建任务类对象,动态分配空间,不能指定父对象
    m_work = new Serial;//防止内存泄漏,关闭窗口时 delete m_work;
    //3.将任务对象移动到某个子线程中
    m_work->moveToThread(t1);
    //4.获取串口设备信息
    getportInfo();
        //添加更新按钮信号与槽,用于更新串口设备信息
        connect(ui->upBtn,&QPushButton::clicked,this,&MainWidget::getportInfo);

}
```

至此,利用**QSerialPortInfo**得到串口号完成。
[/md]
作者: Memory#    时间: 2021-10-30 14:23
可以写个定时器实时刷新串口号
作者: 沉沦之枫叶    时间: 2021-11-9 10:10
Memory# 发表于 2021-10-30 14:23
可以写个定时器实时刷新串口号

懒得写定时器了 直接按钮




欢迎光临 野火电子论坛 (https://www.firebbs.cn/) Powered by Discuz! X3.4