pyqt5+pyserial 实现简单的串口测试工具

前言

最近工作中需要一个产测工具,主要是用于测试模块串口通信功能是否正常,刚好最近在学习python,所以就用pyserial+pyqt5实现了一个简单的产测工具。主要功能如下:
1. 可以记录串口设置,这个功能是用configparser模块实现的,主要逻辑是在程序启动时读取配置文件,并设置界面。在打开串口时,保存配置。
2. 测试串口通信是否ok,是用pyserial 模块实现,主要逻辑是开启串口后,每200ms发送一测试指令,共发送5次,若在发送超时前检测到特定返回指令,则提示测试成功,否则提示测试失败。
产测工具预期界面

界面设计

程序界面主要分为两块,一个用于显示测试结果,另一个用于设置和控制串口。鉴于此,想到用QGroupBox分别布置两块布局,然后把两个组添加到主布局。
1. log_view_groupBox:用于显示测试结果的QGroupBox,里面只有一个组件QTextBrowser用于显示结果。
2. serial_setting_groupBox:用于设置和控制串口,以及启动测试。考虑到这个布局组件比较多,而且可复用性很高,所以把它抽象成一个单独的类SerialSettingLayout,其中包括了对串口的操作,像串口开启和关闭,串口数据读写等,以及串口设置界面布局和配置文件读写。使用时,只需实例化类,并调用类实例的get_serial_setting_layout()方法可以直接返回QVBoxLayout,使用起来方便灵活。

类SerialSettingLayout分析

SerialSettingLayout类只负责生成串口设置界面布局,显示效果类似下图:
SerialSettingLayout类所生成的布局SerialSettingLayout类在生成串口设置布局的同时,也对布局上的按钮事件提供了默认的处理。比如,点击刷新串口会刷新可选择的端口,点击打开串口会自动打开相应串口,同时会保存当前设置,以便下次启动自动填充上次的设置。而且SerialSettingLayout类内部也封装了串口的操作,使用时直接调用相应接口即可,不用考虑错误处理。

#!/user/bin/python3
# -*- coding:utf-8 -*-
'''
串口工具设置面板
'''

__author__ = 'Duanlin D'

import sys
import os
import serial
import serial.tools.list_ports
import configparser

from PyQt5.QtWidgets import (QWidget, QApplication, QComboBox, QLabel, QPushButton, QHBoxLayout,
                             QVBoxLayout, QToolTip, QMessageBox)
from PyQt5.QtGui import (QIcon, QFont)


