一、設計UI界面
使用qt設計師
需要注意的是
調整整體大小
最後設置整體自動調整大小時,對於QMainWindow窗口需要右鍵最基本的框,選擇佈局,不然在右側是沒有佈局選項的
窗口放大時非對話框有最大大小,不跟着變大
設置對應窗口的對齊為 0
按鈕進入對齊後選擇minimum,可以變大
默認時
二、編寫邏輯
編寫基本框架
選擇自己使用的是QMainWindow還是QWidget
導入pyside的庫
from PySide6.QtWidgets import QApplication,QMainWindow,QWidget
引用對應界面庫
#ui為上一層目錄
from ui.Ui_serial_up import Ui_MainWindow
class MyWindow(QMainWindow,Ui_MainWindow):
def __init__(self):
super().__init__()
self.setupUi(self)
if __name__ == "__main__":
app = QApplication()
window = MyWindow()
window.show()
sys.exit(app.exec())
邏輯部分
導入串口所需庫
from PySide6.QtCore import QThread,Signal
import serial
import serial.tools.list_ports
配置大體框架
#接收線程
class SerialReadThread(QThread):
ReadSignal = Signal(str) #接收信號
def __init__(self,serial_obj):
super().__init__()
def run():
def stop():
子窗口
子窗口現在qt設計師設計好,使用QSialog類型
#子窗口界面
class SettingSerial(QDialog,Ui_Dialog):
def __init__(self,parent = None):#parent 為傳入形參接口
super().__init__(parent)
self.setupUi(self)#調用子窗口Ui
#綁定自帶的確認和取消鍵
#連接固定的確定取消方法
#self.reject()和self.accept()是取消和確認方法
self.buttonBox.accepted.connect(self.buttonAccept)
self.buttonBox.rejected.connect(self.reject)
def buttonAccept(self):
self.accept()
def backSetting(self):
'''子窗口返回方法,同與父窗口調用後關閉子窗口時調用'''
父窗口框架
#界面
class MyWindow(QMainWindow,Ui_MainWindow):
def __init__(self):
super().__init__()
self.setupUi(self)#調用父窗口Ui
def SerialSend(self):
def SerialReceive(self):
def SerialStart(self):
def SerialStop(self):
def ClearTx(self):
def ClearRx(self):
編寫代碼
首先編寫主界面的基本操作
#清除
def clearTx(self):
self.TextEdit_2.clear()
def clearRx(self):
self.TextEdit_1.clear()
#變量定義
def initVariable(self):
self.baudrate = "115200"
self.bytesize = '8'
self.stopbits_set = '1'
self.parity_set = 'None'
# 串口對象
self.serial_port = None
self.reader_thread = None
self.send_byte_count = 0 #發送字節總數
self.receive_byte_count = 0 #接收字節總數
#按鍵關聯方法
def ConnectButton(self):
#關聯按鍵
self.pushButton_5.clicked.connect(self.clearTx)
self.pushButton.clicked.connect(self.clearRx)
#打開串口
self.pushButton_2.clicked.connect(self.SerialSwitch)
#刷新端口
self.action10.setShortcut('F5')
self.action10.setStatusTip('刷新端口')
self.action10.triggered.connect(self.RefreshSerial)
#串口設置
self.pushButton_4.clicked.connect(self.open_dialog)
self.action20.setStatusTip('配置串口')
self.action20.triggered.connect(self.open_dialog)
#更改baud下拉欄
self.comboBox_2.currentIndexChanged.
connect(self.backBaudrate)
#發送數據
self.pushButton_3.clicked.connect(self.SendData)
打開串口關閉串口
def SerialSwitch(self):
'''設置標誌位,區分打開關閉串口'''
if self.serial_port and self.serial_port.is_open:
self.closeSerial()
else:
self.StartSerial()
def StartSerial(self):
#讀取當前com口
is_com = self.comboBox.currentText()
if not is_com :
QMessageBox.warning(self,"warning","沒有可用的串口")
return
#讀取的端口從 - 分割開
com = is_com.split(" - ")[0]
#把確認的端口信息賦值給局部變量,後面賦值給串口庫形參
baudrate = self.baudrate
bytesize = int(self.bytesize)
stopbits = serial.STOPBITS_ONE
if self.stopbits_set == '1.5':
stopbits = serial.STOPBITS_ONE_POINT_FIVE
elif self.stopbits_set == '2':
stopbits = serial.STOPBITS_TWO
parity = serial.PARITY_NONE
if self.parity_set == 'Odd':
parity = serial.PARITY_ODD
elif self.parity_set == 'Even':
parity = serial.PARITY_EVEN
elif self.parity_set == 'Mark':
parity = serial.PARITY_MARK
elif self.parity_set == 'Space':
parity = serial.PARITY_SPACE
try:
self.serial_port = serial.Serial(
port=com,
baudrate=baudrate,
bytesize=bytesize,
stopbits=stopbits,
parity=parity,
timeout=0.1 # 讀超時,讓線程可以循環檢查停止標誌
)
except Exception as e:
QMessageBox.critical(self, "錯誤", f"無法打開串口:{e}")
return
#開啓多線程
self.reader_thread = SerialReadThread(self.serial_port)
#判斷ReadSignal信號是否接收到數據,接收到進入數據處理
self.reader_thread.ReadSignal.connect(self.on_data_received)
self.reader_thread.start()
#打開串口後將按鈕改為關閉
self.pushButton_2.setText('關閉串口')
#失能部分按鍵,防止誤觸,出錯
self.set_serial_settings_enabled(False)
def closeSerial(self):
if self.reader_thread:
self.reader_thread.stop()
self.reader_thread = None
if self.serial_port and self.serial_port.is_open:
self.serial_port.close()
self.serial_port = None
self.pushButton_2.setText("打開串口")
self.set_serial_settings_enabled(True)
失能使能模塊方法包裝
def set_serial_settings_enabled(self, enabled):
self.comboBox.setEnabled(enabled)
self.comboBox_2.setEnabled(enabled)
self.pushButton_4.setEnabled(enabled)
self.action20.setEnabled(enabled)
打開串口的時候,關閉其他使能
設置串口 讀 多線程
class SerialReadThread(QThread):
#創建信號 , bytes類型
ReadSignal = Signal(bytes)
def __init__(self,serial_port):
super().__init__()
self.serial_port = serial_port
self.is_running = False
def run(self):
self.is_running = True
while self.is_running and self.serial_port and self.serial_port.is_open:
try:
#接收數據
data = self.serial_port.read(self.serial_port.in_waiting or 1)
if data:
#將接收到的數據連接上信號
self.ReadSignal.emit(data)
except Exception as e:
break
def stop(self):
self.is_running = False
#等待主線程完成,使子線程完全退出
self.wait()
接收數據處理
def on_data_received(self,data):
#狀態欄的變量,接收數據+1
self.receive_byte_count += len(data)
self.update_status_bar()
#判斷hex是否勾選
if self.checkBox_3.isChecked():
hex_str = data.hex().upper()
hex_str = ' '.join(hex_str[i:i+2] for i in range(0, len(hex_str), 2))
self.TextEdit_1.insertPlainText(hex_str + " ")
else:
try:
text = data.decode('utf-8', errors='replace')
except:
text = str(data)
self.TextEdit_1.insertPlainText(text)
#自動滾動
#返回光標
cursor = self.TextEdit_1.textCursor()
#光標移動到最後
cursor.movePosition(cursor.MoveOperation.End)
#頁面到光標處
self.TextEdit_1.setTextCursor(cursor)
子窗口
子窗口
class SettingSerial(QDialog,Ui_Dialog):
def __init__(self,parent = None):
super().__init__(parent)
self.setupUi(self)
self.buttonBox.accepted.connect(self.buttonAccept)
self.buttonBox.rejected.connect(self.reject)
#刷新子窗口com,父子一致
def refreashCom(self,postList,currentPort):
self.comboBox.clear()
self.comboBox.addItems(postList)
if currentPort :
Index = self.comboBox.findText(currentPort)
if Index>=0:
self.comboBox.setCurrentIndex(Index)
#刷新子窗口,父子一致
def refreashBaud(self,baudrate):
if baudrate :
Index = self.comboBox_2.findText(baudrate)
if Index>=0:
self.comboBox_2.setCurrentIndex(Index)
#刷新子窗口,父子一致
def refreashother(self,bytesize,stopbits_set,parity_set):
if bytesize :
Index = self.comboBox_3.findText(bytesize)
if Index>=0:
self.comboBox_3.setCurrentIndex(Index)
if stopbits_set :
Index = self.comboBox_4.findText(stopbits_set)
if Index>=0:
self.comboBox_4.setCurrentIndex(Index)
if parity_set :
Index = self.comboBox_6.findText(parity_set)
if Index>=0:
self.comboBox_6.setCurrentIndex(Index)
def buttonAccept(self):
self.accept()
def backSetting(self):
com = self.comboBox.currentText()#.split(" - ")[0]
return (com
,self.comboBox_2.currentText()
,self.comboBox_3.currentText()
,self.comboBox_4.currentText()
,self.comboBox_6.currentText()
,self.comboBox_5.currentText()
)
父窗口調用子窗口
父窗口按鍵關聯
def open_dialog(self):
dialog = SettingSerial(self)
'''刷新數據'''
self.RefreshSerial()
dialog.refreashCom(self.postList,self.currentPort)
dialog.refreashBaud(self.baudrate)
dialog.refreashother(self.bytesize,self.stopbits_set,self.parity_set)
#使用.exec()方法,等待子窗口點擊確認,此時可以返回一些數據
if dialog.exec() == QDialog.Accepted:
self.settings = dialog.backSetting()
self.settingAccept(self.settings)
返回數據
def settingAccept(self,settings):
self.currentPort,self.baudrate,self.bytesize,self.stopbits_set,self.parity_set,flow_set = settings
if self.currentPort :
Index = self.comboBox.findText(self.currentPort)
if Index>=0:
self.comboBox.setCurrentIndex(Index)
if self.baudrate :
baud = self.comboBox_2.findText(self.baudrate)
if baud>=0:
self.comboBox_2.setCurrentIndex(baud)
發送數據
def SendData(self):
if not self.serial_port or not self.serial_port.is_open:
QMessageBox.warning(self, "警告", "請先打開串口!")
return
#讀取當前發送框有什麼
text = self.TextEdit_2.toPlainText()
if not text:
return
#判斷是不是 使用hex方式
if self.checkBox_3.isChecked():
hex_str = ''.join(text.split())
try:
#.fromhex 轉16進制
data = bytes.fromhex(hex_str)
except ValueError as e:
QMessageBox.warning(self, "錯誤", f"無效的十六進制字符串:{e}")
return
else:
#ASCII發送
data = text.encode('utf-8', errors='ignore')
try:
#返回發送的長度
sent_bytes = self.serial_port.write(data)
#狀態欄
self.send_byte_count += sent_bytes
self.update_status_bar()
except Exception as e:
QMessageBox.critical(self, "錯誤", f"發送失敗:{e}")
運行
if __name__ == "__main__":
app = QApplication()
window = MyWindow()
window.setWindowIcon(QIcon("icon.png"))
window.show()
sys.exit(app.exec())
狀態欄
可以創建QLabel窗口,然後使用.addPermanentWidget(self.label)一直添加到狀態欄右側
def status_bars(self):
'''多狀態欄'''
status_text = f"接收:{self.receive_byte_count} 字節 | 發送:{self.send_byte_count} 字節"
self.serial_status_label = QLabel(status_text)
self.statusBar.addPermanentWidget(self.serial_status_label)
self.textlabel = QLabel(' 天天好心情')
self.statusBar.addPermanentWidget(self.textlabel)
def update_status_bar(self):
"""更新狀態欄顯示:發送/接收字節數"""
# status_text = f"接收:{self.receive_byte_count} 字節 | 發送:{self.send_byte_count} 字節"
# self.statusBar.setStyleSheet("QStatusBar { padding-left: 1000px; }")
# 設置狀態欄文本(showMessage默認顯示5秒,傳0表示永久顯示)
# self.statusBar.showMessage(status_text, 0)
self.serial_status_label.setText(f"接收:{self.receive_byte_count} 字節 | 發送:{self.send_byte_count} 字節")
https://gitee.com/liliww/serial-upper-computer.git