找回密码
 立即注册
首页 业界区 安全 使用pyside6编写简单的串口上位机

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

蚣澡 昨天 20:55
一、设计UI界面

使用qt设计师

需要注意的是
调整整体大小

最后设置整体自动调整大小时,对于QMainWindow窗口需要右键最基本的框,选择布局,不然在右侧是没有布局选项的
窗口放大时非对话框有最大大小,不跟着变大

设置对应窗口的对齐为 0

按钮进入对齐后选择minimum,可以变大


默认时

二、编写逻辑

编写基本框架

选择自己使用的是QMainWindow还是QWidget
导入pyside的库
  1. from PySide6.QtWidgets import QApplication,QMainWindow,QWidget
复制代码
引用对应界面库
  1. #ui为上一层目录
  2. from ui.Ui_serial_up import Ui_MainWindow
复制代码
  1. class MyWindow(QMainWindow,Ui_MainWindow):
  2.     def __init__(self):
  3.         super().__init__()
  4.         self.setupUi(self)
  5. if __name__ == "__main__":
  6.     app = QApplication()
  7.     window = MyWindow()
  8.     window.show()
  9.     sys.exit(app.exec())
复制代码
逻辑部分

导入串口所需库
  1. from PySide6.QtCore import QThread,Signal
  2. import serial
  3. import serial.tools.list_ports
复制代码
配置大体框架
  1. #接收线程
  2. class SerialReadThread(QThread):
  3.     ReadSignal = Signal(str) #接收信号
  4.     def __init__(self,serial_obj):
  5.         super().__init__()
  6.         
  7.     def run():
  8.    
  9.     def stop():
复制代码
子窗口

子窗口现在qt设计师设计好,使用QSialog类型
  1. #子窗口界面
  2. class SettingSerial(QDialog,Ui_Dialog):
  3.     def __init__(self,parent = None):#parent 为传入形参接口
  4.         super().__init__(parent)
  5.         self.setupUi(self)#调用子窗口Ui
  6.         #绑定自带的确认和取消键
  7.         #连接固定的确定取消方法
  8.         #self.reject()和self.accept()是取消和确认方法
  9.         self.buttonBox.accepted.connect(self.buttonAccept)
  10.         self.buttonBox.rejected.connect(self.reject)
  11.         
  12.     def buttonAccept(self):
  13.         self.accept()
  14.     def backSetting(self):
  15.     '''子窗口返回方法,同与父窗口调用后关闭子窗口时调用'''
复制代码
父窗口框架
  1. #界面
  2. class MyWindow(QMainWindow,Ui_MainWindow):
  3.     def __init__(self):
  4.         super().__init__()
  5.         self.setupUi(self)#调用父窗口Ui
  6.         
  7.     def SerialSend(self):
  8.    
  9.     def SerialReceive(self):
  10.    
  11.     def SerialStart(self):
  12.    
  13.     def SerialStop(self):
  14.    
  15.     def ClearTx(self):
  16.    
  17.     def ClearRx(self):
  18.    
复制代码
编写代码

首先编写主界面的基本操作
  1. #清除
  2. def clearTx(self):
  3.         self.TextEdit_2.clear()
  4.    
  5.     def clearRx(self):
  6.         self.TextEdit_1.clear()
复制代码
  1. #变量定义
  2. def initVariable(self):
  3.     self.baudrate = "115200"
  4.     self.bytesize = '8'
  5.     self.stopbits_set = '1'
  6.     self.parity_set = 'None'
  7.     # 串口对象
  8.     self.serial_port = None
  9.     self.reader_thread = None
  10.     self.send_byte_count = 0  #发送字节总数
  11.     self.receive_byte_count = 0  #接收字节总数
