所有文章除特別聲明外,均采用 CC BY-NC-SA 4.0 許可協(xié)議。轉(zhuǎn)載請(qǐng)注明來(lái)自 nixgnauhcuy's blog!
如需轉(zhuǎn)載,請(qǐng)標(biāo)明出處!
Life is short, you need Python. —— Bruce Eckel
前言
工作中偶爾會(huì)用到 python 來(lái)制作一些小工具,所以針對(duì)這些小工具,做了些簡(jiǎn)化,移除了一些比較復(fù)雜的功能,拿來(lái)分享。
UI 界面
在實(shí)現(xiàn)具體邏輯之前,我們要先設(shè)計(jì)好串口工具的 UI 界面,這里我直接使用的 Qt Designer 來(lái)設(shè)計(jì)串口界面的布局。
最終效果布置好布局后是這樣的:

實(shí)際預(yù)覽效果:

實(shí)現(xiàn)
打開 Qt Designer,點(diǎn)擊新建 Main Window 窗體,

進(jìn)入項(xiàng)目界面后,我們需要往里面拖入我們需要的控件,先添加兩個(gè) QGroupBox 控件并且設(shè)置文本為串口接收設(shè)置和串口發(fā)送設(shè)置, 一個(gè)作為我們的接收區(qū),一個(gè)作為我們的發(fā)送區(qū)。

上面操作完成后,我們先來(lái)布置我們的接收區(qū),先添加 Label 控件,分別設(shè)置文本為串口選擇、波特率、停止位、數(shù)據(jù)位、校驗(yàn)位的五個(gè) Label 控件。

然后,我們要增加組合框,方便后面選擇串口的參數(shù),同樣從左邊拖出五個(gè) Combo Box 控件,然后雙擊控件,這里我只拿波特率的組合框來(lái)描述,其他的例如停止位數(shù)據(jù)位還有校驗(yàn)位的操作是一樣的,除了串口選擇的組合框不用設(shè)置外,其他的都要設(shè)置。

雙擊波特率的組合框控件后,點(diǎn)擊左下的 + 號(hào),增加我們可選的波特率就可以了。

其他的 Combo Box 控件設(shè)置如下:
停止位分別設(shè)置 1、1.5、2 三個(gè)選項(xiàng)
數(shù)據(jù)位分別設(shè)置 8、7、6、5 四個(gè)選項(xiàng)
校驗(yàn)位分別設(shè)置 None、Odd、Even 三個(gè)選項(xiàng)
這樣就把常用的波特率、停止位、數(shù)據(jù)位和校驗(yàn)位的選項(xiàng)框設(shè)置好了。
接下來(lái),我們?cè)黾觾蓚€(gè) RadioButton 和一個(gè) Check Box 控件,分別設(shè)置兩個(gè) RadioButton 文本為 ASCII 和 HEX,設(shè)置 Check Box 文本為時(shí)間戳。

最后,增加兩個(gè) Push Button 控件還有一個(gè) TextEdit 控件,Push Button 分別將文本命名為清除接收和打開串口。

這樣基本上接收區(qū)就設(shè)置完畢了。
接下來(lái)是發(fā)送區(qū),發(fā)送區(qū)中 Label、TextEdit、RadioButton、 Push Button 還有 Check Box 上面已經(jīng)說(shuō)過(guò)了,同樣設(shè)置一下就可以了,這里就不占用太多篇幅了。

隨后,我們要添加一個(gè) LineEdit 控件,后面可以作為定時(shí)發(fā)送的時(shí)間。

這樣,發(fā)送區(qū)也設(shè)置完畢了。
上面的接收區(qū)和發(fā)送區(qū)做完后,我們要將控件擺放整齊,并且設(shè)置控件的 objectName,方便后面編寫程序調(diào)用控件時(shí),一眼就知道編輯的是哪個(gè)控件。
控件的擺放我就不過(guò)多贅述了,無(wú)非就是調(diào)整位置和寬高,這里我標(biāo)注一下我設(shè)置的控件 objectName,不然后面看代碼的時(shí)候就不知道我是對(duì)哪個(gè)控件操作。

