Главная страница

ээдд. Прохоренок_Н_А__Дронов_В_А_Python_3_и_PyQt_5_Разработка_приложен. Николай Прохоренок Владимир Дронов


Скачать 7.92 Mb.
НазваниеНиколай Прохоренок Владимир Дронов
Дата05.05.2023
Размер7.92 Mb.
Формат файлаpdf
Имя файлаПрохоренок_Н_А__Дронов_В_А_Python_3_и_PyQt_5_Разработка_приложен.pdf
ТипДокументы
#1111379
страница32 из 83
1   ...   28   29   30   31   32   33   34   35   ...   83
[, <Экземпляр класса>])
Если второй параметр не указан, функция возвращает ссылку на объект формы. С помощью этой ссылки можно получить доступ к компонентам формы и, например, назначить об- работчики сигналов (листинг 17.4). Имена компонентов задаются в программе Qt Designer в свойстве objectName.
Листинг 17.4. Использование функции loadUi(). Вариант 1
# -*- coding: utf-8 -*- from PyQt5 import QtWidgets, uic import sys app = QtWidgets.QApplication(sys.argv) window = uic.loadUi("MyForm.ui") window.btnQuit.clicked.connect(app.quit) window.show() sys.exit(app.exec_())
Если во втором параметре указать ссылку на экземпляр класса, то все компоненты формы будут доступны через указатель self
(листинг 17.5).
Листинг 17.5. Использование функции loadUi(). Вариант 2
# -*- coding: utf-8 -*- from PyQt5 import QtWidgets, uic class MyWindow(QtWidgets.QWidget): def __init__(self, parent=None):
QtWidgets.QWidget.__init__(self, parent) uic.loadUi("MyForm.ui", self) self.btnQuit.clicked.connect(QtWidgets.qApp.quit) if __name__ == "__main__": import sys app = QtWidgets.QApplication(sys.argv) window = MyWindow()

338
Часть II. Библиотека PyQt 5 window.show() sys.exit(app.exec_())
Загрузить UI-файл позволяет также функция loadUiType()
— она возвращает кортеж из двух элементов: ссылки на класс формы и ссылки на базовый класс. Так как функция воз- вращает ссылку на класс, а не на экземпляр класса, мы можем создать множество экземпля- ров класса. После создания экземпляра класса формы необходимо вызвать метод setupUi()
и передать ему указатель self
(листинг 17.6).
Листинг 17.6. Использование функции loadUiType(). Вариант 1
# -*- coding: utf-8 -*- from PyQt5 import QtWidgets, uic class MyWindow(QtWidgets.QWidget): def __init__(self, parent=None):
QtWidgets.QWidget.__init__(self, parent)
Form, Base = uic.loadUiType("MyForm.ui") self.ui = Form() self.ui.setupUi(self) self.ui.btnQuit.clicked.connect(QtWidgets.qApp.quit) if __name__ == "__main__": import sys app = QtWidgets.QApplication(sys.argv) window = MyWindow() window.show() sys.exit(app.exec_())
Загрузить UI-файл можно и вне класса, после чего указать класс формы во втором парамет- ре в списке наследования, — в этом случае наш класс унаследует все методы класса формы
(листинг 17.7).
Листинг 17.7. Использование функции loadUiType(). Вариант 2 from PyQt5 import QtWidgets, uic
Form, Base = uic.loadUiType("MyForm.ui") class MyWindow(QtWidgets.QWidget, Form): def __init__(self, parent=None):
QtWidgets.QWidget.__init__(self, parent) self.setupUi(self) self.btnQuit.clicked.connect(QtWidgets.qApp.quit) if __name__ == "__main__": import sys app = QtWidgets.QApplication(sys.argv) window = MyWindow() window.show() sys.exit(app.exec_())

