Python之串口助手功能(0/4)

util_serial.py
代码实现串口的UI界面

# -*- coding: utf-8 -*-
import sys

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from serial.tools.list_ports import comports
from widget_serial import *
from PyQt5.QtGui import QFont
from data_check import *


'''
    主工具
'''
debug = 0

class SerialWidget(QWidget):
    _instance = None  # Class variable to store the singleton instance

    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super(SerialWidget, cls).__new__(cls)
        return cls._instance

    sgn_recv_data = pyqtSignal(bytes)
    sgn_run_status = pyqtSignal(bool, str)
    sgn_dingding_en = pyqtSignal(bool)
    sgn_alarm_tap = pyqtSignal(int)
    sgn_project_name = pyqtSignal(str)
    sgn_run_time = pyqtSignal(QDateTime)
    sgn_datasave_en = pyqtSignal(bool)

    def __init__(self):
        super().__init__()
        if not hasattr(self, 'initialized'):  # Check if it was initialized already
            self.start_time = QDateTime.currentDateTime()
            self.sec_to_start = 0
            self.sec_to_last = 0
            self.update_tap = 1
            self.mSerialPort = SerialPort()
            self.mProgressDialog = ProgressDialog(self)
            self.mSerialPort.transStateChanged.connect(
                self.mProgressDialog.onPercentChanged)
            self.initUI()
            self.mSerialPort.start()
            self.mSerialStatus = False
            self.portlist = comports()
            self.initialized = True  # Flag to avoid reinitialization

    '''
        UI布局
    '''

    def initUI(self):
        self.bd = 115200
        self.send_id = 0
        self.progress_cnt = 0
        vb = QVBoxLayout()
        gd = QGridLayout()
        vb.addLayout(gd)
        vb.addStretch()
        self.setLayout(vb)
        if debug:
            self.text_send = ['A55A010021026464DD005AA5',  # 1
                              'A55A020021026464DE005AA5',  # 2
                              'A55A030021026464DF005AA5',  # 3
                              'A55A0400A15000000000000000000000000000000000000000000000000000000000000000000000000000004B000000000000000000000000000000000000000007010101010101010101010101010101010101010146005AA5',
                          #   'A55A040021026464D8005AA5',  # 4
                              'A55A050021026464D9005AA5',  # 5
                              'A55A060021026464DA005AA5',  # 6
                              'A55A070021026464DB005AA5',  # 7
                              'A55A080021026464D4005AA5'   # 8
                             ]
        else:
            self.text_send = ['A55A010021026464DD005AA5',  # 1
                              'A55A020021026464DE005AA5',  # 2
                              'A55A030021026464DF005AA5',  # 3
                              'A55A040021026464D8005AA5',  # 4
                              'A55A050021026464D9005AA5',  # 5
                              'A55A060021026464DA005AA5',  # 6
                              'A55A070021026464DB005AA5',  # 7
                              'A55A080021026464D4005AA5'   # 8
                             ]
        self.setStyleSheet('''QGroupBox{
                                        background:#DDFFFF;
                                        border:1px solid gray;
                                        width:300px;
                                        border-radius:10px;
                                        padding:4px;
                                        text-align:center;
                                        font:12px
                                        }
                                    QPushButton{
                                        background:#77FFFF;
                                        border-radius:10px;
                                        border-width: 2px;
                                        border:1px solid gray;
                                        border-style: outset;
                                        padding: 6px;
                                        font:14px
                                        }
                                    QPushButton:disabled { 
                                        background-color: lightgray; 
                                        }
                                    QProgressBar{
                                        background:#AAFFFF;
                                        border-radius:10px;
                                        border-width: 2px;
                                        border:0px solid gray;
                                        border-style: outset;
                                        color:#000000;
                                        padding: 6px;
                                        font:16px;
                                        text-align: center;
                                        }
                                    QProgressBar:chunk { 
                                        background-color: #33FFFF; 
                                        border-radius:10px;
                                        }
                                    QLCDNumber { 
                                        background-color: #77FFFF;
                                        border-radius:10px;
                                        color:#000000;
                                        font-weight: bold;
                                        }''')


        '''
            左边,串口设置
        '''
        settingGroup = QGroupBox('串口设置')
        gd_serial_set = QGridLayout()
        settingGroup.setLayout(gd_serial_set)
        gd.setColumnStretch(0, 2)
        gd.setColumnStretch(1, 3)
        gd.setColumnStretch(2, 2)
        gd.addWidget(settingGroup, 0, 0)
        gd_serial_set.addWidget(QLabel('端口检测:'), 0, 0,
                                2, 1, alignment=Qt.AlignCenter)
        btnDetect = QPushButton('检测')
        btnDetect.setFixedHeight(50)
        gd_serial_set.addWidget(btnDetect, 0, 1, 2, 2)
        gd_serial_set.addWidget(QLabel('端口列表:'), 2, 0,
                                1, 1, alignment=Qt.AlignCenter)
        cbPort = QComboBox()
        gd_serial_set.addWidget(cbPort, 2, 1, 1, 2)
        btnOpen = QPushButton('打开串口')
        btnOpen.setFixedHeight(50)
        gd_serial_set.addWidget(btnOpen, 3, 0, 2, 3)
        cbTimeSend = QCheckBox('定时发送')
        cbTimeSend.setChecked(False)

        '''
            中间,工程 设置
        '''
        projectGroup = QGroupBox('工程设置')
        gd_prj_set = QGridLayout()
        projectGroup.setLayout(gd_prj_set)

        gd.addWidget(projectGroup, 0, 1)
        gd_prj_set.addWidget(QLabel('工程名称:'), 0, 0)
        let_prj_name = QLineEdit('ProjectName')
        let_prj_name.setMaxLength(20)
        ft = let_prj_name.font()
        ft.setPointSize(ft.pointSize() + 2)
        let_prj_name.setFont(ft)
        gd_prj_set.addWidget(let_prj_name, 0, 1, 1, 2)

        gd_prj_set.addWidget(QLabel('更新频度:'), 0, 3)
        sb_update_tap = QSpinBox()
        sb_update_tap.setRange(1, 300)
        sb_update_tap.setSuffix('S')
        sb_update_tap.setSingleStep(1)
        sb_update_tap.setValue(1)
        sb_update_tap.setAlignment(Qt.AlignRight)
        gd_prj_set.addWidget(sb_update_tap, 0, 4, 1, 2)

        gd_prj_set.addWidget(QLabel('钉钉通知:'), 1, 0)
        gd_prj_set.addWidget(QLabel('通知使能'), 1, 1, alignment=Qt.AlignRight)
        ckbDingding = QCheckBox('')
        gd_prj_set.addWidget(ckbDingding, 1, 2, 1, 2)

        gd_prj_set.addWidget(QLabel('通知间隔:'), 1, 3)
        sb_alarm_tap = QSpinBox()
        sb_alarm_tap.setRange(4, 300)
        sb_alarm_tap.setSuffix('S')
        sb_alarm_tap.setSingleStep(1)
        sb_alarm_tap.setValue(4)
        sb_alarm_tap.setAlignment(Qt.AlignRight)
        sb_alarm_tap.setEnabled(False)
        gd_prj_set.addWidget(sb_alarm_tap, 1, 4, 1, 2)

        gd_prj_set.addWidget(QLabel('采集电阻:'), 2, 0)
        cbSmpRes = QComboBox()
        res = [str(i) for i in [0.1, 0.05, 0.01, 0.005, 0.001]]
        cbSmpRes.addItems(res)
        gd_prj_set.addWidget(cbSmpRes, 2, 1, 1, 2)

        gd_prj_set.addWidget(QLabel('放大倍数:'), 2, 3)
        cbOpaGain = QComboBox()
        gain = [str(i) for i in [100, 50, 25, 200]]
        cbOpaGain.addItems(gain)
        gd_prj_set.addWidget(cbOpaGain, 2, 4, 1, 2)

        btnGrpall = QPushButton('全选')
        btnGrpall.setFixedHeight(50)
        ckbGrp1 = QCheckBox('Grp1')
        ckbGrp2 = QCheckBox('Grp2')
        ckbGrp3 = QCheckBox('Grp3')
        ckbGrp4 = QCheckBox('Grp4')
        ckbGrp5 = QCheckBox('Grp5')
        ckbGrp6 = QCheckBox('Grp6')
        ckbGrp7 = QCheckBox('Grp7')
        ckbGrp8 = QCheckBox('Grp8')
        gd_prj_set.addWidget(btnGrpall, 3, 0, 2, 2)
        gd_prj_set.addWidget(ckbGrp1, 3, 2)
        gd_prj_set.addWidget(ckbGrp2, 3, 3)
        gd_prj_set.addWidget(ckbGrp3, 3, 4)
        gd_prj_set.addWidget(ckbGrp4, 3, 5)
        gd_prj_set.addWidget(ckbGrp5, 4, 2)
        gd_prj_set.addWidget(ckbGrp6, 4, 3)
        gd_prj_set.addWidget(ckbGrp7, 4, 4)
        gd_prj_set.addWidget(ckbGrp8, 4, 5)
        checkboxes = [ckbGrp1, ckbGrp2, ckbGrp3,
                      ckbGrp4, ckbGrp5, ckbGrp6, ckbGrp7, ckbGrp8]

        '''
            右边,运行设置
        '''
        runGroup = QGroupBox('运行设置')
        gd_run_set = QGridLayout()
        runGroup.setLayout(gd_run_set)
        gd.addWidget(runGroup, 0, 2)
        gd_run_set.addWidget(QLabel('老化时长:'), 0, 0)
        sb_time_set = QSpinBox()
        sb_time_set.setRange(1, 1080)
        sb_time_set.setSuffix('day')
        sb_time_set.setSingleStep(1)
        sb_time_set.setValue(1)
        sb_time_set.setAlignment(Qt.AlignRight)
        gd_run_set.addWidget(sb_time_set, 0, 1, 1, 2)
        btnRun = QPushButton('运行')
        btnRun.setFixedHeight(50)
        gd_run_set.addWidget(btnRun, 3, 0, 2, 5)
        gd_run_set.addWidget(QLabel('数据记录:'), 0, 3, alignment=Qt.AlignRight)
        ckbDataSave = QCheckBox('使能')
        gd_run_set.addWidget(ckbDataSave, 0, 4, alignment=Qt.AlignLeft)
        self.lcd = QLCDNumber(11, self)
        self.lcd.setFrameShape(QFrame.Box)
        self.lcd.setFrameShadow(QFrame.Plain)
        self.lcd.setSegmentStyle(QLCDNumber.Flat)
        self.lcd.display("00:00:00:00")
        gd_run_set.addWidget(self.lcd, 1, 0, 2, 3)
        self.pgb = QProgressBar(self)
        self.pgb.setFixedHeight(50)
        self.pgb.setValue(self.progress_cnt)
        self.pgb.setFormat('%p%'.format(self.pgb.value()))
        gd_run_set.addWidget(self.pgb, 1, 3, 2, 2)


        timer = QTimer(self)

        '''
            检测串口,更新串口列表cbPort
        '''
        def onBtnDetectClicked():
            nonlocal cbPort
            cbPort.clear()
            portlist = comports()
            for p in portlist:
                cbPort.addItem(p.description, p.device)

            if cbPort.currentText() != '':
                btnOpen.setEnabled(True)

        '''
            收到数据,更新输出文本框
        '''
        def onDataReceive(d: bytes):
            self.sgn_recv_data.emit(d)
            # d = list(d)
            # print(d.hex().upper())

        '''
            打开串口响应函数
        '''
        def onBtnOpenClicked():
            if not self.mSerialStatus:
                s = self.mSerialPort.open(
                    cbPort.currentText().split("(")[1].split(")")[0], self.bd)
                if s:
                    QMessageBox.critical(self, '打开串口失败', s)
                else:
                    self.mSerialStatus = True
                    btnDetect.setEnabled(False)
                    cbPort.setEnabled(False)
            else:
                self.mSerialPort.close()
                self.mSerialStatus = False
                btnDetect.setEnabled(True)
                cbPort.setEnabled(True)

        '''
            串口状态改变响应函数
        '''
        def onSerialStatusChanged(state):
            btnRun.setEnabled(state)
            if not state:
                timer.stop()
            if state:
                btnOpen.setText('关闭串口')
            else:
                btnOpen.setText('打开串口')

        '''
            秒转换成天时分秒
        '''
        def seconds_day_hms(seconds: int):
            # 分钟,秒
            m, s = divmod(seconds, 60)
            # 时,分
            h, m = divmod(m, 60)
            # 天,时
            d, h = divmod(h, 24)
            # print(f"{d}天{h}时{m}分{s}秒")
            return (int(d), int(h), int(m), int(s))

        '''
            刷新进度条
        '''
        def updateProgress():
            self.sec_to_start += (self.update_tap / 8)
            if self.sec_to_start - self.sec_to_last > 1:
                d, h, m, s = seconds_day_hms(self.sec_to_start)
                self.lcd.display("{:02d}:{:02d}:{:02d}:{:02d}".format(d, h, m, s))
                self.sec_to_last = self.sec_to_start
            sec_to_time_set = (sb_time_set.value() * 24 * 60 * 60)
            self.progress_cnt = int(self.sec_to_start * 100 / sec_to_time_set)
            if (self.progress_cnt > 100) and (self.sec_to_start >= sec_to_time_set):
                self.progress_cnt = 100
                timer.stop()
                btnRun.setText('结束')
                self.sgn_run_status.emit(False, btnRun.text())
                print("Test end")
            self.pgb.setValue(self.progress_cnt)
 
        '''
            发送按钮响应函数
        '''
        def onSendClicked():
            updateProgress()
            if checkboxes[self.send_id].isChecked():
                txt = self.text_send[self.send_id]
                if len(txt) == 0:
                    print('send {self.send_id} text is empty')
                    return

                data = []
                while txt != '':
                    try:
                        num = int(txt[0:2], 16)
                    except ValueError:
                        QMessageBox.critical(self, '数据错误', '请输入正确的十六进制数据')
                        return
                    txt = txt[2:].strip()
                    data.append(num)

                if len(data):
                    # print(data)
                    self.mSerialPort.sendData(bytes(data))

            self.send_id += 1
            if self.send_id >= 8:
                self.send_id = 0

        '''
            运行按钮
        '''
        def onBtnRunClicked():
            if not timer.isActive():
                ms = int(self.update_tap * 1000 / 8)
                timer.start(ms)
                if btnRun.text() == '运行':
                    self.start_time = QDateTime.currentDateTime()
                    self.sgn_run_time.emit(self.start_time)
                btnRun.setText('停止')
                settingGroup.setEnabled(False)
                projectGroup.setEnabled(False)
                sb_time_set.setEnabled(False)
                ckbDataSave.setEnabled(False)
                self.sgn_run_status.emit(True, btnRun.text())
                print("Time start", ms, "ms")
            else:
                timer.stop()
                btnRun.setText('继续')
                settingGroup.setEnabled(True)
                projectGroup.setEnabled(True)
                sb_time_set.setEnabled(True)
                # ckbDataSave.setEnabled(True)
                self.sgn_run_status.emit(False, btnRun.text())
                print("Time stop")

        '''
            数据记录使能
        '''
        def onCbkDataSaveClicked(en):
            self.sgn_datasave_en.emit(en)

        '''
            全选按钮响应函数
        '''
        def onBtnGrpallClicked():
            if any(cb.checkState() == False for cb in checkboxes):
                for cb in checkboxes:
                    cb.setChecked(True)
            else:
                for cb in checkboxes:
                    cb.setChecked(False)

        if cbPort.currentText() == '':
            btnOpen.setEnabled(False)

        '''
            通知使能
        '''
        def onCkbDingdingEnClicked(en: bool):
            if en:
                self.sgn_dingding_en.emit(en)
                sb_alarm_tap.setEnabled(True)
            else:
                sb_alarm_tap.setEnabled(False)

        '''
            报警间隔
        '''
        def onSpbAlarmTapChanged(tap: int):
            tap = sb_alarm_tap.value()
            self.sgn_alarm_tap.emit(tap)

        '''
            工程名称
        '''
        def onLetProjectNameChanged():
            name = let_prj_name.text()
            self.sgn_project_name.emit(name)

        '''
            更新频率
        '''
        def onSpbUpdateTapChanged():
            self.update_tap = sb_update_tap.value()

        def list_to_hex_string(txt):
            data = []
            while txt != '':
                try:
                    num = int(txt[0:2], 16)
                except ValueError:
                    QMessageBox.critical(self, '数据错误', '请输入正确的十六进制数据')
                    return
                txt = txt[2:].strip()
                data.append(num)
            return data

        '''
            采样电阻
        '''
        def onCbSmpResChanged():
            for i in range(len(self.text_send)):
                str_res = str(hex(int(float(cbSmpRes.currentText())
                              * 1000)).replace('0x', '').upper()).zfill(2)
                self.text_send[i] = list(self.text_send[i])
                self.text_send[i][12:14] = [str_res[0], str_res[1]]
                self.text_send[i] = ''.join(str(x) for x in self.text_send[i])
                # print(self.text_send[i])
                lst = list_to_hex_string(self.text_send[i])
                lst_bcc = lst[:8]
                data_bcc = bcc(lst_bcc)
                lst[8] = data_bcc
                self.text_send[i] = ''.join(
                    str(hex(x).replace('0x', '').upper()).zfill(2) for x in lst)
                # print(self.text_send[i])

        '''
            放大倍数
        '''
        def onCbOpaGainChanged():
            for i in range(len(self.text_send)):
                str_gain = str(hex(int(cbOpaGain.currentText())
                                   ).replace('0x', '').upper()).zfill(2)
                self.text_send[i] = list(self.text_send[i])
                self.text_send[i][14:16] = [str_gain[0], str_gain[1]]
                self.text_send[i] = ''.join(str(x) for x in self.text_send[i])
                # print(self.text_send[i])
                lst = list_to_hex_string(self.text_send[i])
                lst_bcc = lst[:8]
                data_bcc = bcc(lst_bcc)
                lst[8] = data_bcc
                self.text_send[i] = ''.join(
                    str(hex(x).replace('0x', '').upper()).zfill(2) for x in lst)
                # print(self.text_send[i])

        onSerialStatusChanged(0)
        btnOpen.clicked.connect(onBtnOpenClicked)
        btnDetect.clicked.connect(onBtnDetectClicked)
        self.mSerialPort.received.connect(onDataReceive)
        self.mSerialPort.stateChanged.connect(onSerialStatusChanged)
        btnRun.clicked.connect(onBtnRunClicked)
        timer.timeout.connect(onSendClicked)
        btnGrpall.clicked.connect(onBtnGrpallClicked)
        ckbDingding.clicked.connect(onCkbDingdingEnClicked)
        sb_alarm_tap.valueChanged.connect(onSpbAlarmTapChanged)
        sb_update_tap.valueChanged.connect(onSpbUpdateTapChanged)
        let_prj_name.editingFinished.connect(onLetProjectNameChanged)
        cbSmpRes.currentIndexChanged.connect(onCbSmpResChanged)
        cbOpaGain.currentIndexChanged.connect(onCbOpaGainChanged)
        ckbDataSave.clicked.connect(onCbkDataSaveClicked)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = SerialWidget()
    win.setWindowTitle("Serial")
    win.setWindowIcon(QIcon('res\\logo.png'))
    win.resize(1024, 840)
    win.show()
    sys.exit(app.exec_())



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值