創建工程
布局UI界面
設置名稱
設置數據
設置波特率
波特率默認9600
設置數據位
數據位默認8
設置停止位
設置校驗位
調整串口設置、接收設置、發送設置為Group Box
修改配置
QT += core gui serialport
代碼詳解
mianwindow.h
首先在mianwindow.h當中定義一個串口指針
public:
QSerialPort *serialPort;//定義串口指針
并且添加頭文件
#include <QMainWindow>
#include <QSerialPort>
#include <QString>
#include <QSerialPortInfo>
#include <QMessageBox>
#include <QTimer>
#include <QPainter>
QMainWindow:是 Qt 中主窗口的基類,提供了主窗口的基本功能,如菜單欄、工具欄等。
QSerialPort:用于串口通信的類,可實現與串口設備的數據交互。
QString:Qt 中用于處理字符串的類,提供了豐富的字符串操作方法。
QSerialPortInfo:用于獲取系統中可用串口的信息,如串口名稱、描述等。
QMessageBox:用于顯示消息框,可用于提示用戶信息、警告或錯誤。
QTimer:用于實現定時器功能,可在指定時間間隔后觸發特定操作。
QPainter:用于在窗口或其他繪圖設備上進行繪圖操作。
private:// 發送、接收字節計數long sendNum, recvNum;QLabel *lblSendNum;QLabel *lblRecvNum;QLabel *lblPortState;void setNumOnLabel(QLabel *lbl, QString strS, long num);// 定時發送-定時器QTimer *timSend;
Ui::MainWindow *ui;:指向由 Qt Designer 生成的用戶界面類的指針,用于訪問和操作界面元素。
long sendNum, recvNum;:用于記錄發送和接收的字節數。
QLabel *lblSendNum;、QLabel *lblRecvNum;、QLabel *lblPortState;:分別指向用于顯示發送字節數、接收字節數和串口狀態的 QLabel 控件。
void setNumOnLabel(QLabel *lbl, QString strS, long num);:私有成員函數,用于將指定的數字顯示在 QLabel 控件上。
QTimer *timSend;:指向 QTimer 對象的指針,用于實現定時發送功能。
private slots:/*手動連接槽函數*/void manual_serialPortReadyRead();/*以下為mainwindow.ui文件中點擊“轉到槽”自動生成的函數*/void on_openBt_clicked();void on_sendBt_clicked();void on_clearBt_clicked();void on_btnClearSend_clicked();void on_chkTimSend_stateChanged(int arg1);void on_btnSerialCheck_clicked();
void manual_serialPortReadyRead();:手動連接的槽函數,當串口有數據可讀時觸發。
void on_openBt_clicked();:當 openBt 按鈕被點擊時觸發的槽函數,通常用于打開串口。
void on_sendBt_clicked();:當 sendBt 按鈕被點擊時觸發的槽函數,通常用于發送數據。
void on_clearBt_clicked();:當 clearBt 按鈕被點擊時觸發的槽函數,通常用于清除接收區的數據。
void on_btnClearSend_clicked();:當 btnClearSend 按鈕被點擊時觸發的槽函數,通常用于清除發送區的數據。
void on_chkTimSend_stateChanged(int arg1);:當 chkTimSend 復選框的狀態改變時觸發的槽函數,用于處理定時發送的開啟和關閉。
void on_btnSerialCheck_clicked();:當 btnSerialCheck 按鈕被點擊時觸發的槽函數,通常用于檢查系統中可用的串口。
mianwindow.cpp
MainWindow::MainWindow(QWidget *parent)
serialPort = new QSerialPort(this);connect(serialPort,SIGNAL(readyRead()),this,SLOT(manual_serialPortReadyRead()));
將串口的 readyRead() 信號與自定義的槽函數 manual_serialPortReadyRead() 進行連接。
QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection);
sender:發送信號的對象,這里是 serialPort,即 QSerialPort 對象。
signal:發送的信號,使用 SIGNAL 宏將信號名稱轉換為字符串。readyRead() 是 QSerialPort 類的一個信號,當串口接收到新的數據時會自動發出該信號。
receiver:接收信號的對象,這里是 this,即 MainWindow 對象本身。
method:接收信號后要執行的槽函數,使用 SLOT 宏將槽函數名稱轉換為字符串。manual_serialPortReadyRead() 是在 MainWindow 類中定義的一個私有槽函數,用于處理串口接收到的數據。
type:連接類型,默認為 Qt::AutoConnection,表示根據發送者和接收者所在的線程自動選擇合適的連接方式。
ui->serailCb->clear();//通過QSerialPortInfo查找可用串口foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts()){ui->serailCb->addItem(info.portName());}
ui->serailCb->clear();:
ui 是一個指向 Ui::MainWindow 類對象的指針,Ui::MainWindow 類通常是由 Qt Designer 生成的,用于管理主窗口的用戶界面元素。
serailCb 是用戶界面中的一個下拉列表控件(可能是 QComboBox 類型)。
clear() 是 QComboBox 類的一個成員函數,用于清除下拉列表中的所有現有選項。這一步是為了確保在添加新的串口選項之前,下拉列表中沒有其他無關的選項。
foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts()):
foreach 是 Qt 提供的一個用于遍歷容器的宏。在這里,它用于遍歷 QSerialPortInfo::availablePorts() 返回的可用串口信息列表。
QSerialPortInfo 是 Qt 中用于獲取串口設備信息的類,例如串口名稱、描述、制造商等。
availablePorts() 是 QSerialPortInfo 類的一個靜態成員函數,它返回一個包含系統中所有可用串口信息的 QList 列表。
const QSerialPortInfo &info 聲明了一個常量引用 info,用于在每次迭代中存儲當前遍歷到的串口信息對象。通過引用的方式,可以避免不必要的對象拷貝,提高效率。
ui->serailCb->addItem(info.portName());:
對于 foreach 循環中的每一個串口信息對象 info,調用 portName() 成員函數獲取該串口的名稱。
然后使用 ui->serailCb->addItem() 將獲取到的串口名稱作為一個新的選項添加到下拉列表 serailCb 中。這樣,用戶就可以在下拉列表中選擇系統中可用的串口設備了。
// 發送、接收計數清零sendNum = 0;recvNum = 0;// 狀態欄QStatusBar *sBar = statusBar();// 狀態欄的收、發計數標簽lblSendNum = new QLabel(this);lblRecvNum = new QLabel(this);lblPortState = new QLabel(this);lblPortState->setText("Connected");//設置串口狀態標簽為綠色 表示已連接狀態lblPortState->setStyleSheet("color:red");
sendNum 和 recvNum 是在 MainWindow 類中定義的用于記錄發送和接收字節數的變量。
statusBar() 是 QMainWindow 類的一個成員函數,用于獲取主窗口的狀態欄對象。sBar 是一個指向 QStatusBar 對象的指針,通過這個指針可以對狀態欄進行操作,比如添加控件、設置文本等。
lblSendNum、lblRecvNum 和 lblPortState 是在 MainWindow 類中定義的指向 QLabel 對象的指針。
new QLabel(this) 動態創建了三個 QLabel 控件,分別用于顯示發送字節數、接收字節數和串口連接狀態。this 作為參數傳遞給 QLabel 的構造函數,表示將當前 MainWindow 對象作為這些 QLabel 控件的父對象,這樣當 MainWindow 對象被銷毀時,這些 QLabel 控件也會被自動銷毀,避免內存泄漏。
setText() 是 QLabel 類的一個成員函數,用于設置標簽上顯示的文本內容。將 lblPortState 標簽的文本設置為 “Connected”,表示串口已經成功連接。
// 設置標簽最小大小lblSendNum->setMinimumSize(100, 20);lblRecvNum->setMinimumSize(100, 20);lblPortState->setMinimumSize(550, 20);setNumOnLabel(lblSendNum, "S: ", sendNum);setNumOnLabel(lblRecvNum, "R: ", recvNum);// 從右往左依次添加sBar->addPermanentWidget(lblPortState);sBar->addPermanentWidget(lblSendNum);sBar->addPermanentWidget(lblRecvNum);
lblSendNum、lblRecvNum 和 lblPortState 是之前創建的 QLabel 控件指針,分別用于顯示發送字節數、接收字節數和串口連接狀態。
setMinimumSize(int width, int height) 是 QLabel 類從 QWidget 繼承而來的一個成員函數,用于設置控件的最小寬度和高度。這里將 lblSendNum 和 lblRecvNum 的最小大小設置為寬 100 像素、高 20 像素,將 lblPortState 的最小大小設置為寬 550 像素、高 20 像素。這樣做可以確保在界面布局變化時,這些標簽不會被壓縮到小于指定的大小,保證顯示內容的完整性。
setNumOnLabel 是 MainWindow 類中定義的一個私有成員函數,用于將指定的字符串和數字組合后顯示在 QLabel 控件上。
對于 lblSendNum,傳遞的參數 "S: " 作為前綴,sendNum 是之前清零后的發送字節數計數,函數會將它們組合成一個字符串并顯示在 lblSendNum 標簽上,用于提示用戶發送數據的字節數。
同理,對于 lblRecvNum,傳遞的參數 "R: " 作為前綴,recvNum 是接收字節數計數,函數會將組合后的字符串顯示在 lblRecvNum 標簽上,用于提示用戶接收數據的字節數。
sBar 是之前通過 statusBar() 函數獲取的主窗口狀態欄指針。
addPermanentWidget(QWidget * widget) 是 QStatusBar 類的一個成員函數,用于將一個 QWidget 類型的控件(這里是 QLabel 控件)永久添加到狀態欄中。狀態欄中的控件通常按照添加的順序從左到右排列,但由于這里注釋提到 “從右往左依次添加”,實際效果是 lblPortState 在最右邊,然后是 lblSendNum,最后是 lblRecvNum 在最左邊。這樣在狀態欄中就可以依次顯示串口連接狀態、發送字節數和接收字節數,方便用戶查看相關信息。
// 定時發送-定時器timSend = new QTimer;timSend->setInterval(1000);// 設置默認定時時長1000msconnect(timSend, &QTimer::timeout, this, [=](){on_sendBt_clicked();});
timSend 是 MainWindow 類中定義的一個指向 QTimer 對象的指針。
new QTimer 使用 new 運算符在堆上動態創建一個 QTimer 對象,該對象用于實現定時功能。創建后,timSend 指針指向這個新創建的 QTimer 對象。
setInterval(int msec) 是 QTimer 類的一個成員函數,用于設置定時器的時間間隔,單位是毫秒(ms)。
這里將定時器的時間間隔設置為 1000 毫秒,也就是 1 秒。意味著定時器每隔 1 秒就會觸發一次超時信號 timeout()。
connect 是 Qt 中用于連接信號和槽的函數,它建立了信號發送者、信號、信號接收者和槽函數之間的關聯。
timSend:信號的發送者,即剛剛創建的 QTimer 對象。
&QTimer::timeout:發送的信號,timeout() 是 QTimer 類的一個信號,當定時器超時時會自動發出該信號。
this:信號的接收者,這里是 MainWindow 對象本身。
={ on_sendBt_clicked(); }:一個 Lambda 表達式,作為槽函數。[=] 表示以值捕獲的方式捕獲 Lambda 表達式所在作用域中的所有變量,這樣 Lambda 表達式內部就可以訪問這些變量。on_sendBt_clicked() 是 MainWindow 類中定義的一個槽函數,通常用于處理發送按鈕被點擊時的操作,比如發送數據。當定時器超時發出 timeout() 信號時,這個 Lambda 表達式會被執行,進而調用 on_sendBt_clicked() 函數,實現定時發送數據的功能。
void MainWindow::setNumOnLabel(QLabel *lbl, QString strS, long num)
void MainWindow::setNumOnLabel(QLabel *lbl, QString strS, long num)
{// 標簽顯示// QString strN;// strN.sprintf("%ld", num);// QString strN = strFormat.arg(num);QString strN = QString::number(num);QString str = strS + strN;lbl->setText(str);
}
函數名稱:setNumOnLabel,這是 MainWindow 類的一個成員函數,用于將一個字符串前綴和一個長整型數字組合成一個新的字符串,并將其顯示在指定的 QLabel 控件上。
參數:
QLabel *lbl:一個指向 QLabel 控件的指針,該函數會將組合后的字符串顯示在這個 QLabel 控件上。
QString strS:一個 QString 類型的字符串,作為組合字符串的前綴。
long num:一個長整型數字,將被轉換為字符串并與前綴組合。
返回值:void,表示該函數不返回任何值。
QString::number() 是 QString 類的一個靜態成員函數,用于將各種數值類型(如 int、long、double 等)轉換為 QString 類型的字符串。這里將傳入的長整型數字 num 轉換為對應的字符串,并存儲在 strN 變量中。
void MainWindow::on_sendBt_clicked()
/*發送數據*/
void MainWindow::on_sendBt_clicked()
{QByteArray array;//Hex復選框if(ui->chk_send_hex->checkState() == Qt::Checked){//array = QString2Hex(data); //HEX 16進制array = QByteArray::fromHex(ui->sendEdit->toPlainText().toUtf8()).data();}else{//array = data.toLatin1(); //ASCIIarray = ui->sendEdit->toPlainText().toLocal8Bit().data();}if(ui->chk_send_line->checkState() == Qt::Checked){array.append("\r\n");}// 如發送成功,會返回發送的字節長度。失敗,返回-1。int a = serialPort->write(array);// 發送字節計數并顯示if(a > 0){// 發送字節計數sendNum += a;// 狀態欄顯示計數值setNumOnLabel(lblSendNum, "S: ", sendNum);}
}
QByteArray 是 Qt 中用于處理二進制數據的類,這里定義了一個 QByteArray 類型的對象 array,用于存儲要發送的數據。
ui->chk_send_hex 是界面上的一個復選框,用于表示是否以十六進制格式發送數據。
如果該復選框被選中(checkState() == Qt::Checked),則將發送編輯框(ui->sendEdit)中的文本內容先轉換為 UTF - 8 編碼的 QByteArray,再使用 QByteArray::fromHex() 方法將其解析為十六進制數據,存儲到 array 中。
如果該復選框未被選中,則將發送編輯框中的文本內容轉換為本地編碼(toLocal8Bit())的 QByteArray 并存儲到 array 中。
ui->chk_send_line 是界面上的另一個復選框,用于表示是否在發送數據末尾添加換行符(\r\n)。
如果該復選框被選中,則使用 append() 方法將換行符添加到 array 末尾。
serialPort 是 MainWindow 類中定義的 QSerialPort 指針,用于串口通信。
write(const QByteArray &data) 是 QSerialPort 類的成員函數,用于向串口發送數據。該函數返回實際發送的字節數,如果發送失敗則返回 -1。這里將發送的字節數存儲在變量 a 中。
如果發送成功(a > 0),則將實際發送的字節數 a 累加到 sendNum 變量中,sendNum 用于記錄總的發送字節數。
調用 setNumOnLabel() 函數將更新后的發送字節數顯示在狀態欄的 lblSendNum 標簽上,標簽前綴為 "S: "。
void MainWindow::on_openBt_clicked()
QSerialPort::BaudRate baudRate;
QSerialPort::DataBits dataBits;
QSerialPort::StopBits stopBits;
QSerialPort::Parity checkBits;
定義了四個變量,分別用于存儲串口的波特率、數據位、停止位和奇偶校驗位的設置。它們的類型是 QSerialPort 類中定義的枚舉類型,用于準確表示串口通信的不同參數設置。
if(ui->baundrateCb->currentText()=="1200")baudRate=QSerialPort::Baud1200;
// 其他波特率判斷代碼...
else if(ui->baundrateCb->currentText()=="115200")baudRate=QSerialPort::Baud115200;
通過檢查界面上波特率下拉框(ui->baundrateCb)當前選中的文本內容,來確定對應的波特率枚舉值并賦值給 baudRate 變量。根據不同的文本內容,設置相應的波特率枚舉值,如 Baud1200、Baud2400 等。
if(ui->databitCb->currentText()=="5")dataBits=QSerialPort::Data5;
// 其他數據位判斷代碼...
else if(ui->databitCb->currentText()=="8")dataBits=QSerialPort::Data8;
通過檢查數據位下拉框(ui->databitCb)的當前文本內容,確定對應的串口數據位枚舉值并賦值給 dataBits 變量,如 Data5、Data6 等。
if(ui->stopbitCb->currentText()=="1")stopBits=QSerialPort::OneStop;
// 其他停止位判斷代碼...
else if(ui->stopbitCb->currentText()=="2")stopBits=QSerialPort::TwoStop;
過檢查停止位下拉框(ui->stopbitCb)的當前文本內容,確定對應的串口停止位枚舉值并賦值給 stopBits 變量,如 OneStop、OneAndHalfStop 等。
if(ui->checkbitCb->currentText() == "none"){checkBits = QSerialPort::NoParity;
}// 其他奇偶校驗位判斷代碼...
else if(ui->checkbitCb->currentText() == "偶校驗"){checkBits = QSerialPort::EvenParity;
}
通過檢查奇偶校驗位下拉框(ui->checkbitCb)的當前文本內容,確定對應的串口奇偶校驗位枚舉值并賦值給 checkBits 變量,如 NoParity、OddParity 等。
serialPort->setPortName(ui->serailCb->currentText());
serialPort->setBaudRate(baudRate);
serialPort->setDataBits(dataBits);
serialPort->setStopBits(stopBits);
serialPort->setParity(checkBits);
使用之前獲取到的串口參數,設置 serialPort 對象的屬性。包括設置串口端口號(從端口號下拉框 ui->serailCb 獲取)、波特率、數據位、停止位和奇偶校驗位。
if(ui->openBt->text() == "打開串口"){if(serialPort->open(QIODevice::ReadWrite) == true){ui->openBt->setText("關閉串口");ui->serailCb->setEnabled(false);}else{QMessageBox::critical(this, "錯誤提示", "串口打開失敗!!!\r\n該串口可能被占用\r\n請選擇正確的串口");}// 狀態欄顯示端口狀態代碼...lblPortState->setText(status);lblPortState->setStyleSheet("color:green");
}else{serialPort->close();ui->openBt->setText("打開串口");ui->serailCb->setEnabled(true);// 狀態欄顯示端口狀態代碼...lblPortState->setText(status);lblPortState->setStyleSheet("color:red");
}
根據打開按鈕(ui->openBt)當前的文本內容判斷是要打開還是關閉串口:
如果按鈕文本是 “打開串口”,則嘗試以讀寫模式(QIODevice::ReadWrite)打開串口。如果打開成功,將按鈕文本改為 “關閉串口”,并禁用端口號下拉框(ui->serailCb),同時在狀態欄(lblPortState)顯示串口已打開的狀態信息,且設置狀態欄文本顏色為綠色。如果打開失敗,彈出一個錯誤提示框,顯示串口打開失敗的原因。
如果按鈕文本是 “關閉串口”,則關閉串口,將按鈕文本改回 “打開串口”,啟用端口號下拉框,并在狀態欄顯示串口已關閉的狀態信息,設置狀態欄文本顏色為紅色。
void MainWindow::on_clearBt_clicked()
/*清空接收*/
void MainWindow::on_clearBt_clicked()
{ui->recvEdit->clear();// 清除發送、接收字節計數sendNum = 0;recvNum = 0;// 狀態欄顯示計數值setNumOnLabel(lblSendNum, "S: ", sendNum);setNumOnLabel(lblRecvNum, "R: ", recvNum);
}
setNumOnLabel 是 MainWindow 類中定義的一個私有成員函數,用于將指定的前綴字符串和計數值組合成一個新的字符串,并將其顯示在指定的 QLabel 控件上。
lblSendNum 和 lblRecvNum 是指向 QLabel 控件的指針,分別用于在狀態欄上顯示發送和接收字節的計數信息。
"S: " 和 "R: " 是前綴字符串,分別表示 “發送” 和 “接收”。
sendNum 和 recvNum 是當前的發送和接收字節計數
on_clearBt_clicked 函數的主要作用是為用戶提供一種清除接收數據和重置計數信息的方式。當用戶點擊相應的清除按鈕時,該函數會清空接收編輯框中的內容,將發送和接收字節的計數歸零,并更新狀態欄上的計數顯示,以便用戶重新開始統計和查看數據。
void MainWindow::on_btnClearSend_clicked()
void MainWindow::on_btnClearSend_clicked()
{ui->sendEdit->clear();// 清除發送字節計數sendNum = 0;// 狀態欄顯示計數值setNumOnLabel(lblSendNum, "S: ", sendNum);
}
void MainWindow::on_chkTimSend_stateChanged(int arg1)
// 定時發送開關 選擇復選框
void MainWindow::on_chkTimSend_stateChanged(int arg1)
{// 獲取復選框狀態,未選為0,選中為2if(arg1 == 0){timSend->stop();// 時間輸入框恢復可選ui->txtSendMs->setEnabled(true);}else{// 對輸入的值做限幅,小于10ms會彈出對話框提示if(ui->txtSendMs->text().toInt() >= 10){timSend->start(ui->txtSendMs->text().toInt());// 設置定時時長,重新計數// 讓時間輸入框不可選,避免誤操作(輸入功能不可用,控件背景為灰色)ui->txtSendMs->setEnabled(false);}else{ui->chkTimSend->setCheckState(Qt::Unchecked);QMessageBox::critical(this, "錯誤提示", "定時發送的最小間隔為 10ms\r\n請確保輸入的值 >=10");}}
}
arg1 == 0 表示復選框處于未選中狀態。
timSend 是 QTimer 類型的對象指針,用于實現定時發送功能。timSend->stop() 會停止定時器,即停止定時發送的操作。
ui->txtSendMs 是界面上用于輸入定時時間(毫秒)的文本框。setEnabled(true) 方法將該文本框設置為可編輯狀態,允許用戶修改定時時間。
當 arg1 不等于 0 時,表示復選框被選中。
ui->txtSendMs->text().toInt() 會將文本框中輸入的文本轉換為整數,代表用戶設置的定時時間(毫秒)。
如果該值大于等于 10 毫秒,timSend->start(ui->txtSendMs->text().toInt()) 會啟動定時器,并將定時時間設置為用戶輸入的值,開始重新計數。同時,ui->txtSendMs->setEnabled(false) 會將文本框設置為不可編輯狀態,防止用戶在定時發送功能開啟后誤修改定時時間。
如果該值小于 10 毫秒,ui->chkTimSend->setCheckState(Qt::Unchecked) 會將復選框重新設置為未選中狀態,QMessageBox::critical 會彈出一個錯誤提示框,告知用戶定時發送的最小間隔為 10 毫秒,并提醒用戶確保輸入的值大于等于 10。
void MainWindow::manual_serialPortReadyRead()
QByteArray recBuf = serialPort->readAll();
QString str_rev;// 接收字節計數
recvNum += recBuf.size();
// 狀態欄顯示計數值
setNumOnLabel(lblRecvNum, "R: ", recvNum);
serialPort->readAll() 會讀取串口緩沖區中的所有數據,并將其存儲在 QByteArray 類型的 recBuf 中。
recvNum 是一個用于記錄接收字節總數的變量,recBuf.size() 表示本次接收到的數據字節數,將其累加到 recvNum 中。
setNumOnLabel 函數用于將更新后的接收字節數顯示在狀態欄的 lblRecvNum 標簽上。
if(ui->chk_rev_hex->checkState() == false){if(ui->chk_rev_time->checkState() == Qt::Checked){QDateTime nowtime = QDateTime::currentDateTime();str_rev = "[" + nowtime.toString("yyyy-MM-dd hh:mm:ss") + "] ";str_rev += QString(recBuf).append("\r\n");}else{if(ui->chk_rev_line->checkState() == Qt::Checked){str_rev = QString(recBuf).append("\r\n");}else{str_rev = QString(recBuf);}}
}
ui->chk_rev_hex 是一個復選框,用于控制是否以十六進制顯示接收到的數據。如果該復選框未被選中(checkState() == false),則按非十六進制方式處理數據。
若 ui->chk_rev_time 復選框被選中,會獲取當前時間并格式化為 yyyy-MM-dd hh:mm:ss 的字符串,添加到 str_rev 中,然后將接收到的數據轉換為 QString 類型并添加換行符后追加到 str_rev 中。
若 ui->chk_rev_time 未被選中,再根據 ui->chk_rev_line 復選框的狀態決定是否添加換行符。
else{// 16進制顯示,并轉換為大寫QString str1 = recBuf.toHex().toUpper();// 添加空格QString str2;for(int i = 0; i<str1.length (); i+=2){str2 += str1.mid (i,2);str2 += " ";}if(ui->chk_rev_time->checkState() == Qt::Checked){QDateTime nowtime = QDateTime::currentDateTime();str_rev = "[" + nowtime.toString("yyyy-MM-dd hh:mm:ss") + "] ";str_rev += str2.append("\r\n");}else{if(ui->chk_rev_line->checkState() == Qt::Checked)str_rev += str2.append("\r\n");elsestr_rev = str2;}
}
若 ui->chk_rev_hex 復選框被選中,則按十六進制方式處理數據。
recBuf.toHex().toUpper() 將接收到的數據轉換為十六進制字符串并轉換為大寫。
通過循環在每兩個十六進制字符之間添加一個空格。
同樣根據 ui->chk_rev_time 和 ui->chk_rev_line 復選框的狀態決定是否添加時間戳和換行符。
ui->recvEdit->insertPlainText(str_rev);
ui->recvEdit->moveCursor(QTextCursor::End);
ui->recvEdit 是接收編輯框,insertPlainText(str_rev) 將處理后的字符串 str_rev 插入到編輯框中。
moveCursor(QTextCursor::End) 將編輯框的光標移動到文本末尾,確保新接收到的數據能及時顯示在界面上,避免界面不滾動的問題。
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <QSerialPort>
#include <QString>
#include <QSerialPortInfo>
#include <QMessageBox>
#include <QTimer>
#include <QPainter>QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();QSerialPort *serialPort;//定義串口指針private slots:/*手動連接槽函數*/void manual_serialPortReadyRead();/*以下為mainwindow.ui文件中點擊“轉到槽”自動生成的函數*/void on_openBt_clicked();void on_sendBt_clicked();void on_clearBt_clicked();void on_btnClearSend_clicked();void on_chkTimSend_stateChanged(int arg1);void on_btnSerialCheck_clicked();private:Ui::MainWindow *ui;// 發送、接收字節計數long sendNum, recvNum;QLabel *lblSendNum;QLabel *lblRecvNum;QLabel *lblPortState;void setNumOnLabel(QLabel *lbl, QString strS, long num);// 定時發送-定時器QTimer *timSend;//QTimer *timCheckPort;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "QSerialPortInfo"
#include <QSerialPort>
#include <QMessageBox>
#include <QDateTime>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);QStringList serialNamePort;serialPort = new QSerialPort(this);//當串口接收到新的數據時,QSerialPort 對象會發出 readyRead() 信號,MainWindow 對象會接收到該信號并調用 manual_serialPortReadyRead() 槽函數來處理接收到的數據。connect(serialPort,SIGNAL(readyRead()),this,SLOT(manual_serialPortReadyRead()));/*手動連接槽函數*/ui->serailCb->clear();//清除下拉列表中的所有現有選項//通過QSerialPortInfo查找可用串口//foreach 用于遍歷 QSerialPortInfo foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts()){//將遍歷到info的名稱添加到serailCbui->serailCb->addItem(info.portName());}// 發送、接收計數清零//sendNum 和 recvNum 是在 MainWindow 類中定義的用于記錄發送和接收字節數的變量。sendNum = 0;recvNum = 0;// 狀態欄QStatusBar *sBar = statusBar();// 狀態欄的收、發計數標簽lblSendNum = new QLabel(this);lblRecvNum = new QLabel(this);lblPortState = new QLabel(this);lblPortState->setText("Connected");//設置串口狀態標簽為紅色 表示未連接狀態lblPortState->setStyleSheet("color:red");// 設置標簽最小大小lblSendNum->setMinimumSize(100, 20);lblRecvNum->setMinimumSize(100, 20);lblPortState->setMinimumSize(550, 20);setNumOnLabel(lblSendNum, "S: ", sendNum);setNumOnLabel(lblRecvNum, "R: ", recvNum);// 從右往左依次添加sBar->addPermanentWidget(lblPortState);sBar->addPermanentWidget(lblSendNum);sBar->addPermanentWidget(lblRecvNum);// 定時發送-定時器timSend = new QTimer;//設置定時器的時間間隔,單位是毫秒(ms)。timSend->setInterval(1000);// 設置默認定時時長1000msconnect(timSend, &QTimer::timeout, this, [=](){on_sendBt_clicked();});
}MainWindow::~MainWindow()
{delete ui;
}//檢測通訊端口槽函數
void MainWindow::on_btnSerialCheck_clicked()
{ui->serailCb->clear();//通過QSerialPortInfo查找可用串口foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts()){ui->serailCb->addItem(info.portName());}
}/*手動實現接收數據函數*/
void MainWindow::manual_serialPortReadyRead()
{QByteArray recBuf = serialPort->readAll();;QString str_rev;// 打印接收到的原始數據qDebug() << "Received raw data:" << recBuf;// 接收字節計數recvNum += recBuf.size();// 狀態欄顯示計數值setNumOnLabel(lblRecvNum, "R: ", recvNum);if(ui->chk_rev_hex->checkState() == false){if(ui->chk_rev_time->checkState() == Qt::Checked){QDateTime nowtime = QDateTime::currentDateTime();str_rev = "[" + nowtime.toString("yyyy-MM-dd hh:mm:ss") + "] ";str_rev += QString(recBuf).append("\r\n");}else{// 在當前位置插入文本,不會發生換行。如果沒有移動光標到文件結尾,會導致文件超出當前界面顯示范圍,界面也不會向下滾動。//ui->recvEdit->appendPlainText(buf);if(ui->chk_rev_line->checkState() == Qt::Checked){str_rev = QString(recBuf).append("\r\n");}else{str_rev = QString(recBuf);}}}else{// 16進制顯示,并轉換為大寫QString str1 = recBuf.toHex().toUpper();//.data();// 添加空格QString str2;for(int i = 0; i<str1.length (); i+=2){str2 += str1.mid (i,2);str2 += " ";}if(ui->chk_rev_time->checkState() == Qt::Checked){QDateTime nowtime = QDateTime::currentDateTime();str_rev = "[" + nowtime.toString("yyyy-MM-dd hh:mm:ss") + "] ";str_rev += str2.append("\r\n");}else{if(ui->chk_rev_line->checkState() == Qt::Checked)str_rev += str2.append("\r\n");elsestr_rev = str2;}}ui->recvEdit->insertPlainText(str_rev);ui->recvEdit->moveCursor(QTextCursor::End);// 打印處理后的數據qDebug() << "Processed data:" << str_rev;
}/*打開串口*/
void MainWindow::on_openBt_clicked()
{/*串口初始化*/QSerialPort::BaudRate baudRate;QSerialPort::DataBits dataBits;QSerialPort::StopBits stopBits;QSerialPort::Parity checkBits;// 獲取串口波特率// baudRate = ui->baundrateCb->currentText().toInt();直接字符串轉換為 int 的方法if(ui->baundrateCb->currentText()=="1200")baudRate=QSerialPort::Baud1200;else if(ui->baundrateCb->currentText()=="2400")baudRate=QSerialPort::Baud2400;else if(ui->baundrateCb->currentText()=="4800")baudRate=QSerialPort::Baud4800;else if(ui->baundrateCb->currentText()=="9600")baudRate=QSerialPort::Baud9600;else if(ui->baundrateCb->currentText()=="19200")baudRate=QSerialPort::Baud19200;else if(ui->baundrateCb->currentText()=="38400")baudRate=QSerialPort::Baud38400;else if(ui->baundrateCb->currentText()=="57600")baudRate=QSerialPort::Baud57600;else if(ui->baundrateCb->currentText()=="115200")baudRate=QSerialPort::Baud115200;// 獲取串口數據位if(ui->databitCb->currentText()=="5")dataBits=QSerialPort::Data5;else if(ui->databitCb->currentText()=="6")dataBits=QSerialPort::Data6;else if(ui->databitCb->currentText()=="7")dataBits=QSerialPort::Data7;else if(ui->databitCb->currentText()=="8")dataBits=QSerialPort::Data8;// 獲取串口停止位if(ui->stopbitCb->currentText()=="1")stopBits=QSerialPort::OneStop;else if(ui->stopbitCb->currentText()=="1.5")stopBits=QSerialPort::OneAndHalfStop;else if(ui->stopbitCb->currentText()=="2")stopBits=QSerialPort::TwoStop;// 獲取串口奇偶校驗位if(ui->checkbitCb->currentText() == "none"){checkBits = QSerialPort::NoParity;}else if(ui->checkbitCb->currentText() == "奇校驗"){checkBits = QSerialPort::OddParity;}else if(ui->checkbitCb->currentText() == "偶校驗"){checkBits = QSerialPort::EvenParity;}else{}// 初始化串口屬性,設置 端口號、波特率、數據位、停止位、奇偶校驗位數serialPort->setPortName(ui->serailCb->currentText());serialPort->setBaudRate(baudRate);serialPort->setDataBits(dataBits);serialPort->setStopBits(stopBits);serialPort->setParity(checkBits);// 根據初始化好的串口屬性,打開串口// 如果打開成功,反轉打開按鈕顯示和功能。打開失敗,無變化,并且彈出錯誤對話框。if(ui->openBt->text() == "打開串口"){if(serialPort->open(QIODevice::ReadWrite) == true){//QMessageBox::ui->openBt->setText("關閉串口");// 讓端口號下拉框不可選,避免誤操作(選擇功能不可用,控件背景為灰色)ui->serailCb->setEnabled(false);}else{QMessageBox::critical(this, "錯誤提示", "串口打開失敗!!!\r\n該串口可能被占用\r\n請選擇正確的串口");}//statusBar 狀態欄顯示端口狀態QString sm = "%1 OPENED, %2, 8, NONE, 1";QString status = sm.arg(serialPort->portName()).arg(serialPort->baudRate());lblPortState->setText(status);lblPortState->setStyleSheet("color:green");}else{serialPort->close();ui->openBt->setText("打開串口");// 端口號下拉框恢復可選,避免誤操作ui->serailCb->setEnabled(true);//statusBar 狀態欄顯示端口狀態QString sm = "%1 CLOSED";QString status = sm.arg(serialPort->portName());lblPortState->setText(status);lblPortState->setStyleSheet("color:red");}}/*發送數據*/
void MainWindow::on_sendBt_clicked()
{QByteArray array;//Hex復選框if(ui->chk_send_hex->checkState() == Qt::Checked){//array = QString2Hex(data); //HEX 16進制array = QByteArray::fromHex(ui->sendEdit->toPlainText().toUtf8()).data();}else{//array = data.toLatin1(); //ASCIIarray = ui->sendEdit->toPlainText().toLocal8Bit().data();}if(ui->chk_send_line->checkState() == Qt::Checked){array.append("\r\n");}// 打印需要發送的數據qDebug() << "Data to be sent:" << array;// 如發送成功,會返回發送的字節長度。失敗,返回-1。int a = serialPort->write(array);// 發送字節計數并顯示if(a > 0){// 發送字節計數sendNum += a;// 狀態欄顯示計數值setNumOnLabel(lblSendNum, "S: ", sendNum);}
}
// 狀態欄標簽顯示計數值
void MainWindow::setNumOnLabel(QLabel *lbl, QString strS, long num)
{// 標簽顯示// QString strN;// strN.sprintf("%ld", num);// QString strN = strFormat.arg(num);QString strN = QString::number(num);QString str = strS + strN;lbl->setText(str);
}
/*清空接收*/
void MainWindow::on_clearBt_clicked()
{ui->recvEdit->clear();// 清除發送、接收字節計數sendNum = 0;recvNum = 0;// 狀態欄顯示計數值setNumOnLabel(lblSendNum, "S: ", sendNum);setNumOnLabel(lblRecvNum, "R: ", recvNum);
}void MainWindow::on_btnClearSend_clicked()
{ui->sendEdit->clear();// 清除發送字節計數sendNum = 0;// 狀態欄顯示計數值setNumOnLabel(lblSendNum, "S: ", sendNum);
}
// 定時發送開關 選擇復選框
void MainWindow::on_chkTimSend_stateChanged(int arg1)
{// 獲取復選框狀態,未選為0,選中為2if(arg1 == 0){timSend->stop();// 時間輸入框恢復可選ui->txtSendMs->setEnabled(true);}else{// 對輸入的值做限幅,小于10ms會彈出對話框提示if(ui->txtSendMs->text().toInt() >= 10){timSend->start(ui->txtSendMs->text().toInt());// 設置定時時長,重新計數// 讓時間輸入框不可選,避免誤操作(輸入功能不可用,控件背景為灰色)ui->txtSendMs->setEnabled(false);}else{ui->chkTimSend->setCheckState(Qt::Unchecked);QMessageBox::critical(this, "錯誤提示", "定時發送的最小間隔為 10ms\r\n請確保輸入的值 >=10");}}
}
參考
https://blog.csdn.net/weixin_44788542/article/details/130508621