class SerialSettingLayout(QWidget):

    def __init__(self, open_callback=None, close_callback=None):
        """
        SerialSettingLayout构造函数
        :param open_callback: 串口打开时,执行的回调
        :param close_callback: 串口关闭时,执行的回调
        """
        super(SerialSettingLayout, self).__init__()

        # 初始化serial对象,用于串口通信
        self.serial = serial.Serial()

        # 串口设置界面
        self.open_close_button = QPushButton(u'打开串口')   # 打开或关闭串口按钮
        self.refresh_button = QPushButton(u'刷新串口')      # 刷新串口按钮

        self.serial_setting_layout = QVBoxLayout()      # 串口设置界面布局,垂直布局

        self.serial_parity_comboBox = QComboBox()       # 奇偶校验下拉列表
        self.serial_stopBits_comboBox = QComboBox()     # 停止位下拉列表
        self.serial_data_comboBox = QComboBox()         # 数据位下拉列表
        self.serial_baudRate_comboBox = QComboBox()     # 波特率下拉列表
        self.serial_COM_comboBox = QComboBox()          # 串口号下拉列表

        # 串口设置信息 保存和读取
        self.cfg_serial_dic = {}        # 用于保存串口设置信息的字典
        self.current_path = os.path.dirname(os.path.realpath(__file__))     # 当前目录
        self.cfg_path = ''  # 配置文件的路径
        self.cfg_dir = 'settings'   # 配置文件目录
        self.conf_file_name = "cfg.ini"     # 配置文件名
        self.confParse = configparser.ConfigParser()    # 配置文件解析ConfigParser对象
        self.is_serial_open = False     # 串口状态,默认关闭

        self.serial_open_callback = open_callback   # 串口打开时执行的回调
        self.serial_close_callback = close_callback     # 串口关闭时执行的回调
        self.init_ui()      # 初始化界面布局

    def init_ui(self):
        """
        初始化串口设置界面布局
        :return:
        """
        self.read_config()      # 读取串口设置信息
        self.init_setting_layout()  # 初始化串口界面布局

    def read_config(self):
        """
        读取串口配置
        :return:
        """
        self.cfg_path = os.path.join(self.current_path, self.cfg_dir, self.conf_file_name)  # 获取配置文件路径
        # 判断读取配置文件是否正常
        if self.confParse.read(self.cfg_path, encoding='utf-8'):

            # 判断读取section是否正常
            try:
                items = self.confParse.items('serial_setting')      # 获取 serial_setting section,返回字典
                self.cfg_serial_dic = dict(items)
                # print(self.cfg_serial_dic)
            # 未找到section
            except configparser.NoSectionError:
                self.confParse.add_section('serial_setting')    # 添加section
                self.confParse.write(open(self.cfg_path, 'w'))  # 保存到配置文件
        # 异常
        else:
            # 判断目录是否存在,不存在的话新建目录
            if not os.path.exists(os.path.join(self.current_path, self.cfg_dir)):
                os.mkdir(os.path.join(self.current_path, self.cfg_dir))

            self.confParse.add_section('serial_setting')        # 添加section
            self.confParse.write(open(self.cfg_path, 'w'))      # 保存到配置文件

    def init_setting_layout(self):
        """
        初始化串口设置组
        :return:
        """
        # 串口设置界面初始化
        serial_com_label = QLabel(u'串口号')
        self.serial_COM_comboBox.addItems(self.get_port_list())
        self.serial_COM_comboBox.setCurrentText(self.cfg_serial_dic.get('com', 'COM1'))     # 选择默认端口

        serial_baud_rate_label = QLabel(u'波特率')
        self.serial_baudRate_comboBox.addItems(['100', '300', '600', '1200', '2400', '4800', '9600', '14400', '19200',
                                                '38400', '56000', '57600', '115200', '128000', '256000'])
        self.serial_baudRate_comboBox.setCurrentText(self.cfg_serial_dic.get('baudrate', '9600'))   # 选择默认波特率

        serial_data_label = QLabel(u'数据位')
        self.serial_data_comboBox.addItems(['5', '6', '7', '8'])
        self.serial_data_comboBox.setCurrentText(self.cfg_serial_dic.get('data', '8'))      # 选择默认数据位

        serial_stop_bits_label = QLabel(u'停止位')
        self.serial_stopBits_comboBox.addItems(['1', '1.5', '2'])
        self.serial_stopBits_comboBox.setCurrentText(self.cfg_serial_dic.get('stopbits', '1'))  # 选择默认停止位

        serial_parity_label = QLabel(u'奇偶校验位')
        self.serial_parity_comboBox.addItems(['N', 'E', 'O', 'M', 'S'])
        self.serial_parity_comboBox.setCurrentText(self.cfg_serial_dic.get('parity', 'N'))      # 选择默认奇偶校验位

        # 设置按钮
        QToolTip.setFont(QFont('SansSerif', 10))    # 设置按钮提示样式
        self.open_close_button.setToolTip("open or close selected port")    # 设置打开或关闭串口按钮提示信息
        self.refresh_button.setToolTip("refresh current port")      # 设置刷新串口按钮提示信息
        self.open_close_button.clicked.connect(self.open_close_button_handle)   # 绑定开启按钮点击事件
        self.refresh_button.clicked.connect(self.refresh_button_handle)     # 绑定刷新串口点击事件

        # 设置串口选择布局为水平布局
        serial_com_layout = QHBoxLayout()
        serial_com_layout.addWidget(serial_com_label)   # 添加标签
        serial_com_layout.addWidget(self.serial_COM_comboBox)   # 添加下拉列表

        # 设置波特率选择布局为水平布局
        serial_baud_rate_layout = QHBoxLayout()
        serial_baud_rate_layout.addWidget(serial_baud_rate_label)
        serial_baud_rate_layout.addWidget(self.serial_baudRate_comboBox)

        # 设置数据位选择布局为水平布局
        serial_data_layout = QHBoxLayout()
        serial_data_layout.addWidget(serial_data_label)
        serial_data_layout.addWidget(self.serial_data_comboBox)

        # 设置停止位选择布局为水平布局
        serial_stop_bits_layout = QHBoxLayout()
        serial_stop_bits_layout.addWidget(serial_stop_bits_label)
        serial_stop_bits_layout.addWidget(self.serial_stopBits_comboBox)

        # 设置奇偶校验位选择布局为水平布局
        serial_parity_layout = QHBoxLayout()
        serial_parity_layout.addWidget(serial_parity_label)
        serial_parity_layout.addWidget(self.serial_parity_comboBox)

        # 设置按钮布局为水平布局
        button_layout = QHBoxLayout()
        button_layout.addWidget(self.open_close_button)
        button_layout.addWidget(self.refresh_button)

        # 添加控件布局到串口设置布局
        self.serial_setting_layout.addLayout(serial_com_layout)
        self.serial_setting_layout.addLayout(serial_baud_rate_layout)
        self.serial_setting_layout.addLayout(serial_data_layout)
        self.serial_setting_layout.addLayout(serial_stop_bits_layout)
        self.serial_setting_layout.addLayout(serial_parity_layout)
        self.serial_setting_layout.addLayout(button_layout)

    @staticmethod
    def get_port_list():
        """
        获取当前系统所有COM口
        :return:
        """
        com_list = []   # 用于保存端口名的列表
        port_list = serial.tools.list_ports.comports()    # 获取本机端口,返回list
        for port in port_list:
            com_list.append(port[0])    # 保存端口到列表
        return com_list     # 返回列表

    def save_config(self):
        """
        保存配置
        :return:
        """
        self.confParse.set('serial_setting', 'com', self.serial.port)
        self.confParse.set('serial_setting', 'baudRate', str(self.serial.baudrate))
        self.confParse.set('serial_setting', 'data', str(self.serial.bytesize))
        self.confParse.set('serial_setting', 'stopBits', str(self.serial.stopbits))
        self.confParse.set('serial_setting', 'parity', self.serial.parity)
        self.confParse.write(open(self.cfg_path, 'w'))

    def get_serial_setting(self):
        """
        读取串口配置信息
        :return:
        """
        self.serial.port = self.serial_COM_comboBox.currentText()
        self.serial.baudrate = int(self.serial_baudRate_comboBox.currentText())
        self.serial.bytesize = int(self.serial_data_comboBox.currentText())
        self.serial.stopbits = int(self.serial_stopBits_comboBox.currentText())
        self.serial.parity = self.serial_parity_comboBox.currentText()
        self.serial.timeout = 0

    def open_serial(self):
        """
            打开串口
            :return:
        """
        # 获取当前串口设置信息,并保存
        self.get_serial_setting()
        self.save_config()

        # 打开串口
        try:
            self.serial.open()
        except serial.SerialException:
            QMessageBox.critical(self, "Critical", "无法打开串口!!")  # 打开失败,弹窗提示
        else:
            self.is_serial_open = True      # 更新串口状态
            self.open_close_button.setText(u'关闭串口')     # 更新按钮名称
            self.enable_serial_setting(False)   # 设置串口设置界面为不可修改
            self.serial_open_callback()     # 调用串口打开回调

    def close_serial(self):
        """
            关闭串口
            :return:
        """
        # 更新串口状态
        self.is_serial_open = False
        self.open_close_button.setText(u'打开串口')     # 更新串口打开按钮名称
        self.enable_serial_setting(True)    # 设置串口设置界面为可修改
        self.serial_close_callback()        # 调用串口关闭回调
        self.serial.close()     # 关闭串口

    def open_close_button_handle(self):
        """
        处理打开或关闭串口按键事件
        :return:
        """
        # 判断串口是否已打开,没打开的话打开,打开的话关闭
        if self.is_serial_open:
            self.close_serial()
        else:
            self.open_serial()

    def refresh_button_handle(self):
        """
        处理刷新端口按钮点击事件
        :return:
        """

        self.serial_COM_comboBox.clear()    # 清空端口选择下拉列表
        self.serial_COM_comboBox.addItems(self.get_port_list())     # 重新设置端口下拉列表

    def enable_serial_setting(self, enable):
        """
        使能串口设置组件和刷新串口组件
        :param enable: bool ,True: enable,False: disable
        :return:
        """

        # 设置串口设置界面是否可操作
        self.refresh_button.setEnabled(enable)
        self.serial_parity_comboBox.setEnabled(enable)
        self.serial_stopBits_comboBox.setEnabled(enable)
        self.serial_data_comboBox.setEnabled(enable)
        self.serial_baudRate_comboBox.setEnabled(enable)
        self.serial_COM_comboBox.setEnabled(enable)

    def get_serial_setting_layout(self):
        """
        获取串口设置布局
        :return QVBoxLayout:
        """
        return self.serial_setting_layout

    def serial_readline(self):
        """
        读取一行,串口已打开则返回读取的内容,否则返回空字符串
        :return str:
        """
        if self.is_serial_open:
            try:
                text_line = self.serial.readline()
            except Exception as e:
                print(e)
                self.close_serial()
            else:
                return text_line.decode("utf-8", "ignore")
        else:
            return ""

    def serial_write(self, data):
        """
        串口发送字符串
        :param data 待发送的字符串str:
        :return:
        """
        if self.is_serial_open:
            try:
                self.serial.write(data.encode("utf-8", "ignore"))
            except Exception as e:
                print(e)

