# MainWindow ###### tags: `python` `pyqt` `tutorials` ## 參考 - [QT 5.13 doc - QMainWindow Class](https://doc.qt.io/qt-5/qmainwindow.html) - [pyside2 doc - QMainWindow](https://doc-snapshots.qt.io/qtforpython/PySide2/QtWidgets/QMainWindow.html) - [Qt學習之QMainWindow(一)QMainWindow簡介](https://www.itread01.com/content/1550176744.html) - [Qt 学习之路 2(7):MainWindow 简介](https://www.devbean.net/2012/08/qt-study-road-2-mainwindow/) ## 簡介 QMainWindow是 Qt 框架带来的一个预定义好的主窗口类。所谓主窗口,就是一个普通意义上的应用程序(不是指游戏之类的那种)最顶层的窗口。比如你现在正在使用的浏览器,那么主窗口就是这个浏览器窗口。试着回想一下经典的主窗口,通常是由一个标题栏,一个菜单栏,若干工具栏和一个任务栏。 以上節錄自 https://www.devbean.net/2012/08/qt-study-road-2-mainwindow/ Qt中的頂層視窗稱為MainWindow,屬於類QMainWindow,QMainWindow也是繼承於QWidget。通過子類化QMainWindow可以建立一個應用程式的視窗。 MainWindow的結構分為五個部分:選單欄(Menu Bar)、工具欄(Toolbars)、停靠視窗(Dock Widgets)、狀態列(Status Bar)和中央視窗(Central Widget)。可以用下面的圖形表示之。 ![](https://img-blog.csdn.net/20150312141226941?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaHVsaWZhbmdqaWF5b3U=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 其中,中央視窗可以使用任何形式的widget來填充。一般不建議使中央視窗為空。可以使用setCentralWidget()函式來填充中央視窗。 以上節錄自 https://www.itread01.com/content/1550176744.html ## 操作範例 以下範例會用 第一個視窗程式 中的範例來介紹。專案在 [pyqt-example](https://gitlab.com/MVMC-lab/pyqt-example)的 exp01 中,可以自行 clone 下來研究。 ### 觀察 ui/mainwindow.ui 以下是ui的呈現 ![](https://i.imgur.com/KgJvtj5.png) 看 ui/mainwindow.ui ,可以發現有三個物件 1. textBrowser ```xml <item> <widget class="QTextBrowser" name="textBrowser"/> </item> ``` 2. pushButton_1 ```xml <item> <widget class="QPushButton" name="pushButton_1"> <property name="text"> <string>PushButton 1</string> </property> </widget> </item> ``` 3. pushButton_2 ```xml <item> <widget class="QPushButton" name="pushButton_2"> <property name="text"> <string>PushButton 1</string> </property> </widget> </item> ``` ### 觀察 app/ui_mainwindow.py 透過指令 `pyuic5 ui/mainwindow.ui -o app/ui_mainwindow.py` 轉換。 轉換會依據最上層物件的object name,去生成相對應的class名稱 `Ui_xxxxxx`,在此範例中最上層物件名稱是 `MainWindow`,所以會生成`Ui_MainWindow`的物件 接著,觀察轉換好的檔案,可以看到上述三的物件被轉換成 `class Ui_MainWindow` 的 member。 1. textBrowser ```py self.textBrowser = QtWidgets.QTextBrowser(self.centralwidget) self.textBrowser.setObjectName("textBrowser") ``` 2. pushButton_1 ```py self.pushButton_1 = QtWidgets.QPushButton(self.centralwidget) self.pushButton_1.setObjectName("pushButton_1") ``` 3. pushButton_2 ```py self.pushButton_2 = QtWidgets.QPushButton(self.centralwidget) self.pushButton_2.setObjectName("pushButton_2") ``` 所以如果我們用 `class Ui_MainWindow` 去創建一個物件,我們就可以使用上面三個 member。 Ex: ```py a = Ui_MainWindow() a.pushButton_1.setText('QQ') text = a.textBrowser.currentText() ``` ### 創建 QMainWindow 物件 在每個應用中通常會有一個 MainWindow ,也就是主視窗,通常會由一個class去管理。 創建一個 `class MainWindow` 繼承 `Ui_MainWindow` 以及 `QMainWindow` ,用來管理主視窗,而 `MainWindow` 因為繼承了上述兩個物件,所以繼承了兩個父類別的所有member,可以使用其中的元件及函式。 接著,在物件創立(\_\_init\_\_)時,使用繼承自 `Ui_MainWindow` 的函式 `setupUi()` 來初始化UI介面, setupUi 的參數是指我們要在哪創立、呈現其中的物件。 因為要初始化的是主視窗,所以要傳入的型態是 `QMainWindow` ,也就是`class MainWindow`本身。 ```py from PyQt5.QtWidgets import QMainWindow from .ui_mainwindow import Ui_MainWindow class MainWindow(QMainWindow, Ui_MainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.setupUi(self) ``` 如此一來,我們就可以創立一個 `MainWindow` ,並去存取它其中ui的元件了。 ```py mw = Ui_MainWindow() mw.pushButton_1.setText('QQ') text = a.textBrowser.currentText() ``` 上面是比較常見的寫法,為了讓主視窗相關的物件有較強的相依性才這樣處理。 下面是另一種寫法,缺點是a跟ui應該有強烈的相依性,卻不在同一物件中,會造成程式難以擴充。 ```py from PyQt5.QtWidgets import QMainWindow from .ui_mainwindow import Ui_MainWindow a = QMainWindow() ui = Ui_MainWindow() ui.setupUi(a) ``` ### 執行 MainWindow 物件 QWidget: Must construct a QApplication before a QWidget 以下是 qt 執行 mainwindow 的方法。 ```py if __name__ == "__main__": app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec_()) ``` 先創立一個 `QApplication` 物件,它會在背景創立若干執行續以及先分配好一些資源,並透過此物件管理所有應用需要用到的資源。 接著再創立 `MainWindow` 物件,來處理主視窗,`window.show()`是顯示出來,繼承自 `QWidget` 。 最後透過 `app.exec_()` ,開始執行app這時,所有應用的資源、UI等,才會開始運作。會執行一個永久迴路,離開條件是觸發 Accepted 或 Rejected 等訊號。 離開 `app.exec_()` 時,會傳出錯誤訊號,再透過`sys.exit()`把這個訊號傳到我們的作業系統上,並離開python程序。 也可以寫成 ```py res = app.exec_() sys.exit(res) ``` 補充,`window.show()`會發送訊號給 ui處理執行續,ui處理執行續皆收到後排程、再執行,而ui處理執行續是在app中的。