QT串口助手(二):参数配置

作者:zzssdd2

E-mail:zzssdd2@foxmail.com

一、前言

开发环境:Qt5.12.10 + MinGW

主要实现功能

  • 串口参数的配置:波特率数据位停止位校验位
  • 本机串口设备的查询与添加显示
  • 串口设备的手动更新与打开关闭

涉及的知识点

  • 串口类QSerialPortQSerialPortInfo的使用
  • 自定义波特率的实现
  • QComboBox下拉列表自适应文本长度显示
  • 控件QPushButtonQComboBox的使用

二、功能实现

下面开始逐步讲解以上列举的功能

2.1、串口设备的查询添加

创建一个串口设备信息列表和串口设备号列表,遍历本机可用串口然后添加到列表中,将设备信息列表显示在对应的QComboBox控件中,设备号列表用来后面打开串口设备使用。

/* 遍历可用串口设备 */
QStringList SerialPort_Name;
foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
{
    SerialPort_Name << info.portName()+':'+info.description();
    serialDevice << info.portName();
}
/* 添加到串口设备显示框 */
ui->SerialNum_Box->addItems(SerialPort_Name);

2.2、下拉列表自适应

此时已经将串口设备信息添加到下拉框中了,但是下拉框此时是固定长度,如果串口设备信息比较长则无法显示完全(以省略号展示),那么就需要实现下拉框根据设备信息字符串长度适配功能。原理就是遍历列表中所有设备信息字符串的长度,找出最大值然后将下拉框长度设置到能完全显示最大长度字符串。

/* 获取最长字符串 */
int maxlen = 0;
for (int index = 0; index < ui->SerialNum_Box->count(); index++)
{
    if (ui->SerialNum_Box->itemText(index).length() > maxlen)
    {
        maxlen = ui->SerialNum_Box->itemText(index).length();
    }
}
/*获取字体磅值转换为像素值*/
int fontsize = ui->SerialNum_Box->font().pointSize();//获取字体的磅值
ui->SerialNum_Box->view()->setFixedWidth(fontsize * maxlen * 0.75);//设置像素值

这里涉及字体磅与像素对应关系,因为setFixedWidth函数参数单位是像素。下面列出字号不同单位对照表:

中文字号 英文字号(磅) 毫米 像素
1英寸 72pt 25.30mm 95.6px
大特号 63pt 22.14mm 83.7px
特号 54pt 18.97mm 71.7px
初号 42pt 14.82mm 56px
小初 36pt 12.70mm 48px
一号 26pt 9.17mm 34.7px
小一 24pt 8.47mm 32px
二号 22pt 7.76mm 29.3px
小二 18pt 6.35mm 24px
三号 16pt 5.64mm 21.3px
小三 15pt 5.29mm 20px
四号 14pt 4.94mm 18.7px
小四 12pt 4.23mm 16px
五号 10.5pt 3.70mm 14px
小五 9pt 3.18mm 12px
六号 7.5pt 2.56mm 10px
小六 6.5pt 2.29mm 8.7px
七号 5.5pt 1.94mm 7.3px
八号 5pt 1.76mm 6.7px

由上表可知:1inch = 72pt,那么1pt = 1 / 72(inch),我们使用的9pt字号就是9 * (1 / 72) = 1 / 8(inch).

DPI(dots per inch)表示每英寸能够打印的格点数量(可以理解为像素点数),这个值一般默认值为96。在小字体模式下分辨率是96DPI(大字体模式下分辨率是120DPI),也就是说每英寸能显示96个像素,由此可以算出我们使用的9pt字号对应像素:96dpi / (1 / 8)inch = 12px,那么1px = 9pt / 12px = 0.75pt。

添加该功能后前后对比:

2.3、串口参数的配置

这部分功能实现串口参数的配置,主要有波特率、数据位、停止位、校验位等,根据下拉框选择的参数进行配置。

/* 设置波特率 */
switch (ui->Bandrate_Box->currentIndex())
{
    case 0:
        serial->setBaudRate(QSerialPort::Baud1200,QSerialPort::AllDirections);
        break;
    case 1:
        serial->setBaudRate(QSerialPort::Baud2400,QSerialPort::AllDirections);
        break;
    case 2:
        serial->setBaudRate(QSerialPort::Baud4800,QSerialPort::AllDirections);
        break;
    case 3:
        serial->setBaudRate(QSerialPort::Baud9600,QSerialPort::AllDirections);
        break;
    case 4:
        serial->setBaudRate(QSerialPort::Baud19200,QSerialPort::AllDirections);
        break;
    case 5:
        serial->setBaudRate(QSerialPort::Baud38400,QSerialPort::AllDirections);
        break;
    case 6:
        serial->setBaudRate(QSerialPort::Baud57600,QSerialPort::AllDirections);
        break;
    case 7:
        serial->setBaudRate(QSerialPort::Baud115200,QSerialPort::AllDirections);
        break;
    case 8:
        /*自定义波特率*/
    default:
        break;
}
/* 设置数据位 */
switch (ui->Databit_Box->currentIndex()) {
    case 0:
        serial->setDataBits(QSerialPort::Data5);
        break;
    case 1:
        serial->setDataBits(QSerialPort::Data6);
        break;
    case 2:
        serial->setDataBits(QSerialPort::Data7);
        break;
    case 3:
        serial->setDataBits(QSerialPort::Data8);
        break;
    default:
        break;
}
/* 设置停止位 */
switch (ui->Stopbit_Box->currentIndex()) {
    case 0:
        serial->setStopBits(QSerialPort::OneStop);
        break;
    case 1:
        serial->setStopBits(QSerialPort::OneAndHalfStop);
        break;
    case 2:
        serial->setStopBits(QSerialPort::TwoStop);
        break;
    default:
        break;
}
/* 设置校验位 */
switch (ui->Parity_Box->currentIndex()) {
    case 0:
        serial->setParity(QSerialPort::NoParity);
        break;
    case 1:
        serial->setParity(QSerialPort::EvenParity);
        break;
    case 2:
        serial->setParity(QSerialPort::OddParity);
        break;
    default:
        break;
}
/* 设置流控制 */
serial->setFlowControl(QSerialPort::NoFlowControl);

