Abstract: menus, toolbars and status bars are common and important graphical components of most GUI applications. You can use them to provide your users with a quick way to access application options and features.

This article is shared from Huawei Cloud Community " Python and PyQt: Create Menu, Toolbar and Status Bar ", author: Yuchuan.

When developing graphical user interface (GUI) applications using Python and PyQt, some of the most useful and versatile graphical elements you will use are menus, toolbars, and status bars.

Menus and toolbars can make your application look fine and professional , to provide users with a set of options accessible, and the status bar allows you to display information about the status of the application.

In this tutorial, you will learn:

• What menu, toolbar and status bar are
• How to programmatically create menus, toolbars and status bars
• How to use PyQt operations to fill Python menus and toolbars
• How to use the status bar to display status information

In addition, you will learn some programming best practices that you can apply when creating menus, toolbars, and status bars using Python and PyQt.

Build Python menu bars, menus and toolbars in PyQt

A menu bar is a GUI application area where the menu is saved in the main window. The menu is a drop-down list of options, allowing easy access to the application's options. For example, if you are creating a text editor, you might have the following menus in your menu bar:

• A file menu that provides the following menu options:
o New is used to create a new document
o Open to open an existing document
o Open recently opened recent documents
o Save is used to save the document
o Exit to exit the application
• Provides the "Edit" menu with some of the following menu options:
o Copy is used to copy some text
o Paste is used to paste some text
o Cut is used to cut some text
• A help menu that provides the following menu options:
o Help content is used to start the user manual and help content
o About launching the About dialog

You can also add some of these options to the toolbar . The toolbar is a button panel with meaningful icons that provides quick access to the most frequently used options in the application. In your text editor example, you can add options such as New, Open, Save, Copy, and Paste to the toolbar.

Note: In this tutorial, you will develop a sample application that implements all the above menus and options. You can use this sample application as a starting point for creating a text editor project.

In this section, you will learn how to use Python and PyQt to add the basics of menu bars, menus, and toolbars to GUI applications.

Before continuing, you will create a sample PyQt application that you will use in this tutorial. In each section, you will add new features and functions to this sample application. The application will be a main window style application. This means it will have a menu bar, a toolbar, a status bar and a central widget.

Open your favorite code editor or IDE and create a file called sample_app.py. Then add the following code in it:
import sys

from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QLabel, QMainWindow

class Window(QMainWindow):
    """Main Window."""
    def __init__(self, parent=None):
        """Initializer."""
        super().__init__(parent)
        self.setWindowTitle("Python Menus & Toolbars")
        self.resize(400, 200)
        self.centralWidget = QLabel("Hello, World")
        self.centralWidget.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
        self.setCentralWidget(self.centralWidget)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    win = Window()
    win.show()
    sys.exit(app.exec_())

Now sample_app.py contains all the code needed to create the sample PyQt application. In this case, Window inherits from QMainWindow. Therefore, you are building a main window style application.

Note: Unfortunately, the official documentation of PyQt5 has some incomplete parts. To solve this problem, you can check the PyQt4 documentation or the original Qt documentation.

In the class initializer.__init__(), you first use super(), which calls the initializer of the parent class. Then use to set the title of the window.setWindowTitle() and use to resize the window.resize().

Note: If you are not familiar with PyQt applications and how to create them, then you can check out Python and PyQt: Building a GUI Desktop Calculator.

The central widget of the window is a QLabel object, which you will use to display messages in response to certain user actions. These messages will be displayed in the center of the window. To do this, you call .setAlignment() on the QLabel object with a couple of alignment marks.
If you run the application from the command line, you will see the following window on the screen:

That's it! You have created a main window style application using Python and PyQt. You will use this sample application in all the examples that will appear in this tutorial.

Create a menu bar

In PyQt main window style applications, the default QMainWindow provides an empty QMenuBar object. To access this menu bar, you need to call .menuBar() on your QMainWindow object. This method will return an empty menu bar. The parent of this menu bar will be your main window object.

Now go back to your sample application and add the following method to the definition of Window:

class Window(QMainWindow):
    # Snip...
    def _createMenuBar(self):
        menuBar = self.menuBar()

This is the preferred way to create a menu bar in PyQt. Here, the menuBar variable will contain an empty menu bar, which will be the menu bar of your main window.

Note: A common practice in PyQt programming is to use local variables for objects that you will not use or need from outside of the method defined. Python garbage collects all objects that are out of scope, so you might think that menuBar in the above example will disappear once ._createMenuBar() returns.

The fact is that PyQt retains references to local objects, such as menuBar using their ownership or parent-child relationship. In other words, since the menuBar is owned by your main window object, Python will not be able to garbage collect it.

Another way to add a menu bar to a PyQt application is to create a QMenuBar object and then use .setMenuBar(). With this in mind, you can also write ._createMenuBar() as follows:

from PyQt5.QtWidgets import QMenuBar
# Snip...

class Window(QMainWindow):
    # Snip...
    def _createMenuBar(self):
        menuBar = QMenuBar(self)
        self.setMenuBar(menuBar)

In the above example, menuBar holds a QMenuBar parent set to the object self, which is the main window of the application. Once you have the menu bar object, you can .setMenuBar() to add it to your main window. Finally, note that to work in this example, you first need to import PyQt5.QWidgets from QMenuBar.

In GUI applications, the menu bar will be displayed in different positions according to the underlying operating system:

• Windows: at the top of the main application window, below the title bar
• macOS: At the top of the screen
• Linux: At the top of the main window or the top of the screen, depending on your desktop environment
The last step to create a menu bar for the application is. _createMenuBar() calls .__init__() from the initializer of the main window:

class Window(QMainWindow):
    """Main Window."""
    def __init__(self, parent=None):
        # Snip...
        self._createMenuBar()

If you run the sample application with these new changes, you will not see the menu bar displayed in the main window of the application. That's because your menu bar is still empty. To view the menu bar on the main window of the application, you need to create some menus. This is what you will learn next.

Add the menu to the menu bar

