QT:串口上位機

創建工程

在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述

布局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

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/73121.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/73121.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/73121.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【Pandas】pandas Series asof

Pandas2.2 Series Time Series-related 方法描述Series.asfreq(freq[, method, how, …])用于將時間序列數據轉換為指定的頻率Series.asof(where[, subset])用于返回時間序列中指定索引位置的最近一個非缺失值 pandas.Series.asof pandas.Series.asof 方法用于返回時間序列…

沉浸式CSS學習路徑

好的!我將以魔法學院成長故事為框架,為您設計一套沉浸式CSS學習路徑。以下是敘事化學習提綱: 第一卷:像素學徒的覺醒 章節1:被封印的魔法書 發現HTML的"素顏"本質,通過<!DOCTYPE html>解除網頁封印用style標簽打開CSS魔法書,學會給文字穿上color斗篷和…

使用netlify部署github的vue/react項目或本地的dist,國內也可以正常訪問

提供簡潔的部署流程和豐富功能&#xff0c;如自定義域名、自動構建和服務器端功能。通過連接到 Git 倉庫實現持續部署&#xff0c;每次推送代碼都會自動構建和發布&#xff0c;支持無服務器函數&#xff0c;允許在前端項目中實現后端邏輯&#xff0c;提供直觀的用戶界面來管理和…

復現 MoGe

要復現 MoGe&#xff0c;以下給出一般性的復現訓練過程步驟示例&#xff09;的訓練過程&#xff0c;你可以參考以下步驟&#xff1a; 環境準備 安裝必要的深度學習框架&#xff0c;如 TensorFlow 或 PyTorch&#xff0c;以及相關的庫&#xff0c;例如用于數據處理的 NumPy、Pan…

Redis-緩存穿透擊穿雪崩

1. 穿透問題 緩存穿透問題就是查詢不存在的數據。在緩存穿透中&#xff0c;先查緩存&#xff0c;緩存沒有數據&#xff0c;就會請求到數據庫上&#xff0c;導致數據庫壓力劇增。 解決方法&#xff1a; 給不存在的key加上空值&#xff0c;防止每次都會請求到數據庫。布隆過濾器…

如何在自己的網站接入API接口獲取數據?分步指南與實戰示例

將第三方API接入自己的網站是獲取實時數據、擴展功能的重要手段&#xff08;如展示商品、同步訂單、用戶登錄等&#xff09;。以下是完整的接入流程與關鍵實踐&#xff0c;以微店API為例&#xff0c;適用于多數開放平臺。 一、準備工作&#xff1a;注冊與權限申請 注冊開發者…

刷leetcode hot100--動態規劃3.12

第一題乘積max子數組[1h] emmmm感覺看不懂題解 線性dp【計劃學一下acwing&#xff0c;挨個做一下】 線性動態規劃 相似題解析 最長上升子序列 最大上升子序列和 最大連續子段和 乘積最大子數組_嗶哩嗶哩_bilibili 比較奇怪的就是有正負數和0&#xff0c;如何處理&#xff1f…

Pytortch深度學習網絡框架庫 torch.no_grad方法 核心原理與使用場景

在PyTorch中&#xff0c;with torch.no_grad() 是一個用于臨時禁用自動梯度計算的上下文管理器。它通過關閉計算圖的構建和梯度跟蹤&#xff0c;優化內存使用和計算效率&#xff0c;尤其適用于不需要反向傳播的場景。以下是其核心含義、作用及使用場景的詳細說明&#xff1a; 一…

postgresql 數據庫使用