简单介绍下SerialSettingLayout类下的常用函数

__init__函数

def __init__(self, open_callback=None, close_callback=None):
        """
        SerialSettingLayout构造函数
        :param open_callback: 串口打开时,执行的回调
        :param close_callback: 串口关闭时,执行的回调
        """

SerialSettingLayout类构造函数接受两个默认参数,如果你想在串口开启后,执行一些操作,比如读取串口数据,可以在实例化时传入open_callback参数。或者想要在串口关闭前执行一些操作,可以传入close_callback参数。

__init__函数内部,主要是初始化各种组件,像serial对象,各种按钮和下拉列表,以及configparser对象。最后会调用init_ui()用于初始化布局。init_ui()主要用于初始化界面布局,首先调用read_config(),读取配置文件,没读到的话会新建空的配置。然后调用init_setting_layout(),设置布局。

serial_readline函数

    def serial_readline(self):
        """
        读取一行,串口已打开则返回读取的内容,否则返回空字符串
        :return str:
        """

串口打开成功后,直接调用serial_readline()函数即可读取串口数据。一次读取一行,返回字符串。

serial_write 函数

 def serial_write(self, data):
        """
        串口发送字符串
        :param data 待发送的字符串str:
        :return:
        """

串口开启情况下,调用serial_write()可以通过串口发送数据,数据类型为字符串。

