基于PySide2的串口通信助手

前言:本人是第一次使用Python基于PySide2编写界面,对于python和PySide2,我只是初学者,一边学python一边查阅界面控件函数说明,零基础编写。这个界面是我花了一天多写出来的,但界面里的功能都实现了,本文的本意是记录学习生活,与此同时能帮到正有此需要的你那就更好了。

先看效果:
在这里插入图片描述
设置界面如下:
在这里插入图片描述

首先,我是借鉴下面这位大佬的博客。如有冒犯,烦请告知,必删!

Python3+PyQT5+Pyserial 实现简单的串口工具
他是基于pyqt5写的,与本文所提到的pyside2暂时并没有太大的区别。细微的差异在于我是动态加载UI的,我还根据我的需要,添加了一些功能,如定时保存、保存路径选择、保存文件类型、增加设置界面、支持中文显示等。

其次,编写时为了雅观,把文件都分类存放了,如图片存放在该工程目录下的picture文件夹。待打包程序时,会出现各种文件缺少错误,就是这个花了我几个小时解决,建议大家在打包的时候将所有文件都放在代码src文件目录下,即放在与主py文件同级目录。

主py代码如下:

import sys
import shutil
import os
import serial
import serial.tools.list_ports
from PySide2.QtWidgets import QApplication, QMessageBox
from PySide2.QtUiTools import QUiLoader
from PySide2.QtCore import QFile, Qt, QSize, QTimer
from PySide2.QtGui import QIcon
from PySide2.QtWidgets import QFileDialog
from picture import resource   #导入图标


n = 0