Глава 17. Знакомство с PyQt 5 339 17.5.3. Преобразование UI-файла в PY-файл
Вместо подключения UI-файла можно сгенерировать на его основе программный код на языке Python. Для этого служит утилита pyuic5
, чей исполняемый файл располагается в каталоге
<путь, по которому установлен Python>\Scripts
. Запустим командную строку и перейдем в каталог, в котором находится UI-файл. Для генерации Python-программы выпол- ним команду: pyuic5 MyForm.ui -o ui_MyForm.py
В результате будет создан файл ui_MyForm.py
, который мы уже можем подключить с по- мощью инструкции import
. Внутри файла находится класс
Ui_MyForm с методами setupUi()
и retranslateUi()
. При использовании процедурного стиля программирования следует соз- дать экземпляр класса формы, а затем вызвать метод setupUi()
и передать ему ссылку на экземпляр окна (листинг 17.8).
Листинг 17.8. Использование класса формы. Вариант 1
# -*- coding: utf-8 -*- from PyQt5 import QtWidgets import sys, ui_MyForm app = QtWidgets.QApplication(sys.argv) window = QtWidgets.QWidget() ui = ui_MyForm.Ui_MyForm() ui.setupUi(window) ui.btnQuit.clicked.connect(QtWidgets.qApp.quit) window.show() sys.exit(app.exec_())
При использовании ООП-стиля программирования следует создать экземпляр класса фор- мы, а затем вызвать метод setupUi()
и передать ему указатель self
(листинг 17.9).
Листинг 17.9. Использование класса формы. Вариант 2
# -*- coding: utf-8 -*- from PyQt5 import QtWidgets import ui_MyForm class MyWindow(QtWidgets.QWidget): def __init__(self, parent=None):
QtWidgets.QWidget.__init__(self, parent) self.ui = ui_MyForm.Ui_MyForm() self.ui.setupUi(self) self.ui.btnQuit.clicked.connect(QtWidgets.qApp.quit) if __name__ == "__main__": import sys app = QtWidgets.QApplication(sys.argv) window = MyWindow() window.show() sys.exit(app.exec_())

340
Часть II. Библиотека PyQt 5
Класс формы можно указать во втором параметре в списке наследования — в этом случае он унаследует все методы класса формы (листинг 17.10).
Листинг 17.10. Использование класса формы. Вариант 3 from PyQt5 import QtWidgets import ui_MyForm class MyWindow(QtWidgets.QWidget, ui_MyForm.Ui_MyForm): def __init__(self, parent=None):
QtWidgets.QWidget.__init__(self, parent) self.setupUi(self) self.btnQuit.clicked.connect(QtWidgets.qApp.quit) if __name__ == "__main__": import sys app = QtWidgets.QApplication(sys.argv) window = MyWindow() window.show() sys.exit(app.exec_())
Как видите, в PyQt можно создавать формы, размещать компоненты с помощью мыши, а затем непосредственно подключать UI-файлы в программе или преобразовывать их в Python-код с помощью утилиты pyuic5
, — все это очень удобно. Тем не менее, чтобы пол- ностью овладеть программированием на PyQt, необходимо уметь создавать код вручную.
Поэтому в оставшейся части книги мы больше не станем задействовать программу Qt
Designer.
17.6. Модули PyQt 5
В состав библиотеки PyQt 5 входит множество модулей, объединенных в пакет
PyQt5
. Упо- мянем самые важные из них:

QtCore
— содержит классы, не связанные с реализацией графического интерфейса. От этого модуля зависят все остальные модули;

QtGui
— содержит классы, реализующие низкоуровневую работу с оконными элемента- ми, обработку сигналов, вывод двухмерной графики и текста и др.;

QtWidgets
— содержит классы, реализующие компоненты графического интерфейса: окна, диалоговые окна, надписи, кнопки, текстовые поля и др.;

QtWebEngineCore
— включает низкоуровневые классы для отображения веб-страниц;

