iT邦幫忙

2021 iThome 鐵人賽

DAY 11
0
Software Development

Python GUI 專案設計模式及好用的開發技巧系列 第 12

純手工打造UART版資料清洗工具之 PySide2 GUI 大補帖 - Part B

從七月開始從零開始研究PySide2 GUI相關設計及程式如何撰寫,已經有一段時間了,筆者深深有感,PySide 系列的水很深。

Let's GO ~~

  1. 如何把獲取以下項目的文字

    • 下拉式選單
      self.ui.[opt].currentText()

    • LineEdit
      self.ui.[opt].text()

    • spinbox
      self.ui.[opt].text()

    [筆記]

    • 括號內是變數名稱
  2. 下拉式選單的進階應用, 選擇選單中的任何項目,均得到不同的回應

以常見的UART (Serial Port)的連線設定為例

作法:

  1. 建立Parifc字典表
  2. 依照對應到的選項查表
  3. 回應查到的數值內容
Pari = {'None': "serial.PARITY_NONE", 'Even': "serial.PARITY_EVEN", 'Odd': "serial.PARITY_ODD",
             'Mark': "serial.PARITY_MARK", 'Space': "serial.PARITY_SPACE"}
fc = {"None": [False, False, False], "XON/ XOFF": [True, False, False], "RTS/CTS": [False, True, False],
                  "XON/ XOFF & RTS/CTS": [True, True, False],
                  "DTR/DSR": [False, False, True], "XON/ XOFF & DTR/DSR": [True, False, True]}
    def getdValue(self, whichDict, target):
        if debug:
            print(target, whichDict)
        for key, value in eval(whichDict).items():
            if key == target:
                return value

fxopt = ["'COM{0}'".format(self.ui.le_eqComID.text()),
        int(self.ui.spb_eqBaudRate.text()), int(self.ui.le_eqDatabit.text()),
        self.fn.getdictValue("pari", self.ui.cb_eqParity.currentText()),
        int(self.ui.le_eqStopbit.text()),
        float(self.ui.le_eqTimeout.text()) / 1000,
        self.getdValue("fc",
        self.ui.cb_eqFlowControl.currentText()),
        ]
  1. 新增文字到不同Textbox元件的另類用法,使用eval

作法:

  1. 筆者在function中傳入textbox的變數名稱,利用eval的特性移除雙引號,再將之導入到變數中
  2. 指標移到最後面,簡單的說就是新一行的開頭
  3. 插入文字
 def out(self, edit, message):
        """(edit, msg) Write any message to any textbox.
            edit: any Textbox label , e.g txtbox_logs
            msg: any message
        """
        export = eval("self.ui.{0}".format(edit))
        export.moveCursor(QtGui.QTextCursor.End)
        export.insertPlainText("{0} {1}\r\n".format(self.fn.currDate(), message))
  1. 如何將UI及功能分開,防止UI被凍結

簡單的說就是要使用thread 去切開使用

作法:

  1. 將要呈現在UI上的動作放在一個thread中
  2. 使用class 去做傳入UI前的資料處理
  3. 使用slot及emit的方式將資料傳出去 (類似Queue的原理)
from PySide2.QtWidgets import QApplication, QMainWindow, QMessageBox, QWidget, QCheckBox
from PySide2 import QtGui, QtCore
from PySide2.QtCore import QThread, Signal, QObject, QEvent, Qt
class MainWindow(QMainWindow):

    def __init__(self):
        super(MainWindow, self).__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        .....
        .....
    def runTasks(self):	
        self.Transmitter = from()
        self.thread1 = QThread()
        self.thread1.started.connect(self.Into.work)
        self.Transmitter.from.connect(self.from)
        self.Transmitter.finished.connect(self.Loop_finished)
        self.Transmitter.finished.connect(self.thread1.quit)
        self.Transmitter.finished.connect(self.Transmitter.deleteLater)
        self.thread1.finished.connect(self.thread1.deleteLater)
        self.Transmitter.moveToThread(self.thread1)
        self.thread1.start()        

    def from(self, values):
            self.ui.txtbox_tx.clear()
            self.ui.txtbox_tx.moveCursor(QtGui.QTextCursor.End)
            self.ui.txtbox_tx.insertPlainText(values + "\r\n")
            
    def Loop_finished(self):
        print("Into Loop: Offline")
        print("From Loop: Offline")
class from(QObject):
    finished = Signal()
    from = Signal(str)

    @QtCore.Slot()
    def __init__(self):
        super(Receiver, self).__init__()
        self.working = True
        self.checkTime = 0

    def work(self):
        answer = answer + 100
		while True:
			self.from.emit(autoAnswer)
			time.sleep(0.2)
			if answer > 2000:
			    break        
        self.finished.emit()
  1. 如何在UI中抓取Enter event

使用event handler 偵測鍵盤事件,但筆者想要做的是當某個區域被觸發後鍵盤事件才會開始偵測。

會這麼設計的原因是,不想要UI一執行的時候就占用大量資源,反而是需要用的時候再啟用相關功能就好了

未來在維護上也會相對簡單許多

作法:

  1. 使用按鈕觸發一個事件後讓Groupbox 啟用
  2. 開始在輸入視窗內偵測鍵盤按鍵,如果有出現Enter Key 就直接將輸入的內容傳入上方的文字視窗中
  3. 清除輸入框的文字
  4. 點選Exit , 關閉Groupbox,停止偵測鍵盤事件

效果:

class MainWindow(QMainWindow):

    def __init__(self):
        ...
        ...
        self.ui.btn_conExit.clicked.connect(lambda: self.exitSingleMode())
        self.ui.btnSingle.clicked.connect(lambda: self.singleMode())

    def singleMode(self):
        self.ui.grpObj_SingleMode.setEnabled(True)
        self.ui.cmdline.installEventFilter(self)
        self.ui.btn_single.setEnabled(False)
        
    def exitSingleMode(self):
        self.ui.btn_single.setEnabled(True)
        self.ui.grpObj_SingleMode.setEnabled(False)
        self.ui.cmdline.removeEventFilter(self)

結論:
基本上筆者整理的資料,是以簡單的template為主,很多詳細說明是刻意沒寫出來的,因為筆者需要的很多未來可以使用到的範例。讓未來的自己可以直接複製貼上再改點變數就可以直接套用的。或者是可以把這些範本直接寫入code snippet中當作未來的參考


上一篇
純手工打造UART版資料清洗工具之 Pyside2 GUI 大補帖 - Part A
下一篇
快樂打包又很坑的pyinstaller
系列文
Python GUI 專案設計模式及好用的開發技巧31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言