A menu is a drop-down list of menu options, which you can trigger by clicking on them or pressing a keyboard shortcut. In PyQt, there are at least three ways to add a menu to the menu bar object:

  1. QMenuBar.addMenu(menu) the QMenu object (menu) to the menu bar object. It returns the operation associated with this menu.
  2. QMenuBar.addMenu(title) creates a new QMenu object with a string (title) as the title and attaches it to the menu bar. The menu bar takes ownership of the menu, and this method returns a new QMenu object.
  3. QMenuBar.addMenu(icon, title) creates and adds a new QMenu item and a menu bar object with icon and title. The menu bar takes ownership of the menu, and this method returns a new QMenu object.

If you use the first option, you need to create a custom QMenu object first. For this, you can use one of the following constructors:

  1. QMenu(parent)
  2. QMenu(title, parent)

In both cases, the parent is that the QWidget will hold the ownership of the QMenu object. You usually set the parent to the window in which you will use the menu. In the second constructor, title will hold a string with text describing the menu options.

Here is how to add the File, Edit, and Help menus to the menu bar of the sample application:

from PyQt5.QtWidgets import QMenu
# Snip...

class Window(QMainWindow):
    # Snip...
    def _createMenuBar(self):
        menuBar = self.menuBar()
        # Creating menus using a QMenu object
        fileMenu = QMenu("&File", self)
        menuBar.addMenu(fileMenu)
        # Creating menus using a title
        editMenu = menuBar.addMenu("&Edit")
        helpMenu = menuBar.addMenu("&Help")

First, you import QMenu's PyQt5.QtWidgets. Then in ._createMenuBar(), use the first two variants to add three menus to the menu bar.addMenu(). The third variant requires an icon object, but you have not yet learned how to create and use icons. You will learn how to use icons in the section Using icons and resources in PyQt.

If you run the sample application, you will see that you now have a menu bar like this:

The menu bar of the application has menus File, Edit and Help. When you click on these menus, they will not display a drop-down list of menu options. That's because you haven't added a menu option yet. You will learn how to add menu options to the menu in the section Filling the menu with actions.

Finally, please note that the ampersand () included in each menu title will create an underlined letter in the menu bar display. This is discussed in more detail in the section on defining keyboard shortcuts for menu and toolbar options.

Create toolbar

A toolbar is a removable panel that saves buttons and other components to provide quick access to the most common options of GUI applications. Toolbar buttons can display icons, text, or both to indicate the tasks they perform. The base class of toolbars in PyQt is QToolBar. This class will allow you to create custom toolbars for GUI applications.

When you add a toolbar to a main window style application, the default position is at the top of the window. However, you can place the toolbar in one of the following four toolbar areas:
image.png

The toolbar area is defined as a constant in PyQt. If you need to use them, then you must import QtfromPyQt5.QtCore and use the fully qualified name Qt.LeftToolBarArea like in.

In PyQt, there are three ways to add toolbars to the main window application:

  1. QMainWindow.addToolBar(title) creates a new empty QToolBar object and sets its window title to title. This method inserts the toolbar into the top toolbar area and returns to the newly created toolbar.
  2. QMainWindow.addToolBar(toolbar) Insert the QToolBar object (toolbar) into the top toolbar area.
  3. QMainWindow.addToolBar(area, toolbar) Insert the QToolBar object (tool) into the specified toolbar area (area). If the main window already has a toolbar, the toolbar is placed after the last existing toolbar. If the toolbar already exists in the main window, it will only be moved to the area.

If you use one of the last two options, then you need to create the toolbar yourself. For this, you can use one of the following constructors:

  1. QToolBar(parent)
  2. QToolBar(title, parent)

In both cases, parent represents the object for which QWidget will have ownership of the toolbar. You usually set toolbar ownership to the window in which the toolbar will be used. In the second constructor, title will be a string with the title of the toolbar window. PyQt uses this window title to build a default context menu, allowing you to hide and show your toolbar.

Now you can return to your sample application and add the following method to Window:

from PyQt5.QtWidgets import QToolBar
# Snip...

class Window(QMainWindow):
    # Snip...
    def _createToolBars(self):
        # Using a title
        fileToolBar = self.addToolBar("File")
        # Using a QToolBar object
        editToolBar = QToolBar("Edit", self)
        self.addToolBar(editToolBar)
        # Using a QToolBar object and a toolbar area
        helpToolBar = QToolBar("Help", self)
        self.addToolBar(Qt.LeftToolBarArea, helpToolBar)

First, you QToolBar from PyQt5.QtWidgets. Then, in ._createToolBars(), you first use the title to create the file toolbar.addToolBar(). Next, you create a QToolBar object with a title, "Edit" and use .addToolBar() to add it to the toolbar without passing the toolbar area. In this case, the editing toolbar is located in the top toolbar area. Finally, create a help toolbar and use Qt.LeftToolBarArea to place it in the left toolbar area.

The final step to complete this task is to call Window from the initializer of ._createToolBars():

class Window(QMainWindow):
    """Main Window."""
    def __init__(self, parent=None):
        # Snip...
        self._createToolBars()

The ._createToolBars() call to Window inside the initializer will create three toolbars and add them to your main window. Here is how your application looks now:

Now, there are two toolbars directly below the menu bar, and a toolbar on the left side of the window. Each toolbar has a double dashed line. When you move the mouse over the dotted line, the pointer will change to a hand. If you click and hold the dotted line, you can move the toolbar to any other position or area of the toolbar on the window.

If you right-click the toolbar, PyQt will display a context menu that allows you to hide and show existing toolbars as needed.

So far, there are three toolbars in your application window. These toolbars are still empty-you need to add some toolbar buttons to make them work. For this, you can use PyQt actions, which are QAction. You will learn how to create actions in PyQt in a later section. Now you will learn how to use icons and other resources in PyQt applications.

Use icons and resources in PyQt

The Qt library includes the Qt resource system, which is a convenient way to add binary files such as icons, images, translation files and other resources to the application.

To use the resource system, you need to list your resources in a resource collection file or .qrc file. A .qrc file is an XML containing the location, or file path, of each resource in the file system.

