目錄
1、UI設計
2、將UI檔案轉換為Py檔案
3、邏輯功能實作
3.1、初始化程式
3.2、串口檢測程式
3.3、 設定及打開串口程式
3.4、定時發送資料程式
3.5、發送資料程式
3.6、接收資料程式
3.7、保存日志程式
3.8、加載日志程式
3.9、打開博客、公眾號程式
3.10、清除發送和接收資料顯示程式
3.11、關閉串口程式
Python Qt GUI設計系列博文終于到了實戰篇,本篇博文將貫穿之前的基礎知識點實作一款串口除錯助手,

關注【公眾號】 美男子玩編程,回復關鍵字:串口除錯助手,獲取專案原始碼~
1、UI設計
UI設計使用Qt Creator實作,組件布局如下所示:

2、將UI檔案轉換為Py檔案
這里使用Python腳本的方式將UI檔案轉換為Python檔案,代碼如下所示:
import os
import os.path
dir ='./' #檔案所在的路徑
#找出路徑下所有的.ui檔案
def listUiFile():
list = []
files = os.listdir(dir)
for filename in files:
#print(filename)
if os.path.splitext(filename)[1] == '.ui':
list.append(filename)
return list
#把擴展名未.ui的轉換成.py的檔案
def transPyFile(filename):
return os.path.splitext(filename)[0] + '.py'
#通過命令把.ui檔案轉換成.py檔案
def runMain():
list = listUiFile()
for uifile in list:
pyfile = transPyFile(uifile)
cmd = 'pyuic5 -o {pyfile} {uifile}'.format(pyfile=pyfile, uifile=uifile)
os.system(cmd)
if __name__ =="__main__":
runMain()
3、邏輯功能實作
3.1、初始化程式
首先初始化一些組件和標志位的狀態,設定信號與槽的關系,實作代碼如下所示:
# 初始化程式
def __init__(self):
super(Pyqt5_Serial, self).__init__()
self.setupUi(self)
self.init()
self.ser = serial.Serial()
self.port_check()
# 設定Logo和標題
self.setWindowIcon(QIcon('Com.png'))
self.setWindowTitle("串口除錯助手 【公眾號】美男子玩編程")
# 設定禁止拉伸視窗大小
self.setFixedSize(self.width(), self.height())
# 發送資料和接收資料數目置零
self.data_num_sended = 0
self.Lineedit2.setText(str(self.data_num_sended))
self.data_num_received = 0
self.Lineedit3.setText(str(self.data_num_received))
# 串口關閉按鈕使能關閉
self.Pushbuttom3.setEnabled(False)
# 發送框、文本框清除
self.Text1.setText("")
self.Text2.setText("")
# 建立控制元件信號與槽關系
def init(self):
# 串口檢測按鈕
self.Pushbuttom2.clicked.connect(self.port_check)
# 串口打開按鈕
self.Pushbuttom1.clicked.connect(self.port_open)
# 串口關閉按鈕
self.Pushbuttom3.clicked.connect(self.port_close)
# 定時發送資料
self.timer_send = QTimer()
self.timer_send.timeout.connect(self.data_send)
self.Checkbox7.stateChanged.connect(self.data_send_timer)
# 發送資料按鈕
self.Pushbuttom6.clicked.connect(self.data_send)
# 加載日志
self.Pushbuttom4.clicked.connect(self.savefiles)
# 加載日志
self.Pushbuttom5.clicked.connect(self.openfiles)
# 跳轉鏈接
self.commandLinkButton1.clicked.connect(self.link)
# 清除發送按鈕
self.Pushbuttom7.clicked.connect(self.send_data_clear)
# 清除接收按鈕
self.Pushbuttom8.clicked.connect(self.receive_data_clear)
3.2、串口檢測程式
檢測電腦上所有串口,實作代碼如下所示:
# 串口檢測
def port_check(self):
# 檢測所有存在的串口,將資訊存盤在字典中
self.Com_Dict = {}
port_list = list(serial.tools.list_ports.comports())
self.Combobox1.clear()
for port in port_list:
self.Com_Dict["%s" % port[0]] = "%s" % port[1]
self.Combobox1.addItem(port[0])
# 無串口判斷
if len(self.Com_Dict) == 0:
self.Combobox1.addItem("無串口")
3.3、 設定及打開串口程式
檢測到串口后進行配置,打開串口,并且啟動定時器一直接收用戶輸入,實作代碼如下所示:
# 打開串口
def port_open(self):
self.ser.port = self.Combobox1.currentText() # 串口號
self.ser.baudrate = int(self.Combobox2.currentText()) # 波特率
flag_data = int(self.Combobox3.currentText()) # 資料位
if flag_data == 5:
self.ser.bytesize = serial.FIVEBITS
elif flag_data == 6:
self.ser.bytesize = serial.SIXBITS
elif flag_data == 7:
self.ser.bytesize = serial.SEVENBITS
else:
self.ser.bytesize = serial.EIGHTBITS
flag_data = self.Combobox4.currentText() # 校驗位
if flag_data == "None":
self.ser.parity = serial.PARITY_NONE
elif flag_data == "Odd":
self.ser.parity = serial.PARITY_ODD
else:
self.ser.parity = serial.PARITY_EVEN
flag_data = int(self.Combobox5.currentText()) # 停止位
if flag_data == 1:
self.ser.stopbits = serial.STOPBITS_ONE
else:
self.ser.stopbits = serial.STOPBITS_TWO
flag_data = self.Combobox6.currentText() # 流控
if flag_data == "No Ctrl Flow":
self.ser.xonxoff = False #軟體流控
self.ser.dsrdtr = False #硬體流控 DTR
self.ser.rtscts = False #硬體流控 RTS
elif flag_data == "SW Ctrl Flow":
self.ser.xonxoff = True #軟體流控
else:
if self.Checkbox3.isChecked():
self.ser.dsrdtr = True #硬體流控 DTR
if self.Checkbox4.isChecked():
self.ser.rtscts = True #硬體流控 RTS
try:
time.sleep(0.1)
self.ser.open()
except:
QMessageBox.critical(self, "串口例外", "此串口不能被打開!")
return None
# 串口打開后,切換開關串口按鈕使能狀態,防止失誤操作
if self.ser.isOpen():
self.Pushbuttom1.setEnabled(False)
self.Pushbuttom3.setEnabled(True)
self.formGroupBox1.setTitle("串口狀態(開啟)")
# 定時器接收資料
self.timer = QTimer()
self.timer.timeout.connect(self.data_receive)
# 打開串口接收定時器,周期為1ms
self.timer.start(1)
3.4、定時發送資料程式
通過定時器,可支持1ms至30s之間資料定時,實作代碼如下所示:
# 定時發送資料
def data_send_timer(self):
try:
if 1<= int(self.Lineedit1.text()) <= 30000: # 定時時間1ms~30s內
if self.Checkbox7.isChecked():
self.timer_send.start(int(self.Lineedit1.text()))
self.Lineedit1.setEnabled(False)
else:
self.timer_send.stop()
self.Lineedit1.setEnabled(True)
else:
QMessageBox.critical(self, '定時發送資料例外', '定時發送資料周期僅可設定在30秒內!')
except:
QMessageBox.critical(self, '定時發送資料例外', '請設定正確的數值型別!')
3.5、發送資料程式
可以發送ASCII字符和十六進制型別資料,并且可以在資料前顯示發送的時間,在資料后進行換行,發送一個位元組,TX標志會自動累加,實作代碼如下所示:
# 發送資料
def data_send(self):
if self.ser.isOpen():
input_s = self.Text2.toPlainText()
# 判斷是否為非空字串
if input_s != "":
# 時間顯示
if self.Checkbox5.isChecked():
self.Text1.insertPlainText((time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) + " ")
# HEX發送
if self.Checkbox1.isChecked():
input_s = input_s.strip()
send_list = []
while input_s != '':
try:
num = int(input_s[0:2], 16)
except ValueError:
QMessageBox.critical(self, '串口例外', '請輸入規范十六進制資料,以空格分開!')
return None
input_s = input_s[2:].strip()
send_list.append(num)
input_s = bytes(send_list)
# ASCII發送
else:
input_s = (input_s).encode('utf-8')
# HEX接收顯示
if self.Checkbox2.isChecked():
out_s = ''
for i in range(0, len(input_s)):
out_s = out_s + '{:02X}'.format(input_s[i]) + ' '
self.Text1.insertPlainText(out_s)
# ASCII接收顯示
else:
self.Text1.insertPlainText(input_s.decode('utf-8'))
# 接識訓行
if self.Checkbox6.isChecked():
self.Text1.insertPlainText('\r\n')
# 獲取到Text游標
textCursor = self.Text1.textCursor()
# 滾動到底部
textCursor.movePosition(textCursor.End)
# 設定游標到Text中去
self.Text1.setTextCursor(textCursor)
# 統計發送字符數量
num = self.ser.write(input_s)
self.data_num_sended += num
self.Lineedit2.setText(str(self.data_num_sended))
else:
pass
3.6、接收資料程式
可以接收ASCII字符和十六進制型別資料,并且可以在資料前顯示發送的時間,在資料后進行換行,接收一個位元組,RX標志會自動累加,實作代碼如下所示:
# 接收資料
def data_receive(self):
try:
num = self.ser.inWaiting()
if num > 0:
time.sleep(0.1)
num = self.ser.inWaiting() #延時,再讀一次資料,確保資料完整性
except:
QMessageBox.critical(self, '串口例外', '串口接收資料例外,請重新連接設備!')
self.port_close()
return None
if num > 0:
data = self.ser.read(num)
num = len(data)
# 時間顯示
if self.Checkbox5.isChecked():
self.Text1.insertPlainText((time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) + " ")
# HEX顯示資料
if self.Checkbox2.checkState():
out_s = ''
for i in range(0, len(data)):
out_s = out_s + '{:02X}'.format(data[i]) + ' '
self.Text1.insertPlainText(out_s)
# ASCII顯示資料
else:
self.Text1.insertPlainText(data.decode('utf-8'))
# 接識訓行
if self.Checkbox6.isChecked():
self.Text1.insertPlainText('\r\n')
# 獲取到text游標
textCursor = self.Text1.textCursor()
# 滾動到底部
textCursor.movePosition(textCursor.End)
# 設定游標到text中去
self.Text1.setTextCursor(textCursor)
# 統計接收字符的數量
self.data_num_received += num
self.Lineedit3.setText(str(self.data_num_received))
else:
pass
3.7、保存日志程式
將接收框中收發的資料保存到TXT文本中,實作代碼如下所示:
# 保存日志
def savefiles(self):
dlg = QFileDialog()
filenames = dlg.getSaveFileName(None, "保存日志檔案", None, "Txt files(*.txt)")
try:
with open(file = filenames[0], mode='w', encoding='utf-8') as file:
file.write(self.Text1.toPlainText())
except:
QMessageBox.critical(self, '日志例外', '保存日志檔案失敗!')
3.8、加載日志程式
加載保存到TXT文本中的資料資訊到發送框中,實作代碼如下所示:
# 加載日志
def openfiles(self):
dlg = QFileDialog()
filenames = dlg.getOpenFileName(None, "加載日志檔案", None, "Txt files(*.txt)")
try:
with open(file = filenames[0], mode='r', encoding='utf-8') as file:
self.Text2.setPlainText(file.read())
except:
QMessageBox.critical(self, '日志例外', '加載日志檔案失敗!')
3.9、打開博客、公眾號程式
點擊按鈕,打開我的公眾號二維碼和博客主頁,實作代碼如下所示:
# 打開博客鏈接和公眾號二維碼
def link(self):
dialog = QDialog()
label_img = QLabel()
label_img.setAlignment(Qt.AlignCenter)
label_img.setPixmap(QPixmap("./img.jpg"))
vbox = QVBoxLayout()
vbox.addWidget(label_img)
dialog.setLayout(vbox)
dialog.setWindowTitle("快掃碼關注公眾號吧~")
dialog.setWindowModality(Qt.ApplicationModal)
dialog.exec_()
webbrowser.open('https://blog.csdn.net/m0_38106923')
3.10、清除發送和接收資料顯示程式
清除發送資料框和接收資料框的內容和計數次數,實作代碼如下所示:
# 清除發送資料顯示
def send_data_clear(self):
self.Text2.setText("")
self.data_num_sended = 0
self.Lineedit2.setText(str(self.data_num_sended))
# 清除接收資料顯示
def receive_data_clear(self):
self.Text1.setText("")
self.data_num_received = 0
self.Lineedit3.setText(str(self.data_num_received))
3.11、關閉串口程式
關閉串口,停止定時器,重置組件和標志狀態,實作代碼如下所示:
# 關閉串口
def port_close(self):
try:
self.timer.stop()
self.timer_send.stop()
self.ser.close()
except:
QMessageBox.critical(self, '串口例外', '關閉串口失敗,請重啟程式!')
return None
# 切換開關串口按鈕使能狀態和定時發送使能狀態
self.Pushbuttom1.setEnabled(True)
self.Pushbuttom3.setEnabled(False)
self.Lineedit1.setEnabled(True)
# 發送資料和接收資料數目置零
self.data_num_sended = 0
self.Lineedit2.setText(str(self.data_num_sended))
self.data_num_received = 0
self.Lineedit3.setText(str(self.data_num_received))
self.formGroupBox1.setTitle("串口狀態(關閉)")
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/404145.html
標籤:python