生成對(duì)應(yīng)的 .py 文件
完成后,我們保存我們做好的 UI 到工程下,這里我將文件命名為 uart_tool_ui.ui 然后保存到后面要編寫串口工具的工程目錄下。保存的 .ui 文件還不能直接使用,要將它轉(zhuǎn)為 .py 文件,方便后續(xù)調(diào)用,這里我使用的編譯器是 vscode,使用 vscode 將 .ui 轉(zhuǎn)為 .py 輸出,vscode 要安裝 pyqt 的擴(kuò)展,這里我就不多說(shuō)了,網(wǎng)上搜一下就可以了,我就直接貼上轉(zhuǎn)化好的 .py 代碼。
uart_tool_ui.py
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'd:\test\my\python_uart_tool\uart_tool_ui.ui'
#
# Created by: PyQt5 UI code generator 5.13.0
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 506)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.groupBox = QtWidgets.QGroupBox(self.centralwidget)
self.groupBox.setGeometry(QtCore.QRect(10, 10, 781, 291))
self.groupBox.setObjectName("groupBox")
self.timestamp_check_box = QtWidgets.QCheckBox(self.groupBox)
self.timestamp_check_box.setGeometry(QtCore.QRect(10, 225, 61, 16))
self.timestamp_check_box.setObjectName("timestamp_check_box")
self.label_7 = QtWidgets.QLabel(self.groupBox)
self.label_7.setGeometry(QtCore.QRect(10, 175, 54, 12))
self.label_7.setObjectName("label_7")
self.rec_hex_radio_button = QtWidgets.QRadioButton(self.groupBox)
self.rec_hex_radio_button.setGeometry(QtCore.QRect(70, 200, 41, 16))
self.rec_hex_radio_button.setObjectName("rec_hex_radio_button")
self.com_combo_box = QtWidgets.QComboBox(self.groupBox)
self.com_combo_box.setGeometry(QtCore.QRect(10, 50, 141, 22))
self.com_combo_box.setObjectName("com_combo_box")
self.stopbit_combo_box = QtWidgets.QComboBox(self.groupBox)
self.stopbit_combo_box.setGeometry(QtCore.QRect(58, 110, 91, 22))
self.stopbit_combo_box.setObjectName("stopbit_combo_box")
self.stopbit_combo_box.addItem("")
self.stopbit_combo_box.addItem("")
self.stopbit_combo_box.addItem("")
self.label_6 = QtWidgets.QLabel(self.groupBox)
self.label_6.setGeometry(QtCore.QRect(10, 145, 54, 12))
self.label_6.setObjectName("label_6")
self.baud_combo_box = QtWidgets.QComboBox(self.groupBox)
self.baud_combo_box.setGeometry(QtCore.QRect(58, 80, 91, 22))
self.baud_combo_box.setObjectName("baud_combo_box")
self.baud_combo_box.addItem("")
self.baud_combo_box.addItem("")
self.baud_combo_box.addItem("")
self.baud_combo_box.addItem("")
self.baud_combo_box.addItem("")
self.baud_combo_box.addItem("")
self.baud_combo_box.addItem("")
self.baud_combo_box.addItem("")
self.baud_combo_box.addItem("")
self.baud_combo_box.addItem("")
self.baud_combo_box.addItem("")
self.baud_combo_box.addItem("")
self.baud_combo_box.addItem("")
self.baud_combo_box.addItem("")
self.baud_combo_box.addItem("")
self.baud_combo_box.addItem("")
self.baud_combo_box.addItem("")
self.baud_combo_box.addItem("")
self.baud_combo_box.addItem("")
self.baud_combo_box.addItem("")
self.baud_combo_box.addItem("")
self.baud_combo_box.addItem("")
self.label_5 = QtWidgets.QLabel(self.groupBox)
self.label_5.setGeometry(QtCore.QRect(10, 115, 54, 12))
self.label_5.setObjectName("label_5")
self.label_4 = QtWidgets.QLabel(self.groupBox)
self.label_4.setGeometry(QtCore.QRect(10, 85, 54, 12))
self.label_4.setObjectName("label_4")
self.checkbit_combo_box = QtWidgets.QComboBox(self.groupBox)
self.checkbit_combo_box.setGeometry(QtCore.QRect(58, 170, 91, 22))
self.checkbit_combo_box.setObjectName("checkbit_combo_box")
self.checkbit_combo_box.addItem("")
self.checkbit_combo_box.addItem("")
self.checkbit_combo_box.addItem("")
self.label_3 = QtWidgets.QLabel(self.groupBox)
self.label_3.setGeometry(QtCore.QRect(10, 22, 54, 20))
self.label_3.setObjectName("label_3")
self.databit_combo_box = QtWidgets.QComboBox(self.groupBox)
self.databit_combo_box.setGeometry(QtCore.QRect(58, 140, 91, 22))
self.databit_combo_box.setObjectName("databit_combo_box")
self.databit_combo_box.addItem("")
self.databit_combo_box.addItem("")
self.databit_combo_box.addItem("")
self.databit_combo_box.addItem("")
self.uart_en_push_button = QtWidgets.QPushButton(self.groupBox)
self.uart_en_push_button.setGeometry(QtCore.QRect(10, 250, 141, 23))
self.uart_en_push_button.setObjectName("uart_en_push_button")
self.rec_ascii_radio_button = QtWidgets.QRadioButton(self.groupBox)
self.rec_ascii_radio_button.setGeometry(QtCore.QRect(10, 200, 51, 16))
self.rec_ascii_radio_button.setObjectName("rec_ascii_radio_button")
self.uart_rec_show = QtWidgets.QTextEdit(self.groupBox)
self.uart_rec_show.setGeometry(QtCore.QRect(170, 10, 601, 271))
self.uart_rec_show.setFocusPolicy(QtCore.Qt.NoFocus)
self.uart_rec_show.setObjectName("uart_rec_show")
self.uart_clear_rec_push_button = QtWidgets.QPushButton(self.groupBox)
self.uart_clear_rec_push_button.setGeometry(QtCore.QRect(70, 220, 71, 23))
self.uart_clear_rec_push_button.setObjectName("uart_clear_rec_push_button")
self.groupBox_2 = QtWidgets.QGroupBox(self.centralwidget)
self.groupBox_2.setGeometry(QtCore.QRect(10, 300, 781, 151))
self.groupBox_2.setObjectName("groupBox_2")
self.send_ascii_radio_button = QtWidgets.QRadioButton(self.groupBox_2)
self.send_ascii_radio_button.setGeometry(QtCore.QRect(10, 20, 51, 16))
self.send_ascii_radio_button.setObjectName("send_ascii_radio_button")
self.send_hex_radio_button = QtWidgets.QRadioButton(self.groupBox_2)
self.send_hex_radio_button.setGeometry(QtCore.QRect(70, 20, 41, 16))
self.send_hex_radio_button.setObjectName("send_hex_radio_button")
self.uart_timer_check_box = QtWidgets.QCheckBox(self.groupBox_2)
self.uart_timer_check_box.setGeometry(QtCore.QRect(10, 47, 47, 16))
self.uart_timer_check_box.setObjectName("uart_timer_check_box")
self.uart1_ms_label = QtWidgets.QLabel(self.groupBox_2)
self.uart1_ms_label.setGeometry(QtCore.QRect(140, 45, 20, 20))
self.uart1_ms_label.setObjectName("uart1_ms_label")
self.uart_timer_line_edit = QtWidgets.QLineEdit(self.groupBox_2)
self.uart_timer_line_edit.setGeometry(QtCore.QRect(70, 45, 61, 20))
self.uart_timer_line_edit.setObjectName("uart_timer_line_edit")
self.uart_send_push_button = QtWidgets.QPushButton(self.groupBox_2)
self.uart_send_push_button.setGeometry(QtCore.QRect(80, 70, 75, 23))
self.uart_send_push_button.setObjectName("uart_send_push_button")
self.uart_send_clear_push_button = QtWidgets.QPushButton(self.groupBox_2)
self.uart_send_clear_push_button.setGeometry(QtCore.QRect(0, 70, 75, 23))
self.uart_send_clear_push_button.setObjectName("uart_send_clear_push_button")
self.uart_send_show = QtWidgets.QTextEdit(self.groupBox_2)
self.uart_send_show.setGeometry(QtCore.QRect(170, 10, 601, 131))
self.uart_send_show.setFocusPolicy(QtCore.Qt.NoFocus)
self.uart_send_show.setObjectName("uart_send_show")
self.label = QtWidgets.QLabel(self.groupBox_2)
self.label.setGeometry(QtCore.QRect(10, 100, 18, 12))
self.label.setObjectName("label")
self.uart_tx_data_count_label = QtWidgets.QLabel(self.groupBox_2)
self.uart_tx_data_count_label.setGeometry(QtCore.QRect(30, 120, 100, 12))
self.uart_tx_data_count_label.setObjectName("uart_tx_data_count_label")
self.uart_rx_data_count_label = QtWidgets.QLabel(self.groupBox_2)
self.uart_rx_data_count_label.setGeometry(QtCore.QRect(30, 100, 100, 12))
self.uart_rx_data_count_label.setObjectName("uart_rx_data_count_label")
self.label_2 = QtWidgets.QLabel(self.groupBox_2)
self.label_2.setGeometry(QtCore.QRect(10, 120, 18, 12))
self.label_2.setObjectName("label_2")
MainWindow.setCentralWidget(self.centralwidget)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.menuBar = QtWidgets.QMenuBar(MainWindow)
self.menuBar.setGeometry(QtCore.QRect(0, 0, 800, 23))
self.menuBar.setObjectName("menuBar")
self.menu = QtWidgets.QMenu(self.menuBar)
self.menu.setObjectName("menu")
MainWindow.setMenuBar(self.menuBar)
self.actionguanyu = QtWidgets.QAction(MainWindow)
self.actionguanyu.setObjectName("actionguanyu")
self.menu.addAction(self.actionguanyu)
self.menuBar.addAction(self.menu.menuAction())
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.groupBox.setTitle(_translate("MainWindow", "串口接收設(shè)置"))
self.timestamp_check_box.setText(_translate("MainWindow", "時(shí)間戳"))
self.label_7.setText(_translate("MainWindow", "校驗(yàn)位"))
self.rec_hex_radio_button.setText(_translate("MainWindow", "HEX"))
self.stopbit_combo_box.setItemText(0, _translate("MainWindow", "1"))
self.stopbit_combo_box.setItemText(1, _translate("MainWindow", "1.5"))
self.stopbit_combo_box.setItemText(2, _translate("MainWindow", "2"))
self.label_6.setText(_translate("MainWindow", "數(shù)據(jù)位"))
self.baud_combo_box.setItemText(0, _translate("MainWindow", "110"))
self.baud_combo_box.setItemText(1, _translate("MainWindow", "300"))
self.baud_combo_box.setItemText(2, _translate("MainWindow", "600"))
self.baud_combo_box.setItemText(3, _translate("MainWindow", "1200"))
self.baud_combo_box.setItemText(4, _translate("MainWindow", "2400"))
self.baud_combo_box.setItemText(5, _translate("MainWindow", "4800"))
self.baud_combo_box.setItemText(6, _translate("MainWindow", "9600"))
self.baud_combo_box.setItemText(7, _translate("MainWindow", "14400"))
self.baud_combo_box.setItemText(8, _translate("MainWindow", "19200"))
self.baud_combo_box.setItemText(9, _translate("MainWindow", "38400"))
self.baud_combo_box.setItemText(10, _translate("MainWindow", "43000"))
self.baud_combo_box.setItemText(11, _translate("MainWindow", "57600"))
self.baud_combo_box.setItemText(12, _translate("MainWindow", "76800"))
self.baud_combo_box.setItemText(13, _translate("MainWindow", "115200"))
self.baud_combo_box.setItemText(14, _translate("MainWindow", "128000"))
self.baud_combo_box.setItemText(15, _translate("MainWindow", "230400"))
self.baud_combo_box.setItemText(16, _translate("MainWindow", "256000"))
self.baud_combo_box.setItemText(17, _translate("MainWindow", "460800"))
self.baud_combo_box.setItemText(18, _translate("MainWindow", "921600"))
self.baud_combo_box.setItemText(19, _translate("MainWindow", "1000000"))
self.baud_combo_box.setItemText(20, _translate("MainWindow", "2000000"))
self.baud_combo_box.setItemText(21, _translate("MainWindow", "3000000"))
self.label_5.setText(_translate("MainWindow", "停止位"))
self.label_4.setText(_translate("MainWindow", "波特率"))
self.checkbit_combo_box.setItemText(0, _translate("MainWindow", "None"))
self.checkbit_combo_box.setItemText(1, _translate("MainWindow", "Odd"))
self.checkbit_combo_box.setItemText(2, _translate("MainWindow", "Even"))
self.label_3.setText(_translate("MainWindow", "串口選擇"))
self.databit_combo_box.setItemText(0, _translate("MainWindow", "8"))
self.databit_combo_box.setItemText(1, _translate("MainWindow", "7"))
self.databit_combo_box.setItemText(2, _translate("MainWindow", "6"))
self.databit_combo_box.setItemText(3, _translate("MainWindow", "5"))
self.uart_en_push_button.setText(_translate("MainWindow", "打開串口"))
self.rec_ascii_radio_button.setText(_translate("MainWindow", "ASCII"))
self.uart_clear_rec_push_button.setText(_translate("MainWindow", "清除接收"))
self.groupBox_2.setTitle(_translate("MainWindow", "串口發(fā)送設(shè)置"))
self.send_ascii_radio_button.setText(_translate("MainWindow", "ASCII"))
self.send_hex_radio_button.setText(_translate("MainWindow", "HEX"))
self.uart_timer_check_box.setText(_translate("MainWindow", "定時(shí)"))
self.uart1_ms_label.setText(_translate("MainWindow", "ms"))
self.uart_send_push_button.setText(_translate("MainWindow", "發(fā)送數(shù)據(jù)"))
self.uart_send_clear_push_button.setText(_translate("MainWindow", "清除發(fā)送"))
self.label.setText(_translate("MainWindow", "R:"))
self.uart_tx_data_count_label.setText(_translate("MainWindow", "0"))
self.uart_rx_data_count_label.setText(_translate("MainWindow", "0"))
self.label_2.setText(_translate("MainWindow", "T:"))
self.menu.setTitle(_translate("MainWindow", "幫助"))
self.actionguanyu.setText(_translate("MainWindow", "關(guān)于"))
查看效果
在工程中編寫我們的 main.py,運(yùn)行看看效果,
main.py 代碼如下
import os
import sys
from Ui_uart_tool_ui import Ui_MainWindow
from PyQt5.QtWidgets import (QApplication, QMainWindow)
class MyPyQT_Form(QMainWindow, Ui_MainWindow):
def __init__(self):
super().__init__()
self.setupUi(self)
if __name__ == '__main__':
app = QApplication(sys.argv)
my_pyqt_form = MyPyQT_Form()
my_pyqt_form.show()
sys.exit(app.exec_())
然后運(yùn)行效果如下:

結(jié)語(yǔ)
python 串口邏輯代碼的編寫,得留到下一篇了,平時(shí)工作忙不是特別有空,等我空閑的時(shí)候再補(bǔ)上!我本身不是學(xué) python 的,所以難免會(huì)有出錯(cuò)或者你認(rèn)為代碼不合理的地方,這些也希望多多諒解和指教,我會(huì)及時(shí)修改的!
關(guān)于本篇的相關(guān)代碼我也上傳到 github 和 csdn 上去了,有興趣的可以訪問(wèn) github 或 csdn 查看,后面的代碼也會(huì)在這個(gè)倉(cāng)庫(kù)中補(bǔ)充,不介意也可以點(diǎn)個(gè) star。