Suppose your sample application has a resources directory that contains the icons you want to use in the application's GUI. You have icons for options such as New and Open. You can create a .qrc file containing the path of each icon:

<!DOCTYPE RCC><RCC version="1.0">
<qresource>
    <file alias="file-new.svg">resources/file-new.svg</file>
    <file alias="file-open.svg">resources/file-open.svg</file>
    <file alias="file-save.svg">resources/file-save.svg</file>
    <file alias="file-exit.svg">resources/file-exit.svg</file>
    <file alias="edit-copy.svg">resources/edit-copy.svg</file>
    <file alias="edit-cut.svg">resources/edit-cut.svg</file>
    <file alias="edit-paste.svg">resources/edit-paste.svg</file>
    <file alias="help-content.svg">resources/help-content.svg</file>
</qresource>
</RCC>

Each <file> entry must contain the path of the resource in the file system. The specified path is relative to the directory containing the .qrc file. In the above example, the resources directory needs to be in the same directory as the .qrc file.

alias is an optional attribute, it defines a short alternative name, you can use it in your code to access each resource.
Once you have the resources of the application, you can run pyrcc5's command line tool for your .qrc file. pyrcc5 is provided with PyQt and must be fully functional in your Python environment after installing PyQt.

pyrcc5 reads a .qrc file and generates a Python module, which contains the binary code of all resources:

$ pyrcc5 -o qrc_resources.py resources.qrc

This command will read resources.qrc and generate qrc_resources.py containing the binary code of each resource. You will be able to use these resources in Python code by importing qrc_resources.

Note: If there is a problem with pyrcc5 when running, please make sure you are using the correct Python environment. If you install PyQt in a Python virtual environment, you will not be able to use pyrcc5 outside of that environment.

This qrc_resources.py is the resources.qrc corresponding to your code snippet:

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

# Resource object code
#
# Created by: The Resource Compiler for PyQt5 (Qt v5.9.5)
#
# WARNING! All changes made in this file will be lost!

from PyQt5 import QtCore

qt_resource_data = b"\
\x00\x00\x03\xb1\
\x3c\
\x73\x76\x67\x20\x78\x6d\x6c\x6e\x73\x3d\x22\x68\x74\x74\x70\x3a\
...

With qrc_resources.py in place, you can import it into your application by typing a colon (see each resource:), and then either its alias or its path. For example, to use the alias of file-new.svg for access, you can use access the string ":file-new.svg". If you don't have an alias, you can access it through a path with an access string ":resources/file-new.svg".

If you have an alias, but for some reason you want to access a given resource through its path, then you may have to remove the colon from the access string to make it work.

To use icons in your operations, you first need to import your resource module:
import qrc_resources

After importing a module that contains resources, you can use these resources in the GUI of the application.

Note: Linters, editors, and IDEs may mark the above import statement as unused, because your code will not contain any explicit use of it. Some IDEs may go one step further and delete the line automatically.

In these cases, you must override the suggestions of your linter, editor, or IDE and keep the import in your code. Otherwise, your application will not be able to display your resources.

To create an icon using the resource system, you need to instantiate QIcon, passing the alias or path to the class constructor:
newIcon = QIcon(":file-new.svg")

In this example, you will QIcon use a file to create an object, and the object file-new.svg is located in your resource module. This provides a convenient way to use icons and resources throughout GUI applications.

Now go back to your sample application and update the last line._createMenuBar():

from PyQt5.QtGui import QIcon

import qrc_resources
# Snip...

class Window(QMainWindow):
    # Snip...
    def _createMenuBar(self):
        menuBar = self.menuBar()
        # Using a QMenu object
        fileMenu = QMenu("&File", self)
        menuBar.addMenu(fileMenu)
        # Using a title
        editMenu = menuBar.addMenu("&Edit")
        # Using an icon and a title
        helpMenu = menuBar.addMenu(QIcon(":help-content.svg"), "&Help")

To make this code work, you first need QIcon from PyQt5.QtGui. You also need to import qrc_resources. In the last highlighted line, you added a helpMenu from the resource module using the icon help-content.svg.
If you run the sample application with this update, you will get the following output:

The main window of the application now displays an icon on its help menu. When you click the icon, the menu displays the text Help. It is not common to use icons in the menu bar, but PyQt allows you to do so.

Create actions for Python menus and toolbars in PyQt

PyQt actions are objects that represent a given command, operation or action in the application. They are very useful when you need to provide the same functionality for different GUI components such as menu options, toolbar buttons, and keyboard shortcuts.

You can instantiate QAction. After you create an action, you need to add it to the widget to use it in practice.

You also need to associate your actions with certain functions. In other words, you need to connect them to the function or method to run when the action is triggered. This will allow your application to perform actions in response to user actions in the GUI.

Action is quite versatile. They allow you to reuse and keep the same functions synchronized across menu options, toolbar buttons, and keyboard shortcuts. This provides consistent behavior throughout the application.

For example, when users click the Open... menu option, click the Open toolbar button, or press Ctrl+O on the keyboard, they may want the application to perform the same operation.

QAction provides an abstraction that allows you to track the following elements:

• Text on menu options
• Text on toolbar buttons
• Help tips (tool tips) on toolbar options
• What help tip is this
• Help prompt on the status bar (status prompt)
• Keyboard shortcuts associated with options
• Icons associated with menu and toolbar options
• Action enabled or disabled state
• Action on or off state

To create an action, you need to instantiate a QAction. There are at least three general methods to do this:

  1. QAction(parent)
  2. QAction(text, parent)
  3. QAction(icon, text, parent)

In all three cases, parent represents the object that has ownership of the operation. This parameter can be any QObject. The best practice is to create actions as children of the window in which you will use them.

In the second and third constructors, the text save operation will display the text on the menu option or toolbar button.

The operation text is displayed differently on the menu options and toolbar buttons. For example, the text &Open... is displayed as an option in the Open... menu, such as an open toolbar button.

In the third constructor, icon is an object that QIcon saves action icons. This icon will appear to the left of the text in the menu option. The position of the icon in the toolbar button depends on the .toolButtonStyle property of the toolbar, and can take one of the following values:
image.png

