记录一下Python+PyQt5开发一个串口助手的过程(二)

第二次更新

说明

上一步已经将基本的串口操作完成,现在可以去肝界面逻辑了。
代码上传至 github仓库:访问请点击此处

创建逻辑代码的主体文件

# 创建类,继承工具生成的界面类
class Main_form_UI(QtWidgets.QMainWindow, QtWidgets.QWidget, Main_form.Ui_MainWindow):
	def __init__(self, parent=None):
		super(Main_form_UI, self).__init__(parent)
        self.setupUi(self)
        
# 实例化
def mywindow():
    mywindow = Main_form_UI()
    mywindow.show()
    return mywindow

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    myobj = mywindow()
    sys.exit(app.exec_())

整理代码的流程图

线程分配

主线程

主线程的定时器

主线程
启动定时器,200ms中断一次

定时器处理

改变
没改变
查询当前的串口设备列表
判断设备列表有没有改变
更新设备列表,且刷新到comboBox_Port中进行显示
接收线程
成功
不成功
从串口中读出1个Byte
将读出的数据存入接收FIFO
发送线程

事件处理

未连接
连接
成功
不成功
连接按键点击事件
判断连接状态
连接串口
停止发送线程
停止接收线程
连接按键文字改为未连接
退出
是否连接成功
设置连接成功标志位
将连接按钮的文字设置为已连接
弹窗报错
停止用于端口扫描的定时器
将串口设置的控件设置为只读
启动接收线程
启动发送线程

自定义线程类

自定义一个线程类用于处理多个线程

import threading
from PyQt5.QtCore import QObject,pyqtSignal

class Runthread(QObject):
    '''
    定义一个线程类,实现了线程的阻塞和释放以及线程停止
    适用于一直循环的线程
    '''

    #定义一个信号
    sendmsg = pyqtSignal(object)

    def __init__(self, run_func):
        super(Runthread, self).__init__()

        self.thread = threading.Thread(target=self.run)
        self.__flag = threading.Event()         # 用于暂停线程的标识
        self.__flag.set()                       # 设置为True
        self.__running = threading.Event()      # 用于停止线程的标识
        self.__running.set()                    # 将running设置为True
        self.__run_func = run_func

    def start(self):
        self.thread.start()
    
    def run(self):
        while self.__running.isSet():
            self.__flag.wait()      # 为True时立即返回, 为False时阻塞直到内部的标识位为True后返回
            self.__run_func(sendmsg=self.sendmsg)

    def pause(self):
        self.__flag.clear()         # 设置为False, 让线程阻塞

    def resume(self):
        self.__flag.set()           # 设置为True, 让线程停止阻塞

    def stop(self):
        self.__running.clear()      # 设置为False
        self.__flag.set()           # 将线程从暂停状态恢复, 如何已经暂停的话
        

if __name__ == "__main__":
    import time
    def run1():
        print("run1 sleep 1s.")
        time.sleep(1)

    t1 = Runthread(run1)
    t1.start()
    time.sleep(8)
    t1.pause()
    print("阻塞了")
    time.sleep(10)
    t1.resume()
    print("解除了")
    time.sleep(10)
    t1.stop()
    print("停止了")
    time.sleep(10)
    t1 = Runthread(run1)
    t1.start()
    time.sleep(10)

    print("程序退出")
  • 这里定义了一个类,用于对创建的线程进行管理,加入了线程阻塞和线程停止的方法,适用于需要一直循环的任务函数。
  • 在类的初始化代码里接收一个函数参数,此函数为线程的执行函数。
 def __init__(self, run_func):
		# 。。。。。。
		# 设置一个参数接收线程的处理函数
        self.__run_func = run_func
  • 由于使用的是PYQT5,这里在线程中定义一个信号,用于给主线程发送信号
sendmsg = pyqtSignal(object)
  • 同时在定义线程运行函数时,需要加入此信号的参数,在线程类中调用时将信号传入。
self.__run_func(sendmsg=self.sendmsg)

点击连接按钮的处理

  • 这里在点击连接按钮后,获取界面上的各个控件的设置信息,进行配置串口并打开串口。
  • 串口打开以后,创建串口接收线程,串口发送线程,数据处理线程。
  • 当断开串口时,相应的停止各个线程
	def connecthandle(self):
        # 点击连接按键后的操作

        if self.ComTool_status['isConnect'] is False:
            # 获取状态
            self._get_current_com_status()
            
            if self.ComTool_status['dev_name'] == '':
                # 当前没有选中任何设备,直接弹窗报错
                QtWidgets.QMessageBox.warning(self,'串口设备选择错误', '串口设备没有插入。', QtWidgets.QMessageBox.Ok)
                return

            # 打开串口
            try:
                self.com_dev.open(self.ComTool_status['dev_name'],
                              int(self.ComTool_status['baud_rate']),
                              int(self.ComTool_status['data_bits']),
                              self.ComTool_status['parity'],
                              int(self.ComTool_status['stop_bits']))
            except:
                QtWidgets.QMessageBox.warning(self,'串口设备错误', '串口设备被占用。', QtWidgets.QMessageBox.Ok)
                return

            
            # 停止串口扫描的定时器
            self.scan_uart_timer.stop()

            # 改变连接按键的显示文本
            self.pushButton_connect.setText('断开')

            # 设置连接状态
            self.ComTool_status['isConnect'] = True

            # 创建串口接收和发送的线程
            self.thread_rx = Runthread.Runthread(self.com_dev.com_rxHandler)
            self.thread_tx = Runthread.Runthread(self.com_dev.com_txHandler)
            self.thread_rxData = Runthread.Runthread(self.rxDataHandler)
            self.thread_rxData.sendmsg.connect(self.display) # 将接收数据处理的线程信号连接到此处
            self.thread_rx.start()
            self.thread_tx.start()
            self.thread_rxData.start()

        else:
            self.thread_rx.stop()
            self.thread_rx.stop()
            self.thread_rxData.stop()
            self.thread_rxData.sendmsg.disconnect(self.display)

            # 关闭串口
            self.com_dev.close()

            # 设置定时器的间隔时间并启动定时器
            self.scan_uart_timer.start(500) 
            self.pushButton_connect.setText('连接')
            
            # 设置连接状态
            self.ComTool_status['isConnect'] = False