复制代码
  1. #按键关联方法
  2. def ConnectButton(self):
  3.     #关联按键
  4.     self.pushButton_5.clicked.connect(self.clearTx)
  5.     self.pushButton.clicked.connect(self.clearRx)
  6.     #打开串口
  7.     self.pushButton_2.clicked.connect(self.SerialSwitch)
  8.     #刷新端口
  9.     self.action10.setShortcut('F5')
  10.     self.action10.setStatusTip('刷新端口')
  11.     self.action10.triggered.connect(self.RefreshSerial)
  12.     #串口设置
  13.     self.pushButton_4.clicked.connect(self.open_dialog)
  14.     self.action20.setStatusTip('配置串口')
  15.     self.action20.triggered.connect(self.open_dialog)
  16.     #更改baud下拉栏
  17.     self.comboBox_2.currentIndexChanged.
  18.         connect(self.backBaudrate)
  19.     #发送数据
  20.     self.pushButton_3.clicked.connect(self.SendData)
复制代码
打开串口关闭串口
  1. def SerialSwitch(self):
  2.     '''设置标志位,区分打开关闭串口'''
  3.     if self.serial_port and self.serial_port.is_open:
  4.         self.closeSerial()
  5.     else:
  6.         self.StartSerial()
  7. def StartSerial(self):
  8.     #读取当前com口
  9.     is_com = self.comboBox.currentText()
  10.     if not is_com :
  11.         QMessageBox.warning(self,"warning","没有可用的串口")
  12.         return
  13.     #读取的端口从 - 分割开
  14.     com = is_com.split(" - ")[0]
  15.     #把确认的端口信息赋值给局部变量,后面赋值给串口库形参
  16.     baudrate = self.baudrate
  17.     bytesize = int(self.bytesize)
  18.     stopbits = serial.STOPBITS_ONE
  19.     if self.stopbits_set == '1.5':
  20.         stopbits = serial.STOPBITS_ONE_POINT_FIVE
  21.     elif self.stopbits_set == '2':
  22.         stopbits = serial.STOPBITS_TWO
  23.     parity = serial.PARITY_NONE
  24.     if self.parity_set == 'Odd':
  25.         parity = serial.PARITY_ODD
  26.     elif self.parity_set == 'Even':
  27.         parity = serial.PARITY_EVEN
  28.     elif self.parity_set == 'Mark':
  29.         parity = serial.PARITY_MARK
  30.     elif self.parity_set == 'Space':
  31.         parity = serial.PARITY_SPACE
  32.     try:
  33.         self.serial_port = serial.Serial(
  34.             port=com,
  35.             baudrate=baudrate,
  36.             bytesize=bytesize,
  37.             stopbits=stopbits,
  38.             parity=parity,
  39.             timeout=0.1   # 读超时,让线程可以循环检查停止标志
  40.         )
  41.     except Exception as e:
  42.         QMessageBox.critical(self, "错误", f"无法打开串口:{e}")
  43.         return
  44.     #开启多线程
  45.     self.reader_thread = SerialReadThread(self.serial_port)
  46.     #判断ReadSignal信号是否接收到数据,接收到进入数据处理
  47.     self.reader_thread.ReadSignal.connect(self.on_data_received)
  48.     self.reader_thread.start()
  49.     #打开串口后将按钮改为关闭
  50.     self.pushButton_2.setText('关闭串口')
  51.     #失能部分按键,防止误触,出错
  52.     self.set_serial_settings_enabled(False)
  53. def closeSerial(self):
  54.     if self.reader_thread:
  55.         self.reader_thread.stop()
  56.         self.reader_thread = None
  57.     if self.serial_port and self.serial_port.is_open:
  58.         self.serial_port.close()
  59.     self.serial_port = None
  60.     self.pushButton_2.setText("打开串口")
  61.     self.set_serial_settings_enabled(True)
复制代码
失能使能模块方法包装
  1. def set_serial_settings_enabled(self, enabled):
  2.     self.comboBox.setEnabled(enabled)
  3.     self.comboBox_2.setEnabled(enabled)
  4.     self.pushButton_4.setEnabled(enabled)
  5.     self.action20.setEnabled(enabled)
复制代码