You can also set the text and icon of the operation through their respective setter methods, .setText() and .setIcon().

Note: For a complete list of QAction properties, you can check the documentation.

Here is how to use the different constructors to create some actions QAction for the sample application:

from PyQt5.QtWidgets import QAction
# Snip...

class Window(QMainWindow):
    # Snip...
    def _createActions(self):
        # Creating action using the first constructor
        self.newAction = QAction(self)
        self.newAction.setText("&New")
        # Creating actions using the second constructor
        self.openAction = QAction("&Open...", self)
        self.saveAction = QAction("&Save", self)
        self.exitAction = QAction("&Exit", self)
        self.copyAction = QAction("&Copy", self)
        self.pasteAction = QAction("&Paste", self)
        self.cutAction = QAction("C&ut", self)
        self.helpContentAction = QAction("&Help Content", self)
        self.aboutAction = QAction("&About", self)

In ._createActions(), you created some actions for the sample application. These actions will allow you to add options to the application’s menus and toolbars.

Note that you create actions as instance properties, so you can use self._createActions(). This way, you can use these actions on menus and toolbars.

Note: In ._createActions(), the third constructor that you don't use, QAction, because if you still can't see the action, it doesn't make sense to use the icon. You will learn how to add icons to actions in the section Filling the toolbar with actions.

The next step is to call the initializer Window of the ._createActions() form:

class Window(QMainWindow):
    """Main Window."""
    def __init__(self, parent=None):
        # Snip...
        self._createActions()
        self._createMenuBar()
        self._createToolBars()

If you run the application now, you will not see any changes on the GUI. This is because actions are not displayed until they are added to the menu or toolbar. Please note that you call ._createMenuBar() before calling ._createActions(), ._createToolBars() because you will use these actions on menus and toolbars.

If you add an action to the menu, the action will become a menu option. If you add an operation to the toolbar, the operation becomes a toolbar button. This is the subject of the next few sections.

Add options to the Python menu in PyQt

If you want to add a list of options to a given menu in PyQt, you need to use actions. So far, you have learned how to use QAction. When creating menus in PyQt, actions are a key component.

In this section, you will learn how to use actions to fill a menu with menu options.

Populate the menu with actions

To fill the menu with menu options, you will use actions. In the menu, the operation is represented as a horizontal option, which has at least one descriptive text, such as New, Open, Save, etc. The menu option can also display an icon on its left and a shortcut key sequence on its right, such as Ctrl+S.

You can add actions to objects using QMenu using .addAction(). There are many variants of this method. Most of them are considered to be instant creation operations. In this tutorial, however, you are going to use the change.addAction() is QMenu inherited from QWidget. This is the signature of this variant:

QWidget.addAction(action)
The parameter action represents the object that QAction is to add to the given QWidget object. Using this variant of .addAction(), you can create your actions in advance and then add them to your menu as needed.

Note: QWidget also provides .addActions(). This method takes a series of actions and attaches them to the current widget object.

Using this tool, you can start adding actions to the sample application’s menu. To do this, you need to update ._createMenuBar():

class Window(QMainWindow):
    # Snip...
    def _createMenuBar(self):
        menuBar = self.menuBar()
        # File menu
        fileMenu = QMenu("&File", self)
        menuBar.addMenu(fileMenu)
        fileMenu.addAction(self.newAction)
        fileMenu.addAction(self.openAction)
        fileMenu.addAction(self.saveAction)
        fileMenu.addAction(self.exitAction)
        # Edit menu
        editMenu = menuBar.addMenu("&Edit")
        editMenu.addAction(self.copyAction)
        editMenu.addAction(self.pasteAction)
        editMenu.addAction(self.cutAction)
        # Help menu
        helpMenu = menuBar.addMenu(QIcon(":help-content.svg"), "&Help")
        helpMenu.addAction(self.helpContentAction)
        helpMenu.addAction(self.aboutAction)

With the updated ._createMenuBar(), you can add many options to the three menus of the sample application.

Now the file menu has four options:

  1. New is used to create a new file
  2. Open... is used to open existing files
  3. Save is used to save the changes made to the file
  4. Exit to close the application

There are three options in the edit menu:

  1. Copy content to the system clipboard
  2. Paste is used to paste content from the system clipboard
  3. Cut is used to cut the content to the system clipboard

There are two options in the help menu:

  1. Help content for starting the application's help manual
  2. About is used to display the About dialog box

The order in which the options appear in the menu from top to bottom corresponds to the order in which you add the options in the code.

If you run the application, you will see the following window on the screen:

If you click on a menu, the application displays a drop-down list of the options you saw earlier.

Create Python submenu

Sometimes you need to use submenus in GUI applications. A submenu is a nested menu that will be displayed when you move the cursor over a given menu option. To add a submenu to the application, you need to call the .addMenu() container menu object.

Suppose you need to add a submenu to the Edit menu of the sample application. Your submenu will contain options for finding and replacing content, so you call it Find and Replace. This submenu will have two options:

  1. Find... to find something
  2. Replace... is used to find old content and replace it with new content

Here is how to add this submenu to the sample application:

class Window(QMainWindow):
    # Snip...
    def _createMenuBar(self):
        # Snip...
        editMenu.addAction(self.cutAction)
        # Find and Replace submenu in the Edit menu
        findMenu = editMenu.addMenu("Find and Replace")
        findMenu.addAction("Find...")
        findMenu.addAction("Replace...")
        # Snip...

In the first highlighted line, you use on to add the QMenu object with text "Find and Replace" to the "Edit" menu. The next step is to populate the submenu with the actions you have done so far. If you run the sample application again, you will see a new menu option under the Edit menu: .addMenu()editMenu

In the edit menu there is now a new entry called Find and Replace. When you hover your mouse over this new menu option, a submenu will appear, offering you two new options, Find... and Replace.... That's it! You have created a submenu.

Add options to the toolbar in PyQt

When building GUI applications with Python and PyQt, the toolbar is a very useful component. You can use the toolbar to provide your users with a quick way to access the most frequently used options in the application. You can also add widgets such as spin boxes and combo boxes to the toolbar to allow users to modify certain properties and variables directly from the application's GUI.

