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_())