[PyQT] PySide2 教程 #3: 條條大路通UI

你喜歡手動,自動,還是半自動?

Photo by Eggy Febryano on Unsplash

Qt有個獨特的地方,有類似html+css一般的ui檔可以建構畫面,也能以底層的library去新增以及排版(C++ / Python皆有)。而寫程式有時候,只要喜歡、情況允許,有有何不可呢?

About GUI Rendering

首先,要來了解一下Qt當中的畫面,有一下兩種方式可以實作

  • ui file: 使用QtCreator 或 QtDesigner經由GUI介面拖拉修改完成
  • Qt LIB: 使用Qt 內建的各種元件類別做宣告、初始化、給予客製化參數

其中兩者各有好壞,會在下面一一介紹,但事實上在實際開發中,這兩種方式其實是並用的,差異在於比例分配多寡,這也因開發時程、內容而有所差異。

Using .ui file in PySide2

使用QtDesigner開發UI只需要拖曳,並且在右方的參數欄位修改即可完成,比起Qt Library煩雜的宣告,還要一行行的設定初始化,如果不是需要動態修改的內容,使用ui檔能省上許多時間。

使用ui file 在PySide2中有兩個方法:

  1. 使用QUILoader:將建立好的 .ui 以動態的方式runtime的import到程式中
  2. 使用 pyside2-uic: 將 .ui檔案編譯成python原始碼,直接import 並使用

結論先上!經過研究,在開發上如果不是特別需求,會建議使用QUILoader來做處理,原因請看…

pyside2-uic

這是一個官方提供的套件,非常方便,只要將你由QtDesigner做出的.ui檔用這個tool直行即可。

 pyside2-uic mainwindow.ui > ui_mainwindow.py

使用上也非常方便, 就如同一般python module一樣 (這邊使用官方教學)

import sys
from PySide2.QtWidgets import QApplication, QMainWindow
from PySide2.QtCore import QFile
from ui_mainwindow import Ui_MainWindow

class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

if __name__ == "__main__":
    app = QApplication(sys.argv)

    window = MainWindow()
    window.show()

    sys.exit(app.exec_())

But! 人生就是有這麼個但是,

pyside2-uic所編譯出來的.py檔案,有以下幾個缺點:

  1. coding style: 亂七八糟,完全沒按照 PEP8
  2. 直譯式翻譯: 這才是最大的問題,沒有for loop,也沒有資料結構,因此比較複雜的畫面編譯後將會動輒3、4千行不在話下。

考慮到後續的維護,以及程式的可讀性,最後選擇放棄使用這個方法。

QUILoader

顧名思義,UI Loader使用上也非常簡單,就是將外部的ui檔動態讀取並宣告到記憶體。

缺點也很明顯,會佔用較大的記憶體容量 …so what?

使用方法如下 (再次引用官方教學):

import sys
from PySide2.QtUiTools import QUiLoader
from PySide2.QtWidgets import QApplication
from PySide2.QtCore import QFile

if __name__ == "__main__":
    app = QApplication(sys.argv)

    ui_file_name = "mainwindow.ui"
    ui_file = QFile(ui_file_name)
    if not ui_file.open(QIODevice.ReadOnly):
        print("Cannot open {}: {}".format(ui_file_name, ui_file.errorString()))
        sys.exit(-1)
    loader = QUiLoader()
    window = loader.load(ui_file)
    ui_file.close()
    if not window:
        print(loader.errorString())
        sys.exit(-1)
    window.show()

    sys.exit(app.exec_())

Using Qt Library

UI檔雖然開發上非常方便,也很快速,但有一好沒兩好,同樣一個物件,在QtDesigner 的attribute介面總是會少了幾個參數,要畫表格時,總是想用for-loop一次填滿所有的格子,這時候使用程式碼就更加方便了。

上一篇 [PyQt] PySide2 教程 #10: 專案!學以致用!的範例便是以Qt library所畫的,可以看到其中沒有用到任何ui檔案也能畫出畫面。

#!venv/bin/python3
import sys

from PySide2 import QtWidgets
from PySide2.QtWidgets import QLabel
from PySide2.QtWidgets import QMainWindow
from PySide2.QtWidgets import QPushButton


class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        """Main window, holding all user interface including.

        Args:
          parent: parent class of main window
        Returns:
          None
        Raises:
          None
        """
        super(MainWindow, self).__init__(parent)
        self._width = 800
        self._height = 600
        self._title = QLabel('PySide2 is Great', self)
        self._exit_btn = QPushButton('Exit', self)

        self.setMinimumSize(self._width, self._height)
if '__main__' == __name__:
    app = QtWidgets.QApplication(sys.argv)
    w = MainWindow()
    w.show()

    ret = app.exec_()
    sys.exit(ret)

結論

可以說,動態且複雜的UI處理就交給後台的Qt Library,而靜態又不常修改的就交給ui 檔案去做設定。有沒有覺得很像web的開發呢?沒錯,就是相同的概念喔!

總結一下,我們開發時,ui 以及 Qt Lib 是共用的,兩者可以相互串接、互相修改,而uic這個工具,知道就可以了,如果真的想要靜態生成,那我會建議,全部用Qt Lib手刻吧!(我以前就是這個流派的愛好者呢…只是同事說code太長看的很痛苦…)

下一篇:[PyQt] PySide2 教程 #4: Layout使用,让GUI更加整齐又富有弹性