In the following sections, you will learn how to use actions to add options or buttons to the toolbar, and how to use .addWidget().

Fill the toolbar with actions

To add options or buttons to the toolbar, you need to call .addAction(). In this section, you will rely on the changes. addAction() is QToolBar inherited from QWidget. Therefore, you call .addAction() with the action as a parameter. This will allow you to share your actions between the menu and toolbar.

When creating a toolbar, you often face the problem of deciding which options to add to it. Usually, you only want to add the most frequently used actions to the toolbar.

If you return to the sample application, you will remember that you added three toolbars:

  1. File
  2. Edit
  3. Help

In the file toolbar, you can add the following options:
• New
• Open
• Save

In the editing toolbar, you can add the following options:
• Copy
• Paste
• Cut

Generally, when you want to add buttons to the toolbar, you first select the icon you want to use on each button. This is not mandatory, but it is best practice. After selecting the icons, you need to add them to the corresponding actions.

Here is how to add icons to the operation of the sample application:

class Window(QMainWindow):
    # Snip...
    def _createActions(self):
        # File actions
        self.newAction = QAction(self)
        self.newAction.setText("&New")
        self.newAction.setIcon(QIcon(":file-new.svg"))
        self.openAction = QAction(QIcon(":file-open.svg"), "&Open...", self)
        self.saveAction = QAction(QIcon(":file-save.svg"), "&Save", self)
        self.exitAction = QAction("&Exit", self)
        # Edit actions
        self.copyAction = QAction(QIcon(":edit-copy.svg"), "&Copy", self)
        self.pasteAction = QAction(QIcon(":edit-paste.svg"), "&Paste", self)
        self.cutAction = QAction(QIcon(":edit-cut.svg"), "C&ut", self)
        # Snip...

To add the icon to your action, update the highlighted row. In the case of newAction, you use .setIcon(). In the rest of the operation, you use the constructor with icon, atitle, and parent objects as parameters.

Once the actions you select have icons, you can add these actions to the corresponding toolbar by calling the .addAction() toolbar object:

class Window(QMainWindow):
    # Snip...
    def _createToolBars(self):
        # File toolbar
        fileToolBar = self.addToolBar("File")
        fileToolBar.addAction(self.newAction)
        fileToolBar.addAction(self.openAction)
        fileToolBar.addAction(self.saveAction)
        # Edit toolbar
        editToolBar = QToolBar("Edit", self)
        self.addToolBar(editToolBar)
        editToolBar.addAction(self.copyAction)
        editToolBar.addAction(self.pasteAction)
        editToolBar.addAction(self.cutAction)

With this update._createToolBars(), you can add buttons for new, open, and save options to the file toolbar. You can also add buttons for the Copy, Paste and Cut options to the "Edit" toolbar.

Note: The order in which the buttons appear from left to right on the toolbar corresponds to the order in which you add the buttons in the code.

If you run the sample application now, you will see the following window on the screen:

The sample application now displays two toolbars, each with several buttons. Your users can click these buttons to quickly access the most commonly used options of the application.

Note: When you first reply to ._createToolBars() in the Create Toolbar section, you created a help toolbar. This toolbar aims to show how to use different .addToolBar().

In the above update of ._createToolBars(), you removed the help toolbar, just to make the example short and clear.

Note that because you share the same actions between the menu and the toolbar, the menu options will also display icons on their left side, which is a huge win in terms of productivity and resource usage. This is one of the advantages of using PyQt to create menus and toolbars through Python.

Add widgets to the toolbar

In some cases, you may find it useful to add specific widgets (such as spin boxes, combo boxes, or others) to the toolbar. A common example is the combo box used by most word processors, which allows the user to change the font of the document or the size of the selected text.

To add a widget to the toolbar, you first need to create the widget, set its properties, and then call the .addWidget() toolbar object, passing the widget as a parameter.

Suppose you want to add an object to the "Edit" toolbar of the sample application QSpinBox to allow the user to change the size of some content, possibly the font size. You need to update ._createToolBars():

from PyQt5.QtWidgets import QSpinBox
# Snip...

class Window(QMainWindow):
    # Snip...
    def _createToolBars(self):
        # Snip...
        # Adding a widget to the Edit toolbar
        self.fontSizeSpinBox = QSpinBox()
        self.fontSizeSpinBox.setFocusPolicy(Qt.NoFocus)
        editToolBar.addWidget(self.fontSizeSpinBox)

Here, you first import the spin box class. Then you create a QSpinBox object, set its focusPolicy to Qt.NoFocus, and finally add it to your editing toolbar.

Note: In the above code, you set the attribute of the focusPolicy spin box to Qt.NoFocus because if this widget gets the focus, the keyboard shortcuts of the application will not work.

Now, if you run the application, you will get the following output:

Here, the "Edit" toolbar shows a QSpinBox object that your users can use to set the font size or any other numeric attributes on the application.

Custom toolbar

The PyQt toolbar is very flexible and customizable. You can set a bunch of properties on the toolbar object. The following table shows some of the most useful attributes:
image.png

All these properties have an associated setter method. For example, you can use .setAllowedAreas() to set allowedAreas, .setFloatable() to setfloatable, etc.

Now, suppose you don't want users to move the file toolbar around the window. In this case, you can set movable to False using .setMovable():

class Window(QMainWindow):
    # Snip...
    def _createToolBars(self):
        # File toolbar
        fileToolBar = self.addToolBar("File")
        fileToolBar.setMovable(False)
        # Snip...

The highlighted line makes this magical. Now your users cannot move the toolbar around the application window:

The toolbar of the file no longer displays the double dashed line, so your users will not be able to move it. Please note that the editing toolbar is still movable. You can use the same method to change other properties on the toolbar and customize them according to your needs.

Organize menu and toolbar options

To increase clarity and improve user experience in GUI applications, you can use separators to organize menu options and toolbar buttons. The separator is rendered as a horizontal line that separates or separates menu options or a vertical line that separates toolbar buttons.

