PYQT5:基于QsciScintilla的代码编辑器分析11--集成串口工具
这里提供本编辑器可执行文件(exe)的下载链接:
1.Code51.Code51_STC
2.百度盘链接::https://pan.baidu.com/s/1Ihxb7WX0ozUuRs3KFyzApQ
提取码:i6my
3.源代码:百度盘链接:https://pan.baidu.com/s/1jlRvWgN2LFHTtnKogeUZZw
提取码:w437
4.源代码的码云链接:https://gitee.com/huangweide001/code4STC51
读者在调试代码时,可以直接运行主文件《texteditor2.py》。
“串口工具”几乎完全借用网友ColinFred的代码,见链接:Python3+PyQT5+Pyserial 实现简单的串口工具。主要考虑到一边用串口调试单片机程序,一边用串口下载,经常是下载时串口被占用,集成一个串口工具,下载前判断一下是否已经打开,如果打开就顺便关闭。
1. 将别人的代码集成到自己的软件中
要使用网友ColinFred的代码,只需将“Form”替换成“Dialog”,至于为什么,我也没有搞清楚。估计是Form是作为主窗口用的,Dialog通常作为子窗口。
我在源代码中增加2个变量,其一bIsOpened来记录串口的状态,当然也可以直接使用self.ser.isOpen()函数来判断。其二bIsRunning来记录是否已经打开“串口工具”。
在“菜单->工具”中增加一个名为“串口工具”的动作项:
toolSerialAction =self.createAction("串口工具", self.OpenSerialDlg, None, None,
'打开串口调试工具')
self.addActions(ToolMenu, (toolEncodUTF8Action,toolSerialAction, None))
......
#串口工具-子窗口
self.SerialDlg = Pyqt5_Serial()
......
该动作的槽函数:
def OpenSerialDlg(self):
if self.SerialDlg.bIsRunning is False:
self.SerialDlg.bIsRunning=True
self.SerialDlg.show()
pass
关闭“串口工具”时,在Pyqt5_Serial.closeEvent()清零bIsRunning 。打开或关闭串口时,设置对应的bIsOpened 状态。
class Pyqt5_Serial(QtWidgets.QWidget, Ui_Form):
......
# 打开串口
def port_open(self):
......
self.bIsOpened = True
......
# 关闭串口
def port_close(self):
......
self.bIsOpened = False
......
def closeEvent(self, event):
self.bIsRunning = False
if self.bIsOpened:
self.port_close()
self.close()
主窗口下载Hex文件时,判断串口是否打开,如果打开则先关闭:
def ProjectDownload(self): #
if self.SerialDlg.bIsOpened :
self.SerialDlg.port_close()
......
这样两个窗口的串口使用可以不互相干扰。
关闭主窗口时,记得把没有关闭的串口工具关闭,在QMainWindow.closeEvent()加入下面代码:
def closeEvent(self, event):
if self.SerialDlg.bIsRunning :
e = QCloseEvent()
self.SerialDlg.closeEvent(e)
pass
2.改动后的pyserial_demo.py
import serial
import serial.tools.list_ports
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QMessageBox
from PyQt5.QtCore import QTimer
class Ui_Form(object):
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.resize(707, 458)
self.formGroupBox = QtWidgets.QGroupBox(Dialog)
self.formGroupBox.setGeometry(QtCore.QRect(20, 20, 167, 301))
self.formGroupBox.setObjectName("formGroupBox")
self.formLayout = QtWidgets.QFormLayout(self.formGroupBox)
self.formLayout.setContentsMargins(10, 10, 10, 10)
self.formLayout.setSpacing(10)
self.formLayout.setObjectName("formLayout")
self.s1__lb_1 = QtWidgets.QLabel(self.formGroupBox)
self.s1__lb_1.setObjectName("s1__lb_1")
self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.s1__lb_1)
self.s1__box_1 = QtWidgets.QPushButton(self.formGroupBox)
self.s1__box_1.setAutoRepeatInterval(100)
self.s1__box_1.setDefault(True)
self.s1__box_1.setObjectName("s1__box_1")
self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.s1__box_1)
self.s1__lb_2 = QtWidgets.QLabel(self.formGroupBox)
self.s1__lb_2.setObjectName("s1__lb_2")
self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.s1__lb_2)
self.s1__box_2 = QtWidgets.QComboBox(self.formGroupBox)
self.s1__box_2.setObjectName("s1__box_2")
self.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.s1__box_2)
self.s1__lb_3 = QtWidgets.QLabel(self.formGroupBox)
self.s1__lb_3.setObjectName("s1__lb_3")
self.formLayout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.s1__lb_3)
self.s1__box_3 = QtWidgets.QComboBox(self.formGroupBox)
self.s1__box_3.setObjectName("s1__box_3")
self.s1__box_3.addItem("")
self.s1__box_3.addItem("")
self.s1__box_3.addItem("")
self.s1__box_3.addItem("")
self.s1__box_3.addItem("")
self.s1__box_3.addItem("")
self.s1__box_3.addItem("")
self.s1__box_3.addItem("")
self.s1__box_3.addItem("")
self.s1__box_3.addItem("")
self.s1__box_3.addItem("")
self.s1__box_3.addItem("")
self.formLayout.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.s1__box_3)
self.s1__lb_4 = QtWidgets.QLabel(self.formGroupBox)
self.s1__lb_4.setObjectName("s1__lb_4")
self.formLayout.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.s1__lb_4)
self.s1__box_4 = QtWidgets.QComboBox(self.formGroupBox)
self.s1__box_4.setObjectName("s1__box_4")
self.s1__box_4.addItem("")
self.s1__box_4.addItem("")
self.s1__box_4.addItem("")
self.s1__box_4.addItem("")
self.formLayout.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.s1__box_4)
self.s1__lb_5 = QtWidgets.QLabel(self.formGroupBox)
self.s1__lb_5.setObjectName("s1__lb_5")
self.formLayout.setWidget(5, QtWidgets.QFormLayout.LabelRole, self.s1__lb_5)
self.s1__box_5 = QtWidgets.QComboBox(self.formGroupBox)
self.s1__box_5.setObjectName("s1__box_5")
self.s1__box_5.addItem("")
self.formLayout.setWidget(5, QtWidgets.QFormLayout.FieldRole, self.s1__box_5)
self.open_button = QtWidgets.QPushButton(self.formGroupBox)
self.open_button.setObjectName("open_button")
self.formLayout.setWidget(7, QtWidgets.QFormLayout.SpanningRole, self.open_button)
self.close_button = QtWidgets.QPushButton(self.formGroupBox)
self.close_button.setObjectName("close_button")
self.formLayout.setWidget(8, QtWidgets.QFormLayout.SpanningRole, self.close_button)
self.s1__lb_6 = QtWidgets.QLabel(self.formGroupBox)
self.s1__lb_6.setObjectName("s1__lb_6")
self.formLayout.setWidget(6, QtWidgets.QFormLayout.LabelRole, self.s1__lb_6)
self.s1__box_6 = QtWidgets.QComboBox(self.formGroupBox)
self.s1__box_6.setObjectName("s1__box_6")
self.s1__box_6.addItem("")
self.formLayout.setWidget(6, QtWidgets.QFormLayout.FieldRole, self.s1__box_6)
self.state_label = QtWidgets.QLabel(self.formGroupBox)
self.state_label.setText("")
self.state_label.setTextFormat(QtCore.Qt.AutoText)
self.state_label.setScaledContents(True)
self.state_label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.state_label.setObjectName("state_label")
self.formLayout.setWidget(2, QtWidgets.QFormLayout.SpanningRole, self.state_label)
self.verticalGroupBox = QtWidgets.QGroupBox(Dialog)
self.verticalGroupBox.setGeometry(QtCore.QRect(210, 20, 401, 241))
self.verticalGroupBox.setObjectName("verticalGroupBox")
self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalGroupBox)
self.verticalLayout.setContentsMargins(10, 10, 10, 10)
self.verticalLayout.setObjectName("verticalLayout")
self.s2__receive_text = QtWidgets.QTextBrowser(self.verticalGroupBox)
self.s2__receive_text.setObjectName("s2__receive_text")
self.verticalLayout.addWidget(self.s2__receive_text)
self.verticalGroupBox_2 = QtWidgets.QGroupBox(Dialog)
self.verticalGroupBox_2.setGeometry(QtCore.QRect(210, 280, 401, 101))
self.verticalGroupBox_2.setObjectName("verticalGroupBox_2")
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.verticalGroupBox_2)
self.verticalLayout_2.setContentsMargins(10, 10, 10, 10)
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.s3__send_text = QtWidgets.QTextEdit(self.verticalGroupBox_2)
self.s3__send_text.setObjectName("s3__send_text")
self.verticalLayout_2.addWidget(self.s3__send_text)
self.s3__send_button = QtWidgets.QPushButton(Dialog)
self.s3__send_button.setGeometry(QtCore.QRect(620, 310, 61, 31))
self.s3__send_button.setObjectName("s3__send_button")
self.s3__clear_button = QtWidgets.QPushButton(Dialog)
self.s3__clear_button.setGeometry(QtCore.QRect(620, 350, 61, 31))
self.s3__clear_button.setObjectName("s3__clear_button")
self.formGroupBox1 = QtWidgets.QGroupBox(Dialog)
self.formGroupBox1.setGeometry(QtCore.QRect(20, 340, 171, 101))
self.formGroupBox1.setObjectName("formGroupBox1")
self.formLayout_2 = QtWidgets.QFormLayout(self.formGroupBox1)
self.formLayout_2.setContentsMargins(10, 10, 10, 10)
self.formLayout_2.setSpacing(10)
self.formLayout_2.setObjectName("formLayout_2")
self.label = QtWidgets.QLabel(self.formGroupBox1)
self.label.setObjectName("label")
self.formLayout_2.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label)
self.label_2 = QtWidgets.QLabel(self.formGroupBox1)
self.label_2.setObjectName("label_2")
self.formLayout_2.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_2)
self.lineEdit = QtWidgets.QLineEdit(self.formGroupBox1)
self.lineEdit.setObjectName("lineEdit")
self.formLayout_2.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.lineEdit)
self.lineEdit_2 = QtWidgets.QLineEdit(self.formGroupBox1)
self.lineEdit_2.setObjectName("lineEdit_2")
self.formLayout_2.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.lineEdit_2)
self.hex_send = QtWidgets.QCheckBox(Dialog)
self.hex_send.setGeometry(QtCore.QRect(620, 280, 71, 16))
self.hex_send.setObjectName("hex_send")
self.hex_receive = QtWidgets.QCheckBox(Dialog)
self.hex_receive.setGeometry(QtCore.QRect(620, 40, 71, 16))
self.hex_receive.setObjectName("hex_receive")
self.s2__clear_button = QtWidgets.QPushButton(Dialog)
self.s2__clear_button.setGeometry(QtCore.QRect(620, 80, 61, 31))
self.s2__clear_button.setObjectName("s2__clear_button")
self.timer_send_cb = QtWidgets.QCheckBox(Dialog)
self.timer_send_cb.setGeometry(QtCore.QRect(260, 390, 71, 16))
self.timer_send_cb.setObjectName("timer_send_cb")
self.lineEdit_3 = QtWidgets.QLineEdit(Dialog)
self.lineEdit_3.setGeometry(QtCore.QRect(350, 390, 61, 20))
self.lineEdit_3.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.lineEdit_3.setObjectName("lineEdit_3")
self.dw = QtWidgets.QLabel(Dialog)
self.dw.setGeometry(QtCore.QRect(420, 390, 54, 20))
self.dw.setObjectName("dw")
self.verticalGroupBox.raise_()
self.verticalGroupBox_2.raise_()
self.formGroupBox.raise_()
self.s3__send_button.raise_()
self.s3__clear_button.raise_()
self.formGroupBox.raise_()
self.hex_send.raise_()
self.hex_receive.raise_()
self.s2__clear_button.raise_()
self.timer_send_cb.raise_()
self.lineEdit_3.raise_()
self.dw.raise_()
self.retranslateUi(Dialog)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
self.formGroupBox.setTitle(_translate("Dialog", "串口设置"))
self.s1__lb_1.setText(_translate("Dialog", "串口检测:"))
self.s1__box_1.setText(_translate("Dialog", "检测串口"))
self.s1__lb_2.setText(_translate("Dialog", "串口选择:"))
self.s1__lb_3.setText(_translate("Dialog", "波特率:"))
self.s1__box_3.setItemText(0, _translate("Dialog", "115200"))
self.s1__box_3.setItemText(1, _translate("Dialog", "2400"))
self.s1__box_3.setItemText(2, _translate("Dialog", "4800"))
self.s1__box_3.setItemText(3, _translate("Dialog", "9600"))
self.s1__box_3.setItemText(4, _translate("Dialog", "14400"))
self.s1__box_3.setItemText(5, _translate("Dialog", "19200"))
self.s1__box_3.setItemText(6, _translate("Dialog", "38400"))
self.s1__box_3.setItemText(7, _translate("Dialog", "57600"))
self.s1__box_3.setItemText(8, _translate("Dialog", "76800"))
self.s1__box_3.setItemText(9, _translate("Dialog", "12800"))
self.s1__box_3.setItemText(10, _translate("Dialog", "230400"))
self.s1__box_3.setItemText(11, _translate("Dialog", "460800"))
self.s1__lb_4.setText(_translate("Dialog", "数据位:"))
self.s1__box_4.setItemText(0, _translate("Dialog", "8"))
self.s1__box_4.setItemText(1, _translate("Dialog", "7"))
self.s1__box_4.setItemText(2, _translate("Dialog", "6"))
self.s1__box_4.setItemText(3, _translate("Dialog", "5"))
self.s1__lb_5.setText(_translate("Dialog", "校验位:"))
self.s1__box_5.setItemText(0, _translate("Dialog", "N"))
self.open_button.setText(_translate("Dialog", "打开串口"))
self.close_button.setText(_translate("Dialog", "关闭串口"))
self.s1__lb_6.setText(_translate("Dialog", "停止位:"))
self.s1__box_6.setItemText(0, _translate("Dialog", "1"))
self.verticalGroupBox.setTitle(_translate("Dialog", "接受区"))
self.verticalGroupBox_2.setTitle(_translate("Dialog", "发送区"))
self.s3__send_text.setHtml(_translate("Dialog", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
"p, li { white-space: pre-wrap; }\n"
"</style></head><body style=\" font-family:\'SimSun\'; font-size:9pt; font-weight:400; font-style:normal;\">\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">123456</p></body></html>"))
self.s3__send_button.setText(_translate("Dialog", "发送"))
self.s3__clear_button.setText(_translate("Dialog", "清除"))
self.formGroupBox1.setTitle(_translate("Dialog", "串口状态"))
self.label.setText(_translate("Dialog", "已接收:"))
self.label_2.setText(_translate("Dialog", "已发送:"))
self.hex_send.setText(_translate("Dialog", "Hex发送"))
self.hex_receive.setText(_translate("Dialog", "Hex接收"))
self.s2__clear_button.setText(_translate("Dialog", "清除"))
self.timer_send_cb.setText(_translate("Dialog", "定时发送"))
self.lineEdit_3.setText(_translate("Dialog", "1000"))
self.dw.setText(_translate("Dialog", "ms/次"))
class Pyqt5_Serial(QtWidgets.QWidget, Ui_Form):
def __init__(self):
super(Pyqt5_Serial, self).__init__()
self.setupUi(self)
self.init()
self.setWindowTitle("串口小助手")
self.ser = serial.Serial()
self.port_check()
self.bIsOpened=False
self.bIsRunning=False
# 接收数据和发送数据数目置零
self.data_num_received = 0
self.lineEdit.setText(str(self.data_num_received))
self.data_num_sended = 0
self.lineEdit_2.setText(str(self.data_num_sended))
def init(self):
# 串口检测按钮
self.s1__box_1.clicked.connect(self.port_check)
# 串口信息显示
self.s1__box_2.currentTextChanged.connect(self.port_imf)
# 打开串口按钮
self.open_button.clicked.connect(self.port_open)
# 关闭串口按钮
self.close_button.clicked.connect(self.port_close)
# 发送数据按钮
self.s3__send_button.clicked.connect(self.data_send)
# 定时发送数据
self.timer_send = QTimer()
self.timer_send.timeout.connect(self.data_send)
self.timer_send_cb.stateChanged.connect(self.data_send_timer)
# 定时器接收数据
self.timer = QTimer(self)
self.timer.timeout.connect(self.data_receive)
# 清除发送窗口
self.s3__clear_button.clicked.connect(self.send_data_clear)
# 清除接收窗口
self.s2__clear_button.clicked.connect(self.receive_data_clear)
# 串口检测
def port_check(self):
# 检测所有存在的串口,将信息存储在字典中
self.Com_Dict = {}
port_list = list(serial.tools.list_ports.comports())
self.s1__box_2.clear()
for port in port_list:
self.Com_Dict["%s" % port[0]] = "%s" % port[1]
self.s1__box_2.addItem(port[0])
if len(self.Com_Dict) == 0:
self.state_label.setText(" 无串口")
# 串口信息
def port_imf(self):
# 显示选定的串口的详细信息
imf_s = self.s1__box_2.currentText()
if imf_s != "":
self.state_label.setText(self.Com_Dict[self.s1__box_2.currentText()])
# 打开串口
def port_open(self):
self.ser.port = self.s1__box_2.currentText()
self.ser.baudrate = int(self.s1__box_3.currentText())
self.ser.bytesize = int(self.s1__box_4.currentText())
self.ser.stopbits = int(self.s1__box_6.currentText())
self.ser.parity = self.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.open_button.setEnabled(False)
self.close_button.setEnabled(True)
self.formGroupBox1.setTitle("串口状态(已开启)")
self.bIsOpened=True
# 关闭串口
def port_close(self):
self.timer.stop()
self.timer_send.stop()
try:
self.ser.close()
except:
pass
self.open_button.setEnabled(True)
self.close_button.setEnabled(False)
self.lineEdit_3.setEnabled(True)
# 接收数据和发送数据数目置零
self.data_num_received = 0
self.lineEdit.setText(str(self.data_num_received))
self.data_num_sended = 0
self.lineEdit_2.setText(str(self.data_num_sended))
self.formGroupBox1.setTitle("串口状态(已关闭)")
self.bIsOpened=False
# 发送数据
def data_send(self):
if self.ser.isOpen():
input_s = self.s3__send_text.toPlainText()
if input_s != "":
# 非空字符串
if self.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, '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.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.hex_receive.checkState():
out_s = ''
for i in range(0, len(data)):
out_s = out_s + '{:02X}'.format(data[i]) + ' '
self.s2__receive_text.insertPlainText(out_s)
else:
# 串口接收到的字符串为b'123',要转化成unicode字符串才能输出到窗口中去
self.s2__receive_text.insertPlainText(data.decode('iso-8859-1'))
# 统计接收字符的数量
self.data_num_received += num
self.lineEdit.setText(str(self.data_num_received))
# 获取到text光标
textCursor = self.s2__receive_text.textCursor()
# 滚动到底部
textCursor.movePosition(textCursor.End)
# 设置光标到text中去
self.s2__receive_text.setTextCursor(textCursor)
else:
pass
# 定时发送数据
def data_send_timer(self):
if self.timer_send_cb.isChecked():
self.timer_send.start(int(self.lineEdit_3.text()))
self.lineEdit_3.setEnabled(False)
else:
self.timer_send.stop()
self.lineEdit_3.setEnabled(True)
# 清除显示
def send_data_clear(self):
self.s3__send_text.setText("")
def receive_data_clear(self):
self.s2__receive_text.setText("")
def closeEvent(self, event):
self.bIsRunning = False
if self.bIsOpened:
self.port_close()
self.close()