QtWebEngineWidgets
— реализует высокоуровневые компоненты графического интер- фейса, предназначенные для вывода веб-страниц и использующие модуль
QtWebEngineCore
;
П
РИМЕЧАНИЕ
Ранее для вывода веб-страниц использовались модули QtWebKit и QtWebKitWidgets.
Однако в версии PyQt 5.5 они были объявлены нерекомендованными для использования, а в версии 5.6 удалены.

QtMultimedia
— включает низкоуровневые классы для работы с мультимедиа;

Глава 17. Знакомство с PyQt 5 341

QtMultimediaWidgets
— реализует высокоуровневые компоненты графического интер- фейса с мультимедиа, использующие модуль
QtMultimedia
;

QtPrintSupport
— содержит классы, обеспечивающие поддержку печати и предвари- тельного просмотра документов;

QtSql
— включает поддержку работы с базами данных, а также реализацию SQLite;

QtSvg
— позволяет работать с векторной графикой (SVG);

QtNetwork
— содержит классы, предназначенные для работы с сетью;

QtXml и
QtXmlPatterns
— предназначены для обработки XML;

QtHelp
— содержат инструменты для создания интерактивных справочных систем;

QtWinExtras
— включает поддержку специфических возможностей Microsoft Windows;

Qt
— включает классы из всех модулей сразу.
П
РИМЕЧАНИЕ
Модуль QtOpenGL, обеспечивающий поддержку OpenGL, в версии PyQt 5.9 был объявлен нерекомендованным к использованию и будет удален в одной из последующих версий этой библиотеки. Его функциональность перенесена в модуль QtGui.
Для подключения модулей используется следующий синтаксис: from PyQt5 import <Названия модулей через запятую>
Так, например, можно подключить модули
QtCore и
QtWidgets
: from PyQt5 import QtCore, QtWidgets
В этой книге мы не станем рассматривать все упомянутые модули — чтобы получить ин- формацию по не рассмотренным здесь модулям, обращайтесь к соответствующей докумен- тации.
17.7. Типы данных в PyQt
Библиотека PyQt является надстройкой над написанной на языке C++ библиотекой Qt. По- следняя содержит множество классов, которые расширяют стандартные типы данных языка
C++ и реализуют динамические массивы, ассоциативные массивы, множества и др. Все эти классы очень помогают при программировании на языке C++, но для языка Python они не представляют особого интереса, т. к. весь этот функционал содержат стандартные типы данных. Тем не менее, при чтении документации вы столкнетесь с ними, поэтому сейчас мы кратко рассмотрим основные типы:

QByteArray
— массив байтов. Преобразуется в тип bytes
:
>>> from PyQt5 import QtCore
>>> arr = QtCore.QByteArray(bytes("str", "cp1251"))
>>> arr
PyQt5.QtCore.QByteArray(b'str')
>>> bytes(arr) b'str'

QVariant
— может хранить данные любого типа. Создать экземпляр этого класса можно вызовом конструктора, передав ему нужное значение. А чтобы преобразовать данные, хранящиеся в экземпляре класса
QVariant
, в тип данных Python, нужно вызвать метод value()
:

