- 背景介紹
pyside6提供了Qt6的Python側API. 在GUI程序撰寫方面, 筆者不太喜歡頻繁的編譯過程, 傾向於隨時更改代碼即時查看效果. 因此, 推薦在簡單應用的情況下使用pyside6, 而非直接的Qt6. 本文以一個簡單的計算器實現作為案例展示筆者pyside6使用流程. - 使用pyside6-designer繪制並生成ui文件
①. 安裝pyside6
終端運行: pip install pyside6
②. 打開pyside6-designer
終端運行: pyside6-designer
③. 於pyside6-designer中利用左側控件欄繪制出如下界面
注意, 可以在右側對象欄中改變相應控件對象的名稱.
④. 保存該界面為ui文件, 並命名為calculator.ui. calculator.ui文件內容如下,
1 <?xml version="1.0" encoding="UTF-8"?> 2 <ui version="4.0"> 3 <class>Form</class> 4 <widget class="QWidget" name="Form"> 5 <property name="geometry"> 6 <rect> 7 <x>0</x> 8 <y>0</y> 9 <width>440</width> 10 <height>288</height> 11 </rect> 12 </property> 13 <property name="windowTitle"> 14 <string>Form</string> 15 </property> 16 <layout class="QVBoxLayout" name="verticalLayout"> 17 <item> 18 <widget class="QTextBrowser" name="txtb_show"/> 19 </item> 20 <item> 21 <layout class="QGridLayout" name="gridLayout"> 22 <item row="0" column="0"> 23 <widget class="QPushButton" name="btn_ac"> 24 <property name="text"> 25 <string>AC</string> 26 </property> 27 </widget> 28 </item> 29 <item row="0" column="3"> 30 <widget class="QPushButton" name="btn_divide"> 31 <property name="text"> 32 <string>÷</string> 33 </property> 34 </widget> 35 </item> 36 <item row="1" column="0"> 37 <widget class="QPushButton" name="btn_7"> 38 <property name="text"> 39 <string>7</string> 40 </property> 41 </widget> 42 </item> 43 <item row="1" column="1"> 44 <widget class="QPushButton" name="btn_8"> 45 <property name="text"> 46 <string>8</string> 47 </property> 48 </widget> 49 </item> 50 <item row="1" column="2"> 51 <widget class="QPushButton" name="btn_9"> 52 <property name="text"> 53 <string>9</string> 54 </property> 55 </widget> 56 </item> 57 <item row="1" column="3"> 58 <widget class="QPushButton" name="btn_multiply"> 59 <property name="text"> 60 <string>×</string> 61 </property> 62 </widget> 63 </item> 64 <item row="2" column="0"> 65 <widget class="QPushButton" name="btn_4"> 66 <property name="text"> 67 <string>4</string> 68 </property> 69 </widget> 70 </item> 71 <item row="2" column="1"> 72 <widget class="QPushButton" name="btn_5"> 73 <property name="text"> 74 <string>5</string> 75 </property> 76 </widget> 77 </item> 78 <item row="2" column="2"> 79 <widget class="QPushButton" name="btn_6"> 80 <property name="text"> 81 <string>6</string> 82 </property> 83 </widget> 84 </item> 85 <item row="2" column="3"> 86 <widget class="QPushButton" name="btn_subtract"> 87 <property name="text"> 88 <string>−</string> 89 </property> 90 </widget> 91 </item> 92 <item row="3" column="0"> 93 <widget class="QPushButton" name="btn_1"> 94 <property name="text"> 95 <string>1</string> 96 </property> 97 </widget> 98 </item> 99 <item row="3" column="1"> 100 <widget class="QPushButton" name="btn_2"> 101 <property name="text"> 102 <string>2</string> 103 </property> 104 </widget> 105 </item> 106 <item row="3" column="2"> 107 <widget class="QPushButton" name="btn_3"> 108 <property name="text"> 109 <string>3</string> 110 </property> 111 </widget> 112 </item> 113 <item row="3" column="3"> 114 <widget class="QPushButton" name="btn_add"> 115 <property name="text"> 116 <string>+</string> 117 </property> 118 </widget> 119 </item> 120 <item row="4" column="3"> 121 <widget class="QPushButton" name="btn_equal"> 122 <property name="text"> 123 <string>=</string> 124 </property> 125 </widget> 126 </item> 127 <item row="4" column="0"> 128 <widget class="QPushButton" name="btn_0"> 129 <property name="text"> 130 <string>0</string> 131 </property> 132 </widget> 133 </item> 134 <item row="4" column="1"> 135 <widget class="QPushButton" name="btn_lbracket"> 136 <property name="text"> 137 <string>(</string> 138 </property> 139 </widget> 140 </item> 141 <item row="4" column="2"> 142 <widget class="QPushButton" name="btn_rbracket"> 143 <property name="text"> 144 <string>)</string> 145 </property> 146 </widget> 147 </item> 148 <item row="0" column="2"> 149 <widget class="QPushButton" name="btn_dot"> 150 <property name="text"> 151 <string>.</string> 152 </property> 153 </widget> 154 </item> 155 <item row="0" column="1"> 156 <widget class="QPushButton" name="btn_ce"> 157 <property name="text"> 158 <string>CE</string> 159 </property> 160 </widget> 161 </item> 162 </layout> 163 </item> 164 </layout> 165 </widget> 166 <resources/> 167 <connections/> 168 </ui>
- 使用pyside6演示ui文件
本文以一個main.py文件為例, 采用如下方式演示ui文件, 並補充其中操作邏輯. main.py文件內容如下,
1 # 計算器實現案例 2 3 from PySide6 import QtCore 4 from PySide6 import QtWidgets 5 from PySide6 import QtUiTools 6 7 8 class Calculator(QtWidgets.QWidget): # 此處繼承的類型需與designer中選擇的類型保持一致 9 10 def __init__(self, parent=None): 11 super(Calculator, self).__init__(parent) 12 13 uiname = "/Users/xxhbdk/Desktop/projects_blog/pyside6/calculator.ui" # 1. 設置ui文件路徑 14 self.ui = QtUiTools.QUiLoader().load(uiname) # 2. 實例化 15 self.ui.setParent(self) # 3. 設置父控件 16 self.__equal_tab = False 17 18 self.__create_connections() 19 20 21 def __create_connections(self): 22 self.ui.btn_ac.clicked.connect(self.__print_clr_all) 23 self.ui.btn_ce.clicked.connect(self.__print_clr_end) 24 self.ui.btn_lbracket.clicked.connect(lambda : self.__print_line_end("(")) 25 self.ui.btn_rbracket.clicked.connect(lambda : self.__print_line_end(")")) 26 self.ui.btn_divide.clicked.connect(lambda : self.__print_line_end("÷")) 27 self.ui.btn_7.clicked.connect(lambda : self.__print_line_end("7")) 28 self.ui.btn_8.clicked.connect(lambda : self.__print_line_end("8")) 29 self.ui.btn_9.clicked.connect(lambda : self.__print_line_end("9")) 30 self.ui.btn_multiply.clicked.connect(lambda : self.__print_line_end("×")) 31 self.ui.btn_4.clicked.connect(lambda : self.__print_line_end("4")) 32 self.ui.btn_5.clicked.connect(lambda : self.__print_line_end("5")) 33 self.ui.btn_6.clicked.connect(lambda : self.__print_line_end("6")) 34 self.ui.btn_subtract.clicked.connect(lambda : self.__print_line_end("-")) 35 self.ui.btn_1.clicked.connect(lambda : self.__print_line_end("1")) 36 self.ui.btn_2.clicked.connect(lambda : self.__print_line_end("2")) 37 self.ui.btn_3.clicked.connect(lambda : self.__print_line_end("3")) 38 self.ui.btn_add.clicked.connect(lambda : self.__print_line_end("+")) 39 self.ui.btn_0.clicked.connect(lambda : self.__print_line_end("0")) 40 self.ui.btn_dot.clicked.connect(lambda : self.__print_line_end(".")) 41 self.ui.btn_equal.clicked.connect(self.__equal) 42 43 44 def __equal(self): 45 txt = self.ui.txtb_show.toPlainText() 46 txt = txt.replace("×", "*") 47 txt = txt.replace("÷", "/") 48 try: 49 ret = str(round(eval(txt), 9)) 50 except Exception as e: 51 ret = str(e) 52 53 self.__print_line_new(ret) 54 self.__equal_tab = True 55 56 57 def __print_line_end(self, msg): 58 if self.__equal_tab: 59 self.__print_clr_all() 60 self.__equal_tab = False 61 self.ui.txtb_show.insertPlainText(msg) 62 63 64 def __print_clr_all(self): 65 self.ui.txtb_show.clear() 66 67 68 def __print_clr_end(self): 69 self.ui.txtb_show.textCursor().deletePreviousChar() 70 71 72 def __print_line_new(self, msg): 73 self.ui.txtb_show.append(msg) 74 75 76 77 if __name__ == "__main__": 78 app = QtWidgets.QApplication([]) 79 80 obj = Calculator() 81 obj.show() 82 83 app.exec()
①. 設置ui文件路徑
②. 實例化
③. 設置父控件
執行上述main.py, 演示界面如下, - 使用pyside6-uic翻譯ui文件為py文件
於終端運行如下命令翻譯ui文件, 並生成同名py文件,
pyside6-uic ./calculator.ui -o ./calculator.py
其中, calculator.ui需要提供正確的路徑, calculator.py為翻譯后的py文件. calculator.py文件內容如下,
1 # -*- coding: utf-8 -*- 2 3 ################################################################################ 4 ## Form generated from reading UI file 'calculator.ui' 5 ## 6 ## Created by: Qt User Interface Compiler version 6.2.2 7 ## 8 ## WARNING! All changes made in this file will be lost when recompiling UI file! 9 ################################################################################ 10 11 from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale, 12 QMetaObject, QObject, QPoint, QRect, 13 QSize, QTime, QUrl, Qt) 14 from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor, 15 QFont, QFontDatabase, QGradient, QIcon, 16 QImage, QKeySequence, QLinearGradient, QPainter, 17 QPalette, QPixmap, QRadialGradient, QTransform) 18 from PySide6.QtWidgets import (QApplication, QGridLayout, QPushButton, QSizePolicy, 19 QTextBrowser, QVBoxLayout, QWidget) 20 21 class Ui_Form(object): 22 def setupUi(self, Form): 23 if not Form.objectName(): 24 Form.setObjectName(u"Form") 25 Form.resize(440, 288) 26 self.verticalLayout = QVBoxLayout(Form) 27 self.verticalLayout.setObjectName(u"verticalLayout") 28 self.txtb_show = QTextBrowser(Form) 29 self.txtb_show.setObjectName(u"txtb_show") 30 31 self.verticalLayout.addWidget(self.txtb_show) 32 33 self.gridLayout = QGridLayout() 34 self.gridLayout.setObjectName(u"gridLayout") 35 self.btn_ac = QPushButton(Form) 36 self.btn_ac.setObjectName(u"btn_ac") 37 38 self.gridLayout.addWidget(self.btn_ac, 0, 0, 1, 1) 39 40 self.btn_divide = QPushButton(Form) 41 self.btn_divide.setObjectName(u"btn_divide") 42 43 self.gridLayout.addWidget(self.btn_divide, 0, 3, 1, 1) 44 45 self.btn_7 = QPushButton(Form) 46 self.btn_7.setObjectName(u"btn_7") 47 48 self.gridLayout.addWidget(self.btn_7, 1, 0, 1, 1) 49 50 self.btn_8 = QPushButton(Form) 51 self.btn_8.setObjectName(u"btn_8") 52 53 self.gridLayout.addWidget(self.btn_8, 1, 1, 1, 1) 54 55 self.btn_9 = QPushButton(Form) 56 self.btn_9.setObjectName(u"btn_9") 57 58 self.gridLayout.addWidget(self.btn_9, 1, 2, 1, 1) 59 60 self.btn_multiply = QPushButton(Form) 61 self.btn_multiply.setObjectName(u"btn_multiply") 62 63 self.gridLayout.addWidget(self.btn_multiply, 1, 3, 1, 1) 64 65 self.btn_4 = QPushButton(Form) 66 self.btn_4.setObjectName(u"btn_4") 67 68 self.gridLayout.addWidget(self.btn_4, 2, 0, 1, 1) 69 70 self.btn_5 = QPushButton(Form) 71 self.btn_5.setObjectName(u"btn_5") 72 73 self.gridLayout.addWidget(self.btn_5, 2, 1, 1, 1) 74 75 self.btn_6 = QPushButton(Form) 76 self.btn_6.setObjectName(u"btn_6") 77 78 self.gridLayout.addWidget(self.btn_6, 2, 2, 1, 1) 79 80 self.btn_subtract = QPushButton(Form) 81 self.btn_subtract.setObjectName(u"btn_subtract") 82 83 self.gridLayout.addWidget(self.btn_subtract, 2, 3, 1, 1) 84 85 self.btn_1 = QPushButton(Form) 86 self.btn_1.setObjectName(u"btn_1") 87 88 self.gridLayout.addWidget(self.btn_1, 3, 0, 1, 1) 89 90 self.btn_2 = QPushButton(Form) 91 self.btn_2.setObjectName(u"btn_2") 92 93 self.gridLayout.addWidget(self.btn_2, 3, 1, 1, 1) 94 95 self.btn_3 = QPushButton(Form) 96 self.btn_3.setObjectName(u"btn_3") 97 98 self.gridLayout.addWidget(self.btn_3, 3, 2, 1, 1) 99 100 self.btn_add = QPushButton(Form) 101 self.btn_add.setObjectName(u"btn_add") 102 103 self.gridLayout.addWidget(self.btn_add, 3, 3, 1, 1) 104 105 self.btn_equal = QPushButton(Form) 106 self.btn_equal.setObjectName(u"btn_equal") 107 108 self.gridLayout.addWidget(self.btn_equal, 4, 3, 1, 1) 109 110 self.btn_0 = QPushButton(Form) 111 self.btn_0.setObjectName(u"btn_0") 112 113 self.gridLayout.addWidget(self.btn_0, 4, 0, 1, 1) 114 115 self.btn_lbracket = QPushButton(Form) 116 self.btn_lbracket.setObjectName(u"btn_lbracket") 117 118 self.gridLayout.addWidget(self.btn_lbracket, 4, 1, 1, 1) 119 120 self.btn_rbracket = QPushButton(Form) 121 self.btn_rbracket.setObjectName(u"btn_rbracket") 122 123 self.gridLayout.addWidget(self.btn_rbracket, 4, 2, 1, 1) 124 125 self.btn_dot = QPushButton(Form) 126 self.btn_dot.setObjectName(u"btn_dot") 127 128 self.gridLayout.addWidget(self.btn_dot, 0, 2, 1, 1) 129 130 self.btn_ce = QPushButton(Form) 131 self.btn_ce.setObjectName(u"btn_ce") 132 133 self.gridLayout.addWidget(self.btn_ce, 0, 1, 1, 1) 134 135 136 self.verticalLayout.addLayout(self.gridLayout) 137 138 139 self.retranslateUi(Form) 140 141 QMetaObject.connectSlotsByName(Form) 142 # setupUi 143 144 def retranslateUi(self, Form): 145 Form.setWindowTitle(QCoreApplication.translate("Form", u"Form", None)) 146 self.btn_ac.setText(QCoreApplication.translate("Form", u"AC", None)) 147 self.btn_divide.setText(QCoreApplication.translate("Form", u"\u00f7", None)) 148 self.btn_7.setText(QCoreApplication.translate("Form", u"7", None)) 149 self.btn_8.setText(QCoreApplication.translate("Form", u"8", None)) 150 self.btn_9.setText(QCoreApplication.translate("Form", u"9", None)) 151 self.btn_multiply.setText(QCoreApplication.translate("Form", u"\u00d7", None)) 152 self.btn_4.setText(QCoreApplication.translate("Form", u"4", None)) 153 self.btn_5.setText(QCoreApplication.translate("Form", u"5", None)) 154 self.btn_6.setText(QCoreApplication.translate("Form", u"6", None)) 155 self.btn_subtract.setText(QCoreApplication.translate("Form", u"\u2212", None)) 156 self.btn_1.setText(QCoreApplication.translate("Form", u"1", None)) 157 self.btn_2.setText(QCoreApplication.translate("Form", u"2", None)) 158 self.btn_3.setText(QCoreApplication.translate("Form", u"3", None)) 159 self.btn_add.setText(QCoreApplication.translate("Form", u"+", None)) 160 self.btn_equal.setText(QCoreApplication.translate("Form", u"=", None)) 161 self.btn_0.setText(QCoreApplication.translate("Form", u"0", None)) 162 self.btn_lbracket.setText(QCoreApplication.translate("Form", u"(", None)) 163 self.btn_rbracket.setText(QCoreApplication.translate("Form", u")", None)) 164 self.btn_dot.setText(QCoreApplication.translate("Form", u".", None)) 165 self.btn_ce.setText(QCoreApplication.translate("Form", u"CE", None)) 166 # retranslateUi
- 使用pyside6演示py文件
同樣以一個main.py文件為例, 展示如何引入翻譯后的calculator.py, 並演示之.
當前工程結構如下,
main.py文件內容如下,1 # 計算器實現案例 2 3 from PySide6 import QtCore 4 from PySide6 import QtWidgets 5 from PySide6 import QtUiTools 6 import argparse # 0. 額外導入, 暫時無需關注 7 8 from calculator import Ui_Form # 1. 引入calculator模塊 9 10 11 class Calculator(QtWidgets.QWidget): # 此處繼承的類型需與designer中選擇的類型保持一致 12 13 def __init__(self, parent=None): 14 super(Calculator, self).__init__(parent) 15 16 # uiname = "/Users/xxhbdk/Desktop/projects_blog/pyside6/calculator.ui" # ui文件路徑 17 # self.ui = QtUiTools.QUiLoader().load(uiname) 18 # self.ui.setParent(self) 19 self.ui = Ui_Form() # 2. 實例化 20 self.ui.setupUi(self) # 3. 設置父控件 21 self.__equal_tab = False 22 23 self.__create_connections() 24 25 26 def __create_connections(self): 27 self.ui.btn_ac.clicked.connect(self.__print_clr_all) 28 self.ui.btn_ce.clicked.connect(self.__print_clr_end) 29 self.ui.btn_lbracket.clicked.connect(lambda : self.__print_line_end("(")) 30 self.ui.btn_rbracket.clicked.connect(lambda : self.__print_line_end(")")) 31 self.ui.btn_divide.clicked.connect(lambda : self.__print_line_end("÷")) 32 self.ui.btn_7.clicked.connect(lambda : self.__print_line_end("7")) 33 self.ui.btn_8.clicked.connect(lambda : self.__print_line_end("8")) 34 self.ui.btn_9.clicked.connect(lambda : self.__print_line_end("9")) 35 self.ui.btn_multiply.clicked.connect(lambda : self.__print_line_end("×")) 36 self.ui.btn_4.clicked.connect(lambda : self.__print_line_end("4")) 37 self.ui.btn_5.clicked.connect(lambda : self.__print_line_end("5")) 38 self.ui.btn_6.clicked.connect(lambda : self.__print_line_end("6")) 39 self.ui.btn_subtract.clicked.connect(lambda : self.__print_line_end("-")) 40 self.ui.btn_1.clicked.connect(lambda : self.__print_line_end("1")) 41 self.ui.btn_2.clicked.connect(lambda : self.__print_line_end("2")) 42 self.ui.btn_3.clicked.connect(lambda : self.__print_line_end("3")) 43 self.ui.btn_add.clicked.connect(lambda : self.__print_line_end("+")) 44 self.ui.btn_0.clicked.connect(lambda : self.__print_line_end("0")) 45 self.ui.btn_dot.clicked.connect(lambda : self.__print_line_end(".")) 46 self.ui.btn_equal.clicked.connect(self.__equal) 47 48 49 def __equal(self): 50 txt = self.ui.txtb_show.toPlainText() 51 txt = txt.replace("×", "*") 52 txt = txt.replace("÷", "/") 53 try: 54 ret = str(round(eval(txt), 9)) 55 except Exception as e: 56 ret = str(e) 57 58 self.__print_line_new(ret) 59 self.__equal_tab = True 60 61 62 def __print_line_end(self, msg): 63 if self.__equal_tab: 64 self.__print_clr_all() 65 self.__equal_tab = False 66 self.ui.txtb_show.insertPlainText(msg) 67 68 69 def __print_clr_all(self): 70 self.ui.txtb_show.clear() 71 72 73 def __print_clr_end(self): 74 self.ui.txtb_show.textCursor().deletePreviousChar() 75 76 77 def __print_line_new(self, msg): 78 self.ui.txtb_show.append(msg) 79 80 81 82 if __name__ == "__main__": 83 app = QtWidgets.QApplication([]) 84 85 obj = Calculator() 86 obj.show() 87 88 app.exec()
①. 引入翻譯后的py文件模塊
②. 實例化
③. 設置父控件
執行上述main.py, 演示界面如下, - 使用nuitka工具打包為獨立app
①. 安裝nuitka
終端運行: pip3 install nuitka
②. 將當前工程打包為獨立app
終端運行: nuitka3 --onefile --macos-disable-console --enable-plugin=pyside6 main.py -o calculator.app
打包完成后, 於當前目錄下生成可執行文件"calculator.app". 雙擊該可執行文件, 運行效果如下,
可以看到, 可執行文件運行符合預期. - 注意事項
①. macos下python部分模塊導入可能存在問題, 終端運行app發現后, 可手動在py文件中額外導入, 如筆者手動額外導入的argparse模塊;
②. macos下nuitka最終輸出文件之后綴".app"需手動添加, 如不添加, 則運行帶有終端;
③. 本文着重闡述pyside6配合nuitka之使用流程, 具體API使用細節, 請大家參考官方文檔等資料. - 參考文檔