2.4、自定义波特率

在QT的源码文件qserialport.h中枚举了常用的波特率:

    enum BaudRate {
        Baud1200 = 1200,
        Baud2400 = 2400,
        Baud4800 = 4800,
        Baud9600 = 9600,
        Baud19200 = 19200,
        Baud38400 = 38400,
        Baud57600 = 57600,
        Baud115200 = 115200,
        UnknownBaud = -1
    };

但是如果没有列出我们用到的波特率,那么就需要实现自定义波特率(比如我以前参与的一个项目就要求串口使用500000bps波特率)。实现方式就是将波特率QComboBox列表最后一项设置为自定义波特率,当使用自定义波特率时则获取填写的自定义值并转换为qint32类型数值,然后将该参数设置为波特率,如下:

qint32 CustomBandrate = ui->Bandrate_Box->currentText().toUInt();
serial->setBaudRate(CustomBandrate, QSerialPort::AllDirections);

这里需要注意的是波特率列表自定义项之前的都是对应的固定波特率值,如果不是选择自定义波特率应当设置为不可编辑状态,只有选择自定义波特率时才可以编辑。这里使用QComboBox的信号currentIndexChanged触发槽函数实现该功能:如果选择了自定义波特率,则将下拉框设为可编辑,否则设为不能编辑

/*下拉项改变信号槽:重载函数若有多种不同参数类型则使用信号槽连接时有必要指定参数类型*/
connect(ui->Bandrate_Box, QOverload<int>::of(&QComboBox::currentIndexChanged),[=](int index){
    if (index == 8) {
        ui->Bandrate_Box->setEditable(true);
        ui->Bandrate_Box->setCurrentText(NULL);
    } else {
        ui->Bandrate_Box->setEditable(false);
    }
});

参数配置列表完成如下:

2.5、串口打开与关闭

串口开关按钮点击信号对应的槽函数实现内容如下:

/*
    函   数:on_SerialPortSwitch_Bt_clicked
    描   述:开关串口按键点击槽函数
    输   入:无
    输   出:无
*/
void Widget::on_SerialPortSwitch_Bt_clicked()
{
    if (ui->SerialPortSwitch_Bt->text() == "打开")
    {
        /* 创建串口对象 */
        serial = new QSerialPort(this);
        /* 创建接收数据信号槽 */
        connect(serial, &QSerialPort::readyRead, this, &Widget::SerialPortReadyRead_slot);
        /* 设置串口号 */
        QString dev = serialDevice.at(ui->SerialNum_Box->currentIndex());
        serial->setPortName(dev);
        /* 设置波特率 */
        /* 设置数据位 */
        /* 设置停止位 */
        /* 设置校验位 */
        /* 设置流控制 */
        /* 打开串口 */
        if (serial->open(QIODevice::ReadWrite) != true)
        {
            QMessageBox::critical(this, "提示", "打开失败");
            return;
        }
        /*标记串口已打开*/
        global_struct.isSerialOpen = true;
        /*设置文本*/
        ui->SerialPortSwitch_Bt->setText("关闭");
    }
    else
    {
        /*关闭并删除串口对象*/
        serial->close();
        serial->deleteLater();
        /*标记串口已关闭*/
        global_struct.isSerialOpen = false;
        /*设置文本*/
        ui->SerialPortSwitch_Bt->setText("打开");
    }
}

2.6、串口设备的刷新

串口设备刷新按钮点击信号对应的槽函数功能实现如下:

/*
    函   数:on_ComFlush_BT_clicked
    描   述:串口设备刷新按钮槽函数
    输   入:无
    输   出:无
*/
void Widget::on_ComFlush_BT_clicked()
{
    ui->SerialNum_Box->clear();
    FindSerialPort();
}

/*
    函   数:FindSerialPort
    描   述:查找串口并添加到QComboBox
    输   入:无
    输   出:无
*/
void Widget::FindSerialPort(void)
{
    /* 遍历可用串口并添加到显示框 */
    QStringList SerialPort_Name;
    foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
    {
        SerialPort_Name << info.portName()+':'+info.description();
        serialDevice << info.portName();
    }
    ui->SerialNum_Box->addItems(SerialPort_Name);
    /*设置下拉框列表宽度*/
    int maxlen = 0;
    for (int index = 0; index < ui->SerialNum_Box->count(); index++)
    {
        if (ui->SerialNum_Box->itemText(index).length() > maxlen)
        {
            maxlen = ui->SerialNum_Box->itemText(index).length();
        }
    }
    int fontsize = ui->SerialNum_Box->font().pointSize();
    ui->SerialNum_Box->view()->setFixedWidth(fontsize * maxlen * 0.75);
}

三、总结

本篇文章主要是讲如何对串口参数进行配置及QComboBox等控件的一些基本操作。除此之外,还应该了解一定C++知识,比如类的继承、函数重载、Lambda表达式等;还有就是应该掌握QT信号槽的使用,因为这是QT框架中非常重要的通信机制,可以说使用QT开发必然离不开信号槽的应用。

posted @ 2021-01-21 22:20  树·哥  阅读(3659)  评论(0编辑  收藏  举报