打开串口的时候,关闭其他使能
设置串口 读 多线程
  1. class SerialReadThread(QThread):
  2.     #创建信号 , bytes类型
  3.     ReadSignal = Signal(bytes)
  4.     def __init__(self,serial_port):
  5.         super().__init__()
  6.         self.serial_port = serial_port
  7.         self.is_running = False
  8.     def run(self):
  9.         self.is_running = True
  10.         while self.is_running and self.serial_port and self.serial_port.is_open:
  11.             try:
  12.             #接收数据
  13.                 data = self.serial_port.read(self.serial_port.in_waiting or 1)
  14.                 if data:
  15.                     #将接收到的数据连接上信号
  16.                     self.ReadSignal.emit(data)
  17.             except Exception as e:
  18.                 break
  19.         
  20.     def stop(self):
  21.         self.is_running = False
  22.         #等待主线程完成,使子线程完全退出
  23.         self.wait()
复制代码
接收数据处理

  1. def on_data_received(self,data):
  2.     #状态栏的变量,接收数据+1
  3.     self.receive_byte_count += len(data)
  4.     self.update_status_bar()
  5.     #判断hex是否勾选
  6.     if self.checkBox_3.isChecked():
  7.         hex_str = data.hex().upper()
  8.         hex_str = ' '.join(hex_str[i:i+2] for i in range(0, len(hex_str), 2))
  9.         self.TextEdit_1.insertPlainText(hex_str + " ")
  10.     else:
  11.         try:
  12.             text = data.decode('utf-8', errors='replace')
  13.         except:
  14.             text = str(data)
  15.         self.TextEdit_1.insertPlainText(text)
  16.     #自动滚动
  17.     #返回光标
  18.     cursor = self.TextEdit_1.textCursor()
  19.     #光标移动到最后
  20.     cursor.movePosition(cursor.MoveOperation.End)
  21.     #页面到光标处
  22.     self.TextEdit_1.setTextCursor(cursor)
复制代码
子窗口


子窗口
  1. class SettingSerial(QDialog,Ui_Dialog):
  2.     def __init__(self,parent = None):
  3.         super().__init__(parent)
  4.         self.setupUi(self)
  5.         self.buttonBox.accepted.connect(self.buttonAccept)
  6.         self.buttonBox.rejected.connect(self.reject)
  7.     #刷新子窗口com,父子一致
  8.     def refreashCom(self,postList,currentPort):
  9.         self.comboBox.clear()
  10.         self.comboBox.addItems(postList)
  11.         if currentPort :
  12.             Index = self.comboBox.findText(currentPort)
  13.             if Index>=0:
  14.                 self.comboBox.setCurrentIndex(Index)
  15.     #刷新子窗口,父子一致
  16.     def refreashBaud(self,baudrate):
  17.         if baudrate :
  18.             Index = self.comboBox_2.findText(baudrate)
  19.             if Index>=0:
  20.                 self.comboBox_2.setCurrentIndex(Index)
  21.     #刷新子窗口,父子一致
  22.     def refreashother(self,bytesize,stopbits_set,parity_set):
  23.         if bytesize :
  24.             Index = self.comboBox_3.findText(bytesize)
  25.             if Index>=0:
  26.                 self.comboBox_3.setCurrentIndex(Index)
  27.         if stopbits_set :
  28.             Index = self.comboBox_4.findText(stopbits_set)
  29.             if Index>=0:
  30.                 self.comboBox_4.setCurrentIndex(Index)
  31.         if parity_set :
  32.             Index = self.comboBox_6.findText(parity_set)
  33.             if Index>=0:
  34.                 self.comboBox_6.setCurrentIndex(Index)
  35.    
  36.     def buttonAccept(self):
  37.         self.accept()
  38.     def backSetting(self):
  39.         com = self.comboBox.currentText()#.split(" - ")[0]
  40.         return (com
  41.                 ,self.comboBox_2.currentText()
  42.                 ,self.comboBox_3.currentText()
  43.                 ,self.comboBox_4.currentText()
  44.                 ,self.comboBox_6.currentText()
  45.                 ,self.comboBox_5.currentText()
  46.                 )     
