当前位置: 首页 > news >正文

使用pyside6编写简单的串口上位机

使用pyside6编写简单的串口上位机

一、设计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,Signalimport 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)#调用父窗口Uidef 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 = Noneself.reader_thread = Noneself.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.baudratebytesize = int(self.bytesize)stopbits = serial.STOPBITS_ONEif self.stopbits_set == '1.5':stopbits = serial.STOPBITS_ONE_POINT_FIVEelif self.stopbits_set == '2':stopbits = serial.STOPBITS_TWOparity = serial.PARITY_NONEif self.parity_set == 'Odd':parity = serial.PARITY_ODDelif self.parity_set == 'Even':parity = serial.PARITY_EVENelif self.parity_set == 'Mark':parity = serial.PARITY_MARKelif self.parity_set == 'Space':parity = serial.PARITY_SPACEtry: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 = Noneif self.serial_port and self.serial_port.is_open:self.serial_port.close()self.serial_port = Noneself.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_portself.is_running = Falsedef run(self):self.is_running = Truewhile 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:breakdef stop(self):self.is_running = False#等待主线程完成,使子线程完全退出self.wait()

接收数据处理

def on_data_received(self,data):#状态栏的变量,接收数据+1self.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 = settingsif 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}")returnelse:#ASCII发送data = text.encode('utf-8', errors='ignore')try:#返回发送的长度sent_bytes = self.serial_port.write(data)#状态栏self.send_byte_count += sent_bytesself.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} 字节")