Skip to content

[PyQt5] 基本教學(4) 菜單、工具欄

Last Updated on 2021-04-09 by Clay

一個好的、完善的界面通常都具有著選單(也就是上方的菜單),方便我們針對我們的程式使用各種功能。雖然說小一點的界面並不需要有這種功能,但我最近還是在研究我的記譜程式,所以也研究了一下『菜單』、『工具欄』等等的使用方始。

我想等到我記譜工具完成的那一天,會以『實戰分享』的方式紀錄成筆記放上來的。

那麼,以下就是我今天的心得筆記:紀錄如何使用菜單、工具欄等等的工具!


菜單

在我們開始之前,我先說明一下一件事情:我通常都實做界面、邏輯分離的程式,所以若想要直接拿我的程式進行測試,界面的部份只要拿我轉好的 .py 檔測試即可,並不是一定得要使用 Qt Designer。(當然,PyQt5 這個 package 就是一定要有的了。)

首先,我使用 Qt Designer 拉出一個界面:

大家可以注意到,我在右上角的 Type Here 新增了一個『文件』的選單,這就是我們今天要講的『菜單』。

我也建立了菜單裡的選項:

存檔、轉檔之後,我們會得到一份這樣的檔案:

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'Menu.ui'
#
# Created by: PyQt5 UI code generator 5.11.3
#
# 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, 600)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 25))
        self.menubar.setObjectName("menubar")
        self.menu = QtWidgets.QMenu(self.menubar)
        self.menu.setObjectName("menu")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)
        self.actionNew_File = QtWidgets.QAction(MainWindow)
        self.actionNew_File.setObjectName("actionNew_File")
        self.actionOpen_File = QtWidgets.QAction(MainWindow)
        self.actionOpen_File.setObjectName("actionOpen_File")
        self.actionClose = QtWidgets.QAction(MainWindow)
        self.actionClose.setObjectName("actionClose")
        self.menu.addAction(self.actionNew_File)
        self.menu.addAction(self.actionOpen_File)
        self.menu.addAction(self.actionClose)
        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.menu.setTitle(_translate("MainWindow", "文件"))
        self.actionNew_File.setText(_translate("MainWindow", "New File"))
        self.actionOpen_File.setText(_translate("MainWindow", "Open File"))
        self.actionClose.setText(_translate("MainWindow", "Close"))


跟之前的檔案不同的是,這次我們設定好的 Menu 並不是放在 setupUi() 底下,而是放在我們之前一直忽略的 retranslateUi() 底下。不過,當然,使用方法跟之前定義的元件並沒有太大的區別。

我們在自己的邏輯程式腳本裡頭寫下這樣的程式碼:

# -*- coding: utf-8 -*-
from PyQt5 import QtWidgets, QtGui, QtCore
from Menu import Ui_MainWindow
import sys


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

        # Menu
        self.ui.retranslateUi(self)
        self.ui.actionClose.setShortcut('Ctrl+Q')
        self.ui.actionClose.triggered.connect(app.exit)


if __name__ == '__main__':
    app = QtWidgets.QApplication([])
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())


Output:

我們可以看到沒有任何東西的界面!
按下文件長這個樣子!

目前前面兩樣 New File、 Open File 還沒有實做任何的功能,唯一做出的就是 Close,因為示範起來比較方便哈哈哈哈。

在我們的程式試中,首先,我們可以使用 setShortcut() 來製作簡單的快捷鍵。
下面那一行:self.ui.actionClose.triggered.connect(app.exit)
代表著被觸發時,這個應用程式便結束。

當然,如果你希望有其他的功能,依此類推,一樣使用 triggered.connect() 來觸發事先寫好的功能。

這樣,我們簡單的菜單按鈕就完成了。

如果還希望再界面上再加著圖示的話,可以使用我們之前教過得 QIcon。

在程式碼中加入行程式碼:

self.ui.actionClose.setIcon(QtGui.QIcon('pic/Cancel.png'))


pic 是我放圖片的資料夾名稱、而 Cancel.png 為我要放的圖片。

Output:

我們可以看到,選單前面加上了個 X 的圖示!


工具欄

講完了 Menu,其實『工具欄』這個欄位也就差不多了;通常工具欄這個欄位都只是設定成 Menu 的圖示按鈕,點擊就會產生相似的功能。

不過,我還是來匆匆瀏覽下吧!

首先,我使用了 ToolBar 這個元件。ToolBar 可以很好地將元件放置在一起,甚至還可以新增頁面切換不同的工具欄位。

在這裡可以看到,我放了兩個 ToolButton 進去這個 ToolBar。然後,我們存檔。