class Second:
    def __init__(self):
        # 从文件加载UI
        qfile_stats = QFile("../ui/frist_pyserial.ui")
        qfile_stats.open(QFile.ReadOnly)
        qfile_stats.close()

        self.ui = QUiLoader().load(qfile_stats)

        #初始化串口配置
        self.traslate_params_set()
        self.init()
        self.ser = serial.Serial()
        self.port_check()

        # 接收数据和发送数据数目置零
        self.data_num_received = 0
        self.ui.lineEdit.setText(str(self.data_num_received))
        self.data_num_sended = 0
        self.ui.lineEdit_2.setText(str(self.data_num_sended))

        # 设置定时 5 秒 保存接收内容
        # self.save_timer.start(5000)

    def init(self):
        # 串口检测按钮
        self.ui.s1__box_1.clicked.connect(self.port_check)

        # 串口信息显示
        self.ui.s1__box_2.currentTextChanged.connect(self.port_imf)

        # 打开串口按钮
        self.ui.open_button.clicked.connect(self.port_open)

        # 关闭串口按钮
        self.ui.close_button.clicked.connect(self.port_close)

        # 发送数据按钮
        self.ui.s3__send_button.clicked.connect(self.data_send)

        # 定时发送数据
        self.timer_send = QTimer()
        self.timer_send.timeout.connect(self.data_send)
        self.ui.timer_send_cb.stateChanged.connect(self.data_send_timer)

        # 定时器接收数据
        self.timer = QTimer(self.ui)
        self.timer.timeout.connect(self.data_receive)

        # 清除发送窗口
        self.ui.s3__clear_button.clicked.connect(self.send_data_clear)

        # 清除接收窗口
        self.ui.s2__clear_button.clicked.connect(self.receive_data_clear)

        # 保存接收内容为文件
        self.ui.save_button.clicked.connect(self.save_file)

        # 定时保存接收窗口内容
        self.save_timer = QTimer()
        self.save_timer.timeout.connect(self.save_file)
        self.ui.timer_save_cb.stateChanged.connect(self.save_timer_func)

    # 串口检测
    def port_check(self):
        # 检测所有存在的串口,将信息存储在字典中
        self.Com_Dict = {}
        port_list = list(serial.tools.list_ports.comports())
        self.ui.s1__box_2.clear()
        for port in port_list:
            self.Com_Dict["%s" % port[0]] = "%s" % port[1]
            self.ui.s1__box_2.addItem(port[0])
        if len(self.Com_Dict) == 0:
            self.ui.state_label.setText(" 无串口")

    # 串口信息
    def port_imf(self):
        # 显示选定的串口的详细信息
        imf_s = self.ui.s1__box_2.currentText()
        if imf_s != "":
            self.ui.state_label.setText(self.Com_Dict[self.ui.s1__box_2.currentText()])

    # 打开串口
    def port_open(self):
        self.ser.port = self.ui.s1__box_2.currentText()
        self.ser.baudrate = int(self.ui.s1__box_3.currentText())
        self.ser.bytesize = int(self.ui.s1__box_4.currentText())
        self.ser.stopbits = int(self.ui.s1__box_6.currentText())
        self.ser.parity = self.ui.s1__box_5.currentText()

        try:
            self.ser.open()
        except:
            QMessageBox.critical(self, "Port Error", "此串口不能被打开!")
            return None

        # 打开串口接收定时器,周期为2ms
        self.timer.start(2)

        if self.ser.isOpen():
            self.ui.open_button.setEnabled(False)
            self.ui.close_button.setEnabled(True)
            self.ui.formGroupBox1.setTitle("串口状态(已开启)")

    # 关闭串口
    def port_close(self):
        self.timer.stop()
        self.timer_send.stop()
        try:
            self.ser.close()
        except:
            pass
        self.ui.open_button.setEnabled(True)
        self.ui.close_button.setEnabled(False)
        self.ui.lineEdit_3.setEnabled(True)
        # 接收数据和发送数据数目置零
        self.data_num_received = 0
        self.ui.lineEdit.setText(str(self.data_num_received))
        self.data_num_sended = 0
        self.ui.lineEdit_2.setText(str(self.data_num_sended))
        self.ui.formGroupBox1.setTitle("串口状态(已关闭)")

    # 发送数据
    def data_send(self):
        if self.ser.isOpen():
            input_s = self.ui.s3__send_text.toPlainText()
            if input_s != "":
                # 非空字符串
                if self.ui.hex_send.isChecked():
                    # hex发送
                    input_s = input_s.strip()
                    send_list = []
                    while input_s != '':
                        try:
                            num = int(input_s[0:2], 16)
                        except ValueError:
                            QMessageBox.critical(self.ui, 'wrong data', '请输入十六进制数据,以空格分开!')
                            return None
                        input_s = input_s[2:].strip()
                        send_list.append(num)
                    input_s = bytes(send_list)
                else:
                    # ascii发送
                    input_s = (input_s + '\r\n').encode('utf-8')

                num = self.ser.write(input_s)
                self.data_num_sended += num
                self.ui.lineEdit_2.setText(str(self.data_num_sended))
        else:
            pass

    # 接收数据
    def data_receive(self):
        try:
            num = self.ser.inWaiting()
        except:
            self.port_close()
            return None
        if num > 0:
            data = self.ser.read(num)
            num = len(data)
            # hex显示
            if self.ui.hex_receive.checkState():
                out_s = ''
                for i in range(0, len(data)):
                    out_s = out_s + '{:02X}'.format(data[i]) + ' '
                self.ui.s2__receive_text.insertPlainText(out_s)
            else:
                # 串口接收到的字符串为b'123',要转化成unicode字符串才能输出到窗口中去
                self.ui.s2__receive_text.insertPlainText(data.decode('utf-8'))

            # 统计接收字符的数量
            self.data_num_received += num
            self.ui.lineEdit.setText(str(self.data_num_received))

            # 获取到text光标
            textCursor = self.ui.s2__receive_text.textCursor()
            # 滚动到底部
            textCursor.movePosition(textCursor.End)
            # 设置光标到text中去
            self.ui.s2__receive_text.setTextCursor(textCursor)
        else:
            pass

    # 定时发送数据
    def data_send_timer(self):
        if self.ui.timer_send_cb.isChecked():  # 判断checkbox是否被选中,是就返回1,否则返回0
            self.timer_send.start(int(self.ui.lineEdit_3.text()))
            self.ui.lineEdit_3.setEnabled(False)
        else:
            self.timer_send.stop()
            self.ui.lineEdit_3.setEnabled(True)

    # 清除显示
    def send_data_clear(self):
        self.ui.s3__send_text.setText("")

    def receive_data_clear(self):
        self.ui.s2__receive_text.setText("")

    # 串口参数配置
    def traslate_params_set(self):
        self.ui.s1__box_3.addItems(['115200', '9600', '38400', '4800'])
        self.ui.s1__box_4.addItems(['8', '7', '6', '5'])
        self.ui.s1__box_5.addItem('N')
        self.ui.s1__box_6.addItem('1')

    # 保存文件
    def save_file(self):
        save_list = {}
        my_text = {}
        file = {}
        global n
        save_path = 'D:/Python/python_project/UI_2/data/'
        if n < 3:
            # save_list[n] = QFileDialog.getSaveFileName(
            #     self.ui,  # 父窗口对象
            #     "保存文件",  # 标题
            #     r"D:\Python\python_project\UI_2\data",  # 起始目录
            #     "txt类型 (*.txt)"  # 选择类型过滤项,过滤内容在括号中
            # )
            # print(save_list[n])
            # if save_list[n] == ('', ''):
            #     pass
            # else:
            with open(save_path+str(n)+str('.txt'), 'w+') as file[n]:
                my_text[n] = self.ui.s2__receive_text.toPlainText()
                file[n].write(my_text[n])
                n = n + 1
        else:
            shutil.rmtree(save_path)
            os.mkdir(save_path)
            n = 0

        # 定时保存接收窗口内容
    def save_timer_func(self):
        if self.ui.timer_save_cb.isChecked():  # 判断checkbox是否被选中,是就返回1,否则返回0
            self.save_timer.start(int(self.ui.lineEdit_4.text()))
            self.ui.lineEdit_4.setEnabled(False)
        else:
            self.save_timer.stop()
            self.ui.lineEdit_4.setEnabled(True)

app = QApplication([])
app.setWindowIcon(QIcon('../picture/Setting_Logo.png'))
second_ui = Second()
second_ui.ui.show()
app.exec_()

由于是动态加载ui,所以只要ui界面里控件名称与主py代码里对应起来就可以实现功能了。如果对控件名称有疑问的话,可以参考上述大佬的博客研究源码,对照一下就可以很清晰的哪个是哪个了。

PS:对于ico图标和背景图片,大家可以自行更换,关于PySide2的学习,我是跟着白月黑羽老师的教程零基础入门,有兴趣的小伙伴可以去他家看看,他家的官网关于pyside2控件的函数说明也挺详细的!
  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值