作者:zzssdd2
E-mail:zzssdd2@foxmail.com
一、前言
開發環境:Qt5.12.10 + MinGW
主要實現功能
- 串口參數的配置:
波特率
、數據位
、停止位
、校驗位
- 本機串口設備的查詢與添加顯示
- 串口設備的手動更新與打開關閉
涉及的知識點
- 串口類
QSerialPort
及QSerialPortInfo
的使用 - 自定義波特率的實現
QComboBox
下拉列表自適應文本長度顯示- 控件
QPushButton
、QComboBox
的使用
二、功能實現
下面開始逐步講解以上列舉的功能
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開發必然離不開信號槽的應用。