接收数据处理线程

  • 从接收队列中将队列读空,根据当前的HEX或者ASCII控件状态,来确定当前的显示。
  • 将需要显示的字符串通过信号的方式发送给主线程
  • 主线程中接收到信号事件,直接进行显示
    def rxDataHandler(self,sendmsg = None):
        # 从接收缓存中获取数据进行处理
        rx_data = []

        # 从接收缓存中读空数据
        while not self.com_dev.rx_queue.empty():
            rx_data.append(self.com_dev.rx_queue.get_nowait())
        
        if len(rx_data) != 0:
            if self.radioButton_rev_hex.isChecked():
                display_str = ''
                # 将接收到的数据按照hex格式显示
                hex_rx_data = self.hex_handler.byte_to_hexString(rx_data)

                # 将字符串追加到显示区
                for str in hex_rx_data:
                    display_str += str+' '

                sendmsg.emit(display_str) # 将数据发送到显示函数中进行显示
            else:
                # 将接收的数据以ascii字符串的形式保存
                if self.comboBox_encode.currentText() == 'UTF-8':
                    utf8_rx_data = self.hex_handler.byte_to_utf8str(rx_data)
                    sendmsg.emit(utf8_rx_data) # 将数据发送到显示函数中进行显示

        else:
            time.sleep(0.08)


    def display(self, msg):
        self.plainTextEdit_rev.insertPlainText(msg)
        self.plainTextEdit_rev.moveCursor(self.plainTextEdit_rev.textCursor().End)

至此接收初步完成。

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是使用Python和PyQt制作串口通信助手的基本步骤: 1. 安装PyQt:如果您还没有安装PyQt,则需要先安装它。您可以通过以下命令安装: ```python pip3 install pyqt5 ``` 2. 创建GUI:使用Qt Designer创建GUI界面。您可以使用Qt Designer创建GUI并保存为.ui文件。您还可以使用PyQt5.uic模块将.ui文件转换为Python代码。 3. 编写Python代码:在Python中编写代码以处理GUI界面。您需要使用PyQt5.QtSerialPort模块连接到串口设备,并使用PyQt5.QtWidgets模块创建GUI元素。 以下是一个简单的示例代码,显示如何使用PyQt5创建串口通信助手: ```python import sys import serial from PyQt5.QtWidgets import QApplication, QMainWindow from PyQt5.QtSerialPort import QSerialPortInfo, QSerialPort class SerialAssistant(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("Serial Assistant") self.setGeometry(100, 100, 800, 600) # Create a serial port object self.serial_port = QSerialPort() # Create GUI elements self.text_box = QTextEdit(self) self.text_box.move(20, 20) self.text_box.resize(760, 500) self.connect_button = QPushButton("Connect", self) self.connect_button.move(20, 530) self.connect_button.clicked.connect(self.connect) self.disconnect_button = QPushButton("Disconnect", self) self.disconnect_button.move(120, 530) self.disconnect_button.clicked.connect(self.disconnect) self.send_button = QPushButton("Send", self) self.send_button.move(220, 530) self.send_button.clicked.connect(self.send) self.clear_button = QPushButton("Clear", self) self.clear_button.move(320, 530) self.clear_button.clicked.connect(self.clear) self.baud_rate_box = QComboBox(self) self.baud_rate_box.move(450, 530) self.baud_rate_box.addItems(["9600", "115200"]) self.port_box = QComboBox(self) self.port_box.move(550, 530) self.port_box.addItems([port.portName() for port in QSerialPortInfo.availablePorts()]) def connect(self): # Set the serial port settings self.serial_port.setPortName(self.port_box.currentText()) self.serial_port.setBaudRate(int(self.baud_rate_box.currentText())) # Open the serial port if self.serial_port.open(QSerialPort.ReadWrite): self.text_box.append("Connected to {}".format(self.serial_port.portName())) else: self.text_box.append("Failed to connect to {}".format(self.serial_port.portName())) def disconnect(self): # Close the serial port self.serial_port.close() self.text_box.append("Disconnected from {}".format(self.serial_port.portName())) def send(self): # Write data to the serial port data = self.text_box.toPlainText().encode() self.serial_port.write(data) def clear(self): # Clear the text box self.text_box.clear() if __name__ == "__main__": app = QApplication(sys.argv) window = SerialAssistant() window.show() sys.exit(app.exec_()) ``` 在代码中,我们使用QMainWindow类创建应用程序窗口,并在其上添加了几个QPushButton和QComboBox元素。我们还使用QTextEdit创建一个文本框,以显示串口通信的数据。 在connect()、disconnect()、send()和clear()方法中,我们使用PyQt5.QtSerialPort模块中的QSerialPort类连接到串口设备,并读取和写入数据。 当我们运行该应用程序时,它将显示一个窗口,其中包含连接到串口设备的选项,以及文本框,用于显示串口通信的数据。用户可以使用按钮将应用程序连接到串口设备,发送和接收数据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Berte_Yu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值