轉換成 Python 檔之後如下:

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'Menu.ui'
#
# Created by: PyQt5 UI code generator 5.11.3
#
# 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, 600)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.toolBox = QtWidgets.QToolBox(self.centralwidget)
        self.toolBox.setGeometry(QtCore.QRect(0, 0, 91, 551))
        self.toolBox.setObjectName("toolBox")
        self.page = QtWidgets.QWidget()
        self.page.setGeometry(QtCore.QRect(0, 0, 91, 491))
        self.page.setObjectName("page")
        self.toolButton = QtWidgets.QToolButton(self.page)
        self.toolButton.setGeometry(QtCore.QRect(0, -10, 91, 81))
        self.toolButton.setText("")
        self.toolButton.setObjectName("toolButton")
        self.toolButton_2 = QtWidgets.QToolButton(self.page)
        self.toolButton_2.setGeometry(QtCore.QRect(0, 70, 91, 81))
        self.toolButton_2.setObjectName("toolButton_2")
        self.toolBox.addItem(self.page, "")
        self.page_2 = QtWidgets.QWidget()
        self.page_2.setGeometry(QtCore.QRect(0, 0, 91, 221))
        self.page_2.setObjectName("page_2")
        self.toolBox.addItem(self.page_2, "")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 25))
        self.menubar.setObjectName("menubar")
        self.menu = QtWidgets.QMenu(self.menubar)
        self.menu.setObjectName("menu")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)
        self.actionNew_File = QtWidgets.QAction(MainWindow)
        self.actionNew_File.setObjectName("actionNew_File")
        self.actionOpen_File = QtWidgets.QAction(MainWindow)
        self.actionOpen_File.setObjectName("actionOpen_File")
        self.actionClose = QtWidgets.QAction(MainWindow)
        self.actionClose.setObjectName("actionClose")
        self.menu.addAction(self.actionNew_File)
        self.menu.addAction(self.actionOpen_File)
        self.menu.addAction(self.actionClose)
        self.menubar.addAction(self.menu.menuAction())

        self.retranslateUi(MainWindow)
        self.toolBox.setCurrentIndex(0)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.toolButton_2.setText(_translate("MainWindow", "Hello World"))
        self.toolBox.setItemText(self.toolBox.indexOf(self.page), _translate("MainWindow", "Page 1"))
        self.toolBox.setItemText(self.toolBox.indexOf(self.page_2), _translate("MainWindow", "Page 2"))
        self.menu.setTitle(_translate("MainWindow", "文件"))
        self.actionNew_File.setText(_translate("MainWindow", "New File"))
        self.actionOpen_File.setText(_translate("MainWindow", "Open File"))
        self.actionClose.setText(_translate("MainWindow", "Close"))


然後我們再次撰寫邏輯部份的程式碼。(改寫自剛才的程式碼。)

# -*- coding: utf-8 -*-
from PyQt5 import QtWidgets, QtGui, QtCore
from Menu import Ui_MainWindow
import sys


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


        # Menu
        self.ui.retranslateUi(self)
        self.ui.actionClose.setShortcut('Ctrl+Q')
        self.ui.actionClose.setIcon(QtGui.QIcon('pic/Cancel.png'))
        self.ui.actionClose.triggered.connect(self.exit)

        # ToolBar
        self.ui.toolButton.setShortcut('Ctrl+E')
        self.ui.toolButton.setIcon(QtGui.QIcon('pic/Cancel.png'))
        self.ui.toolButton.clicked.connect(self.exit)

    def exit(self):
        app.exit()


if __name__ == '__main__':
    app = QtWidgets.QApplication([])
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())


Output:

可以看到,我新增了兩個按鈕。其中一個我還是設定使用相同的圖片。

我們點擊這個圖片,應該可以順利地離開程式。

順帶一題,可以看到我設定了兩組不同的快捷鍵,這是因為我選單的 Exit 按鈕已經設定了相當的快捷鍵,兩個元件設定相同的快捷鍵是會報錯的!

然後,由於這次是 Button ,故我們的使用 clicked.connect() 來綁定觸發的事件。

以上,就是今天的教學,謝謝大家的過目。


前天熬了一整夜和朋友熬夜聊天,然後整個人睏得很啊,果然是上了年紀了嗎 ...... 我昨天早上寫個圍棋的吃子判定老半天寫不出來,那明明就是我數年前大學時代就寫過的程式啊!

然後晚上八點從實驗室回家,立刻躺平了,一直睡到凌晨五點才醒,連文章都忘了發。

然後我振筆疾書(?),俐落地刪掉昨天寫的吃子判定,重寫。

這次不到一個小時就寫完了,測試圖形化界面,嗯,OK!看起來沒有 Bug。

離奇的是,我覺得我是寫一模一樣的程式下去跑啊!果然不該撐著熬夜寫程式啊 XDDDD 我連我前一個版本怎麼錯的都不知道。

想要能持之以恆地開發程式、撰寫部落格,看來每天固定地運動也不錯。也不要節食、更要好好睡覺。


References


Read More

Leave a Reply