get_serial_setting_layout函数

    def get_serial_setting_layout(self):
        """
        获取串口设置布局
        :return QVBoxLayout:
        """

调用此函数,返回一个QVBoxLayout布局。

类SerialQtTool分析

此类是主程序主体,实现产测程序逻辑。界面如下:

#!/user/bin/python3
# -*- coding:utf-8 -*-
'''
产测工具,用于测试串口通信是否ok.主要流程是,串口发送测试指令,然后检查返回指令是否为期待指令。
'''

__author__ = 'Duanlin D'

import sys
import threading
import SerialSettingLayout as SSLayout

from PyQt5.QtWidgets import (QWidget, QApplication, QPushButton, QGridLayout,
                             QVBoxLayout, QGroupBox, QToolTip, QDesktopWidget, QTextBrowser)
from PyQt5.QtGui import (QIcon, QFont, QColor)
from PyQt5.QtCore import (QBasicTimer)


class SerialQtTool(QWidget):
    Qt_Test_interval = 0.2  # 产测时间间隔
    Qt_Test_Count = 5   # 测试次数
    Qt_Test_Message = "test_cmd"    # 测试指令
    Qt_Test_Return = "ok"   # 期待的返回指令

    def __init__(self):
        super(SerialQtTool, self).__init__()
        self.read_data_timer = QBasicTimer()    # 定时器,用于定时读取串口返回值

        # 产测状态
        self.qt_test_count = SerialQtTool.Qt_Test_Count     # 测试次数,每次启动测试自减
        self.qt_test_interval = SerialQtTool.Qt_Test_interval   # 测试间隔
        self.is_qt_test_ok = False  # 测试成功标志

        # 产测工具界面
        self.qt_test_button = QPushButton(u'start')     # 启动测试按钮,
        self.log_view_groupBox = QGroupBox("log view")  # 测试结果显示窗口组
        self.serial_setting_groupBox = QGroupBox("serial_setting")     # 串口设置界面组

        # 产测结果显示文本框设置
        self.log_view_textBrowser = QTextBrowser()      # 用于显示结果的文本框
        self.log_view_textBrowser.setFont(QFont('Helvetica', 28))   # 配置显示格式

        # 串口工具界面布局
        self.serial_setting_layout = SSLayout.SerialSettingLayout(self.start_read_data, self.stop_read_data) #初始化串口设置界面布局,并绑定串口打开和关闭时的操作

        # 初始化界面
        self.init_ui()

    def init_log_view_group(self):
        """
        初始化打印窗口
        :return:
        """
        # 初始化测试结果界面布局为垂直布局
        log_view_layout = QVBoxLayout()
        # 添加文本框
        log_view_layout.addWidget(self.log_view_textBrowser)

        # 添加布局到组
        self.log_view_groupBox.setLayout(log_view_layout)

    def init_serial_setting_group(self):
        """
        初始化串口设置界面
        :return:
        """

        # 设置产测开始按钮
        QToolTip.setFont(QFont('SansSerif', 10))    # 设置按钮提示样式
        self.qt_test_button.setToolTip("start qt test")     # 设置按钮提示文本
        self.qt_test_button.clicked.connect(self.qt_test_button_handle)     # 绑定按钮点击事件
        self.qt_test_button.setEnabled(False)       # 按钮默认不可点击

        # 设置串口设置界面
        serial_setting_layout = self.serial_setting_layout.get_serial_setting_layout()  # 获取串口设置界面布局
        serial_setting_layout.addWidget(self.qt_test_button)    # 添加开始测试按钮到布局

        # 添加布局到组
        self.serial_setting_groupBox.setLayout(serial_setting_layout)

    def init_ui(self):
        """
        初始化界面布局
        :return:
        """
        # 初始化串口设置界面组
        self.init_serial_setting_group()
        # 初始化测试结果显示界面组
        self.init_log_view_group()
        # 设置窗口布局
        self.set_frame()

    def qt_test_fun(self):
        """
        产测执行程序,以一定的时间间隔执行指定次数,直到成功为止
        :return:
        """

        # 写入产测指令
        self.serial_setting_layout.serial_write(SerialQtTool.Qt_Test_Message)
        self.qt_test_count -= 1     # 产测次数自减
        # 判断是否还有产测次数
        if self.qt_test_count > 0:
            # 有,则继续执行产测
            self.qt_fun_timer = threading.Timer(self.qt_test_interval, self.qt_test_fun)    # 设置产测定时器
            self.qt_fun_timer.start()   # 启动产测定时器

    def qt_test_button_handle(self):
        """
        产测按钮响应程序
        :return:
        """

        # 判断串口工具是否已打开
        if self.serial_setting_layout.is_serial_open:
            # 已打开
            self.qt_test_button.setEnabled(False)   # 设置测试按钮为不可点击
            self.log_view_textBrowser.clear()       # 清除测试结果显示文本框
            self.qt_test_count = SerialQtTool.Qt_Test_Count     # 初始化测试次数
            self.qt_fun_timer = threading.Timer(self.qt_test_interval, self.qt_test_fun)    # 设置产测定时器,定时执行产测
            self.qt_fun_timer.start()   # 启动产测定时器

    def timerEvent(self, e):
        """
        定时器执行程序,复写此函数,则当系统定时器启动时,定时执行此程序
        :param e:
        :return:
        """

        # 读取一行串口数据,返回字符串
        text_line = self.serial_setting_layout.serial_readline()

        # 判断返回的是否为期待指令
        if text_line and SerialQtTool.Qt_Test_Return in text_line:
            # 是期待指令,则测试成功
            self.log_view_textBrowser.setTextColor(QColor(0, 255, 0))   # 设置测试结果显示字体颜色为绿色
            self.log_view_textBrowser.setText("测试成功")       # 设置测试结果显示内容为测试成功
            self.qt_fun_timer.cancel()      # 关闭定时发送指令定时器
            self.qt_test_button.setEnabled(True)    # 设置产测按钮为可点击
        # 判断测试次数是否已用完
        elif self.qt_test_count <= 0:
            # 测试超时
            self.log_view_textBrowser.setTextColor(QColor(255, 0, 0))   #设置测试结果显示字体颜色为红色
            self.log_view_textBrowser.setText("测试失败")   #设置测试结果显示内容为测试失败
            self.qt_test_button.setEnabled(True)    # 设置测试按钮为可点击

    def start_read_data(self):
        """
        开始读取数据,此函数与SerialSettingLayout 类下的,串口开启按钮关联,并在串口打开时自动执行
        :return:
        """
        # 启动数据读取定时器
        self.read_data_timer.start(2, self)     # 开启定时读取定时器,间隔2ms
        self.qt_test_button.setEnabled(True)    # 设置产测按钮为可点击
        pass

    def stop_read_data(self):
        """
        停止读取数据,此函数与SerialSettingLayout 类下的,串口开启按钮关联,并在串口关闭时自动执行
        :return:
        """
        self.read_data_timer.stop()     # 停止定时读取定时器
        self.qt_test_button.setEnabled(False)   # 设置产测按钮为不可点击
        pass

    def center(self):
        """
        将程序串口置于屏幕中央
        :return:
        """
        qr = self.frameGeometry()   # 获取当前程序窗口
        cp = QDesktopWidget().availableGeometry().center()  # 获取桌面中心点
        qr.moveCenter(cp)   # 设置当前窗口中心为桌面中心
        self.move(qr.topLeft())     # 移动当前窗口到,相对于当前窗口中心的左上角

    def set_frame(self):
        """
        设置窗口布局
        :return:
        """
        main_layout = QGridLayout()     # 初始化主窗口布局为网格布局
        main_layout.addWidget(self.log_view_groupBox, 0, 0, 1, 4)   # 在(0,0)格子上放置一个跨度1行,4列的控件组
        main_layout.addWidget(self.serial_setting_groupBox, 0, 5)   # 在(0,5) 格子上放置一个控件组
        self.setLayout(main_layout)     # 设置窗口布局

        self.setWindowTitle('serial qt tool')   # 设置主窗口标题

        self.setWindowIcon(QIcon('images/logo.png'))    # 设置窗口图标
        self.resize(600, 400)   # 设置窗口大小
        self.center()   # 窗口居中显示
        self.show()     # 显示窗口


