ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 05.12.2023
Просмотров: 845
Скачиваний: 3
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
СОДЕРЖАНИЕ
328
Часть II. Библиотека PyQt 5 17.2. Первая программа
При изучении языков и технологий принято начинать с программы, выводящей надпись
«Привет, мир!». Не станем нарушать традицию и напишем программу (листинг 17.1), соз- дающую окно с приветствием и кнопкой для закрытия окна (рис. 17.1).
Рис. 17.1. Результат выполнения листинга 17.1
Листинг 17.1. Первая программа на PyQt
# -*- coding: utf-8 -*- from PyQt5 import QtWidgets import sys app = QtWidgets.QApplication(sys.argv) window = QtWidgets.QWidget() window.setWindowTitle("Первая программа на PyQt") window.resize(300, 70) label = QtWidgets.QLabel("
Для создания файла с программой можно по-прежнему пользоваться редактором IDLE.
Однако запуск оконного приложения из IDLE нажатием клавиши
До сих пор мы создавали файлы с расширением py и все результаты выполнения программы выводили в консоль. Оконное приложение также можно сохранить с расширением py
, но тогда при его запуске, помимо основного окна, также будет выведено окно консоли, что, впрочем, на этапе разработки дает возможность вывести отладочную информацию (таким способом мы будем пользоваться в дальнейших примерах). Чтобы избавиться от окна кон- соли, следует сохранять файл с расширением pyw
Попробуйте создать два файла с различными расширениями и запустить двойным щелчком каждый из них.
Глава 17. Знакомство с PyQt 5 329 17.3. Структура PyQt-программы
Запускать программу мы научились. Теперь рассмотрим код из листинга 17.1 построчно.
В первой строке указывается кодировка файла. Поскольку в Python 3 по умолчанию для сохранения исходного кода используется кодировка UTF-8, эту строку можно и не указы- вать. Во второй строке импортируется модуль
QtWidgets
— он содержит классы, реали- зующие компоненты графического интерфейса: окна, надписи, кнопки, текстовые поля и др. В третьей строке импортируется модуль sys
, из которого нам потребуется список па- раметров, переданных в командной строке (
argv
), а также функция exit()
, позволяющая завершить выполнение программы.
Выражение: app = QtWidgets.QApplication(sys.argv) создает объект приложения в виде экземпляра класса
QApplication
. Конструктор этого класса принимает список параметров, переданных в командной строке. Следует помнить, что в программе всегда должен быть объект приложения, причем обязательно только один.
Может показаться, что после создания объекта он в программе больше нигде не использу- ется, однако надо понимать, что с его помощью осуществляется управление приложением незаметно для нас. Получить доступ к этому объекту из любого места в программе можно через атрибут qApp из модуля
QtWidgets
. Например, вывести список параметров, передан- ных в командной строке, можно так: print(QtWidgets.qApp.argv())
Следующее выражение: window = QtWidgets.QWidget() создает объект окна в виде экземпляра класса
QWidget
. Этот класс наследуют практически все классы, реализующие компоненты графического интерфейса. И любой компонент, не имеющий родителя, обладает своим собственным окном.
Выражение: window.setWindowTitle("Первая программа на PyQt") задает текст, который будет выводиться в заголовке окна, для чего используется метод setWindowTitle()
Очередное выражение: window.resize(300, 70) задает минимальные размеры окна. В первом параметре метода resize()
указывается ши- рина окна, а во втором параметре — его высота. При этом надо учитывать, что метод resize()
устанавливает размеры не самого окна, а его клиентской области, при этом разме- ры заголовка и ширина границ окна не учитываются. Также следует помнить, что эти раз- меры являются рекомендацией, — т. е., если компоненты не помещаются в окне, оно будет увеличено.
Выражение: label = QtWidgets.QLabel("
QLabel
. Обратите внимание, что внутри строки мы указали HTML-теги, — а именно: с по- мощью тега
произвели выравнивание текста по центру компонента. Возможность
330
Часть II. Библиотека PyQt 5 использования HTML-тегов и CSS-атрибутов является отличительной чертой библиотеки
PyQt — например, внутри надписи можно вывести таблицу или отобразить изображение.
Это очень удобно.
Следующее выражение: btnQuit = QtWidgets.QPushButton("&Закрыть окно") создает объект кнопки. Текст, который будет отображен на кнопке, задается в качестве па- раметра в конструкторе класса
QPushButton
. Обратите внимание на символ
&
перед буквой
З
— таким образом задаются клавиши быстрого доступа. Если нажать одновременно кла- вишу
&
, то кнопка срабо- тает.
Выражение: vbox = QtWidgets.QVBoxLayout() создает вертикальный контейнер. Все компоненты, добавляемые в этот контейнер, будут располагаться по вертикали сверху вниз в порядке добавления, при этом размеры добавлен- ных компонентов будут подогнаны под размеры контейнера. При изменении размеров кон- тейнера будет произведено изменение размеров всех компонентов.
В следующих двух выражениях: vbox.addWidget(label) vbox.addWidget(btnQuit) с помощью метода addWidget()
производится добавление созданных ранее объектов надпи- си и кнопки в вертикальный контейнер. Так как объект надписи добавляется первым, он будет расположен над кнопкой. При добавлении компонентов в контейнер они автомати- чески становятся потомками контейнера.
Новое выражение: window.setLayout(vbox) добавляет контейнер в основное окно с помощью метода setLayout()
. Таким образом, кон- тейнер становится потомком основного окна.
Выражение: btnQuit.clicked.connect(app.quit) назначает обработчик сигнала clicked()
кнопки, который генерируется при ее нажатии.
Этот сигнал доступен через одноименный атрибут класса кнопки и поддерживает метод connect()
, который и назначает для него обработчик, передаваемый первым параметром.
Обработчик представляет собой метод quit()
объекта приложения, выполняющий немед- ленное завершение его работы. Такой метод принято называть слотом.
П
ОЯСНЕНИЕ
Сигналом в PyQt называется особое уведомление, генерируемое при наступлении какого- либо события в приложении: нажатия кнопки, ввода символа в текстовое поле, закрытия окна и пр.
Очередное выражение: window.show() выводит на экран окно и все компоненты, которые мы ранее в него добавили.
Глава 17. Знакомство с PyQt 5 331
И, наконец, последнее выражение: sys.exit(app.exec_()) запускает бесконечный цикл обработки событий в приложении.
Код, расположенный после вызова метода exec_()
, будет выполнен только после заверше- ния работы приложения, — поскольку результат выполнения метода exec_()
мы передаем функции exit()
, дальнейшее выполнение программы будет прекращено, а код возврата передан операционной системе.
17.4. ООП-стиль создания окна
Библиотека PyQt написана в объектно-ориентированном стиле (ООП-стиле) и содержит несколько сотен классов. Иерархия наследования всех классов имеет слишком большой размер, и приводить ее в книге возможности нет. Тем не менее, чтобы показать зависимо- сти, при описании компонентов иерархия наследования конкретного класса будет показы- ваться. В качестве примера выведем базовые классы класса
QWidget
:
>>> from PyQt5 import QtWidgets
>>> QtWidgets.QWidget.__bases__
(
Как видно из примера, класс
QWidget наследует два класса:
QObject и
QPaintDevice
. Класс
QObject является классом верхнего уровня, и его в PyQt наследуют большинство классов.
В свою очередь, класс
QWidget является базовым классом для всех визуальных компонентов.
В
НИМАНИЕ
!
В описании каждого класса PyQt приводятся лишь атрибуты, методы, сигналы и слоты, оп- ределенные непосредственно в описываемом классе. Атрибуты, методы, сигналы и слоты базовых классов там не описываются — присутствуют лишь ссылки на соответствующие страницы документации.
В своих программах вы можете наследовать стандартные классы и добавлять новую функ- циональность. В качестве примера переделаем соответствующим образом код из листин- га 17.1 и создадим окно в ООП-стиле (листинг 17.2).
Листинг 17.2. ООП-стиль создания окна
# -*- coding: utf-8 -*- from PyQt5 import QtCore, QtWidgets class MyWindow(QtWidgets.QWidget): def __init__(self, parent=None):
QtWidgets.QWidget.__init__(self, parent) self.label = QtWidgets.QLabel("Привет, мир!") self.label.setAlignment(QtCore.Qt.AlignHCenter) self.btnQuit = QtWidgets.QPushButton("&Закрыть окно") self.vbox = QtWidgets.QVBoxLayout() self.vbox.addWidget(self.label) self.vbox.addWidget(self.btnQuit) self.setLayout(self.vbox) self.btnQuit.clicked.connect(QtWidgets.qApp.quit)
332
Часть II. Библиотека PyQt 5 if __name__ == "__main__": import sys app = QtWidgets.QApplication(sys.argv) window = MyWindow() # Создаем экземпляр класса window.setWindowTitle("ООП-стиль создания окна") window.resize(300, 70) window.show() # Отображаем окно sys.exit(app.exec_()) # Запускаем цикл обработки событий
В первых двух строках кода, как обычно, указывается кодировка файла и импортируются необходимые модули. На этот раз, помимо уже знакомого модуля
QtWidgets
, нам понадо- бится модуль
QtCore
, в котором объявлены атрибуты, задающие, в том числе, и режим вы- равнивания текста в объекте надписи.
Далее мы определяем класс
MyWindow
, который наследует класс
QWidget
: class MyWindow(QtWidgets.QWidget):
Можно наследовать и другие классы, являющиеся наследниками
QWidget
, — например,
QFrame
(окно с рамкой) или
QDialog
(диалоговое окно). При наследовании класса
QDialog окно будет выравниваться по центру экрана (или по центру родительского окна) и иметь в заголовке окна только две кнопки: Справка и Закрыть. Кроме того, можно наследовать класс
QMainWindow
, который представляет главное окно приложения с меню, панелями инст- рументов и строкой состояния. Наследование класса
QMainWindow имеет свои отличия, кото- рые мы рассмотрим в главе 27.
Выражение: def __init__(self, parent=None): определяет конструктор класса. В качестве параметров он принимает ссылки на экземпляр класса (
self
) и на родительский компонент (
parent
). Родительский компонент может отсут- ствовать, поэтому в определении конструктора параметру присваивается значение по умол- чанию (
None
). Внутри метода
__init__()
вызывается конструктор базового класса, и ему передается ссылка на родительский компонент:
QtWidgets.QWidget.__init__(self, parent)
Следующие выражения внутри конструктора создают объекты надписи, кнопки и контей- нера, затем добавляют компоненты в контейнер, а сам контейнер — в основное окно. Сле- дует обратить внимание на то, что объекты надписи и кнопки сохраняются в атрибутах экземпляра класса. В дальнейшем из методов класса можно управлять этими объектами — например, изменять текст надписи. Если объекты не сохранить, то получить к ним доступ будет не так просто.
В предыдущем примере (см. листинг 17.1) мы выравнивали надпись с помощью HTML- тегов. Однако выравнивание можно задать и вызовом метода setAlignment()
, которому следует передать атрибут
AlignHCenter из модуля
QtCore
: self.label.setAlignment(QtCore.Qt.AlignHCenter)
В выражении, назначающем обработчик сигнала: self.btnQuit.clicked.connect(QtWidgets.qApp.quit) мы получаем доступ к объекту приложения через рассмотренный ранее атрибут qApp моду- ля
QtWidgets
Глава 17. Знакомство с PyQt 5 333
Создание объекта приложения и экземпляра класса
MyWindow производится внутри условия: if __name__ == "__main__":
Если вы внимательно читали первую часть книги, то уже знаете, что атрибут модуля
__name__
будет содержать значение
__main__
только в случае запуска модуля как главной программы. Если модуль импортировать, этот атрибут будет содержать другое значение.
Поэтому весь последующий код создания объекта приложения и объекта окна выполняется только при запуске программы двойным щелчком на значке файла. Может возникнуть во- прос, зачем это нужно? Дело в том, что одним из преимуществ ООП-стиля программирова- ния является повторное использование кода. Следовательно, можно импортировать модуль и использовать класс
MyWindow в другом приложении.
Рассмотрим эту возможность на примере, для чего сохраним код из листинга 17.2 в файле с именем
MyWindow.py
, а затем создадим в той же папке еще один файл (например, с име- нем test.pyw
) и вставим в него код из листинга 17.3.
Листинг 17.3. Повторное использование кода при ООП-стиле
# -*- coding: utf-8 -*- from PyQt5 import QtCore, QtWidgets import MyWindow class MyDialog(QtWidgets.QDialog): def __init__(self, parent=None):
QtWidgets.QDialog.__init__(self, parent) self.myWidget = MyWindow.MyWindow() self.myWidget.vbox.setContentsMargins(0, 0, 0, 0) self.button = QtWidgets.QPushButton("&Изменить надпись") mainBox = QtWidgets.QVBoxLayout() mainBox.addWidget(self.myWidget) mainBox.addWidget(self.button) self.setLayout(mainBox) self.button.clicked.connect(self.on_clicked) def on_clicked(self): self.myWidget.label.setText("Новая надпись") self.button.setDisabled(True) if __name__ == "__main__": import sys app = QtWidgets.QApplication(sys.argv) window = MyDialog() window.setWindowTitle("Преимущество ООП-стиля") window.resize(300, 100) window.show() sys.exit(app.exec_())
Теперь запустим файл test.pyw двойным щелчком — на экране откроется окно с надписью и двумя кнопками (рис. 17.2). По нажатию на кнопку Изменить надпись производится изме-
334
Часть II. Библиотека PyQt 5
Рис. 17.2. Результат выполнения листинга 17.3 нение текста надписи, и кнопка делается неактивной. Нажатие кнопки Закрыть окно будет по-прежнему завершать выполнение приложения.
В этом примере мы создали класс
MyDialog
, который наследует класс
QDialog
. Поэтому при выводе окно автоматически выравнивается по центру экрана, а в заголовке окна выводятся только две кнопки: Справка и Закрыть. Внутри конструктора мы создаем экземпляр клас- са
MyWindow и сохраняем его в атрибуте myWidget
: self.myWidget = MyWindow.MyWindow()
С его помощью позже мы получим доступ ко всем атрибутам класса
MyWindow
. Например, в следующей строке произведем изменение отступа между границами контейнера и грани- цами соседних элементов: self.myWidget.vbox.setContentsMargins(0, 0, 0, 0)
В следующих инструкциях внутри конструктора создаются кнопки и контейнер, затем экземпляр класса
MyWindow и кнопка добавляются в контейнер, а сам контейнер помещается в основное окно.
Выражение: self.button.clicked.connect(self.on_clicked) назначает обработчик нажатия кнопки. В качестве параметра указывается ссылка на метод on_clicked()
, внутри которого производится изменение текста надписи (с помощью метода setText()
), и кнопка делается неактивной (с помощью метода setDisabled()
). Внутри ме- тода on_clicked()
доступен указатель self
, через который можно получить доступ к атри- бутам классов
MyDialog и
MyWindow
Вот так и производится повторное использование ранее написанного кода: мы создаем класс и сохраняем его внутри отдельного модуля, а чтобы протестировать модуль или ис- пользовать его как отдельное приложение, размещаем код создания объекта приложения и объекта окна внутри условия: if __name__ == "__main__":
Тогда при запуске с помощью двойного щелчка на значке файла производится выполнение кода как отдельного приложения. Если модуль импортируется, то создание объекта прило- жения не производится, и мы можем использовать класс в других приложениях. Например, так, как это было сделано в листинге 17.3, или путем наследования класса и добавления или переопределения методов.
В некоторых случаях использование ООП-стиля является обязательным. Например, чтобы обработать нажатие клавиши на клавиатуре, необходимо наследовать какой-либо класс и переопределить в нем метод с предопределенным названием. Какие методы необходимо переопределять, мы рассмотрим при изучении обработки событий.
Глава 17. Знакомство с PyQt 5 335 17.5. Создание окна с помощью программы Qt Designer
Если вы ранее пользовались Visual Studio или Delphi, то вспомните, как с помощью мыши размещали на форме компоненты: щелкали левой кнопкой мыши на нужной кнопке в пане- ли инструментов и перетаскивали компонент на форму, затем с помощью инспектора свойств производили настройку значений некоторых свойств, а остальные свойства получа- ли значения по умолчанию. При этом весь код генерировался автоматически. Произвести аналогичную операцию в PyQt позволяет программа Qt Designer, которая входит в состав этой библиотеки.
К огромному сожалению, в составе последних версий PyQt эта полезная программа отсут- ствует. Однако ее можно установить отдельно в составе программного пакета PyQt 5 Tools, отдав в командной строке команду: pip3 install pyqt5-tools
17.5.1. Создание формы
Запустить Qt Designer можно щелчком на исполняемом файле designer.exe
, который рас- полагается по пути
<путь, по которому установлен Python>\Lib\site-packages\pyqt5-tools
К сожалению, через меню Пуск это сделать не получится.
В окне New Form открывшегося окна (рис. 17.3) выбираем пункт Widget и нажимаем кноп- ку Create — откроется окно с пустой формой, на которую с помощью мыши можно пере- таскивать компоненты из панели Widget Box.
Рис. 17.3. Программа Qt Designer
336
Часть II. Библиотека PyQt 5
В качестве примера добавим на форму надпись и кнопку. Для этого на панели Widget Box в группе Display Widgets щелкнем левой кнопкой мыши на пункте Label и, не отпуская кнопку мыши, перетащим компонент на форму. Затем проделаем аналогичную операцию с компонентом Push Button, находящимся в группе Buttons, и разместим его ниже надписи.
Теперь выделим одновременно надпись и кнопку, щелкнем правой кнопкой мыши на лю- бом компоненте и в контекстном меню выберем пункт Lay out | Lay Out Vertically. Чтобы компоненты занимали всю область формы, щелкнем правой кнопкой мыши на свободном месте формы и в контекстном меню выберем пункт Lay out | Lay Out Horizontally.
Теперь изменим некоторые свойства окна. Для этого в панели Object Inspector (рис. 17.4) выделим первый пункт (Form), перейдем в панель Property Editor, найдем свойство objectName и справа от свойства введем значение
MyForm
. Затем найдем свойство geometry, щелкнем мышью на значке уголка слева, чтобы отобразить скрытые свойства, и зададим ширину равной
300
, а высоту равной
70
(рис. 17.5), — размеры формы автоматически изме- нятся. Указать текст, который будет отображаться в заголовке окна, позволяет свойство windowTitle.
Чтобы изменить свойства надписи, следует выделить компонент с помощью мыши или вы- брать соответствующий ему пункт в панели Object Inspector. Для примера изменим значе- ние свойства text (оно задает текст надписи). После чего найдем свойство alignment, щелк- нем мышью на значке уголка слева, чтобы отобразить скрытые свойства, и укажем для свойства Horizontal значение
AlignHCenter
. Теперь выделим кнопку и изменим значение свойства objectName на btnQuit
, а в свойстве text укажем текст надписи, которая будет вы- водиться на кнопке. (Кстати, изменить текст надписи или кнопки также можно, выполнив двойной щелчок мышью на компоненте.)
Рис. 17.4. Панель Object Inspector
Рис. 17.5. Панель Property Editor