Python и PyQt: создание меню, панелей инструментов и строк состояния
Когда дело доходит до разработки приложений с графическим пользовательским интерфейсом (GUI) с помощью Python и PyQt, одними из самых полезных и универсальных графических элементов, которые вы когда-либо будете использовать, являются меню, панели инструментов и строки состояния.
Меню и панели инструментов могут придать вашим приложениям безупречный и профессиональный вид, предоставляя пользователям доступный набор опций, а строки состояния позволяют отображать соответствующую информацию о состоянии приложения.
В этом руководстве вы узнаете:
- Что такое меню, панели инструментов и строка состояния
- Как создать меню, панели инструментов и строку состояния программно
- Как заполнить меню и панель инструментов Python с помощью действий в PyQt
- Как использовать строки состояния для отображения информации о состоянии
Кроме того, вы изучите некоторые передовые методы программирования, которые можно применять при создании меню, панелей инструментов и строк состояния с помощью Python и PyQt.
Создание меню и панелей инструментов в PyQt
Строка меню представляет собой область главного окна графического интерфейса пользователя приложения, которая содержит меню. Меню - это раскрывающиеся списки параметров, обеспечивающие удобный доступ к параметрам вашего приложения. Например, если вы создавали текстовый редактор, в строке меню могли бы быть некоторые из следующих элементов:
- File - меню которое предоставляет некоторые из следующих опций:New - для создания нового документаOpen - для открытия существующего документаOpen Recent - для открытия недавних документовSave - для сохранения документаExit - для выхода из приложения
- Edit - меню, которое предоставляет некоторые из следующих опций:Copy - для копирования текстаPaste - для вставки текстаCut - для вырезания текста
- Help - меню, которое предоставляет дополнительные опции:
Вы также можете добавить некоторые из этих параметров на панель инструментов. Панель инструментов - это панель кнопок с значками, которые обеспечивают быстрый доступ к наиболее часто используемым параметрам в приложении. В примере с текстовым редактором вы можете добавить на панель инструментов такие параметры, как New, Open, Save, Copy и Paste.
Прежде чем идти дальше, вы создадите образец приложения PyQt, которое вы будете использовать в этом руководстве. В каждом разделе вы будете добавлять новые функции и возможности в этот образец приложения. Это означает, что у него будет строка меню, панель инструментов, строка состояния и центральный виджет.
Откройте ваш любимый редактор кода или IDE и создайте файл Python с именем sample_app.py
. Затем добавьте к нему следующий код:
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_())
Теперь sample_app.py
содержит весь код, необходимый для создания образца приложения PyQt. В этом случае Window
наследуется от QMainWindow
. Итак, вы создаете приложение в стиле главного окна.
В инициализаторе класса .__init__()
вы сначала вызываете инициализатор родительского класса, используя super()
. Затем вы устанавливаете заголовок окна с помощью .setWindowTitle()
и изменяете размер окна с помощью .resize()
.
Центральный виджет окна - это объект QLabel
, который вы будете использовать для отображения сообщений в ответ на определенные действия пользователя. Эти сообщения будут отображаться в центре окна. Чтобы сделать это, вы вызываете .setAlignment()
у объекта QLabel
с параметром выравнивания.
Если вы запустите приложение из командной строки, вы увидите на экране следующее окно:
Вы создали приложение в стиле главного окна с помощью Python и PyQt. Вы будете использовать этот образец приложения для всех следующих примеров в этом руководстве.
Создание панелей меню
В приложении в стиле главного окна PyQt по умолчанию QMainWindow
предоставляет пустой объект QMenuBar
. Чтобы получить доступ к этой строке меню, вам нужно вызвать .menuBar()
у объекта QMainWindow
. Этот метод вернет пустую строку меню. Родителем для этой строки меню будет объект вашего главного окна.
Теперь вернитесь к вашему образцу приложения и добавьте следующий метод в определение Window
:
class Window(QMainWindow):
# Snip...
def _createMenuBar(self):
menuBar = self.menuBar()
Это предпочтительный способ создания строки меню в PyQt. Здесь переменная menuBar
будет содержать пустую строку меню, которая будет строкой меню вашего главного окна.
Другой способ добавить строку меню в ваши приложения PyQt - создать объект QMenuBar
, а затем установить его в качестве строки меню главного окна с помощью .setMenuBar()
. Имея это в виду, вы также можете написать ._createMenuBar()
так:
from PyQt5.QtWidgets import QMenuBar
# Snip...
class Window(QMainWindow):
# Snip...
def _createMenuBar(self):
menuBar = QMenuBar(self)
self.setMenuBar(menuBar)
В приведенном выше примере menuBar
содержит объект QMenuBar
с установленным родительским элементом self
, который является главным окном приложения. Если у вас есть объект строки меню, вы можете использовать его .setMenuBar()
для добавления в главное окно. Наконец, обратите внимание, что для того, чтобы этот пример работал, вам сначала нужно выполнить импорт QMenuBar
из PyQt5.QWidgets
.
В приложении с графическим интерфейсом строка меню будет отображаться в разных положениях в зависимости от базовой операционной системы:
- Windows: вверху главного окна приложения под строкой заголовка.
- macOS: вверху экрана.
- Linux: либо вверху главного окна, либо вверху экрана, в зависимости от среды рабочего стола.
Последний шаг по созданию строки меню для вашего приложения - это вызов ._createMenuBar()
из инициализатора главного окна .__init__()
:
class Window(QMainWindow):
"""Main Window."""
def __init__(self, parent=None):
# Snip...
self._createMenuBar()
Если вы запустите образец приложения с этими новыми изменениями, вы не увидите строку меню, отображаемую в главном окне приложения. Это потому, что ваша строка меню все еще пуста. Чтобы увидеть строку меню в главном окне вашего приложения, вам нужно создать несколько меню. Вот что вы узнаете дальше.
Добавление меню в строку меню
Меню - это раскрывающиеся списки пунктов меню, которые можно вызвать, щелкнув их или нажав сочетание клавиш. Есть как минимум три способа добавить меню к объекту строке меню в PyQt:
QMenuBar.addMenu(menu)
добавляет объектQMenu
(menu
) к объекту строки меню. Он возвращает действие, связанное с этим меню.QMenuBar.addMenu(title)
создает и добавляет новый объектQMenu
со строкой (title
) в качестве заголовка к строке меню. Строка меню становится владельцем меню, а метод возвращает новый объектQMenu
.QMenuBar.addMenu(icon, title)
создает и добавляет новый объектQMenu
с помощьюicon
иtitle
к объекту строки меню. Строка меню становится владельцем меню, а метод возвращает новый объектQMenu
.
Если вы используете первый вариант, вам нужно сначала создать свои собственные объекты QMenu
. Для этого вы можете использовать один из следующих конструкторов:
QMenu(parent)
QMenu(title, parent)
В обоих случаях parent
это тот QWidget
, который будет владеть объектом QMenu
. Обычно вы устанавливаете parent
окно, в котором будете использовать меню. Во втором конструкторе title
будет содержаться строка с текстом, описывающим параметр меню.
Вот как вы можете добавить меню File, Edit и Help в строку меню вашего примера приложения:
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")
Сначала вы импортируете QMenu
из PyQt5.QtWidgets
. Затем ._createMenuBar()
вы добавляете три меню в строку меню, используя первые два варианта .addMenu()
. Для третьего варианта требуется объект иконки, но вы еще не научились создавать и использовать иконки. Об этом мы расскажем ниже в статье.
Если вы запустите пример приложения, то увидите, что теперь у вас есть строка меню, подобная этой:
В строке меню приложения есть меню File, Edit и Help. Когда вы щелкаете по этим меню, они не показывают раскрывающийся список опций меню. Это потому, что вы еще не добавили пункты меню.
Наконец, обратите внимание, на символ амперсанда (&
), который вы включаете в заголовок каждого меню, создает подчеркнутые буквы на отображении строки меню.
Создание панелей инструментов
Панель представляет собой выпадающее меню, которое содержит кнопки и другие виджеты для быстрого доступа к наиболее распространенным вариантам приложения с графическим интерфейсом. Кнопки панели инструментов могут отображать значки, текст или и то, и другое, чтобы представлять задачу, которую они выполняют. Базовый класс для панелей инструментов в PyQt - это QToolBar
. Этот класс позволит вам создавать настраиваемые панели инструментов для ваших приложений с графическим интерфейсом.
Когда вы добавляете панель инструментов в приложение в главное окно, позиция по умолчанию находится в верхней части окна. Однако вы можете разместить панель инструментов в любой из следующих четырех областей панели инструментов:
Область панели инструментов<br> | Положение в главном окне<br> |
Qt.LeftToolBarArea<br> | Левая сторона<br> |
Qt.RightToolBarArea<br> | Правая сторона<br> |
Qt.TopToolBarArea<br> | Верх |
Qt.BottomToolBarArea<br> | Низ |
Области панели инструментов определены в PyQt как константы. Если вам нужно их использовать, вам нужно импортировать Qt
из PyQt5.QtCore
а затем использовать полностью определенные имена, как в Qt.LeftToolBarArea
.
Есть три способа добавить панели инструментов в ваше главное окно приложения в PyQt:
QMainWindow.addToolBar(title)
создает новый пустой объектQToolBar
и устанавливает для его заголовка окна значениеtitle
. Этот метод вставляет панель инструментов в область верхней панели инструментов и возвращает только что созданную панель инструментов.QMainWindow.addToolBar(toolbar)
вставляет объектQToolBar
(toolbar
) в область верхней панели инструментов.QMainWindow.addToolBar(area, toolbar)
вставляет объектQToolBar
(toolbar
) в указанную область панели инструментов (area
). Если в главном окне уже есть панели инструментовtoolbar
, оно помещается после последней существующей панели инструментов. Еслиtoolbar
уже существует в главном окне, он будет перемещен только вarea
.
Если вы используете один из двух последних вариантов, вам необходимо создать панель инструментов самостоятельно. Для этого можно использовать один из следующих конструкторов:
QToolBar(parent)
QToolBar(title, parent)
В обоих случаях parent
представляет объект QWidget
, который будет владеть панелью инструментов. Обычно вы устанавливаете владельцем панели инструментов окно, в котором вы собираетесь использовать панель инструментов. Во втором конструкторе title
будет строка с заголовком окна панели инструментов. PyQt использует этот заголовок окна для создания контекстного меню по умолчанию, которое позволяет скрывать и отображать панели инструментов.
Теперь вы можете вернуться к вашему образцу приложения и добавить следующий метод в 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)
Сначала вы импортируете QToolBar
из PyQt5.QtWidgets
. Затем, в ._createToolBars()
, вы сначала создаете панель инструментов File, используя .addToolBar()
с заголовком. Затем вы создаете объект QToolBar
с заголовком "Edit"
и добавляете его на панель инструментов .addToolBar()
, не передавая область панели инструментов. В этом случае панель инструментов Edit размещается в верхней области панели инструментов. Наконец, вы создаете панель инструментов Help и размещаете ее в левой области панели инструментов с помощью Qt.LeftToolBarArea
.
Последним шагом для выполнения этой работы является вызов ._createToolBars()
из инициализатора Window
:
class Window(QMainWindow):
"""Main Window."""
def __init__(self, parent=None):
# Snip...
self._createToolBars()
Вызов ._createToolBars()
внутри инициализатора Window
создаст три панели инструментов и добавит их в ваше главное окно. Вот как теперь выглядит ваше приложение:
Теперь у вас есть две панели инструментов прямо под строкой меню и одна панель инструментов вдоль левой стороны окна. Каждая панель инструментов имеет двойную пунктирную линию. Когда вы наводите указатель мыши на пунктирные линии, указатель принимает вид руки. Если вы нажмете и удерживаете пунктирную линию, вы можете переместить панель инструментов в любое другое положение или область панели инструментов в окне.
Если вы щелкните правой кнопкой мыши панель инструментов, PyQt покажет контекстное меню, которое позволит вам скрывать и отображать существующие панели инструментов в соответствии с вашими потребностями.
На данный момент у вас есть три панели инструментов в окне вашего приложения. Эти панели инструментов по-прежнему пусты - вам нужно добавить несколько кнопок на панели инструментов, чтобы они работали. Для этого вы можете использовать действия PyQt, которые являются экземплярами QAction
. Вы узнаете, как создавать действия в PyQt в следующем разделе. А пока вы узнаете, как использовать иконки и другие ресурсы в своих приложениях PyQt.
Использование иконок и ресурсов в PyQt
Библиотека Qt включает систему ресурсов Qt, которая представляет собой удобный способ добавления двоичных файлов, таких как иконки, изображения, файлы перевода и другие ресурсы, в ваши приложения.
Чтобы использовать систему ресурсов, вам необходимо указать свои ресурсы в файле коллекции ресурсов или файле .qrc
. Файл .qrc
представляет собой XML
файл, который содержит местоположение или путь, каждый ресурс в файловой системе.
Предположим, что в вашем примере приложения есть каталог resources
, содержащий иконки, которые вы хотите использовать в графическом интерфейсе приложения. У вас есть иконки для таких опций, как Create, Open и т.д. Вы можете создать файл .qrc
, содержащий путь к каждой иконке:
<!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>
Каждая запись <file>
должна содержать путь к ресурсу в вашей файловой системе. Указанные пути указаны относительно каталога, в котором находится файл .qrc
. В приведенном выше примере каталог resources
должен находиться в том же каталоге, что и файл .qrc
.
alias
- необязательный атрибут, определяющий короткое альтернативное имя, которое вы можете использовать в своем коде для доступа к каждому ресурсу.
Когда у вас появятся ресурсы для вашего приложения, вы можете запустить инструмент командной строки pyrcc5
для своего файла .qrc
. pyrcc5
поставляется с PyQt и должен быть полностью функциональным в вашей среде Python после установки PyQt.
pyrcc5
читает .qrc
файл и создает модуль Python, содержащий двоичный код для всех ваших ресурсов:
pyrcc5 -o qrc_resources.py resources.qrc
Эта команда будет читать resources.qrc
и генерировать qrc_resources.py
двоичный код для каждого ресурса. Вы сможете использовать эти ресурсы в своем коде Python, импортировав qrc_resources
.
Вот фрагмент кода qrc_resources.py
, который соответствует вашему resources.qrc
:
# -*- 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\
...
После этого вы можете импортировать qrc_resources.py
в свое приложение и ссылаться на каждый ресурс, набрав двоеточие (:), а затем либо alias
, либо его путь. Например, чтобы получить доступ file-new.svg
с его псевдонимом, вы должны использовать строку доступа ":file-new.svg"
. Если бы у вас не было alias
, вы могли бы получить к нему доступ по его пути со строкой доступа ":resources/file-new.svg"
.
Если у вас есть псевдонимы, но по какой-то причине вы хотите вместо этого получить доступ к данному ресурсу по его пути, вам, возможно, придется удалить двоеточие из строки доступа, чтобы это работало правильно.
Чтобы использовать иконки в своих действиях, вам сначала нужно импортировать модуль ресурсов:
import qrc_resources
После того, как вы импортировали модуль, содержащий ваши ресурсы, вы можете использовать ресурсы в графическом интерфейсе вашего приложения.
Чтобы создать иконку с использованием системы ресурсов, вам необходимо создать экземпляр QIcon
, передав псевдоним или путь к конструктору класса:
newIcon = QIcon(":file-new.svg")
В этом примере вы создаете объект QIcon
с файлом file-new.svg
, который находится в вашем модуле ресурсов. Это обеспечивает удобный способ использования иконок и ресурсов в вашем приложении с графическим интерфейсом.
Теперь вернитесь к вашему образцу приложения и обновите последнюю строку ._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")
Чтобы этот код работал, вам сначала нужно выполнить импорт QIcon
из PyQt5.QtGui
. Вам также необходимо импортировать qrc_resources
. В последней строке вы добавляете иконку help-content.svg
для использования helpMenu
из модуля ресурсов.
Если вы запустите образец приложения с этим обновлением, вы получите следующий результат:
В главном окне приложения теперь отображается значок в меню Help. Когда вы щелкаете иконку, в меню отображается текст Help
. Использование иконок в строке меню - не обычная практика, но PyQt все равно позволяет это делать.
Создание действий для меню и панелей инструментов Python в PyQt
Действия PyQt - это объекты, которые представляют заданную команду, операцию или действие в приложении. Они полезны, когда вам нужно предоставить одинаковые функциональные возможности для различных компонентов графического интерфейса, таких как параметры меню, кнопки панели инструментов и сочетания клавиш.
Вы можете создавать действия, создавая экземпляры QAction
. После того, как вы создали действие, вам необходимо добавить его в виджет, чтобы иметь возможность использовать его на практике.
Также необходимо связать свои действия с каким-то функционалом. Другими словами, вам необходимо подключить их к функции или методу, которые вы хотите запустить при запуске действия. Это позволит вашему приложению выполнять операции в ответ на действия пользователя в графическом интерфейсе.
Действия довольно разносторонние. Они позволяют повторно использовать и синхронизировать одни и те же функции в параметрах меню, кнопках панели инструментов и сочетаниях клавиш. Это обеспечивает единообразное поведение во всем приложении.
Например, пользователи могут ожидать, что приложение выполнит то же действие, когда они щелкнут пункт меню Open…, нажатие кнопки Open на панели инструментов или нажмите Ctrl+O на своей клавиатуре.
QAction
предоставляет абстракцию, которая позволяет отслеживать следующие элементы:
- Текст в параметрах меню
- Текст на кнопках панели инструментов
- Подсказка по параметрам панели инструментов (всплывающая подсказка)
- Справочный совет "Что это за"
- Подсказка в строке состояния (подсказка состояния)
- Сочетание клавиш, связанное с параметрами
- Иконка, связанный с параметрами меню и панели инструментов
enabled
илиdisabled
состояниеon
илиoff
состояние
Чтобы создать действия, вам нужно создать экземпляр QAction
. Есть как минимум три основных способа сделать это:
QAction(parent)
QAction(text, parent)
QAction(icon, text, parent)
Во всех трех случаях parent
представляет объект, которому принадлежит действие. Этот аргумент может быть любым QObject
. Лучше всего создавать действия как дочерние по отношению к окну, в котором вы собираетесь их использовать.
Во втором и третьем конструкторах text
содержит текст, который будет отображать в пункте меню или на кнопке панели инструментов.
Текст действия по-разному отображается в параметрах меню и на кнопках панели инструментов. Например, текст &Open...
отображается как Open… в пункте меню и как Open на кнопке панели инструментов.
В третьем конструкторе находится объект QIcon
, содержащий иконку действия. Эта иконка будет отображаться слева от текста в пункте меню. Положение иконки на кнопке панели инструментов зависит от свойства .toolButtonStyle
панели инструментов, которое может принимать одно из следующих значений:
Стиль | Дисплей кнопок |
Qt.ToolButtonIconOnly<br> | Только значок<br> |
Qt.ToolButtonTextOnly<br> | Только текст<br> |
Qt.ToolButtonTextBesideIcon<br> | Текст рядом со значком<br> |
Qt.ToolButtonTextUnderIcon<br> | Текст под значком<br> |
Qt.ToolButtonFollowStyle<br> | Соответствует общему стилю базовой платформы<br> |
Вы также можете установить текст и иконку действия, используя соответствующие методы установки .setText()
и .setIcon()
.
Вот как вы можете создать некоторые действия для вашего примера приложения, используя различные конструкторы QAction
:
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)
В ._createActions()
, вы создаете несколько действий для вашего примера приложения. Эти действия позволят вам добавлять параметры в меню и панели инструментов приложения.
Обратите внимание, что вы создаете действия как атрибуты экземпляра, поэтому вы можете получить к ним доступ извне ._createActions()
, используя self
. Таким образом, вы сможете использовать эти действия как в меню, так и на панелях инструментов.
Следующим шагом будет вызов ._createActions()
из инициализатора Window
:
class Window(QMainWindow):
"""Main Window."""
def __init__(self, parent=None):
# Snip...
self._createActions()
self._createMenuBar()
self._createToolBars()
Если вы запустите приложение сейчас, вы не увидите никаких изменений в графическом интерфейсе. Это потому, что действия не отображаются, пока они не добавлены в меню или на панель инструментов. Обратите внимание, что вы вызываете ._createActions()
перед вызовом ._createMenuBar()
и ._createToolBars()
потому что вы будете использовать эти действия в своих меню и панелях инструментов.
Если вы добавляете действие в меню, оно становится опцией меню. Если вы добавляете действие на панель инструментов, то действие становится кнопкой панели инструментов. Это тема для следующих нескольких разделов.
Добавление параметров в меню Python в PyQt
Если вы хотите добавить список опций к данному меню в PyQt, вам необходимо использовать действия. Итак, вы узнали, как создавать действия с помощью различных конструкторов QAction
. Действия - ключевой компонент при создании меню в PyQt.
В этом разделе вы узнаете, как использовать действия для заполнения меню параметрами меню.
Заполнение меню действиями
Чтобы заполнить меню параметрами меню, вы будете использовать действия. В меню действие представлено как горизонтальный параметр, который имеет как минимум описательный текст, например New, Open, Save и т.д. Параметры меню также могут отображать иконку с левой стороны и последовательность сочетаний клавиш, например Ctrl+S с правой стороны.
Вы можете добавлять действия к объекту QMenu
, используя .addAction()
. У этого метода есть несколько вариаций. Считается, что большинство из них создают действия на лету. Однако в этом руководстве вы собираетесь использовать вариант .addAction()
, QMenu
унаследованный от QWidget
. Вот пример этого варианта:
QWidget.addAction(action)
Аргумент action
представляет объект QAction
, который вы хотите добавить к данному объекту QWidget
. С помощью этого варианта вы можете заранее создавать свои действия, а затем добавлять их в свои меню по мере необходимости.
С помощью этого инструмента вы можете начать добавлять действия в меню вашего образца приложения. Для этого вам необходимо обновить ._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)
С этим обновлением ._createMenuBar()
вы добавляете множество параметров в три меню вашего образца приложения.
Порядок, в котором параметры отображаются в меню сверху вниз, соответствует порядку, в котором вы добавляете параметры в свой код.
Если вы запустите приложение, то вы увидите на экране следующее окно:
Если щелкнуть меню, приложение покажет раскрывающийся список с параметрами, которые вы видели ранее.
Создание подменю Python
Иногда вам нужно использовать подменю в ваших графических приложениях. Подменю - это вложенное меню, которое появляется, когда вы наводите курсор на заданный пункт меню. Чтобы добавить подменю в приложение, вам нужно вызвать .addMenu()
у объекта контейнера.
Скажем, вам нужно добавить подменю в меню Edit вашего примера приложения. Ваше подменю будет содержать параметры для поиска и замены содержимого, поэтому вы назовете его Find and Replace. В этом подменю есть два варианта:
- Find… для поиска содержания
- Replace… для поиска и замены старого содержания новым содержанием.
Вот как вы можете добавить это подменю в свой образец приложения:
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...
В первой выделенной строке вы добавляете объект QMenu
с текстом "Find and Replace"
в меню Edit с помощью .addMenu()
у editMenu
. Следующий шаг - заполнить подменю действиями, как вы это делали до ранее. Если вы снова запустите образец приложения, вы увидите новый параметр в меню Edit:
Добавление параметров на панели инструментов в PyQt
Панели инструментов - довольно полезный компонент, когда дело доходит до создания приложений с графическим интерфейсом пользователя с помощью Python и PyQt. Вы можете использовать панель инструментов, чтобы предоставить пользователям быстрый доступ к наиболее часто используемым параметрам в вашем приложении. Вы также можете добавить на панель инструментов виджеты, такие как счетчики и поля со списком, чтобы пользователь мог напрямую изменять некоторые свойства и переменные из графического интерфейса приложения.
В следующих нескольких разделах вы узнаете, как добавлять параметры или кнопки на панели инструментов с помощью действий, а также как добавлять виджеты на панель инструментов с помощью .addWidget()
.
Заполнение панелей инструментов действиями
Чтобы добавить параметры или кнопки на панель инструментов, вам нужно вызвать .addAction()
. В этом разделе вы будете полагаться на вариацию .addAction()
, QToolBar
унаследованную от QWidget
. Итак, вы вызовете .addAction()
и действие в качестве аргумента. Это позволит вам делиться своими действиями между меню и панелями инструментов.
Когда вы создаете панели инструментов, вы обычно сталкиваетесь с проблемой решения, какие параметры добавить к ним. Как правило, вы хотите добавить на свои панели инструментов только наиболее часто используемые действия.
Если вы вернетесь к своему образцу приложения, то вспомните, что добавили три панели инструментов:
- File
- Edit
- Help
На панели инструментов File вы можете добавить следующие параметры:
- New
- Open
- Save
На панели инструментов Edit вы можете добавить следующие параметры:
- Copy
- Paste
- Cut
Обычно, когда вы хотите добавить кнопки на панель инструментов, вы сначала выбираете иконки, которые хотите использовать на каждой кнопке. Это не обязательно, но это лучшая практика. После того, как вы выбрали иконки, вам нужно добавить их к соответствующим действиям.
Вот как вы можете добавить иконки к действиям вашего примера приложения:
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...
В случае newAction
используется .setIcon()
. В остальных действиях вы используете конструктор с объектом icon
, а title
и parent
в качестве аргументов.
После того как выбранные вами действия имеют иконки, вы можете добавить эти действия на соответствующую панель инструментов, вызвав .addAction()
у объекта панели инструментов:
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)
В этом обновлении ._createToolBars()
вы добавляете кнопки для параметров Create, Open и Save на панель инструментов File. Вы также добавляете кнопки для параметров Copy, Paste и Cut на панель инструментов Edit.
Если вы сейчас запустите образец приложения, на экране появится следующее окно:
В примере приложения теперь отображаются две панели инструментов с несколькими кнопками на каждой. Ваши пользователи могут нажимать эти кнопки, чтобы получить быстрый доступ к наиболее часто используемым параметрам приложения.
Обратите внимание, что, поскольку вы используете одни и те же действия в своих меню и панелях инструментов, параметры меню также будут отображать значки с левой стороны, что является большим преимуществом с точки зрения производительности и использования ресурсов. Это одно из преимуществ использования действий PyQt для создания меню и панелей инструментов с помощью Python.
Добавление виджетов на панель инструментов
В некоторых ситуациях вам будет полезно добавить на панель инструментов определенные виджеты, такие как счетчики, поля со списком или другие. Типичным примером этого являются поля со списком, которые большинство текстовых процессоров используют, чтобы позволить пользователю изменять шрифт документа или размер выделенного текста.
Чтобы добавить виджеты на панель инструментов, вам сначала нужно создать виджет, настроить его свойства, а затем вызвать объект панели инструментов, передав .addWidget()
виджет в качестве аргумента.
Предположим, вы хотите добавить объект QSpinBox
на панель инструментов Edit в вашем примере приложения, чтобы пользователь мог изменять размер чего-либо, например, размера шрифта. Вам необходимо обновить ._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)
Здесь вы сначала импортируете класс счетчика. Затем вы создаете объект QSpinBox
, устанавливаете focusPolicy
значение Qt.NoFocus
и наконец, добавляете его на панель инструментов редактирования.
Теперь, если вы запустите приложение, вы получите следующий результат:
Здесь на панели инструментов Edit отображается объект QSpinBox
, который пользователи могут использовать для установки размера шрифта или любого другого числового свойства в приложении.
Настройка панелей инструментов
Панели инструментов PyQt довольно гибкие и настраиваемые. Вы можете установить набор свойств для объекта панели инструментов. Некоторые из наиболее полезных свойств показаны в следующей таблице:
Свойство<br> | Функция под контролем<br> | Настройки по умолчанию<br> |
allowedAreas<br> | Области панели инструментов, в которых вы можете разместить данную панель инструментов<br> | Qt.AllToolBarAreas<br> |
floatable | Можно ли перетащить панель инструментов как отдельное окно<br> | True |
floating | Является ли панель инструментов независимым окном<br> | True |
iconSize<br> | Размер иконок, отображаемых на кнопках панели инструментов<br> | Определяется стилем приложения<br> |
movable | Можно ли перемещать панель инструментов внутри области панели инструментов или между областями панели инструментов<br> | True |
orientation<br> | Ориентация панели инструментов<br> | Qt.Horizontal<br> |
Все эти свойства имеют связанный метод установки. Например, вы можете использовать .setAllowedAreas()
для установки allowedAreas
, .setFloatable()
для установки floatable
и так далее.
Теперь предположим, что вы не хотите, чтобы ваши пользователи перемещали панель инструментов File по окну. В этом случае, вы можете установить movable
в False
используя .setMovable()
:
class Window(QMainWindow):
# Snip...
def _createToolBars(self):
# File toolbar
fileToolBar = self.addToolBar("File")
fileToolBar.setMovable(False)
# Snip...
Выделенная линия создает здесь волшебство. Теперь ваши пользователи не могут перемещать панель инструментов по окну приложения:
На панели инструментов File больше не отображается двойная пунктирная линия, поэтому ваши пользователи не смогут ее перемещать. Обратите внимание, что панель инструментов Edit по-прежнему подвижна. Вы можете изменить другие свойства на своих панелях инструментов, используя тот же подход, и настроить их в соответствии с вашими потребностями.
Организация параметров меню и панели инструментов
Чтобы добавить ясности и улучшить взаимодействие с пользователем в ваших приложениях с графическим интерфейсом, вы можете организовать параметры меню и кнопки панели инструментов с помощью разделителей. Разделитель отображается как горизонтальная линия, разделяющая пункты меню, или как вертикальная линия, разделяющая кнопки панели инструментов.
Чтобы вставить или добавить разделитель к объекту меню, подменю или панели инструментов, вы можете вызвать .addSeparator()
для любого из этих объектов.
Например, вы можете использовать разделитель, чтобы отделить параметр Exit в меню File от остальных параметров, просто чтобы прояснить, что Exit логически не связан с остальными параметрами в меню. Вы также можете использовать разделитель, чтобы отделить опцию Find and Replace в меню Edit от остальных опций, соответствующих тому же правилу.
Перейдите в образец приложения и обновите ._createMenuBar()
, как показано в следующем коде:
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...
В первой выделенной строке вы добавляете разделитель между параметрами Save и Exit в меню File. Во второй выделенной строке вы добавляете разделитель, который отделяет опцию Find and Replace от остальных опций в меню Edit. Вот как работают эти дополнения:
В меню File теперь отображается горизонтальная линия, отделяющая параметр Edit от остальных параметров меню. В меню Edit также отображается разделитель в конце раскрывающегося списка параметров. Последовательное использование разделителя может немного улучшить ясность ваших меню и панелей инструментов, делая ваши приложения с графическим интерфейсом пользователя более удобными.
Создание контекстных или всплывающих меню в PyQt
Контекстные меню, также известные как всплывающие меню, представляют собой особый тип меню, которое появляется в ответ на определенные действия пользователя, такие как клик правой кнопкой мыши по заданному виджету или окну. Эти меню предлагают небольшой список параметров, доступных в конкретном контексте используемой вами операционной системы или приложения.
Например, если вы щелкните правой кнопкой мыши рабочий стол компьютера с Windows, вы получите меню с параметрами, которые соответствуют этому конкретному контексту или пространству операционной системы. Если вы щелкните правой кнопкой мыши рабочую область текстового редактора, вы получите совершенно другое контекстное меню, которое будет зависеть от редактора, который вы используете.
В PyQt у вас есть несколько вариантов создания контекстных меню. В этом руководстве вы узнаете о двух из этих вариантов:
- Установка свойства
contextMenuPolicy
на определенных виджетах наQt.ActionsContextMenu
- Обработка события контекстного меню в окне приложения через
contextMenuEvent()
Первый вариант является наиболее распространенным и удобным из двух, поэтому вы сначала узнаете о нем.
Второй вариант немного сложнее и основан на обработке пользовательских событий. В программировании с графическим интерфейсом событие - это любое действие пользователя в приложении, такое как нажатие кнопки или меню, выбор элемента из поля со списком, ввод или обновление текста в текстовом поле, нажатие клавиши на клавиатуре и т.д.
Создание контекстных меню с помощью политики контекстного меню
Все графические компоненты или виджеты PyQt, являющиеся производными QWidget
, наследуют свойство с именем contextMenuPolicy
. Это свойство определяет, как виджет отображает контекстное меню. Одно из наиболее часто используемых значений этого свойства - Qt.ActionsContextMenu
. Это заставляет виджет отображать свой внутренний список действий в виде контекстного меню.
Чтобы виджет отображал контекстное меню на основе его внутренних действий, вам необходимо выполнить два шага:
- Добавьте действия к виджету, используя
QWidget.addAction()
. - Набор
contextMenuPolicy
дляQt.ActionsContextMenu
виджета с помощью.setContextMenuPolicy()
.
При выборе значения contextMenuPolicy
для Qt.ActionsContextMenu
виджетов с действиями они отображаются в контекстном меню. Это действительно быстрый способ создать контекстное меню с помощью Python и PyQt.
С помощью этого метода вы можете добавить контекстное меню к центральному виджету вашего образца приложения и предоставить пользователям способ быстрого доступа к некоторым параметрам приложения. Для этого вы можете добавить следующий метод в 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)
В ._createContextMenu()
, вы перввой строчкой добавляете contextMenuPolicy
со значением Qt.ActionsContextMenu
с помощью метода .setContextMenuPolicy()
. Затем вы добавляете действия к виджету с помощью .addAction()
, как обычно. Последний шаг - вызов ._createContextMenu()
инициализатора Window
:
class Window(QMainWindow):
"""Main Window."""
def __init__(self, parent=None):
# Snip...
self._createToolBars()
self._createContextMenu()
Если вы запустите образец приложения после этих добавлений, вы увидите, что центральный виджет приложения показывает контекстное меню, когда вы щелкаете по нему правой кнопкой мыши:
Теперь в вашем примере приложения есть контекстное меню, которое всплывает всякий раз, когда вы щелкаете правой кнопкой мыши в центральном виджете приложения. Центральный виджет растягивается, чтобы занять все доступное пространство в окне, поэтому вы не ограничены щелчком правой кнопкой мыши по тексту метки, чтобы увидеть контекстное меню.
Наконец, поскольку вы используете одни и те же действия во всем приложении, параметры в контекстном меню показывают одинаковый набор значков.
Создание контекстных меню посредством обработки событий
Альтернативный способ создания контекстных меню в PyQt - обработка события контекстного меню главного окна приложения. Для этого вам необходимо выполнить следующие действия:
- Переопределите метод обработчика событий
.contextMenuEvent()
на объектеQMainWindow
. - Создайте объект
QMenu
, передающий виджет (виджет контекста) в качестве своего родителя. - Заполните объект меню действиями.
- Запустите объект меню
QMenu.exec()
, используя событие.globalPos()
в качестве аргумента.
Этот способ управления контекстными меню немного сложнее. Однако он дает вам точный контроль над тем, что происходит при вызове контекстного меню. Например, вы можете включать или отключать опции меню в зависимости от состояния приложения и так далее.
Вот как вы можете повторно реализовать контекстное меню вашего примера приложения, переопределив метод обработчика событий в объекте главного окна:
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())
В contextMenuEvent()
, вы сначала создаете объект QMenu
(menu
) с родительским виджетом centralWidget
. Затем вы заполняете меню действиями, используя .addAction
. Наконец, вы вызываете .exec()
на объект QMenu
, чтобы показать его на экране.
Второй аргумент .contextMenuEvent()
представляет событие, которое перехватывает метод. В этом случае event
это будет щелчок правой кнопкой мыши по центральному виджету приложения.
В вызове .exec()
вы используете event.globalPos()
в качестве аргумента. Этот метод возвращает глобальную позицию указателя мыши, когда пользователь щелкает окно PyQt или виджет. Положение мыши подскажет .exec()
, где в окне показывать контекстное меню.
Если вы запустите образец приложения с этими новыми изменениями, вы получите тот же результат, что и в предыдущем разделе.
Организация параметров контекстного меню
В отличие от меню и панелей инструментов, в контекстных меню нельзя добавить разделитель и визуально разделить параметры меню в соответствии с их взаимосвязью. Когда дело доходит до организации контекстных меню, вам нужно создать действие-разделитель:
separator = QAction(parent)
separator.setSeparator(True)
Вызов .setSeparator(True)
у объекта действия превратит это действие в разделитель. Когда у вас есть действие разделителя, вам нужно вставить его в нужное место в контекстном меню, используя QMenu.addAction()
.
Если вы вернетесь к своему образцу приложения, то, возможно, вы захотите визуально отделить параметры, поступающие из меню File, от параметров, поступающих из меню Edit. Для этого вы можете обновить .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...
В первых двух выделенных строках вы создаете действие-разделитель. В третьей выделенной строке вы добавляете действие-разделитель в меню, используя .addAction()
.
Это добавит горизонтальную линию между параметрами файла и параметрами редактирования. Вот как выглядит ваше контекстное меню с этим дополнением:
Подключение сигналов и слотов в меню и панелях инструментов
В PyQt вы используете сигналы и слоты для обеспечения функциональности ваших приложений с графическим интерфейсом. Виджеты PyQt излучают сигналы каждый раз, когда на них происходит такое событие, как щелчок мыши, нажатие клавиши или изменение размера окна.
Слот является обратной функцией, которую можно подключить к сигналу виджета, чтобы выполнить какие - либо действия в ответ на пользовательские события. Если сигнал и слот связаны, то слот будет вызываться автоматически каждый раз, когда сигнал запускается. Если данный сигнал не подключен к слоту, то при его передаче ничего не произойдет.
Чтобы параметры меню и кнопки панели инструментов запускали некоторые операции, когда пользователь нажимает на них, вам необходимо связать сигналы основных действий с некоторыми настраиваемыми или встроенными слотами.
QAction
объекты могут излучать самые разные сигналы. Однако наиболее часто используемый сигнал в меню и панелях инструментов - это .triggered()
. Этот сигнал излучается каждый раз, когда пользователь щелкает пункт меню или кнопку панели инструментов. Чтобы подключить .triggered()
к слоту, вы можете использовать следующий синтаксис:
action = QAction("Action Text", parent)
# Connect action's triggered() with a slot
action.triggered.connect(slot)
В этом примере slot это функция. Другими словами, slot
может быть функцией, методом, классом или экземпляром класса, который реализует .__call__()
.
В вашем примере приложения уже есть набор действий. Теперь вам нужно закодировать слоты, которые вы будете вызывать каждый раз, когда пользователь щелкает пункт меню или кнопку панели инструментов. Перейдите к определению Window
и добавьте следующие методы:
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 > Pate</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")
Эти методы будут играть роль слотов вашего примера приложения. Они будут вызываться каждый раз, когда пользователь щелкает соответствующий пункт меню или кнопку панели инструментов.
Когда у вас есть слоты, которые обеспечивают функциональность, вам нужно связать их с сигналом действия .triggered()
. Таким образом, приложение будет выполнять действия в ответ на пользовательские события. Чтобы выполнить эти подключения, перейдите к образцу приложения и добавьте следующий метод в 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)
Этот метод свяжет все .triggered()
сигналы ваших действий с соответствующими слотами или обратными вызовами. С этим обновлением ваше примерное приложение будет отображать сообщение в объекте QLabel
, который вы установили в качестве центрального виджета, с указанием того, какой пункт меню или кнопка панели инструментов была нажата.
В случае exitAction
, вы подключаете его triggered()
сигнал со встроенным слотом QMainWindow.close()
. Таким образом, если вы выберете File → Exit, ваше приложение закроется.
Наконец, перейдите к инициализатору Window
и добавьте вызов ._connectActions()
:
class Window(QMainWindow):
"""Main Window."""
def __init__(self, parent=None):
# Snip...
# self._createContextMenu()
self._connectActions()
С этим последним обновлением вы можете снова запустить приложение. Вот как работают все эти изменения:
Динамическое заполнение меню Python
При создании меню для приложения вам иногда необходимо заполнить эти меню параметрами, которые неизвестны на момент создания графического интерфейса приложения. Например, меню Open Recent в текстовом редакторе показывает список недавно открытых документов. Вы не можете заполнить это меню во время создания графического интерфейса приложения, потому что каждый пользователь будет открывать разные документы, и нет никакого способа узнать эту информацию заранее.
В этом случае вам необходимо динамически заполнять меню в ответ на действия пользователя или состояние приложения. QMenu
имеет сигнал .aboutToShow()
говорящий о том, что вы можете подключиться к настраиваемому слоту для динамического заполнения объекта меню до его отображения на экране.
Чтобы продолжить разработку примера приложения, предположим, что вам нужно создать подменю Open Recent в разделе File и динамически заполнить его недавно открытыми файлами или документами. Для этого вам необходимо выполнить следующие действия:
- Создайте подменю Open Recent в разделе File.
- Закодируйте пользовательский слот, который динамически генерирует действия для заполнения меню.
- Подключите сигнал меню к пользовательскому слоту с помощью
.aboutToShow()
.
Вот код для создания подменю:
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...
В выделенной строке вы добавляете подменю в меню File с заголовком "Open Recent"
. В этом подменю пока нет параметров меню. Вам необходимо динамически создавать действия, чтобы заполнить его.
Вы можете сделать это, написав метод динамического создания действий и добавив их в подменю. Вот пример, показывающий общую логику, которую вы можете использовать:
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)
В .populateOpenRecent()
сначала удалите старые параметры, если они есть, из меню с помощью .clear()
. Затем вы добавляете логику для динамического создания и подключения действий. Наконец, вы добавляете действия в меню, используя .addActions()
.
В цикле for
вы используете functools.partial()
для добавления .triggered()
сигнала, потом вы хотите передаете filename
в качестве аргумента .openRecentFile()
. Это довольно полезный метод, когда речь идет о соединении сигнала со слотом, который принимает дополнительные аргументы. Чтобы он работал, вам нужно импортировать метод partial()
из functools
.
Следующим шагом будет подключение сигнала .openRecentMenu
к .populateOpenRecent()
. Для этого добавьте следующую строку в конец ._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)
Теперь вам нужно написать код .openRecentFile()
. Это метод, который ваше приложение будет вызывать, когда пользователи щелкают любое из динамически созданных действий:
class Window(QMainWindow):
# Snip...
def openRecentFile(self, filename):
# Logic for opening a recent file goes here...
self.centralWidget.setText(f"<b>{filename}</b> opened")
Этот метод обновит текст объекта QLabel
, который вы используете в качестве центрального виджета в вашем примере приложения.
Вот как ваше динамически создаваемое подменю работает на практике:
Когда указатель мыши находится над меню Open Recent, меню издает сигнал. Это приводит к вызову .populateOpenRecent()
, который создает и связывает действия. Если вы щелкните имя файла, вы увидите, что центральная метка изменится соответствующим образом, чтобы отобразить сообщение.
Определение сочетаний клавиш для параметров меню и панели инструментов
Сочетания клавиш - важная функция в приложении с графическим интерфейсом. Сочетание клавиш - это комбинация клавиш, которую вы можете нажать на клавиатуре для быстрого доступа к некоторым из наиболее распространенных опций в приложении.
Вот несколько примеров сочетаний клавиш:
- Ctrl+C копирует что-то в буфер обмена.
- Ctrl+V вставляет что-то из буфера обмена.
- Ctrl+Z отменяет последнюю операцию.
- Ctrl+O открывает файлы.
- Ctrl+S сохраняет файлы.
В следующем разделе вы узнаете, как добавить в приложение сочетания клавиш, чтобы повысить производительность и удобство работы пользователей.
Использование ключевых последовательностей
До сих пор вы узнали, что QAction
это универсальный класс для заполнения меню и панелей инструментов. QAction
также предоставляет удобный способ определения сочетаний клавиш для пунктов меню и кнопок панели инструментов.
QAction
имеет метод .setShortcut()
. Этот метод принимает объект QKeySequence
в качестве аргумента и возвращает shortcut.
QKeySequence
предоставляет несколько конструкторов. В этом руководстве вы узнаете о двух из них:
QKeySequence(ks, format)
принимает в качестве аргументов последовательность клавиш на основе строки (ks
) и формат (format
) и создает объектQKeySequence
.QKeySequence(key)
принимает константуStandardKey
в качестве аргумента и создает объектQKeySequence
, который соответствует этим последовательностям ключей на базовой платформе.
Первый конструктор распознает следующие строки:
"Ctrl"
"Shift"
"Alt"
"Meta"
Вы можете создавать строки на основе последовательности клавиш, комбинируя эти строки с буквами, знаки препинания, цифры, именные ключи (Up
, Down
, Home
), и функциональные клавиши ("Ctrl+S"
, "Ctrl+5"
, "Alt+Home"
, "Alt+F4"
). Вы можете передать до четырех из этих последовательностей строковых ключей в списке, разделенном запятыми.
Второй конструктор удобен, если вы разрабатываете многоплатформенное приложение и хотите использовать стандартные сочетания клавиш для каждой платформы. Например, QKeySequence.Copy
вернет стандартное сочетание клавиш платформы для копирования объектов в буфер обмена.
Имея это общее представление о том, как определять сочетания клавиш для действий в PyQt, вы можете вернуться к своему образцу приложения и добавить несколько сочетаний клавиш. Для этого вам необходимо обновить ._createActions()
:
from PyQt5.QtGui import QKeySequence
# Snip...
class Window(QMainWindow):
# Snip...
def _createActions(self):
# File actions
# Snip...
# Using string-based key sequences
self.newAction.setShortcut("Ctrl+N")
self.openAction.setShortcut("Ctrl+O")
self.saveAction.setShortcut("Ctrl+S")
# Edit actions
# Snip...
# Using standard keys
self.copyAction.setShortcut(QKeySequence.Copy)
self.pasteAction.setShortcut(QKeySequence.Paste)
self.cutAction.setShortcut(QKeySequence.Cut)
# Snip...
Сначала вам нужно импортировать QKeySequence
. Внутри ._createActions()
первые три строки создают сочетания клавиш с использованием последовательности клавиш на основе строки. Это быстрый способ добавить к вашему действию сочетания клавиш. Во вторых трех строках вы используете QKeySequence
со стандартным сочетанием клавиш.
Если вы запустите пример приложения с этими дополнениями, ваши меню будут выглядеть следующим образом:
Теперь в пунктах вашего меню справа отображается сочетание клавиш. Если вы нажмете любую из этих комбинаций клавиш, вы выполните соответствующее действие.
Использование быстрого доступа с клавиатуры
Есть еще одна альтернатива, которую вы можете использовать для добавления сочетаний клавиш или быстрого доступа в пункты меню ваших приложений.
Вы могли заметить, что при установке текста для меню или параметра меню вы обычно вставляете в текст символ амперсанда (&
). Вы делаете это так, чтобы буква сразу после амперсанда была подчеркнута при отображении в тексте меню или пункта меню. Например, если вы поместите амперсанд перед буквой F в заголовке меню File ("&File"
), то при отображении заголовка меню буква F будет подчеркнута.
В случае строки меню использование амперсанда позволяет вызывать любое меню, нажимая Alt
вместе с подчеркнутой буквой в заголовке меню.
После того, как вы запустили меню, вы можете получить доступ к любой опции меню, нажав подчеркнутую букву в тексте опции. Например, в файле вы можете получить доступ к опции Exit
, нажав букву E.
Эта функция позволит вам предоставить быстрые доступ к опциям с помощью клавиатуры для пользователей, которые предпочитают использовать клавиатуру для работы с вашими приложениями. Этот метод особенно полезен для параметров, которые не предоставляют явного сочетания клавиш.
Создание меню и панелей инструментов: передовой опыт и советы
Когда вы создаете меню и панели инструментов с помощью Python и PyQt, вы должны следовать некоторым стандартам, которые обычно считаются лучшими практиками в программировании графического интерфейса. Вот краткий список:
- Расставьте меню в общепринятом порядке. Например, если у вас есть меню File, оно должно быть первым слева направо. Если у вас есть меню Edit, то оно должно быть вторым. Help должна быть в самом конце меню и тд.
- Заполните свои меню общими опциями для типа разрабатываемого приложения. Например, в текстовом редакторе меню File обычно включают такие параметры, как Create, Open, Save и Exit. Меню редактирования часто включают такие параметры, как Copy, Paste, Cut, Undo и т.д.
- Для общих параметров используйте стандартные сочетания клавиш. Например, используйте Ctrl+C для копирования, Ctrl+V для вставки, Ctrl+X для вырезания и так далее.
- Используйте разделители для разделения несвязанных опций. Эти визуальные подсказки упростят навигацию по вашему приложению.
- Добавьте многоточие (
...
) к заголовку параметров, запускающих дополнительные диалоги. Например, используйте Save As… вместо Save As, About… вместо About и т.д. - Используйте амперсанды (
&
) в параметрах меню, чтобы обеспечить удобного и быстрого доступа с клавиатуры. Например,"&Open"
вместо"Open"
,"&Exit"
вместо"Exit"
.
Если вы будете следовать этим рекомендациям, ваши приложения с графическим пользовательским интерфейсом будут знакомыми и привлекательными для ваших пользователей.
Создание строк состояния Python в PyQt
Строка состояния представляет собой горизонтальную панель, которая обычно находится в нижней части главного окна в приложении GUI. Его основная цель - отображать информацию о текущем статусе приложения. Строку состояния также можно разделить на разделы, чтобы отображать различную информацию по каждому разделу.
Согласно документации Qt, существует три типа индикаторов состояния:
- Временные индикаторы на короткое время занимают почти всю строку состояния для отображения текстов всплывающих подсказок, пунктов меню и другой информации, зависящей от времени.
- Обычные индикаторы занимают часть строки состояния и отображают информацию, к которой пользователи могут периодически обращаться, например, количество слов в текстовом процессоре. Они могут быть ненадолго скрыты временными индикаторами.
- Постоянные индикаторы всегда отображаются в строке состояния, даже если временный индикатор активирован. Они используются для отображения важной информации о текущем режиме приложения, например, когда была нажата клавиша Caps Lock.
Вы можете добавить строку состояния к своему приложению в стиле главного окна, используя один из следующих вариантов:
- Вызвать
.statusBar()
у объектQMainWindow
..statusBar()
создает и возвращает пустую строку состояния для главного окна. - Создайте объект
QStatusBar
, затем вызовите.setStatusBar()
у главного окна с объектом строки состояния в качестве аргумента. Таким образом, ваш объект строки состояния будет установлен в качестве строки состояния главного окна.
Здесь у вас есть две альтернативные реализации для добавления строки состояния в образец приложения:
# 1. Using .statusBar()
def _createStatusBar(self):
self.statusbar = self.statusBar()
# 2. Using .setStatusBar()
def _createStatusBar(self):
self.statusbar = QStatusBar()
self.setStatusBar(self.statusbar)
Обе реализации дают одинаковый результат. Однако в большинстве случаев вы будете использовать первую реализацию для создания строк состояния. Обратите внимание, что для работы второй реализации вам необходимо выполнить импорт QStatusBar
из PyQt5.QtWidgets
.
Добавьте одну из вышеперечисленных реализаций в свое приложение в Window
, а затем вызовите ._createStatusBar()
в инициализаторе класса. С этими дополнениями, когда вы снова запустите приложение, вы увидите такое окно:
Теперь ваше приложение имеет строку состояния в нижней части главного окна. Строка состояния почти не видна, но если вы присмотритесь, то заметите небольшой пунктирный треугольник в правом нижнем углу окна.
Отображение временных сообщений о состоянии
Основная цель строки состояния - представить информацию о состоянии пользователям вашего приложения. Чтобы отображать временные сообщения о состоянии в строке состояния, вам необходимо использовать QStatusBar.showMessage()
. Этот метод принимает следующие два аргумента:
message
содержит сообщение индикатора состояния в виде строки.timeout
содержит количество миллисекунд, в течение которых сообщение будет отображаться в строке состояния.
Если timeout
равен 0
, что является его значением по умолчанию, то сообщение остается в строке состояния до вызова .clearMessage()
или .showMessage()
в строке состояния.
Если в строке состояния есть активное сообщение и вы вызовете .showMessage()
с новым сообщением, новое сообщение будет скрывать или заменять старое.
Перейдите в свой образец приложения и добавьте следующую строку в ._createStatusBar()
:
class Window(QMainWindow):
# Snip...
def _createStatusBar(self):
self.statusbar = self.statusBar()
# Adding a temporary message
self.statusbar.showMessage("Ready", 3000)
Последняя строка ._createStatusBar()
заставит ваше приложение отображать сообщение Ready
в строке состояния приложения в течение миллисекунд 3000
:
Когда вы запускаете приложение, в строке состояния отображается сообщение Ready
. Через 3000
миллисекунды сообщение исчезает, а строка состояния очищается и готова к отображению нового сообщения о состоянии.
Отображение постоянных сообщений в строке состояния
Вы также можете отображать постоянные сообщения в строке состояния вашего приложения. Постоянное сообщение информирует пользователя об общем состоянии приложения. Например, в текстовом редакторе вы можете отобразить постоянное сообщение с информацией о кодировке текста в текущем открытом файле.
Чтобы добавить постоянные сообщения в строки состояния, вы используете объект QLabel
для хранения сообщения. Затем вы добавляете QLabel
в строку состояния, вызвав .addPermanentWidget()
. Этот метод добавляет данный виджет в текущую строку состояния. Родитель виджета установлен в строку состояния.
.addPermanentWidget()
принимает следующие два аргумента:
widget
содержит объект виджета, который вы хотите добавить в строку состояния. Некоторые часто используемые виджеты:QLabel
,QToolButton
иQProgressBar
.stretch
используется для вычисления подходящего размера для виджета по мере увеличения и уменьшения строки состояния. По умолчанию0
это означает, что виджет будет занимать минимальное количество места.
Имейте в виду, что постоянный виджет не будет скрыт или заменен временными сообщениями. .addPermanentWidget()
находит виджеты в правой части строки состояния.
Допустим, вы хотите превратить образец приложения в текстовый редактор и добавить в строку состояния сообщение, в котором отображается информация о количестве слов в текущем файле. Для этого вы можете создать вызываемый метод .getWordCount()
, а затем добавить постоянное сообщение, используя .addPermanentWidget()
и объект QLabel
:
class Window(QMainWindow):
# Snip...
def getWordCount(self):
# Logic for computing the word count goes here...
return 42
Этот метод добавляет логику для вычисления количества слов в текущем открытом документе. Теперь вы можете отображать эту информацию как постоянное сообщение:
class Window(QMainWindow):
# Snip...
def _createStatusBar(self):
self.statusbar = self.statusBar()
# Adding a temporary message
self.statusbar.showMessage("Ready", 3000)
# Adding a permanent message
self.wcLabel = QLabel(f"{self.getWordCount()} Words")
self.statusbar.addPermanentWidget(self.wcLabel)
В последних двух строках вы сначала создаете объект QLabel
(wcLabel
) для хранения сообщения о количестве слов. Чтобы создать сообщение, вы используете f-строку, в которую вы вставляете вызов .getWordCount()
, чтобы получить информацию о количестве слов. Затем вы добавляете метку в строку состояния с помощью .addPermanentWidget()
.
В этом случае вы создаете объект QLabel
как атрибут экземпляра, потому что количество слов необходимо обновлять в соответствии с изменениями, которые пользователь вносит в текущий файл.
Если вы запустите приложение с этим обновлением, вы увидите сообщение о подсчете слов в правой части строки состояния:
В строке состояния отображается сообщение, информирующее пользователя о количестве слов в гипотетическом текущем файле. Возможность предоставить пользователю постоянную информацию или другие параметры в строке состояния весьма полезна и может помочь вам значительно улучшить взаимодействие с пользователем для ваших приложений.
Добавление подсказок к действиям
Когда дело доходит до создания приложений с графическим пользовательским интерфейсом, важно предлагать вашим пользователям полезные советы о конкретных функциях интерфейса приложения. Подсказки - это короткие сообщения, которые предоставляют пользователю краткое руководство по некоторым параметрам, которые предлагает приложение.
Действия PyQt позволяют определять следующие типы подсказок:
- Подсказки по состоянию - это подсказки, которые приложение показывает в строке состояния, когда пользователь наводит указатель мыши на пункт меню или кнопку панели инструментов. По умолчанию всплывающая подсказка содержит пустую строку.
- Всплывающие подсказки - это подсказки, которые приложение показывает как плавающие сообщения, когда пользователь наводит указатель мыши на кнопку или виджет панели инструментов. По умолчанию всплывающая подсказка содержит текст, определяющий текущее действие.
Чтобы узнать, как работают подсказки, вы можете добавить несколько подсказок по состоянию и всплывающих подсказок в образец приложения. Перейдите к ._createActions()
и добавьте следующие строки кода:
class Window(QMainWindow):
# Snip...
def _createActions(self):
# File actions
# Snip...
self.saveAction.setShortcut("Ctrl+S")
# Adding help tips
newTip = "Create a new file"
self.newAction.setStatusTip(newTip)
self.newAction.setToolTip(newTip)
# Edit actions
self.copyAction = QAction(QIcon(":edit-copy.svg"), "&Copy", self)
# Snip...
Три выделенные строки устанавливают сообщение "Create a new file"
как статус и всплывающую подсказку для параметра New. Если вы запустите приложение сейчас, то увидите, что параметр New показывает пользователю краткую, но информативную подсказку:
Если щелкнуть меню File и удерживать указатель мыши на New, вы увидите сообщение с подсказкой, отображаемое в левой части строки состояния. С другой стороны, если вы наведете указатель мыши на кнопку New на панели инструментов, вы увидите сообщение в строке состояния, а также в виде небольшого плавающего прямоугольника рядом с указателем мыши.
В общем, добавление справочных подсказок в меню и панели инструментов Python считается передовой практикой. Это упростит пользователям навигацию и изучение ваших приложений с графическим интерфейсом.