目錄 索引 查看索引 創建 刪除索引 修改數據庫時區 索引 查看索引 select * from pg_indexes where tablenamet_table_data; 或者 select * from pg_statio_all_indexes where relnamet_table_data; 創建 CREATE INDEX ix_table_data_time ON t_table_data (id, crea…

為什么大模型網站使用 SSE 而不是 WebSocket?

在大模型網站&#xff08;如 ChatGPT、Claude、Gemini 等&#xff09;中&#xff0c;前端通常使用 EventSource&#xff08;Server-Sent Events, SSE&#xff09; 來與后端對接&#xff0c;而不是 WebSocket。這是因為 SSE 更適合類似流式文本生成的場景。下面我們詳細對比 SSE…

TDengine 數據對接 EXCEL

簡介 通過配置使用 ODBC 連接器&#xff0c;Excel 可以快速訪問 TDengine 的數據。用戶可以將標簽數據、原始時序數據或按時間聚合后的時序數據從 TDengine 導入到 Excel&#xff0c;用以制作報表整個過程不需要任何代碼編寫過程。 前置條件 準備以下環境&#xff1a; TDen…

【具身相關】legged_gym, isaacgym、rsl_rl關系梳理

【legged_gym】legged_gym, isaacgym代碼邏輯梳理 總體關系IsaacGymlegged_gymrsl_rl三者的關系 legged_gym代碼庫介紹環境模塊env 總體關系 IsaacGym Isaac Gym 是 NVIDIA 開發的一個高性能物理仿真平臺&#xff0c;專門用于強化學習和機器人控制任務。它基于 NVIDIA 的 Phy…

【每日學點HarmonyOS Next知識】狀態變量、動畫UI殘留、Tab控件顯示、ob前綴問題、文字背景拉伸

1、HarmonyOS 怎么用一個變量觀察其他很多個變量的變化&#xff1f; 有一個提交按鈕的顏色&#xff0c;需要很多個值非空才變為紅色&#xff0c;否則變為灰色&#xff0c;可不可以用一個變量統一觀察這很多個值&#xff0c;去判斷按鈕該顯示什么顏色&#xff0c;比如Button().…

全鏈條自研可控|江波龍汽車存儲“雙輪驅動”體系亮相MemoryS 2025

3月12日&#xff0c;MemoryS 2025在深圳盛大開幕&#xff0c;匯聚了存儲行業的頂尖專家、企業領袖以及技術先鋒&#xff0c;共同探討存儲技術的未來發展方向及其在商業領域的創新應用。江波龍董事長、總經理蔡華波先生受邀出席&#xff0c;并發表了題為《存儲商業綜合創新》的主…

基于Python+SQLite實現校園信息化統計平臺

一、項目基本情況 概述 本項目以清華大學為預期用戶&#xff0c;作為校內信息化統計平臺進行服務&#xff0c;建立網頁端和移動端校內信息化統計平臺&#xff0c;基于Project_1的需求實現。 本項目能夠滿足校內學生團體的幾類統計需求&#xff0c;如活動報名、實驗室招募、多…

(每日一題) 力扣 2418. 按身高排序

文章目錄 &#x1f984; LeetCode 2418.按身高排序&#xff5c;雙解法對比與下標排序的精妙設計&#x1f4dd; 問題描述&#x1f4a1; 解法思路分析方法一&#xff1a;Pair打包法&#xff08;直接排序&#xff09;方法二&#xff1a;下標排序法&#xff08;當前實現&#xff09…

計算機畢業設計:ktv點歌系統

ktv點歌系統mysql數據庫創建語句ktv點歌系統oracle數據庫創建語句ktv點歌系統sqlserver數據庫創建語句ktv點歌系統springspringMVChibernate框架對象(javaBean,pojo)設計ktv點歌系統springspringMVCmybatis框架對象(javaBean,pojo)設計 ktv點歌系統mysql數據庫版本源碼&#xf…

Deepin通過二進制方式升級部署高版本 Docker

一、背景&#xff1a; 在Deepin系統中通過二進制方式升級部署高版本 Docker&#xff0c;下面將詳細介紹二進制方式升級部署高版本 Docker 的具體步驟。 二、操作步驟 1.根據需求下載二進制文件&#xff0c;下載地址如下&#xff1a; https://mirrors.tuna.tsinghua.e…

2025年Draw.io最新版本下載安裝教程,附詳細圖文

2025年Draw.io最新版本下載安裝教程&#xff0c;附詳細圖文 大家好&#xff0c;今天給大家介紹一款非常實用的流程圖繪制軟件——Draw.io。不管你是平時需要設計流程圖、繪制思維導圖&#xff0c;還是制作架構圖&#xff0c;甚至是簡單的草圖&#xff0c;它都能幫你輕松搞定。…

道路運輸安全員考試備考:循序漸進,穩步提升

備考道路運輸安全員考試是一個循序漸進的過程&#xff0c;需要穩步提升自己的知識和能力。? 第一階段是基礎鞏固階段。這一階段要以教材為核心&#xff0c;全面系統地學習各個知識板塊。從道路運輸法規開始&#xff0c;逐章逐節地學習&#xff0c;理解每一條法規的含義和適用…