342
Часть II. Библиотека PyQt 5
>>> from PyQt5 import QtCore
>>> n = QtCore.QVariant(10)
>>> n
>>> n.value()
10
Также можно создать «пустой» экземпляр класса
QVariant
, вызвав конструктор без па- раметров:
>>> QtCore.QVariant() # Пустой объект
Если какой-либо метод ожидает данные типа
QVariant
, ему можно передать данные лю- бого типа.
Еще этот класс поддерживает метод typeName()
, возвращающий наименование типа хра- нящихся в экземпляре данных:
>>> from PyQt5 import QtCore
>>> n = QtCore.QVariant(10)
>>> n.typeName()
'int'
Кроме того, PyQt 5 поддерживает классы
QDate
(значение даты),
QTime
(значение времени),
QDateTime
(значение даты и времени),
QTextStream
(текстовый поток),
QUrl
(URL-адрес) и некоторые другие.
17.8. Управление основным циклом приложения
Для взаимодействия с системой и обработки возникающих сигналов предназначен основной цикл приложения. После вызова метода exec_()
программа переходит в бесконечный цикл.
Инструкции, расположенные после вызова этого метода, будут выполнены только после завершения работы всего приложения. Цикл автоматически прерывается после закры- тия последнего открытого окна приложения. С помощью статического метода setQuitOnLastWindowClosed()
класса
QApplication это поведение можно изменить.
Чтобы завершить работу приложения, необходимо вызвать слот quit()
или метод exit([returnCode=0])
класса
QApplication
. Поскольку программа находится внутри цикла, вызвать эти методы можно лишь при наступлении какого-либо события, — например, при нажатии пользователем кнопки.
После возникновения любого сигнала основной цикл прерывается, и управление передается в обработчик этого сигнала. После завершения работы обработчика управление возвраща- ется основному циклу приложения.
Если внутри обработчика выполняется длительная операция, программа перестает реагиро- вать на события. В качестве примера изобразим длительный процесс с помощью функции sleep()
из модуля time
(листинг 17.11).
Листинг 17.11. Выполнение длительной операции
# -*- coding: utf-8 -*- from PyQt5 import QtWidgets import sys, time

Глава 17. Знакомство с PyQt 5 343 def on_clicked(): time.sleep(10) # "Засыпаем" на 10 секунд app = QtWidgets.QApplication(sys.argv) button = QtWidgets.QPushButton("Запустить процесс") button.resize(200, 40) button.clicked.connect(on_clicked) button.show() sys.exit(app.exec_())
В этом примере при нажатии кнопки Запустить процесс вызывается функция on_clicked()
, внутри которой мы приостанавливаем выполнение программы на десять секунд и тем са- мым прерываем основной цикл. Попробуйте нажать кнопку, перекрыть окно другим окном, а затем заново его отобразить, — вам не удастся это сделать, поскольку окно перестает реа- гировать на любые события, пока не закончится выполнение процесса. Короче говоря, про- грамма просто зависнет.
Длительную операцию можно разбить на несколько этапов и по завершении каждого этапа выходить в основной цикл с помощью статического метода processEvents([flags=AllEvents])
класса
QCoreApplication
, от которого наследуется класс
QApplication
. Переделаем преды- дущую программу, инсценировав с помощью цикла длительную операцию, которая выпол- няется 20 секунд (листинг 17.12).
Листинг 17.12. Использование метода processEvents()
# -*- coding: utf-8 -*- from PyQt5 import QtWidgets import sys, time def on_clicked(): button.setDisabled(True) # Делаем кнопку неактивной for i in range(1, 21):
QtWidgets.qApp.processEvents() # Запускаем оборот цикла time.sleep(1) # "Засыпаем" на 1 секунду print("step -", i) button.setDisabled(False) # Делаем кнопку активной app = QtWidgets.QApplication(sys.argv) button = QtWidgets.QPushButton("Запустить процесс") button.resize(200, 40) button.clicked.connect(on_clicked) button.show() sys.exit(app.exec_())
В этом примере длительная операция разбита на одинаковые этапы, после выполнения каж- дого из которых выполняется выход в основной цикл приложения. Теперь при перекрытии окна и повторном его отображении оно будет перерисовано — таким образом, приложение по-прежнему будет взаимодействовать с системой, хотя и с некоторой задержкой.