To insert or add separators to menu, submenu, or toolbar objects, you can call .addSeparator() on any of these objects.

For example, you can use a separator to separate the "Exit" option on the "File" menu from the rest of the options to make it clear that "Exit" is logically independent of the rest of the menu. You can also use a separator to separate the Find and Replace option on the Edit menu from the rest of the options that follow the same rules.

Go to your sample application and update ._createMenuBar() with the following code:

class Window(QMainWindow):
    # Snip...
    def _createMenuBar(self):
        # File menu
        # Snip...
        fileMenu.addAction(self.saveAction)
        # Adding a separator
        fileMenu.addSeparator()
        fileMenu.addAction(self.exitAction)
        # Edit menu
        # Snip...
        editMenu.addAction(self.cutAction)
        # Adding a separator
        editMenu.addSeparator()
        # Find and Replace submenu in the Edit menu
        findMenu = editMenu.addMenu("Find and Replace")
        # Snip...

In the first line highlighted, add a separator between the "Save" and "Exit" options in the "File" menu. In the second highlighted line, add a separator to separate the "Find and Replace" option from the rest of the options in the "Edit" menu. Here is how these additions work:

Your "File" menu now displays a horizontal line, separating the "Edit" option from the rest of the menu. The edit menu also shows the last separator in the drop-down list of options. The continuous use of separators can cleverly improve the clarity of menus and toolbars, making your GUI applications more user-friendly.

As an exercise, you can go to the definition of ._createToolBars() and add a separator to separate the QSpinBox object from the rest of the options on the toolbar.

Build context or popup menus in PyQt

A context menu, also known as a pop-up menu, is a special type of menu that will appear in response to certain user actions (such as right-clicking on a given widget or window). These menus provide a small set of options that are available in a given context of the operating system or application you are using.

For example, if you right-click on the desktop of a Windows computer, you will get a menu with options corresponding to the specific context or space of the operating system. If you right-click on the workspace of a text editor, you will get a completely different context menu, depending on the editor you are using.

In PyQt, you have multiple options for creating context menus. In this tutorial, you will learn about two of these options:

  1. Set the attribute of the contextMenuPolicy specific widget to Qt.ActionsContextMenu
  2. By handling the context menu event contextMenuEvent() on the application window

The first option is the most common and user-friendly of the two, so you will learn about it first.

The second option is slightly more complicated and relies on handling user events. In GUI programming, an event is any user action on the application, such as clicking a button or menu, selecting an item from a combo box, entering or updating text in a text field, pressing a key on the keyboard, etc.

Create a context menu through the context menu strategy

All PyQt graphical components or widgets derived from QWidget inherit a named contextMenuPolicy. This property controls how the widget displays the context menu. One of the most commonly used values for this property is Qt.ActionsContextMenu. This makes the widget display its internal operation list as a context menu.

To make a widget display a context menu based on its internal operations, you need to run two steps:

  1. Use QWidget.addAction() to add some actions to the widget.
  2. Set the contextMenuPolicy on the widget used on Qt.ActionsContextMenu.setContextMenuPolicy().

Set contextMenuPolicy to Qt.ActionsContextMenu to make widgets with actions display them in the context menu. This is a very fast way to create context menus using Python and PyQt.

Using this technique, you can add a context menu to the central widget of the sample application and provide your users with a way to quickly access certain application options. For this, you can add the following methods to Window:

class Window(QMainWindow):
    # Snip...
    def _createContextMenu(self):
        # Setting contextMenuPolicy
        self.centralWidget.setContextMenuPolicy(Qt.ActionsContextMenu)
        # Populating the widget with actions
        self.centralWidget.addAction(self.newAction)
        self.centralWidget.addAction(self.openAction)
        self.centralWidget.addAction(self.saveAction)
        self.centralWidget.addAction(self.copyAction)
        self.centralWidget.addAction(self.pasteAction)
        self.centralWidget.addAction(self.cutAction)

In ._createContextMenu(), you first set the contextMenuPolicy to Qt.ActionsContextMenu using the setter method.setContextMenuPolicy(). Then .addAction() adds actions to the widget as usual. The last step is to call Window from the initializer of ._createContextMenu():

class Window(QMainWindow):
    """Main Window."""
    def __init__(self, parent=None):
        # Snip...
        self._createToolBars()
        self._createContextMenu()

If you run the sample application after adding these, when you right-click the central widget of the application, you will see that it displays a context menu:

Now, your sample application has a context menu, which will pop up whenever you right-click on the central widget of the application. The central widget stretches to take up all the available space in the window, so you are not limited to right-clicking on the label text to see the context menu.

Finally, because you use the same actions throughout the application, the options on the context menu display the same set of icons.

Create a context menu through event handling

Another way to create a context menu in PyQt is to handle context menu events in the main window of the application. To do this, you need to run the following steps:

  1. Override the event handler method QMainWindow on the object .contextMenuEvent().
  2. Create a QMenu object that passes widgets (context widgets) as its parent object.
  3. Fill the menu object with actions.
  4. Use QMenu.exec() event.globalPos() as a parameter to start the menu object.

This way of managing context menus is a bit complicated. However, it gives you great control over what happens when the context menu is called. For example, you can enable or disable menu options based on the status of the application, etc.

Note: Before continuing this section, you need to disable the code you wrote in the previous section. To do this, just go to the initializer of Window and comment out the call to self._createContextMenu().

Here is how to reimplement the context menu of the sample application, overriding the event handler method on the main window object:

class Window(QMainWindow):
    # Snip...
    def contextMenuEvent(self, event):
        # Creating a menu object with the central widget as parent
        menu = QMenu(self.centralWidget)
        # Populating the menu with actions
        menu.addAction(self.newAction)
        menu.addAction(self.openAction)
        menu.addAction(self.saveAction)
        menu.addAction(self.copyAction)
        menu.addAction(self.pasteAction)
        menu.addAction(self.cutAction)
        # Launching the menu
        menu.exec(event.globalPos())

In contextMenuEvent(), you first create a QMenu object (menu) centralWidget as its parent widget. Next, you use .addAction. Finally, call .exec() on the QMenu object to display it on the screen.

