物联网驿站

PyQt5快速上手应用篇2-打通串口工具数据交互

2020-04-03  本文已影响0人  一叶孤沙

前言

本节我们继续使用Pyqt5开发串口工具,目标是打通串口工具数据交互功能,实现串口工具核心功能,细节部分将在之后章节会继续优化。

一、实例

1. QT Designer修改UI

打开应用篇1中uart_tool.ui,首先修改串口QCombox配置信息,然后修改textBrowser为Text Edit,这是由于textBrowser只能显示数据,不能获取输入,所以本节修改为Text Edit,以便获取输入信息,并发送给串口设备。

image

UI界面修改完成后,接下里需要搞清楚界面中各个组件的名字和代码如何对应,我们以串口为例。

image

同理,工具栏中对应关系如下:

image

2 将*.ui转换为ui_uart_tool.py

进入run.py目录,输入如下命令:

pyuic5-oui_uart_tool.pyuart_tool.ui

3 编写python程序

正式编写程序之前,我们需要明确下本节具体需求,打开软件时候,首先从配置文件config.ini中获取串口配置信息,然后渲染到软件显示;接着点击工具栏中开始图标,调用pyserial包连接串口设备,然后再输入text Edit中输入信息,点击发送按钮,textBrowser中显示收到的串口信息。

3.1 配置信息处理

配置信息处理需要参考”Pyqt5快速上手基础篇10-QSettings用法“,config.ini文件中设置了默认的串口信息(波特率9600,数据位8位,停止位一位,无奇偶校验,无流控),内容如下所示:

[SETUP]
BAUD_VALUE=9600
DATABIT_VALUE=8
POLARITY_VALUE=none
STOPBIT_VALUE=1
FLOW_VALUE=none

核心代码如下:

        self.settings = qc.QSettings("config.ini", qc.QSettings.IniFormat)
        # self.com = self.settings.value("SETUP/COM_VALUE")
        self.baud = self.settings.value("SETUP/BAUD_VALUE", 0, type=int)
        self.databit = self.settings.value("SETUP/DATABIT_VALUE")
        self.polarity = self.settings.value("SETUP/POLARITY_VALUE")
        # odd even  none
        if (self.polarity == "odd"):
            self.polarity = "奇校验"
        elif(self.polarity == "even"):
            self.polarity = "偶校验"
        elif(self.polarity == "none"):
            self.polarity = "无"
        self.stopbit = self.settings.value("SETUP/STOPBIT_VALUE")
        self.flow =  self.settings.value("SETUP/FLOW_VALUE")
        if (self.flow == "cts"):
            self.flow =  "RTS/CTS"
            print("odd check taest")
        elif(self.flow == "xon"):
            self.flow =  "XON/XOFF"
        elif(self.flow == "none"):
            self.flow =  "无"      

3.2 串口工具通信架构

本工具逻辑上分为三层,分别是最上层的UI层、最底层串口通信层、中间层的逻辑处理层,其中UI层负责获取用户输入,同时更新UI信息;串口通信层负责和串口设备通信,包括建立连接、收发数据、关闭连接等;逻辑处理层起到承上启下的作用,负责连接UI层和串口通信层,同时该层完成业务核心逻辑的处理。

(1)下行数据处理

下行数据是指串口工具发送数据给串口设备的过程,逻辑比较简单,最上层class myForm对象实例化的时候,会同时实例化中间层Tool对象,

self.tool = tool.Tool(self, logger.info)#串口工具逻辑处理对象

中间层Tool实例化后,上层打开串口时候,也会实例化底层Uart对象,

self.uart = uart.Uart(port,self.parent.baud,self.log)

因此最上层需要发送数据时候,直接调用如下函数即可:

self.tool.send_data_fun(send_text.encode())

(2) 上行数据处理

上行数据是指串口设备发送数据给串口工具并显示在textBrowser的过程,class Uart(object)类定义了最底层串口通信层的方法,实例化时候,其构造函数会同时创建recv_data_thread()和send_data_thread()两个线程,其中recv_data_thread()线程负责接收串口设备发来的数据,核心处理代码如下:

  def recv_data_thread():
            while True:
                try:
                    if (1 == self.run_status):
                        nums = self.uart.inWaiting()
                        if (nums > 0):
                            recv_msg = self.uart.read(nums)
                        else:
                            continue
                        if self.recv_queue.full():
                            self.recv_queue.get()
                        self.recv_queue.put(recv_msg.decode())
                        data = "[module->pc] " +str(recv_msg)
                        print(data)
                        # self.log(data)
                    sleep(0.05)
                except Exception as e:
                    self.log("Error")
                    self.log(e)

从上述代码中,可以看到,串口通信层收到数据后会放入recv_queue队列中,现在有个问题是如何将队列中的数据实时的发给UI界面显示呢?

本文处理方法是利用中间层class Tool(object)对象来传递消息,Tool对象初始化时候,其构造函数中会自动开启一个tool_logic_thread线程,当线程检测到UI层工具栏开启图标被点击后,会实例化Uart对象,同时开启一个数据接收线程recv_data_thread(),该线程负责将收到的队列中的数据读取出来,同时发送给UI的TextBrowser显示。

    def tool_logic_thread(self):
        self.log("start tool logic thread")
        while (True):
            if (1 == self.parent.uart_start_status and 0 == self.run_status):
                port = self.parent.comboBox_uart.currentText()
                self.uart = uart.Uart(port, self.parent.baud, self.log)
                self.run_status = 1
                # start recv data thread
                threading.Thread(target=self.recv_data_thread, daemon=True).start()
            elif(2 == self.parent.uart_start_status):
                pass
            elif(0 == self.parent.uart_start_status):
                self.close_uart()
            sleep(0.1)

    def recv_data_thread(self):
        self.log("start recv_data_thread")
        while (True):
            if (False == self.uart.recv_queue.empty()):
                data = self.uart.recv_queue.get()
                self.parent.signal_recv_data.emit(data)
            sleep(0.1)

注意收到的数据发送给UI的textBrowser用到了自定义信号量signal_recv_data,当中间层Tool触发信号后,UI层对应槽函数会响应。

     self.signal_recv_data.connect(self.update_textbrowser_recv_cb)#连接信号和槽函数
    #槽函数
    def update_textbrowser_recv_cb(self, data):
        self.textBrowser_recv.insertPlainText(data)
        cursor = self.textBrowser_recv.textCursor()
        self.textBrowser_recv.moveCursor(cursor.End)

二、运行

1.硬件连接:

image

2.运行:

进入文件目录,输入python3 run.py,即可弹出上述用QT Designer设计出来的MainWindow页面。

image

然后选择端口,点击工具栏中开始按钮:

image

接着在Text Edit框中输入hello world并点击发送按钮。


image

可以看到textBrowser显示了从串口设备接收的数据,至此我们打通了数据通信的道路。

三、结语

(1)自定义信号

本节用到了自定义信号,之后再基础篇会更新。

一叶孤沙出品:一沙一世界,一叶一菩提

上一篇下一篇

猜你喜欢

热点阅读