344
Часть II. Библиотека PyQt 5 17.9. Многопоточные приложения
При обработке больших объемов данных не всегда можно равномерно разбить операцию на небольшие по времени этапы, поэтому при использовании метода processEvents()
возмож- ны проблемы, и тогда имеет смысл вынести длительную операцию в отдельный поток, — в этом случае операция станет выполняться параллельно с основным циклом приложения и не будет его блокировать.
В одном процессе можно запустить сразу несколько независимых потоков, и если ваш ком- пьютер оснащен многоядерным процессором, потоки будут равномерно распределены по его ядрам. За счет этого можно не только избежать блокировки GUI-потока приложения, в котором выполняется обновление его интерфейса, но и значительно увеличить эффектив- ность выполнения кода. Завершение основного цикла приложения приводит к завершению работы всех потоков.
17.9.1. Класс QThread: создание потока
Для создания потока в PyQt предназначен класс
QThread
, который объявлен в модуле
QtCore и наследует класс
QObject
. Конструктор класса
QThread имеет следующий формат:
<Объект> = QThread([parent=None])
Чтобы использовать потоки, следует создать класс, который будет наследником класса
QThread
, и определить в нем метод run()
. Код, расположенный в методе run()
, будет вы- полняться в отдельном потоке, а после завершения выполнения метода run()
этот поток прекратит свое существование. Затем нужно создать экземпляр класса и вызвать метод start()
, который после запуска потока вызовет метод run()
. Обратите внимание, что если напрямую вызвать метод run()
, то код станет выполняться в основном, а не в отдельном потоке. Метод start()
имеет следующий формат: start([priority=QThread.InheritPriority])
Параметр priority задает приоритет выполнения потока по отношению к другим потокам.
Следует учитывать, что при наличии потока с самым высоким приоритетом поток с самым низким приоритетом в некоторых операционных системах может быть просто проигнори- рован. Приведем допустимые значения параметра (в порядке увеличения приоритета) и со- ответствующие им атрибуты из класса
QThread
:

0

IdlePriority
— самый низкий приоритет;

1

LowestPriority
;

2

LowPriority
;

3

NormalPriority
;

4

HighPriority
;

5

HighestPriority
;

6

TimeCriticalPriority
самый высокий приоритет;

7

InheritPriority
— автоматический выбор приоритета (значение по умолчанию).
Задать приоритет потока также позволяет метод setPriority(<Приоритет>)
. Узнать, какой приоритет использует запущенный поток, можно с помощью метода priority()
После запуска потока генерируется сигнал started()
, а после завершения — сигнал finished()
. Назначив обработчики этим сигналам, можно контролировать статус потока из

Глава 17. Знакомство с PyQt 5 345 основного цикла приложения. Если необходимо узнать текущий статус, следует воспользо- ваться методами isRunning() и isFinished()
: метод isRunning()
возвращает значение
True
, если поток выполняется, а метод isFinished()
— значение
True
, если поток закончил выполнение.
Потоки выполняются внутри одного процесса и имеют доступ ко всем глобальным пере- менным. Однако следует учитывать, что из потока нельзя изменять что-либо в GUI-потоке приложения, — например, выводить текст на надпись. Для изменения данных в GUI-потоке нужно использовать сигналы. Внутри потока у нужного сигнала вызывается метод emit()
, который, собственно, и выполняет его генерацию. В параметрах метода emit()
можно ука- зать данные, которые будут переданы обработчику сигнала. А внутри GUI-потока назна- чаем обработчик этого сигнала и в обработчике пишем код, который и будет обновлять ин- терфейс приложения.
Рассмотрим использование класса
QThread на примере (листинг 17.13).
Листинг 17.13. Использование класса QThread
# -*- coding: utf-8 -*- from PyQt5 import QtCore, QtWidgets class MyThread(QtCore.QThread): mysignal = QtCore.pyqtSignal(str) def __init__(self, parent=None):
QtCore.QThread.__init__(self, parent) def run(self): for i in range(1, 21): self.sleep(3) # "Засыпаем" на 3 секунды
# Передача данных из потока через сигнал self.mysignal.emit("i = %s" % i) class MyWindow(QtWidgets.QWidget): def __init__(self, parent=None):
1   ...   28   29   30   31   32   33   34   35   ...   83


написать администратору сайта