The second parameter of .contextMenuEvent() represents the event captured by this method. In this case, event will right-click on the central widget of the application.

In the call to .exec(), you use event.globalPos() as a parameter. When the user clicks on a PyQt window or widget, this method returns the global position of the mouse pointer. The mouse position will tell the .exec() window where the context menu is displayed.

If you run the sample application with these new changes, you will get the same results as in the previous section.

Organize context menu options

Unlike menus and toolbars, in context menus, you cannot use .addSeparator() to add separators and visually separate menu options based on their relationship. When organizing the context menu, you need to create a separator operation:

separator = QAction(parent)
separator.setSeparator(True)

The call to .setSeparator(True) on the action object will turn the action into a separator. After completing the separator operation, you need to insert it into the correct position in the context menu using QMenu.addAction().

If you review your sample application, you might want to visually separate the options from the File menu from the options from the Edit menu. To do this, you can update .contextMenuEvent():

class Window(QMainWindow):
    # Snip...
    def contextMenuEvent(self, event):
        # Snip...
        menu.addAction(self.saveAction)
        # Creating a separator action
        separator = QAction(self)
        separator.setSeparator(True)
        # Adding the separator to the menu
        menu.addAction(separator)
        menu.addAction(self.copyAction)
        # Snip...

In the highlighted lines in the first two lines, you created a separator operation. In the third highlighted line, you add the separator action to the menu using .addAction().

This will add a horizontal line between the file options and the editing options. Here is how the context menu to add this content looks like:

Your context menu now contains a horizontal line that visually separates the options from File from the options from Edit. In this way, you improve the visual quality of the menu and provide a better user experience.

Connect signals and slots in the menu and toolbar

In PyQt, you use signals and slots to provide functionality to GUI applications. Each time an event such as a mouse click, key press, or window resizing occurs on a PyQt widget, they will emit a signal.

A slot is a Python callable that you can connect to a widget signal to perform certain actions in response to user events.

If a signal and a slot are connected, the slot will be called automatically every time a signal is sent. If the given signal is not connected to the slot, nothing will happen when the signal is sent.

In order for your menu options and toolbar buttons to initiate some operations when the user clicks on them, you need to connect the signals of the underlying operations with some custom or built-in slots.

QAction objects can emit various signals. However, the most commonly used signal in menus and toolbars is .triggered(). This signal is emitted every time the user clicks a menu option or toolbar button. To .triggered() to connect with the slot, you can use the following syntax:

action = QAction("Action Text", parent)
# Connect action's triggered() with a slot
action.triggered.connect(slot)

In this example, slot is a Python callable. In other words, slot can be an instance of a function, a method, a class, or an implemented class.__call__().

There is already a set of operations in your sample application. Now you need to code the slot that is called every time the user clicks a menu option or toolbar button. Go to the definition of Window and add the following method:

class Window(QMainWindow):
    # Snip...
    def newFile(self):
        # Logic for creating a new file goes here...
        self.centralWidget.setText("<b>File > New</b> clicked")

    def openFile(self):
        # Logic for opening an existing file goes here...
        self.centralWidget.setText("<b>File > Open...</b> clicked")

    def saveFile(self):
        # Logic for saving a file goes here...
        self.centralWidget.setText("<b>File > Save</b> clicked")

    def copyContent(self):
        # Logic for copying content goes here...
        self.centralWidget.setText("<b>Edit > Copy</b> clicked")

    def pasteContent(self):
        # Logic for pasting content goes here...
        self.centralWidget.setText("<b>Edit > Paste</b> clicked")

    def cutContent(self):
        # Logic for cutting content goes here...
        self.centralWidget.setText("<b>Edit > Cut</b> clicked")

    def helpContent(self):
        # Logic for launching help goes here...
        self.centralWidget.setText("<b>Help > Help Content...</b> clicked")

    def about(self):
        # Logic for showing an about dialog content goes here...
        self.centralWidget.setText("<b>Help > About...</b> clicked")

These methods will play the role of a slot in the sample application. They are called every time the user clicks the corresponding menu option or toolbar button.

Once you have slots that provide functions, you need to connect them to the action's .triggered() signal. In this way, the application will perform actions based on user events. To make these connections, go to the sample application and add the following method to Window:

class Window(QMainWindow):
    # Snip...
    def _connectActions(self):
        # Connect File actions
        self.newAction.triggered.connect(self.newFile)
        self.openAction.triggered.connect(self.openFile)
        self.saveAction.triggered.connect(self.saveFile)
        self.exitAction.triggered.connect(self.close)
        # Connect Edit actions
        self.copyAction.triggered.connect(self.copyContent)
        self.pasteAction.triggered.connect(self.pasteContent)
        self.cutAction.triggered.connect(self.cutContent)
        # Connect Help actions
        self.helpContentAction.triggered.connect(self.helpContent)
        self.aboutAction.triggered.connect(self.about)

This method connects the .triggered() signals of all your operations with their respective slots or callbacks. With this update, your sample application will display a message on the QLabel object you set as the central widget, telling you which menu option or toolbar button you clicked.

In the case of exitAction, you connect its triggered() signal to the built-in slot QMainWindow.close(). In this way, if you select File → Exit, your application will close.

Finally, go to the initializer Window and add the call to ._connectActions():

class Window(QMainWindow):
    """Main Window."""
    def __init__(self, parent=None):
        # Snip...
        # self._createContextMenu()
        self._connectActions()

With this final update, you can run the application again. Here is how all these changes work:

If you click a menu option, toolbar button, or context menu option, the tab in the center of the application window displays a message indicating the operation that has been performed. This feature is not very useful outside the learning environment, but it allows you to understand how to make your application perform real-world operations when the user interacts with the GUI.

Finally, when you choose File → Exit, the application will close because the .triggered() signal exitAction has been connected to the built-in slot QMainWindow.close().

As an exercise, you can try to create custom slots for the Find... and Replace... option in the Find and Replace submenu, and then connect their signals to these slots to make it effective. You can also try to use the slots you wrote in this section and try to do new things with them. .triggered()

Dynamically populate the Python menu