复制代码
父窗口调用子窗口

父窗口按键关联
  1. def open_dialog(self):
  2.     dialog = SettingSerial(self)
  3.     '''刷新数据'''
  4.     self.RefreshSerial()
  5.     dialog.refreashCom(self.postList,self.currentPort)
  6.     dialog.refreashBaud(self.baudrate)
  7.     dialog.refreashother(self.bytesize,self.stopbits_set,self.parity_set)
  8.     #使用.exec()方法,等待子窗口点击确认,此时可以返回一些数据
  9.     if dialog.exec() == QDialog.Accepted:
  10.         self.settings = dialog.backSetting()
  11.         self.settingAccept(self.settings)
复制代码
返回数据
  1. def settingAccept(self,settings):
  2.     self.currentPort,self.baudrate,self.bytesize,self.stopbits_set,self.parity_set,flow_set = settings
  3.     if self.currentPort :
  4.         Index = self.comboBox.findText(self.currentPort)
  5.         if Index>=0:
  6.             self.comboBox.setCurrentIndex(Index)
  7.     if self.baudrate :
  8.         baud = self.comboBox_2.findText(self.baudrate)
  9.         if baud>=0:
  10.             self.comboBox_2.setCurrentIndex(baud)
复制代码
发送数据
  1. def SendData(self):
  2.     if not self.serial_port or not self.serial_port.is_open:
  3.         QMessageBox.warning(self, "警告", "请先打开串口!")
  4.         return
  5.     #读取当前发送框有什么
  6.     text = self.TextEdit_2.toPlainText()
  7.     if not text:
  8.         return
  9.     #判断是不是 使用hex方式
  10.     if self.checkBox_3.isChecked():
  11.         hex_str = ''.join(text.split())
  12.         try:
  13.             #.fromhex 转16进制
  14.             data = bytes.fromhex(hex_str)
  15.         except ValueError as e:
  16.             QMessageBox.warning(self, "错误", f"无效的十六进制字符串:{e}")
  17.             return
  18.     else:
  19.         #ASCII发送
  20.         data = text.encode('utf-8', errors='ignore')
  21.     try:
  22.         #返回发送的长度
  23.         sent_bytes = self.serial_port.write(data)
  24.         #状态栏
  25.         self.send_byte_count += sent_bytes
  26.         self.update_status_bar()
  27.     except Exception as e:
  28.         QMessageBox.critical(self, "错误", f"发送失败:{e}")
复制代码
运行
  1. if __name__ == "__main__":
  2.     app = QApplication()
  3.     window = MyWindow()
  4.     window.setWindowIcon(QIcon("icon.png"))
  5.     window.show()
  6.     sys.exit(app.exec())
复制代码
状态栏


可以创建QLabel窗口,然后使用.addPermanentWidget(self.label)一直添加到状态栏右侧
  1. def status_bars(self):
  2.     '''多状态栏'''
  3.     status_text = f"接收:{self.receive_byte_count} 字节 | 发送:{self.send_byte_count} 字节"
  4.     self.serial_status_label = QLabel(status_text)
  5.     self.statusBar.addPermanentWidget(self.serial_status_label)
  6.     self.textlabel = QLabel(' 天天好心情')
  7.     self.statusBar.addPermanentWidget(self.textlabel)
  8. def update_status_bar(self):
  9.     """更新状态栏显示:发送/接收字节数"""
  10.     # status_text = f"接收:{self.receive_byte_count} 字节 | 发送:{self.send_byte_count} 字节"
  11.     # self.statusBar.setStyleSheet("QStatusBar { padding-left: 1000px; }")
  12.     # 设置状态栏文本(showMessage默认显示5秒,传0表示永久显示)
  13.     # self.statusBar.showMessage(status_text, 0)
  14.     self.serial_status_label.setText(f"接收:{self.receive_byte_count} 字节 | 发送:{self.send_byte_count} 字节")
复制代码
https://gitee.com/liliww/serial-upper-computer.git

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册