if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = SerialQtTool()
    sys.exit(app.exec_())

其他

源码:https://github.com/duanduanlin/Qt_Test_Tool.git

  • 8
    点赞
  • 62
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是使用Python3PyQT5Pyserial实现串口工具的方法: 1. 安装PyserialPyQT5库 在终端或命令行中输入以下命令来安装PyserialPyQT5: ``` pip install pyserial pip install pyqt5 ``` 2. 创建GUI界面 使用PyQT5创建一个简单的GUI界面。在这个界面中,你需要添加一个下拉菜单来选择串口、一个文本框来显示串口数据和一些按钮来打开/关闭串口和发送数据。 下面是一个简单PyQT5串口工具界面: ```python import sys from PyQt5.QtWidgets import QApplication, QMainWindow, QComboBox, QTextEdit, QPushButton, QHBoxLayout, QVBoxLayout, QWidget class SerialTool(QMainWindow): def __init__(self): super().__init__() self.initUI() def initUI(self): # 设置窗口大小 self.setGeometry(100, 100, 400, 300) # 设置窗口标题 self.setWindowTitle('Serial Tool') # 添加下拉菜单 self.portComboBox = QComboBox(self) self.portComboBox.move(10, 10) self.portComboBox.resize(180, 30) # 添加文本框 self.textEdit = QTextEdit(self) self.textEdit.move(10, 50) self.textEdit.resize(380, 180) # 添加按钮 self.openButton = QPushButton('Open', self) self.openButton.move(10, 240) self.openButton.resize(80, 30) self.closeButton = QPushButton('Close', self) self.closeButton.move(100, 240) self.closeButton.resize(80, 30) self.sendButton = QPushButton('Send', self) self.sendButton.move(190, 240) self.sendButton.resize(80, 30) self.clearButton = QPushButton('Clear', self) self.clearButton.move(280, 240) self.clearButton.resize(80, 30) # 添加布局管理器 hbox = QHBoxLayout() hbox.addStretch(1) hbox.addWidget(self.openButton) hbox.addWidget(self.closeButton) hbox.addWidget(self.sendButton) hbox.addWidget(self.clearButton) vbox = QVBoxLayout() vbox.addWidget(self.portComboBox) vbox.addLayout(hbox) widget = QWidget() widget.setLayout(vbox) self.setCentralWidget(widget) ``` 3. 实现串口操作 使用Pyserial库来创建串口连接。在打开串口之前,你需要获取可用的串口列表并将它们添加到下拉菜单中。在发送数据之前,你需要从文本框中获取数据。 下面是一个简单Pyserial串口操作代码: ```python import serial class SerialTool(QMainWindow): def __init__(self): super().__init__() self.initUI() self.initSerial() def initSerial(self): # 获取可用串口列表 ports = serial.tools.list_ports.comports() # 将可用串口添加到下拉菜单中 for port in ports: self.portComboBox.addItem(port.device) # 创建串口连接 self.ser = serial.Serial() def openSerial(self): # 设置串口参数 port = self.portComboBox.currentText() baudrate = 9600 bytesize = serial.EIGHTBITS parity = serial.PARITY_NONE stopbits = serial.STOPBITS_ONE self.ser.port = port self.ser.baudrate = baudrate self.ser.bytesize = bytesize self.ser.parity = parity self.ser.stopbits = stopbits # 打开串口连接 try: self.ser.open() self.ser.flushInput() self.ser.flushOutput() self.textEdit.append('Serial port is open.') except Exception as e: self.textEdit.append(str(e)) def closeSerial(self): # 关闭串口连接 try: self.ser.close() self.textEdit.append('Serial port is closed.') except Exception as e: self.textEdit.append(str(e)) def sendSerial(self): # 发送数据 data = self.textEdit.toPlainText() try: self.ser.write(data.encode()) self.textEdit.clear() self.textEdit.append('Data is sent: ' + data) except Exception as e: self.textEdit.append(str(e)) ``` 4. 运行程序 将GUI界面和串口操作代码组合起来,并在主函数中运行程序。 下面是一个完整的PyQT5串口工具代码: ```python import sys import serial from PyQt5.QtWidgets import QApplication, QMainWindow, QComboBox, QTextEdit, QPushButton, QHBoxLayout, QVBoxLayout, QWidget class SerialTool(QMainWindow): def __init__(self): super().__init__() self.initUI() self.initSerial() def initUI(self): # 设置窗口大小 self.setGeometry(100, 100, 400, 300) # 设置窗口标题 self.setWindowTitle('Serial Tool') # 添加下拉菜单 self.portComboBox = QComboBox(self) self.portComboBox.move(10, 10) self.portComboBox.resize(180, 30) # 添加文本框 self.textEdit = QTextEdit(self) self.textEdit.move(10, 50) self.textEdit.resize(380, 180) # 添加按钮 self.openButton = QPushButton('Open', self) self.openButton.move(10, 240) self.openButton.resize(80, 30) self.closeButton = QPushButton('Close', self) self.closeButton.move(100, 240) self.closeButton.resize(80, 30) self.sendButton = QPushButton('Send', self) self.sendButton.move(190, 240) self.sendButton.resize(80, 30) self.clearButton = QPushButton('Clear', self) self.clearButton.move(280, 240) self.clearButton.resize(80, 30) # 添加布局管理器 hbox = QHBoxLayout() hbox.addStretch(1) hbox.addWidget(self.openButton) hbox.addWidget(self.closeButton) hbox.addWidget(self.sendButton) hbox.addWidget(self.clearButton) vbox = QVBoxLayout() vbox.addWidget(self.portComboBox) vbox.addLayout(hbox) widget = QWidget() widget.setLayout(vbox) self.setCentralWidget(widget) # 绑定按钮事件 self.openButton.clicked.connect(self.openSerial) self.closeButton.clicked.connect(self.closeSerial) self.sendButton.clicked.connect(self.sendSerial) self.clearButton.clicked.connect(self.textEdit.clear) def initSerial(self): # 获取可用串口列表 ports = serial.tools.list_ports.comports() # 将可用串口添加到下拉菜单中 for port in ports: self.portComboBox.addItem(port.device) # 创建串口连接 self.ser = serial.Serial() def openSerial(self): # 设置串口参数 port = self.portComboBox.currentText() baudrate = 9600 bytesize = serial.EIGHTBITS parity = serial.PARITY_NONE stopbits = serial.STOPBITS_ONE self.ser.port = port self.ser.baudrate = baudrate self.ser.bytesize = bytesize self.ser.parity = parity self.ser.stopbits = stopbits # 打开串口连接 try: self.ser.open() self.ser.flushInput() self.ser.flushOutput() self.textEdit.append('Serial port is open.') except Exception as e: self.textEdit.append(str(e)) def closeSerial(self): # 关闭串口连接 try: self.ser.close() self.textEdit.append('Serial port is closed.') except Exception as e: self.textEdit.append(str(e)) def sendSerial(self): # 发送数据 data = self.textEdit.toPlainText() try: self.ser.write(data.encode()) self.textEdit.clear() self.textEdit.append('Data is sent: ' + data) except Exception as e: self.textEdit.append(str(e)) if __name__ == '__main__': app = QApplication(sys.argv) serialTool = SerialTool() serialTool.show() sys.exit(app.exec_()) ``` 运行程序后,你应该可以打开串口并发送数据了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值