When creating menus for an application, it is sometimes necessary to populate these menus with options that were unknown when the application GUI was created. For example, the "Open Recent" menu in a text editor displays a list of recently opened documents. You cannot populate this menu when creating the application's GUI, because each user will open a different document and there is no way to know this information in advance.

In this case, you need to dynamically populate the menu in response to user actions or the state of the application. QMenu has a signal called .aboutToShow() that you can connect to a custom slot to dynamically fill it before the menu object is displayed on the screen.

To continue developing the sample application, suppose you need to create an open recent submenu under the file and dynamically populate it with the recently opened file or document. To do this, you need to run the following steps:

  1. Create a recent submenu of Open under File.
  2. Write dynamic generation actions to fill the custom slots of the menu.
  3. Connect the .aboutToShow() menu signal to the custom slot.

Here is the code to create the submenu:

class Window(QMainWindow):
    # Snip...
    def _createMenuBar(self):
        # Snip...
        fileMenu.addAction(self.openAction)
        # Adding an Open Recent submenu
        self.openRecentMenu = fileMenu.addMenu("Open Recent")
        fileMenu.addAction(self.saveAction)
        # Snip...

In the highlighted line, you add a submenu titled "Open Recent" under the "File" menu. There are no menu options in this submenu. You need to dynamically create operations to populate it.

You can achieve this by writing a method to dynamically create actions and add them to the submenu. This is an example that shows the general logic you can use:

from functools import partial
# Snip...

class Window(QMainWindow):
    # Snip...
    def populateOpenRecent(self):
        # Step 1. Remove the old options from the menu
        self.openRecentMenu.clear()
        # Step 2. Dynamically create the actions
        actions = []
        filenames = [f"File-{n}" for n in range(5)]
        for filename in filenames:
            action = QAction(filename, self)
            action.triggered.connect(partial(self.openRecentFile, filename))
            actions.append(action)
        # Step 3. Add the actions to the menu
        self.openRecentMenu.addActions(actions)

In .populateOpenRecent(), first use the old option in the delete menu (if any).clear(). Then add logic for dynamic creation and connection operations. Finally, you add actions to the menu using .addActions().

In the for loop, you use functools.partial() to connect the .triggered() signal, and .openRecentFile() because you want filename to be passed as a parameter to .openRecentFile(). This is a very useful technique when connecting signals to slots that require additional parameters. To make it work, you need partial() from functools.

Note: The logic in the second step of this example does not actually load the list of recently opened files. It just created one of the five hypothetical files in the list, and its sole purpose is to show the way to implement this technique.

The next step is to connect the signal from .aboutToShow() .openRecentMenu to .populateOpenRecent(). To do this, add the following line at the end._connectActions():

class Window(QMainWindow):
    # Snip...
    def _connectActions(self):
        # Snip...
        self.aboutAction.triggered.connect(self.about)
        # Connect Open Recent to dynamically populate it
        self.openRecentMenu.aboutToShow.connect(self.populateOpenRecent)

In the highlighted line, you connect the .aboutToShow signal with .populateOpenRecent(). This ensures that your menu is filled before it is displayed.

Now you need to encode .openRecentFile(). This is the method your application will call when your user clicks on any dynamically created action:

class Window(QMainWindow):
    # Snip...
    def openRecentFile(self, filename):
        # Logic for opening a recent file goes here...
        self.centralWidget.setText(f"<b>{filename}</b> opened")

This method will update the text of the QLabel object that you use as the central widget of the sample application.

The following is how the dynamically created submenu works in practice:

When your mouse pointer hovers over the open recent menu, the menu will emit the .aboutToShow() signal. This will cause .populateOpenRecent() to be called, which creates and connects the operation. If you click on the file name, you will see that the central label changes accordingly to display a message.

Define keyboard shortcuts for menu and toolbar options

Keyboard shortcuts are an important function in GUI applications. A keyboard shortcut is a key combination that you can press on the keyboard to quickly access some of the most common options in the application.

Here are some examples of keyboard shortcuts:
• Ctrl+ Copy some content of C to the clipboard.
• Ctrl+V to paste something from the clipboard.
• Ctrl+Z undoes the last operation.
• Ctrl+O to open the file.
• Ctrl+S to save the file.

In the following section, you will learn how to add keyboard shortcuts to the application to improve user productivity and experience.

Use key sequence

So far, you have learned that QAction is a multifunctional class used to fill menus and toolbars. QAction also provides a user-friendly way to define keyboard shortcuts for menu options and toolbar buttons.

QAction implements.setShortcut(). This method takes a QKeySequence object as a parameter and returns the keyboard shortcut.
QKeySequence provides several constructors. In this tutorial, you will learn about two of them:

  1. QKeySequence(ks, format) takes the string-based key sequence (ks) and format (format) as parameters and creates a QKeySequence object.
  2. QKeySequence(key) accepts a StandardKey constant as a parameter and creates an object whose QKeySequence matches the key sequence on the underlying platform.

The first constructor recognizes the following strings:

• "Ctrl"
• "Shift"
• "Alt"
• "Meta"

You can combine these strings with letters, punctuation marks, numbers, named keys (Up, Down, Home) and function keys ("Ctrl+S", "Ctrl+5", "Alt+Home", "Alt+F4 ") combination to create a string-based key sequence. You can pass up to four string-based key sequences in a comma-separated list.

Note: For a complete reference of standard shortcuts on different platforms, please refer to the QKeySequence document in the standard shortcuts section.

If you are developing a multi-platform application and want to stick to the standard keyboard shortcuts for each platform, the second constructor is handy.

For example, QKeySequence.Copy will return the platform standard keyboard shortcuts used to copy objects to the clipboard.

Note: For a complete reference of the standard keys provided by PyQt, please refer to the QKeySequence.StandardKey document.

With a general background on how to define keyboard shortcuts for actions in PyQt, you can go back to the sample application and add some shortcuts. To do this, you need to update ._createActions():

from PyQt5.QtGui i

华为云开发者联盟
1.4k 声望1.8k 粉丝

生于云,长于云,让开发者成为决定性力量