Python 3 и PyQt 6 PDF
Python 3 и PyQt 6 PDF
Владимир Дронов
Python 3
и PyQt &
Разработка
nриnожений
Санкт-Петербург
« БХВ-Петербург»
2023
УДК 004.43
ББК 32.973.26-018.1
П84
Прохоренок, Н. А.
П84 Python 3 и PyQt 6. Разработка приложений/ Н. А. Прохоренок,
В. А. Дронов. - СПб.: Б:ХВ-Петербурr, 2023. - 832 с.: ил. -
(Профессиональное программирование)
ISBN 978-5-9775-1706-5
Описан язык Python 3: типы данных, операторы, условия ветвления и выбора,
циклы, регулярные выражения, функции, классы, работа с файлами и каталогами,
взаимодействие с механизмами Windows, часто используемые модули стандартной
библиотеки. Особое внимание уделено библиотеке PyQt, позволяющей создавать
приложения с графическим интерфейсом. Описаны средства для создания и выво
да окон, основных компонентов (кнопок, полей, списков, таблиц, меню, панелей
инструментов и др.). Р!iССмотрена обработка событий и сигналов, разработка мно
гопоточных программ, работа с базами данных, вывод графики, воспроизведение
мультимедиа, запись аудио, видео и фото, печать документов, экспорт их в формат
Adobe PDF и сохранения настроек программ. Дан пример полнофункционального
приложения для создания и решения головоломок судоку. На сайте издательства
размещен электронный архив со всеми примерами из книги.
Для программистов
УДК 004.43
ББК 32.973.26-018.1
Предисловие ................................................................................................................... 15
Python ..............................................................................................................................................15
PyQt .................................................................................................................................................. 16
Использованное ПО.......................................................................................................................16
Типографские соглашения ............................................................................................................16
Python
Python - зто высокоуровневый, объектно-ориентирова1ПП,IЙ, тьюринг-полный, интерпре
тируемый язык программирования, предназначенный для решения самого широкого круга
задач. С его помощью можно обрабатывать числовую и текстовую информацию, создавать
изображения, работать с базами данных, разрабатывать веб-сайты и оконные программы.
Python - язык кросс-платформенный, он позволяет создавать программы, которые будут
работать во всех операционных системах.
Согласно официальной версии, название языка произошло вовсе не от змеи. Создатель язы
ка, Гвидо ван Россум (Guido van Rossum), назвал свое творение в честь британского коме
дийного телешоу Би-би-си «Летающий цирк Монти Пайтона» (Monty Python's Flying
Circus). Поэтому правильно название этого замечательного языка должно звучать как «Пай
тон».
Программа на языке Python представляет собой обычный текстовый файл с расширением ру
(консольная программа) или pyw (программа с графическим интерфейсом). Все инструкции
из этого файла выполняются интерпретатором построчно. Для ускорения работы при пер
вом импорте модуля создается промежуточный байт-код, который сохраняется в оператив
ной памяти или одноименном файле с расширением рус. Для выполнения низкоуровневых
операций и задач, требующих высокой скорости работы, можно написать модуль на языке
С или С++, скомпилировать его, а затем подключить к программе.
Поскольку Python, как было только что отмечено, является языком объектно-ориенти
рованным, практически все данные в нем представляются объектами - даже значения, от
носящиеся к элементарным типам данных, наподобие чисел и строк, а также сами типы
данных. При этом в переменной всегда сохраняется только ссылка на объект, а не сам объ
ект. Например, можfю создать функцию, сохранить ссылку на нее в переменной, а затем
вызвать функцию через эту переменную. Такое обстоятельство делает язык Python идеаль
ным инструментом для создания программ, использующих функции обратного вызова, -
например, при разработке графического интерфейса.
Python - самый стильный язык программирования в мире, он не допускает двоякого написа
ния кода. В Python отсутствуют лишние языковые конструкции, и код можно написать только
одним способом. Все программисты, работающие с языком Python, должны придерживаться
стандарта оформления программного кода, описанного в документе РЕР-8 (bttps://peps.
pytbon.org/pep-0008/). Соответственно, более читаемого кода нет ни в одном ином языке про
граммирования.
16 Предисловие
Поскольку программа на языке Python представляет собой текстовый файл, Python-кoд
можно писать в любом текстовом редакторе (например, Блокноте, поставляемом в составе
Windows). Однако лучше использовать какой-либо редактор, специально предназначенный
для программистов: Visual Studio Code (https://code.visualstudio.com/), PyCharm (https://
www.jetbrains.com/ru-ru/pycharm/), Atom (https://atom.io/), SuЬlime Text (https://www.
suЫimetext.com/), Brackets (https://Ьrackets.io/), Notepad++ (https://notepad-plus-plus.org/)
и др. Мы же в процессе изучения материала этой книги будем пользоваться утилитой IDLE,
которая позволяет исполнять инструкции языка интерактивно и входит в сосщв стандарт
ной поставки Python.
PyQt
Библиотека PyQt служит для создания кросс-платформенных оконных программ с графиче
ским интерфейсом. Библиотека очень проста в использовании и идеально подходит для раз
работки весьма серьезных программ. Пользуясь исключительно ее средствами, мы можем
выводить на экран графику практически любой сложности, работать с базами данньIХ наи
более распространенньrх форматов, воспроизводить мультимедийные файлы, записывать
звук и видео, делать фото, выводить документьr на печать и экспортировать их в популяр
ный формат Adobe PDF. Библиотека PyQt основана на популярном фреймворке Qt и служит
своего рода связкой между ним и Python.
В самом конце книги мы с вами самостоятельно напишем на языке Python с применением
библиотеки PyQt полнофункциональную программу «Судоку», предназначенную для соз
дания и решения одноименньIХ головоломок.
Использованное ПО
Авторы применяли при работе над книгой следующее ПО:
♦ Microsoft Windows 10, русская 64-разрядная редакция со всеми установленными обнов-
лениями;
♦ Python - 3.10.1, 64-разрядная редакция;
♦ winpath - 202002.24
♦ pyshortcuts - 1.8.1;
♦ PyQt- 6.2.3;
♦ Qt Designer- 5.11.1;
♦ PyQt6-WebEngine - 6.2.1.
Типографские соглашения
В книге будут часто приводиться форматы написания различных языковых конструкций,
применяемьIХ в Python. В них использованы особые типографские соглашения, приведен
ные далее.
♦ Программный код набирается моноширинным шрифтом. Например:
print ("Привет, мир! ")
input ()
Предисловие 17
Первые шаги
Прежде чем мы начнем знакомство с языком Python, хочется отметить, что книги по про
граммированию нужно не только читать, - весьма желательно выполнять все имеющиеся
в них примеры, а также экспериментировать, что-нибудь в этих примерах изменяя. Поэто
му, если вы удобно устроились на диване и настроились просто читать, у вас практически
нет ш�нсов изучить язык. Чем больше вы будете делать самостоятельно, тем большему на
учитесь.
ВНИМАНИЕ!
Текущая версия Python поддерживает только Microsoft Windows версий 8.1, 1 О и 11. Более
старые версии этой операционной системы не поддерживаются.
➔ !nstall Now
C:\Users\vl11d\AppD11t11\Local\Programs\Python\Python310
1➔ Cystomize insta11ation
Choose focotion and features
i
python
Б21 lnstall !auncher for all users (recommended)
python
fot ,,
windows
Рис. 1.2. Установка Python: шаг 2
4. На следующем шаге (рис. 1.3) задаем дополнительные настройки и выбираем путь уста
новки. Проверяем; установлены ли флажки Associate files with Python (requires the ру
launcher) (Ассоциировать Руthоn-файлы с исполняющей средой), Create shortcuts for
Глава 1. Первые шаги 23
Advanced Options
0 lnstall for ан users
0 Associate filt!s with Python (requires the ру launcher)
0 Create shortOJts for installed applications
0 Add Python to tnvironment variables
0 frecompile standard liЬrary
О Download deЬugging aymЬols
О Download deЬug Ьinaries (requires VS 2017 or later)
С помощью этой редакции можно выполнять и око1rnые программы, однако в этом слу
чае на экране появится окно консоли, что может обескуражить пользователя;
♦ pythonw.exe - служит для исполнения оконных программ и задействуется при щелчке
мышью на файле с расширением pyw. Консоль при этом не вьmодится.
See what's new in this release. or find more info about ш1щ1
Python on Windows.
python
windows
Рис. 1.4. Установка Python: шаг 4
ff' IDLEShellЗ.10.1 □ х
Eile .Edit Sl1e!I !2ebug Qptions 'l\lindow t:!elp
jPython 3.10.1 {tags/v3.10.1:2cd268a, Dec 6 2021, 19:10:37) [MSC v.1929 64 bit "'
i{AМD64}] on win32
;Туре "help", "copyright", "credits" or "license{)" for more iпformation.
»>!2 + 2
!4
>»il
у
Ln: 5 -Col: О
Отметим, что в окне ШLЕ Shell приглашение»> отображается на свободной полосе, рас
положенной левее области редактирования, в которой вводятся инструкции Python и выво
дятся результаты. Поэтому при копировании кода в буфер обмена комбинацией клавиш
<Ctrl>+<C> приглашение в него не попадает.
Кроме того, IDLE позволяет работать с Руthоn-программами, хранящимися в файлах. Со
держимое _таких файлов выводится в отдельных окнах. Более подробно функциональные
возможности этой утилиты будут рассмотрены позже.
ВНИМАНИЕ!
В дальнейшем для написания и выполнения Руthоп-программ мы будем использовать ути-
литу IDLE.
ВНИМАНИЕ!
В дальнейшем в подобных фрагментах кода инструкции, вводимые вручную, будут поме
чаться расположенными левее символами приглашения >>>, а результаты, выведенные
интерпретатором, показываться без этих символов.
Для создания файла с Руthоn-программой в ме_ню File окна ШLЕ Shell выбираем пункт New
File или нажимаем комбинацию клавиш <Ctrl>+<N>. В открывшемся окне набираем код из
листинга 1.1, а затем сохраняем его в каком-либо каталоге в файле с именем hello_world.py,
выбрав пункт меню File I Save (или пажа� комбинацию клавиш <Ctrl>+<S>).
Кстати, файл с Руthоn-кодом в терминологии этого языка называется модулем.
Запустить написанную программу на выполнение можно, выбрав в окне с кодом этой про
граммы пункт меню Run I Run Module или нажав клавишу <F5>. Результат выполнения
программы будет отображен в окне ШLЕ Shell.
Запустить Руthоn-программу вне среды IDLE можно двумя способами:
♦ набрав имя хранящего ее файла вместе с расширением (например: hello_world.py}
в консоли и нажав клавишу <Enter>.
Результат выполнения будет вьmеден там же, в консоли;
♦ выполнив щелчок мышью (двойной или одинарный - в зависимости от настроек систе
мы) на значке файла с этой программой.
В этом случае после вьmода результата окно консоли сразу закроется. Чтобы предотвра
тить его закрытие, необходимо добавить в конец кода программы вызов функции
Глава 1. Первые шаги 27
input(), которая станет ожидать нажатия клавиши <Enter> и не позволит окну сразу за
крыться. С учетом сказанного наша первая программа будет выглядеть так, как показано
в листинге 1.2.
Отметим, что функция input() в листинге 1.2 вызывается без параметров. В таком случае
после имени функции ставятся пустые круглые -скобки.
ПРИМЕЧАНИЕ
Если до выполнения функции input() в коде возникнет ошибка, то сообщение о ней будет
выведено в консоли, но последняя после этого сразу закроется, и вы не сможете прочитать
сообщение об ошибке. Попав в подобную ситуацию, запустите программу из консоли или
утилиты IDLE, и вы сможете прочитать это сообщение.
Чтобы открыть Руthоn-файл для редактирования, запустим IDLE, выберем пункт меню File 1
Open (комбинация клавиш <Ctrl>+<O>) и выберем нужный файл в появившемся на экране
диалоговом окне открытия файла. Файл будет открыт в новом окне утилиты IDLE.
х = 15 + 20 + 30
print(x)
1 Доступны как базовая бесплатная, так и расширенная платная редакции этого редактора.
28 Часть /. Основы языка Python
ПРИМЕЧАНИЕ
4 пробела - общепринятая величина отступа в Python.
import encodings.aliases
arr = encodings.aliases.aliases
keys = list( arr.keys() )
keys.sort()
for key in keys: print("%s => %s" % (key, arr[key]))
1 ВОМ (Byte Order Mark) - метка порядка байтов. Указывает порядок, в котором записываются байты, ко
дирующие символы в UTF-8.
32 Часть /. Основы языка Python
Также следует разрешить выполнять Python-пporpaммy, указав у ее файла права 755 (-rwxr
xr-x).
1
2
>>>
Если ввести какое-либо значение - например, строку или число, и нажать <Enter>, это зна
чение появится в следующей строке:
>>> "Привет, мир!"
'Привет, мир! '
>>>
Обратите внимание на то, что строки выводятся в апострофах. Этого не произойдет, если
выводить строку с ПОМОЩЬЮ функции print():
>>> рrint("Привет, мир!")
Привет, мир!
>>>
Как говорилось в разд. ·1.2, окно ШLЕ Shell можно использовать для изучения языка, а
также в качестве многофункционального калькулятора (здесь * - это оператор умноже
ния):
>>> 12 * 32 + 54
438
>>>
Вместо функции print() можно использовать· метод write() объекта stdout из модуля sys
(методы очень похожи на функции и в подробностях, вместе с объектами, будут рассмотре
ны позже):
import sys # Подключаем модуль sys, чтобы использовать
# содержащийся в нем объект stdout
sys.stdout.write("Строка")
Как мы помним из разд. 1.3, модуль - это просто файл с Руthоn-кодом. Однако модуль sys
поставляется в составе Python и входит в стандартную библиотеку (набор модулей, содер
жащих полезные функции, объекты и др.) этого языка. Подключив этот модуль, мы можем
использовать созданнь1е в нем функции и объекты.
В первой строке с помощью оператора import подключаем модуль sys, в котором объявлен
объект stdout. Далее с помощью метода write() этого объекта выводим строку. Следует
заметить, что метод не вставляет символ перевода строки, поэтому при необходимости сле
дует добавить его самим с помощью символа \n:
import sys
sys .stdout.write( "Строка 1\n")
sys.stdout.write("Cтpoкa 2")
Метод wr.:L te() возвращает результат - значение, полученное в процессе выполненных
методом вычислений. Таковым результатом является количество символов в выведенной
строке. Его можно присвоить какой-либо переменной и ис�ользовать в дальнейшем:
import sys
cnt = sys.stdout.write("Пpивeт, Python\n")
рrint("Символов в выведенной строке: ", cnt)
Результат:
Привет, Python
Символов в выведенной строке: 15
import sys
arr = sys.argv(:]
for n in arr:
print (n)
Вместо любого из номеров, указываемых в составе версии, можно записать символ под
становки *. В таком случае будет установлена версия библиотеки, в которой соответст
вующий номер будет максимальным из присутствующих в репозитории. Пример уста
новки библиотеки Pillow со старшей версией 7 и наиболее актуальной младшей:
p ip install "p illow=7.*"
Поддерживаются следующие операторы сравнения:
• =-равно;
• <-меньше;
• >-больше;
• <=-не больше (меньше или равно);
• >=-не меньше (больше или равно);
• ,...,= - совместимо (будет описан позже).
Пример установки библиотеки Pillow версии 5.0.0 или более новой:
p ip install "p illow>=S.0.0"
После названия библиотеки можно указать несколько конструкций формата:
<Оператор сравнения><Номер версии>
разделив их запятыми (после которых можно поставить пробелы). Каждая из этих кон
струкций задаст одно условие, которому должна удовлетворять устанавливаемая версия
библиотеки. Будет установлена версия, удовлетворяющая всем указанным условиям.
Пример установки библиотеки Pillow версии не ниже 8.4.0 и меньше 8.6.0:
p ip install "p illow>=B.4.0, <8.6.0"
Пример:
p ip list
Pillow 8.4.0
p ip 21.2.4
setup tools 58.1.0
Единственная достуШiая здесь оrщия: --format=<Фopмaт вывода>, задающая формат вы
вода. В качестве параметра <Формат вывода> можно указать columns (вьmод в виде таб
лицы, как было показано в приведенном примере, - это формат по умолчанmо), freeze
(вьmод в виде перечня) или json (вьmод в формате JSON). Вот пример вывода списка
установлеmn.1х библиотек, оформленного в виде перечня:
pip list --format=freeze
У авторов бьшо вьmедено:
Pillow==8.4.0
pip=2l.2.4
setup tools= 58.l.0
Вьmод в формате JSON:
p ip list --format=json
У авторов получилось:
[ {"name": "Pillow", "version": "8.4.0"},
{"name": "p ip ", "version": "21.2.4"},
{"name": "setup tools", "version": "58.1.0"}]
ПРИМЕЧАНИЕ
Изначально в комплекте поставки Python уже присутствуют две библиотеки такого рода:
pip, реализующая функциональность одноименной утилиты, и setuptools, предоставляющая
специфические инструменты для установки дополнительных библиотек.
Единственная доступная опция: -f (или --files), которая указывает утилите pip допол
нительно вывести список всех файлов, составляющих библиотеку. Вот пример вывода
сведений о библиотеке Pillow, включая перечень составляющих ее файлов:
pip show -f pillow
Сразу после запуска pydoc откроется веб-браузер, в котором будет выведен список всех
модулей стандартной библиотеки. Щелкнув на имени модуля, мы откроем страницу с опи
санием всех ·классов, функций и констант, объявленных в этом модуле.
Чтобы завершить работу pydoc, следует перекточиться в его окно (см. рис. 1.7), ввести
в нем команду q (от quit, выйти) и нажать клавишу <Enter> - окно при этом автоматически
закроется. А введенная там команда ь (от browser, браузер) повторно выведет в браузере
страницу со списком модулей.
Документацшо по различным языковым конструкциям Python можно получить, запустив
интерпретатор в интерактивном режиме (см. разд. 1.2) и вызвав функцшо help(). В качест
ве примера отобразим документацшо по встроенной функции input() :
»> help(input)
Результат выполнения:
Help on built-in function input in module builtins:
input(prompt=None, /)
Read а string from standard input. The trailing newline is stripped.
If the user hits EOF (*nix: Ctrl-D, Windows: Ctrl-Z+Return), raise EOFError.
On *nix systems, readline is used if availaЫe.
Запустим эту программу в окне IDLE Shell или щелчком мыши и получим,результат:
Help on module testЗ:
NАМЕ
testЗ - Это описание нашего модуля
FUNCTIONS
func()
Это описание функции
FILE
d:\dаtа\документы\работа\книги\руthоn 3 и pyqt 6 разработка
приложений\ftр\1\tеstЗ.ру
Теперь получим содержимое строк документирования с помощью атрибута _doc_. Как это
делается, показывает код из листинга 1.10.
Результат выполнения:
Это описание нашего модуля
Это описание функции
44 Часть /. Основы языка Python
Атрибут _doc_ можно использовать вместо функции help(). В качестве примера получим
документацmо по функции input():
>>> print(input. doc
Результат выполнения:
Read а string from standard input. The trailing newline is stripped.
If the user hits EOF (*nix: Ctrl-D, Windows: Ctrl-Z+Return), raise EOFError.
On *nix systems, readline is used if availaЫe.
Получить список всех идентификаторов внутри модуля позволяет функция dir(). Пример
ее использования показывает код из листинга 1.11.
Результат выполнения:
[' builtins cached doc file loader name
'_y ackage_', '_spec_', 'func']
Теперь получим список всех встроенных идентификаторов:
>>> import builtins
>>> print(dir(builtins))
Будучи вызванной без параметров, функция dir() возвращает список идентификаторов из
текущего модуля:
»> print(dir())
Результат выполнения:
[' annotations ' builtins doc loader name
'_y ackage_', '_spec_']
Однако можно выполнить компиляцию указанного модуля (и даже всех модулей, храня
щихся в указанном каталоге) с сохранением откомпилированного кода в файле. При запуске
такого файла хранящаяся в нем программа запустится очень быстро, поскольку интерпрета
тору не придется выполнять предварительную компиляцию кода.
Для компиляции Руthоn-модулей следует набрать в консоли команду:
python -m compileall <Путь к файлу>l<Путь к каталогу> <Опции>
Можно указать либо путь к файлу модуля, который следует откомпилировать:
python -m compileall c:\work\python\programl.py
либо путь к каталогу - в этом случае будут откомпилированы все модули, хранящиеся как
в каталоге с указанным путем, так и во вложенньIХ в него каталогах:
python -m compileall c:\work\python\big_program
Откомпилированные модули сохранятся в каталоге _pycache_, который создается в ката
логе с исходными Руthоn-модулями, в файлах с именами формата:
<Имя исходного модуля>.сруthоn-<Номер версии Python>.pyc
Например, откомпилированный код модуля hello_world.py (см. листинг 1.1) будет сохранен
в файле _pycache_\hello_world.cpython-31 О.рус.
Расширение рус также ассоциируется с исполняющей средой Python, поэтому файль1 с та
ким расширением можно запускать простым щелчком мышью. А при распространении
Руthоn-программы можно передать конечным пользователям только откомпилированные
файлы, содержащие ее код.
При повторной компиляции будут откомпилированы только те модули, чье содержимое
изменилось после предыдущей компиляции. Python отслеживает изменение содержимого
модулей по дате и времени последнего изменения файлов, в которьIХ они хранятся.
Команда компиляции поддерживает следуюIЦИе полезные опции:
♦ -1 - компилировать только модули, непосредственно находящиеся в каталоге с указан
ным путем. Модули во вложенньrх каталогах компилироваться не будут;
♦ -f - принудцтельно перекомпилировать все модули в указанном каталоге, даже не из-
менившиеся после последней компиляции.
Следует отметить, что при подключении какого-либо модуля с целью использовать создан
ные в нем функции и объекты он всегда компилируется в сохранением результирующего
кода в файл. Это делается для ускорения запуска программ, использующих этот же модуль.
ГЛАВА 2
2.1. Переменные
Переменная - это ячейка в оперативной памяти компьютера, предназначенная для хране
ния какого-либо значения и имеющая уникальное имя. Обращение к переменной с целью
извлечь хранящееся в ней значение или занести в нее другое значение выполняется по име
ни этой переменной.
Чтобы создать переменную в языке Python, достаточно лишь занести в нее какое-либо зна
чение. Впоследствии в эту же переменную можно заносить любые другие значения. Если
занести в переменную другое значение, хранившееся в ней ранее будет потеряно.
Операция занесения значения в переменную называется присваиванием. Она выполняется
посредством оператора присваивания = (знак равенства), слева от которого ставится имя
нужной переменной, а справа - присваиваемое значение. Примеры:
>>># Присваиваем переменной language_name строковое значение 'Python'
>>> language_name = 'Python'
>>># Присваиваем переменной n число 123
>>> n = 123
Чтобы извлечь из переменной хранящееся в ней значение ·для использования его в вычисле
ниях, следует просто указать имя этой переменной:
>>>#Выводим результат сложения числа из переменной n и 321
>>> print(n + 321)
444
Имя переменной должно состоять из букв (можно использовать буквы любых алфавитов, но
традиционно применяется лишь латиница), цифр и знаков подчеркивания, причем имя пе
ременной не может начинаться с цифры. Кроме того, следует избегать указания символа
подчеркивания в начале имени, поскольку идентификаторам с таким символом определено
специальное назначение (подробности будут приведены в главе 12).
В качестве имен переменной нельзя использовать ключевые слова - слова, посредством
которых в Python составляются всевозможные языковые конструкции. Получить список
всех ключевых слов позволяет такой код:
>>> irnport keyword
>>> keyword.kwlist
['False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await', 'break',
'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally',
Глава 2. Переменные и типы данных 47
»> type(func)
<class 'function'>
♦ module - модули:
>>> import sys
>>> type(sys)
<class 'module'>
♦ tуре - классы и типы данных:
>>> class С: pass
>» type(C)
<class 'type'>
>>> type(type(""))
<class 'type'>
Значение каждого типа хранится в оперативной памяти в виде довольно сложной структу
ры, называемой объектом. Помимо собственно значения, в объекте содержатся всевозмож
ные данные, описывающие это значение (например, у строкового значения также хранится
количество символов, содержащихся в строке).
А теперь - важный момент! При присваивании значения, представленного объектом, ка
кой-либо переменной в последmою заносится не само это значение, а ссьmка на него (ссыл
ку можно рассматривать как особый компактный указатель на определенный объект). По
этому, если, например, список присвоить одной переменной, а потом значение этой пере
менной присвоить другой переменной, обе переменные будут содержать ссылки на один и
тот же список. Пример, илmострирующий сказанное:
>>> а [1, 2, 3] # Присваиваем список переменной а
>>> ь = а # Присваиваем значение переменной а (список)
>>> # переменной Ь
>>> print(a, Ь)
[1, 2, 3] [1, 2, 3]
>>> a[l ] = 40 # Изменяем второй элемент списка из переменной а
>>> # как видим, обе переменные сСЫJJаются
>>> print(a, Ь) # на ОДИН и ТОТ же список
[1, 40, 3] [1, 40, 3]
Типы делятся на изменяемые и неизменяемые. Значения изменяемых типов, к которым от
носятся list, set, bytearray и dict, можно изменять (так, можно изменить значение эле-
50 Часть /. Основы языка Python
Результатом выполнения этой программы будет не число, а строка "512". Как видим, функ
ция input() возвращает результат в виде строки. Чтобы просуммировать два числа, необхо
димо преобразовать полученные строки в числа (листинг 2.2).
В этом случае мы получим число 17, как и должно быть. Однако если пользователь вместо
числа введет строку, то программа завершится с фатальной ошибкой. Как обработать ошиб
ку, мы разберемся по мере изучения языка.
56 Часть /. Основы языка Python
Операторы
♦ - - вычитание:
>>> 10 - 5 # Целые числа
5
>>> 12.4 - 5.2 # Вещественные числа
7.2
>>> 12 - 5.2 # Целые и вещественные числа
6.8
♦ * - умножение:
>>> 10 * 5 # Целые числа
50
>>> 12.4 * 5.2 # Вещественные числа
64.48
>>> 10 * 5.2 # Целые и вещественные числа
52.0
· ♦ / - деление. Результатом деления всегда является вещественное число, даже если про
изводится деление целых чисел. Примеры:
58 · Часть /. Основы языка Python
>>> 10 ** 2, 10.0 ** 2
(100, 100.0)
♦ унарный минус ( - ) - изменяет знак числа на противоположный:
>>> -10, -10.0, -(-10), -(-10.0)
(-10, -10.0, 10, 10.0)
♦ унарный плюс ( +) - ничего не делает с числом 1 :
»> +10, +10.0)
(10, 10.0)
Как видно из приведенных примеров, операции над числами разных типов возвращают чис
ло, имеющее более сложный из типов, участвующих в операции. Целые числа имеют самый
простой тип, далее идут веществе�rnые числа и самый сложный тип - комплексные числа.
Таким образом, если в операции участвуют целое число и вещественное, то целое число
будет автоматически преобразовано в вещественное число, затем будет произведена опера
ция над вещественными числами, а результатом станет вещественное число.
При выполнении операций над веществеmп,1ми числами следует учитывать ограничения
точности вычислений. Например, результат следующей операции может показаться стран
ным:
>>> 0.3 - 0.1 - 0.1 - 0.1
-2.7755575615628914е-17
Ожидаемым бьш бы результат о . о, но, как видно из примера, мы получили совсем другой
результат. Если необходимо производить операции с фиксированной точностью, следует
использовать модуль decimal. Пример:
>>> from decimal import Decimal
>>> Decimal("0.3") - Decimal("0.l") - Decimal("0.l") - Decimal("0.l")
Decimal( ' О . О ' )
3.3. Операторы
для работы с последовательностями
Для работы с последовательностями предназначены следующие операторы:
♦ + - конкатенация (объединение):
>>> print("Cтpoкal" + "Строка2") # Конкатенация строк
Строка1Строка2
>>> (1, 2, 3] + (4, 5, 6] # Списки
(1, 2, 3, 4, 5, 6]
>>> (1, 2, 3) + (4, 5, 6) # Кортежи
(1, 2, 3, 4, 5, 6)
♦ * - повторение. Повторяет последовательность из первого операнда количество раз,
указанное вторым операндом. Примеры:
>>> "s" * 20 # Строки
'ssssssssssssssssssss'
>>> (1, 2] * 3 # Списки
(1, 2, 1, 2, 1, 2]
>>> (1, 2) * 3 # Кортежи
(1, 2, 1, 2, 1, 2)
Глава 3. Операторы 61
>>> х = (5 + 10) * 3 / 2
>>> х
22.5
Перечислим операторы в порядке убывания приоритета:
1. -х, +х, ~х, ** - унарный минус, унарный плюс, двоичная инверсия, возведение в сте
пень. Если унарные операторы расположены слева от оператора **, то возведение в сте
пень имеет больший приоритет, а если справа - то меньший. Например, выражение
-10 ** -2
эквивалентно следующей расстановке скобок:
- (10 ** (-2))
2. *, %, /, // - умножение (повторение), остаток от деления, деление, деление с округле-
нием вниз.
3. +,--сложение (конкатенация), вычитание.
4. «, » - двоичные сдвиги.
5. & - двоичное И.
6. л - двоичное искmочающее ИЛИ.
7. двоичное ИЛИ.
1 -
♦ or - логическое ИЛИ - если хотя бы один операнд равен тrue, возвращается True,
в противном случае - False:
>>> 1 < 5 or 2 < 5 # True or True == True
True
>>> 1 < 5 or 2 > 5 # True or False True
True
>>> 1 > 5 or 2 < 5 # False or True = True
True
>>> 1 > 5 or 2 > 5 # False or False == False
False
Если значение первого операнда не относится к логическому типу, интерпретатор пре
образует его в логический тип. Если после преобразования получается False, то возвра
щается значение второго операнда, в противном случае - значение первого операнда.
Пример:
>>> 10 or 20, О or 20, 10 or О
(10, 20, 10)
>>> О or "" or None or [] or "s"
's'
[else:
<Блок, испо/Iняемый, если ни одно условие не выполняется>
Если блок состоит из одной инструкции, эту инструкцmо можно разместить на одной стро
ке с языковой конструкцией if, elif или else:
х = int(input("Введите число: ") )
if х % 2 == О: print(x, " - четное число")
else: print(x, " - нечетное число")
В таком случае концом блока является конец строки. Это означает, что инструкции, входя
щие в блок, можно разместить на одной строке, разделяя их точкой с запятой. Пример:
х = int(input("Введите число: ") )
if х % 2 == О: print(x, end=" "); print("- четное число")
else: print(x, end=" "); print("- нечетное число")
СОВЕТ
Код, подобный приведенному в этом примере, плохо читается. Поэтому всегда размещайте
все инструкции на отдельных строках.
Как говорилось ранее, выражение ветвления может содержать произвольное количество
условий и соответствующих им блоков. Рассмотрим это на примере (листинг 4.2).
1 Листинr;с.2. п· .
elif os = "4":
print("Bы вы6рали: Windows 8")
elif os == "5":
print("Вы вы6рали: Windows 7")
elif os == "6":
print("Bы вы6рали: другая")
elif not os:
print("Bы не ввели число")
else:
print("Мы не смогли определить вашу операционную систему")
input()
Пустая строка всегда преобразуется в False, и, чтобы условие в этом случае считалось вы
полненным, нам следует инвертировать полученный результат оператором not.
Одну инструкцию ветвления можно вложить в другую. В этом случае отступ у блоков вло
женной инструкции должен быть в два раза больше (листинг 4.3).
c:lse:
print("Bы не ввели число")
:nput ()
3 - Windows 8.1
4 - Windows 8
5 - Windows 7
6 - Другая'""')
os = inрut("Введите число, соответствук:щее ответу: ")
match os:
case "1":
print("Вы выбрали: Windows 11")
case "2":
print("ВЫ выбрали: Windows 10")
case "3":
print("ВЫ выбрали: Windows 8 .1")
case "4":
print("Bы выбрали: Windows 8")
case "5":
print("Вы выбрали: Windows 7")
case "6":
print("Вы выбрали: другая")
case ""·
print("ВЫ не ввели число")
case -:
print("Мы не смогли определить вашу операционную систему")
input()
Сразу уясним, что исполняется только один блок - соответствующий первому совпавшему
образцу. После этого обработка инструкции прекращается, и начинают исполняться инст
рукции, следующие за ней. Так что при выполнении кода:
os = "1"
match os:
case "1":
print("ВЫ выбрали: Windows 11")
case "1":
рrint("Отличный выбор!")
case "2":
print("ВЫ выбрали: Windows 10")
в консоли появится лишь строка:
ВЫ выбрали: Windows 11
Рассмотрим особенности обработки инструкцией выбора проверяемых значений разных
типов:
♦ числа, строки и логические величины - образцы указьшаются в виде соответственно
чисел, строк и логических величин (пример см. в листинге 4.4);
♦ последовательности - образец указьшается в виде кортежа (также можно указать спи
сок). При этом образец может быть задан:
• с явным перечислением всех значений - проверяемая последовательность должна
содержать только заданные в образце значения, находящиеся в заданных позициях:
match seq:
case (1, 2, 3):
рrint("Последовательность 1: 1, 2, 3")
Глава 4. Инструкции ветвления, выбора и циклы 73
Результаты:
seq (1, 2, 3) => Последовательность 1: 1, 2, 3
seq (1, 2, 3, 4) => Последовательность 2: 1, 2, 3, 4
seq (3, 2, 1) => Последовательность 3: 3, 2, 1
seq (1, 2, 3, 4, 5) => Другая последовательность
seq ( 3, 2) => Другая последовательность
• с явным указанием части значений (вместо остальных явно заданных значений ста
вятся какие-либо переменные)- проверяемая последовательность должна содер
жать, по крайней мере, заданные значения, находящиеся в заданных позициях.
Остальные значения будут присвоены указанным в образце переменным, которые
станут доступны в блоке. Пример:
match seq:
case (1, 2, 3):
pr,int("Последовательность 1: 1, 2, 3")
case (1, 2, 3, rn):
рrint("Последовательность 2: 1, 2, 3, " + str(rn))
case (n, 2, 1):
print("Последовательность 3: " + str(n) + ", 2, 1")
case
print("Дpyгaя последовательность")
Результаты:
seq (1, 3)
2, => Последовательность 1: 1, 2, 3
seq (1, 2,
3, 4) => Последовательность 2: 1, 2, 3, 4
seq (1, 3, 56)
2, => Последовательность 2: 1, 2, 3, 56
seq (3, 2,
1) => Последовательность 3: 3, 2, 1
seq (800, 2, 1) => Последовательность 3: 800, 2, 1
seq (1, 2, 3, 4, 5) => Другая последовательность
Результаты:
seq (1, 2, 3, 4) => Последовательность 1: 1, 2, 3, [ 4]
seq (1, 2, 3 , 4, 5) => Последовательность 1: 1, 2, 3, [4, 5]
74 Часть /. Основы языка Python
Результаты:
mpn {"а": 1, "Ь": 2} => Отображение 1: а 1, ь 2
mpn {"а": 1, "Ь": 2, "с": 3}, => Отображение 1: а - 1, ь 2
mpn {"а": 1, "с": 3} => Отображение 2: а - 1, с - 3
mpn {"е": 11, "f" : -32) => Другое отображение
• с явным указанием части значений (у остальных после кточей ставятся перемен
ные) - проверяемое отображение должно содержать, по крайней мере, заданные
значения и какие-либо значения, указанные под кточами, в которых поставлены пе
ременные. Значения из кточей с переменными будут присвоены этим переменным,
которые станут доступными в блоке. Пример:
match mpn:
case {"а": 1, "Ь": 2, "d": d}:
print("Отображение 1: а - 1, Ь - 2, d - " + str(d))
case {"а": 1, "Ь": 2}�
рrint("Отображение 2: а - 1, Ь - 2")
Результаты:
mpn {"а": 1, "Ь": 2, "d": 3) => Отображение 1: а - 1, ь - 2, d - 3
mpn {"а": 1, "Ь": ·2, "d": 25} => Отображение 1: а - 1, ь - 2, d - 25
mpn {"а": 1, "Ь": ·2) => Отображение 2: а - 1, ь - 2
rnatch mpn:
case {"а": 1, "Ь": 2, **d):
print("Отображение 1: а - 1, Ь - 2, " + str(d))
case {"а": 1, "d": 4):
рrint("Отображение 2: а - 1, d - 4")
Результаты:
mpn {"а": 1, "Ь": 2, "с": 3)
=> Отображение 1: а - 1, Ь - 2, {'с': 3)
mpn {"а": 1, "Ь": 2, "с": 3, "d": 4)
=> Отображение 1: а - 1, ь - 2, {'с': 3, 'd': 4)
mpn {"а'': 1, "Ь": 2) => Отображение 1: а - 1, ь - 2, {)
mpn d :
{"а": 1, " " 4) = > Отображение 2: а - 1, d - 4
♦ объекты классов (описаны в главе 1 3) - можно проверять:
• принадлежность проверяемого объекта указанному классу - образец записывается
в формате <Имя нужного класса>():
import datetime
rnatch val:
case str():
print("Строка")
case int():
print("Цeлoe число")
case datetime.datetime():
рrint("Временная отметка")
Результаты:
val "Python" => Строка
val 589 => Целое число
val datetime.datetime(2020, 12, 28) => Временная отметка
. Результаты:
val "Python" => Строка
val datetime.datetime(2020, 12, 28) => 28 декабря 2020 года
val datetime.datetime(2021, 1, 1) => 1 января 2021 года
val datetime.datetime(2021, 1, 2) => Другие дата и время
В конструкции <Атрибут>=<Значение атрибута> вместо значения атрибута можно за
дать переменную. Эта переменная получит значение, хранящееся в соответствующем
атрибуте проверяемого объекта класса, и будет доступна в блоке. Пример:
import datetime
match val:
case datetime.datetime(year=2021, month=l, day=l):
print("l января 2021 года")
case datetime.datetime(year=2021, month=2, day=d):
print(str(d) + " февраля 2021 года")
Результаты:
val datetime.datetime(2021, 1, 1) => 1 января 2021 года
val = datetime.datetime(2021, 2, 20) => 20 февраля 2021 года
В качестве образца можно указать набор значений, разделенных символами вертикальной
черты ( 1 ). Соответствующий блок будет выполнен, если проверяемое значение совпадает
с mобым из перечисленных образцов. Пример:
match n:
case "Python" 1 "JavaScript" 1 "РНР" 1 "RuЬy":
print("Язык программирования")
case "Django" 1 "Express" 1 ". Laravel" 1 "Rails":
print("Веб-фреймворк")
Результаты:
n = "Python" => Язык программирования
n = "Laravel" => Веб-фреймворк
n = "Express" => Веб-фреймворк
n = "RuЬy" => Язык программирования
В языковой конструкции case можно дополнительно указать конструкцию if следующего
формата:
case <Образец> if <Условие>
В этом случае проверяемое значение будет считаться совпавшим с указанным образцом
только в том случае, если заданное условие в качестве результата выдаст True. Пример
(функция len() возвращает количество значений в последовательности):
match seq:
case (1, 2, 3, *а) if (len(seq) > 4):
print("Бoлee 4 значений: 1, 2, 3, " + str(a))
case (1, 2, 3, *а):
print("He менее 4 значений: 1, 2, 3, " + str(a))
Результаты:
seq (1, 2, 3, 4, 5) => Более 4 значений: 1, 2, 3, [4, 5)
seq = (1, 2, 3, 4) => Не менее 4 значений: 1, 2, 3, [4]
Глава 4. Инструкции ветвления, выбора и циклы 77
<Блок else> выполняется, если указанная последовательность пуста (не содержит ни одного
элемента), и исполнение цикла не бьшо прервано оператором break (описан в разд. 4.8).
Цикл такого типа может обрабатывать любые последовательности: списки, кортежи, диапа
зоны, строки и др.
Пример перебора символов в строке приведен в листинге 4.5.
for s in "str":
print(s, end=" ")
else:
print("\nЦикл выполнен")
Результат выполнения:
s t r
Цикл выполнен
Перебор элементов списка и кортежа с построчным выводом (листинг 4.6).
5 6 7 8 9 10 11 12 13 14
78 Часть /. Основы языка Python
1 2
З 4
<Блок else> будет выполнен, если указанное условие изначально не выполняется и испол
нение цикла не бьшо прервано оператором break (описан в разд. 4. 8).
В теле цикла с условием следует произвести какие-либо действия, которые в определенный
момент сделают так, чтобы указанное условие перестало выполняться и цикл завершился.
Например, условие может проверять, не превысило ли значение какой-либо переменной
указанную величину, - в таком случае в блоке следует увеличивать значение этой пере
менной на 1.
В качестве примера вьmедем все числа от 1 до 100, используя цикл while (листинг 4.7).
i = 1
while i < 101:
print(i)
i += 1
ВНИМАНИЕ/
Если в теле цикла с условием не предпринимается никаких действий, способных прервать
цикл, он будет выполняться бесконечно (бесконечный цикл).
Прервать выполнение бесконечного цикла, выводящего данные, можно нажатием комбина
ции ·клав.иш <Ctrl>+<C>. В результате генерируется исключение кeyboardinterrupt (исклю
чения рассматриваются в главе 14), и выполнение программы останавливается.
Выведем все числа от 100 до 1 (листинг 4.8).
i = 100
while i:
print (i)
i -= 1
Глава 4. Инструкции ветвления, выбора и циклы 79
Поскольку в условии указано значение тrue, цикл станет выполняться бесконечно. Однако
оператор break прерывает цикл, как только он будет выполнен 100 раз.
Цикл с условием совместно с оператором break удобно использовать для получения не
определенного заранее количества данных от пользователя. В качестве примера просумми
руем произвольное количество чисел (листинг 4.12).
80 Часть /. Основы языка Python
Процесс ввода трех чисел и получения суммы выглядит так (значения, введеm1ые пользова
телем, здесь выделены полужирным шрифтом):
Введите слово 'stop' для получения результата
Введите число: 10
Введите число: 20
Введите число: 30
Введите число: stop
Сумма чисел равна: 60
Числа
Длинные целые числа для улучшения читаемости кода можно разбивать на группы цифр,
вставляя символы подчеркивания:
»> 1 ООО ООО
1000000
»> OЫlll 1111
255
»> Оо2 777
1535
>>> ОхаЬ cd
43981
Вещественное число может содержать точку и (или) быть представлено в экспоненциаль
ной форме с буквой Е (регистр не имеет значения). Начальный О можно не указывать. При
меры:
»> 10., .14, 3.14, 11Е20, 2.5е-12
(10.0, 0.14, 3.14, l.le+21, 2.5е-12)
При выполнении операций над вещественными числами следует учитывать ограничения
точности вычислений. Например, результат следующей операции может показаться стран
ным:
>>> 0.3 - 0.1 - 0.1 - 0.1
-2.7755575615628914е-17
Ожидаемым был бы результат о. о, но, как видно из примера, мы получили совсем другое
значение. Если необходимо производить операции с фиксированной точностью, то следует
использовать модуль decimal:
>>> from decimal import Decimal
>>> Decimal("0.3") - Decimal("O.l") - Decimal("O.l") - Decimal("O.l")
Decimal( ' О. О' )
Кроме того, можно использовать дроби, поддержка которых реализована в модуле
fractions. При создании дроби можно указать два целых числа (числитель и знаменатель),
одно вещественное число или строку с вещественным числом.
Для примера создадим несколько дробей. Вот так формируется дробь 4/5:
>>> from fractions import Fraction
>>> Fraction(4, 5)
Fraction (4, 5)
1
А вот так- дробь /2, причем можно сделать это тремя способами:
>>> Fraction(l, 2)
Fraction(l, 2)
>>> Fraction(0.5)
Fraction(l, 2)
>>> Fraction("0.5")
Fraction(l, 2)
Над дробями можно производить арифметические операции, как и над обычными числами:
>>> Fraction(9, 5) - Fraction(2, 3)
Fraction(17, 15)
84 Часть /. Основы языка Python
♦ float( [<Число или строка>J ) - преобразует заданное целое число или строку в вещест
венное число:
>>> float(7), float("7.1"), float("12.")
(7.0, 7.1, 12.0)
>>> float("inf"), float("-Infinity"), float("nan")
(inf, -inf, пап)
»> float()
о.о
♦ Ьin(<Число>) - преобразует заданное десятичное число в двоичное и возвращает его
в виде строки:
>>> Ьin(255), Ьin(l), Ьin(-45)
('OЫlllllll', 'ОЫ', '-ОЫ01101')
♦ oct(<Число>) - преобразует заданное десятичное число в восьмеричное и возвращает
его в виде строки:
>>> oct(7), oct(B), oct(64)
('Оо7', 'OolO', 'OolOO')
♦ hex(<Число>) - преобразует заданное десятичное число в шестнадцатеричное и возвра
щает его в виде строки:
>>> hex(lO), hex(lб), hex(255)
('Оха', 'OxlO', 'Oxff')
Глава 5. Числа 85
ПРИМЕЧАНИЕ
Для работы с комплексными числами необходимо использовать модуль cmath.
88 Часть /. Основы языка Python
♦ е - константа е:
>>> math.e
2.718281828459045
ПРИМЕЧАНИЕ
В этом разделе мы рассмотрели только основные функции, поддерживаемые модулем
math. Чтобы получить полный список функций, обращайтесь к документации по Python.
>>> random.seed(l0)
>>> random.random()
О.5714025946899135
>>> random.seed(l0)
>>> random.random()
О.5714025946899135
♦ uniform(<Haчaлo>, <Конец>) - возвращает псевдослучайное вещественное число в диа
пазоне от параметра <Начало> до параметра <Конец>:
>>> random.uniform(0, 10)
9.965569925394552
>>> random.uniform(0, 10)
О.4455638245043303
♦ randint(<Начало>, <Конец>) - возвращает псевдослучайное целое число в диапазоне от
параметра <Начало> до параметра <Конец>:
>>> random.randint(0, 10)
4
>>> random.randint(0, 10)
10
import random
def passw_generator(count_char=8):
arr = [,а,,,Ь,, ,с ,,,d,,,е,,,f,,,g,,,h,,,i,, , j ,,,k ,, , 1,,1 m,,
'n','о','р','q','r','s','t','u'' 'v'' 'w','х'' 'у','z''
'А','В','С ','D','Е','F','G','Н','I','J','К','L',
'М','N','О','Р','Q','R','S','Т','U','V', 'W',
'X','Y','Z 1 ,1 l','2',1 З','4','5 ','6','7','8','9','0']
passw = []
for i in range(count_char):
passw.append(random.choice(arr))
return .join(passw)
1111
При попытке преобразовать двоичные данные типа byte или bytearray (описаны
в разд. 6.11 и 6.12) в строку будет выдано строковое представление двоичных данных:
»> str(b"\xfl\xf2\xf0\xee\xea\xe0")
"b'\\xfl\\xf2\\xf0\\xee\\xea\\xe0'"
Чтобы получить строку, следует указать кодировку:
>>> str(b"\xfl\xf2\xf0\xee\xea\xe0", "ср1251")
'строка'
В третьем параметре могут быть указаны значения "strict" (при ошибке возбуждается
искmочение UnicodeDecodeError - значение по умолчанmо), "replace" (неизвестный
символ заменяется символом с кодом \uFFFD) или ignore 11 (неизвестные символы игно
II
рируются):
>>> objl = bytes("cтpoкal", utf-8")
11
('строкаl' , 'строка2')
>>> str(objl, "ascii", strict")
11
♦ \f-перевод формата;
♦ \О-нулевой символ;
♦ \" - двойная кавычка;
♦ \' - одинарная кавычка (апостроф);
♦ \\ - обратный слеш;
♦ \<N> - символ с восьмеричным кодом <N>. Например, \ 7 4 соответствует символу <;
♦ \x<N> - символ с шестнадцатеричным кодом <N>. Например, \хба соответствует симво
лу j;
♦ \u<nnnn> - символ с 16-битным Unicode-кoдoм <nnnn>. Например, \u04За соответствует
русской букве к;
♦ \U<nnnnnnnn> - символ с 32-битным Unicode-кoдoм <nnnnnnnn>;
♦ \N{<name>J - символ с Uniсоdе-именем <name>. Например, \N{Registered Sign} соот
ветствует знаку зарегистрированной торговой марки ®·
Комбинация обратного слеша с тобым другим символом выводится как есть:
>>> print("Этот символ \не специальный")
Этот символ \не специальный
Тем не менее для вставки обратного слеша лучше использовать специальный символ \ \:
>>> print("Этoт символ \\не специальный")
Этот символ \не специальный
Так как строки относятся к неизменяемым типам данных, то изменить символ с указанным
индексом нельзя:
>>> s = "Python"
>>> s[О] = "J" # Изменить строку нельзя
Traceback (most recent call last):
File "<pyshell#94>", line 1, in <module>
s[О] = "J" # Изменить строку нельзя
TypeError: 'str' object does not support item assignment
Глава б. Строки и двоичные данные 97
Вместо значения ширины можно указать символ «*». В этом случае ширину следует
задать внутри кортежа. Пример:
>>>'" %*s"%10s'" % (10, "string", "str")
"' string'' str'"
♦ <Точность>- количество знаков после точки у вещественных чисел. Перед этим пара
метром обязательно должна стоять точка. Пример:
>>> irnport math
>>>"%s %f %.2f"· % (math.pi, math.pi, math.pi)
'3.141592653589793 3.141593 3.14'
Вместо значения точности можно указать символ«*». В этом случае точность следует
задать внутри кортежа. Пример:
>>>"'%*.*f"' % (8, 5, math.pi)
"' 3.14159'"
♦ <Тип преобразования>:
• s - преобразование выводимого значения в строку с помощью функции str() :
>>>print("%s" % ("Обычнаястрока"))
Обычнаястрока
>» print("%s %s %s" % (10, 10.52, [1, 2, 3]))
10 10.52 [1, 2, 3]
• r - преобразование выводимого значения в строку с помощью функции repr() :
>>>print("%r" % ("Обычнаястрока"))
'Обычнаястрока'
• а - преобразование выводимого значения в строку вызовом функции ascii() :
>>>print("%a" % ("строка"))
'\u0441\u0442\u0440\u043e\u043a\u0430'
• с - вывод символа с указанным кодом. Вьшедем числовое значение и соответст
вующий ему символ:
>>>for i in range(33, 127): print("%s =>%с" % (i, i))
• d и i - вывод целой части заданного числа:
>>>print("%d %d %d" % (10, 25.6, -80))
10 25 -80
>>>print("%i %i %i" % (10, 25.6, -80))
10 25 -80
• о - вывод заданного целого числа в восьмеричном представлении:
>>>print("%о %о" % (Оо77, 10))
77 12
Глава 6. Строки и двоичные данные 101
html = """<html>
<head><title>%(title)s</title>
</head>
<body>
<h1>%(hl)s</hl>
<div>%(content)s</div>
</body>
</html>"""
arr = { "title": "Это название документа",
"hl": "Это заголовок первого уровня",
"content": "Это основное содержание страницы"}
print(html % arr) # Подставляем значения и выводим шаблон
input()
Результат выполнения:
<html>
<head><title>Этo название документа</titlе>
</head>
<body>
<hl>Это заголовок первого уровня</hl>
<div>Этo основное содержание страницы</div>
</body>
</html>
Дшr форматирования строк также можно использовать следующие методы, поддерживае
мые объектом строки:
♦ expandtabs( [ <Ширина поля> J )- заменяет каждый символ табуляции в текущей строке
пробелами так, чтобы общая ширина фрагмента вместе с текстом, расположеннь1м перед
символом табуляции, была равна указанной величине. Если параметр не указан, то
ширина поля предполагается равной 8 символам. Пример:
>>> s = "1\tl2\tl23\l ri
>>> "'%s'" % s.expandtabs(4)
"'l 12 123 '"
В этом примере ширина задана равной четырем символам. Поэтому во фрагменте 1\t
табуляция будет заменена тремя пробелами, во фрагменте 12\t- двумя пробелами, а во
фрагменте 123\t- одним пробелом. Во всех трех фрагментах ширина будет равна
четырем символам.
Если перед символом табуляции нет текста или количество символов перед табуляцией
равно указанной в вызове метода ширине, то табуляция заменяется указанным количест
вом пробелов:
Глава б. Строки и двоичные данные 103
>>> s = "\t"
>>> "'%s' - '%s'" % (s.expandtabs(), s.expandtabs(4))
1 - ' '"
>>> s = "1234\t"
>>> "'%s'" % s.expandtaЬs(4)
"'1234
Если количество символов перед табуляцией больше ширины, то табуляция заменяется
пробелами таким образом, чтобы ширина фрагмента вместе с текстом делилась без
остатка на указанную ширину:
>>> s = "12345\t123456\t1234567\t1234567890\t"
>>> "'%s'" % s.expandtabs(4)
"'12345 123456 1234567 1234567890
Таким образом, если количество символов перед табуляцией больше 4, но менее 8, то
фрагмент дополняется пробелами до 8 символов. Если количество символов больше 8,
но менее 12, то фрагмент дополняется пробелами до 12 символов и т. д. Все это справед
ливо при указании, в качестве параметра числа 4;
♦ center(<Ширина> [, <Символ>] ) - выравнивает текущую строку по центру внутри поля
указанной ширины с добавлением слева и справа символов из второго параметра (если
он не указан, будут добавлены пробелы):
>>> s = "str"
>>> s.center(l5), s.center(ll, "-")
( 1
str ', '----str----')
Теперь произведем выравнивание трех фрагментов шириной 15 символов: первого - по
правому краю, второго - по левому, а третьего - по центру:
>>> s = "str"
>>> "'%15s' '%-15s' '%s'" % (s, s, s.center(15))
str' 'str str 111
указан, будут добавлены пробелы). Если количество символов в текущей строке превы
шает ширину поля, то значение ширины игнорируется и строка возвращается полно
стью. Пример:
>>> s = "string"
>>> s.rjust(15), s.rjust(15, "-")
(' string', '---------string')
>>> s.rjust(б), s.rjust(5)
('string', 'string')
♦ zfill(<Ширина>) - выравнивает текущую строку по правому краю внутри поля указан
ной ширины с добавлением слева нулей. Если количество символов в текущей строке
превышает ширину поля, то значение ширины шнорируется и строка возвращается пол
ностью. Примеры:
>>> "5".zfill(20), "123456".zfill(5)
('00000000000000000005', '123456')
• <Выравнивание>:
Q
< - по левому краю;
Q
> - по правому краю (поведение по умолчаншо);
□ л - по центру поля.
Пример:
>>> "'{0:<10}' '{1:>10}' '{2: л l0}"'.fonnat(3, 3, 3)
"' 3 3' ' 3
»> Ь = 12.3
>>> f"(b=}", f"(b = }"
('Ь=12. 3', 'Ь = 12. 3')
>>> f"Значение переменной (Ь = :5.2f}"
'Значение переменной Ь = 12.30'
Этот программный инструмент может быть применен для вывода значений переменных при
отладке программ.
♦ lstrip( [<Символы>] J - удаляет заданные символы в начале текущей строки (если пара-
метр не указан, удаляет пробелы):
>>> sl, s2 = 11 str 11
,
11
strstrstrokstrstrstr 11
>>> 11 1 %
s' - %
' s 1 11 %
(sl.lstrip(), s2.lstrip( 11 tsr 11))
"'str ' - 'okstrstrstr' 11
♦ rstrip( [<Символы> J ) - удаляет задаш1ые символы в ко�ще текущей строки (если пара
метр не указан, удаляет пробелы):
>>> sl, s2 = 11 str 11
,
11
strstrstrokstrstrstr 11
>>> 11 1 %
s' - '%s 1 11 % (sl.rstrip(), s2.rstrip( 11 tsr 11 ))
str' - 'strstrstrok' 1 1
>>> 11 1,,2,,3".split(",")
[ 11', 1 1, 12 1' '1, 131]
Если разделитель в текущей строке не найден, то список будет состоять из одного эле
мента, представляющего текущую строку:
>>> "wordl word2 word3 11 .split( 11 \n 1 1)
['wordl word2 wordЗ']
'Python'
>>> 11 CPython".remove refix( Active11 p 11 )
'CPython'
♦ removesuffix(<Префикс>) (начиная с Python 3.9) ...:_ удаляет у текущей строки указа�mый
суффикс и возвращает результат . Если текущая строка не содержит такого суффикса,
возвращает текущую строку. Пример :
>>> 11 rogram. y".removesuffix(11 . y")
p p p
' rogram'
p
' rogram.js'
p
Строки относятся к неизменяемым типам данных, поэтому, если попытаться изменить ка
кой-либо символ в строке, возникнет ошибка. Однако можно преобразовать строку в список
с помощью функции list(), изменить нужные символы по их индексам, а затем вызовом
метода join() превратить список обратно в строку. Пример:
>>> s = Python11
11
'Jython'
♦ istitle() - возвращает True, если все слова в текущей строке начинаются с заглавной
буквы, в противном случае - False. Если строка пустая или не содержит букв, возвра
щается False. Примеры:
>>> "Str Str".istit le(), "Стр Стр".istit le()
(True, True)
>>> "Str Str 123".istit le(), "Стр Стр 123".istit le()
(True, True)
>>> "Str str".istitle(), "Стр стр".istitle()
(False, False)
>>> "".istitle(), "123".istit le()
(False, False)
♦ isprint aЫe() - возвращает True, если текущая строка содержит только печатаемые
символы, в противном случае - False. Пробел относится к печатаемым символам. Если
строка пустая, возвращается тrue. Примеры:
>>> "123".isprint aЫe()
True
>>> "РНР Python".isprint aЫe()
True
>>> "\n".isprintaЫe()
False
♦ isspace() - возвращает True, если текущая строка содержит только пробельные симво
лы, в противном случае - False. Если строка пустая, возвращается False. Пример:
>>> "".isspace(), " \n\r \t ".isspace(), "str str".isspace()
(False, True, False)
♦ iside ntifier() - возвращает тrue, если текущая строка представляет собой допустимое
с точки зрения Python имя переменной, функции или класса, в противном случае -
False:
>>> "s".iside ntifier()
True
>>> "func".iside ntifier()
True
120 Часть /. Основы языка Python
>>> "123func".isidentifier()
False
Следует иметь в виду, что метод isidentifier() лишь проверяет, удовлетворяет ли за
данное имя правилам языка. Он не проверяет, совпадает ли это имя с каким-либо ключе
вым словом Python. Для выполнения такой проверки надлежит применять функцmо
iskeyword() из модуля keyword, которая возвращает True, если переданная ей строка
совпадает с одним из ключевых слов, например:
>>> keyword.iskeyword("else")
True
>>> keyword.iskeyword("elsewhere")
False
Переделаем нашу программу суммирования произвольного количества целых чисел, вве
денных пользователем (см. листинг 4.13), таким образом, чтобы при вводе строки вместо
числа программа не завершалась с фатальной ошибкой. Кроме того, предусмотрим возмож
ность ввода отрицательных целых чисел (листинг 6.2).
Процесс ввода значений и получения результата выглядит так (значения, введенные поль
зователем, выделены полужирным шрифтом):
Введите слово 'stop' для получения результата
Введите число: 10
Введите число:
Вы не ввели значение!
Введите число: str
Необходимо ввести число, а не строку!
Введите число: -5
Введите число: -str
Необходимо ввести число, а не строку!
Глава б. Строки и двоичные данные 121
В третьем параметре могут быть указаны значения "strict" (при ошибке возбуждается
искточение UnicodeEncodeError- значение по умолчанию), "replace" (неизвестный
символ заменяется знаком вопроса) или "ignore" (неизвестные символы игнорируются):
>>> bytes("string\uFFFD", "ср1251", "strict")
Traceback (most recent call last):
File "<pyshell#ЗS>", line 1, in <module>
bytes("string\uFFFD", "ср1251", "strict")
File "C:\Python310\lib\encodings\cp1251.py", line 12, in encode
return codecs.charmap_encode(input,errors,encoding_taЬle)
UnicodeEncodeError: 'charmap' codec can't encode character
'\ufffd' in position 6: character maps to <undefined>
>>> bytes("string\uFFFD", "ср1251", "replace")
b'string?'
>>> bytes("string\uFFFD", "ср1251", "ignore")
b'string'
♦ с помощью строкового метода encode( [encoding="utf-8" J · [, errors="strict" J ) • В па
раметре encoding задается кодировка (по умолчанию UTF-8). В параметре errors могут
быть указаны значения "strict" (значение по умолчанию), "replace", "ignore",
"xmlcharrefreplace" или "backslashreplace". Примеры:
>>> "строка".еnсоdе()
b'\xdl\x81\xdl\x82\xdl\x80\xdO\xЬe\xd0\xЬa\xd0\xЬO'
>>> "cтpoкa".encode(encoding="cp1251")
b'\xfl\xf2\xf0\xee\xea\xe0'
122 Часть /. Основы языка Python
>>> "cтpoкa\uFFFD".encode(encoding="cp1251",
errors="xrnlcharrefreplace")
b'\xfl\xf2\xf0\xee\xea\xe0�'
>>> "cтpoкa\uFFFD" .encode(encoding="cp1251",
errors="backslashreplace")
b'\xfl\xf2\xf0\xee\xea\xe0\\ufffd'
♦ указав букву ь (регистр не имеет значения) перед строкой в апострофах, кавычках, трой
ных апострофах или тройных кавычках. В строке могут присутствовать только символы
из кодировки ASCII, все остальные символы должны быть представлены специальными
последовательностями. Примеры:
>>> b"string", b'string', b"""string""", b'''string'''
(Ь'string', Ь'string', Ь'string', Ь'string')
>>> Ь"строка"
SyntaxError: bytes can only contain ASCII literal characters.
>>> b"\xfl\xf2\xf0\xee\xea\xe0"
b'\xfl\xf2\xf0\xee\xea\xe0'
♦ с помощью функции bytes(<Последовательность>), которая преобразует заданную по
следовательность целых чисел от О до 255 в объект типа bytes. Если какое-либо число из
последовательности не попадает в диапазон, возбуждается исключение valueError.
Примеры:
»> b = bytes([225, 226, 224, 174, 170, 160])
>>> ь
b'\xel\xe2\xe0\xae\xaa\xa0'
>>> str(b, "ср866")
'строка'
♦ с помощью функции bytes(<Количество>), которая возвращает последовательность из
заданного количества нулевых элементов:
»> bytes(10)
Ь'\х00\х00\х00\х00\х00\х00\х00\х00\х00\х00'
♦ с помощью метода bytes.fromhex(<Строка>). Заданная строка в этом случае должна со
держать только шестнадцатеричные числа. Начиная с Python 3. 7, числа в строке можно
разделять пробелами, которые будут проигнорированы. Пример:
>>> Ь = bytes.fromhex(" el е2е0ае аааО ")
>>> ь
b'\xel\xe2\xe0\xae\xaa\xa0'
>>> str(Ь, "ср866")
'строка'
Как и все последовательности, значения типа bytes поддерживают обращение к элементу
по индексу, получение среза, конкатенацию, повторение и проверку на вхождение:
>>> Ь = b�es( "string", "ср1251")
>>> ь
Ь'string'
>>> Ь[О] # Обращение по индексу
115
>>> b[l:3] # Получение среза
b'tr'
Глава б. Строки и двоичные данные 123
Преобразовать значение типа bytes в строку позволяет метод decode(). Метод имеет сле
дующий формат:
decode( [encoding="utf-8"] [, errors="strict"])
Параметр encoding задает кодировку символов (по умолчанию UTF-8) в текущей последо
вательности, а параметр errors - способ обработки ошибок при преобразовании: "strict"
(значение по умолчанию), "replace" или "ignore". Пример преобразования:
>>> Ь = bytes("строка", "ср1251")
>>> b.decode(encoding="cp1251")
'строка'
Для преобразования также можно воспользоваться функцией str() :
>>> Ь = Ьуtеs("строка", "ср1251")
>>> str(Ь, "ср1251")
'строка'
Чтобы изменить кодировку данных, сначала следует преобразовать значение типа bytes
в строку, а затем произвести обратное преобразование, указав нужную кодировку. Преобра
зуем данные из кодировки Windows-1251 в кодировку KOI8-R, а затем обратно:
>>> w = Ьуtеs("Строка", "ср1251") # Данные в кодировке windows-1251
>>> k = w.decode("cp1251") .encode("koiB-r")
>>> k, str(k, "koiB-r") # Данные в кодировке KOIB-R
(b'\xf3\xd4\xd2\xcf\xcb\xcl', 'Строка')
>>> w = k.decode("koiB-r") .encode("cp1251")
>>> w, str(w, "ср1251") # Данные в кодировке windows-1251
(b'\xdl\xf2\xf0\xee\xea\xe0', 'Строка')
Последовательности типа bytes можно форматировать с применением описанного в разд. 6.3
оператора%:
>>> b"%i - %i - %f" % (10, 20, 30)
b'l0 - 20 - 30.000000'
Однако тип преобразования s (т. е. вывод в виде Unicode-cтpoки) в этом, случае не поддер
живается, и его использование приведет к возбуждению искточения TypeError:
>>> b"%s - %s - %s" % (10, 20, 30)
Traceback (most recent call last):
File "<pyshell#S0>", line 1, in <module>
b"%s - %s - %s" % (10, 20, 30)
TypeError: %Ь requires а bytes-like object, or an object that implements
_bytes_, not 'int'
Метод hex() возвращает строку с шестнадцатеричным представлением текущей последова
тельности типа bytes. Формат метода:
hex( [ <Разделитель> [, <Число байтов в группе>] ] )
Поддержка параметров у этого метода появилась в Python 3.8. Параметр <Разделитель> за
дает разделитель, который будет вставляться между отдельными группами байтов в выда
ваемой строке (если он не ·указан, никакой разделитель не вставляется). Если параметру
<Число байтов в группе> присвоено положительное число, отсчет байтов ведется справа,
если отрицательное - слева, если параметр вообще не задан, число байтов в группе прини
мается равным 1. Примеры:
Глава б. Строки и двоичные данные 125
>>> b"string".hex()
'737472696е67'
>>> b"string".hex(' '), b"string" .hex(' ', 4), b"string" .hex(' ', -4)
('73_74_72_69_6е_67', '7374_72696е67', '73747269_6е67')
Любой элемент в последовательности типа bytearray можно изменить. При этом важно
помнить, что присваиваемое ему новое значение должно быть целым числом в диапазоне от
О до 255. Примеры:
>>> Ь = bytearray("Python", "ascii")
>>> Ь[О] # Можем получить значение
80
>>> Ь[О] = b"J"[О] # Можем изменить значение
>>> ь
bytearray(Ь' Jython')
Для изменения значения типа bytearray также можно использовать следующие методы:
♦ append(<Число>) - добавляет заданное число в конец текущей последовательности:
>>> Ь = bytearray("string", "ascii")
»> b.append(b"l"[0]); Ь
bytearray(Ь'stringl')
>>> ь
bytearray(b'stringl23')
♦ insert(<Индекс>, <Число>) - добавляет заданное число в текущую последователь
ность по указанному индексу. Остальные элементы смещаются. Добавим элемент в на
чало последовательности:
>>> Ь = bytearray("string", "ascii")
>>> b.insert(O, b"l"[O]); Ь
bytearray(Ь'lstring')
Чтобы добавить несколько элементов, можно воспользоваться операцией присваивания
значения срезу. Добавим несколько элементов в начало объекта:
>>> Ь = bytearray("string", "ascii")
>>> Ь[:0] = Ь"123"; Ь
bytearray(b'l23string')
♦ рор( [ <Индекс> J ) - удаляет из текущей последовательности элемент, расположенный
по указанному индексу, и возвращает его. Если индекс не указан, удаляет и возвращает
последний элемент. Примеры:
>>> Ь = bytearray("string", "ascii")
>>> Ь.рор() # Удаляем последний элемент
103
»> ь
bytearray(b'strin')
»> Ь.рор(О) # Удаляем первый элемент
115
>>> ь
bytearray(Ь'trin')
Удалить элемент последовательности также позволяет оператор ctel:
>>> Ь = bytearray("string", "ascii")
>>> del Ь[5] # Удаляем последний элемент
>>> ь
bytearray(b'strin')
>» del Ь[ :2) # Удаляем первый и второй элементы
>>> ь
bytearray(b'rin')
♦ remove (<Число>) - удаляет из текущей последовательности первый встреченный эле
мент, хранящий указанное число. Если элемент не найден, возбуждается искточение
ValueError. Пример:
>>> Ь = bytearray("strstr", "ascii")
>>> b.remove(b"s"[O]) # Удаляет только первый элемент
>>> ь
bytearray(b'trstr')
♦ reverse() - изменяет порядок следования элементов текущей последовательности на
противоположный:
>>> Ь = bytearray("string", "ascii")
>>> b.reverse(); Ь
bytearray(b'gnirts')
128 Часть /. Основы языка Python
Преобразовать значение типа bytearray в строку позволяет метод decode(). Метод имеет
следующий формат:
decode( [ encoding="utf-8"] [, errors="strict"] )
Параметр encoding задает кодировку символов (по умолчанию UTF-8) в текущей последо
вательности, а параметр errors - способ обработки ошибок при преобразовании: "strict"
(значение по умолчанию), "replace" или "ignore". Пример:
>>> Ь = bytearray("cтpoкa", "ср1251")
>>> b.decode(encoding="cpl251"), b.decode("cpl251")
('строка', 'строка')
Для преобразования также можно воспользоваться функцией str():
>>> Ь = bytearray("cтpoкa", "ср1251")
>>> str(Ь, "ср1251")
'строка'
>>> pickle.loads(b'\x80\x04\x95\x0e\x00\x00\x00\x00\x00\x00\x00(K\x06K\�
x07K\x08K\tK\nt\x94. ')
(6, 7, 8, 9, 10)
Модуль предоставляет следующие функции: mdS (), shal(), sha224(), sha256(), sha384(),
sha512(), sha3_224(), sha3_256(), sha3_384(), sha3_512(), shake_l28() и shake_256() .
В качестве необязательного параметра функциям можно передать последовательность бай
тов, на основе которой следует вычислить хеш:
>>> import hashlib
>>> hl = hashlib.sha512(b"password")
Получить хеш в виде последовательности байтов типа ь�еs и строки позволяют методы
соответственно digest() и hexdigest():
>>> dl = hl.digest()
>>> dl
b'\xЫ\t\xf3\xЬb\xbc$N\xb8$A\x91~\xd0ma\x8b\x90\x08\xdd\t\xb3\xbe\�
xfd\xlbл \x079Lpj\x8b\xЬ9\x80\xЫ\xd7xл Yv\xec\x04\x9bF\xdf_\xl3&\xafZ.\�
xa6\xdl\x03\xfd\x07\xc9S\x85\xff\xab\x0c\xac\xЬc\x86'
>>> hl.hexdigest()
'Ы09f3bbbc244eb82441917ed06d618b9008dd09b3befdlb5e07394c706a8bb980Ыd7�
785e5976ec049b46df5fl326af5a2eaбdl03fd07c95385ffab0cacbc86'
>>> dl == h2.digest() # Имитируем сверку хеша пароля, введенного
>>> # пользователем, с хешем сохраненного пароля
True
>>> # Пользователь ввел верный пароль!
ПРИМЕЧАНИЕ
Функции Ыаkе2Ь() и Ыake2s() поддерживают большое количество параметров, которые
применяются только в специфических случаях. Полное описание этих функций можно най
ти в документации по Python.
Функция хеширования pbkdf'2_hrnac(), поддерживавшаяся в предыдущих версиях Python,
начиная с Python 3.1 О, объявлена устаревшей и не рекомендуется к использованию.
ГЛАВА 7
Регулярные выражения
Регулярное выражение - это шаблон, применяемый для поиска совпадающих.с ним фраг
ментов в строках. Регулярное выражение состоит как из обычных знаков, так и всевозмож
ных специальных символов: метасимволов, квантификаторов и др.
В Python использовать регулярные выражения позволяет модуль re, который необходимо
предварительно подкточить с помощью инструкции:
import re
ПРИМЕЧАНИЕ
,Флаги u и UNICODE, включающие режим соответствия классов \w, \W, \Ь, \В, \d, \О, \s и \S
Uniсоdе-символам, сохранены в Python лишь для совместимости с ранними версиями этого
языка и никакого влияния на обработку регулярных выражений не оказывают.
�
♦ L или LOCALE - учитываются настройки текущей локали. Флаг принимается во внима
ние, только если шаблон регулярного выражения задан в виде последовательности бай
тов типа bytes или bytearray.
Шаблоны регулярных выражений удобно записывать в виде необрабатываемых строк (со
держащих модификатор r):
р = re.compile(r"л\w+$")
димо найти именно точку, то перед точкой нужно указать символ \ или разместить точку
внутри квадратных скобок: [ . J • Продемонстрируем это на примере проверки правильности
введенной даты (листинг 7.1).
Листинr
р = re.compile(r"л (0-3] (0-9] [.] (01] (0-9] [.] (12] (09] (0-9] [0-9] $")
# Точка внутри квадратнь� скобок
if p.search(d):
print("Дaтa введена правильно")
else:
print("Дaтa введена неправильно")
# Вьrnедет: Дата введена неправильно
input()
и s (или DOTALL);
♦ $ - к концу строки или подстроки. Поведение зависит от флагов м (или МULTILINE)
и s (или DOTALL);
♦ \А- к началу строки (не зависит от флагов);
♦ \Z - к концу строки (не зависит от флагов).
Если указан флаг м (или MULTILINE), то при поиске в строке, состоящей из нескольких под
строк, разделенных символом \n, метасимвол л обозначает привязку к началу каждой такой
подстроки (сразу после символа \n), а метасимвол $ - к концу каждой подстроки (непо
средственно перед символом \n):
134 Часть /. Основы языка Python
Если убрать привязку к началу и концу строки, то любая строка, содержащая хотя бы одну
цифру, будет распознана как «Число» (листинг 7.3).
Можно указать привязку только к началу или только к концу строки (листинг 7.4).
Также поддерживаются два метасимвола, указывающие привязку к началу или концу слова:
♦ \Ь - к началу слова (началом слова считается пробел или тобой символ, не являющий-
ся буквой, цифрой или знаком подчеркивания);
♦ \В -к позиции, не являющейся началом слова.
Рассмотрим несколько примеров:
>>> р = re.compile(r"\bpython\b")
>>> print("Haйдeнo" if p.search("python") else "Нет")
Найдено
>>> print("Haйдeнo" if p.search("pythonware") else "Нет")
Нет
>>> р = re.compile(r"\Bth\B")
>>> print("Haйдeнo" if p.search("python") else "Нет")
Найдено
>>> print("Haйдeнo" if p.search("this") else "Нет")
Нет
В квадратных скобках [J можно перечислить символы, которые могут встречаться на этом
месте в строке, или указать диапазон таких символов через дефис:
♦ (09) - соответствует числу О или 9;
♦ (0-9) - соответствует mобому числу от О до 9;
♦ [абвJ -соответствует буквам «а», «б» и «в»;
♦ [а-гJ -соответствует буквам «а», «б», «в» и «r»;
♦ [а-яёJ - соответствует тобой букве от «а» до «я»;
♦ [АБВJ - соответствует буквам «А», «Б» и «В»;
♦ [А-ЯЁJ - соответствует тобой букве от «А» до «Я»;
♦ [а-яА-ЯеЁJ -соответствует тобой русской букве в mобом регистре;
♦ [О-9а-яА-ЯеЁа-zА-ZJ -mобая цифра и mобая буква независимо от регистра и языка.
ВНИМАНИЕ!
Буква«�» не входит в диапазон [а-я], а буква с&» - в диапазон [А-Я].
Значение в скобках инвертируется, если после первой скобки вставить символ л, что позво
ляет указать символы, которых не должно быть на этом месте в строке:
♦ [л 09J - не цифра О или 9;
♦ [л О-9) - не цифра от О до 9;
♦ [л а-яА-ЯеЁа-zА-Z] -не буква.
136 Часть /. Основы языка Python
Как вы уже знаете, точка теряет свое специальное значение, если ее заюпочить в квадрат
ные скобки. Кроме того, внутри квадратных скобок могут встретиться символы, которые
имеют специальное значение (например, л и -). Символ л теряет свое специальное значщrие,
если он не расположен сразу после открывающей квадратной скобки. Чтобы отменить спе
циальное значение символа -, его необходимо указать после всех символов, перед закры
вающей квадратной скобкой или сразу после открывающей квадратной скобки. Все специ
альные символы можно сделать обычными, если перед ними указать символ \.
Метасимвол I позволяет сделать выбор между альтернативными значениями. Выражение
n I m соответствует одному из символов: n или m, например:
>>> р = re.compile(r"кpacн( (ая) 1 (ое))")
>>> print("Haйдeнo" if р.search("красная") else "Нет")
Найдено
>>> print("Haйдeнo" if р.search("красное") else "Нет")
Найдено
>>> print("Haйдeнo" if р.sеаrсh("красный") else "Нет")
Нет
Класс символов - это специальный символ, обозначающий какой-либо символ из опреде
ленного набора, символ с указанным Unicode-кoдoм или именем:
♦ \d-любая цифра. При указании флага А (АSсп) эквивалентен [О-9];
♦ \w - любая буква, цифра или символ подчеркивания. При указании флага А (АSсп)
эквивалентен [a-zA-Z0-9_];
♦ \s-любой пробельный символ. При указании флага А (ASCII) эквивалентен [\t\n\r\f\v];
♦ \D- любой символ, не являющийся цифрой. При указании флага А (АSсп) эквивалентен
[ Л О-9];
♦ \W - любой символ, не являющийся буквой, цифрой или подчеркиванием. При указании
флага А (ASCII) эквивалентен [л a-zA-Z0-9_:J;
♦ \s-любой символ, не являющийся пробельным. При указании флага А (АSсп) эквива-
лентен [ л\t\n\r\f\v].
ПРИМЕЧАНИЕ
Упомянутые здесь классы трактуются довольно широко. Так, класс \d соответствует не
только десятичным цифрам, но и другим цифрам из кодировки Unicode (например, дробям),
класс \w включает буквы не только латиницы, но и других алфавитов, а класс \s охватыва
ет также неразрывные пробелы. Поэтому, если необходимо явно указать набор требуемых
символов, лучше привести эти символы внутри квадратных скобок, а не использовать
классы.
♦ \u<nnnn> - символ с 16-битным Unicode-кoдoм <nnnn>. Например, \u04За соответствует
русской букве к;
♦ \U<nnnnnnnn>-символ с 32-битным Unicode-кoдoм <nnnnnnnn>;
♦ \N {<name>) (начиная с Python 3.8)- символ с Uniсоdе-именем <name>. Например,
\N {Registered Sign) соответствует знаку зарегистрированной торговой марки®.
Квантификатор - специальный символ, задающий количество экземпляров указанного
символа, которое должно присутствовать в строке:
♦ {n) - строго n экземпляров. Например, шаблон r"л [0-9] (2)$" соответствует двум вхо
ждениям любой цифры;
Глава 7. Регулярные выражения 137
♦ {n,} - n или более вхождений символа в строку. Например, шаблон r"л [0-9] (2, )$"
соответствует двум и более экземплярам любой цифры;
♦ {n,m) - не менее'n и не более m экземШIЯрам. Значения количества указьшаются через
запятую без пробела. Например, шаблон r"л[О-9] (2,4)$" соответствует 2-4 экземШIЯ
рам любой цифры;
♦ * - ноль или больше экземШIЯров. Эквивалентно комбинации {о, );
♦ + - один или больше экземШiяров. Эквивалентно комбинации {1, );
♦ ? - ноль или один экземШIЯр. Эквивалентно комбинации {о, 1).
Все квантификаторы являются «жадными». При поиске соответствия ишется самая длинная
подстрока, соответствующая шаблону, и не учитываются более короткие соответствия. Для
примера получим содержимое всех тегов <Ь> вместе с самими тегами:
>>> s = "<b>Textl</b>Text2<b>Text3</b>"
>>> р ·= re.compile(r"<b>.*</b>", re.S)
»> p.findall(s)
[ '<b>Textl</b>Text2<b>Text3</b>']
Вместо желаемого результата мы получили всю строку. Чтобы ограничить «жадность»,
необходимо после квантификатора указать символ ?, например:
>>> s = "<b>Textl</b>Text2<b>Text3</b>"
>>> р = re.compile(r"<b>.*?</b>", re.S)
»> p.findall(s)
[J<b>Textl</b>', '<Ь>ТехtЗ</Ь>']
Этот код вывел то, что мы искали. Если необходимо получить содержимое без тегов, то
нужный фрагмент внутри шаблона следует разместить внутри круглых скобок:
>>> s = "<b>Textl</b>Text2<b>Text3</b>"
>>> р = re.compile(r"<b>(.*?)</b>", re.S)
»>.p.findall(s)
['Textl', 'ТехtЗ']
Круглые скобки используются для групп внутри регулярных выражений. Фрагменты, соот
ветствующие группам, будут запоминаться и станут доступны в результатах поиска (это
называется захватом фрагмента).
Чтобы избежать захвата фрагмента, после открьшающей круглой скобки в группе следует
разместить символы ? : (вопросительный знак и двоеточие):
>>> s = "test text"
»> р = re.compile(r"([a-z]+((st) 1 (xt)))", re.S)
»> p.findall(s)
[('test'' 'st'' 'st'' '')' ('text'' 'xt'' ''' 'xt')]
»> р = re.compile(r"([a-z]+(?:(?:st)l(?:xt)))", re.S)
»> p.findall(s)
['test', 'text' ]
В первом примере мы получили список с двумя элементами. Каждый элемент списка явля
ется кортежем, содержащим четыре элемента. Все эти элементы соответствуют группам,
заключенным в шаблоне в круглые скобки: первый элемент соответствует первому фраг
менту, второй - второму и т. д. Три последних элемента кортежа являются лишними. Что
бы они не включались в результаты поиска, во втором примере мы добавили символы ? :
138 Часть /. Основы языка Python
после каждой открывающей круглой скобки. В результате список состоит только из фраг
ментов, полностью соответствующих регулярному выражению.
К найденному фрагменту можно обратиться с помощью механизма обратных ссылок. Для
этого в шаблоне регулярного выражения следует указать обратный слеш и порядковый но
мер нужной группы (начиная с 1) (например, \1). Для примера получим текст между одина
ковыми парными тегами:
>>> s = "<b>Textl</b>Text2<I>Text3</I><b>Text4</b>"
>>> р = re.compile(r"<([a-z]+)>(.*?)</\1>", re.S I re.I)
>>> p.findall(s)
[('Ь', 'Textl'), ('I', 'ТехtЗ'), ('Ь', 'Text4')]
Группам можно дать имена, создав тем самым именованные группы. Для этого после откры
вающей круглой скобки в группе следует указать комбинацию символов ?Р<Имя группы>.
Для примера разберем e-mail на составные части:
>>> email = "[email protected]"
>>> р = re.compile(r"""(?P<name>[a-z0-9_.-]+) # Название ящика
@ # Символ "@"
(?P<host>(?: [a-z0-9-]+\.)+[a-z]{2,6}) # Домен
""" , re.I 1 re.VERВOSE)
>>> r = p.search(email)
>>> r.group("name") # Название ящика
'test'
>>> r.group("host") # Домен
'mail.ru'
Чтобы внутри шаблона обратиться к фрагментам из именованных групп, используется сле
дующий синтаксис: (?P=name). Для примера получим текст между одинаковыми парными
тегами:
>>> s = "<b>Textl</b>Text2<I>Text3</I>"
>>> р = re.compile(r"<(?P<tag>[a-z]+)>(.*?)</(?P=tag)>", re.S I re.I)
»> p.findall(s)
[('Ь', 'Textl'), ('I', 'ТехtЗ')]
Кроме того, внутри круглых скобок могут быть расположены следующие конструкции:
♦ (?#<Комментарий>) - комментарий. Текст внутри круглых скобок игнорируется;
♦ (?=...) - положительный просмотр вперед. Выведем все слова, после которых распо
ложена запятая:
>>> ,s = "textl, text2, textЗ text4"
>>> р = re.compile(r"\w+(?=[,])", re.S I re.I)
»> p.findall(s)
['textl', 'text2']
♦ (? ! •••) - отрицательный просмотр вперед. Выведем все слова, после которых нет за
пятой:
>>> s = "textl, text2, textЗ text4"
»> р = re.compile(r"[a-z]+[0-9](?! [,])", re.S I re.I)
>>> p.findall(s)
['textЗ', 'text4']
Глава 7. Регулярные выражения 139
♦ (?<= ...)- положительный просмотр назад. Выведем все слова, перед которыми распо
ложена запятая с пробелом:
>>> s = 11 textl, text2, textЗ text4 11
»> р = re.compile(r 11 (?<=[ ,J [ ])[a-z]+[0-9]11 re.S I re.I)
,
»> p.findall(s)
['text2', 'textЗ']
♦ (?< ! ••• )- отрицательный просмотр назад. Вьmедем все слова, перед которыми распо
ложен пробел, но перед пробелом нет запятой:
>>> s = 11 textl, text2, textЗ text4 11
»> р = re.compile(r 11 (?<! [ ,]) ( [a-z]+[0-9]) 11 re.S I re.I)
,
>>> p.findall(s)
11 'text2') , ('', 'text4')]
[ ( 11' ,
[ 'word2', 'word4']
Мы получили только два слова вместо пяти. Первое и последнее слова не попали в резуль
тат, поскольку расположены в начале и в конце строки. Чтобы эти слова попали в результат,
необходимо добавить альтернативный выбор ( \s) - для начала строки и (\s $) - для
л
1 1
конца строки. Чтобв1 найденные выражения внутри круглых скобок не попали в результат,
следует добавить символы ?: после открывающей скобки, например:
»> re.findall(r 1 (?:л l\s)\-([a-z0-9]+ )(?:\s1$) 1
1 1, s, re.S I re.I)
['wordl ', 'wordЗ', 'word5 ']
В этом случае в результат не попали слова word2 и word4. Чтобы понять причину, рассмот
рим поиск по шагам. Первое слово успешно попадает в результат, т. к. перед дефисом рас
положено начало строки и после слова есть пробел. После поиска указатель перемещается,
и строка для дальнейшего поиска примет следующий вид:
1 -wordl
1 <Указатель>-wоrd2 -wordЗ -word4 -word5 1 1
140 Часть /. Основы языка Python
Обратите внимание, что перед фрагментом -word2 больше нет пробела и дефис не располо
жен в начале строки. Поэтому следующим совпадением окажется слово wordЗ, и указатель
снова будет перемещен:
"-wordl -word2 -wordЗ <Указатель>-wоrd4 -wordS "
Опять перед фрагментом -word4 нет пробела, и дефис не расположен в начале строки. По
этому следующим совпадением окажется слово wordS, и поиск будет завершен. Таким обра
зом, слова word2 и word4 не попадают в результат, поскольку пробел до фрагмента уже был
использован в предыдущем поиске. Чтобы этого избежать, следует воспользоваться поло
жительным просмотром вперед (?= ...), например:
»> re.findall(r"(?:"l\s)\-([a-z0-9]+)(?=\s1$)", s, те.S I re.I)
[ 'wordl', 'word2', 'wordЗ', 'word4', 'wordS']
Теперь все слова успешно попали в список совпадений.
import re
рrint("Введите слово 'stop' дпя получения результата")
summa = О
р = re.compile(r"л(-]?(0-9]+$", re.S)
while (х := input("Введите число: ")) != "stop":
if not p.search(x):
print("Необходимо ввести число, а не строку!")
continue # Переходим на следуюцую итерацию цикла
х = int(x) # Преобразуем строку в число
summa += х
print("Cyммa чисел равна:", summa)
input()
♦ lastgroup - имя последней груmп,1 или значение None, если эта группа не имеет имени
или поиск завершился неудачей:
>>> р = re.compile(r"(?P<num>[0-9]+)(?P<str>[a-z]+)")
>>> m = p.search("l23456string 67890text")
>>> m
<re.Match object; span=(0, 12), match='l23456string'>
>>> m.re.groups, m.re.groupindex
(2, mappingproxy({'num': 1, 'str': 2)))
>>> m.re.groups, m.re.groupindex["num"]
(2, 1)
>>> p.groups, p.groupindex
(2, mappingproxy({'num': 1, 'str': 2)))
>>> p.groups, p.groupindex["str"]
(2, 2)
>>> m.string
'123456string 67890text'
>>> m.lastindex, m.lastgroup
(2, 'str')
>>> m.pos, m.endpos
(0, 22)
♦ group([<Номер 1> 1 <Имя 1>[, . . ., <Номер n> 1 <Имя n> J J ) - возвращает фрагменты,
соответствующие группам. Если параметры не заданы или указано значение О, возвра
щается фрагмент, полностью соответствующий шаблону. Если указан номер или имя
груmп,1, возвращается фрагмент, совпадающий с этой группой. Можно указать несколь
ко номеров или имен групп - в этом случае возвращается кортеж, содержащий фраг
менты, что соответствует группам. Если нет группы с указанным номером или именем,
то возбуждается исключение IndexError. Примеры:
>>> р = re.compile(r"(?P<num>[0-9]+)(?P<str>[a-z]+)")
>>> m.= p.search("123456string 67890text")
>>> m.group(), m.group(0) # Полное соответствие шаблону
('123456string', '123456string')
>>> m.group(l), m.group(2) # Обращение по индексу
('123456', 'string')
>>> m.group("num"), m.group("str") # Обращение по имени
('123456', 'string')
>>> m.group(l, 2), m.group("num", "str") # Несколько параметров
(('123456', 'string'), ('123456', 'string'))
import re
email = inрut("Введите e-mail: ")
ре = r"л([a-z0-9_.-]+)@(([a-z0-9-]+\.)+[a-z]{2,6))$"
р = re.compile(pe, re.I I re.S)
m = р.search(email)
if not m:
print("E-mail не соответствует шаблону")
else:
print("E-mail", m.group(0), "соответствует шаблону")
рrint("ящик:", m.group(l), "домен:", m.group(2))
input()
Textl
Text3
>>> import re
>>> р = re.compile(r"<(?P<tagl>[a-z]+)><(?P<tag2>[a-z]+)>")
>>> p.suЬ(r"<\2><\l>", "Теги <br><hr>") # \<Номер>
'Теги <hr><br>'
>>> p.suЬ(r"<\g<2>><\g<l>>", "Теги <br><hr>") # \g<Номер>
'Теги <hr><br>'
>>> p.suЬ(r"<\g<tag2>><\g<tagl>>", "Теги <br><hr>") # \g<Имя>
'Теги <hr><br>'
В первом параметре можно указать ссылку на функцmо. В эту функцmо будет передаваться
объект класса мatch, соответствующий найденному фрагменту. Результат, возвращаемый
этой функцией, послужит заменяющим фрагментом. Для примера найдем все числа в стро
ке и прибавим к ним 10 (листинг 7.7).
р = re.compile(r"[0-9]+")
# Заменяем все вхождения
print(p.suЬ(repl, "2019, 2020, 2021, 2022"))
# Заменяем только первые два вхождения
print(р. suЬ(repl, "20.19, 2020, 2021, 2022", 2))
input()
Результат выполнения:
2029, 2030, 2031, 2032
2029, 2030, 2021, 2022
Вместо метода suЬ() можно воспользоваться функцией sub() . Формат функции:
re.suЬ(<illaблoн>, <Регулярное выражение, задающее замену>,
<Строка>[, <Максимальное количество замен>[, flags=0]])
В качестве параметров <шаблон> и <Регулярное выражение, задающее замену> можно указать
строки с регулярными выражениями или скомпилированные реГУ,лярные выражения. В па
раметре flags можно указать флаги, используемые в функции compile() . Для примера
поменяем два тега местами, а также изменим регистр букв (листинг 7.8).
р = r"<(?P<tagl>[a-z]+)><(?P<tag2>[a-z]+)>"
print(re.suЬ(p, repl, "<br><hr>"))
input()
Результат выполнения:
<HR><BR>
Метод suЬn( J аналогичен методу suЬ( J, но возвращает кортеж из двух элементов: изменен
ной строки и количества произведенных замен. Метод имеет следующий формат:
suЬn(<Регулярное выражение, задакхцее замену>, <Строка>[,
<Максимальное количество замен>])
Заменим все числа в строке на о:
>>> р = re.compile(r"[0-9]+")
>>> p.suЬn("0", "2019, 2020, 2021, 2022")
('О, О, О, О', 4)
Однако, если в числе элементов копируемого списка имеются другие списки (вложенные),
будут скопированы не сами эти списки, а ссьшки на них. Как говорят в таких случаях про
граммисты, будет создана поверхностная, копия. Рассмотрим пример:
>>> х= (1, (2, 3, 4, 5]] #Создали вложенный список
>>>у= list(x) # Якобы сделали копию списка
>>> х is у # Разные объекты
False
»>y[l][1]= 100 # Изменяем элемент вложенного списка
>>> х, у # Изменение затронуло переменную х ! ! !
( (1, (2, 100, 4, 5]], [1, (2, 100, 4, 5]])
Чтобы получить полную копию списка вместе с вложенными списками, следует воспользо
ваться функцией deepcopy() из модуля сору:
>>> import сору # Подключаем модуль сору
>>> х= (1, (2, 3, 4, 5]]
>>>у= copy.deepcopy(x) # Делаем полную копию списка
>>>y[l][1]= 100 # Изменяем элемент вложенного списка
>>> х, у # Изменился только список в переменной у
( (1, (2, 3, 4, 5]], (1, (2, 100, 4, 5]])
Функция deepcopy() создает копию каждого объекта, при этом сохраняя внутренmою
структуру списка. Иными словами, если в списке существуют два элемента, ссылающиеся
152 Часть /. Основы языка Python
на один объект, то будет создана копия объекта и элементы будут ссылаться на этот новый
объект, а не на разные объекты. Пример:
>>> import сору
>>> х= [1, 2]
>>> у= [х, х] # Два элемента ссылаются на один список
>>> у
[ [1, 2], [1, 2]]
>>> z= copy.deepcopy(y) # Сделали копию списка
>>> z[0] is х, z[l] is х, z[0] is z[l]
(False, False, True)
»> z[О] [О]= 300 # Изменили один элемент вложенного списка
>>> z # Значение изменилось сразу в двух вложенных списках!
[ [300, 2], [300, 2)]
>>> х # Начальный список не изменился
(1, 2)
Перед одной из переменных слева от оператора = можно указать звездочку - и тогда в этой
перемепиой будет сохранен список из «лишних» элементов. Если таких элементов нет, спи
сок будет пустым. Примеры:
>>> х, у, *z= [1, 2, 3); х, у, z
(1, 2, [3])
>>> х, у, *z = [1, 2, 3, 4, 5]; х, у, z
(1, 2, [3, 4, 5])
>>> х, у, *z= [1, 2]; х, у, z
(1, 2, [])
>>> *х, у, z= [1, 2); х, у, z
( [], 1, 2)
Глава В. Списки, кортежи, множества и диапазоны 153
С помощью среза можно изменить фрагмент списка. Если срезу присвоить пустой список,
то элементы, попавшие в срез, будут удалены. Примеры:
>>> arr = [1, 2, 3, 4, 5)
>>> arr[l:3] = [6, 7) # Изменяем значения элементов с индексами 1 и 2
>>> arr
[1, 6, 7, 4, 5)
>>> arr[l:3] = [] # Удаляем элементы с индексами 1 и 2
>>> arr
[1, 4, 5)
Объединить два списка _в один список позволяет оператор +. Результатом объединения
будет новый список. Пример:
>>> arrl [1, 2, 3, 4, 5)
>>> arr2 [6, 7, 8, 9)
>>> arr3 arrl + arr2
>>> arr3
[1, 2, 3, 4, 5, 6, 7, 8, 9)
Вместо оператора + можно использовать оператор +=. Следует учитывать, что в этом случае
элементы добавляются в текуший список. Пример:
>>> arr = [1, 2, 3, 4, 5)
>>> arr += [6, 7, 8, 9)
>>> arr
[1, 2, 3, 4, 5, 6, 7, 8, 9)
Еще списки поддерживают операции повторения (оператор * ), проверки на вхождение
(оператор in) и на невхождение (оператор not in):
Глава В. Списки, кортежи, множества и диапазоны 155
Как вы уже знаете, выражение внутри скобок может располагаться на нескольких строках.
Следовательно, предыдущий пример можно записать иначе:
>>> arr = [
(1, 2, 3],
(4, 5, 6],
(7, 8, 9]
Чтобы получить значение элемента из вложенного списка, следует указать два индекса:
»> arr [1] [1]
5
Вложенные списки также могут содержать вложенные списки. В этом случае для доступа
к элементам указывается несколько индексов подряд, например:
>>> arr = [ [1, ["а", "Ь"], 3], [4, 5, 6], [7, 8, 9] ]
>>> arr[0] [1][О]
'а'
>>>arr = [ (1, { "а": 10, "Ь": ["s", 5] } ] ]
>» arr[0J [1]["Ь"][О]
's'
Однако изменить список в цикле все же можно, если для генерирования индексов его эле
ментов воспользоваться функцией raпge(), возвращающей диапазон. Функция имеет сле
дующий формат:
range([<Начало>, ] <Конец> [, <Шаг>])
Первый параметр задает начальное значение диапазона. Если он не указан, используется
значение О. Во втором параметре указывается конечное значение. Следует заметить, что это
значение не входит в возвращаемый диапазон. Если параметр <Шаг> не указан, используется
значение 1. Для примера умножим каждый элемент списк� на 2:
arr = [1, 2, 3, 4]
for i in range(len(arr)):
arr[i] *= 2
print(arr) # Результат вьmолнения: [2, 4, 6, 8]
Также можно воспользоваться функцией enumerate (<Список>[, start=OJ), которая на каж
дой итерации цикла for возвращает кортеж из индекса и значения очередного элемента
указанного списка. Параметр start задает индекс элемента, с которого начнется перебор
списка. Умножим каждый элемент списка на 2:
arr = [1, 2, 3, 4]
for i, elem in enumerate(arr):
arr[i] *= 2
print(arr) # Результат выполнения: [2, 4, 6, 8]
Перебрать список можно и с помощью цикла с условием, но нужно помнить, что он выпол
няется медленнее цикла перебора последовательности. Для примера умножим каждый эле
мент списка на 2:
arr = [1, 2, 3, 4]
i, с = О, len(arr)
while i < с:
arr[i] *= 2
i += 1
print(arr) # Результат выполнения: [2, 4, 6, 8]
1.0
1.4142135623730951
1.7320508075688772
2.0
2.23606797749979
def func(elem):
""" Увеличение значения каждого элемента списка
return elem + 10 # Возвращаем новое значение
arr = [1, 2, 3, 4, 5)
print( list( map(func, arr) ) )
# Результат вьmолнения: [11, 12, 13, 14, 15)
arrl [1, 2, 3, 4, 5]
arr2 [10, 20]
arr3 [100, 200, 300, 400, 500]
print( list( map(func, arrl, arr2, arr3) ) )
# Результат вьmолнения: [111 , 222]
Если же параметру strict дано значение True, при передаче функции zip() последовательно
стей разного размера будет сгенерировано исключение valueError:
>>> list(zip([l, 2, 3], [4, 6], [7, 8, 9, 10], strict=True))
Traceback (most recent call last):
File "<pyshell#8>", line 1, in <module>
list(zip([l, 2, 3], [4, 6], [7, 8, 9, 10], st'rict=Тrue))
ValueError: zip() argument 2 is shorter than argument 1
Переделаем программу суммирования элементов трех списков из листинга 8.3, использовав
функцшо zip () (листинг 8.4).
arrl [1, 2, 3, 4, 5]
arr2 [10, 20, 30, 40, 50]
160 Часть /. Основы языка Python
def func(elem):
return elem >= О
arr = [l, 2, 3, 4, 5]
summa = reduce(func, arr)
# Последовательность: (1, 2) (3, 3) (6, 4) (10, 5)
print(summa) # Результат вьшолнения: 15
summa = reduce(func, arr, 10)
# Последовательность: (l·o, 1) (11, 2) (13, 3) (16, 4) (20, 5)
print(summa) # Результат вьшолнения: 25
summa = reduce(func, [], 10)
print(summa) # Результат вьпюлнения: 10 ·
Метод index() возвращает индекс элемента, имеющего указанное значение. Если значение
не входит в список, то возбуждается исключение ValueError. Примеры:
>>> arr = [1, 2, 1, 2, 1]
>>> arr.index(l), arr.index(2)
(О, 1)
>>> arr.index(l, 1), arr .index(l, 3, 5)
(2, 4)
>>> arr. index(3)
Traceback (most recent call last):
File "<pyshell#lб>", line 1, in <module>
arr.index(3)
ValueError: 3 is not in list
Узнать количество элементов списка с указанным значением позволяет метод
count(<Значение>):
>>> arr = [1, 2, 1, 2, 1]
>>> arr.count(l), arr.count(2)
(3, 2)
>>> arr.count(3) # Элемент не входит в список
о
С помощью функций max () и min(), описанных в разд. 5.2, можно узнать максимальное и
минимальное значения из всех, что входят в список, соответственно:
>>> arr = [1, 2, 3, 4, 5]
>>> max(arr), min(arr)
(5, 1)
Функция аnу(<Последовательность>) возвращает значение True, если в заданной последова
тельности существует хотя бы один элемент, который в логическом контексте возвращает
значение True. Если последовательность не содержит элементов, возвращается значение
False. Пример:
>>> any([O, None]), any([O, None, 1]), any([])
(False, True, False)
Если необходимо изменить порядок следования и получить новый список, следует восполь
зоваться функцией reversed (<Последовательность>) . Она возвращает итератор, который
можно преобразовать в список с помощью функции list() или генератора списков. При
меры:
>>> arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> reversed(arr)
<list reverseiterator object at 0x00FD5150>
>>> list(reversed(arr)) # Использование функции list()
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
>>> for i in reversed(arr): print(i, end=" ") # Вывод с помощью цикла
10 9 8 7 6 5 4 3 2 1
>>> [i for i iri reversed(arr)] # Использование генератора списков
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
Функция shuffle(<Список>) из модуля random перемешивает элементы списка случайным
образом. Результата не возвращает. Примеры:
>>> import random # Подключаем модуль random
>>> arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> random.shuffle(arr) # Перемешиваем список случайным образом
>>> arr
(2, 7, 10, 4, 6, 8, 9, 3, 1, 5]
ПРИМЕЧАНИЕ
Второй параметр функции shuffle() в Python 3.9 объявлен устаревшим и нерекомендо
ванным к применению. Его поддержка будет удалена в Python 3.11.
Метод sort() сортирует сам текущий список и не возвращает результата. Если необходимо
получить отсортированный список, а текущий оставить без изменений, следует воспользо
ваться функцией sorted() . Функция имеет следующий формат:
sоrtеd(<Последовательность>[, key=None] [, reverse=False])
В первом параметре указывается список, который необходимо отсортировать. Остальные
параметры эквивалентны параметрам метода sort() . Пример:
>>> arr = [2, 7, 10, 4, 6, 8, 9, 3, 1, 5]
>>> sorted(arr) # Возвращает новый список!
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> sorted(arr, reverse=Тrue) # Возвращает новый список!
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
>>> arr = ["единицаl", "Единый", "Единица2"]
>>> sorted(arr, key=str.lower)
['единицаl', 'Единица2', 'Единый']
168 Часть /. Основы языка Python
8.14. Кортежи
Кортеж - это неизменяемая пронумерованная последовательность, фактически - неиз
меняемый список.
Создать кортеж можно следующими способами:
♦ с помощью функции tuple( [<Последовательность>] ). Функция возвращает кортеж, со
ставленный из элементов заданной последовательности. Если параметр не указан, выда
ется пустой кортеж. Примеры:
>>> tuple() # Создаем пустой кортеж
()
>>> tuple("String") # Преобразуем строку в кортеж
('S', 't', 'r', 'i', 'n', 'g ')
>>> tuple( [1, 2, 3, 4, 5]) # Преобразуем список в кортеж
(1, 2, 3, 4, 5)
♦ приведя элементы создаваемого кортежа через запятую внутри круглых скобок (или без
скобок):
>>> tl () # Создаем пустой кортеж
>>> t2 (5,) # Создаем кортеж из одного элемента
>>> t3 (1, "str", (3, 4)) # Кортеж из трех элементов
>>> t4 1, "str", (3, 4) # Кортеж из трех элементов
>>> tl, t2, t3, t4
( () , (5,), (1, 'str', (3, 4)), (1, 'str', (3, 4)))
Обратите особое внимание на вторую строку примера. Чтобы создать кортеж из одного
элемента, необходимо в ко�ще указать запятую. Именно запятые формируют кортеж,
а не круглые скобки. Если внутри круглых скобок нет запятых, будет создан объект дру
гого типа:
>>> t = (5); type(t) # Получили число, а не кортеж!
<class 'int'>
>>> t = ("str"); type(t) # Получили строку, а не кортеж!
<class 'str'>
Четвертая строка в исходном примере также доказывает, что не скобки формируют кор
теж, а запятые. Помните, что для создания кортежа необходимо указать запятые.
Как и списки, кортежи поддерживают обращение к элементу по индексу, получение среза,
объединение (оператор +), повторение (оператор *), проверку на вхождение (оператор in)
и невхождение (оператор not in):
>>> t = (1, 2, 3, 4, 5, 6,· 7, 8, 9)
>>> t[O] # Получаем значение первого элемента кортежа
1
170 Часть /. Основы языка Python
Кортежи поддерживают уже знакомые нам по спискам функции len(), min(), max(), методы
index() и count():
>>> t = (1, 2, 3) # Создаем кортеж
»> len(t) # Получаем размер
3
>>> t = (1, 2, 1, 2, 1)
>>> t.index(l), t.index(2) # Ищем элементы в кортеже
(О, 1)
Будучи неизменяемыми, кортежи занимают меньше оперативной памяти и обрабатываются
быстрее, чем изменяемые списки. Поэтому по возможности рекомендуется предпочитать
кортежи спискам.
8.16. Диапазоны
Диапазон - это неизменяемая пронумерованная последовательность целых чисел, распо
ложенных между заданными начальным и конечным значениями и отличающихся друг от
друга на значение указанного шага.
Для создания диапазона применяется функция range():
raпge( [<Начало>, ] <Конец> [, <Шаг>] )
Первый параметр задает начальное значение - если он не указан, используется значение О.
Во втором параметре указывается конечное значение. Следует заметить, что это значение
не войдет в создаваемый диапазон. Если параметр <Шаг> не указан, используется значение 1.
Примеры:
176 Часть /. Основы языка Python
1 2 3 4 5 6 7 8 9
>>> r = range(l0, 110, 10)
>>> for i in r_: print(i, end " ")
10 20 30 40 50 60 70 80 90 100
>>> r = range(l0, 1, -1)
>>> for i in r: print(i, end = " ")
10 9 8 7 6 5 4 3 2
Преобразовать диапазон в список, кортеж, обычное или неизменяемое множество можно
с помощью функции list(), tuple(), set() или frozenset() соответственно:
>>> list(range(l, 10)) # Преобразуем в список
[1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> tuple(range(l, 10)) # Преобразуем в кортеж
(1, 2, 3, 4, 5, 6, 7, 8, 9)
>>> set(range(l, 10)) # Преобразуем в множество
{1, 2, 3, 4, 5, 6, 7, 8, 9)
Диапазоны поддерживают доступ к элементу по индексу, получение среза (в результате
возвращается также диапазон), проверку на вхождение и невхождение, функции len(),
min(), max(), методы index() и count():
>>> r = range(l, 10)
»> r[2], r[-1]
(3, 9)
»> r[2:4]
range(3, 5)
>>> 2 in r, 12 in r
(True, False)
>>> 3 not in r, 13 not in r
(False, True)
>>> len(r), min(r), max(r)
(9, 1, 9)
>>> r.index(4), r.count(4)
(3, 1)
Поддерживается ряд операторов, позволяющих сравнить два диапазона:
♦ = - возвращает тrue, если диапазоны равны, и False - в противном случае. Диапазо
ны считаются равными, если они содержат одинаковые последовательности чисел. При
меры:
>>> range(l, 10) range(l, 10, 1)
True
>>> range(l, 10, 2) range(l, 11, 2)
True
>>> range(l, 10, 2) range (1, 12, 2)
False
Глава 8. Списки, кортежи, множества и диапазоны 177
import itertools
О 1 2 3 4 5 6 7 8 9 10
>>> list(zip(itertools.count(), "абвгд"))
[(О, 'а'), (1, 'б'), (2, 'в'), (3, 'г'), (4, 'д')]
178 Часть /. Основы языка Python
а б в а б в а б в а
>>> list(zip(itertools.cycle( [О, 1]), "абвгд"))
((0, 'а'), (1, 'б'), (0, 'в'), (1, 'г'), (О, 'д')]
♦ repeat(<Значение>[, <Количество>]) - возвращает заданное значение указанное коли
чество раз. Если количество не указано, значение возвращается бесконечно. Примеры:
>>> list(itertools.repeat(l, 10))
(1, 1, 1, 1, .1, 1, 1, 1, 1, 1]
>>> list(zip(itertools.repeat(5), "абвгд"))
((5, 'а'), (5, 'б'), (5, 'в'), (5, 'г'), (5, 'д')]
('в', 'в'), ('в', 'г'), ('г', 'а'), ('г', 'б'), ('г', 'в'),
( 'г', 'г')]
>>> list(itertools .product('aб', 'вг'))
[('а', 'в'), ('а', 'г'), ('б', 'в'), ('б', 'г')]
>>> list(itertools.product('aб', 'вг', repeat=3))
[('а', 'в', 'а', 'в', 'а', 'в'), ('а', 'в', 'а', 'в', 'а', 'г'),
('а', 'в', 'а', 'в', 'б', 'в'), ('а', 'в', 'а', 'в', 'б', 'г'),
('а', 'в', 'а', 'г', 'а', 'в'), ('а', 1 в',· 'а', 'г', 'а', 'г'),
('а', 'в', 'а', 'г', 'б', 'в'), ('а', 'в', 'а', 'г', 'б', 'г'),
... Часть вывода пропущена
('б', 'г', 'б', 'г', 'б 1 , 'в'), ('б', 'г', 'б', 'г', 'б', 'г')]
Примеры:
>>> arrl, arr2, arr3 = [1, 2, 3], [4, 5], [6, 7, 8, 9]
>>> list(itertools.chain(arrl, arr2, arr3))
[1, 2, 3, 4, 5, 6, 7, в, 9]
>>> list(itertools.chain("abc", "defg", "hij"))
['а', 'Ь', 'с', 'd', 'е', 'f', 'g', 'h', 'i', 'j ']
>>> list(itertools.chain("abc", ["defg", "hij"]))
['а', 'Ь', 'с', 'defg', 'hij' ]
Если функция не указана, в качестве ключа будет использовано значение элемента по
следовательности:
>>> for i, j in itertools.groupby(arr):
print(str(i) + ": " + str(list(j)), end=" ")
-4: [-4] -20: [-20] -4: [-4] 2: [2] 5: [5] 7: [7] 1: [1]
ГЛАВА 9
Словари
Словарь - это изменяемый набор значений (элементов), каждому из которых дана уни
кальная пометка, назьmаемая ключом. В качестве ключа может выступать произвольное
значение любого типа, но практически всегда используются строки. Зная ключ, можно из
влечь из словаря соответствующий ему элемент. Кqличество элементов в словаре называет
ся размером.
Словари, а также другие подобные им типы данных назьmаются отображениями, посколь
ку в них каждый ключ отображается на соответствующий ему элемент.
Для сортировки ключей также можно воспользоваться функцией sorted() (листинг 9.3).
Так как на каждой итерации возвращается ключ словаря , функции sorted() можно передать
сам словарь, а не результат выполнения метода keys() (листинг 9.4).
а Ь
>>> dl.keys() 1 d2.keys() # Объединение
{'а'' 'с'' 'Ь', 'd')
>>> dl.keys() - d2.keys() # Разница
{'Ь')
>>> d2.keys() - dl.keys() # Разница
{'с'' 'd')
>>> dl.keys() & d2.keys() # Одинаковые КЛЮЧИ
{'а')
>>> dl.keys() л d2.keys() # Уникальные ключи
{'с'' 'Ь', 'd')
♦ values() - возвращает объект dict_values, содержащий все значения текущего слова
ря. Этот объект поддерживает итерации. Примеры:
>>> d = { "а": 1, "Ь": 2
>>> d.values() # Получаем объект dict values
dict_values( (1, 2])
>>> list(d.values()) # Получаем список значений
(1, 2]
>>> [ v for v in d.values()
(1, 2]
♦ items() - возвращает объект dict_items, содержащий кортежи с двумя элементами:
ключом и значением соответствующего элемента текущего словаря. Этот объект под
держивает итерации. Примеры:
>>> d = { "а": 1, "Ь": 2 )
>>> d.items() # Получаем объект dict items
dict_items([('a', 1), ('Ь', 2)])
Глава 9. Словари 191
>>> for k in reversed(d): print(k + " => " + str(d[k]), end=" ")
Python предоставляет средства для работы со значениями даты и времени, а также комбина
циями даты и времени (временнь1ми отметками). Кроме того, он содержит инструменты
для вывода календаря в виде текста или в формате HTML и измерения времени выполнения
определеmп,1х фрагментов программы для целей отладки.
import time
d = [ "понедельник", "вторник", "среда", "четверг",
"пятница", "суббота", "воскресенье" ]
196 Часть /. Основы языка Python
import time
import locale
locale.setlocale(locale.LC_ALL, "Russian_Russia.1251")
s = "Сегодня:\n%А %d %Ь %У %H:%M:%S\n%d.%m.%Y"
print(time.strftime(s))
input()
>>> t = datetime.timedelta(days=l0)
>>> dl + t, dl - t # Прибавляем и вычитаем 10 дней
(datetime.date(2022, 1, 23), datetime.date(2022, 1, 3))
>>> dl - d2 # Разница между датами
datetime.timedelta(days=l3)
>>> dl < d2, dl > d2, dl <= d2, dl >= d2
(False, True, False, True)
>>> dl == d2, dl ! = d2
(False, True)
Можно получить строковое представление даты с помощью функций str() и repr() :
>>> d = datetime.date(2022, 1, 13)
>>> str(d), repr(d)
('2022-01-13', 'datetime.date(2022, 1, 13)')
♦ ctime() - возвращает текущую дату в виде строки формата "%а %Ь %ct %Н:%М:%S %У":
>>> d = datetime.date(2021, 12, 17)
»> d.ctime()
'Fri Dec 17 00:00:00 2021'
♦ timetuple() - возвращает текущую дату в виде объекта struct_time:
>>> d = datetime.date(2021, 12, 17)
>>> d.timetuple()
time.struct_time(tm_year=2021, tm_mon=l2, tm_mday=17, tm_hour=0,
tm_min=0, tm_sec=0, tm_wday= 4, tm_yday=351, tm_isdst=-1)
204 Часть /. Основы языка Python
♦ toordinal () - возвращает текущую дату в виде количества дней, прошедших с 1-го года:
>>> d = datetime.date(2021, 12, 17)
>>> d.toordinal()
738141
♦ weekday() - возвращает порядковый номер дня в неделе (О - для понедельника, 6 -
для воскресенья):
>>> d = datetime.date(2021, 12, 17)
>>> d.weekday() # 4 - это пятница
4
♦ isoweekday() - возвращает порядковый номер дня в неделе (1 - для понедельника,
7 � для воскресенья):
>>> d = datetime.date(2021, 12, 17)
>>> d.isoweekday() # 5 - это пятница
5
ПРИМЕЧАНИЕ
Класс time поддерживает также методы dst(), utcoffset() и tzname(). За подробной
информацией по этим методам, а также по абстрактному классу tzinfo обращайтесь к до
кументации по модулю datetime.
Пример:
>>> datetime.datetime.fromisocalendar(2022, 2, 4)
datetime.datetime(2022, 1, 13, О, О)
♦ comЬine(<Дата>, <Время> [, <Временная зона>] ) - возвращает временную отметку, соз
данную на основе переданных ему даты и времени:
>>> d = datetime.date(2022, 1, 14)
>>> t = datetime.time(14, 35, 14)
>>> datetime.datetime.comЬine(d, t)
datetime.datetime(2022, 1, 14, 14, 35, 14)
♦ strptime(<Строка с датой>, <Строка формата>) - возвращает временную отметку, соз
данную на основе разбора заданной строки с датой в соответствии с указанной строкой
формата. Если строка с датой не соответствует формату, возбуждается искшочение
ValueError. Метод учитывает текущую локаль. Примеры:
>>> datetime.datetime.strptime("17.12.2021", "%d.%m.%Y")
datetime.datetime(2021, 12, 17, О, О)
>>> datetime.datetime.strptime("17.12.2021", "%d-%m-%Y")
... Фрагмент опущен ...
ValueError: time data '17.12.2021' does not match format '%d-%m-%Y'
Получить результат можно с помощью следующих атрибутов:
♦ year - год (число в диапазоне от MINYEAR до МАХУЕАR) ;
♦ month - месяц (число от 1 до 12);
♦ day- день (число от 1 до количества дней в месяце);
♦ hour- часы (число от О до 23);
♦ minute - минуты (число от О до 59);
♦ second - секунды (число от О до 59);
♦ microsecond- микросекунды (число от О до 999999);
♦ tzinfo - временная зона (объект класса tzinfo или значение None).
♦ fold- порядковый номер отметки времени (число О или 1 ).
Примеры:
>>> d = datetime.datetime(2022, 1, 14, 14, 25, 21, 998076)
>>> d.year, d.month, d.day
(2022, 1, 14)
>>> d.hour, d.minute, d.second, d.microsecond
(14, 25, 21, 998076)
Над временнь1ми отметками можно производить следующие операции:
♦ datetime2 = datetimel + timedelta - сложение временной отметки datetimel и вре
менного промежутка timedelta;
♦ datetime2 = datetimel - timedelta - вычитание из временной отметки datetimel вре
менного промежутка timedelta;
♦ timedelta = datetimel - datetime2 - возвращает разницу между временньrми отмет
ками datetimel и datetime2 в виде временного промежутка.
210 Часть /. Основы языка Python
• "seconds" - часы,
минуты и секунды;
• "milliseconds" - часы, минуты, секунды и миллисекунды (вычисляются путем
окрутления микросекунд до тысячных);
• "microseconds" - часы, минуты, секунды и микросекунды (выводятся всегда, даже
если их количество равно О).
Примеры:
>>> d = datetime.datetime(2022, 1, 14, 14, 25, 21, 998076)
>>> d.isoformat() # Разделитель не указан
'2022-01-14Т14:25:21.998076'
>>> d.isoformat(" ") # Пробел в качестве разделителя
'2022-01-14 14:25:21.998076'
>>> d.isoformat(timespec="minutes")
'2022-01-14Т14:25'
♦ ctime() - возвращает текущие дату и время в виде строки формата "%а %Ь %d %Н:%М:%S
%У":
>>> d = datetime.datetime(2022, 1, 14, 14, 25, 21, 998076)
>>> d.ctime()
'Fri Jan 14 14:25:21 2022'
ПРИМЕЧАНИЕ
Класс datetime также поддерживает методы astimezone(), dst(), utcoffset() и
tzname(). За подробной информацией по этим методам, а также по абстрактному классу
tzinfo обращайтесь к документации по модулю datetime.
Глава 10. Работа с датой и временем 213
tl = Timer(stmt=codel)
print("while:", tl.timeit(numЬer=l0000) )
code2 = """\
j = о
Глава 10. Работа с датой и временем 221
t2 = Tirner(stmt=code2)
print("for:", t2.timeit(nшnЬer=10000))
code3 = """\
j = sum(range(l, 10001))
t3 = Timer(stmt=code3)
print("sum:", t3.timeit(nшnЬer=10000))
input()
tl = Timer(stmt=codel)
print("append:", tl.repeat(repeat=3, nшnЬer=2000))
code2 = """\
arr2 = [str(i) for i in range(l, 10001)]
t2 = Timer(stmt=code2)
print("гeнepaтop:", t2.repeat(repeat=3, nшnЬer=2000))
input()
Функции
Если тело функции состоит из одного выражения, его можно набрать в одной строке с язы
ковой конструкцией def. Пример определения функции, не принимающей параметров и
выводящей в консоль случайное число:
>>> def rnd(): print(randorn.randorn())
Если в теле функции отсутствует инструкция, возвращающая результат, функция при вызо
ве все равно будет неявно возвращать в качестве результата значение None.
Обращение к ранее определенной функции называется вызовом. И встроенные, и пользова
тельские функции вызываются одинаково - записью выражения в формате:
<Имя функции>([<Значения параметров через запятую>])
Количество параметров в выражении вызова функции должно совпадать с количеством па
раметров в ее определении, иначе будет выведено сообщение об ошибке. Если вызываемая
функция не принимает параметров, все равно следует указать пустые круглые скобки. При
меры:
>>>#Вызываем функцию, принимакщую параметры
>>> division(l, 2)
1 / 2 0.5
>>> division(2, 1)
2 / 1 2.0
>>>#Вызываем функцию, не принимакхцую параметров
>>> rnd()
О.5221364008680583
Результат, возвращенный функцией, можно сохранить в какой0 либо переменной или ис
пользовать в дальнейших вычислениях:
224 Часть /. Основы языка Python
>>> n = division2(4, 6)
>>> n
'4 / 6 = О.6666666666666666'
>>> print("Получен результат: division2(7, 5))
Получен результат: 7 / 5 = 1.4
Также его можно IШгде не сохранять и никак не использовать - в таком случае результат
будет потерян. Так поступают, если результат выполнения функции не нужен для работы.
Пример:
division2(4, 6) # Результат вьmолнения функции потерян ·
Поскольку в теле функции для хранения полученных параметров используются специально
создаваемые локальнь1е переменные, значения параметров могут быть изменены в теле
функции - и зто не вызовет никаких сторонних эффектов в коде, вызвавшем функцию. Вот
пример:
>>> def func(a):
print("Внутри функции: а)
а = 20 # Изменяем значение параметра внутри функции
print("Bнyтpи функции: ", а)
Чтобы избежать ошибки, определения функций размещают в самом начале программы по
сле подключения модулей или в отдельном модуле (о модулях речь пойдет в главе 12).
Если определения функций располагаются в том же модуле, что и их вызовы, определения
функций часто отделяют от остального кода - для наглядности.
С помощью инструкции ветвления можно при выполнении заданного условия определить
одну функцию, а при его невыполнении - другую (листинг 11.1).
При вводе числа 1 мы получим сообщение "Вы ввели число 1", в противном случае -
"Альтернативная функция".
Если определение одной и той же функции встречается в программе несколько раз, будет
использоваться функция, которая была определена последней:
def echo():
print("Bы ввели число 1")
def echo():
рrint("Альтернативная функция")
echo() # Всегда выводит "Альтернативная функция"
с именами параметров, которые указаны в определении функции. Так, при вызове функции,
определенной в предыдущем примере, будут созданы локальные переменные а и ь.
Переменные, созданные вне тобых функций, называются глобш�ьными. Они доступны
в теле тобой функции - но только на чтение. Глобальную переменную можно создать как
перед определением функции, так и после него. Пример:
»> def func():
print(glob)
>>> glob = 10
>>> func ()
10
При попытке в теле функции присвоить глобальной переменной новое значение создается
одноименная локальная переменная, к которой в дальнейшем и будет выполняться обраще
ние:
>>> def func2():
glob = 20 # Будет создана локальная переменная glob
print(glob)
»> func2()
20
>>> glob # Значение глобальной переменной не изменилось
10
Однако попытка в теле функции присвоить значение локальной переменной после об
ращения к одноименной глобальной переменной приведет к возбуждению искточения
UnЬoundLocalError:
>>> def func():
print(glob)
glob = 20
>>> func ()
... Фрагмент пропущен ...
UnЬoundLocalError: local variaЫe 'glob' referenced before assignment
Чтобы значения глобальных переменных можно было изменять внутри функции, необхо
димо в теле функции объявить эти переменные глобальными с помощью языковой конст
рукции формата:
global <Имена переменных через запятую>
Продемонстрируем это на примере (листинг 11.2).
Результат выполнения:
Значение glob вне функции= 10
Значение glob внутри функции = 25
Значение glob после функции= 25
def func():
locall = 54
glob2= 25
print("Глобальные идентификаторы внутри функции")
print(sorted(globals().keys()))
рrint("Локальные идентификаторы внутри функции")
print(sorted(locals().keys()))
Результат выполнения:
Глобальные идентификаторы внутри функции
[' annotations builtins doc file loader
'_name_', '__package_', '_spec_', 'func', 'gloЫ', 'glob2']
Локальные идентификаторы внутри функции
['glob2', 'locall']
Глобальные идентификаторы вне функции
[' annotations builtins doc file loader
'_name_', '_package_', '_spec_', 'func', 'gloЫ',, /glob2']
♦ vars( [<Объект> J ) - если вызывается без параметра внутри функции, возвращает сло
варь с локальными идентификаторами. Если вызывается без параметра вне функции,
возвращает словарь с глобальными идентификаторами. При указании объекта в качестве
параметра возвращает идентификаторы этого объекта {эквивалентно вызову <Объект>.
_dict_). Пример использования этой функции можно увидеть в листинге 11.4.
1 Листмнr 11.4.:ИcliOl"I
def func():
locall = 54
228 Часть /. Основы языка Python
glob2 = 25 .
рrint("Локальные идентификаторы внутри функции")
print(sorted(vars() .keys()))
»> division(5)
5 / 2 2.5
>>> n=3 # Изменяем значение переменной
»> division(5) # Значение параметра по умолчанию не изменилось
5 / 2 2.5
Если в качестве значения по умолчанию указать значение изменяемого типа, это значение
будет сохраняться между вызовами функции:
>>> def func(a=[]):
a.append(2)
return а
>>> funcl(l, 2, 3, 4)
(1, 2, 3, 4)
>>> funcl(1, 2, 3, 4, 5, 6, "аЬс")
(1, 2, 3, 4, 5, 6, 'аЬс')
>>> def func2(**pars): print(pars)
232 Часть /. Основы языка Python
>>> func5(1, 2, 3, 4, 5)
1 (2, 3, 4, 5) 100
>>> func5(a= l0, с=20)
10 () 20
>>> func5 ()
1 () 100
Глава 11. Функции 233
1 Листи
def summa(a, Ь, с):
return а + Ь + с
seql = [1, 2, 3]
print(summa(.*seql)) # Распаковка списка
seq2 = (2, 3)
print(summa(1, *seq2)) # Можно совмещать указание
·# обычных параметров и распаковку
mpnl = {"а": 1, "Ь": 2, "с": 3)
print(summa(**mpnl)) # Распаковка словаря
seq3, mpn2 = (1, 2), {"с": 3)
print(summa(*seq3, **mpn2)) # Можно совмещать распаковку
# списков и словарей
Ли
def summa(x, у):
return х + у
»> dir(summa)
[' annotations builtins call class closure
code defaults delattr dict dir
'_doc_', '_eq__', '_foпnat_', '_ge_ _get_
_getattribute_ _globals_ _gt_ hash init
init suЬclass kwdefaults le lt module
name ne new _qualname_ reduce
reduce ех _repr_ setattr sizeof str
suЬclasshook ' ]
>>> summa. name
1
summa'
>>> summa. code .со varnames
('х', 'у')
>>> summa. doc
' Суммирование двух чисел '
1 Листи
f = lamЬda х, у=2: х+ у
print(f(5)) # Выведет: 7
print(f(5, 6)) # Выведет: · 11
При указании глобальной переменной внутри анонимной функции будет сохранена ссьmка
на эту переменную, а не ее значение в момент определения функции:
>>> х = 5
>>> func = lamЬda: х # Сохраняется ссылка, а не значение переменной х! ! !
>>> х = 80 # Изменили значение
>>> print(func()) # Выведет: 80, а не 5
Если необходимо сохранить именно текущее значение глобальной переменной, можно вос
пользоваться следующим способом:
>>> х = 5
>>> func = (lamЬda у: lamЬda: у)(х) # Сохраняется значение переменной х
>>> х = 80 # Изменили значение
>>> print(func()) # Выведет: 5
Во второй строке кода мы определили анонимную функцию с одним параметром, возвра
щающую ссылку на вложенную анонимную функцию. Далее мы вызываем первую функ
цию с помощью круглых скобок и передаем ей значение переменной х. В результате сохра
няется текущее значение переменной, а не ссьmка на нее.
Сохранить текущее значение глобальной переменной также можно, указав эту переменную
в качестве значения параметра по умолчанию:
>>> х "' 5
>>> func = lamЬda х=х: х # Сохраняется значение переменной х
>>> х = 80 # Изменили значение
>>> print(func()) # Выведет: 5
236 Часть /. Основы языка Python
11.3. Функции-генераторы
Функция-генератор при последовательных вызовах возвращает один за другим элементы
какой-либо последовательности. Для возврата элемента в таких функциях применяется язы
ковая конструкция формата:
yield <Возвращаемый элемент>
Напишем функцию, которая генерирует диапазон из заданного количества чисел и последо
вательно возвращает его элементы, возведенные в заданную степень (листинг 11.11).
i func(3, 3)
print(i. next ()) # Выведет: 1 (1 ** 3)
print(i. next ()) # Выведет: 8 (2 ** 3)
print(i. next () ) # Выведет: 27 (3 ** 3)
print(i. next () ) # Исключение Stopiteration
def gen(l):
for е in 1:
yield from range(l, е + 1)
1 = (5, 10]
for i in gen([5, 10]): print(i, end =" ")
1 Листинr·11.1
def gen2(n):
for е in range(l, n + 1):
yield е * 2
def gen(l):
for е in 1:
yield from gen2(e)
1 = (5, 10]
for i in gen([5, 10]): print(i, end =" ")
@deco
def func (х) :
return "х {0}".format(x)
print(func(10))
def deco(f):
рrint("Вызвана функция func()")
return f
def func(х):
return "х = {0}".format(x)
У определения изменяемой функции можно указать сразу несколько декораторов. Для при
мера укажем у функции func() два декоратора: decol () и deco2( J (листинг 11.17).
@decol
@deco2
def func(х):
return "х {0}".format(x)
print ( func(10))
Глава 11. Функции 239
def teqt_passw(p):
def deco(f):
if р == "10": # Сравниваем пароли
return f
else:
return lamЬda: "Доступ закрыт"
return deco # Возвращаем функцию-декоратор
@test_passw(passw)
def func():
return "Доступ открыт"
11.5. Рекурсия
Рекурсия - это вызов функцией самой себя. Рекурсmо удобно использовать для перебора
объекта с неизвестной структурой или для выполнения неопределенного количества опера
ций. Для примера рассмотрим вычисление факториала (листинг 11.19).
def factorial(n):
if n == О or n == 1: return 1
else:
return n * factorial(n - 1)
240 Часть /. Основы языка Python
while True:
х = input( "Введите число: ")
if x.isdigit(): # Если строка содержит только цифры
х = iлt(х) # Преобразуем строку в число
break # Выходим из цикла
else:
print("Bы ввели не число!")
рrint("Факториал числа {О)= {1}" .format(x, factorial(x)))
f1 funcl (10)
f2 funcl(99)
f1 () # Выведет: 10
f2() # Выведет: 99
Здесь мы определили функцmо funcl (), принимающую один параметр, а внутри нее -
вложенную функцmо func2(). Результатом выполнения функции funcl () станет ссьmка на
эту вложенную функцmо. Внутри функции func2() мы производим вывод значения пере
менной х, созданной в функции funcl().
Следует учитывать, что в момент определения функции сохраняются ссылки на перемен
ные, а не их значения. Например, если после определения функции func2() произвести из
менение переменной х, то будет использоваться это новое значение (листинг 11.21).
Глава 11. Функции 241
f1 = funcl (10)
f2 = funcl (99)
f1 () # Выведет: 30
f2() # Выведет: 30
f1 funcl (10)
f2 funcl (99')
f1 () ,# Выведет: 10
f2() # Выведет: 99
print(x)
х = ь # Можем изменить значение х в funcl()
return func2
f = funcl (10)
f(S) # Выведет: 10
f(12) # Выведет: 5
f(З) # Выведет: 12
При использовании языковой конструкции nonlocal следует помнить, что переменная обя
зательно должна существовать внутри функции-родителя. В противном случае будет выве
дено сообщение об ошибке.
Модуль-это любой файл с программным кодом. Любой модуль может использовать иден
тификаторы (переменные, функции и классы, о которых речь пойдет в главе 13), созданные
в другом модуле, выполнив процедуру подключения, или импорта, последнего.
В составе интерпретатора Python поставляется большой набор модулей, содержащих полез
ные переменные, функции и классы. Эти модули называются встроенными, а их совокуп
ность - стандартной библиотекой языка.
ПРИМЕЧАНИЕ
Модули можно создавать не только на самом Python, но и на языке С++, компилируя их
в машинный код. В стандартной библиотеке содержится ряд таких модулей.
Модуль Python представляется особым объектом, содержащим ряд атрибутов. Так, атрибут
_name_ содержит имя модуля в виде строки. У модуля, непосредственно запущенного на
исполнение, этот атрибут хранит строку "_main_". Пример:
print( name # Выведет: main
Проверить, является модуль непосредственно запущенным (главным модулем, или главной
программой) или импортированным, позволяет следующий код:
if name main
print(".Это главная программа")
else:
рrint("Импортированный модуль")
import math
def hasattr_math(attr):
if hasattr(math, attr):
return "Атрибут существует"
else:
return "Атрибут не существует"
print(hasattr_math("pi")) # Атрибут существует
print(hasattr_math("x")) # Атрибут не существует
Если имя модуля слишком длинное и его неудобно указывать каждый раз для доступа
к атрибутам, то можно дать модуmо псевдоним. После чего для доступа к модуmо следует
Глава 12. Модули, пакеты и импорт 245
х = 50
Оба модуля размещаем в одном каталоге и запускаем модуль с главной программой. Про
грамма выведет числа 50 и 22 - значения переменных с именем х, хранящихся в разных
модулях. Как видно из результата, одноименные переменные из разных модулей никак не
конфликтуют друг с другом.
Объект каждого импортированного модуля заносится в словарь modules из модуля sys. При
попытке импорта модуля сначала проверяется, есть ли этот модуль в упомянутом словаре,
и если он там есть, повторный импорт выполнен не будет.
Выведем кmочи словаря modules, предварительно отсортировав их (листинг 12.4).
Инструкция импорта требует явного указания объекта модуля. Задать имя модуля в виде
строки нельзя.
Чтобы подкmочить модуль, имя которого формируется программно, следует воспользовать
ся функцией _import_( <Имя модуля>) . Функция возвращает объект импортированного
модуля. Для примера импортируем модуль test.py с помощью функции _import_() (лис
тинг 12.5).
246 Часть /. Основы языка Python
ПРИМЕЧАНИЕ
При импорте модуля он всегда компилируется (о компиляции рассказывалось в разд. 1. 10).
Второй формат инструкции импорта позволяет разбить слишком длинный перечень импор
тируемых идентификаторов на несколько строк:
Глава 12. Модули, пакеты и импорт 247
Размещаем все три модуля в одном каталоге, запускаем модуль с главной программой и
смотрим на результат...
...согласно которому, переменная s получила значение из модуля module2.py, который был
импортирован последним. Это довольно опасная ситуация, чреватая возникновением труд-
248 Часть /. Основы языка Python
Затем напишем главную программу, которая будет его импортировать (листинг 12.13).
def _getattr_(name):
# При попытке обратиться к переменной с вьщаем значение переменной d.
# При обращении к другим переменным генерируем исключение.
if name == "с":
return d
else:
raise AttributeError
а = 10
Ь 20
d 10000
j
import module4
print(module4. dir ()) # Выведет: ('а', 'Ь', 'd')
print(module4.a) # Выведет: 10
print(module4.b) # Выведет: 20
print(module4.c) # Выведет: 10000
ПРИМЕЧАНИЕ
Обратите внимание на то, что каталоги должны существовать, в противном случае они не
будут добавлены в список sys.path.
При поиске модуля список sys.path просматривается от начала к концу. Поцск прекраща
ется после первого найденного модуля. Таким образом, если в каталогах C:\folder1 и
C:\folder2 существуют одноименные модули, то будет использоваться модуль из папки
C:\folder1, поскольку он расположен первым в списке путей поиска.
Список sys.path можно изменять программно с помощью соответствующих методов.
Например, добавить каталог в конец списка можно с помощью метода append() , а в его на
чало - с помощью метода insert() (листинг 12.16).
В этом примере мы добавили папку C:\folder2 в начало списка. Теперь, если в каталогах
C:\folder1 и C:\folder2 существуют одноименные модули, будет использоваться модуль из
папки C:\folder2, а не из папки C:\folder1, как в предыдущем примере.
Также можно указать полностью свои пути для поиска модулей, при этом список, храня
щийся в переменной sys.path, будет проигнорирован. Для этого достаточно поместить
в папку, где установлен Python, файл с именем python<nepвыe два числа из номера версии
Python>._pth (так, для Python 3.10 этот файл должен иметь имя python310._pth) или
python._pth, в котором записать все нужные пути в том же формате, который используется
Глава 12. Модули, пакеты и импорт 251
при создании файлов pth. Первый файл будет использоваться программами, вызывающими
библиотеку времени выполнения Python, в частности IDLE Shell. А второй файл будет счи
тан при запуске Руthоn-программы щелчком мыши на ее файле.
ВНИМАНИЕ!
В файл руthоп<первые два числа из номера версии Python>._pth обязательно следует вклю
чить пути для поиска модулей, составляющих стандартную библиотеку Python (их можно
получить из списка, хранящегося в переменной sys.path). Если этого не сделать, утилита
IDLE Shell вообще не запустится.
12.5. Пакеты
Пакет- это обычный каталог, содержащий модули и обязательный файл инициализации
_init_.py. Последний может быть пустым или содержать код, который будет выполнен при
первой операции импорта любого модуля из этого пакета.
Чтобы импортировать модуль, находящийся в па.кете, надо записать путь к этому модулю,
разделяя имена пакетов и самого модуля точками. Например, чтобы импортировать модуль
module2.py из пакета folder1\folder2, следует записать инструкцию:
import folderl.folder2.module2
Правда, переменная, создаваемая интерпретатором для хранения объекта импортированно
го модуля, получит имя, совпадающее с указанным путем к модулю:
folderl.folder2.module.func()
Поэтому для упрощения доступа к созданным в таком модуле идентификаторам рекоменду
ется задавать у модуля псевдоним:
import folderl.folder2.module2 as mod
mod.func()
или импортировать только нужные идентификаторы:
from folderl.folder2.module import func
func()
Также можно импортировать модуль из пакета, записав языковую конструкцию формата:
from <Путь к пакету> import <Модуль>[ as <Псевдоним>]
Например:
from folderl.folder2 import module
module.func ()
В качестве примера создадим следующую структуру каталогов и файлов:
main.py # Файл с главной программой
folderl\ # Каталог на одном уровне вложенности с main.py
init .ру # Файл инициализации
modulel.py # Модуль folderl\modulel.py
folder2\ # Вложенный каталог
init .ру # Файл инициализации
module2.py # Модуль folderl\folder2\module2.py
moduleЗ.py # Модуль folderl\folder2\module3.py
Содержимое файлов _init_.py приведено в листинге 12.18.
Теперь импортируем эти модули в главном модуле main.py и получим значение переменной
rnsg разными способами. Содержимое модуля main.py приведено в листинге 12.20.
input()
Здесь после ключевого слова frorn указывается лишь путь к пакету без имени модуля. В ре
зультате будут импортированы все модули, указанные в списке _:_ан_.
Чтобы импортировать модуль, расположенный в том же каталоге, что и импортирующий
модуль, перед именем модуля указывается точка, например:
frorn .rnodule irnport *
или
frorn . irnport rnodule
Чтобы импортировать модуль, расположенный в родительском каталоге, перед именем мо
дуля указываются две точки:
frorn ..rnodule irnport *
254 Часть /. Основы языка Python
или
from .. import module
Для примера создадим в каталоге C:\folder1\folder2\ модуль module4.py, чей код показан
в листинге 12.22.
Следует помнить очень важную вещь. Если при запуске Руthоn-программы в список
sys.path автоматически добавляется путь к каталогу с запущенным файлом, то при относи
тельном импорте внутри пакета этого не происходит. Для примера изменим содержимое
модуля module4.py на следующее:
import module2 # Ошибка! Поиск модуля по абсолютному пути
varl = "Значение из: {0}".format(module2.msg)
var2 = varЗ = var4 = О
Здесь мы пытаемся импортировать из модуля module4.py модуль module2.py, находящийся
в том же пакете. Однако раз путь к импортирующему модулю (и, соответственно, к содер
жащему его пакету) не был добавлен в его список sys .path, интерпретатор не найдет тре-
Глава 12. Модули, пакеты и импорт 255
буемый модуль и выдаст ошибку. А если по одному из путей поиска модулей находится
модуль с таким же именем, то он и будет импортирован и программа станет работать не так,
как нужно (и, скорее всего, «вылетит» с ошибкой).
Чтобы импортировать модуль из того же пакета, следует использовать такую инструкцию:
from . import module2
или указать полный путь относительно каталога, в котором находится главный модуль:
import folderl.folder2.module2 as module2
ГЛАВА 13
Объекты и классы
Атрибуты создаются в теле методов простым присваиванием им значений, для чего приме
няются инструкции формата:
<Объект>.<Атрибут> = <Значение атрибута>
В качестве <Объекта> следует использовать значение первого параметра, полученного мето
.1ом (как отмечалось ранее, обычно его называют self).
Кроме того, внутри методов можно обращаться к созданным ранее атрибутам текущего
объекта с целью получить их значения, применяя следующий синтаксис (известный под
названием точечной нотации):
<Объект>.<Атрибут>
и вызывать методы, использовав выражение формата:
<Объект>.<Метод>([Параметры через запятую])
При вызове метода следует указать только значения второго и последующих параметров,
передаваемых ему. Значение первого параметра (self) - ссьшка на текущий объект -
будет передано методу самим интерпретатором.
Определения классов должны предшествовать инструкциям, создающим на их основе объ
екты. Как правило, определения класса располагают в начале модуля или в отдельном мо
дуле, отделяют от остального кода и друг от друга пустыми строками - для наглядности.
При вводе определения класса в интерпретаторе, работающем в интерактивном режиме,
после ввода языковой конструкции class и нажатия клавиши <Enter> последующие строки
станут выводиться с отступом слева, и можно будет занести строку документирования, оп
ределения атрибутов и методов. При вводе определения метода после набора языковой кон
струкции def следующие строки станут выводиться с двойным отступом слева, и можно
будет занести тело метода. Завершив ввод тела очередного метода, следует уменьшить
отступ нажатием клавиши <Backspace>, чтобы ввести языковую конструкцию. def опреде
.1ения следующего метода. Завершив ввод всех методов, нужно нажать <Enter> дважды,
чтобы закончить ввод определения класса.
Пример определения класса Platfor.m, содержащего атрибуты narne (название программной
платформы) и type (тип платформы: клиентская или серверная, изначальное значение:
"Серверная"), методы setNarne(<Название>) (задает новое название у текущей платформы)
и getFullNarne() (возвращает строку с названием и типом текущей платформы):
>>> class Platfonn:
def setNarne(self, narne):
self.narne = narne # Создаем атрибут narne
self.type = "Серверная" # Создаем атрибут type
def getFullNarne(self):
return self.narne + " (" + self.type + ") "
Определив класс, на его основе можно создать произвольное количество объектов. Для соз
.1ания объекта класса примеряется следующий синтаксис:
<Класс>()
Возвращеннь1й объект класса необходимо присвоить какой-либо переменной, атрибуту дру
гого объекта, передать в качестве параметра функции или методу. Если этого не сделать,
созданный объект будет потерян.
Для примера создадим пару объектов только что определенного класса Platfonn:
258 Часть /. Основы языка Python
Любые атрибуты и методы, определенные в классе, доступны как изнутри этого класса, так
и извне его. Такие атрибуты и методы называются общедоступными. Искmочение состав
ляют лишь атрибуты и методы, чьи имена начинаются с двойного подчеркивания U -
они доступны только внутри класса (закрытые атрибуты и методы). Закрытые атрибуты
и методы будут описаны далее.
Определение класса представляет собой объект типа type (см. разд. 2.4):
>>> type(Platform)
<class 'type'>
>>> type(Platform) == type
True
Получить доступ к атрибутам класса можно как через сам класс (наиболее часто используе
мый вариант):
>>> Python.type
'Язык программирования'
>>> Python.version = "3.10.2"
>>> Python.version
'3.10.2'
так и через объекты этого класса:
>» р = Python()
>>> p.type
'Язык программирования'
Однако если попытаться изменить значение атрибута класса через объект, в этом объекте
будет создан одноименный атрибут экземпляра объекта:
>>> p.type "Среда разработки" # Якобы меняем значение атрибута класса type
>>> # В результате будет создан одноименный атрибут экземпляра объекта
>>> p.type
'Среда разработки'
>» р2 = Python() # Создаем еще один объект класса Python
>>> p2.type # Проверяем значение атрибута класса type
'Язык программирования'
Атрибут класса будет хранить одно и то же значение у всех объектов этого класса. Так, если
в атрибуте класса сохранить список, все объекты этого класса станут ссылаться на один и
тот же объект списка, например:
>>> class Platforms:
platform_list = [] # Атрибут класса, хранящий список
class MyClass:
def init (self, parl, par2): # Конструктор класса
self.attrl = parl
self.attr2 = par2
print("Вызван конструктор")
def del (self): # Деструктор класса
print("Вызван деструктор")
13.4. Наследование
Наследование - это создание одного класса (производного, или подкласса) на основе дру
гого (базового, или суперкласса). Производный класс получает все атрибутъr и методы, при
сутствующие в базовом классе.
Для определения производного класса применяется инструкция следующего формата:
class <Имя класса>(<Базовый класс>):
<Остальная часть определения класса>
Если и в базовом, и в производном классе присутствуют методы с одинаковыми именами,
при вызове такого метода из объекта производного класса будет вызван метод производно
го класса. То есть метод производного класса произведет перекрытие одноименного метода
из базового класса. Рассмотрим пример из листинга 13.2.
Листинr·1з.. 2. На�вание
class Classl:
def init (self):
рrint("Конструктор базового класса")
def funcl(self):
print("Meтoд funcl() класса Classl")
def func2(self):
print("Meтoд func2() класса Classl")
class Class2(Classl):
def init (self):
print("Конструктор производного класса")
Classl. init (self) # Вызываем конструктор базового класса
Глава 13. Объекты и классы 263
def funcl(self):
print("Метод funcl() класса Class2")
super() . funcl() # Вызываем метод базового класса
def func2(self):
print("Meтoд func2() класса Class2")
super(Class2, self) . func2() # Вызываем метод базового класса
Выведет:
�онструктор производного класса
�онструктор базового класса
,1етод funcl() класса Class2
:-Iетод funcl () класса Classl
:-Iетод func2() класса Class2
'1етод func2() класса Classl
def func4(self):
print("Метод func4() класса ClassЗ")
Метод funcl() определен в двух классах: Classl и ClassЗ. Так как вначале просматривают
ся все базовые классы, непосредственно указанные в определении текущего класса, метод
�uncl() будет найден в классе ClassЗ (поскольку он указан в числе базовых классов в опре
делении Class4), а·не в классе Classl.
Метод func2() также определен в двух классах: Class2 и ClassЗ. Так как класс Class2 стоит
первым в списке базовых классов, то метод будет найден именно в нем. Чтобы наследовать
метод из класса ClassЗ, следует указать это явным образом. Переделаем определение класса
Class4 из предыдущего примера и наследуем метод func2 () из класса ClassЗ (листинг 13.5).
асса
,..,.· ·-· .. .· ,
Вернемся к листингу 13.4. Метод funcЗ() определен только в классе ClassЗ, поэтому метод
наследуется от этого класса. Метод func4(), определенный в. классе ClassЗ, переопределя
ется в производном классе.
Если искомый метод найден в производном классе, то вся иерархия наследования просмат
риваться не будет.
Для получения кортежа с перечнем базовых классов можно воспользоваться атрибутом
_bases_ объекта определения класса. В качестве примера выведем базовые классы для
всех классов из предыдущего примера:
>>> print(Classl. bases
>>> print(Class2. bases
>>> print(ClassЗ . bases
>>> print(Class4. bases
Выведет:
(<class 'object'>,)
(<class ' main .Classl'>,)
(<class ' main .Classl'>,)
(<class ' main .Class2'>, <class ' main .Class3'>)
Глава 13. Объекты и классы 265
class Classl: х = 10
class Class2(Classl): pass
class Class3(Class2): pass
class Class4(Class3): pass
clas.s Class�(Class2): pass
class Classб(Class5): pass
class Class7(Class4, Classб): pass
с = Class7()
print(c.x)
Получить кортеж со всей цепочкой наследования позволяет атрибут _mro_ объекта опре
деления класса:
>>> print(Class7. mro
Результат выполнения:
(<class 1
main .Class7'>, <class ' main .Class4'>,
<class 1
main .ClassЗ'>, <class ' main .Classб'>,
<class 1
_main .Class5'>, <class ' main .Class2'>,
<class 1
main .Classl'>, <class 'object'>)
cl = Classl()
cl.methodl()
cl.mixin
1 -
method() # Classl поддерживает метод примеси
с2 = Class2()
с2 . methodl()
с2.method2()
с2 . mixin_method() # Class2 также поддерживает метод примеси
Результат:
Метод класса Classl
Метод примеси
Метод класса Classl
Метод класса Class2
Метод примеси
Примеси активно применяются в различ�1х дополнительных библиотеках - в частности,
в популярном веб-фреймворке Django.
class MyClass:
def init (self):
self.i = 20
def _getattr_(self, attr):
рrint("Вызван метод _getattr_()")
return О
с = MyClass()
# Атрибу� i существует
print(c.i) # Выведет: 20. Метод _getattr_() не вызывается
# Атрибут s не существует
print(c.s) # Выведет: Вызван метод _getattr_() О
♦ _getattribute_(self, <Имя атрибута>) - вызывается при обращении к mобому, даже
существующему атрибуту текущего объекта. Необходимо учитывать, что использование
точечной нотации (для обращения к атрибутам) внутри этого метода приведет к зацик
ливаншо. Чтобы избежать зацикливания, следует вызвать метод _getattribute_()
объекта object и внутри этого метода вернуть значение атрибута или сгенерировать
искmочение Attr1buteError. Пример:
class MyClass:
def init (self):
self.i = 20
def _getattribute_(self, attr):
рrint("Вызван метод _getattribute ()")
return object._getattribute_(self, attr) # Только так!!!
с = MyClass()
print(c.i) # Выведет: Вызван метод _getatt,ribute_() 20
♦ _setattr_(self, <Имя атрибута>, <Значение>) - вызывается при попытке присваи
вания значения атрибуту текущего объекта. Если внутри метода необходимо присвоить
значение атрибуту, следует использовать словарь из атрибута _ctict_, поскольку при
применении точечной нотации метод _setattr_() будет вызван повторно, что приве
дет к зацикливаншо. Пример:
class MyClass:
def setattr (self, attr, value):
рrint("Вызван метод setattr ()")
self. dict [attr] = value # Только так! !!
с = MyClass()
c.i = 10 # Выведет: Вызван метод setattr ()
print(с.i) # Выведет: 10
с = MyClass()
print(len(c)) # Выведет: 50
♦ _bool_(self) - вызывается при использовании функции bool() применительно к те
кущему объекту. Должен возвращать логическое значение;
♦ _int_(self) - вызывается при преобразовании текущего объекта в целое число с по
мощью функции int(). Должен возвращать целое число;
♦ _float_(self) - вызывается при преобразовании текущего объекта в вещественное
число с помощью функции float(). Должен возвращать вещественное число;
♦ _complex_(self) - вызывается при преобраз�вании текущего объекта в комплексное
число с помощью функции complex(). Должен возвращать комплексное число;
♦ _round_(self, n) - вызывается при округлении текущего объекта функцией round().
Должен возвращать вещественное число;
♦ _index_(self) - вызывается при преобразовании текущего объекта в целое число при
извлечении среза, вызове функций Ьin(), oct() или hex(). Должен возвращать целое
число;
♦ _str_(self) - вызывается при использовании функций str() и print() примени
тельно к текущему объекту. Должен возвращать строку. Если отсутствует, будет исполь
зован метод _repr_() (описан далее). Пример:
class MyClass:
def init (self, m):
self.msg = m
def str (self):
return "Вызван метод str () {0}".format(self.msg)
с = МуСlаss("Значение")
print(str(c)) # Выведет: Вызван метод str () Значение
print(с) # Выведет: Вызван метод str () Значение
♦ _repr_(self) - вызывается при выводе текущего объекта интерпретатором, рабо
тающим в интерактивном режиме, и использовании функции repr() применительно
к текущему объекту. Должен возвращать строку. Пример:
cJ.ass MyClass:
def init (self, m):
self.msg = m
def _repr_(self):
return "Вызван метод _repr_() {0}".format(self.msg)
с = МуСlаss("Значение")
print(repr(c)) # Выведет: Вызван метод _repr_() Значение
♦ _hash_(self) - должен возвращать строковое представление текущего объекта, со
держащее только цифры. Такое представление можно получить, использовав функцию
hash(<Значение>). Этот метод следует переопределить, если объекты текущего класса
планируется использовать в качестве ключей словаря или внутри множества. Пример:
class MyClass:
def init (self, у):
self.x = у
def hash (self):
return hash(self.x)
Глава 13. Объекты и классы 269
с= MyClass(lO)
d = {}
d[c] = "Значение"
print(d[c]) # Выведет: Значение
Классы поддерживают еще несколько специальных методов, которые применяются лишь
в особых случаях и будут рассмотрены в главе 15.
class MyClass:
def init (self, у):
self.x = у
def add (self, у) : # Перегрузка оператора +
print("Объект слева")
return self.x + у
def radd (self, у) : # Перегрузка оператора +
print( "Объект справа")
return self.x + у
def iadd (self, у) : # Перегрузка оператора +=
print("Сложение с присваиванием")
self.x += у
return self
с = MyClass(S0)
print(c + 10) # Выведет: Объект слева 60
print(20 + с) # Выведет: Объект справа 70
с += 30 # Выведет: Сложение с присваиванием
print(c.x) # Выведет: 80
class MyClass:
def init (self):
self.x = 50
self.arr = [1, 2, З, 4, 5)
def _eq__(self, у): # Перегрузка оператора
return self.x = у
def contains (self, у): # Перегрузка оператора in
return у in self.arr
с = MyClass()
print("Paвнo" if с = 50 else "Не равно") # Выведет: Равно
print("Paвнo" if с = 51 else "Не равно") # Выведет: Не равно
print("Ecть" if 5 in с else "Нет") # Выведет: Есть
class MyClass:
@staticmethod
def funcl (х, у): . # Статический метод
return х + у
def func2(self, х, у): # Обычный метод
return х + у
272 Часть /. Основы языка Python
Метод класса похож на статический, только ему первым параметром передается ссылка на
текущий класс (не объект). Перед определением метода класса следует поставить декоратор
@classmethod. Пример использования методов класса пр иведен в листинге 1З .11.
class MyClass:
@classmethod
def func(cls, х): # Метод класса
print(cls, х)
Статические методы и методы класса часто пр именяются для выполнения каких-либо вы
числений общего характера или для создания специфических объектов класса, содержащих
определенные значения в своих атрибутах.
class Class2(Classl):
def func(self, х): # Перекрываем абстрактный метод
print(x)
с2 = Class2()
c2.func(50) # Выведет: 50
try:
сЗ = ClassЗ() # Ошибка. Метод func() не перекрыт
c3.func(50)
except TypeError as msg:
print(msg) # Can't instantiate aЬstract class ClassЗ
# with abstract methods func
Можно создавать абстрактные статические методы и абстрактные методы класса, для чего
необходимые декораторы указываются одновременно, друг за другом (листинг 13.13).
@classmethod
@aЬstractmethod
def class_func(self, х): # Абстрактный метод класса
pass
class MyClass:
slots [ "х", "у"]
def init (self, а, Ь):
self.x, self.y= а, Ь
с= MyClass(l, 2)
print(c.x, с.у) # Выведет: 1 2
с.х, с.у= 10, 20 # Изменяем значения атрибутов
print(c.x, с.у) # Выведет: 10 20
try: # Перехватываем исключения
c.z = 50 # Атрибут z не указан в slots
# поэтому генерируется исключение
except AttributeError as msg:
print(msg) # 'MyClass' object has no attribute 1 z1
13.10. Свойства
Свойство - это своего рода атрибут, при обращении к которому вызьmается один из трех
следующих методов, определенных в классе:
♦ геттер - вызывается при попытке получить значение свойства. Должен возвращать
результат, который и станет значением свойства;
Глава 13. Объекты и классы 275
class MyClass:
def get_var(self): # Геттер
return self . var
def set_var(self, value): # Сеттер
self. var = value
def del_var(self): # Делетер
del self. var
v = property(fget=get_var, fset=set_var, fdel=del_var, dос="Свойство")
с = MyClass()
c.v = 35 # Вызывается сеттер
print(c.v) # Вызывается геттер
del c.v # Вызывается делетер
class MyClass:
@property
def v(self): # Геттер
return self. var
@v.setter
def v(self, value): # Сеттер
self. var = value
@v.de1eter
def v(self): # Делетер
del self. var
с = MyClass()
c.v = 35 # Вызывается сеттер
print(c.v) # вызывается геттер
del c.v # вызывается делетер
Можно создать абстрактное свойство - в этом случае все реализующие его методы долж
нъ1 быть переопределены в производном классе. Абстрактное свойство создается с по
мощью декоратора @aЬstractrnethod и класса АБС из модуля аЬс (см. разд. 13. 8). Пример
показан в листинге 13 .17.
Иесо
:lass MyClass:
def init (self, value):
self.v = value
MyClass(S)
;;rint(c.v)
ГЛАВА 14
Исключения и их обработка
>>> "Строка".indех("текст")
Traceback (most recent call last):
File "<pyshell#S>", line 1, in <module>
"Строка".indех("текст")
ValueError: suЬstring not found
except NameError:
рrint("Неопределенный идентификатор")
except IndexError:
print("Несуществу�аций индекс")
рrint("Выражение после вложенного обработчика")
except ZeroDivisionError:
рrint("Обработка деления на 0")
х = о
print(x) # Выведет: О
try:
х = 1 / О
except (NameError, IndexError, ZeroDivisionError):
# Обработка сразу нескольких исключений
х = о
print(x) # Выведет: О
try:
х = 1 / О # Ошибка деления на О
except (NameError, IndexError, ZeroDivisionError) as err:
print(err. class__name # Имя класса исключения
print(err) # Текст сообщения об ошибке
Результат выполнения:
ZeroDivisionError
division Ьу zero
-----------pri nt_exception()-----------
Traceback (rnost recent call last):
File "D:/Dаtа/Документы/Работа/Книги/Руthоn 3 и PyQt 6 Разработка
приложений/FТР/14/14.5.ру", line 3, in <rnodule>
х = 1 / О
ZeroDivisionError: division Ьу zero
---------------print_tb() ---------------
File "D:/Dаtа/Документы/Работа/Книги/Руthоn 3 и PyQt 6 Разработка
приложений/FТР/14/14.5.ру", line 3, in <rnodule>
х = 1 / О
-----------forrnat_exception() ----------
['Traceback (rnost recent call last) :\n',
' File "D:/Dаtа/Документы/Работа/Книги/Руthоn 3 и PyQt 6 Разработка
приложений/FТР/14/14.5.ру", line 3, in <rnodule>\n х = 1 / 0\n',
'ZeroDivisionError: division Ьу zero\n']
-----�--forrnat_exception_only() --------
['ZeroDivisionError: division Ьу zero\n']
Если в языковой конструкции except не указан класс исключения, то записанный в ней блок
будет перехватывать все исключения. На практике следует избегать пустых инструкций
282 Часть /. Основы языка Python
except, иначе можно перехватить искточение, которое является лишь сигналом системе, а
не ошибкой. Пример перехвата всех искточений приведен в листинге 14.6.
Блок finally
Traceback (most recent call last):
File "<pyshell#17>", line 2, in <module>
х = 10 / О
ZeroDivisionError: division Ьу zero
Глава 14. Исключения и их обработка 283
Процесс ввода значений и получения результата выглядит так (значения, введенные поль
зователем, выделены полужирным шрифтом):
Зведите слово 'stop' для получения результата
Введите число: 10
Зведите число: str
�еобходимо ввести целое число!
Введите число: -5
Введите число:
Необходимо ввести целое число!
Введите число: stop
Сумма чисел равна: 5
Если искmочение не возникло, последние три параметра получат значение None, и метод
не должен возвращать результат. В противном случае метод должен вернуть значение
тrue, если возникшее искmочение было им обработано, или False, если он не обработал
искmочение (которое в этом случае будет передано вышестоящему обработчику).
Пример обработчика контекста приведен в листинге 14.9.
class MyClass:
def enter (self):
print("Бызван метод _enter_()")
return self
def exit (self, Туре, Value, Trace):
print("Вызван метод _exit_()")
if Туре is None: # Если исключение не возникло
рrint("Исключение не возникло")
else: # Если возникло исключение
print("Value =", Value)
return False # False - исключение не обработано
# True - исключение обработано
Результат выполнения:
Последовательность при отсутствии исключения:
Вызван метод enter ()
Блок внутри with
Вызван метод exit ()
Исключение не возникло
Глава 14. Исключения и их обработка 285
Некоторые встроенные классы (например, класс файла) также являются менеджерами кон
текста. Они будут рассмотрены в последующих главах.
FileNotFoundError
InterruptedError
IsADirectoryError
NotADirectoryError
PermissionError
ProcessLookupError
TimeoutError
RuntimeError
NotimplementedError, RecursionError
SyntaxError
IndentationError
TabError
SystemError
TypeError
ValueError
UnicodeError
UnicodeDecodeError, UnicodeEncodeError
UnicodeTranslateError
Warning
BytesWarning, DeprecationWarning, FutureWarning, ImportWarning,
PendingDeprecationWarning, ResourceWarning, RuntimeWarning,
SyntaxWarning, UnicodeWarning, UserWarning
Можно указать базовый класс для перехвата всех исключений соответствующих производ
ных классов. Например, указав базовый класс ArithmeticError, можно перехватывать ис
кmочения классов FloatingPointError, OverflowError и ZeroDivisionError:
try:
х = l / О # Ошибка: деление на О
except ArithmeticError: # Указываем базовый класс
рrint("Обработали деление на 0")
try:
raise ValueError("Oпиcaниe исключения")
except ValueError as msg:
print(msg) # Выведет: Описание исключения
Второй формат инструкции генерирует искmочение на основе указанного класса, при этом
объект искmочения создается автоматически:
try:
raise ValueError # Эквивалентно: raise ValueError()
except ValueError:
print ("Сообщение об ошибке")
Третий формат инструкции при возникновении одного искmочения (первичного) генери
рует другое (вторичное). Первичное искmочение сохраняется в атрибуте _cause_ объекта
вторичного искmочения. Пример использования этого формата можно увидеть в листин
ге 14.11.
try:
х = 1 / О
except Exception as err:
raise ValueError() from err
Результат
_ выполнения:
Traceback (most recent call last):
File "D:/Dаtа/Документы/Работа/КНИги/Руthоn 3 и PyQt 6 Разработка
приложений/FТР/14/14.11.ру", line 2, in <module>
х = 1 / О
ZeroDivisionError: division Ьу zero
The above exception was the direct cause of the following exception:
Результат выполнения:
Сообщение об ошибке
Traceback (most recent call last):
File "D:/Dаtа/Документы/Работа/КНИги/Руthоn 3 и PyQt 6 Разработка
приложений/FТР/14/14.12.ру", line З, in <module>
raise МуЕrrоr("Сообщение об ошибке")
MyError: ·сообщение об ошибке
class MyError(Exception):
def init (self, value):
self.msg = value
def str (self):
return self.msg
Результат выполнения:
Описание исключения
Описание исключения
Traceback (most recent call last):
File "D:/Dаtа/Документы/Работа/КНИги/Руthоn 3 и PyQt 6 Разработка
приложений/FТР/14/14.13.ру", line 14, in <module>
raise MyError("Описание исключения")
MyError: Описание исключения
Класс Exception подцерживает все необходимые методы для вывода сообщения об ошибке.
Поэтому в большинстве случаев для создания пользовательского исключения достаточно
определить пустой класс, производный от него (листинг 14.14).
Итераторы, контейнеры
и перечисления
Python позволяет создавать классы особого назначения: итераторы, контейнеры и перечис
ления.
15.1. Итераторы
Итератор - это класс, который при каждом обращении выдает очередной элемент задан
ной последовательности. Объект такого класса можно перебрать в цикле перебора последо
вательности. Можно сказать, что итератор аналогичен функции-генератору (см. разд. 11.3),
только является классом.
Чтобы превратить класс в итератор, следует переопределить в нем два специальных метода:
♦ _iter_ (self) - служит признаком того, что класс является итератором. Должен воз
вращать текущий объект. Как правило, этот метод выполняет всевозможные предуста
новки.
Если в классе определены методы _iter_ () и _getitem_() (о последнем будет рас
сказано позже), предпочтение отдается первому методу;
♦ _next_(self) - вызывается при выполнении каждой итерации и должен возвращать
очередной элемент последовательности. Если последовательность закончилась, в этом
методе следует сгенерировать исключение stopiteration, которое сообщит вызываю
щему коду о завершении перебора.
Рассмотрим класс, хранящий строку и на каждой итерации возвращающий очередной ее
символ, начиная с конца (листинг 15.1).
class ReverseString:
def init (self, s):
self. s = s
def iter (self):
self. i = О
return self
def next (self):
if self._i > len(self._s) - 1:
raise Stopiteration
Глава 15. Итераторы, контейнеры и перечисления 293
else:
а = self. s[-self. i - 1)
self. i = self. i + 1
return а
nohtyP
Также мы можем переопределить специальный метод _len() _) возвращающий размер
последовательности, и специальные методы _str() _ и _repr() _, выдающие строковое
представление объекта (были рассмотрены в разд. 13.5). Перепишем код нашего класса
итератора, добавив в него определение методов _len()_ и _str()_ (в листинге 15.2
часть кода опущена).
Листинr
class ReverseString:
15.2. Контейнеры
Контейнер - это класс с функциональностью либо пронумерованной последовательности
(контейнер-последовательность) с произвольным доступом к любому ее элементу
·' по его
индексу, либо отображения (контейнер-отображение).
15.2.1. Контейнеры-последовательности
Чтобы превратить класс в контейнер-последовательность, следует переопределить в нем
следующие специальные методы:
♦ _getitem_(self, <Индекс>) - вызывается при извлечении элемента последовательно
сти с заданным индексом и должен возвращать этот элемент. Если индекс не является
целым числом или срезом, метод должен генерировать исключение TypeError, а если
такого индекса не существует, - исключение IndexError;
294 Часть /. Основы языка Python
class MutaЫeString:
def init (self, s):
self._s = list(s)
# Реализуем функциональность итератора
def iter (self):
self. i = о
return self
def next (self):
if self. i > len(self._s) - 1:
raise Stopiteration
else:
а = self._s[self._i]
self. i = self. i + 1
return а
def len (self):
return len(self._s)
def -'str (self):
return "".join(self._s)
# Определяем вспомогательный метод, который будет проверять
# корректность индекса
def _iscorrectindex(self, i):
if type(i) == int or type(i) == slice:
if type(i) = int and i > self. len {) - 1:
raise IndexError
else:
raise TypeError
Глава 15. Итераторы, контейнеры и перечисления 295
15.2.2. Контейнеры-отображения
Превратить класс в контейнер-отображение можно, переопределив оrшсанные в разд. 15.2.1
методы: _getitem_(), _setitem_(), _delitem_() и _contains_(). При этом следует
учесть, что вместо индексов здесь будут использоваться кmочи произвольного типа (как
правило, строкового). А при обращении по несуществующему кmочу следует генерировать
искmочение KeyError.
Наrшшем класс Version, который будет хранить версию интерпретатора Python, разбитую
на части: старшая версия, младшая версия и модификация. Доступ к частям версии будет
осуществляться по строковым кmочам (листищ 15.4). Ради простоты функщюнальность
итератора реализовывать не станем, а также заблокируем операцию удаления элемента сло
варя, генерируя в методе_delitem_() искmочение NotimplementedError.
class Version:
def init (self, major, minor, suЬ):
self._major = major # Старшая версия
self. minor = minor # Младшая версия
self. suЬ = suЬ # Модификация
296 Часть /. Основы языка Python
15.3. Перечисления
Перечисление - это набор каких-либо именованных значений, назьmаемых элементами.
Перечисление можно рассматривать как своего рода словарь, тол�ко неизменяемый.
Перечисление представляет собой класс, содержащий непосредственные определения атри
бутов класса, каждый из которых представляет один из элементов. Имя атрибута класса
Глава 15. Итераторы, контейнеры и перечисления 297
>>> Colors.RED
<Colors.RED: l>
>>> Colors.WHITE
<Colors.WHITE: 6>
Однако выполнять арифметические операции с элементами такого перечисления нельзя,
поскольку их значения не являются целыми числами:
>>> Colors.GREEN + 3
... Фрагмент пропущен ...
TypeError: unsupported operand type(s) for +: 'Colors' and 'int'
Если конкретные значения элементов целочисленного перечисления не важны, для их
занесения можно использовать функцmо auto() из модуля enшn. Эта функция возвраща
ет последовательно увеличивающиеся целые числа, начиная с 1. Пример:
>>> from enшn import Enшn, auto
>>> class Colors2(Enшn):
RED = auto()
GREEN = auto()
BLUE = auto()
WНITE = auto()
>>> Colors2.RED
<Colors2.RED: l>
>>> Colors2.WHITE
<Colors2.WНITE: 4>
♦ IntEnшn - базовый класс для создания перечислений, элементы которых хранят лишь
целочисленные значения. Значения элементов преобразуются в целые числа и, следова
тельно, могут быть использованы в арифметических операциях и выражениях сравнения
с целыми числами. Пример:
>>> from enшn import IntEnшn
>>> class ColorsЗ(IntEnшn):
1RED = l
GREEN = 2
BLUE = 3
WНITE = RED + GREEN + BLUE
>>> ColorsЗ.GREEN
<ColorsЗ.GREEN: 2>
>>> ColorsЗ.GREEN + 3
5
>>> ColorsЗ.GREEN * ColorsЗ.BLUE
6
>>> ColorsЗ.WHITE == 6
True
♦ Flag - базовый класс для перечислений, элементы которых хранят цел·очисленные зна
чения. Значения элементов могут выступать в качестве операндов для двоичных опера
торов (описаны в разд. 3.2). Функция auto() при использовании в таком перечислении
последовательно выдает значения 1 и числа, являющиеся степенями числа 2 (2, 4, 8, 16
и т. д.). Пример:
Глава 15. Итераторы, контейнеры и перечисления 299
>>> Colors4.BLACK
<Colors4.BLACK: О>
>>> Colors4.RED
<Colors4.RED: l>
>>> Colors4.GREEN
<Colors4.GREEN: 2>
>>> Colors4.BLUE
<Colors4.BLUE: 4>
>>> Colors4.WHIТE
<Colors4.WHITE: 7>
>>> Colors4.RED & Colors4.GREEN
<Colors4.BLACK: О>
>>> Colors4.RED I Colors4.GREEN
<Colors4.GREENIRED: З>
>>> bool(Colors4.WHITE & Colors4.BLUE)
True
Однако при манипуляциях с элементами такого перечисления использовать в качестве
операндов двоичных операторов целые числа нельзя:
>>> Colors4.RED 1 2
... Фрагмент пропущен
TypeError: unsupported operand type(s) for 1: 'Colors4' and 'int'
♦ IntFlag - аналогичен Flag, только значения элементов преобразуются в целые числа.
Следовательно, при манипуляциях с элементами такого перечисления можно использо
вать в качестве операндов двоичных операторов целочисленные значения. Пример:
>>> from entш1 import IntFlag, auto
>>> class ColorsS(IntFlag):
BLACK = О
RED = auto()
GREEN = auto()
BLUE = auto()
WHITE = RED I GREEN I BLUE
>>> ColorsS.RED 1 2
<ColorsS.GREENIRED: 3>
>>> ColorsS.WHITE == 7
True
Все классы перечислений принадлежат типу EnumМeta из модуля entш1:
>>> type(Colors)
<class 'entш1.EnumМeta'>
300 Часть /. Основы языка Python
Действия, которые можно выполнять над элементами перечислений, были описаны ранее.
Помимо того, над элементами перечислений можно производить следующие операции:
♦ обращение к элементу в стиле словарей, используя имя элемента в качестве кточа:
>>> Frameworks["EXPRESS"]
<Frameworks.EXPRESS: 'Express'>
♦ обращение к элементу по его значению, записывая его в круглых скобках после имени
класса перечисления:
>>> Frameworks("Laravel")
<Frameworks.IARAVEL: 'Laravel'>
♦ получение имени атрибута класса перечисления, соответствующ�го заданному элементу,
и его значения из свойств элементов name и value соответственно:
>>> Frameworks.RAILS.name, Frameworks.RAILS.value
('RAILS'' 'RuЬy оп Rails')
♦ проверка на вхождение или невхождение элемента в· перечисление с помощью операто
ров in и not in соответственно:
>>> f = Frameworks.DJANGO
>>> f in Frameworks
True
>>> f not in Frameworks
False
>>> f in Colors
False
Перечисление можно использовать в качестве итератора (необходимая для этого функцио
нальность определена в базовом классе):
»> list(Colors)
[<Colors.RED: 1>, <Colors.GREEN: 2>, <Colors.BLUE: 3>, <Colors.WHITE: 6>]
>>> for f in Frameworks: print(f.value, end=" 1 ")
b'Stringl\r\n'
b'String2'
>>>#Текстовый подрежим (символ \r удаляется)
>>> with open(r"c:/book/file.txt", "r") as f:
for line in f:
print(repr(line))
'Stringl\n'
'String2'
Для ускорения работы производится буферизация записываемых данных. Информация из
буфера записывается в файл полностью только в момент закрытия файла или после вызова
функции или метода flush (). В необязательном параметре buffering можно указать размер
буфера. Если указано значение О, то данные будут сразу записьmаться в файл (допустимо
только в двоичном подрежиме). Значение 1 используется при построчной записи в файл
(допустимо только в текстовом подрежиме). Другое положительное число задает пример
нъ1й размер буфера, а отрицательное значение (или отсутствие значения) означает установ
ку размера, применяемого в системе по умолчанию. По умолчанию текстовые файлъ1 буфе
ризуются построчно, а двоичные - частями, размер которых интерпретатор выбирает са
мостоятельно в диапазоне от 4096 до 8192 байтов.
При чтении файла в текстовом подрежиме производится попытка преобразовать данные
в кодировку Unicode, а при записи выполняется обратная операция - строка преобразуется
в последовательность байтов в определенной кодировке. По умолчанию назначается коди
ровка, применяемая в системе. Если преобразование невозможно, генерируется исключе
ние. Указать кодировку, которая будет использоваться при записи и чтении файла, позволя
ет параметр encoding. Для примера запишем даннъ1е в кодировке UTF-8:
304 Часть /. Основы языка Python
Строка
При работе с текстовыми файлами в кодировках UTF-8, UTF-16 и UTF-32 следует учиты
вать, что в начале файла может присутствовать метка порядка байтов (ВОМ). Для коди
ровки UTF-8 эта метка является необязательной, и в предыдущем примере она не была
добавлена в файл при записи. Чтобы ВОМ были добавлена, в параметре encoding следует
указать значение utf__:8-sig. Запишем строку в файл в кодировке UTF-8 с ВОМ:
>>> f = open(r"c:/book/file.txt", "w", encoding="utf-8-sig ")
>>> f.write("Cтpoкa") # Записываем строку в файл
6
>>> f.close() # Закрываем файл
Теперь прочитаем файл с разными значениями параметра encoding:
>>> with open(r"c:/book/file.txt", "r", encoding="utf-8") as f:
for line in f:
print(repr(line))
'\ufeffCтpoкa'
>>> with open(r"c:/book/file.txt", "r", encoding="utf-8-sig ") as f:
for line in f:
print(repr(line))
'Строка'
Если неизвестно, есть ли ВОМ в файле, и необходимо получить данные без нее, то при чте
нии файла в кодировке UTF-8 всегда следует указывать кодировку utf-8-sig.
При указании кодировок utf-16 и utf-32 в параметре encoding обработка ВОМ производит
ся автоматически: при записи ВОМ автоматически вставляется в начало файла, а при чте
нии она игнорируется. Запишем строку в файл, а затем прочитаем ее из файла:
>>> with open(r"c:/book/file.txt", "w", encoding="utf-16") as f:
f.write("Строка")
6
>>> with open(r"c:/book/file.txt", "r", encoding="utf-16") as f:
for line in f:
print(repr(line))
'Строка'
При указании кодировок utf-16-le, utf-16-be, utf-32-le и utf-32-be ВОМ необходимо
добавить в начало файла самостоятельно, а при чтении удалить ее.
Глава 16. Работа с файлами и каталогами 305
В первом примере последний слеш экранирует закрывающую кавычку, что приводит к син
таксической ошибке. Решить эту проблему можно, удвоив последний слеш. Однако тогда
два слеша превратятся в четыре (см. второй пример). Так что в этой ситуации лучше ис
пользовать обычные строки, например:
>>> "C:\\temp\\new\\" # Правильно
'C:\\temp\\new\\'
>>> r"C:\temp\new\\" [ :-1] # Можно и удалить слеш
'C:\\temp\\new\\'
Вместо абсолютного пути (отсчитываемого от обозначения диска) можно указать относи
тельный, который отсчитывается от текущего рабочего каталога (о нем - позже). Возмож
ны следующие варианты:
♦ если открываемый файл находится в текущем рабочем каталоге, можно указать только
имя файла:
>>> open(r"filel.txt")
♦ если каталог с файлом расположен ниже уровнем, перед именем файла указываются две
точки и слеш (" ../"):
>>> open(r" ../file4.txt")
♦ если в начале пути расположен слеш, путь отсчитывается от корня текущего диска.
В этом случае местоположение текущего рабочего каталога не имеет значения:
»> open(r"/book/folder4/file4.txt")
»> open(r"/book/folder5/folder6/file5 .txt")
Задать в качестве текущего рабочего каталог с указанным путем можно вызовом функции
chdir(<Путь>) из модуля os:
>>> os.chdir(r"c:\book")'
Указать в качестве текущего рабочего каталог, в котором хранится запущенный Руthоn
файл, можно, вставив в код этого файла следующие инструкции:
:.mport os
os.chdir(os.path.dirname(os.path.abspath( file )))
Переменная _file_ хранит в разных случаях путь к запущенному файлу или только имя
файла без пути. Чтобы гарантированно получить полный путь к файлу, следует-передать
значение переменной в функцию abspath() из модуля os .path. Функция dirname() извлека
ет из переданного ей полного пути к файлу путь к хранящему его каталогу, который пере
.:rается функции chdir().
'Строкаl\nСтрока2'
>>>#Двоичный подрежим
>>> with open(r"c:\book\file.txt", "rb") as f:
f.read()
b'\xdl\xf2\xf0\xee\xea\xe0l\n\xdl\xf2\xf0\xee\xea\xe02'
В параметре можно указать количество символов или байтов, которое требуется прочи
тать из файла. Когда достигается конец файла, метод возвращает пустую строку или
пустую последовательность байтов. Пример:
>>> f = open(r"c:\book\file.txt", "r", encoding="cpl251")
>>> f .read(8) #Считываем 8 символов
'Строкаl\n'
>» f.read(8) #Считываем 8 символов
'Строка2'
>>> f.read(8) #Достигнут конец файла
11
>>> f.close()
Глава 1 б. Работа с файлами и каталогами 309
>>> f.close()
>>>#Двоичный подрежим
>>> f = open(r"c:\book\file.txt", "rb")
>>> f.readline(), f.readline()
(Ь'\xdl\xf2\xf0\xee\xea\xe01\n', Ь'\xdl\xf2\xf0\xee\xea\xe02') ,
>>> f.readline() #Достигнут конец файла
Ь',
>>> f.close()
В параметре можно указать количество символов или байтов, которое следует прочитать
из файла. В этом случае считывание будет выполняться до тех пор, пока не встретится
символ новой строки (\n), символ конца файла или из файла не будет прочитано указан
ное количество символов или байтов. Пример:
>>> f = open(r"c:\book\file.txt", "r", encoding="cpl251")
>>> f.readline(2), f.readline(2)
. ('Ст', 'ро')
>>> f.readline(lOO)# Возвращаются остатки данных
'кal\n'
»> f.close()
['Строкаl\n', 'Строка2']
>>>#Двоичный подрежим
>>> with open(r"c:\book\file.txt", "rb") as f:
f.readlines()
[b'\xdl\xf2\xf0\xee\xea\xe01\n', b'\xdl\xf2\xf0\xee\xea\xe02']
310 Часть /. Основы языка Python
В параметре можно указать количество символов или байтов, которое должно быть счи
тано из файла. Если указанное количество приходится на середину какой-либо строки,
эта строка считывается полностью. Если в файле хранится меньше символов (байтов),
чем указано, будет считано все содержимое файла. Примеры:
>>> f = open(r"c:\book\file.txt", "r", encoding="cpl251")
>>> f.readlines(4)
[ 'Строкаl \n']
>>> f.close()
>>> f = open(r"c:\book\file.txt", "r", encoding="cp1251")
>>> f.readlines(lO)
['Строкаl\n', 'Строка2']
>>> f.close()
♦ _next_() - при каждом вызове считывает из текущего файла одну строку и возвраща
ет ее. Если файл открыт в текстовом подрежиме, возвращается строка, а если в двоич
ном - последовательность байтов. При достижении конца файла генерируется исклю
чение Stopiteration. Пример:
>>> f = open(r"c:\book\file.txt", "r", encoding="cpl251")
>>> f. next (), f. next ()
( 'Строкаl \n', 'Строка2 ' )
>>> f. next () # Достигнут конец файла
Traceback (most recent call last):
File "<pyshell#26>", line 1, in <module>
f. next () # Достигнут конец файла
Stopiteration
>>> f.close()
Содержимое файла можно перебрать построчно в цикле перебора последовательности,
поскольку этот цикл на каждой итерации автоматически вызывает метод _next_().
Для примера выведем все строки, предварительно удалив символ перевода строки:
>>> f = open(r"c:\book\file.txt", "r", encoding="cpl251")
>>> for line in f: print(line.rstrip("\n"), end=" ")
Строкаl Строка2
>>> f.close()
♦ readaЫe() - возвращает True, если из текущего файла можно читать, и False - в про
тивном случае:
>>> f = open(r"c:\book\file.txt", "w") # Открываем файл дпя записи
>>> f.writaЫe()
False
>>> f = open(r"c:\book\file.txt", "r") # Открываем файл дпя чтения
>>> f.writaЫe()
True
♦ flush() - принудительно записывает данные из буфера на диск;
♦ fileno() - возвращает целочисленный дескриптор текущего файла. Возвращаемое зна
чение всегда будет больше числа 2, т. к. числа 0-2 закреплены за стандартными потока
ми ввода/вывода. Пример:
Глава 16. Работа с файлами и каталогами 311
'Строк'
♦ tel1() - возвращает позицию указателя относительно начала текущего файла в виде
целого числа. В Windows при открытии файла в текстовом подрежиме метод считает
символ \r как дополнительный байт, хотя этот символ удаляется из считанных строк.
Пример:
>>> with open(r"c:\book\file.txt", "w", encoding="cp1251") as f:
f.write("Stringl\nString2")
15
>>> f = open(r"c:\book\file.txt", "r", encoding="cp1251")
>>> f. tel1() # Указатель расположен в начале файла
о
>>> f.readline() # Перемещаем указатель
'Stringl\n'
>>> f.tel1 () # Возвращает 9 (8 + '\r'), а не В!!!
9
>>> f.close()
♦ seek(<Смещение> [, <Позиtiия>] ) - устанавливает указатель текущего файла в позицию,
имеющую заданное <Смещение> относительно параметра <Позиция>. В качестве парамет
ра <Позиция> могут быть указаны следующие атрибуты из модуля io или соответствую
щие им значения:
• io.SEEK_SET или О - начало файла (значение по умолчанию);
• io. SEEK_CUR или 1 - те1<;ущая позиция указателя. Положительное значение смещения
вызывает перемещение к концу файла, отрицательное - к его началу;
• io.SEEK_END или 2 - конец файла.
Примеры:
>>> import io
>>> f = open(r"c:\book\file.txt", "rb")
312 Часть /. Основы языка Python
Stringl
String2
>>> f.close()
ет, что права на выполнение нет. Следующие три символа задают права доступа для группы,
в которую входит владелец, - здесь только чтение (r--). Последние три символа (r--)
задают права для всех остальных пользователей - также только чтение.
Права доступа к каталогу определяются такой строкой:
drwxr-xr-x
Первая буква (d) означает, что это каталог. Владелец может выполнять в каталоге любые
действия (rwx), а его группа и все остальные пользователи - только читать и выполнять
поиск (r-x). Для того чтобы каталог можно было просматривать, должны быть установлены
права на выполнение (х).
Права доступа могут обозначаться и числом (маской прав доступа). Число состоит из трех
цифр: от О до 7. Первая цифра задает права для владельца, вторая - для его группы, а тре
тья - для всех остальных пользователей. Например, права доступа -rw-r--r-- соответст
вуют числу 644. Сопоставим числам, входящим в маску прав доступа, двоичную и буквен
ную записи (табл. 16.1).
Согласно этой таблице права доступа rw-r--r-- можно записать так: 110 100 100. То есть,
если право предоставлено, в соответствующей позиции стоит 1, а если нет - то О. Именно
поэтому число 11О 100 100 в двоичной записи соответствует числу 644 в восьмеричной.
Для проверки прав доступа к файлу или каталогу с указанным путем предназначена функ
ция access(<Путь>, <Режим>) из модуля os. Она возвращает True, 1 если проверка прошла
успешно, или False - в противном случае. В параметре <Режим> могут быть указаны сле
дующие константы, определяющие тип проверки:
♦ os .F_ок - проверка наличия файла или каталога:
♦ os . R_ок - проверка на возможность чтения файла или каталога;
♦ os.w_ок - проверка на возможность записи в файл или каталог;
♦ os.х_ок - определение, является ли файл или каталог выполняемым.
Примеры:
>>> import os
>>> os.access(r"c:\book\file.txt", os .F_OK) # Файл существует
True
>>> os.access(r"C:\book", os.F_OK) # Каталог существует
True
>>> os.access(r"C:\book2", os.F_OK) # каталог не существует
False
Глава 16. Работа с файлами и каталогами 321
Задать другие права доступа к файлу или каталогу с указанным путем можно вызовом
функции сhmоd(<Путь>, <Права доступа>) из модуля os. Права доступа задаются в виде
восьмеричного числа. Пример:
>>> os.chmod(r"c:\book\file.txt", 00777) #Полный доступ к файлу
Вместо числа можно указать комбинащпо констант из модуля stat. За дополнительной ин
формацией обращайтесь к документации по модуmо.
♦ getctime (<Путь к файлу>) - возвращает дату создания файла с указанным путем в виде
количества секунд, прошедших с начала эпохи. Если файл не существует, генерируется
исключение OSError. Пример:
>>> t = os.path.getct:i.me(r"c:\book\file.txt")
>>> t ,
1643210664.3589096
>>> time.strftime("%d.%m.%Y %H:%M:%S", time.localtime(t))
'26.01.2022 18:24:24'
:_,'!lport sys
:'!lp_in = sys.stdin #·Сохраняем ссылку на поток stdin
:: = operi(r"c:\book\file.txt", "r") # Открываем файл на чтение
sys.stdin = f # Перенаправляем ввод
·,.-:,ile True:
try:
line = input() # Считываем строку из файла
print(line) # Выводим строку
except EOFError: # Если достигнут конец файла,
break # выходим из цикла
sys.stdin tmp_in # Восстанавливаем стандартный ввод
:: . close() # Закрываем файл
�nput()
\1етод isatty() объекта стандартного потока ввода возвращает True, если объект потока
ссылается на консоль, и False - в противном случае:
>>> tmp_in = sys.stdin # Сохраняем ссылку на поток stdin
>>> f = open(r"c:\book\file.txt", "r")
328 Часть /. Основы языка Python
Сохраним код из листинга в файле и запустим его щелчком мыши. Индикатор процесса
в консоли будет обновляться каждую секунду.
В параметре flag можно указать режим открытия файла в виде строкового обозначения:
♦ r - то�ько чтение;
♦ w - чтение и запись;
♦ с - чтение и запись (значение по умолчанию). Если файл не существует, он будет соз
дан;
♦ n - чтение и запись. Если файл не существует, он будет создан. Если файл существует,
он будет перезаписан.
Параметр protocol задает протокол, применяемый при сериализации записываемых объек
тов, в виде числа от О до 4. По умолчанию используется значение переменной
pickle.DEFAULT_PROТOCOL (4). Если параметру writeback дать значение True, считываемые
из файла объекты будут кешироваться в памяти, что в ряде случаев может повысить произ
водительность.
Функция open() возвращает объект, представляющий открытый файл. В этот объект можно
добавлять новые элементы · и считывать существующие, применяя тот же синтаксис, что
используется для работы с отображениями (см. главу 9).
Объект-файл поддерживает следующие методы:
♦ close() - закрывает текущий файл. Для примера создадим файл и сохраним в нем спи
сок и кортеж:
>>> import shelve # Подключаем модуль
>>> dЬ = shelve.open(r"c:\book\dЬl.dat") # Открываем файл
>>> dЬ["objl"] = [1, 2, 3, 4, 5] # Сохраняем список
>>> dЬ["obj2"] = (6, 7, 8, 9, 10) # Сохраняем кортеж
>>> dЬ["objl"], dЬ [ "obj2"] # Выводим значения
( (1, 2, 3, 4, 5], (6, 7, 8, 9, 10))
>>> dЬ. close() # Закрываем файл
Параметр mode задает права доступа у созданных каталогов. Если каталог с указанным
путем существует и параметру exist_ok дано значение False, будет сгенерировано ис
кmочение FileExistsError, если же параметру дать значение True, ничего не произой
дет;
♦ rmdir(<Имя каталога>) - удаляет каталог с заданным именем из текущего рабочего
каталога. Можно удалить лишь пустой каталог. Если каталог не пуст или не существует,
генерируется искmочение, производное от класса OSError. Пример:
>>> os.rmdir("newfolder")
♦ listdir (<Путь>) - возвращает список имен файлов и каталогов, хранящихся в каталоге
с указанным путем:
»> os.listdir("C:\\book\\folderl\\")
['filel.txt', 'file2.txt', 'fileЗ.txt', 'folderl', 'folder2']
♦ walk() - последовательно просматривает сначала начальный каталог, потом вложенные
в него каталоги, потом каталоги следующего уровня 13ложенности и выдает хранящиеся
в них файлы и каталоги. Формат функции:
wаlk(<Путь к начальному каталогу>[, topdown=True] [, onerror=None] [,
followlinks=False])
Функция walk() возвращает итератор, при каждом вызове выдающий кортеж из трех
элементов: пути к очередному каталогу, списка путей к каталогам и списка путей к фай
лам, находящимся в этом каталоге.
Параметр onerror задает поведение функции при возникновении ошибки. Значение None
(используется по умолчанию) предписывает ничего не делать при их возникновении.
Также можно указать функцию, которая должна принимать в качест�е параметра объект
искmочения. Эта функция может как просто сообщить об ошибке (тогда ж:полнение
функции walk() продолжится), так и сгенерировать другое исключение (что вызовет
прерьшание работы функции walk()).
Параметр followlinks указывает функции, как поступить, если встретится символиче
ская ссылка: проследовать по ней (значение тrue) или не следовать по ней (значение
False, используемое по умолчанию).
Параметр topdown задает последовательность обхода каталогов. Если в качестве значе
ния указано тrue (значение по умолчанию), последовательность обхода будет такой:
»> for (р, d, f) in os.walk("C:\\book\\folderl\\"): print(p)
C:\book\folderl\
C:\book\folderl\folderl 1
C:\book\folderl\folderl_l\folderl 1 1
C:\book\folderl\folderl_l\folderl_l_2
C:\book\folderl\folderl_2
Если в параметре topdown указано значение False, последовательность обхода будет
другой:
>>> for (р, d, f) in os.walk("C:\\book\\folderl\\", False): print(p)
C:\book\folderl\folderl l\folderl 1 1
C:\book\folderl\folderl l\folderl_l_2
Глава 16. Работа с файлами и каталогами 333
C:\book\folderl\folderl_l
C:\book\folderl\folderl_2
C:\book\folderl\
Благодаря такой последовательности обхода перед удалением заданного каталога можно
удалить все находящиеся в нем файлы и каталоги (поскольку функция rmdir() позволяет
удалять только пустые каталоги):
import os
for (р, d, f) in os.walk("C:\\book\\folderl\\", topdown=False):
for file_name in f: # Удаляем все файлы
os.remove(os.path.join(p, file_name))
for dir_name in d: # Удаляем все каталоги
os.rmdir(os.path.join(p, dir_name))
ВНИМАНИЕ!
Очень осторожно используйте этот код. Если в качестве первого параметра в функции
walk() указать корневой каталог диска, то все имеющиеся в нем файлы и каталоги будут
удалены.
Удалить дерево каталогов позволяет также функция rmtree() из модуля shutil. Функ
ция имеет следующий формат:
rmtree(<Пyть>[, <Обработка ошибок>[, <Обработчик ошибок>]])
Если в параметре <Обработка ошибок> указано значение True, ошибки будут проигнори
рованы. Если указано значение False (используется по умолчанmо), в третьем параметре
можно задать ссылку на функцию, которая будет вызываться при возникновении искmо
чения. Указываемая функция должна принимать три параметра: ссылку на функцию,
выполнение которой вызвало возникновение ошибки, путь к файлу или каталогу, по
пытка удаления которого вызвала ошибку, и объект со сведениями о возникшей ошибке.
Пример:
import shutil
shutil.rmtree("C:\\book\\folderl\\")
♦ path.normcase(<Путь>) - преобразует заданный путь к каталогу к виду, подходящему
для использования в текущей операционной системе. В Windows преобразует все пря
мые слеши в обратные. Также во всех системах приводит все буквы пути к нижнему
р�гистру. Пример:
>>> os.path.normcase(r"c:/BoOk/filE.TxT")
'c:\\book\\file.txt'
Проверить, на что указывает заданный путь, можно с помощью следующих функций из
модуля os.path:
♦ isdir(<Путь>) - возвращает True, если заданный путь указывает на каталог, и False -
в противном случае:
>>> import os.path
>>> os.path.isdirlr"C:\book\file.txt")
False
>>> os.path.isdir("C:\\book\\")
True
334 Часть /. Основы языка Python
./DLL5
./Doc
./include
... Фрагмент пропущен
./Tool5
./vcruntime140.dll
./vcruntime140_1.dll
Видно, что путь, хранимый атрибутом path, составляется из пути, заданного в вызове функ
ции 5candir() (в нашем случае это используемый по умолчанию путь . ), и имени файла
(каталога). Теперь попробуем указать путь явно:
>>> for entry in o5.5candir("c:\python310"):
print(entry.path)
c:\python310\DLL5
c:\python310\Doc
c:\python310\include
... Фрагмент пропущен
336 Часть /. Основы языка Python
c:\python310\Tools
c:\python310\vcruntimel40.dll
c:\python310\vcruntimel40_1.dll
Еще класс DirEntry поддерживает следующие методы:
♦ is_file( [follow_symlinks=True]) - возвращает тrue, если текущий объект является
файлом, и False - в противном случае. Если текущий объект представляет собой сим
волическую ссьmку, и параметру follow_symlinks дано значение True, проверяется эле
мент, на который указывает эта символическая ссылка. Если же параметр follow_symlinks
имеет значение False, всегда возвращается False;
♦ is_dir( [follow_symlinks=Тrue]) - возвращает True, если текущий объект является ка
талогом, и· False - в противном случае. Если текущий объект представляет собой сим
волическую ссылку и параметру follow_symlinks дано значение True, проверяется эле
мент, на который указьmает эта символическая ссылка. Если же параметр follow_symlinks
имеет значение False, всегда возвращается False;
♦ is_symlink() - возвращает True, если текущий объект является символической ссыл
кой, и False - в противном случае;
♦ stat( [follow_symlinks=Тrue]) - возвращает объект stat_result (см. разд. 16.6), хра
нящий сведения о файле. Если текущий объект представляет собой символическую
ссылку и параметр follow_:_symlinks имеет значение True, возвращаются сведения об
элементе, на который указывает эта символическая ссылка. Если же параметр
follow_symlinks имеет значение False, возвращаются сведения о самой символической
ссьmке. В Windows атрибуты st_ino, st_dev и st_nlink объекта stat_result, возвра
щенного методом stat(), всегда хранят О, и для получения их значений следует вос
пользоваться функцией stat() из модуля os, описанной в разд. 16.6.
Пара примеров:
♦ вьmод списка всех каталогов, что находятся в каталоге, где установлен Python:
>>> for entry in os.scandir("c:\python310"):
if entry.is_dir():
print(entry.narne, end=", ")
Путь в вызове функции scandir () также можно указать в виде объекта bytes. В таком слу
чае значения атрибутов name и path класса DirEntry тоже будут представлять собой объекты
bytes. Пример:
>>> with os.scandir(b"c:\python310") as it:
for entry in it:
print(entry.name)
b'DLLs'
b'Doc'
b'include'
. . . Фрагмент пропущен ...
16.11. Исключения,
генерируемые файловыми операциями
При возникновении нештатной ситуации во время работы с файлами и каталогами генери
руется искточение класса OSError или одно из следующих искточений, являющихся его
подклассами:
♦ ВlockingIOError - не удалось заблокировать файл или поток ввода/вьmода;
♦ ConnectionError - ошибка сетевого соединения. Может возникнуть при открытии фай
ла по сети. Является базовым классом для р'яда более специфических искточений, опи
санных в документации по Python;
♦ FileExistsError - файл или каталог с заданным путем уже существует;
♦ FileNotFoundError - файл или каталог с заданным путем не. найден;
♦ InterruptedError - файловая операция прервана операционной системой;
♦ IsADirectoryError - вместо пути к файлу указан путь к каталогу;
♦ NotADirectoryError - вместо пути к каталогу указан путь к файлу;
♦ PermissionError - отсутствуют права на доступ к указанному файлу или каталогу;
♦ TimeoutError - истекло время, отведенное системой на выполнение операции.
Пример кода, обрабатьmающего некоторые из указанных искточений:
try
open("C:\temp\new\file.txt")
except FileNotFoundError:
print("Файл отсутствует")
except IsADirectoryError:
print("Это не файл, а каталог")
except PermissionError:
print("Отсутствуют права на доступ к файлу")
except OSError:
рrint("Неустановленная 01Ш1бка открытия файла")
ГЛАВА 17
3.10.1
Чтобы открыть ветвь реестра удаленного компьютера, сначала следует подключиться к его
реестру. Это выполняется вызовом функции ConnectRegistry(), которая вызывается в сле
дующем формате:
ConnectRegistry(<Ceтeвoe имя компьютера>, <Обозначение куста>)
Сетевое имя компьютера указывается в виде строки. Если задать значение None, будет вы
полнено подключение к реестру локального компьютера. Обозначение куста задается в ви
де значения одной из приведенных ранее переменных.
В качестве результата возвращается объект класса РуНКЕУ, представляющий указанный куст
реестра заданного удаленного компьютера. Если подключиться к удаленному реестру не
удалось, генерируется исключение OSError или одно из исключений, производных от этого
класса.
Ьбъект куста, полученный от функции ConnectRegistry(), следует передать первым пара
метром функции OpenKey() или OpenKeyEx(), чтобы открыть нужную ветвь удаленного рее
стра. Пример:
rh = winreg.ConnectRegistry(r"\\SorneCornputer", winreg.HКEY_CURRENT_USER)
with winreg.OpenKey(rh, r"Software\Python") as k:
('3.10.1', 1)
>>># 1 - это обозначение строкового типа
♦ QueryValue(<Ветвь>, <Имя вложенной ветви>) - возвращает значение вложенной ветви
с заданным именем, находящейся в указанной ветви. Возвращаемое значение всегда
представляется в виде строки. Если ветвь не имеет значения, возвращается пустая стро
ка. Если вторым параметром задано значение None, возвращает значение ветви, заданной
первым параметром. Пример:
>>> with winreg.OpenKey(winreg.HКEY_CURRENT_USER,
r"Software\Python") as k:
print(winreg.QueryValue(k, "3.10.1"))
print(winreg.QueryValue(k, None))
Installed
ВНИМАНИЕ!
Функция DeleteKeyEx() работает только в 64-разрядных редакциях Windows. При nоnытке
вызвать ее в 32-разрядной редакции будет сгенерировано исключение NotimplementedError.
Все операции записи данных в какую-либо ветвь реестра, создания, удаления элементов
и вложенных ветвей буферизуются в оперативной памяти для повышения производи
тельности. Перенос измененной ветви реестра из памяти на диск выполняется только
после закрытия этой ветви или вызова функции FlushKey() ;
♦ FlushKey(<Beтвь>) - принудительно переносит хранящуюся в буфере исправленную
копию заданной ветви на диск;
♦ QueryinfoKey(<Beтвь>) - возвращает сведения о заданной ветви в виде кортежа из трех
элементов: количества вложенных ветвей, количества элементов и времени последнего
изменения содержимого заданной ветви, которое представлено количеством сотен нано
секунд, прошедших с 1 января 1601 года:
>>> with winreg.OpenKey(winreg.HКEY_CURRENT_USER,
r"Software\Python") as k:
print(winreg.QueryinfoKey(k))
(2, 3, 132882022669686509)
for i in itertools.count():
try:
print(winreg.EnщnValue(k, i))
except OSError:
break
('Version', '3.10.1', 1)
('Versions', ['3 .10.1', '3.10.0', '3.9.0'], 7)
( 'BName', Ь' Python', 3)
♦ ЕnumКеу(<Ветвь>, <Индекс>) - возвращает имя вложенной ветви с заданным индексом
(нумерация элементов начинается с О), находящейся в указанной ветви. При попытке
получить вложенную ветвь с несуществующим индексом генерируется . исключение
OSError. Пример:
>>> with winreg.OpenKey(winreg.HКEY_CURRENT_USER,
r"Software\Python") as k:
for i in itertools.count():
try:
print(winreg.EnumКey(k, i))
except OSError:
break
3.10.0
3.10.1
ПРИМЕЧАНИЕ
Полное руководство по библиотеке wiпpath можно найти на странице:
https://pypi.org/project/winpath/.
ПРИМЕЧАНИЕ
Полная документация по библиотеке pyshortcuts находится по интернет-адресу:
https://github.com/newville/pyshortcuts.
Создание ярлыка, указывающего на заданный файл программы, выполняет функция
make_shortcut() из модуля pyshortcuts. Формат вызова функции:
make_shortcuts(<Путь к файлу программы> [, name=None] [,
description=None] [, icon=None] [, folder=None] [,
terminal=True] [, desktop=True] [, startmenu=True])
Путь к файлу программы задается в виде строки. Остальные параметры указывают:
♦ name- имя ярлыка (если None, будет использовано имя файла программы из первого
параметра);
♦ description- описание ярлыка (если None, будет использовано имя ярлыка);
♦ icon- путь к файлу со значком (если None, будет использован значок с логотипом
Python, поставляемый в составе библиотеки);
♦ folder- относительный путь, указанный относительно каталога Рабочий стол, к ката
логу, в котором будет создан ярлык (если None, ярлык будет создан непосредственно
в каталоге Рабочий стол);
♦ terminal- если True, программа, на которую указывает ярлык, будет запущена в кон
соли, если False- нет. У программ с графическим интерфейсом этому параметру сле
дует дать значение False;
♦ desktop- если тrue, ярлык будет создан на рабочем столе, если False- нет;
♦ startmenu- если тrue, ярлык будет создан в меню П�ск, если False - нет.
Функция возвращает объект класса Shortcut, определенного в модуле pyshortcuts, который
представляет созданный ярлык. Обычно возвращаемый функцией результат игнорируют.
Пример:
from pyshortcuts import make_shortcut
make_shortcut(r"c:\book\program.py", nаmе="Программа",
• description="Heбoльшaя учебная программа", startmenu=False)
ЧАСТЬ 11
Библиотека PyQt 6
Глава 18. Введение в PyQt 6
Глава 19. Окна
Глава 20. Обработка сигналов и событий
Глава 21. Размещение компонентов в окнах. Контейнеры
Глава 22. Основные компоненты
Глава 23. Списки и таблицы
Глава 24. Работа с базами данных
Глава 25. Работа с графикой
Глава 26. Графическая сцена
Глава 27. Диалоговые окна
Глава 28. Создание SDI- и МDl-программ
Глава 29. Мультимедиа
Глава 30. Печать документов
Глава 31. Сохранение настроек программ
Глава 32. Программа «Судаку»
ГЛАВА 18
Введение-в PyQt 6
ПРИМЕЧАНИЕ
Описание библиотеки PyQt приведено по интернет-адресу: https://www.riverbankcomputing.
com/. Полная документация по фреймворку Qt 6 находится на сайте: https://doc.qt.io/qt-6/
index.html.
ВНИМАНИЕ!
Эта команда устанавливает библиотеку в базовой комплектации. Дополнительные ком
поненты (в частности, веб-браузер) и средства разработчика (такие как программа Qt
Designer) следует установить отдельно.
Чтобы проверить правильность установки, вьmедем версии PyQt и Qt:
>>> from PyQt6 import QtCore
>>> QtCore.PYQT_VERSION_STR
'6.2.3'
>>> QtCore.QТ_VERSION_STR
'6.2.3'
г Jакрыть окно
Файлу с оконной программой следует дать расширение pyw, чтобы при ее запуске не выво
дилось окно консоли.
♦ Следующая инструкция:
window = QtWidgets.QWidget()
создает объект окна в виде объекта класса QWidget. Этот класс наследуют практически
все классы, реализующие компоненты графического интерфейса. Поэтому тобой ком
понент, не имеющий родителя, выводится в своем собственном окне.
♦ Инструкция:
window.setWindowТitle("Пepвaя программа на PyQt")
задает текст, который будет выводиться в заголовке окна, для чего используется метод
setWindowТitle().
♦ Очередная инструкция:
window.resize(ЗOO, 70)
задает минимальные размеры клиентской области окна (размеры заголовка и толщина
границ окна не учитываются). В первом параметре метода resize()указывается ширина
окна, а во втором параметре - его высота. Следует помнить, что эти размеры являются
рекомендацией, - т. е. если компонеmы не помещаются в окне, оно будет увеличено.
♦ Инструкция:
label = QtWidgets.QLabel("<center>Пpивeт, миp!</center>")
создает объект надписи. Текст надписи задается в качестве параметра в конструкторе
класса QLabel. С помощью НТМL-тега <center> производится выравнивание текста по
центру компонента. Возможность использования НТМL-тегов и СSS-атрибутов является
отличительной чертой библиотеки PyQt (например, внутри надписи можно вывести таб
лицу или графическое изображение).
♦ Следующая инструкция:
btnQuit = QtWidgets.QPushButton("&Закрыть окно")
создает объект-кнопки. Текст, который будет отображен на кнопке, задается в качестве
параметра в конструкторе класса QPushButton. Символ & перед буквой з задает клавишу
быстрого доступа. Если нажать одновременно клавишу <Alt> и клавишу с буквой, перед
которой в строке указан символ &, то кнопка сработает.
♦ Инструкция:
vbox = QtWidgets.QVBoxLayout()
создает вертикальный коmейнер. Все компоненты, добавляемые в этот контейнер, будут
располагаться по вертикали сверху вниз в порядке добавления, при этом размеры добав
ленных компонентов будут подогнаны под размеры контейнера. При изменении разме
ров контейнера будет произведено изменение размеров всех компонентов.
♦ В следующих двух инструкциях:
vbox.addWidget(label)
vbox.addWidget(btnQuit)
с помощью метода addWidget() контейнера производится добавление в него созданных
ранее надписи и кнопки. Добавленные в контейнер компоненты станут его потомками, а
сам контейнер - родителем добавленных в него компонентов.
352 Часть 11. Библиотека PyQt б
♦ Инструкция:
window.setLayout(vbox)
помещает контейнер в окно с помощью метода setLayout(). В результате контейнер
станет потомком окна (а окно - родигелем контейнера).
♦ Инструкция:
btnQuit.clicked.connect(app.quit)
назначает обработчик сигнала clicked() кнопки, активизирующегося при возникнове
нии события нажатия кнопки.
Событие - это уведомление о том, что в программе что-либо произошло: пользователь
щелкнул мышью, нажал клавишу на клавиатуре, переместил окно и др.
Сигнал - это особый объект, соответствующий событию определенного типа и генери
руемый каждый раз, когда возникает событие, относящееся к этому типу.
Обработчик сигнала - это метод (или функция), связанный с сигналом и вызьmаемый
при его генерировании. Обработчик назначается сигналу вызовом у последнего метода
connect(). Метод, используемый в качестве обработчика, называется слотом.
Сигнал clicked(), генерируемый при возникновении события нажатии кнопки, доступен
через одноименный атрибут класса кнопки. Обработчик представляет собой метод
quit() объекта программы, немедленно завершающий ее работу.
Отметим, что не все события связаны со слотами. Часть событий обрабатьmается осо
быми специальными методами, называемых обработчиками событий. Подробнее об
обработке событий и сигналов будет рассказано в главе 20.
♦ Очередная инструкция:
window.show()
выводит на экран окно и все компоненты, которые мы ранее в него добавили.
♦ И, наконец, .последняя инструкция:
sys.exit(app.exec() )
запускает цикл обработки сигналов в программе.
Код, расположенный после вызова метода ехес () , будет выполнен только после завер
шения работы программы, - поскольку результат выполнения метода ехес () мы пере
даем функции exit(), дальнейшее выполнение программы будет прекращено, а код воз
врата - передан операционной системе.
Как видно из этого примера, класс QWidget наследует два класса: QObject и QPaintDevice.
Класс QObject является классом верхнего уровня, и его в PyQt наследует большинство клас
сов. В свою очередь, класс QWidget является базовым для всех визуальных компонентов.
ВНИМАНИЕ!
В описании каждого класса PyQt будут приведены лишь атрибуты, методы, сигналы и сло
ты, определенные непосредственно в описываемом классе. Атрибуты, методы, сигналы и
слоты базовых классов здесь не описываются - присутствуют лишь ссылки на соответст
вующие страницы документации.
class MyWindow(QtWidgets.QWidget):
def init (self, parent=None):
QtWidgets.QWidget. init (self, parent)
self.label = QtWidgets.QLabel("Пpивeт, мир!")
self.label.setAlignment(QtCore.Qt.AlignmentFlag.AlignHCenter)
self.ЬtnQuit = QtWidgets.QPushButton("&Зaкpыть окно")
self.vbox = QtWidgets.QVBoxLayout()
self.vbox.addWidget(self.label)
self.vbox.addWidget(self.ЬtnQuit)
self.setLayout(self.vbox)
self.ЬtnQuit.clicked.connect(QtWidgets.QApplication.instance().quit)
Помимо уже знакомого модуля QtWidgets из пакета PyQtб, мы импортируем модуль QtCore,
содержащий модуль Qt, в котором определено перечисление AlignmentFlag, задающее вы
равнивание текста.
Далее мы определяем класс MyWindow, который наследует класс QWidget:
:lass MyWindow(QtWidgets.QWidget):
class MyDialog(QtWidgets.QDialog):
def init (self, parent=None):
QtWidgets.QDialog. init (self, parent)
self.myWidget = MyWindow.MyWindow()
Глава 18. Введение в PyQt б 355
self.myWidget.vbox.setContentsMargins(0, О, О, О)
self.button = QtWidgets.QPushВutton("&Измeнить надпись")
mainBox = QtWidgets.QVВoxLayout()
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("Hoвaя надпись")
self.button.setDisaЫed(True)
Запустим файл test.pyw. На экране появится окно с надписью и двумя кнопками (рис. 18.2).
По нажатии на кнопку Изменить надпись производится изменение текста надписи, и кнопка
.Jелается неактивной. Нажатие кнопки Закрыть окно будет по-прежнему завершать работу
программы.
jакрыть окно
.Изменить надпись
С его помощью позже мы получим доступ ко всем атрибутам класса MyWindow. Например,
в следующей строке произведем изменение отступа между границами контейнера и грани
uами соседних элементов :
oelf.myWidget.vbox.setContentsMargins(0, О, О, О)
Далее в конструкторе создаются кнопки и контейнер, объект класса MyWindow и кнопка до
бавляются в контейнер, а сам контейнер помещается в окно.
Инструкция :
=elf.button.clicked.connect(self.on_clicked)
356 Часть 11. Библиотека PyQt 6
templates\forms
Dialog with Buttons Bottom
Dialog with Buttons Right
Dialog without Buttons
MainWindow
Widaets
EmЬedded Deslgn
' Г QtDesigr>e>r о х
Ve11k.iil Щ'OUt i{
11] Hori.zon�l layout
.
;;
;;� •·
• Hoпzoot-,1 Sp.acer
1 :
.......
Verti"'I�
а Push&Jtton
� ToolВ... tton
11): Radio8utton
• Ch@d:Boi
]I Lmv-
"'-����
-::8 TreeView
gт�1ev1-
Object Class л
(!f )(
Fllter
myform : QWldget
л
objectName и в поле справа от свойства введем новое имя класса формы: MyFoпn. Затем
найдем свойство geometry, щелкнем мышью на значке V слева от него, чтобы отобразить
скрытые свойства, и зададим ширину равной 300, а высоту равной 70, - размеры формы
автоматически изменятся. Указать текст, который будет отображаться в заголовке окна,
позволяет свойство windowTitle.
Чтобы изменить свойства надписи, следует выделить компонент с помощью мыши или вы
брать соответствующий ему пункт в панели Object Inspector. Для примера изменим значе
ние свойства text (оно задает текст надписи). После чего найдем свойство alignment, щелк
нем мышью на значке V слева от него, чтобы отобразить скрытые свойства, и укажем для
свойства Horizontal значение AlignHCenter. Теперь выделим кнопку и изменим значение
свойства objectName на btnQuit, а в свойстве text укажем текст надписи, которая будет вы
водиться на кнопке. (Кстати, изменить текст надписи или кнопки также можно, выполнив
двойной щелчок мышью на компоненте.)
Закончив, выберем в меню File пункт Save и сохраним готовую форму в файле MyForm.ui.
При необходимости внести в этот файл какие-либо изменения его можно открыть в про
грамме Qt Designer, выбрав в меню File пункт Open.
Глава 18. Введение в PyQt б 359
class MyWindow(QtWidgets.QWidget):
def init (self, parent=None):
QtWidgets.QWidget. init (self, parent)
uic.loadUi("MyForm.ui", self)
self.btnQuit.clicked.connect(QtWidgets.QApplication.instance().quit)
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.QApplication.instance().quit)
if name = " main "·
import sys
арр = QtWidgets.QApplication(sys.argv)
window = MyWindow()
window.show()
sys.exit(app.exec())
Загрузить UI-файл можно и вне класса, после чего при определении класса окна указать
полученный от функции loadUiType() .класс вторым в списке наследования, - чтобы опре
деляемый· класс унаследовал все его методы (листинг 18. 7).
if name main
import sys
арр·= QtWidgets.QApplication(sys.argv)
window = MyWindow()
window. show()
sys.exit(app.exec())
Глава 18. Введение в PyQt б 361
=lass 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.QApplication.instance().quit)
window = MyWindow()
window.show()
sys.exit(app.exec())
Класс формы можно указать во втором параметре в списке наследования - в этом случае
он унаследует все методы класса формы (листинг 18.1 О).
Создавать формы в программе Qt Designer очень удобно. Тем не менее, чтобы полностью
овладеть программированием на PyQt, необходимо уметь писать код вручную. Поэтому
в оставшейся части книги мы больше не станем задействовать программу Qt Designer.
♦ QtSql - вкточает средства для работы с базами данных, а также реализацию SQLite;
♦ QtSvg - служит для работы с векторной графикой (SVG);
♦ QtNetwork- содержит классы, предназначенные для работы с сетью;
♦ QtXml и QtXmlPatterns - предназначены для обработки ХМL;
♦ QtHelp - содержат инструменты для создания интерактивных справочных систем.
В этой книге не описываются все упомянутые модули - чтобы получить информацmо по
нерассматриваемым здесь модулям, обращайтесь к соответствующей документации.
зрр = QtWidgets.QApplication(sys.argv)
jutton = QtWidgets. QPushВutton("Запустить процесс")
jutton.resize(200, 40)
jutton.clicked.connect(on_clicked)
jutton.show()
sys.exit(app.exec())
364 Часть 11. Библиотека PyQt б
def on_clicked():
button.setDisaЫed(True) # Делаем кнопку неактивной
for i in range(l, 21):
# Обрабатываем накопившиеся события всех типов в течение 1 с
QtWidgets.QApplication.instance() .processEvents(
QtCore.QEventLoop.ProcessEventsFlag .AllEvents, 1000)
time.sleep( 1) # "Засыпаем" на 1 секунду
print("step -", i)
button.setDisaЬled(False) # Делаем кнопку активной
арр = QtWidgets.QApplication(sys.argv)
button = QtWidgets.QPushButton("Зaпycтить процесс")
Глава 18. Введение в PyQt б 365
button.resize(200, 40)
button.clicked.connect(on_clicked)
button.show()
sys.exit(app.exec())
В этом примере длительная операция разбита на одинаковые этапы, после выполнения каж
дого из которых выполняется принудительная обработка накопившихся событий. Теперь
программа останется отзывчивой, хотя и с некоторой задержкой.
18.8.1. Потоки
Для создания потока в PyQt предназначен класс QThread, который определен в модуле
QtCore и наследует класс QObject. Конструктор класса QThread имеет следующий формат:
QТhread( [parent=None])
Параметр parent задает родительский объект создаваемого потока. При уничтожении объ
екта-родителя поток также будет "уничтожен.
Чтобы создать поток, следует определить класс, производный QThread, и вставить в него
метод run(). Код, расположенный в методе run(), будет выполняться в отдельном потоке,
а после завершения выполнения метода run() этот поток прекратит свое существование.
Чтобы запустить поток, нужно создать объект потока и вызвать у него метод start() в сле
дующем формате:
start( [priority=Priority. InheritPriority])
Параметр priority задает приоритет выполнения потока по отношенmо к другим потокам
в виде значения одного из следующих элементов перечисления Priority из класса QТhreact:
IdlePriority (самый низкий приоритет), LowestPriority, LowPriority, NoпnalPriority,
:!ighPr.:i.ority, Highestpriority, TirneCriticalPriority (самый высокий приоритет) и
InheritPriority (автоматический выбор приоритета - значение по умолчанmо).
Следует учитывать, что при наличии потока с самым высоким приоритетом поток с самым
низким приоритетом в некоторых операционных системах может быть проигнорирован.
Задать приоритет потока также позволяет метод setPriority(<Пpиopитeт Priority>). Узнать,
какой приоритет использует запущенный поток, можно с помощью метода priority() .
После запуска потока генерируется сигнал started(), а после завершения - сигнал
finished(). Назначив этим сигналам обработчики, можно контролировать состояние потока
Збб Часть 11. Библиотека PyQt б
из основного потока программы. Метод isRunning() потока возвращает значение тrue, если
текущий поток выполняется, а метод isFinished() - значение True, если поток закончил
выполнение.
Потоки выполняются внутри одного процесса и имеют доступ ко всем глобальным пере
менным. Однако из потока нельзя изменять что-либо в основном потоке программы (на
пример, менять текст надписи). Для изменения данных в основном потоке нужно использо
вать сигналы. Внутри потока у нужного сигнала вызывается метод emit(), который, собст
венно, и генерирует его. В параметрах метода emit() можно указать данные, которые будут
переданы обработчику сигнала. А внутри основного потока назначается обработчик этого
сигнала, который и будет обновлять интерфейс программы.
Рассмотрим использование потоков на примере (листинг 18.13).
class MyТhread(QtCore.QThread):
mysignal = QtCore.pyqtSignal(str)
def init (self, parent=None):
QtCore.QТhread. init (self, parent)
def run(self):
for i in range(l, 21):
self.sleep(З) # "Засыnаем" на 3 секунды
# Передача данных из потока через сигнал
self.mysignal.emit("i = %s" % i)
class MyWindow(QtWidgets.QWidget):
def init (self, parent=None):
QtWidgets.QWidget. init (self, parent)
self.label = QtWidgets.QLabel("Haжмитe кнопку для запуска потока")
self.label.setAlignrnent(QtCore.Qt.AlignrnentFlag.AlignHCenter)
self.button = QtWidgets.QPushButton("Зaпycтит1;, процесс")
self.vbox = QtWidgets.QVВoxLayout()
self.vbox.addWidget(self.label)
self.vbox.addWidget(self.button)
self.setLayout(self.vbox)
self.mythread = MyТhread() # Создаем поток
self.button.clicked.connect(self.on_clicked)
self.mythread.started.connect(self.on_started)
self.mythread.finished.coпnect(self.on_finished)
self.mythread.mysignal.connect(self.on_change,
QtCore.Qt.ConnectionType.QueuedConnection)
def on_clicked(self):
self.button.setDisaЫed(True) # Делаем кнопку неактивной
self.mythread.start() # Запускаем поток
def on_started(self): # Вызывается при запуске потока
self.label.setText("Bызвaн метод on_started()")
def on_finished(self): # Вызывается при завершении потока
self.label.setText("Вызван метод on_finished()")
self.button.setDisaЫed(False) # Делаем кнопку активной
Глава 18. Введение в PyQt б 367
if name main
import sys
арр = QtWidgets.QApplication(sys.argv)
window = MyWindow ()
window.setWindowТitle("Иcпoльзoвaниe потоков")
window.resize(ЗOO, 70)
window.show()
sys.exit(арр.ехес(). )
Здесь мы создали класс MyТhread, производный от QThread. В нем мы определили свой сиг
нал mysignal, для чего создали атрибут с таким же именем и занесли в него значение, воз
вращенное функцией pyqtSignal() из модуля QtCore. Функции pyqtSignal() мы передали
в качестве параметра тип str (строка Python), тем самым указав PyQt, что вновь определен
ный сигнал будет принимать единственный параметр строкового типа:
mysignal = QtCore.pyqtSignal(str)
В том же классе мы определили обязательный для потоков метод run() - в нем произво
дится имитация процесса с помощью цикла перебора последовательности и метода sleep() :
каждые три секунды выполняется генерация сигнала mysignal и передача текущего значе
ния переменной i в составе строки:
self.mysignal.emit("i = %s" % i)
Внутри конструктора класса MyWindow мы назначили обработчик этого сигнала с помощью
выражения
self.mythread.mysignal.connect(self.on_change,
QtCore.Qt.ConnectionType.QueuedConnection)
Здесь все нам уже знакомо: у свойства mysignal потока, которое представляет одноименный
сигнал, вызывается метод connect( J , и ему первым параметром передается обработчик. Во
втором параметре метода connect() с помощью элемента QueuedConnection перечисления
connectionType указывается, что сигнал помещается в очередь обработки событий и обра
ботчик должен выполняться в основном потоке программы. Из основного потока мы можем
смело изменять свойства компонентов.
Теперь рассмотрим код метода класса MyWindow, который станет обработчиком сигнала
mysignal:
def on_change(self, s):
self.laЬel.setText(s)
Второй параметр этого метода служит для приема параметра, переданного сигналу. Значе
ние этого параметра будет выведено в надписи с помощью метода setтext().
Еще в конструкторе класса MyWindow производится создание надписи и кнопки, а затем их
размещение внутри вертикального контейнера. Далее создается объект класса MyТhread и
сохраняется в атрибуте mythread, чтобы в дальнейшем потоком можно бьmо управлять. За
пуск потока производится с помощью метода start( J внутри обработчика нажатия кнопки.
Чтобы исключить повторный запуск потока, мы с помощью метода setDisaЫed() делаем
368 Часть 11. Библиотека PyQt б
class MyТhread(QtCore.QТhread):
mysignal = QtCore.pyqtSignal(str)
def init (self, parent=None):
QtCore.QТhread. init (self, parent)
self.running False # Флаг вьmолнения
self.count = О
def run(self):
self.running True
while self.running: # Проверяем значение флага
self.count += l
self.mysignal.emit("count = %s" % self.count)
self.sleep(l) # Имитируем процесс
Глава 18. Введение в PyQt б 369
class MyWindow(QtWidgets.QWidget):
def init (self, parent=None):
QtWidgets.QWidget. init (self, parent)
self.label = QtWidgets.QLabel("Haжмитe кнопку для запуска потока")
self.label.setAlignment(QtCore.Qt.AlignmentFlag.AlignHCenter)
self.ЬtnStart = QtWidgets. QPushButton("Запустить поток")
self. ЬtnStop = QtWidgets.QPushButton( "Остановить поток")
self.vbox = QtWidgets.QVВoxLayout()
self.vbox.addWidget(self.label)
self.vbox.addWidget(self.ЬtпStart)
self.vbox.addWidget(self.ЬtnStop)
self.setLayout(self.vbox)
self.mythread = MyТhread()
self.ЬtnStart.clicked.connect(self.on_start)
self.ЬtnStop.clicked.connect(self.on_stop)
self.mythread.mysignal.connect(self.on_change,
QtCore.Qt.ConnectionType.QueuedConnection)
def on_start(self):
if not self.mythread.isRunning():
self. mythread.start() # Запускаем поток
def on_stop(self):
self.mythread.running = False # Изменяем флаг вьшолнения
def on_change(self, s):
self.label.setтext(s)
def closeEvent(self, event): # Вызывается при закрытии окна
self.hide() # Скрываем окно
self.mythread.running = False # Изменяем флаг вьшолнения
self.mythread.wait(S000) # Даем время, чтобы закончить
event.accept() # Закрываем окно
if name main
import sys
арр = QtWidgets.QApplication(sys.argv)
window = MyWindow()
window.setWindowТitle("Зaпycк и остановка потока")
window.resize(З00, 100)
window.show()
sys.exit(app.exec())
В этом примере в конструкторе класса MyThread создается атрибут running, и ему присваи
вается значение False. При запуске потока внутри метода run () значение атрибута изменя
ется на тrue. Затем запускается цикл, в котором значение этого атрибута указывается в ка
честве условия. Как только значение атрибута станет равным значению False, цикл будет
остановлен.
Внутри конструктора класса MyWindow производится создание надписи, двух кнопок и объ
екта класса MyТhread. Далее назначаются обработчики сигналов. По нажатии кнопки Запус
тить поток выполнится метод on_start(), внутри которого с помощью метода isRunning()
производится проверка текущего статуса потока. Если поток не запущен, он запускается
370 Часть 11. Библиотека PyQt 6
class Threadl(QtCore.QТhread):
sl = QtCore.pyqtSignal(int)
def init (self, parent=None):
QtCore.QТhread. init (self, parent)
self.count = О
def run(self):
self.ехес () # Запускаем цикл обработки сигналов
def on_start(self):
self.count += 1
self.sl.emit(self.count)
Глава 18. Введение в PyQt 6 371
class Thread2(QtCore.QТhread):.
s2 = QtCore.pyqtSignal(str)
def init (self, parent=None):
QtCore.QThread. init (self, parent)
def run(self):
self.ехес() # Запускаем цикл обработки сигналов
def on_change(self, i):
i += 10
self.s2.emit("%d" % i)
class MyWindow(QtWidgets.QWidget):
def init (self, parent=None):
QtWidgets.QWidget. init (self, parent)
self.laЬel = QtWidgets.QLabel("Haжмитe кнопку")
self.label.setAlignment(QtCore.Qt.AlignmentFlag.AlignHCenter)
self.button = QtWidgets.QPushВutton("Cгeнepиpoвaть сигнал")
self.vbox = QtWidgets.QVВoxLayout()
self.vbox.addWidget(self.laЬel)
self.vЬox.addWidget(self.button)
self.setLayout(self.vbox)
self.threadl = Threadl()
self.thread2 = Thread2()
self.threadl.start()
self.thread2.start()
self.button.clicked.connect(self.threadl.on_start)
self.threadl.s1.connect(self.thread2.on_change)
self.thread2.s2.connect(self.on_thread2_s2)
def on_thread2_s2(self, s):
self.label.setText(s)
if name main
import sys
арр = QtWidgets.QApplication(sys.argv)
window = MyWindow()
window.setWindowТitle("Oбмeн сигналами между потоками")
window.resize(З00, 70)
window.show()
sys.exit(app.exec())
В этом примере мы создали классы Threadl, Thread2 и MyWindow. Первые два класса пред
ставляют собой потоки. Внутри них в методе run() вызывается метод ехес(), который
запускает цикл обработки событий. В конструкторе класса окна MyWindow производится соз
дание надписи, кнопки и объектов классов Threadl и Thread2. Далее выполняется запуск
сразу двух потоков.
В следующей инструкции сигнал нажатия кнопки соединяется с методом on..:._start() перво
го потока. Внутри этого метода производится какая-либо операция (в нашем случае - уве
личение значения атрибута count), а затем с помощью метода emit() генерируется сигнал
s1, и в параметре передается результат выполнения метода. Сигнал s1 соединен с методом:
372 Часть 11. Библиотека PyQt 6
on_change() второго потока. Внутри этого метода также производится какая-либо операция,
а затем генерируется сигнал s2, и передается результат выполнения метода. В свою очередь,
сигнал s2 соединен со слотом on_thread2_s2 объекта окна, который выводит в надпись зна
чение, переданное с этим сигналом. Таким образом, по нажатии кнопки Сгенерировать
сигнал вначале будет вызван метод on_ start() класса Threadl, затем - метод on_change()
класса Thread2, а потом� метод on_thread2 _s2 () класса MyWindow, который выведет ре
зультат выполнения на экран.
18.8.3. Очереди
Очередь - это подобие списка с возможностью добавлять элементы лишь в его конец
и извлекать элементы, в зависимости от типа очереди,· либо из начала, либо из конца, при
этом извлеченный элемент удаляется из очереди. Очереди часто применяются, если требу
ется распределять постоянно. поступающие задачи между ограниченным набором работаю
щих потоков.
Модуль queue из стандартной библиотеки Python содержит следующие классы потокобезо
пасных очередей:
♦ Queue - обычная очередь типа «первым пришел, первым вышел». Формат конструктора:
Queue( [maxsize=O])
Параметр maxsize в этом и последующих случаях задает максимальный размер очереди
(количество элементов, которое может содержать очередь). Если параметр равен нулю
или отрицательному значению, то размер очереди не ограничен. Пример:
>>> import queue
>>> q = queue.Queue()
>>> q.put_nowait("eleml")
>>> q.put_nowait("elem2 ")
>>> q.get_nowait()
'eleml'
>>> q.get_nowait()
'elem2'
♦ LifoQueue - стек (очередь типа «первым пришел, последним вышел»). Формат конст
руктора:
LifoQueue ( [maxsize=O])
Пример:
>>> q = queue.LifoQueue()
>>> q.put_nowait("eleml")
>>> q.put_nowait("elem2")
>>> q.get_nowait()
'elem2'
>>> q.get_nowait()
'eleml'
♦ PriorityQueue - обычная очередь с приоритетами. Элементы очереди должны быть
кортежами, в которых первым элементом является число, означающее приоритет, а вто
рым - значение заносимого в очередь элемента. При получении значения возвращается
Глава 18. Введение в PyQt б 373
Если очередь пуста и параметр Ыосk имеет значение False, генерируется исключение
Empty из модуля que�e. Если в параметре Ыосk указано значение True, метод приостано
вит выполнение потока и будет ожидать, когда в очереди появится хотя бы один эле
мент, в течение времени, заданного параметром timeout в секундах. Если в очереди за
заданное время так и не появилось ни одного элемента, также генерируется исключение
Empty;
♦ get_nowait() - извлекает элемент из текущей очереди без ожидания. Эквивалентен вы
ражеюпо
get(False);
♦ qsize() - возвращает приблизительное количество элементов в текущей очереди. Если
доступ к очереди имеют несколько потоков, доверять этому значению не следует -
в любой момент времени размер очереди может измениться;
♦ empty() - возвращает тrue, если текущая очередь пуста, и False - в противном случае.
Если доступ к очереди имеют несколько потоков, доверять этому значеюпо также не
следует - в любой момент времени размер очереди может измениться.
Следующие методы не поддерживаются классом SimpleQueue:
♦ j oin() - блокирует поток, пока из текущей очереди не будут выбраны все элементы.
Другие потоки после выборки очередного элемента из очереди должны вызьmать метод
task_done();
♦ task_done() - уведомляет текущую очередь о выборке очередного элемента;
♦ full() - возвращает True, если текущая очередь заполнена до предела, и False -
в противном случае. Если доступ к очереди имеют несколько потоков, доверять этому
значению также не следует - в любой момент времени размер очереди может изме
ниться.
Рассмотрим использование очереди в многопоточной программе на примере (листинг 18.16).
class MyТhread(QtCore.QТhread):
task_done = QtCore.pyqtSignal(int, int, name='taskDone')
def init (self, id, queue, parent=None):
QtCore.QТhread. init (self, parent)
self.id = id
self.queue = queue
def run(self):
while True:
task = self;queue.get() # Получаем задание
self.sleep(5) # Имитируем обработку
self .task_done.emit(task, self.id) # Передаем данные обратно
self.queue.task_done()
class MyWindow(QtWidgets.QPushButton):
def init (self):
QtWidgets.QPushButton. init (self)
Глава 18. Введение в PyQt б 375
self.setText("Раздать задания")
self.queue = queue.Queue() # Создаем очередь
self.threads = []
for i in range(l, 3): # Создаем потоки и запускаем
thread = MyТhread(i, self.queue)
self.threads.append(thread)
thread.task done.connect(self.on_task_done,
·otcore.Qt.ConnectionType.QueuedConnection)
thread.start( )
self.clicked.connect(self.on_add_task)
def on_add_task(self):
for i in range(O, 11):
self.queue.put(i) # Добавляем элементы в очередь
def on_task_done(self, data, id):
print(data, "- id =", id) # Выводим обработанные данные
if name main
irnport sys
арр = QtWidgets.QApplication(sys.argv)
window = MyWindow()
window.setWindowТitle("Иcпoльзoвaниe очереди")
window.resize(300, 30)
window.show()
sys.exit(app.exec())
Внугри конструктора класса MyWindow с помощью метода setText() задается текст надписи
на кнопке, затем создается объект класса Queue и сохраняется в атрибуте queue. В следую
щем выражении создается список, в котором будут храниться ссылки на объекты потоков.
Сами объекты потоков (в нашем случае их два) создаются внутри цикла и добавляются
в список. Внутри цикла также производится назначение обработчика сигнала taskDone и
запуск потока с помощью метода start(). Далее назначается обработчик нажатия кнопки.
По нажатии кнопки Раздать задания вызываетс11 метод on_add_task(), внутри которого
обрабатываемые элементы добавляются в очередь. После этого потоки начинают работать,
и каждый из них выбирает из очереди один элемент. Завершив обработку элемента, поток
генерирует сигнал taskDone и вызьmает метод task_done(), информирующий об окончании
обработки задания. Главный поток получает сигнал и вызывает метод on_task_done(),
внутри котqрого через параметры будут доступны результаты обработки. В нашем примере
эти результаты просто выводятся в консоль (чтобы увидеть их, следует сохранить файл
с расширением ру или просто запустить программу из среды IDLE Shell). Обработав эле
мент и сигнализировав об этом, поток выбирает из очереди следующий элемент и т. д. Если
очередь окажется пуста, потоки приостановят работу, пока в очереди не Щ)Явятся элементы.
class MyТhread(QtCore.QThread):
х = 10 # Атрибут класса
Глава 18. Введение в PyQt 6 377
class MyWindow(QtWidgets.QPushButton):
def init (self):
QtWidgets.QPushButton. init (self)
self.setText("Зaпycтt-iть")
self.threadl = MyТhread(l)
self.thread2 = MyТhread(2)
self.clicked.connect(self.on_start)
def on_start(self):
if not self.threadl.isRunning(): self.threadl.start()
if not self.thread2.isRunning(): self.thread2.start()
if name main
import sys
арр = QtWidgets.QApplication(sys.argv)
window = MyWindow()
window.setWindowТitle("Иcпoльзoвaниe блокировщика")
window.resize(300, 30)
window.show()
sys.exit(app.exec())
х = 49 id = 1
х = 49 id = 2
х = 54 id = 2
х = 88 id = 2
Как можно видеть, сначала значение атрибута класса изменил поток с идешификатором 1, а
лишь затем - поток с идеirrИфикатором 2. Если блокировку не указать, то результат будет
иным:
х = 10 id = 1
х = 15 id = 2
х = 20 id = 1
х = 54 id = 1
х = 54 id = 2
х = 88 id = 2
def change_x(self):
# Создаем автоблокировщик и тем самым накладываем блокировку
ml = QtCore.QМutexLocker(MyТhread.mutex)
print("x =", MyТhread.x, "id =", self.id)
MyТhread.x += 5
self.sleep(2)
print("x =", MyТhread.x, "id =", self.id)
MyТhread.x += 34
print("x =", MyТhread.x, "id =", self.id)
# Здесь локальная переменная с автоблокировщиком будет уничтожена,
# автоблокировщик перестанет существовать, и блокировка снимется
def change_x(self):
with QtCore.QМutexLocker(MyТhread.mutex):
print("x =,", MyТhread.x, "id =", self.id)
MyТhread.x += 5
self.sleep(2)
print("x =", MyТhread.x, "id -"
- , self.id)
MyThread.x += 34
print("x =", MyТhread.x, "id -"
- , self.id)
# Блокировка автоматически снимется
class MyWindow(QtWidgets.QPushButton):
def init (self):
QtWidgets.QPushButton. init (self)
self .setText("Зaкpьrrь окно")
self .clicked.connect(QtWidgets.QApplication.instance().quit)
def load_data(self, sp):
for i in range(l, 11): # Имитируем процесс
time.sleep(2) # Что-то загружаем
sp .showMessage("Зaгpyзкa данных... {0)%".format(i * 10),
QtCore.Qt.AlignmentFlag.AlignHCenter 1
QtCore.Qt.AlignmentFlag.AlignВottom,
QtCore.Qt.GlobalColor.Ыack)
# Принудительно обрабатываем собьrrия
QtWidgets.QApplication.instance().processEvents()
Глава 18. Введение в PyQt б 381
if name main
import sys
арр = QtWidgets.QApplication(sys.argv)
splash = QtWidgets.QSplashScreen(QtGui.QPixmap("splashscreen.svg"))
splash.showМessage("Загрузка данных... 0%",
QtCore.Qt.AlignmentFlag.AlignHCenter
QtCore.Qt.AlignmentFlag.AlignBottom,
QtGui.QColor("Ыасk"))
splash.show() # Отображаем заставку
# Принудительно обрабатываем события
QtWidgets.QApplication.instance().processEvents()
window = MyWindow()
window.setWindowТitle("Bывoд заставки")
window.resize(З00, 30)
window.load_data(splash) # Загружаем данные
window.show()
splash.finish(window) # Скрываем заставку
sys.exit(app.exec())
Окна
Параметр flags определяет тип создаваемого oIOia. Доступные для задания ТШIЪI будут рас
смотрены в разд. 19.1.1.
Указать ссьmку на родительский компонент и, возможно, тип окна уже после его создания
позволяет метод setParent() окна. Формат метода:
sеtРаrеnt(<Родитель>[, <Тип окна>])
Получить ссылку на родительский компонент можно с помощью метода parentWidget().
Если компонент не имеет родителя, возвращается значение None.
Для изменения текста в заголовке окна предназначен метод setWindowТitle(<Новый текст>).
А метод windowТitle() возвращает текст, в1�1веденный в заголовке OIOia.
После создания окна необходимо вызвать у него метод show(), чтобы вывести окно на эк
ран. Для скрытия OIOia предназначен метод hide(). Для отображения и скрытия окна также
можно пользоваться методом setVisiЫe(<Состояние>). Если параметром этого метода пе
редано значение тrue, oIOiq будет отображено, а если значение False - скрыто. Пример
отображения окна:
window.setVisiЬle(True)
Проверить, видимо ли окно в настоящее время или нет, позволяет метод isVisiЫe(), кото
рый возвращает True, если OIOIO видимо, и False.-11 противном случае. Кроме того, можно
воспользоваться методом isHidden()- он возвращает тrue, если окно скрыто, и False -
в противном случае.
QtCore.Qt.WindowТype.FramelessWindowHint
QtCore.Qt.Window'Гype.Window'ГitleHint)
♦ WindowsystemМenuHint - добавляет в заголовок системное меню и кнопку закрытия;
♦ WindowМinimizeButtonHint - добавляет в заголовок кнопку сворачивания;
♦ WindowМaximizeButtonHint - добавляет в заголовок кнопку разворачивания;·
♦ WindowМinМaxButtonsHint - добавляет в заголовок кнопки сворачивания и разворачива
ния;
♦ WindowCloseButtonHin� - добавляет в заголовок кнопку закрытия;
♦ WindowContextHelpButtonHint - добавляет в заголовок кнопку вывода справки;
♦ WindowStaysOnTopHint - сообщает системе, что окно всегда должно отображаться по
верх всех других окон;
♦ WindowStaysOnBottomНint - сообщает системе, что окно всегда должно быть располо
жено позади всех других окон.
Получить все установленные элементы-типы окна из программы позволяет метод
windowFlags() .
ВНИМАНИЕ!
Начало координат расположено в левом верхнем углу. Ось х направлена вправо, а ось У -
вниз.
Примеры:
window.setGeornetry(l00, 100, 100, 70)
window.setGeornetry(QtCore.QRect(l00, 100, 100, 70))
♦ setFixedSize() - задает у текущего окна фиксированные ширину и высоту и делает его
размеры неизменяемыми. Форматы вызова такие же, как и у метода resize(). Пример:
window.setFixedSize(l00, 70)
window. setFixedSize(QtCore.QSize(l00, 70))
♦ setFixedWidth(<Ширина>) - задает у текущего окна фиксированную ширину и делает ее
неизменяемой;
♦ setFixedHeight (<Высота>) - задает у текущего окна фиксированную высоту и делает ее
неизменяемой;
♦ setMinirnurnSize() - задает минимальные размеры окна. Форматы вызова такие же, как
и у метода resize(). Пример:
window. setMinirnurnSize(l00, 70)
window.setMinirnurnSize(QtCore.QSize(l00, 70))
♦ setMinirnurnWidth(<Ширина>) - задает минимальную ширину;
♦ setMinirnurnНeight (<Высота>) - задает минимальную высоту;
♦ setMaxirnurnSize() - задает максимальные размеры окна. Форматы вызова такие же, как
и у метода resize(). Пример:
window.setMaxirnurnSize(l00, 70)
window.setMaxirnurnSize(QtCore.QSize(l00, 70))
♦ setMaxirnurnWidth (<Ширина>) - задает максимальную ширину;
386 Часть 11. Библиотека PyQt б
screen = window.screen()
rect = screen.geometry()
print(rect.left(), rect.top()) # о о
print(rect.width(), rect.height()) # 2560 1440
♦ availaЬleGeometry() - возвращает объект класса QRect, содержащий координаты и
размеры доступной части экрана (не занятой панелью задач и иными панелями):
rect = screen.availaЬleGeometry()
print(rect.left(), rect.top()) # о о
print(rect.width(), rect.height()) # 2560 100
Пример отображения окна приблизительно по центру экрана показан в листинге 19.2.
Если нужно расположить окно в правом верхнем углу экрана, надо,заменить код из преды
дущего примера, выравнивающий окно по центру, следующим кодом:
screen_size = window.screen() .availaЫeSize()
х = screen_size.width() - window.frarneSize().width()
window.move(x, О)
Следует заметить, что экран может быть разделен на несколько рабочих столов. Это необ
ходимо учитывать при размещении окна (за более подробной информацией обращайтесь
к документации по классу QScreen).
ПРИМЕЧАНИЕ
Классы QPoint, QSize и QRect предназначены для работы с целыми числами. Чтобы рабо
тать с вещественными числами, необходимо использовать классы QPointF, QSizeF и QRectF
из модуля QtCore.
♦ isNull() - возвращает True, если ширина и высота равны нуmо, и False - в противном
случае;
♦ isValid() - возвращает тrue, если ширина и высота больше или равны нулю, и
False - в противном случае;
♦ isEmpty() - возвращает тrue, если ширина или высота меньше или равна нулю, и
False - в противном случае;
♦ scale() - задает у текущей области новые размеры. Форматы метода:
sсаlе(<Новые размеры QSize>, <ТИп преобразования>)
sсаlе(<Новая ширина>, <Новая высота>, <Тип преобразования>)
В параметре <Тип преобразования> могут быть указаны следующие элементы перечис
ления AspectRatioMode из модуля QtCore.Qt:
• IgnoreAspectRatio - не сохраняет пропорции сторон;
• KeepAspectRatio - производится попытка масштабирования старой области внутри
новой области без нарушения пропорций;
• KeepAspectRatioByExpanding - производится попытка полностью заполнить новую
область без нарушения пропорций старой области.
Если новая ширина или высота имеет значение О, размеры изменяются без сохранения
пропорций, вне зависимости от значения параметра <Тип преобразования>. Примеры:
>>> s = QtCore.QSize(50, 20)
>>> s.scale(70, 60, QtCore.Qt.AspectRatioMode.IgnoreAspectRatio); s
PyQt6.QtCore.QSize(70, 60)
>>> s = QtCore.QSize(50-, 20)
>>> s.scale(70, 60, QtCore.Qt.AspectRatioMode.KeepAspectRatio); s
PyQt6.QtCore.QSize(70, 28)
>>> s = QtCore.QSize(50, 20)
>>> s.scale(70, 60, QtCore.Qt.AspectRatioMode.KeepAspectRatioByExpanding); s
PyQt6.QtCore.QSize(150, 60)
♦ isValid() - возвращает True, если left() < right() и top() < bottom(), и False -
в противном случае;
♦ isEmpty() - возвращает True, если left() > right() или top() > bottom(), и False -
в противном случае;
♦ noпnalized() - исправляет ситуацию, при которой left() > right() или top() >
bottom(), и возвращает новый объект класса QRect с исправлешп,1ми координатами:
>>> r = QtCore.QRect(QtCore.QPoint(409, 314), QtCore.QPoint(l0, 15))
>>> r
PyQt6.QtCore.QRect(409, 314, -398, -298)
>>> r.noпnalized()
PyQtб.QtCore.QRect(l0, 15, 400, 300)
Если во втором параметре указано значение True, то точка должна быть расположена
строго внутри области, а не на ее границе (значение параметра по умолчанию - False).
Пример:
>>> r = QtCore.QRect(0, О, 400, 300)
>>> r.contains(0, 10), r.contains(0, 10, True)
(True, False)
QtCore.Qt.WindowState.WindowMaximized))
QtCore.Qt.WindowState.WindowFullScreen)
Проверить текущее состояние окна позволяют следующие методы:
♦ isMinimized() - возвращает тrue, если окно свернуто, и False - в противном случае;
♦ isMaximized() - возвращает True, если окно развернуто, и False - в противном случае;
♦ isFullScreen() - возвращает True, если у окна вкточен полноэкранный режим, и
False - в противном случае;
♦ isActiveWindow() - возвращает True, если окно активно, и False - в противном случае;
♦ windowstate() - возвращает комбинацию флагов, обозначающих текущее состояние
окна.
Пример проверки использования полноэкранного режима:
if window.windowState() & QtCore.Qt.WindowState.WindowFullScreen:
рrint("Полноэкранный режим")
Пример разворачивания и сворачивания окна приведен в листинге 19.4.
class MyWindow(QtWidgets.QWidget):
def init (self, parent=None):
QtWidgets.QWidget. init (self, parent)
self.btnМin = QtWidgets.QPushВutton("Свернуть")
self.btnМax = QtWidgets.QPushButton("Paзвepнyть")
self.btnFull = QtWidgets.QPushВutton("Полный экран")
self.btnNormal = QtWidgets.QPushButton("Hopмaльный размер")
vbox = QtWidgets.QVВoxLayout()
vbox.addWidget(self.btnМin)
vbox.addWidget(self.btnМax)
vbox.addWidget(self.btnFull)
vbox.addWidget(self.btnNormal)
self.setLayout(vbox)
self.btnМin.clicked.connect(self.on_min)
self.btnМax.clicked.connect(self.on_max)
self.btnFull.clicked.connect(self.on_full)
self.btnNormal.clicked.connect(self.on_normal)
def on_min(self\:
self.showМinimized()
def on_max(self):
self.showМaximized()
def on_full(self):
self.showFullScreen()
def on_normal(self):
self.showNormal()
if name main
import sys
Глава 19. Окна 401
арр = QtWidgets.QApplication(sys.argv)
window = MyWindow()
window.setWindowТitle("Paзвopaчивaниe и сворачивание окна")
window.resize(ЗOO, 100)
window.show()
sys.exit(app.exec())
Создадим два независимых окна. В первом окне разместим кнопку, по нажатии которой
откроется модальное окно - оно будет блокировать только первое окно, но не второе. Мо
дальное окно выведем примерно по центру родительского окна (листинг 19.6).
арр = QtWidgets.QApplication(sys.argv)
windowl = QtWidgets.QWidget()
windowl.setWindowТitle("Oбычнoe окно")
windowl.resize(З00, 100)
button = QtWidgets.QPushButton("Oткpыть модальное окно")
button.clicked.connect(show_modal_window)
vbox = QtWidgets.QVВoxLayout()
vbox.addWidget(button)
windowl.setLayout(vbox)
windowl .show()
window2 = QtWidgets.QWidget()
window2.setWindowТitle("Этo окно не будет блокировано")
window2.resize(S00, 100)
window2. show()
sys.exit(app.exec())
Ес�и запустить программу и нажать кнопку Открыть модальное окно, откроется окно,
выровненное примерно по центру родительского окна. При этом получить доступ к роди
тельскому окну можно только после закрытия модального окна. Второе окно блокировано
не будет. Однако, если заменить элемент WindowМodal перечисления WindowМodality элемен-
том ApplicationМodal, оба окна будут блокированы.
О_братите внимание, что в конструктор модального окна мы передали ссьшку на первое
окно и элемент Window перечисления WindowТype. Если не указать ссьшку, то первое окно
блокироваться не будет, а если не указать элемент Window, окно вообще не откроется. Кроме
того, мы объявили переменную modalWindow глобальной, иначе при достижении конца
функции переменная выйдет из области видимости и окно будет автоматически удалено.
Чтобы объект окна автоматически удалялся при закрытии окна, атрибуту WA_ DeleteOnClose
в методе setAttribute() бьшо присвоено значение True.
Глава 19. Окна 403
PyQt предоставляет класс QDialog, который предназначен для создания диалоговых окон,
самостоятельно делает окно модальным и выводит его в центре экрана или родительского
окна. Кроме того, этот класс предоставляет множество специальных методов, позволяющих
дождаться закрытия окна, определить статус завершения и выполнить друтие действия.
Подробно класс QDialog мы рассмотрим в главе 27.
pal = window.palette()·
pal.setColor(QtGui.QPalette.ColorGroup.Normal, QtGui.QPalette.ColorRole.Window,
QtGui.QColor("#008800"))
pal.setColor(QtGui.QPalette.ColorGroup.Inactive,
QtGui.QPalette.ColorRole.Window, QtGui.QColor("#ff0000"))
window.setPalette(pal)
label = QtWidgets.QLabel("Teкcт надписи")
label.setAlignrnent(QtCore.Qt.AlignrnentFlag.AlignHCenter)
label.setStyleSheet("background-color: #ffffff;")
label.setAutoFillBackground(True)
vbox = QtWidgets.QVBoxLayout()
vbox.addWidget(label)
window.setLayout(vbox)
'нindow.show()
sys.exit(app.exec())
Третий, четвертый и пятый форматы позволяют указать изображение в ·виде объекта класса
QPiюnap или Qirnage. Конструкторы этих классов принимают путь к файлу с нужным изо
бражением.
Шестой формат создает новую кисть на основе rрадиента, представленного объектом класса
QGradient (см. главу 25), а седьмой- на основе указанной кисти.
После настройки палитры у окна также следует вызвать метод setpalette() (см.разд. 19.9),
передав ему измененный объект палитры. Следует помнить, что компоненты-потомки по
умолчанию имеют прозрачный фон и не перерисовываются автоматически. Чтобы вклю
чить перерисовку, необходимо передать значение True в метод setAutoFillBackground().
Указать фоновое изображение также можно с помощью СSS-атрибутов background и
background-irnage. Воспользовавшись СSS-атрибутом background-repeat, можно задать ре
жим повтора фонового рисунка: repeat (повтор по горизонтали и вертикали), repeat-x
(только по горизонтали), repeat-y (только по вертикали) и no-repeat (не повторяется).
Создадим окно с надписью. Для активного окна установим одно изображение (с помощью
изменения палитры), а для надписи- другое (с помощью СSS-атрибута background-irnage)
(листинг 19.9).
ВНИМАНИЕ!
После закрытия последнего окна программы выполнение программы завершится.
При указании таблицы стилей у программы и окна можно использовать привычный формат
объявления СSS-стилей:
<Селектор> {<Определение стилей>}
<Селектор> записывается в следующем формате:
<Основной селектор>[<Дополнительный селектор>] [<Псевдокласс>] [<Псевдоселектор>]
Параметр <Основной селектор> указывает на класс компонента. Его можно задать в одном
из следующих форматов:
♦ * (звездочка) - указывает на все компонентъ1. Например, так можно задать у всех ком
понентов зеленый цвет текста:
* {color: green;}
♦ <Класс> - указывает на компоненты, относящиеся к заданному <Классу> и его подклас
сам. Задание красного цвета текста у всех компонентов, относящихся к классу
QAЬstractButton и его подклассам (т. е. у командных кнопок, флажков и перекточате
лей), осуществляется так:
QAЬstractButton {color: red;}
♦ .<Класс> - указывает только на компоненты, относящиеся лишь к заданному <Классу>,
но не к его подклассам. Указание полужирного шрифта у всех компонентов класса
QPushвutton (командных кнопок), но не у его подклассов, осуществляется так:
.QPushВutton {font-weight: bold;}
Параметр <Дополнительный селектор> задает дополнительные параметры компонента. Его
форматы:
♦ [ <Свойство>="<Значение>"] - указанное <Свойство> компонента должно иметь заданное
<Значение>. Полужирный шрифт у кнопки, чье свойство default имеет значение true
(кнопки по умолчанию), задается так:
Qi?ushВutton[default="true"] {font-weight: bold;}
♦ #<Имя> - указывает на компонент с заданным <Именем>. <Имя> можно задать вызовом
у компонента метода setObjectName (<Имя>) , а' получить вызовом метода
objectName (). Красный цвет текста у кнопки с именем ЬtnRed задается так:
QPushВutton#ЬtnRed {color: red;}
Параметр <Псевдокласс> указывает на отдельную составную часть сложного компонента.
Он записывается в формате : : <Обозначение составной части>. Вот пример указания графи
ческого изображения у кнопки разворачивания раскрывающегося списка (обозначение этой
составной части - down-a�row):
QComЬoBox::down-arrow {image: url(arrow.png);}
Параметр <Псевдоселектор> указывает на состояние компонента (должна ли быть кнопка
нажата, должен ли флажок быть установленным и т. п.). Он может быть записан в двух
форматах:
♦ :<Обозначение состояния> - компонент должен находиться в указанном состоянии.
Пример указания белого цвета фона у кнопки, когда она нажата (это состояние имеет
обозначение pressed):
QPushВutton:pressed {background-color: white;}
Глава 19. Окна 411
Jlистинr 19.13. Ис
from PyQtб import QtWidgets
import sys
арр = QtWidgets.QApplication(sys.argv)
# На уровне программы задаем синий цвет текста у надписей, вложенных в группы,
# и курсивное начертание текста кнопок
app.setStyleSheet(
"QGroupBox QLabel {color: Ыuе;} QPushВutton {font-style: italic}")
window = QtWidgets.QWidget()
window.setWindowТitle("Taблицы стилей")
# На уровне окна задаем зеленый цвет текста у надписи с именем first и
# красный цвет текста у надписи, на которую наведен курсор МЬШIИ
window.setStyleSheet("QLabel#first {color: green;} QLabel:hover {color: red;}")
window.resize(200, 150)
# Создаем три надписи
lЫl = QtWidgets.QLaЬel("Зeлeный текст")
# Даем первой надписи имя first
lЫl.setObjectName("first")
1Ы2 = QtWidgets.QLabel("Пoлyжиpный текст")
# У второй надписи указываем полужирный шрифт
lЫ2.setStyleSheet("font-weight: bold")
lЫЗ = QtWidgets.QLabel("Cиний текст")
# Создаем кнопку
btn = QtWidgets.QPushВutton("Курсивный текст")
# Создаем группу
Ьох = QtWidgets.QGroupBox ("Группа")
# Создаем контейнер, помещаем в него третью надпись и вставляем в группу
ЬЬох = QtWidgets.QVBoxLayout()
bbox.addWidget(lЫЗ)
box.setLayout(bbox)
# Создаем еще один контейнер, помещаем в него две первые надписи, группу,
# кнопку и вставляем в окно
mainЬox = QtWidgets.QVВoxLayout()
mainЬox.addWidget(lЬll)
Глава 19. Окна 413
rnainЬox.addWidget(lЫ2)
rnainЬox.addWidget(box)
rnainЬox.addWidget(btn)
window.setLayout(rnainЬox)
window.show()
sys.exit(app.exec())
� Табл... □ х
Зеленый текст
Полужирный текст
Группа
Синий текст
def on_clicfed():
рrint("КНопка нажата. Функция on_clicked()")
class MyClass():
def init (self, х=О):
self.x = х
def call (self) :
рrint("КНопка нажата. Метод MyClass. call ()")
print("x =", self.x)
def on_clicked(self):
print("кнопка нажата. Метод MyClass.on_clicked()")
obj = MyClass ()
арр = QtWidgets.QApplication(sys.argv)
button = QtWidgets.QPushВutton("Н= меня")
# Назначаем обработчиком функцию
button.clicked.connect(on_clickep)
# Назначаем обработчиком метод класса
button.clicked.connect-(obj .on_clicked)
416 Часть 11. Библиотека PyQt б
'
кнопка нажата. Метод MyClass._call_()
х = 10
кнопка нажата. Метод MyClass. call ()
х = 5
Тип соединения в вызове метода connect() следует указывать при' использовании несколь
ких потоков. Можно указать один из следующих элементов перечисления connectionType
из модуля QtCore.Qt:
♦ AutoConnection - если источник сигнала и обработчик находятся в одном потоке, то он
эквивалентен элементу DirectConnection, а если в разных потоках, то - QueuedConnection;
♦ Directcoiшection - обработчик вызывается сразу после генерирования сигнала и вы
полняется в потоке его источника;
♦ QueuedConnection - сигнал помещается в очередь обработки событий, а его обработчик
выполняется в потоке приемника сигнала;
♦ BlockingQueuedConnection - аналогичен элементу QueuedConnection за тем искточени
ем, что поток блокируется на время обработки сигнала. Обратите внимание, что источ
ник и обработчик сигнала обязательно должны выполняться в разных потоках;
♦ UniqueConnection - обработчик можно назначить только один раз. Этот элемент с по
мощью оператора I может быть объединен с любым из представленных. Примеры:
# Эти два обработчика будут успешно назначены и вьmолнены
button.clicked.connect(on_clicked)
button.clicked.connect(on_clicked)
# А эти два обработчика назначены не будут
button.clicked.connect(on_clicked, QtCore.Qt.AutoConnection 1 \
QtCore.Qt.UniqueConnection)
button.clicked.connect(on_clicked, QtCore.Qt.AutoConnection 1 \
QtCore.Qt.UniqueConnection)
# Тем не менее эти два обработчика будут назначены, поскольку они разные
button.clicked.connect(on_clicked, QtCore.Qt.AutoConnection 1 \
QtCore.Qt.UniqueConnection)
button.clicked.connect(obj.on_clicked, QtCore.Qt.AutoConnection 1 \
QtCore.Qt.UniqueConnection)
Глава 20. Обработка сигналов и событий 417
♦ SingleShotConnection - обработчик выполнится всего один раз, после чего его назна
чение сигналу будет автоматически отменено. Этот элемент с помощью оператора I мо
жет быть объединен с любым из представленных. Пример:
# Назначенный обработчик будет выполнен лишь единожды
button .clicked.connect(on_once_clicked, QtCore.Qt.AutoConnection 1 \
QtCore.Qt.SingleShotConnection)
Если параметру no_receiver_check дано значение False, то в случае, если приемник сигнала
уже не существует, возникнет ошибка. Если же параметру дано значение тrue, то в таком
случае ошибка.не возникнет.
20.1.1. Слоты
Классы PyQt уже содержат ряд слотов, которые могут быть использованы в качестве обра
ботчиков сигналов. Например, класс QApplication поддерживает слот quit(), завершающий
программу. В листинге 19.2 показан пример его использования.
Любой пользовательский метод можно сделать слотом, для чего необходимо перед его оп
ределением вставить декоратор @pyqtSlot(). Формат декоратора:
@QtCore.pyqtSlot([<Tип 1>, <Тип 2>, . . ., <Тип N>] [, name=None] [, result=None])
Можно указать типы параметров, принимаемых слотом (bool, int и др.). При задании типа
данных С++ его имя необходимо указать в виде строки. Если слот не принимает парамет
ров, типы не указьшаются. В параметре name можно передать имя слота в виде строки. Если
этот параметр не задан, в качестве имени слота будет использовано имя метода. Параметр
result задает тип результата, возвращаемого слотом. Если слот не возвращает результата,
этот параметр не задается.
Можно создать несколько одноименных слотов, принимающих разные параметры (пере
груженный слот). Пример приведен в листинге 20.3.
class MyClass(QtCore.QObject):
def init (self):
QtCore.QObject. init (self)
418 Часть 11. Библиотека PyQt б
@QtCore.pyqtSlot()
def on_clicked(self):
print("КНопка нажата. Слот on_clicked()")
@QtCore.pyqtSlot(bool, name="myslot")
def on_clicked2(self, status):
рrint("КНопка нажата. Слот myslot(bool)", status)
obj = MyClass()
арр = QtWidgets.QApplication(sys.argv)
button = QtWidgets.QPushБutton("Haжми меня")
button.clicked.connect(obj.on_clicked)
button.clicked.connect(obj.myslot)
button.show()
sys.exit(app.exec())
self.buttonl.clicked.connect(MyClass(l0))
♦ передать ссылку на обработчик и данные в функцmо paitial() из модуля functools.
Формат функции:
partial(<Функция> [, *<Позиционные параметры>] [, **<Именованные параметры>])
Пример передачи параметра в обработчик:
from functools import partial
self.buttonl.clicked.connect(partial(self.on_clicked buttonl, 10))
Глава 20. Обработка сигналов и событий 419
class MyWindow(QtWidgets.QWidget):
def init (self, parent=None):
QtWidgets.QWidget. init (self, parent)
self.setWindoWТitle("Блoкиpoвкa и удаление обработчика")
self.reize(З00, 150)
self.buttonl = QtWidgets.QPushButton("Нажми меня")
420 Часть 1/. Библиотека PyQt 6
self.button2 = QtWidgets.QPushВutton("Блoкиpoвaть")
self.buttonЗ = QtWidgets.QPushButton("Paзблoкиpoвaть")
self.button4 = QtWidgets.QPushButton("Yдaлить обработчик")
self.buttonЗ.setEnaЬled(False)
vbox = QtWidgets.QVВoxLayout()
vbox.addWidget(self.buttonl)
vbox.addWidget(self.button2)
vbox.addWidget(self.buttonЗ)
vbox.addWidget(self.button4)
self.setLayout(vbox)
self.buttonl.clicked.connect(self.on_clicked buttonl)
self.button2.clicked.connect(self.on_clicked_button2)
self.button3.clicked.connect(self.on_clicked_button3)
self.button4.clicked.connect(self.on_clicked_button4)
@QtCore.pyqtSlot()
def on_clicked_buttonl(self):
print("Нажата кнопка buttonl")
@QtCore.pyqtSlot()
def on_clicked_button2(self):
self.buttonl.ЫockSignals(True)
self.button2.setEnaЬled(False)
self.buttonЗ.setEnaЫed(True)
@QtCore.pyqtSlot()
def on_clicked_buttonЗ(self):
self.buttonl.ЫockSignals(False)
self.button2.setEnaЫed(True)
self.buttonЗ.setEnaЫed(False)
@QtCore.pyqtSlot()
def on_clicked_button4(self):
self.buttonl.clicked.disconnect(self.on_clicked buttonl)
self.button2.setEnaЫed(False)
self.buttonЗ.setEnaЫed(False)
self.button4.setEnaЫed(False)
if name main
import sys
арр = QtWidgets.QApplication(sys.argv)
window = MyWindow()
window.show()
sys.exit(app.exec())
Если нажать кнопку Нажми меня, в консоли будет выведена строка Нажата кнопка buttonl.
Нажатие кнопки Блокировать производит блокировку обработчика - теперь при нажатии
кнопки Нажми меня никаких сообщений в консоли не выводится., Отменить блокировку
можно с помощью кнопки Разблокировать. Нажатие кнопки Удалить обработчик произ
водит полное удаление обработчика - в этом случае, чтобы обрабатывать нажатие кнопки
Нажми меня, необходимо заново назначить обработчик.
Также можно временно сделать компонент недоступным, воспользовавшись следующими
методами:
Глава 20. Обработка сигналов и событий 421
class MyWindow(QtWidgets.QWidget):
mysignal = QtCore.pyqtSignal(int, int)
def init (self, parent=None):
QtWidgets.QWidget. init (self, parent)
self.setWindowТitle("Пoльзoвaтeльcкий сигнал")
self.resize(З00, 100)
self.buttonl = QtWidgets.QPushButton("Haжми меня")
self.button2 = QtWidgets.QPushВutton("КНoпкa 2")
vbox = QtWidgets.QVВoxLayout()
vbox.addWidget(self.buttonl)
vbox.addWidget(self.button2)
self.setLayout(vbox)
self.buttonl.clicked.connect(self.on_clicked_buttonl)
self.buttoп2.clicked.connect(self.on_clicked_button2)
self.mysignal.connect(self.on_mysignal)
def on_clicked_buttonl(self):
print("Нажата кнопка buttonl")
# Генерируем сигналы
self.button2.clicked[bool] .emit(False)
self.mysignal.emit(l0, 20)
def on_clicked_button2(self):
print("Нажата кнопка button2")
def on_mysignal(self, х, у):
рrint("Обработан пользовательский сигнал mysignal()")
print("х =", х, "у =", у)
self.mysignal.emit(20)
self.mysignal.emit("Пpивeт!")
self.mysignal.emit([l, "2"])
class MyWindow(QtWidgets.QWidget):
def init (self, parent=None):
QtWidgets.QWidget. init (self, parent)
self.setWindowТitle("Чacы ")
self.resize(200, 100)
self.timer id = О
self.label = QtWidgets.QLabel("")
self.laЬel.setAlignment(QtCore.Qt.AlignmentFlag.AlignНCenter)
self.buttonl = QtWidgets.QPushButton("Зaпycтить")
self.button2 = QtWidgets.QPushButton("Ocтaнoвить")
self.button2.setEnaЫed(False)
vbox = QtWidgets.QVВoxLayout()
vbox.addWidget(self.label)
vbox.addWidget(self.buttonl)
vbox.addWidget(self.button2)
self.setLayout(vbox)
self.buttonl.clicked.connect(self.on_clicked_buttonl)
self.button2.clicked.connect(self.on_clicked_button2)
def on_clicked_buttonl(self):
# Задаем интервал в 1 секунду и «приблизительный» таймер
self.timer_id = self.startTimer(l000,
timerType=QtCore.Qt.TimerType.VeryCoarseTimer)
self.buttonl.setEnaЫed(False)
self.button2.setEnaЬled(True)
def on_clicked_button2(self):
if self.timer id:
self. killTimer (
. self.timer_id)
self.timer id = О
self.buttonl.setEnaЬled(True)
self.button2.setEnaЬled(False)
def timerEvent(self, event):
self.label.setText(time.strftime ("%И: %М: %S"))
if name main
import sys
арр = QtWidgets.QApplication(sys.argv)
window = MyWindow( )
window.show()
sys.exit(app.exec())
class MyWindow(QtWidgets.QWidget):
def init (self, parent=None):
QtWidgets.QWidget. init (self, parent)
self.setWindoWТitle("Чacы")
self.resize(200, 100)
self.label = QtWidgets.QLabel("")
self.label.setAlignment(QtCore.Qt.AlignmentFlag.AlignHCenter)
self.buttonl = QtWidgets.QPushВutton("Зaпycтить")
self.button2 = QtWidgets.QPushВutton("Ocтaнoвить")
self.button2.setEnaЫed(False)
vbox = QtWidgets.QVВoxLayout()
vbox.addWidget(self.label)
vbox.addWidget(self.buttonl)
vbox.addWidget(self.button2)
self.setLayout(vbox)
self.buttonl.clicked.connect(self.on_clicked buttonl)
self.button2.clicked.connect(self.on_clicked_button2)
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.on_timeout);
def on_clicked_buttonl(self):
self.timer.start(l000) # 1 секунда
426 Часть 11. Библиотека PyQt б
self.buttonl.setEnaЫed(False)
self.button2.setEnaЫed(True)
def on_clicked_button2(self):
self.timer.stop()
self.buttonl.setEnaЫed(True)
self.button2.setEnaЫed(False)
def on_timeout(self):
self.laЬel.setText(time.strftime("%H:%M:%S"))
if -name- main
import sys
арр = QtWidgets.QApplication(sys.argv)
window = MyWindow()
window.show()
sys.exit(app.exec())
Статический метод singleShot() класса QТimer запускает таймер, настраивает его для одно
кратного срабатывания и указьIВает функцию или метод, который будет вызван по истече
нии заданного интервала. Формат метода:
singleShot(<Интepвaл>[, <ТИп таймера>], <Функция или метод>)
/
Примеры:
QtCore.QTimer.singleShot(lOOO, self.on_timeout)
QtCore.QTimer.singleShot(lOOO, QtWidgets.QApplication.instance() .quit)
·c1ass MyWindow(QtWidgets.QWidget):
def init (self, parent=None):
QtWidgets.QWidget. init (self, parent)
self.resize(300, 100)
def event(self, е):
if e.type() �= QtCore.QEvent.Type.KeyPress:
print("Нажата клавиша на клавиатуре")
print("Koд:", e.key(), ", текст:", e.text())
elif e.type() == QtCore.QEvent.Type.Close:
print("Oкнo закрЫ'l'о")
Глава 20. ·Обработка сигналов и событий 429
if name main
import sys
арр = QtWidgets.QApplication(sys.argv)
window = MyWindow ()
window.show()
sys.exit(app.exec())
Перехватывать все события следует только в самом крайнем случае. В обычных ситуациях
надо обрабатывать только нужные события.
class MyWindow(QtWidgets.QWidget):
def init (self, parent=None):
QtWidgets.QWidget. init (self, parent)
self.resize(З00, 100)
def changeEvent(self, е):
if e.type() = QtCore.QEvent.Type.WindowStateChange:
if self.isMinimized():
print("Oкнo свернуто")
elif self.isMaximized():
print("Окно раскрыто до максимаJJЬНЫХ размеров")
430 Часть 11. Библиотека PyQt б
elif self.isFullScreen():
print("Полноэкранный режим")
elif self.isActiveWindow():
print("Oкнo находится в фокусе ввода")
QtWidgets.QWidget.changeEvent(self, е) # Отправляем дальше
def showEvent(self, е):
print("Oкнo отображено")
QtWidgets.QWidget.showEvent(self, е) # Отправляем дальше
def hideEvent(self, е):
print("Oкнo скрыто")
QtWidgets.QWidget.hideEvent(self, е) # Отправляем дальше
class MyWindow(QtWidgets.QWidget):
def init (self, parent=None):
QtWidgets.QWidget._init_(self, parent)
self.resize(З00, 100)
def moveEvent(self, е):
print("x = {О}; у= {l}".format(e.pos().x(), e.pos().y()))
QtWidgets.QWidget.moveEvent(self, е) # Отправляем дальше
def resizeEvent(self, е):
print("w = {О}; h = {l}".format(e.size().width(), e.size().height()))
QtWidgets.QWidget.resizeEvent(self, е) # Отправляем дальше
Глава 20. Обработка сигналов и событий 431
class MyWindow(QtWidgets.QWidget):
def init (self, parent=None):
QtWidgets.QWidget. init (self, parent)
self.resize(З00, 100)
def closeEvent(self, е):
result = QtWidgets.QMessageBox.question(self,
"Подтверждение закрытия окна",
"Вы действительно хотите закрыть окно?")
if result == QtWidgets.QМessageBox.StandardButton.Yes:
e.accept ()
QtWidgets.QWidget.closeEvent(self, е)
else:
e.ignore()
class MyLineEdit(QtWidgets.QLineEdit):
def init (self, id, parent=None):
QtWidgets.QLineEdit. init (self, parent)
self.id = id
def focusinEvent(self, е):
рrint("Получен фокус полем", self.id)
QtWidgets.QLineEdit.focusinEvent(self, е)
def focusOutEvent(self, е):
рrint("Потерян фокус полем", self.id)
QtWidgets.QLineEdit.focusOutEvent(self, е)
class MyWindow(QtWidgets.QWidget):
def init (self, parent=None):
QtWidgets.QWidget. init (self, parent)
self.resize(З00, 100)
self.button = QtWidgets.QPushButton("Ycтaнoвить фокус на поле 2")
self.linel = MyLineEdit(l)
self.line2 = MyLineEdit(2)
self.vbox = QtWidgets.QVВoxLayout()
. self.vbox.addWidget(self.button)
self.vbox.addWidget(self.linel)
self.vbox.addWidget(self.line2)
self.setLayout(self.vbox)
self.button.clicked.connect(self.on_clicked)
# Задаем порядок обхода с помощью клавиши <ТаЬ>
QtWidgets.QWidget.setTabOrder(self.linel, self.line2)
QtWidgets.QWidget.setTaЬOrder(self.line2, self.button)
Глава 20. Обработка сигналов и событий 435
def on_clicked(self):
self.line2.setFocus()
if name main
import sys
арр = QtWidgets.QApplication(sys.argv)
window = MyWindow()
window.show()
sys.exit(app.exec())
class MyLineEdit(QtWidgets.QLineEdit):
def init (self, parent=None):
QtWidgets.QLineEdit. init (self, parent)
self.id = None
def event(self, е):
if e.type() == QtCore.QEvent.Type.Shortcut:
if self.id == e.shortcutid():
self.setFocus(QtCore.Qt.FocusReason.ShortcutFocusReason)
return True
return QtWidgets.QLineEdit.event(self, е)
class MyWindow(QtWidgets.QWidget):
def init (self, parent=None):
QtWidgets.QWidget. init (self, parent)
self.resize(З00, 100)
self.label = QtWidgets.QLabel( "Устано&вить фокус на поле l")
self.lineEditl = QtWidgets.QLineEdit()
self.label.setBuddy(self.lineEditl)
self.lineEdit2 = MyLineEdit()
self.lineEdit2.id = self.lineEdit2.grabShortcut(
QtGui. QKeySequence .mnemonic("&е") )
Глава 20. Обработка сигналов и событий 437
if name main
import sys
арр = QtWidgets.QApplication(sys.argv)
window = MyWindow()
window.show()
sys.exit(app.exec())
Для назначения клавиш быстрого доступа также можно воспользоваться классом QShortcut
из модуля QtGui. В этом случае назначение клавиши у второго текстового поля будет вы
глядеть так:
self.lineEdit2 = QtWidgets.QLineEdit()
self.shc = QtGui.QShortcut(QtGui.QKeySequence.mnemonic("&e"), self)
self.shc.setContext(QtCore.Qt.ShortcutContext.WindowShortcut)
self.shc.�ctivated.connect(self.lineEdit2.setFocus)
Еще можно использовать класс QAction из модуля QtGui. Назначение клавиши у второго
текстового. поля выглядит следующим образом:
self.lineEdit2 = QtWidgets.QLineEdit()
self.act = QtGui.QAction(self)
self.act.setShortcut(QtGui.QKeySequence.mnemonic("&e"))
self.act.triggered.connect(self.lineEdit2.setFocus)
self.addAction(self.act)
Если событие было успешно обработано, необходимо вызвать метод accept() объекта
события. Чтобы родительский компонент мог получить событие, вместо метода accept()
необходимо вызвать метод ignore().
ранее установленный для действия курсор будет отменен. Действие задается в виде
одного из приведенных ранее элементов перечисления DropAction. Пример изменения
курсора мыши для перемещения:
drag.setDragCursor(QtGui.QPixmap("move_cursor.png"),
QtCore.Qt.DropAction.MoveAction)
♦ dragcursor (<Действие>) -возвращает объект класса QPixmap, представляющий курсор
мыши для заданного действия;
♦ source() - возвращает ссылку на компонент-источник;
♦ target() - возвращает ссьmку на компонент-приемник или значение None, ·если компо
нент находится в другой программе;
♦ supportedActions() - возвращает значение, представляющее набор допустимых в те
кущей операции действий, в виде комбинации перечисленных ранее элементов перечис
ления DropAction;
♦ defaultAction() - возвращает действие по умолчанию в виде одного из приведенных
ранее элементов перечисления DropAction.
Класс QDrag поддерживает два сигнала:
♦ actionChanged(<Действие>) - генерируется при изменении действия. Новое действие
представляется одним из приведенных ранее элементов перечисления DropAction;
♦ targetChanged (<Компонент-приемник>) - генерируется при изменении компонента-прием-
ника.
Примеры назначения обработчиков этих сигналов:
drag.actionChanged.connect(self.on_action_changed)
drag.targetChanged.connect(self.on_target_changed)
мые дairnыe. Для этого внутри конструктора компонента следует вызвать метод
setAcceptDrops(<Состояние>), передав ему True, например:
self.setAcceptDrops(True)
Обработка перетаскивания и сброса объекта выполняется следующим образом:
1. Внутри специального метода dragEnterEvent () компонента проверяется МIМЕ-тип пе
ретаскиваемых данных и предлагаемое действие. Если компонент способен обработать
сброс этих данных и соглашается с предложенным действием, необходимо вызвать ме
тод acceptproposedAction() объекта события. Если нужно изменить действие, методу
setDropAction() объекта события передается новое действие, а затем у того же объекта
вызывается метод accept() вместо acceptProposedAction().
2. Если необходимо ограничить область сброса некоторым участком компонента, следует
дополнительно определить в нем метод dragMoveEvent(). Этот метод будет постоянно
вызываться при перетаскивании внутри компонента. При достижении курсором мыши
нужного участка компонента следует вызвать метод accept() объекта события, передав
ему объект класса QRect с координатами и размерами этого участка. В этом случае при
перетаскивании внутри участка метод dragMoveEvent() повторно вызываться не будет.
3. Внутри метода dropEvent() компонента производится обработка сброса.
Обработать события, возникающие в процессе перетаскивания и сбрасывания, позволяют
следующие специальные методы класса QWidget:
♦ dragEnterEvent(self, <event>) - вызывается, когда перетаскиваемые данные входят
в область компонента. Через параметр <event> доступен объект класса QDragEnterEvent;
♦ dragLeaveEvent(self, <event>) - вызывается, когда перетаскиваемые данные покида
ют область компонента. Через параметр <event> доступен объект класса QDragLeaveEvent;
♦ dragMoveEvent(self, <event>) - вызывается при перетаскивании данных внутри ком
понента. Через параметр <event> доступен объект класса QDragMoveEvent;
♦ dropEvent(self, <event>) - вызывается при сбрасывании данных в компонент. Через
параметр <event> доступен объект класса QDropEvent.
Класс QDragLeaveEvent наследует класс QEvent и не несет никакой дополнительной инфор
мации.
Цепочка наследования остальных классов выглядит так:
QEvent - QDropEvent - QDragMoveEvent - QDragEnterEvent
Класс QDragEnterEvent не содержит собственных методов.
Класс QDropEvent поддерживает следующие методы:
♦ mimeData() - возвращает объект класса QMimeData с перемещаемыми данными;
♦ possiЫeActions() - возвращает комбинацию допустимых действий при сбрасьmании:
if e.possiЫeActions() & QtCore.Qt.DropAction.MoveAction:
print("MoveAction")
if e.possiЫeActions() & QtCore.Qt.DropAction.CopyAction:
print("CopyAction")
♦ proposedAction () - возвращает предлагаемое действие по умолчанию;
♦ acceptproposedAction() - сообщает о готовности принять переносимые данные и со
гласии с предлагаемым действием по умолчанию. Этот метод (или метод accept(), под-
448 Часть 11. Библиотека PyQt б
button.move(l0, 80)
window.show()
sys.exit(app.exec())
21.2. Контейнеры-стопки
Контейнер-стопка выстраивает находящиеся в нем компоненты в линию. Существуют два
следующих класса, представляющих подобного рода контейнеры:
♦ QHBoxLayout - горизонтальная стопка;
♦ QVBoxLayout - вертикальная стопка.
Иерархия наследования для этих классов выглядит так:
(QObject, QLayoutitem) - QLayout - QBoxLayout - QHBoxLayout
(QObject, QLayoutitem) - QLayout - QBoxLayout - QVВoxLayout
Контейнеры-стопки не являются наследниками класса QWidget, вследствие чего не облада
ют собственнъ1м окном и не могут использоваться отдельно. Поэтому они обязательно
должны быть привязаны к родительскому компоненту (обычно окну или другому контейне
ру). При привязке контейнера к родитеmо контейнер автоматически вьmодится на экран.
Формат вызова конструктора обоих классов:
QHBoxLayoutlQVВoxLayout([<Poдитeль>])
Если родитель не был указан при создании объекта контейнера, привязать контейнер к ро
дитеmо можно вызовом у последнего метода setLayout (<Привязываемый контейнер>).
Пример использования класса QHBoxLayout показан в листинге 21.2, а увидеть результат
выполнения этого кода можно на рис. 21.1.
коl(Тейнер,
'(
button4 = QtWidgets.QPushButton("4")
hЬох = QtWidgets.QHBoxLayout() # Создаем контейнер
hЬox.addWidget(buttonl) # Добавляем компоненты
hЬox.addWidget(button2)
hЬox.addWidget(buttonЗ)
hЬox.addWidget(button4)
window.setLayout(hЬox) # Привязываем контейнер к родителю
window.show()
sys.exit(app.exec())
[u QHBoxlayout □ х
21.3. Контейнер-сетка
Контейнер-сетка располагает добавленные в него компоненты в ячейках воображаемой
сетки. Он реализуется классом QGridLayout. Иерархия его наследования:
(QObject, QLayoutitem) - QLayout - QGridLayout_
window.resize(250, 100)
buttonl QtWidgets.QPushButton("l")
button2 QtWidgets.QPushButton("2")
buttonЗ QtWidgets.QPushButton("3")
button4 QtWidgets.QPushButton("4")
grid = QtWidgets.QGridLayout() # Создаем сетку
grid.addWidget(buttonl, О, О) # Добавляем компоненты
grid.addWidget(button2, О, 1)
grid.addWidget(buttonЗ, 1, О)
grid.addWidget(button4, 1, 1)
window.setLayout(grid) # Привязываем контейнер к родителю
window.show()
sys.exit(app.exec())
liJ QGridlayout о х
21.4. Контейнер-форма
Контейнер-форма выстраивает компоненты в виде таблицы из двух столбцов: в левом
столбце располагаются надписи для компонентов, в правом - сами компоненты. Он реали
зуется классом QFonnLayout. Иерархия наследования выглядит так:
(QObject, QLayoutitem) - QLayout - QFonnLayout
Формат конструктора класса QFonnLayout:
QFonnLayout( [<Родитель>])
Если родитель не был указан при создании объекта контейнера, привязать контейнер к ро
дителю можно вызовом у последнего метода setLayout().
В листинге 21.4 показан код, создающий форму с применением контейнера QFonnLayout.
Результат выполнения этого кода можно увидеть на рис. 21.3.
458 Часть 11. Библиотека PyQt 6
1!J QFormlayout □ х
21.5. Стеки
Контейнер-стек в каждый момент времени выводит лишь один компонент-потомок. Он
реализуется классом QStackedLayout. Иерархия наследов_ания выглядит так:
(QObject, QLayoutitem) - QLayout - QStackedLayout
Формат конструктора класса QStackedLayout:
QStackedLayout([<Poдитeль>])
Если родитель не был указан при создании объекта контейнера,_привязать контейнер к ро
дитето можно вызовом у последнего метода setLayout() .
Класс QStackedLayout поддерживает следующие методы:
♦ addWidget(<Компонент>) - добавляет компонент в конец контейнера. Метод возвращает
индекс добавленного компонента;
♦ insertWidget(<Индекс>, <Компонент>) - добавляет компонент в позицюо контейнера,
обозначенную указанным индексом. Метод возвращает индекс добавленного компо
нента;
♦ setcurrentindex(<Индекс>) - делает видимым компонент с указанным индексом. Ме
тод является слотом;
♦ currentindex() - возвращает индекс видимого компонента;
♦ setCurrentWidget(<Компонент>) - делает видимым указа�rnый компонент. Метод явля
ется слотом;
♦ currentWidget() - возвращает ссылку на видимый компонент;
♦ setStackingMode(<Режим>) - задает режим отображения компонентов. В параметре
могут быть указаны следующие элементы перечисления StackingMode · из класса
QStackedLayout:
• StackOne - видим только один компонент (значение по умолчанию);
• StackAll - видны все компоненты;
♦ stackingMode() - возвращает режим отображения компонентов;
♦ count() - возвра�цает количество компонентов внутри контейнера;
♦ widget(<Индекс>) - возвращает ссылку на компонент, который расположен по указан-
ному индексу, или значение None при обращении по несуществующему индексу.
Для удаления и. замены компонентов следует пользоваться методами removeWidget() и
replaceWidget(), описанными в разд. 21.2.
Класс QStackedLayout поддерживает следующие сигналы:
♦ currentChanged(<Индекс>) - генерируется при выводе другого компонента. Через пара
метр внутри обработчика доступен целочисленный индекс выведенного компонента;
♦ widgetRemoved(<Индекс>) - генерируется при удалении компонента из контейнера.
Через параметр внутри обработчика доступен целочисленный индекс удаленного ком
понента.
Класс QStackedWidget также реализует стек компонентов, но представляет собой полноцен
ный компонент. Иерархия наследования выглядит так:
(QObject, QPaintDevice) - QWidget - QFrame - QStackedWidget
Глава 21. Размещение компонентов в окнах. Контейнеры 461
21.7. Группа
Группа - это область, содержащая какой-либо набор компонентов и окруженная рамкой,
на верхней границе которой выводится текст заголовка. Она реализуется классом QGroupBox.
Иерархия наследования выглядит так:
(QObj.ect, QPaintDevice) - QWidget - QGroupBox
Формат конструктора класса QGroupBox:
QGroupBox([<Родитель>] )
QGroupBox(<Teкcт заголовка>[, <Родитель>])
В тексте заголовка символ &, указанный перед буквой, задает комбинацию клавиш быстрого
доступа.
После создания объекта класса QGroupвox следует добавить компоненты в какой-либо кон
тейнер, а затем передать ссылку на этот контейнер в метод setLayout() группы.
Глава 21. Размещение компонентов в окнах. Контейнеры 463
fjJ QGroupBox □ х
�ы знает,е язык·Руthоn?
О .!::!ет
tab = QtWidgets.QТabWidget()
tab.addTaЬ(QtWidgets.QLabel("Coдepжимoe вкладки l"), "Вкладка &l")
tab.addTaЬ(QtWidgets.QLabel("Coдepжимoe вкладки 2"), "Вкладка &2")
tab.addTaЬ(QtWidgets.QLaЬel("Coдepжимoe вкладки 3"), "Вкладка &3")
tab.setCurrentindex(O)
vbox = QtWidgets.QVBoxLayout()
vbox.addWidget(tab)
window.setLayout(vbox)
window.show()
window.show()
sys.exit(app.exec())
li] QТabWidget □ х
Вкладка 1 , Вкладка l Вкладка J
:содержимое вкладки 1
♦ setTaЬShape (<Форма>) - задает форму углов у ярлыков вкладок. Могут быть указаны
следующие элементы перечисления TabShape из класса QТabWidget: Rounded (скругленная
форма-значение по умолчанmо) или Triangular (треугольная форма);
♦ setTabsClosaЫe(<Флаг>) - если в качестве параметра указано значение True, то после
текста на ярлыке вкладки будет отображена кнопка ее закрытия. При нажатии этой
кнопки генерируется сигнал tabCloseRequested;
♦ setMovaЫe(<Флаг>) - если в качестве параметра указано значение тrue, вкладки можно
перетаскивать мышью за их ярлыки;
♦ setDocurnentMode(<Флаг>) - если в качестве параметра указано значение True, панель не
будет окружена рамкой;
♦ setUsesScrollButtons (<Флаг>) - если в качестве параметра указано значение True, то,
если все ярлыки вкладок не помещаются в заголовке панели, появятся две кнопки, с по
мощью которых можно прокручивать содержимое заголовка. Значение False предписы
вает панели выводить все ярлыки в несколько строк;
♦ setTabBarAutoHide(<Флаг>) - если в качестве параметра указано значение True, и в па
нели присутствует всего одна вкладка (или ни одной), заголовок панели будет скрыт.
Значение False указывает панели всегда отображать заголовок;
♦ sеtТаЬТооlТiр(<Индекс>, <Текст>) - задает текст вспльIВающей подсказки у ярлыка
вкладки с указанным индексом;
468 Часть //. Библиотека PyQt б
21.1 О. Аккордеон
Аккордеон похож на панель с вкладками, только отдельные панели в нем расположены по
вертикали: при перекmочении на какую-либо панель она разворачивается, а развернутая
ранее - сворачивается. Аккордеон представляется классом QТoolBox. Иерархия наследова
ния выглядит так:
(QObject, QPaintDevice) - QWidget - QFrame - QТoolBox
Конструктор класса QToolBox имеет следующий формат:
QToolBox( [parent=None] [, flags=0])
В параметре parent указьшается ссьmка на родительский компонент. Если этот параметр
имеет значение None, и у параметра flags указано значение О, компонент не будет иметь
родителя и получит свое собственное окно. Параметр flags указывает тип создаваемого
окна. Доступные для задания типы рассмотрены в разд. 19.1.1.
Пример кода, создающего аккордеон, представлен в листинге 21.7, а созданный им аккор
деон - на рис. 21.6.
liJ QТoolBox □ х
Вкладка!· . 1
Содер)Кj,\мое вкладки 1
Вкладка�
Вкладка]
liJ QSplitter о х
Содержимое компонента 1
Содержимое компонента 2
Основные компоненты
22.1. Надпись
Надпись применяется для вывода подсказки пользоватешо, информирования пользователя
о ходе выполнения операции, назначении клавиш быстрого доступа и т. п. Надпись может
выводить обычный текст, текст, отформатированный НТМL-тегами или тегами Markdown.
Надпись реализуется классом QLabel. Иерархия наследования выглядит так:
(QObject, QPaintDevice) - QWidget - QFrame - QLabel
Конструктор класса QLabel имеет два формата:
:)Label( [parent=None] [, flags=O])
:)Label(<Текст>[, parent=None] [, flags=OJ)
В параметре parent указывается ссьmка на родительскиf{ компонент. Если он не указан или
имеет значение None, компонент будет обладать своим собственным окном, тип которого
можно задать с помощью параметра flags. Параметр <Текст> позволяет задать текст, кото
рый будет отображен на надписи. Пример:
label = QtWidgets .QLabel("Teкcт надписи", flags=QtCore.Qt.WindowТype.Window)
label.resize(ЗOO, 50)
label.show()
Класс QLabel поддерживает следующие основные методы (полный их список смотрите на
странице https://doc.qt.io/qt-6/qlabel.html):
♦ setтext (<Текст>) - задает текст, который будет отображен на надnиси. Можно указать
как обычный текст, так и содержащий СSS-форматирование текст в формате НТМL.
Пример:
label.setText("Teкcт <Ь>полужирный</Ь>")
Перевод строки в простом тексте осуществляется с помощью символа \n, а в тексте
в формате НТМL - с помощью тега <br>:
label.setText("Teкcт\nнa двух строках")
476 Часть 11. Библиотека PyQt б
Внутри текста символ &, указа1rnый перед буквой или цифрой, задает комбинацию кла
виш быстрого доступа. В этом случае буква, перед которой указан символ &, будет -
в качестве подсказки пользователю - подчеркнута. При одновременном нажатии кла
виши <Alt> и подчеркнутой буквы компонент, ссылка. на который передана в метод
setBuddy(), окажется в фокусе ввода. Чтобы вывести сам символ &, необходимо его
удвоить. Если надпись не связана с другим компонентом, символ & выводится в составе
текста. Пример:
label = QtWidgets.QLabel("&Пapoль")
lineEdit = QtWidgets.QLineEdit()
label.setBuddy(lineEdit)
Метод является слотом;
♦ setNwn(<Чиcлo>) - выводит в надписи заданное целое или вещественное число. Метод
является слотом;
♦ setwordWrap(<Флаг>) - если в параметре указано значение True, текст может перено
ситься на другую строку. По умолчанию перенос строк не осуществляется;
♦ text() - возвращает текст надписи;
♦ setTextFormat(<Режим>) - задает режим отображения текста. Могут быть указаны сле
дующие элементы перечисления TextFormat из модуля QtCore.Qt:
• PlainText - простой текст;
• RichText - текст, отформатированный тегами НТМL;
• Autoтext - автоматическое определение (режим по умолчанию). Если текст содер
жит НТМL-теги, то используется режим Richтext, в. противном случае - режим
PlainText;
• MarkdownText - текст, отформатированный тегами Markdown;
♦ setAlignment(<Режим>) - задает режим выравнивания текста внутри надписи (допусти
мые значения рассмотрены в разд. 21.2):
label.setAlignment(QtCore.Qt.AlignrnentFlag.AlignRight 1
QtCore.Qt .Alignrnent.Flag.AlignBottom)
♦ setOpenExternalLinks(<Флаг>) - если в качестве параметра указано значение True, теги
<а>, присутствующие в тексте, будут преобразованы в гиперссылки:
label.setText('<a href="https://www.google.ru/">Этo гиперссылка</а>')
label.setOpenExternalLinks(True)
♦ setBuddy(<Компонент>) - связывает надпись с другим компонентом. В этом случае
в тексте надписи можно задавать клавиши быстрого доступа, указав символ & перед бук
вой или цифрой;
♦ setPixmap(<Изображение QPixmap>) - позволяет вьmести изображение на надпись:
label.setPixmap(QtGui.QPixmap("picture. jpg"))
Метод является слотом;
♦ setPicture (<Рисунок QPicture>) - выводит заданный рисунок. Метод является слотом;
♦ setScaledContents(<Флаг>) - если в параметре указано значение тrue, то при измене
нии размеров надписи размер содержимого также будет изменяться. По умолчанию
изменение размеров содержимого не осуществляется;
Глава 22. Основные компоненты 477
22.2. Кнопка
Кнопка реализуется классом QPushВutton. Иерархия наследования:
(QObject, QPaintDevice) - QWidget - QAЬstractButton - QPushButton
478 Часть 11. Библиотека PyQt 6
♦ setDown(<Флаг>) - если в качестве параметра указано значение True, кнопка будет на
ходиться в нажатом состоянии;
♦ isDown() - возвращает значение True, если кнопка находится в нажатом состоянии,
и False - в противном случае.
Класс QAЬstractвutton поддерживает следующие сигналы:
♦ pressed() - генерируется при нажатии кнопки;
♦ released() - генерируется при отпускании ранее нажатой кнопки;
♦ clicked (<Состояние>) - генерируется при нажатии и отпускании кнопки. Передавае
мый обработчику параметр имеет значение тrue, если кнопка-переключатель установле
на, и False, если она сброшена или это обычная кнопка, а не переключатель;
♦ toggled(<Состояние>) - генерируется при изменении состояния кнопки-переключателя.
Через параметр доступно новое состояние кнопки.
Класс QPushButton· определяет свои собственные методы (здесь приведены только основ
ные - полный их список смотрите на странице https://doc.qt.io/qt-6/qpushbutton.html):
♦ setFlat(<Флаг>) - если в качестве параметра указано значение True, кнопка будет ото
бражаться без рамки;
♦ setAutoDefault(<Флаг>) - если в качестве параметра указано значение True, кнопка
может быть нажата с помощью клавиши <Enter> при ,условии, что она находится в фо
кусе. По умолчанию нажать кнопку позволяет только клавиша <Пробел>. В диалоговых
окнах у всех кнопок по умолчанию указано значение тrue, а в остальных окнах - зна
чение False;
♦ setDefault(<Флаг>) - задает кнопку по умолчанию. Эта кнопка �ожет быть нажата
с помощью клавиши <Enter>, когда фокус ввода установлен на другой компонент, -
например, на текстовое поле. Метод работает только в диалоговых окнах;
♦ setMenu(<Меню QMenu>) - устанавливает всплывающее меню, которое будет отображать
ся при нажатии кнопки;
♦ menu() - возвращает ссылку на вспльIВающее меню или значение None;
♦ showМenu() - отображает вспльIВающее меню. Метод является слотом.
22.3. Переключатель
ПереJ(JПОчатели всегда компонуются группами. Для объединения переключателей в группу
можно воспользоваться компонентом QGroupBox (см разд. 21. 7) или классом QButtonGroup.
Переключатель реализуется классом QRadioButton. Иерархия наследования:
(QObject, QPaintDevice) - QWidget - QAЬstractButton - QRadioButton
Конструктор класса QRadioButton имеет два формата:
QRadioButton([parent=<None>])
QRadioButton(<Teкcт>[, parent=None])
Класс QRadioButton наследует все методы класса QAЬstractButton (см. разд. 22.2). Устано
вить или сбросить переключатель позволяет метод setChecked(<Флаг>), а проверить его те
кущее состояние можно с помощью метода isChecked(). Отследить изменение состояния
480 Часть 11. Библиотека PyQt 6
22.4. Флажок
Флажок может находиться в нескольких состояниях: установленном, сброшенном и проме
жуточном (неопределенном) - последнее состояние может быть запрещено программно.
Флажок реализуется с помощью класса QCheckВox. Иерархия наследования:
(QObject, QPaintDevice) - QWidget - QAЬstractButton - QCheckBox
Конструктор класса QCheckBox имеет два формата:
QCheckBox( [parent=None] )
QCheckВox(<Текст> [, parent=None])
Класс QCheckBox наследует все методы класса QAЬstractвutton (см. разд. 22.2), а также до
бавляет несколько новых:
♦ setCheckState(<Состояние>) - задает состояние флажка. Могут быть указаны следую
щие элементы перечисления Checkstate из модуля QtCore.Qt:
• unchecked - флажок сброшен;
• PartiallyChecked -,- флажок находится в промежуточном состоянии;
• Checked - флажок установлен;
♦ checkState() - возвращает текущее состояние флажка;
♦ setтristate( [<Флаг>=Тruе] ) - если в качестве параметра указано· значение True (значе
ние по умолчанию), флажок может находиться во всех трех состояниях. По умолчанию
поддерживаются только установленное·и сброшенное состояния;
♦ isTristate() - возвращает значение True, если флажок поддерживает три состояния.
и False - если только два.
Чтобы перехватить изменение состояния флажка, следует назначить обработчик сигна..1а
stateChanged(<Состояние>). Через параметр внутри обработчика доступно новое состояние
флажка, заданное в виде целого числа: О (сброшенное), 1 (промежуточное) или 2 (установ
ленное).
Если используется флажок, поддерживающий только два состояния, установить или сбро
сить его позволяет метод setChecked(), а проверить текущее состояние - метод isChecked
Обработать изменение состояния можно в обработчике сигнала toggled(<Состояние>
параметр которого имеет логический тип.
♦ end (<Флаг>) - перемещает текстовый курсор в конец поля. Если в параметре указано
значение тrue, выполняется выделение фрагмента;
♦ cut () - вырезает выделенный текст в буфер обмена при условии, что есть выделенный
фрагмент и используется режим Normal. Метод является слотом;
♦ сору () - копирует выделенный текст в буфер обмена при условии, что есть выделен
ный фрагмент и используется режим Normal. Метод является слотом;
♦ paste () - вставляет текст из буфера обмена в текущую позицию текстового курсора
при условии, что поле доступно для редактирования. Метод является слотом;
♦ undo () - отменяет последнюю операцию ввода пользователем при условии, что отмена
возможна. Метод является слотом;
♦ redo () - повторяет последнюю отмененную операцию ввода пользователем, если это
возможно. Метод является слотом;
♦ isUndoAvailaЫe () - возвращает значение True, если можно отменить последнюю опе
рацию ввода, и False - в противном случае;
♦ isRedoAvailaЫe () - возвращает значение True, если можно повторить последнюю от
мененную операцию ввода, и False - в противном случае;
♦ createStandardContextMenu () - создает стандартное контекстное меню, которое ото
бражается при щелчке правой кнопкой мыши в текстовом поле. Чтобы изменить стан
дартное меню, следует создать класс, производный от QLineEdit, и переопределить в нем
метод contextMenuEvent (self., <event>). Внутри этого метода можно создать свое соб
ственное меню или добавить новый пункт в стандартное меню;
♦ setClearвuttonEnaЫed (<Флаг>) - �ели передано тrue, в правой части непустого поля
будет выводиться кнопка, нажатием которой можно очистить это поле, если False,
кнопка очистки выводиться не будет.
Класс QLineEdit поддерживает следующие сигналь�:
♦ cursorPositionChanged (<Старая позиция>, <Новая позиция>) - генерируется при пере
мещении текстового курсора. Внутри обработчика через первый параметр доступна ста
рая позиция курсора, а через второй параметр - новая позиция. Оба параметра являют
ся целочисленными;
♦ editingFinished () - генерируется при нажатии клавиши <Enter> или потере полем
фокуса ввода;
♦ inputRej ected () - генерируется, если операция ввода, выполненная пользователем,
отвергнута полем ввода. Это может случиться, например, при попытке ввести значение
с длиной большей, чем задана вызовом метода setMaxLength ();
♦ returnPressed () - генерируется при нажатии клавиши <Enter>;
♦ selectionChanged () - генерируется при изменении выделения;
♦ textChanged (<Новый текст>) - генерируется при изменении текста внутри поля пользо
вателем или программно. Внутри обработчика через параметр доступен новый текст
в виде строки;
♦ textEdited (<Новый текст>) - генерируется при изменении текста внутри поля пользо
вателем. При задании текста вызовом метода setтext () не генерируется. Внутри обра
ботчика через параметр доступен новый текст в виде строки.
484 Часть 1/. Библиотека PyQt б
ПРИМЕЧАНИЕ
Если помержка HTML не нужна, то следует воспользоваться классом QPlainтextEdit,
который оптимизирован для работы с простым текстом большого объема.
вого курсора сигнал не генерируется. Внутри обработчика через параметр доступен объ
ект обновленного курсора;
♦ modificationChanged(<Флaг>) - генерируется при изменении состояния документа: из
неизмененного в измененное или наоборот. Значение параметра тrue обозначает, что
документ помечен как измененный, значение False - что он теперь неизмененный;
♦ redoAvailaЫe.(<Флаг>) - генерируется при изменении возможности повторить отме
ненную операцmо ввода. Значение параметра True обозначает наличие возможности по
вторить отмененную операцmо ввода, а False - отсутствие такой возможности;
♦ undoAvailaЫe(<Флаг>) - генерируется при изменении возможности отменить опера
цmо ввода. Значение параметра True обозначает наличие возможности отменить опера
цmо ввода, а False - отсутствие такой возможности;
♦ undoCommandAdded () - генерируется при добавлении операции ввода в список возмож
ных отмен.
ВНИМАНИЕ!
Если выделенный фрагмент занимает несколько строк, то вместо символа перевода строки
вставляется символ с кодом \u2029. Попытка вывести этот символ в консоли приведет к ис
ключению, поэтому следует произвести замену символа с помощью метода replace():
print(cur.selectedText().replace("\u2029", "\n"))
Пример:
# Загружаем и выводим содержимое текстового файла
url = QtCore,QUrl("text.txt")
browser.setSource(url)
Метод является слотом;
♦ source() - возвращает объект класса QUr 1 с адресом текущего ресурса;
♦ reload() - перезагружает текущий ресурс; Метод является слотом;
♦ home() - загружает первый ресурс из списка истории. Метод является слотом;
♦ backward() � загружает предыдущий ресурс из списка истории. Метод является слотом;
♦ forward() - загружает следующий ресурс из списка истории. Метод является слотом;
♦ backwardHistoryCount() - возвращает количество предыдущих ресурсов из списка ис
тории;
♦ forwardНistoryCount() - возвращает количество следующих ресурсов из списка исто
рии;
♦ isBackwardAvailaЫe () - возвращает значение True, если существует предыдущий
ресурс в списке истории, и False - в противном случае;
♦ isForwardAvailaЫe() - возвращает значение True, если существует следующий ресурс
в списке истории, и False ...:.... в противном случае;
♦ clearHistory() - очищает список истории;
♦ historyТitle(<Количество позиций>) - если в качестве параметра указано отрицатель
ное число, возвращает заголовок предыдущего ресурса, отстоящего от текущего на за
данное число позиций, если О - заголовок текущего ресурса, а если положительное
число - заголовок следующего ресурса, также отстоящего от текущего на заданное
число позиций;
♦ historyUrl (<Количество позиций>) - то же самое, что historyТitle(), но возвращает
адрес ресурса в виде объекта класса QUrl;
♦ setOpenLinks(<Флаг>) - если в качестве параметра указано значение тrue, то переход
по гиперссылкам будет разрешен (поведение по умолчанmо). Значение False запрещает
переход;
♦ setOpenExternalLinks(<Флаг>) - если в . качестве параметра указано значение тrue, то
переход по гиперссылкам, ведущим на внешние ресурсы, будет разрешен (при этом сиг
нал anchorClicked() не генерируется). Значение False запрещает переход (поведение по
умолчанию).
Класс QTextBrowser поддерживает сигналы:
♦ anchorClicked(<Интepнeт-aдpec>) - генерируется при переходе по гиперссылке. Внут
ри обработчика через параметр доступен интернет-адрес гиперссылки в виде объекта
класса QUrl;
♦ backwardAvailaЫe(<Признак>) - генерируется при изменении списка предыдущих
ресурсов. Внутри обработчика через параметр доступно значение тrue, если в списке
истории имеются предыдущие ресурсы, и False - в противном случае;
♦ forwardAvailaЫe(<Признак>) - генерируется при изменении списка следующих ресур
сов. В обработчике через параметр доступно значение True, если в списке истории име
ют.ся следующие ресурсы, и False - в противном случае;
Глава 22. Основные компоненты 499
UQSpinBox □ х
♦ setAccelerated (<Флаг>) - если в качестве парамеrра указано значение True, то при удержа
нии какой-либо из кнопок нажатой скорость смены значений в поле увеличиrся;
♦ setAlignrnent (<Режим>) - задает режим выравнивания значения внутри поля (допусти
мые значения рассмотрены в разд. 21. 2);
♦ setwrapping (<Флаг>) - если в качестве параметра указано значение True, то значение
внутри поля будет при нажатии кнопок изменяться по кругу: максимальное значение
сменится минимальным и наоборот;
♦ setSpecialValueтext (<Строка>) - задает строку, которая будет отображаться внутри
поля вместо минимального значения;
♦ setReadOnly(<Флaг>) - если в качестве параметра указано значение True, поле будет
доступно ТОЛЬКО для чтения;
♦ setFrame (<Флаг>) - если в качестве параметра указано значение False, поле будет ото-
бражаться без рамки;
♦ stepDown () - уменьшает значение на одно прирашение. Метод является слотом;
♦ stepUp () - увеличивает значение на одно приращение. Метод является слотом;
♦ stepBy(<Количество>) - увеличивает (при положительном значении) или уменьшает
(при отрицательном значении) значение поля на указанное количество приращений;
♦ text () - возвращает текст, содержащийся внутри поля;
♦ clear () - очищает поле. Метод является слотом;
♦ selectAll () - выделяет все содержимое поля. Метод является слотом.
Класс QAЬstractSpinВox поддерживает сигнал editingFinished (), который генерируется
при потере полем фокуса ввода или при нажатии клавиши <Enter>.
Классы QSpinBox и QDouЬleSpinBox поддерживают следующие методы (здесь приведены
только основные - полные их списки доступны на страницах https://doc.qt.io/qt-6/
qspinbox.html и https://doc.qt.io/qt�6/qdouЫespinbox.html соответственно):
♦ setValue (<Число>) - задает значение поля. Метод является слотом, принимающим,
в зависимости от компонента, целое или вещественное значение;
♦ value () - возвращает целое или вещественное число, содержащееся в поле;
♦ cleanText () - возвращает целое или вещественное число в виде строки, без дополни
тельного текста, заданного методами setPrefix () и setSuffix (), начальных и конечных
пробелов;
♦ setRange (<Минимум>, <Максимум>) - задает минимальное и максимальное допустимые
значения;
♦ setMinimurn (<Минимум>) - задает минимальное значение;
♦ setMaximurn (<Максимум>) � задает максимальное значение;
♦ setPrefix (<Текст>) - задает текст, который будет отображаться внутри поля перед зна
чением;
♦ setSuffix (<Текст>) - задает текст, который будет отображаться внутри поля после зна
чения;
♦ setSingleStep (<Число>) - задает число, которое будет прибавляться или вычитаться из
текущего значения поля на каждом шаге.
Глава 22. Основные компоненты 501
iu QDateEdit о х
!■ 02.2022
Иерархия наследования:
(QObject, QPaintDevice) - QWidget - QAЬstractSpinBox - QDateTimeEdit
(QObject, QPaintDevice) - QWidget - QAЬstractSpinВox - QDateTimeEdit - QDateEdit
(QObject, QPaintDevice) - QWidget - QAЬstractSpinВox - QDateTimeEdit - QТimeEdit
Форматы конструкторов классов:
QDateTimeEdit([parent=None])
QDateTimeEdit(<Bpeмeннaя отметка>[, parent=None])
QDateTimeEdit(<Дaтa>[, parent=None])
QDateTimeEdit(<Bpeмя>[, parent=None])
QDateEdit([parent=None])
QDateEdit(<Дaтa>[, parent=None])
QТi�eEdit([parent=None])
QTimeEdit(<Bpeмя>[, parent=None])
В параметре <Временная отметка> можно указать объект класса QDateTime или datetime (из
Python). Преобразовать объект класса QDateTime в объект класса datetime позволяет метод
toPyDateTime () класса QDateTime:
>>> from PyQtб import QtCore
>>> d = QtCore.QDateTime(2022, 2, 18, 14, 23)
>>> d
PyQt6.QtCore.QDateTime(2022, 2, 18, 14, 23)
>>> d.toPyDateTime()
datetime.datetime(2022, 2, 18, 14, 23)
В качестве параметра <Дата> можно указать объект класса QDate или date (из Python). Пре
образовать объект класса QDate в объект класса date позволяет метод toPyDate() класса
QDate.
502 Часть 1/. Библиотека PyQt 6
В параметре <Время> можно указать объект класса QTime или time (из Python). Преобразо
вать объект класса QTime в объект класса time позволяет метод toPyТime() класса QТime.
Классы QDateTime, QDate и QТime определены в модуле QtCore.
Класс QDateTimeEdit наследует все методы из класса QAЬstractSpinBox (см. разд. 22.8) и
дополнительно реализует следующие методы (здесь приведены только самые п_олезные -
полный их список смотрите на странице https://doc.qt.io/qt-6/qdatetimeedit.html):
♦ setDateTime (<Временная отметка QDateTime или <;latetime>)_ - устанавливает времен-
ную отметку. Метод является слотом;
♦ setDate (<Дата QDate или date>) -устанавливает дату. Метод является слотом;
♦ setTime (<Время QTime или time>) -устанавливает время. Метод является слотом;
♦ dateтime () -возвращает объект класса QDateTime с временной отметкой;
♦ date () -возвращает объект класса QDate с датой;
♦ time () -возвращает объект класса QTime со временем;
♦ setDateTimeRange (<Минимум>, <Максимум>) - задает минимальное и максимальное
допустимые значения для временной отметки;
♦ setMinimumDateTime (<Минимум>) - задает минимальное допустимое значение времен
ной отметки;
♦ setMaximumDateTime (<Максимум>) - задает максимальное допустимое значение времен
ной отметки.
В параметрах трех последних методов указываются объекты класса QDateTime или
datetime;
♦ setDateRange (<Минимум>, <Максимум>) - задают минимальное и максимальное допус
тимые значения для даты;
♦ setMinimumDate (<Минимум>) -задает минимальное допустимое значение даты;
♦ setМaximumDate (<Макс:имуы>) - задает максимальное допустимое значение даты.
В параметрах трех последних методов указываются объекты класса·ооаtе или date;
♦ setTimeRange (<Минимум>, <Максимум>) - задает минимальное и максимальное допус
тимые значения для времени;
♦ setMinimumTime (<Минимум>) -задает минимальное допустимое значение времени;
♦ setMaximumTime (<Максимум>) -задает максимальное допустимое значение времени.
В параметрах трех последних методов указываются объекты класса QTime или time;
♦ setDisplayFormat(<Формат>) -задает формат отображения даты и времени. В качестве
параметра указывается строка, содержащая специальные символы. Пример задания
строки формата:
dateTimeEdit .setDisplayFormat("dd.ММ.yyyy HH:mm:ss")
♦ setTimeSpec(<Зона>) - задает временную зону. В качестве параметра можно указать
следующие элементы перечисления TimeSpec из модуля QtCore.Qt: LocalTime (местное
время), uтс (всемирное координированное время) или OffsetFromuтc (смещение относи
тельно всемирного координированного времени, исчисляемое в секундах);
Глава 22. Основные компоненты 503
[jJ QDateEdit □ х
-
Пн Вт Ср Чт Пт Сб Вс
31 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 19 20
21 22 23 24 25 26 27
28 2 3 4 5 6
7 8 9 10 11 12 13
22.10. Календарь
Класс QCalendarWidget реализует календарь с возможностью выбора даты и перемещения
по месяцам с помощью мыши и клавиатуры (рис. 22.4). Иерархия наследования:
(QObject, QPaintDevice) - QWidget - QCalendarWidget
U QCalendarWidget □ х
Пн Вт Ср Чт Пт Сб Вс
•
5 31 2 3 4 5 6
6 7 8 9 10 11 12 13
7 14 15 16 17 19 20
8 21 22 23 24 25 26 27
9 28 2 3 4 5 6
10 7 8 9 10 11 1" 13
[U QLDCNumber □ х
UQDial □ х
/
,,..
22.15. Полос
, а прокрутки
Класс QScrollBar реализует горизонтальную или вертикальную полосу прокрутки. Иерар
хия наследования:
(QObject, QPaintDevice) - QWidget - QAЬstractSlider - QScrollBar
Форматы конструктора класса QScrollBar:
QScrollBar([parent=None])
QSсrоllВаr(<Ориентация>[, parent=None])
В качестве значения параметра <Ориентация> указываются элементы Horizontal (горизон
тальная) или vertical (вертикальная - значение по умолчанию) перечисления Orientation
из модуля QtCore .Qt.
Класс QScrollBar наследует все методы и сигналы класса QAЬstractSlider (см. разд. 22.13)
и не определяет дополнительных методов.
22.16. Веб-браузер
Чтобы использовать компонент полнофункционального веб-браузера, потребуется устано
вить дополнительную библиотеку PyQt6-WebEngine, для чего следует в консоли подать
следующую команду:
pip install PyQtб-WebEngine
Веб-браузер реализуется классом QWebEngineView из модуля QtWebEngineWidgets (рис. 22.7).
Иерархия наследования:
(QObject, QPaintDevice) - QWidget - QWebEngineView
512 Часть 11. Библиотека PyQt 6
lit] QWebEngineViev, о х
Россия
Всё о Goog!e Реклама Дл� бизнеса Как работает Google По�ск Конфидемциальность Условия Настройки .
Класс QWebEngineView поддерживает следующие полезные для нас методы (полный их спи
сок смотрите на странице https://doc.qt.io/qt-6/qwebengineview.html):
♦ lоаd(<Интернет-адрес>) и sеtUrl(<Интернет-адрес>) - загружают и-выводят страницу
с указа1rnым в параметре адресом, который задается в виде объекта класса QUrl из моду
ля QtCore:
web.load(QtCore.QUrl('https://www.google.ru/'))
♦ selectedText() - возвращает выделенный текст или пустую строку, если ничего не бы
ло выделено;
♦ hasSelection() - возвращает Tr1:1e, если какой-либо фрагмент страницы был выделен,
и False- в противном случае;
♦ setzoomFactor(<Множитель>) - задает масштаб самой страницы. Значение 1 указывает,
что страница будет выведена в оригинальном масштабе, значение меньше· единицы -
в уменьшенном, значение больше единицы - увеличенном масштабе;
♦ zoomFactor() ;__ возвращает масштаб страницы;
♦ back() - загружает предыдущий ресурс из списка истории. Метод является слотом;
♦ forward() - загружает следующий ресурс из списка истории . Метод является слотом;
♦ reload() - перезагружает с траницу. Метод является слотом;
♦ stop() - останавливает загрузку страницы. Метод является слотом;
♦ icon() - возвращает в виде объекта класса Qicon значок, заданный для с траницы;
♦ findText (<Искомый текст>[, options= {}] [, resultCallback=0]) - ищет на странице
заданный фрагмент текста. Все найденные фрагменты будут выделены желтым фоном
непосредственно в компоненте.
Необязательный: параметр option задает дополнительные параметры в виде одного из
следующих элементов перечисления FindFlag класса QWebEnginePage из модуля
QtWebEngineCore (или их комбинации через оператор 1 ):
• FindВackward - вьtполнять поиск в обратном, а не в прямом направлении;
• FindCaseSensitively- поиск с учетом регистра символов (по умолчанию выполня-
ется поиск без учета регистра).
В необязательном параметре resultCallback можно указать функцию, которая будет вы
звана по окончании поиска. Эта функция должна принимать в качестве единственного
параметра объект класса QW�bEngineFindTextResult из модуля QtWebEngineCore. Этот
класс поддерживает следующие методы:
• numЬerOfМatches() - возвращает количество совпавших фрагментов;
• activeMatch() - индекс выделенного в текущий момент совпавшего фрагмента.
Пример поиска с учетом регистра и выделением всех найденнь1х совпадений:
web. findText( 'Python' ,
options= QtWebEngineCore.QWebEnginePage.FindFlag.FindBackward 1
QtWebEngineCore.QWebEnginePage.FindFlag.FindCaseSensitively)
♦ triggerPageAction(<Действие>[, checked=False]) - выполняет над страницей указан
ное действие. В качестве действия задается один из элементов перечисления WebAction
класса QWebEnginePage из модуля QtWebEngineCore - этих атрибутов очень много, и все
они приведены на странице Ьttps://doc.qt.io/qt-6/qwebenginepage.Ьtml#WebAction
enum. Необязательный параметр checked имеет смысл указывать лишь для действий,
принимающих логический флаг:
# Выделение всей страницы
web.triggerPageAction(QtWebEngineCore.QWebEnginePage.WebAction.SelectAll)
# Копирование выделенного фрагмента страницы
web.triggerPageAction(QtWebEngineCore.QWebEnginePage.WebAction.Copy)
514 Часть 11. Библиотека PyQt б
Списки и таблицы
♦ setEditтext (<Текст>) -вставляет текст в поле редактир ования. Метод является слотом;
♦ clearEditтext() -удаляет текст из поля редактирования. Метод является слотом;
♦ setCompleter(<Список QCompleter>) - задает список вариантов значений для автоза
вершения;
♦ setValidator(<Валидатор>) - устанавливает валидатор в виде объекта класса, произ
водного от QValidator (см. разд. 22.5.3);
♦ setDuplicatesEnaЫed(<Флaг>) - если в качестве параметра _указано значение True,
пользователь может добавить элемент с повторяющимся текстом. По умолчанию повто
ры запрещены;
♦ setMaxcount(<Количество>) -задает максимальное количество элементов в списке. Если
до вызова метода количество элементов превышало это количество, лишние элементы
будут удалены;
♦ setMaxVisiЫeiterns(<Количество>) - задает максимальное количество видимых эле
ментов в раскрывающемся списке;
♦ setMinirnurnContentsLength(<Количество>) - задает минимальное количество символов,
которое должно помещаться в раскрывающемся списке;
♦ setSizeAdjustPolicy(<Режим>) - задает режим установки ширины списка при измене
нии содержимого, В качестве параметра указываются следующие элементы перечисле
ния SizeAdjustPolicy из класса QCornЬoBox:
• AdjustToContents -ширина списка подстраивается под ширину текущего содержи
мого;
• AdjustToContentsOnFirstShow - ширина списка подстраивается под ширину содер
жимого, имевшегося в списке при первом его отображении;
• AdjustToMinirnurnContentsLengthWithicon - используется значение минимальной
ширины, которое установлено с помощью метода setMinirnurnContentsLength() , плюс
ширина значка;
♦ setFrarne(<Флаг>) - если в качестве параметра указано значение _False, список будет
отображаться без рамки;
♦ seticonSize(<Размеры QSize>) -задает максимальные размеры значков;
♦ showPopup() - разворачивает список;
♦ hidePopup() - сворачивает список.
23.1.4. Сигналы
Класс QComЬoBox поддерживает следующие сигналы:
♦ activated(<Индекс>)- генерируется при выборе пользователем пункта в списке (даже
если индекс не изменился). Внутри обработчика доступен целочисленный индекс эле
мента;_
♦ currentindexChanged(<Индeкc>) - генерируется при изменении текущего индекса.
Внутри обработчика доступен целочисленный индекс элемента (или значение -1, если
ни один элемент не выбран);
♦ currentTextChanged(<Текст>) - то же самое, что И currectindexChanged(), только в об
работчик передается текст элемента (или пустая строка, если ни один элемент не вы
бран);
♦ editTextChanged(<Текст>) - генерируется при изменении текста в поле. Внутри обра
ботчика через параметр доступен новый текст;
♦ highlighted(<Индeкc>) - генерируется при наведении курсора мыши на пункт в списке.
Внутри обработчика доступен целочисленный индекс элемента;
♦ textActivated (<Индекс>) - то же самое, что и activated(), только в обработчик пере
дается текст элемента;
♦ textHighlighted(<Текст>) - то же самое, что и highlighted(), только в обработчик пе
редается текст элемента.
Класс QFontcomЬoBox наследует. все методы и сигналы класса QComЬoBox (см. разд. 23.1)
и определяет несколько дополнительных методов:
♦ setCurrentFont (<Шрифт QFont>) - делает текущим элемент, соответствующий указан
ному шрифту:
comЬoBox.setCurrentFont(QtGui.QFont("Verdana"))
Метод является слотом;
♦ currentFont() - возвращает объект класса QFont с выбранным шрифтом. Вот пример
вывода названия шрифта:
print(comЬoBox.currentFont().farnily())
♦ setFontFilters(<Фильтр>) - выводит в списке только шрифты типов, соответствующих
указанному фильтру. В качестве параметра указывается комбинация следующих элемен
тов перечисления FontFilter из класса QFontComЬoBox:
• AllFonts -все типы шрифтов;
•· scalaЬleFonts -масштабируемые шрифты;
• NonScalaЬleFonts -немасштабируемые шрифты;
• MonospacedFonts - моноширинные шрифты;
• ProportionalFonts -пропорциональные шрифты.
Класс QFontComЬoBox поддерживает сигнал currentFontChanged (<Шрифт QFont>), который
генерируется при изменении выбранного шрифта. Внутри 'обработчика доступен выбран
ный шрифт.
23.4. Модели
Для отображения данных в виде списков и таблиц применяется концепция «модель
представление»," позволяющая отделить данные от их внешнего вида и избежать дублиро
вания данных. В основе концепции лежат следующие составляющие:
♦ модель-являет<;я «оберткой» над данными. Позволяет считывать, добавлять, изменять,
удалять данные и управлять ими;
♦ представление-отображает данные модели на экране. Сразу несколько представлений
могут выводить одну и ту же модель;
♦ модель выделения- управляет выделением. 'Если одна модель выделения установлена
сразу в нескольких представлениях, то выделение элемента в одном представлении при
ведет к выделению соответствующего элемента в другом;
♦ промежуточная модель-является прослойкой между базовой моделью и представле
нием. Позволяет производить сортировку и фильтрацию данных без изменения порядка
следования элементов в базовой модели;
♦ делегат-представляет компонент для редактирования данных. Существуют стандарт
ные классы делегатов, кроме того, разработчик может создать свои классы.
уровня, в который будет вложена добавляемая строка, - если параметр не задан, строка
добавляется на самый верхний уровень иерархии. Метод возвращает значение тrue, если
операция успешно выполнена;
♦ insertRows(<Индекс>, <Количество>[, parent=QModelindex()]) - добавляет несколько
пустых строк в указанную позицmо модели. Необязательный параметр parent указывает
элемент верхнего уровня, в который будут вложены добавляемые строки, - если пара
метр не задан, строки добавляются на самый верхний уровень иерархии. Метод возвра
щает значение True, если операция успешно выполнена;
♦ insertColшnn(<Индeкc столбца>, <Список элементов QStandarditem>) - добавляет один
столбец в указанную позицmо модели. В качестве параметра <Список> указывается спи
сок отдельных строк добавляемого столбца;
♦ insertColшnn (<Индекс>[, parent=QМodelindex()]) - добавляет один пустой столбец
в указанную позицmо. Необязательный параметр parent указывает элемент верхнего
уровня - владелец элементов, в который будет добавлен столбец. Если этот параметр
не задан, столбец добавляется в элементы самого верхнего уровня иерархии. Метод воз
вращает значение True, если операция выполнена успешно;
♦ inser'tColшnns(<Индекс>, <Количество>[, parent=QModelindex()]) - добавляет не
сколько пустых столбцов в указанную позицmо. Необязательный параметр parent ука
зывает элемент верхнего уровня - владелец элементов, в который будут добавлены
столбцы. Если этот параметр не задан, столбцы добавляются в элементы самого верхне
го уровня иерархии. Метод возвращает значение тrue, если операция успешно вьшол
нена;
♦ removeRows(<Индекс>, <Количество> [, parent=QМodelindex()]) - удаляет указанное
количество строк, начиная со строки, имеющей заданнь1й индекс. Необязательный пара
метр parent указывает элемент верхнего уровня - владелец удаляемых строк. Если этот
параметр не задан, будут удалены строки из самого верхнего уровня иерархии. Метод
возвращает значение True, если операция успешно выполнена;
♦ removeColшnns(<Индекс>, <Количество> [, parent=QModelindex()]) - удаляет указанное
количество столбцов, начиная со столбца, имеющего заданный индекс. Необязательный
параметр parent указывает элемент верхнего уровня - владелец элементов, из которых
будут удалены столбцы. Если этот параметр не задан, удаляются столбцы из элементов
самого верхнего уровня иерархии. Метод возвращает значение тrue, е�ли операция
успешно выполнена;
♦ takeitem(<Строка> [, <Столбец>=О J ) - удаляет указанный элемент из модели и возвра
щает его в виде объекта класса QStandarditem;
♦ takeRow(<Ин.rieкc>) - удаляет строку с указаннь1м индексом и возвращает ее в виде спи
ска объектов класса QStandarditem;
♦ takeColшnn(<Индекс>) - удаляет столбец с указанным индексом и возвращает его в виде
списка объектов класса QStandarditem;
♦ clear() - удаляет все элементы из модели;
♦ item(<Строка> [, <Столбец>=О J ) - возвращает ссьmку на элемент (объект класса
QStandarditem), расположенный в указанной ячейке;
♦ invisiЫeRootitem() - возвращает ссылку на невидимый· корневой элемент модели
в виде объекта класса QStandarditem;
Глава 23. Списки и таблицы 527
tv = QtWidgets.QTreeView(parent=window)
sti = QtGui.QStandarditemМodel(parent=window)
rootiternl = QtGui.QStandarditern('QAЬstractitemView')
rootitem2 = QtGui.QStandarditem('Базовый класс' )
iteml = QtGui.QStandarditem('QListView')
item2 = QtGui.QStandarditem( 'Список' )
rootiteml.appendRow([iteml, item2])
iternl = QtGui.QStandarditem( 'QTaЬleView' )
item2 = QtGui.QStandarditem('Таблица')
rootiternl.appendRow([iteml, item2])
iteml = QtGui.QStandarditem('QTreeView')
item2 = QtGui.QStandarditem( 'Иерархический список')
rootiternl.appendRow([iteml, item2])
sti.appendRow([rootiteml, rootitern2])
sti.setHorizontalHeaderLabels(['Клacc', 'Описание'])
tv .setModel(sti)
tv.setColumпWidth(0, 170)
tv.resize(400, 100)
window. show()
sys.exit(app.exec())
23.5. Представления
Для отображения элементов модели предназначены следующие классы представлений:
♦ QListView - простой список с возможностью выбора как одного, так и нескольких
пунктов. Пункты списка, помимо текстовой надписи, могут содержать значки;
♦ QTaЫeView - таблица;
♦ QТreeView - иерархический список.
Также можно воспользоваться классами QComЬoBox (раскрьmающийся список - см. разд. 23. 1),
QListWidget (простой список), QTaЫeWidget (таблица) и QTreeWidget (иерархический спи
сок). Последние три класса нарушают концепцию «модель-представление», хотя и отчасти
базируются на ней. За подробной информацией по этим классам обращайтесь к докумен
тации.
532 Часть 11. Библиотека PyQt б
той клавишу <Shift>, все элементы от текущей позиции до позиции щелчка мышью
выделяются;
♦ setSelectionBehavior(<Режим>) - задает режим представления выделенных элементов.
В качестве параметра указьmаются следующие элементы перечисления Selectionвehavior
из класса QAЬstractitemView:
• Selectitems - выделяется отдельный элемент;
• SelectRows- выделяется строка целиком;
• SelectColuпms- выделяется столбец целиком;
♦ selectAll() - выделяет все элементы. Метод является слотом;
♦ clearSelection() - снимает выделение. Метод является слотом;
♦ edit(<Индекс QМodelindex>) - переключает элемент с заданным индексом в режим
редактирования, не делая его выделенным. Метод является слотом;
♦ setEditTriggers (<Действие>) - задает действие, при котором элемент переключается
в режим редактирования. В качестве параметра указывается комбинация следующих
элементов перечисления EditTrigger из класса QAЬstractitemView:
• NoEditTriggers - элемент не будет поддерживать редактирование;
• currentChanged - при выделении-элемента;
• DouЬleClicked - при двойном щелчке мышью;
• SelectedClicked- при щелчке мышью на уже выделенном элементе;
• EditKeyPressed - при нажатии клавиши <F2>;
• AnyKeyPressed - при нажатии любой символьной клавиши;
• AllEditTriggers - при любом упомяm-том ранее действии;
♦ seticonSize(<Размеры QSize>) - задает размеры значков;
♦ setTextElideMode(<Режим>) - задает режим обрезки текста, если он не помещается
в отведенную область (в месте пропуска выводится троеточие). Могут быть указаны
следующие элементы перечисления тextElideмode из модуля QtCore .Qt:
• ElideLeft- текст обрезается слева;
•. ElideRight - текст обрезается справа;
• • ElideMiddle- текст вырезается посередине;
• ElideNone- текст не обрезается;
♦ setTabKeyNavigation(<Флаг>) - если в качестве параметра указано значение True, меж
ду элементами можно перемещаться с помощью клавиш <ТаЬ> и <Shift>+<Tab>;
♦ sсrоllТо(<Индекс QМodelindex>[, hint=ScrollHint.EnsureVisiЫe]) - прокручивает
представление таким образом, чтобы элемент, на который ссылается заданный индекс,
бьm видим. В параметре hint указьmаются следующие элементы перечисления ScrollHint
из класса QAЬstractitemView:
• EnsureVisiЫe- элемент должен находиться в области видимости;
• PositionAtTop - элемент должен находиться в верхней части;
• PositionAtBottom - элемент должен находиться в нижней части;
• PositionAtCenter-элeмeнт доЛЖен находиться в центре;
534 Часть 11. Библиотека PyQt б
[iJ QListView □ х
:jJ: Perl
·'/J:- РНР
� Python
·'/J:- Ruby
Класс QListView наследует все методы и сигналы из класса QAЬstractitemView (см.разд. 23.5.1),
включая методы setModel(), model() и selectedlndexes () . Помимо этого, он дополнительно
определяет следующие методы (здесь приведены только основные - полный их список
можно найти на странице https://doc.qt.io/qt-6/qlistview.html):
536 Часть 11. Библиотека PyQt б
23.5.3. Таблица
Класс QTaЬleView реализует таблицу (рис. 23.2). Иерархия наследования выглядит так:
(QObject, QPaintDevice) - QWidget � QFrame - QAЬstractScrollArea -
QAЬstractiternView - QTaЬleView
! i] QTaЫeView о х
Значок Название Сайт
1 [=]Perl http://www.per!.org/
РНР http://php.net/
Python https://www.python.org/
Ruby https://www.ruby
-lan����
------ 1
liJ QТreeView □ х
Класс Описание
V OAbstractltemView Базовый класс
QlistView Список
QTaЫeView Таблица
QTreeView Иерархический список
> OAbstractltemModel Базовый класс
Класс QTreeView наследует все методы и сигналы класса QAЬstractrtemView (см. разд. 22.5.1)
и дополнительно поддерживает следующие методы (здесь приведены только основные -
полный их список можно найти на странице https://doc.qt.io/qt-6/qtreeview.html):
540 Часть 11. Библиотека PyQt 6
ПРИМЕЧАНИЕ
Чтобы включить сортировку столбцов пользователем, следует передать значение True
в метод setSortingEnaЫed () объекта представления.
арр = QtWidgets.QApplication(sys.argv)
window = QtWidgets.QТaЬleView()
window.setWindowТitle("Иcпoльзoвaниe делегата")
sti = QtGui.QStandarditemМodel(parent=window)
lstl = ['Флеш-диск', 'Бумага для принтера', 'картридж для принтера']
lst2 = ["10", "3", "8"]
for row in range(0, 3):
iteml = QtGui.QStandarditem(lstl[row])
item2 = QtGui.QStandarditem(lst2[row])
sti.appendRow([iteml, item2]) ·
sti.setHorizontalHeaderLabels(['Toвap', 'Кол-во'])
window.setModel(sti)
# Назначаем делегат второму столбцу таблицы
window.setitemDelegateForColumn(l, SpinBoxDelegate())
window.setColumnWidth(0, 150)
window.resize(350, 150)
window.show()
sys.exit(app.exec())
PyQt вкточает в свой состав средства для работы с базами данных формата SQLite, MySQL,
MariaDB, Oracle, PostgreSQL, IВМ DB2 и др., не требующие установки никаких дополни
тельных Руthоn-библиотек. Кроме того, поддерживается работа с mобыми базами данных
посредством платформы ODBC.
ВНИМАНИЕ!
Для успешного доступа к базам данных всех форматов, кроме SQLite и ODBC, требуется
установить соответствующие клиенты.
Все классы, обеспечивающие работу с базами данных и рассмотренные в этой главе, опре
делены в модуле QtSql.
♦ setDatabaseName (<Имя � путь к базе данных>) - задает имя базы данных (для сер
верных баз), путь к ней (для локальных баз данных, таких как SQLite) или полный набор
параметров подключения (если используется ODBC);
♦ setUserName (<Имя>) - задает имя пользователя для подключения к базе. Используется
только для серверных баз данных;
♦ setPassword(<Пapoль>) - задает пароль для подключения к базе. Используется только
для серверных баз данных;
♦ setConnectOptions (<Параметры>) - задает набор дополнительных параметров для под
ключения к базе в виде строки. Набор поддерживаемых дополнительных параметров
различен в зависимости от выбранного формата и приведен в документации по классу
QSqlDatabase.
Для работы с базой предназначены следующие методы класса QSqlDataЬase:
♦ open () - открывает базу данных. Возвращает тrue, если база бьша успешно открыта,
и False - в противном случае;
ВНИМАНИЕ!
Перед созданием соединения с базой данных обязательно следует создать объект про
граммы (объект класса QApplication). Если этого не сделать, PyQt не сможет загрузить
драйвер указанного формата баз данных, и соединение не будет создано.
Открываемая база данных уже должна существовать на локальном диске или сервере.
Единственное исключение - база формата SQLite, которая в случае ее отсутствия будет
создана автоматически.
♦ open (<Имя>, <Пароль>) - открывает базу данных под указанными именем пользователя
и паролем. Возвращает тrue, если база была успешно открыта, и False - в противном
случае;
♦ isOpen () - возвращает True, если база данных в настоящее время открыта, и False -
в противном случае;
♦ isOpenError () - возвращает True, если при попытке открытия базы данных возникли
ошибки, и False - в противном случае;
♦ transaction () - запускает транзакцию, если формат базы поддерживает таковые. Если
же формат базы не поддерживает транзакции, то не делает ничего. Возвращает True,
если транзакция была успешно запущена, и False - в противном случае;
♦ commit () - подтверждает транзакцию, если формат базы поддерживает таковые: Если
же формат базы не поддерживает транзакции, то не делает ничего. Возвращает True,
если транзакция бьша успешно подтверждена, и False - в противном случае;
♦ rollback () - отклоняет транзакцию, если формат базы поддерживает таковые. Если же
формат базы не поддерживает транзакции, то не делает ничего. Возвращает True, если
транзакция бьша успешно отклонена, и False - в противном случае;
♦ lastError () - возвращает сведения о последней возникшей при работе с базой ошибке
в виде объекта класса QSqlError;
♦ connectionName () - возвращает строку с именем соединения с базой или пустую строку
для соединения по умолчанию;
♦ taЫes ( [tуре=ТаЫеТуре. ТаЫеs]) - возвращает список таблиц, хранящихся в базе.
В параметре type можно указать тип таблиц в виде одного из элементов перечисления
таЫеТуре из класса QSql или их комбинации через оператор 1:
Глава 24. Работа с базами данных 553
В листинге 24.1 показан код, выполняющий соединение с базами даюn,1х различных форма
тов и их открытие.
♦ fiеld(<Индекс поля>) - возвращает сведения о поле (объект класса QSqlField), чей ин
декс задан в качестве параметра;
♦ field(<Имя поля>) - возвращает сведения о поле (объект класса QSqlField), чье имя
задано в качестве параметра;
♦ indexOf(<Имя поля>) - возвращает- индекс поля с указанным именем или -1, если тако�
го поля нет. При поиске поля не учитывается регистр символов;
♦ contains(<Имя поля>) - возвращает True, если поле с указанным именем существует,
и False -в противном случае;
♦ isEmpty() -возвращает тrue, если в таблице нет полей, и False - в противном случае.
Полное описание класса QSqlRecord приведено на странице https://doc.qt.io/qt-6/
qsqlrecord.html.
ПРИМЕЧАНИЕ
Далее будут рассмотрены лишь наиболее часто используемые возможности класса
QSqlQuery. Полное его описание приведено на странице https://doc.qt.io/qt-6/
qsqlquery.html.
СОВЕТ
Метод ехес() следует использовать в тех случаях, если SQL-эanpoc не принимает пара
метров. В противном случае рекомендуется применять методы, рассмотренные далее.
ЛиtrИНr 2-t.5. �
' дnJI эадаl;t� 3Нa't8tltli и ., '
В листинге 24.7 приведен код, извлекающий данные из таблицы good созданной ранее базы
данных и выводящий их на экран.
# Создаем модель
sqm = QtSql.QSqlQueryModel(parent=window)
sqm.setQuery('select * from good order Ьу goodname')
# Задаем заголовки для столбцов модели
sqm.setHeaderData(l, QtCore.Qt .Orientation.Horizontal, 'Название')
sqm.setHeaderData(2, QtCore.Qt.Orientation.Horizontal, 'Кол-во')
# Задаем для таблицы только что созданную модель
window.setModel(sqm)
# Скрываем первый столбец, в котором выводится идентификатор
window.hideColumn(0)
window.setColumnWidth(l, 150)
window.setColumnWidth(2, 60)
window.resize(260, 160)
window .show()
sys.exit(app.exec())
stm.setSort(l, QtCore.Qt.SortOrder.DescendingOrder)
stm.setFilter( 'goodcount > 2' )
stm.select()
М�тод является слотом;
♦ setEditStrategy(<Peжим>) -указывает режим редактирования даюn,rх в модели. В ка
честве параметра задается один из элементов перечисления Ed itStrategy из класса
QSqlTaЬleModel:
• OnF ieldChange - все изменения переносятся в базу даюn,rх немедленно;
• OnRowChange - изменения переносятся в базу лишь после того, как пользователь пе
рейдет на другую строку;
• OnМanualSuЬmit - изменения переносятся в базу только после вызова метода
suЬmit() или suЬmitAll() ;
♦ insertRow(<Индeкc>) -добавляет пустую запись по индексу, заданному первым пара
метром. Возвращает значение тrue, если запись была успешно добавлена, и False -
в противном случае;
♦ insertRows(<Индекс>, <Количество>) - добавляет указанное количество пустых запи
сей по индексу, заданному первым параметром. Если указан индекс -1, записи добавля
ются в конец модели. Возвращает значение True, если записи были успешно добавлены,
и False - в противном случае;
♦ removeRow(<Индeкc>) - удаляет запись с указанным индексом. Возвращает значение
True, если запись бьша успешно удалена, и False - в противном случае;
♦ removeRows(<Индекс>, <Количество>) -удаляет указанное количество записей, начиная
с записи с указаюn,rм индексом. Возвращает значение тrue, если записи были успешно
удалены, и False -в противном случае;
ПРИМЕЧАНИЕ
Нужно отметить, что после удаления записи вызовом метода removeRow() или removeRows ()
в модели останется пустая запись, реально не представляющая никакой записи из табли
цы. Чтобы убрать ее, достаточно выполнить повторное считывание данных в модель вызо
вом метода select() .
♦ insertRecord(<Индекс>, <Запись>) - добавляет в модель новую запись по индексу, ука
занному первым параметром. Если задан отрицательный индекс, запись добавляется
. в конец модели. Добавляемая запись представляется объектом класса QSqlRecord, уже
заполненным необходимыми даюn,rми. Возвращает True, если запись была успешно
добавлена, и False - в противном случае;
♦ setRecord(<Индeкc>, <Запись>) -заменяет запись по индексу, указанному первым па
раметром, другой записью, которая передается вторым параметром в виде объекта клас
са QSqlRecord, уже заполненного необходимыми даюn,rми. Возвращает тrue, если запись
бьша успешно изменена, и False - в противном случае;
♦ suЬmit() -переносит в базу данных изменения, сделанные в текущей записи, если был
задан режим редактирования OnМanualSuЬmit. Возвращает True, если изменения были
успешно перенесены, и False -в противном случае. Метод является слотом;
♦ suЬmitAll() - переносит в базу данных изменения, сделаюn,rе во всех записях, если
был задан режим редактирования OnМanualSuЬmit. Возвращает True, если изменения
были успешно перенесены, и False - в противном случае. Метод является слотом;
Глава 24. Работа с базами данных 567
♦ revert() - отменяет изменения, сделанные в текущей записи, если был задан режим
редактирования OnМanualSuЬmit. Возвращает тrue, если изменения были успешно отме
нены, и False - в противном случае. Метод является слотом;
♦ revertRow(<Индекс заГ)иси>) - отменяет изменения, сделанные в записи с заданным
индексом, если бьш задан режим редактирования OnМanualSuЬmit;
♦ revertAll() - отменяет изменения, сделанные во всех записях, если был задан режим
редактирования OnМanualSuЬmit. Возвращает True, если изменения были успешно отме
нены, и False - в противном случае. Метод является слотом;
♦ selectRow(<Индeкc записи>) - обновляет содержимое записи с указанным индексом.
Возвращает True, если запись была успешно обновлена, и False - в противном случае.
Метод является слотом;
♦ isDirtу(<Индекс QModelindex>) - возвращает True, если запись с указанным индексом
была изменена, но эти изменения еще не бьши перенесены в базу данных, и False -
в противном случае;
♦ isDirty() - возвращает тrue, если хотя бы одна запись в модели была изменена, но эти
изменения еще не были перенесены в базу данных, и False - в противном случае;
♦ fieldindex(<Имя поля>) - возвращает индекс поля с указанным именем или -1, если
такого поля нет;
♦ primaryKey() - возвращает сведения о ключевом индексе таблицы, представленные
объектом класса QSqlindex, или пустой объект этого класса, если таблица не содержит
ключевого индекса.
Методы insertRecord() и setRecord(), предназначенные соответственно для добавления и
изменения записи, принимают в качестве второго параметра объект класса QSqlRecord.
Формат вызова конструктора этого класса:
QSqlRecord([<Oбъeкт класса QSqlRecord>])
Если в параметре указать объект класса QSqlRecord, будет создана его копия. Обычно при
создании новой записи здесь указывают значение, возвращенное методом record() класса
QSqlDatabase (оно хранит сведения о структуре таблицы и, следовательно, представляет
пустую запись), а при правке существующей записи - значение, возвращенное методом
record(), который унаследован классом QSqlтаыемоdеl от класса QSqlQueryМodel (оно
представляет запись, которую нужно отредактировать).
Класс QSqlRecord, в дополнение к методам, рассмотренным нами в разд. 24.2.1, поддержи
вает следующие методы:
♦ value(<Индекс поля>) - возвращает значение поля текущей записи с заданным индек
сом;
♦ value(<Имя поля>) - возвращает значение поля текущей записи с заданным именем;
♦ setValue (<Индекс поля>, <Значение>) - заносит в поле с указанным индексом новое
значение;
♦ setValue (<Имя поля>, <Значение>) - заносит в поле с указанным именем новое значе
ние;
♦ isNull(<Индекс поля>) - возвращает True, если в поле с указанным индексом нет зна
чения, и False - в противном случае;
568 Часть 11. Библиотека PyQt б
♦ isNull(<Имя поля>) - возвращает True, если в поле с указанным именем нет значения,
и False - в противном случае;
♦ setNull(<Индекс поля>) - удаляет значение из поля с указанным индексом;
♦ setNull(<Имя поля>) -удаляет значение из поля с указанным именем;
♦ clearvalues() -удаляет значения из всех полей записи;
♦ setGenerated(<Индeкc поля>, <Флаг>) - если вторым параметром передано False, поле
с указанным индексом помечается как неактуальное, и хранящееся в нем значение не
будет перенесено в таблицу;
♦ setGenerated(<Имя поля>, <Флаг>) -. если вторым параметром передано False, поле
с указанным именем помечается как неактуальное, и хранящееся в нем значение не
будет перенесено в таблицу;
♦ isGenerated(<Индeкc поля>) - возвращает False, если поле с указанным индексом по
мечено как неактуальное, и тrue - в противном случае;
♦ isGenerated(<Имя поля>) - возвращает False, если поле с указанным именем помечено
как неактуальное, и тrue - в противном случае.
Пример кода, добавляющего новую запись в модель:
con = QtSql.QSqlDatabase.addDatabase('QSQLITE')
con.setDatabaseName('data. sqlite')
con.open()
stm = QtSql.QSqlTaЬleModel()
stm. setTaЫe('good')
stm.select()
rec = con.record('good' )
rec. setValue( 'goodname', 'Коврик дr1я мьШIИ')
rec.setValue('goodcount', 2)
stm.insertRecord(-1, rec)
Пример кода, редактирующего существующую запись с индексом 3:
rec = stm.record(З)
rec.setValue('goodcount', 5)
stm.setRecord(З, rec)
def addRecord():
# Вставляем пустую запись, в которую пользователь сможет
# ввести нужные данные
stm.insertRow(stm.rowCount())
def delRecord():
# Удаляем запись из модели
stm.removeRow(tv.currentlndex().row())
# Вьmолняем повторное считывание данных в модель,
# чтобы убрать пустую "мусорную" запись
stm.select ()
арр = QtWidgets.QApplication(sys.argv)
window = QtWidgets.QWidget()
window.setWindowТitle("QSqlTaЫeModel")
# Устанавливаем соединение с базой данных
con = QtSql.QSqlDatabase.addDataЬase('QSQLITE')
con.setDataЬaseName('data.sqlite')
con.open()
# Создаем модель
stm = QtSql.QSqlTaЬleМodel(parent=window)
stm.setTaЫe('good')
stm.setSort(l, QtCore.Qt.SortOrder.AscendingOrder)
stm.select()
# Задаем заголовки для столбцов модели
stm.setHeaderData(l, QtCore.Qt.Orientation.Horizontal, 'Название')
stm.setHeaderData(2, QtCore.Qt.Orientation.Horizontal, 'Кол-во')
# Задаем для таблицы только что созданную модель
vbox = QtWidgets.QVВoxLayout()
tv = QtWidgets.QТaЬleView()
tv.setModel(stm)
570 Часть //. Библиотека PyQt б
i] QSqlTaЫeModel · □ х
Название Коп-во
!
Бумага офисная 115
2 Кабель 11
·тi
3 Картридж ;з
-t----ll
4 Компакт-диск
�;1
5 Флеш-накопитель ;20
6 Фотобумага 8
Добавить запи_сь·
}'далить запись
[i] QSqlTaЬleModel о х
Название Кол-во Категор
ия
1 !Бума га офисная
2 б ль
l
11
1s
Ка е
а тридж 1з
З К р г-
4 К омпакт-д иск 1·· ... ·13·
;5
20
5 Флеш- нак опитель
!З
6 Ф т бумага
о о
Удалить запись
Рис. 24.2. Пример складской программы после доработки: вместо названий категорий
выводятся их числовые идентификаторы
Поля, чьи имена указываются во втором и третьем параметрах, относятся к первичной таб
лице.
В листинге 24.1 О приведен код исправленной складской программы, а на рис. 24.3 показан
ее интерфейс.
def addRecord():
stm.insertRow(stm.rowCount())
def delRecord():
stm.removeRow(tv.currentindex().row())
stm.select()
арр = QtWidgets.QApplication(sys.argv)
window = QtWidgets.QWidget()
window.setWindowТitle("QRelationalSqlTaЬleМodel")
con = QtSql.QSqlDatabase.addDatabase('QSQLITE')
con.setDatabaseName('data.sqlite')
con.open()
stm = QtSql.QSqlRelationalTaЫeModel(parent=window)
stm.setTaЫe('good')
stm.setSort(l, QtCore.Qt.SortOrder.AscendingOrder)
# Задаем для поля категории связь с таблицей списка категорий
stm.setRelation(З, QtSql.QSqlRelation('category', 'id', 'catname'))
stm. select()
stm.setНeaderData(l, QtCore.Qt.Orientation.Hoiizontal, 'Название')
stm.setHeaderData(2, QtCore.Qt.Orientation.Horizontal, 'Кол-во')
stm.setHeaderData(З, QtCore.Qt.Orientation.Horizontal, 'категория')
vbox = QtWidgets.QVВoxLayout()
tv = QtWidgets.QТaЬleView()
tv.setModel(stm)
tv.hideColumn(O)
tv.setColumnWidth(l, 150)
tv.setColumnWidth(2, 60)
tv.setColumnWidth(З, 150)
vbox.addWidget(tv)
ЬtnAdd = QtWidgets.QPushButton("&Дoбaвить запись")
ЬtnAdd.clicked.connect(addRecord)
vbox.addWidget(ЬtnAdd)
ЬtnDel = QtWidgets.QPushВutton("&Удалить запись")
ЬtnDel.clicked.connect(delRecord)
vbox.addWidget(ЬtnDel)
window.setLayout(vbox)
window.resize(420, 250)
window.show()
sys.exit(app.exec())
Глава 24. Работа с базами данных 573
iiJ QRelationalSqlTaЬleModel □ х
Н азвание Кол-во К атего и я
р
11 s
Бумага офисная j Расходные материалы
2 К абел
ь ... ]_1 �· J�р�ч�� ��- .•..
3 и
Картр дж
j\.3 j Расходные материал ы
. 11
4 К ом пак т-диск j5 Н опите ли
5 Флеш-накоnитель
+· ...
! 20
+Нак .... .............. .
j акопите ли
. ·10;
!
tv = QtWidgets.QTaЬleView()
tv. setModel (stm)
tv.setitemDelegateForColumn(З, Qtsql.QSqlRelationalDelegate(tv))
tv.hideColumn(O)
[i] QRelationalSqlTaЬleModel □ х
Название Кол-во Катеrория
2 Кабель Прочее
5 Прочее
4 Компакт-диск
-··· ···--·-----··--·---- Ра�одные материалы
5 Флеш-накопитель 20 Накопители
Работа с графикой
Четвертый формат позволяет задать все характеристики пера за один раз. В параметре style
указывается стиль линий. Необязательный параметр сар задает стиль концов линий в виде
элемента перечисления PenCapStyle из модуля QtCore.Qt:
♦ Flatcap - квадратные концы линии. Длина линии не превышает указанных граничных
точек;
♦ Squarecap - квадратные концы. Длина линии увеличивается с обоих концов на полови
ну ширины линии;
♦ RoundCap - скрутленные концы. Длина линии увеличивается с обоих концов на полови-
ну ширины линии.
Необязательный параметр j oin задает стиль соединения линий - в качестве значения ука
зываются следующие элементы перечисления PenJoinStyle из модуля QtCore.Qt:
♦ BevelJoin - линии соединяются под �стрым углом;
♦ MiterJoin - острые утлы срезаются на определенную величину;
♦ RoundJoin - скругленные углы;
♦ SvgMiterJoin - линии соединяются под острым утлом, как определено в спецификации
SVG 1.2 Tiny.
Последний формат создает новый объект на основе указанного в параметре.
Класс QPen поддерживает следующие методы (здесь приведены только основные - полный
их список можно найти на странице https://doc.qt.io/qt-6/qpen.html):
♦ setColor(<Цвет QColor>) - задает цвет линий;
♦ setBrush(<КИсть QBrush>) - задает кисть;
♦ setWidth(<Ширина типа int>) и setWidthF(<Ширина типа float>) - задают ширину ли-
ний целым числом или числом с плавающей точкой соответственно;
♦ setStyle(<Стиль PenStyle>) - задает стиль линий;
♦ setCapStyle(<Стиль PenCapStyle>) - задает стиль концов линий;
♦ setJoinStyle(<Стиль PenJoinStyle>) - задает стиль соединения линий;
♦ setMiterLimit(<Величина срезания>) - задает величину срезания острых углов, если
указан режим соединения MiterJoin.
Во втором и пятом форматах цвет может быть задан в виде объекта класса QColor или эле
мента перечисления GlobalColor из модуля QtCore . Qt (например: Ыасk).
Во втором и третьем форматах стиль кисти в параметрах <Стиль кисти> и style указывается
в виде элемента перечисления BrushStyle из модуля QtCore. Qt: NoBrush, SolidPattern,
JenselPattern, Dense2Pattern, DenseЗPattern, Dense4Pattern, Dense5Pattern, DenseбPattern,
Dense7Pattern, CrossPattern и др. Так, можно сделать цвет сплошным (SolidPattern) или
имеющим текстуру (например, элемент CrossPattern задает текстуру в виде сетки).
Четвертый формат создает кисть с градиентной заливкой. В параметре <Градиент> указыва
ется объект одного из классов, производных от QGradient: QLinearGradi,ent (линейный гра
диент) , QConicalGradient (конический градиент) или QRadialGradient (радиальный гради
ент). За подробной информацией по этим классам обращайтесь к документации.
Пятый формат создает кисть с заданным сплошным цветом и текстурой, создаваемой ука
занным изображением. Шестой формат создает кисть с черным цветом и текстурой, созда
ваемой указанным изображением.
Последний формат создает копию указанной кисти.
Класс QBrush поддерживает следующие полезные для нас методы (полный их список приве
ден на странице https://doc.qt.io/qt-6/qbrush.html):
♦ setColor(<Цвет>) - задает цвет кисти (объект класса QColor или элемент перечисления
GlobalColor из модуля QtCore. Qt);
♦ setStyle(<Стиль BrushStyle>) -задает стиль кисти;
♦ setTexture (<Изображение QPixmap>) -устанавливает растровое изображение в качестве
текстуры;
♦ setTextureimage (<Изображение Qimage>) - устанавливает изображение в качестве тек
стуры.
ПРИМЕЧАНИЕ
Класс QLine предназначен для работы с целыми числами. Чтобы работать с вещественны
ми числами, необходимо использовать класс QLineF.
ПРИМЕЧАНИЕ
Класс QFontмetrics предназначен для работы с целыми числами. Чтобы работать с веще
ственными числами, необходимо использовать класс QFontмetricsF.
Устанавливать перо или кисть необходимо перед каждой операцией рисования, требующей
изменения цвета или стиля. Если перо или кисть не установлены, будут использоваться
объекты с настройками по умолчанию. После установки пера и кисти можно приступать
к рисованию точек, линий, фигур, текста и др.
Для рисования точек, линий и фигур класс QPainter предоставляет следующие наиболее
часто употребляемые методы (полный их список приведен на странице https://doc.qt.io/
qt-6/qpainter.html):
♦ drawPoint() - рисует точку. Форматы метода:
drawPoint(<X>, <У>)
drawPoint(<Koopдинaты QPoint или QPointF>)
5760 = 16 х 360. Нулевой угол находится в позиции «трех часов». Положительные зна
чения углов отсчитываются против часовой стрелки, а отрицательные - по часовой
стрелке;
♦ drawChord() - рисует замкнутую дугу. Аналогичен методу drawArc(), но соединяет
крайние точки дуги прямой линией. Форматы метода:
drawChord(<X>, <У>, <ШИрина>, <Высота>, <Начальный угол>, <Угол>)
drаwСhоrd(<Прямоугольник QRect или QRectF>, <Начальный угол>, <Угол>)
♦ drawPie() - рисует замкнутый сектор. Аналогичен методу drawArc(), но соединяет
крайние точки дуги с центром окружности. Форматы метода:
drawPie(<X>, <У>, <ШИрина>, <Высота>, <Начальный угол>, <Угол>)
drаwРiе(<Прямоугольник QRect или QRectF>, <Начальный угол>, <Угол>)
При выводе некоторых фигур (например, эллипса) контур может отображаться в виде
«лесенки». Чтобы сгладить контуры фигур, следует вызвать метод setRenderHint (<Режим
сглаживания>) и передать ему в качестве единственного параметра элемент Antialiasing
перечисления RenderHint из класса QPainter:
painter.setRenderHint(QtGui.QPainter.RenderHint.Antialiasing)
Если требуется отключить сглаживание, следует вызвать тот же метод, но передать ему
вторым параметром значение False:
painter.setRenderHint(QtGui.QPainter.RenderHint.Antialiasing, False)
♦ scale(<По оси Х>, <По оси У>) - масштабирует систему координат. В качестве значе
ний указываются вещественные числа. Если значение меньше единицы, то выполняется
уменьшение, а если больше единицы - то увеличение;
♦ shear(<По горизонтали>, <По вертикали>) - сдвигает систему координат. В качестве
значений указываются вещественные числа.
Все указанные трансформации влияют на последующие операции рисования и не изменяют
ранее нарисованные фигуры. Чтобы после трансформации восстановить систему координат,
следует предварительно сохранить состояние в стеке с помощью метода save(), а после
окончания рисования вызвать метод restore():
painter.save() # Сохраняем состояние
# Трансформируем и рисуем
painter.restore() # Восстанавливаем состояние
Несколько трансформаций можно произвести последовательно друг за другом. При этом
надо учитывать, что порядок следования трансформаций имеет значение.
Если одна и та же последовательность трансформаций выполняется несколько раз, то ее
можно сохранить в объекте класса QTransfoпn, а затем установить с помощью метода
setTransforrn(<Tpaнcфopмaция>):
transforrn = QtGui.QTransfonn()
transfonn.translate(105, 105)
transfonn.rotate(45.0)
painter.setTransfonn(transforrn)
painter.fillRect(-25, -25, 50, 50, QtCore.Qt.green)
Поскольку класс QPixrnap ·наследует класс QPaintDevice, мы можем использовать его ка!<
поверхность для рисования. Вывести изображение позволяет метод drawPixrnap () класса
QPainter (см. разд. 25.2.3).
Форматы конструктора класса:
QPixmap()
QРiхmар(<Ширина>, <Высота>)
QРiхmар(<Размеры QSize>)
QРiхmар(<Путь к файлу>[, foпnat=None][, flags =Image.ConversionFlag .AutoColor])
QРiхmар(<Исходный объект QPixrnap>)
Первый формат создает пустой объект изображения. Второй и третий форматы позволяют
указать размеры изображения - если размеры равны нулю, то будет создан пустой объект.
Четвертый формат предназначен для загрузки изображения из файла. Во втором параметре
указывается тип изображения в виде строки (например, "PNG")- если он не указан, то
формат будет определен по расширению загружаемого файла. Пятый конструктор создает
коmпо указанного изображения.
Класс QPixrnap поддерживает следующие методы (здесь приведены только основные - пол
ный их список можно найти на странице https://doc.qt.io/qt-6/qpixmap.html):
♦ isNull () - возвращает значение True, если объект является пустым, и False - в про
тивном случае;
♦ load()_ - загружа:ет изображение из файла с указанным путем. Формат метода:
lоаd(<Путь к файпу>[, format =None][, flags= ImageConversionFlag .AutoColor])
Во втором параметре можно задать формат файла в виде строки - если он не указан,
формат определяется по расширению файла. Необязательный параметр flags задает тип
преобразования цветов. Метод возвращает значение тrue, если изображение успешно
загружено, и False - в противном случае;
♦ loadFromData () - загружает изображение из заданного объекта класса QByteArray.
Формат метода:
loadE'romData(<Объект QByteArray или bytes> [, foпnat=None] [,
flags = ImageConversionFlag.AutoColor])
Метод возвращает значение True, если изображение успешно загружено, и False -
в противном случае;
♦ sаvе(<Путь к файлу>[, format=None][, quality=-1]) - сохраняет изображение в файл
с указанным пут-ем. Во втором параметре можно задать формат файла в виде строки -
если он не указан, формат будет определен по расширению файла. Необязательный
параметр quality позволяет задать качество изображения. Можно передать значение
в диапазоне от О до 100, значение -1 указывает качество по умолчанию. Метод возвраща
ет значение True, если изображение успешно сохранено, и False - в противном случае;
♦ convertFromimage() - преобразует заданный объект класса Qimage в объект класса
QPixrnap. Формат метода:
convertFromimage(<Изoбpaжeниe Qimage>[, flags= ImageConversionFlag.AutoColor])
Метод возвращает значение тrue, если изображение успешно преобразовано, и False -
в противном случае;
596 Часть 11. Библиотека PyQt 6
В параметре <Индекс или цвет> у 8-битных изображений задается индекс цвета в палит
ре, а у 32-битных - целочисленное значение цвета, получить которое позволяют мето
ды rgb() и rgba() класса QColor;
♦ pixel() - возвращает целочисленное значение цвета пиксела с указанными координа
тами. Это значение можно передать конструктору класса QColor, а затем получить зна
чения различных составляющих цвета. Форматы метода:
pixel(<X>, <У>)
рiхеl(<Координаты QPoint>)
♦ convertToForrnat() - преобразует текущее изображение в указанный формат (задается
в виде элемента перечисления Forrnat из класса Qii;na_ge) и возвращает новый объект клас
са Qimage. Исходное изображение не изменяется. Форматы метода:
convertToForrnat(<Фopмaт>[, flags=ImageConversionFlag.AutoColor])
convertToFoпnat(<Фopмaт>, <Таблица цветов>[,
flags=ImageCpnversionFlag.AutoColor])
♦ сору() - возвращает объект класса Qimage с прямоутольным фрагментом изображения
с заданными параметрами. Форматы метода:
сору([<Параметры фрагмента QRect>])
сору(<Х>, <У>, <Ширина>, <Высота>)
Если в первом формате параметры фрагмента не указаны, изображение копируется
целиком;
♦ scaled() - изменяет размер изображения и возвращает результат в виде объекта класса
Qimage. Исходное изображение не изменяется. Форматы метода:
sсаlеd(<Ширина>, <Высота>[,
�spectRatioMode7AspectRatioMode.IgnoreAspectRatio][,
transformМode=TransfoпnationМode .FastTransforrnation])
sсаlеd(<Размеры QSize>[,
aspectRatioMode=AspectRatioMode.IgnoreAspectRatio][,
transformМode=TransforrnationМode.FastTransforrnation])
В необязательном параметре aspectRatioMode могут быть указаны следующие элементы
перечисления AspectRatioMode из модуля QtCore.Qt:
• IgnoreAspectRatio - изменяет размеры без сохранения пропорций сторон;
• KeepAspectRatio - изменяет размеры с сохранением пропорций сторон. При этом
часть области нового изображения может оказаться незаполненной;
• KeeF.1}\spectRatioByExpqflding - изменяет размеры с сохранением пропорций сторон.
При этом часть нового изображения может выйти за пределы его области.
В необязательном параметре transformМode могут быть указаны следующие элементы
перечисления TransforrnationМode из модуля QtCore.Qt:
• FastTransforrnation - сглаживание выключено;
• SmoothTransforrnation - сглаживание включено;
♦ scaledToWidth() - изменяет ширину изображения и возвращает результат в виде объек
та класса Qimage. Формат метода:
scaledToWidth(<Шиpинa>[, mode=TransforrnationМode.FastTransforrnation])
Глава 25. Работа с графикой 601
Графическая сцена
Параметр mode аналогичен таковому у метода collidingrtems() (см. разд. 26.1.4). Вто-
' рой параметр первого формата и параметр deviceтransform второго формата задают
примененные к сцене преобразования системы координат (см. разд. 25.2.4).
Третий формат позволяет дополнительно указать, какую операцию следует выполнить
с ранее выделенными объектами сцены, в виде одного. из следующих элементов пере
числения ItemSelectiorIOperation из модуля QtCore.Qt:
• ReplaceSelection - ранее выделенные объекты перестанут быт�; выделенными;
• AddToSelection - ранее выделенные объекты останутся выделенными;
♦ selectionArea() - возвращает область выделения в виде объекта класса QPainterPath;
♦ selectedrtems() - возвращает список со ссьmками на выделенные объекты или пустой
список, если выделенных объектов нет;
♦ clearSelection() - снимает выделение. Метод является слотом.
В параметре layers, задающем слои, которые требуется перерисовать, могут быть указа
ны следующие элементы перечисления SceneLayers из класса QGraphicsScene:
• ItemLayer - слой объекта;
• BackgroundLayer - слой заднего плана;
• ForegroundLayer - слой переднего плана;
• AllLayers - все слои. Сначала отрисовывается слой заднего плана, затем - слой
объекта и в конце - слой переднего плана.
Второй формат метода является слотом;
♦ update() - вызывает перерисовку прямоугольной области с указанными параметрами.
Форматы метода:
update(<X>, <У>, <Ширина>, <Высота>)
update([rect=QRectF()])
Второй формат метода является слотом.
Класс QGraphicsScene поддерживает следующие сигналы:
♦ changed(<Список областей>) - генерируется при изменении сцены. Внутри обработчика
через параметр доступен список с объектами класса QRectF, представляющими изме
нившиеся области сцены;
♦ sceneRectChanged(<Paзмepы QRectF>) - генерируется при изменении размеров сцены.
Внутр.и обработчика через параметр доступен объект класса QRectF с новыми координа
тами и размерами сцены;
♦ selectionChanged() - генерируется при изменении выделения объектов;
♦ focusitemChanged(<Oбъeкт 1>, <Объект 2>, <Причина изменения фокуса>) - генериру
ется при изменении фокуса клавиатурного ввода. Через параметры доступны объект, по
лучивший фокус ввода, объект, потерявший фокус ввода, и причина изменения фокуса
(см. разд. 20.8.1).
26.2.2. Преобразования
между координатами представления и сцены
Для преобразования между координатами представления и сцены предназначены следую
щие методы класса QGraphicsView:
♦ mapFromScene() - преобразует заданные координаты точки из системы координат сцены
в систему координат представления. Формать1 метода (справа указан тип возвращаемого
значения):
mapFromScene(<X>, <У>) -> QPoint
mapFromScene(<QPointF>) -> QPoint
mapFromScene(<X>, <У>, <Ширина>, <Высота>) -> QPolygon
612 Часть //. Библиотека PyQt б
♦ scale(<По оси Х>, <По оси У>) - масштабирует систему координат. В качестве значе
ний указываются вещественные числа. Если значение меньше единицы, то выполняется·
. уменьшение, а если больше единицы - то увеличение;
♦ shear(<По горизонтали>, <По вертикали>) - сдвигает систему координат. В качестве
значений указьmаются вещественные числа;
♦ resetTransfoпn () - отменяет все преобразования.
Несколько преобразований можно произвести последовательно друг за другом. При этом
надо учитывать, что порядок следования преобразований имеет значение.
Если одна и та же последовательность преобразований выполняется несколько раз, то ее
можно сохранить в объекте класса QTransform, а затем установить с помощью метода
setTransfoпn(). Получить ссылку на установленное преобразование позволяет метод
transfoпn ().
♦ setcursor(<�сор>) - задает внешний вид курсора мыши при наведении его на объект
(см.разд. 20.9.5);
♦ unsetcursor() - отменяет изменение курсора мыши;
♦ setVisiЫe(<Флаг>) - если в качестве параметра указано значение True, то объект будет
видим. Значение False скрывает объект;
♦ show() - делает объект видимым;
♦ hide() - скрывает объект;
♦ isVisiЫe() - возвращает значение True, если объект видим, и False - если скрыт;
♦ sеtЕnаЫеd(<Флаг>) - если в качестве параметра указано значение True, объект будет
доступен. Значение False делает объект недоступным. Недоступный объект не получает
никаких событий, и его нельзя выделить;
♦ isEnaЫed t) - возвращает значение True, если объект доступен, и False - если недос
тупен;
♦ setSelected(<Флaг>) - если в качестве параметра указано значение тrue, объект будет
выделен. Значение False снимает выделение. Чтобы объект можно было выделить, не
обходимо установить флаг ItemisSelectaЫe - например, с помощью метода setFlag();
♦ isSelected() - возвращает значение True, если объект выделен, и False - в противном
случае;
♦ setFocus( [ focusReason=FocusReason. OtherFocusReason]) - устанавливает фокус ввода на
объект. В параметре focusReason можно указать причину изменения фокуса ввода
(см. разд. 20.8.1). Чтобы объект мог принимать фокус ввода, необходимо установить флаг
ItemisFocusaЫe - например, с помощью метода setFlag ();
♦ clearFocus() - убирает фокус ввода с объекта;
♦ hasFocus() - возвращает значение True, если объект находится в фокусе ввода, и
False - в противном случае;
♦ graЬKeyboard() - захватывает ввод с клавиатуры;
♦ ungrabKeyboard() - освобождает ввод с клавиатуры;
♦ grabMouse () - захватывает мышь;
♦ ungraЬMouse() - освобождает мышь.
26.3.2.1. Линия
Класс QGraphicsLineitem описывает линию. Иерархия наследования:
QGraphicsitem - QGraphicsLineitem
Форматы конструктора класса:
QGraphicsLineitem([parent=None])
QGraphicsLineitem(<Xl>, <Yl>, <Х2>, <У2>[, parent=None])
QGraphicsLineitem(<Линия QLineF>[, parent=None])
В параметре parent можно указать ссылку на родительский объект.
Класс QGraphicsLineitem наследует все методы класса QGraphicsitem и поддерживает сле
дующие методы (полный их список можно найти на странице https://doc.qt.io/qt-6/
qgraphicslineitem.html):
♦ setLine() - задает параметры линии. Форматы метода:
setLine(<Xl>, <Yl>, <Х2>, <У2>)
sеtLinе(<Линия QLineF>)
♦- line() - возвращает параметры линии в виде объекта класса QLineF;
♦ setPen(<Перо QPen>) - устанавливает перо.
26.3.2.3. Прямоугольник
Класс QGraphicsRectitem описывает прямоугольник. Иерархия наследования:
QGraphicsitem - QAЬstractGraphicsShapeitem - QGraphicsRectitem
Форматы конструктора класса:
QGraphicsRectitem([parent=None])
QGraphicsRectit·em(<X>, <У>, <Ширина>, <Высота>[, parent=None])
QGraphicsRectitem(<Пpямoyгoльник QRectF>[, parent=None])
26.3.2.4. Многоугольник
Класс QGraphicsPolygonitem описывает многоугольник. Иерархия наследования:
QGraphicsitem - QAЬstractGraphicsShapeitem - QGraphicsPolygonitem
Форматы конструктора класса:
QGraphicsPolygonitem( [parent=None])
QGraphicsPolygonitem(<Мнoгoyгoльник QPolygonF>[, parent=None])
26.3.2.5. Эллипс
Класс QGraphicsEllipseitem описывает эллипс. Иерархия наследования:
QGraphicsitem - QAЬstractGraphicsShapeitem - QGraphicsEllipseitem
620 Часть /1. Библиотека PyQt б
26.3.2.6. Изображение
Класс QGraphicsPixrnapitem описывает изображение. Иерархия наследования:
QGraphicsitem - QGraphicsPixrnapitem
Форматы конструктора класса:
QGraphicsPixrnapitem([parent=None])
QGraphicsPixrnapitem(<Изoбpaжeниe QPixrnap>[, parent=None])
В параметре parent можно указать ссылку на родительский объект.
Класс QGraphicsPixrnapitem наследует все методы из класса QGraphicsitem и поддерживает
следующие методы (здесь приведены только основные - пощ1ый их список можно найти
на странице bttps://doc.qt.io/qt-6/qgraphicspixmapitem.html):
♦ setPixrnap(<Изображение QPixrnap>) - задает изображение;
♦ pixrnap() - возвращает представляющий изображение объект класса QPixrnap;
♦ setOffset() - задает местоположение изображения в объекте. Форматы метода:
setOffset(<X>, <У>)
setOffset(<Koopдинaты QPointF>)
♦ offset() - возвращает местоположение изображения в виде объекта класса QPointF;
♦ setShapeMode(<Режим>) - задает режим определения формы изображения. В качестве
параметра могут быть указаны следующие элементы перечисления Shapeмocte из класса
QGraphicsPixrnapitem:
Глава 26. Графическая сцена 621
26.5. Эффекты
К графическим объектам допускается применить различные эффекты: изменение прозрач
ности, цвета, отображение тени, размытие и пр. Наследуя класс QGraphicsEffect и переоп
ределяя в нем метод draw(), можно создать свой эффект.
Для установки эффекта и получения ссылки на него предназначены следующие методы
класса QGraphicsitem:
♦ setGraphicsEffect(<Эффект>) - задает эффект в виде объекта класса, производного от
QGraphicsEffect;
♦ graphicsEffect() - возвращает ссьmку на эффект (объект класса, производного от
QGraphicsEffect) или значение None, если эффект не был установлен.
26.5.2. Тень
·Класс QGraphicsDropShadowEffect реализует вывод тени у. объекта. Иерархия наследования:
QObject - QGraphicsEffect - QGraphicsDropShadowEffect
Формат конструктора класса:
QGraphicsDropShadowEffect([parent=None])
Класс QGraphicsDropShadowEffect наследует все методы базовых классов и поддерживает
следующие методы (здесь приведены только осщ>вные - полный их список можно найти
на странице https://doc.qt.io/qt-6/qgraphicsdropshadoweffect.html):
♦ setColor(<Цвет QColor>) - задает цвет тени. По умолчанmо используется полупро
зрачный темно-серый цвет (QColor(63, 63, 63, 180} ). Метод является слотом;
♦ color() -возвращает цвет тени (объект класса QColor);
♦ setBlurRadius(<Радиус>} - задает радиус размытия. тени в виде вещественного числа.
По умолчанmо используется значение 1. Метод является слотом;
♦ ЫurRadius(} -возвращает радиус размытия тени;
♦ setOffset() - задает смещение тени. По умолчанmо тень смещена на 8 пикселов вниз и
вправо. Форматы метода:
setOffset(<Пo оси Х>, <По оси У>)
setOffset(<CМeщeниe по обеим осям>)
setOffset(<CМeщeниe QPointF>}
Второй конструктор задает смещение сразу по обеим осям координат. В первом и вто
ром конструкторах параметры задаются вещественнь1ми числами. Метод является сло
том;
♦ offset(} - возвращает смещение тени в виде объекта класса QPointF;
♦ setXOffset(<Смещение>) - задает смещение по оси х в виде вещественного числа. Метод
является слотом;
♦ xOffset(} - возвращает смещение по оси х;
♦ setYOffset(<Смещение>} - задает смещение по оси У в виде вещественного числа. Метод
является слотом;
♦ yOffset(} - возвращает смещение пр оси У.
Класс QGraphicsDropShadowEffect поддерживает сигналы:
♦ colorChanged(<Цвет QColor>} - генерируется при изменении цвета тени. Внутри обра
ботчика через параметр доступен новый цвет;
♦ ыurRadiusChanged(<Радиус размытия>) - генерируется при изменении радиуса размы
тия. Внутри обработчика чере:1 параметр доступно новое значение в виде вещественного
числа;
♦ offsetChanged(<Cмeщeниe QPointF>} - генерируется при изменении смещения. Внутри
обработчика через параметр доступно новое смещение.
Глава 26. Графическая сцена 625
26.5.З. Размытие
Класс QGraphicsBlurEffect реализует эффект размытия. Иерархия наследования:
QObject - QGraphicsEffect - QGraphicsBlurEffect
Формат конструктора класса:
QGraphicsBlurEffect([parent=None])
Класс QGraphicsВlurEffect наследует все методы из базовых классов и поддерживает сле
дующие методы (здесь приведены только основные - полный их список можно найти на
странице https://doc.qt.io/qt-6/qgraphicsblureffect.html):
♦ setВlurRadius(<Радиус>) - задает радиус размытия в виде вещественного числа.- По
умолчанию используется значение 5. Метод является слотом;
♦ ЫurRadius() - возвращает радиус размытия.
Класс QGraphicsBlurEffect поддерживает сигнал ЫurRadiusChanged(<Радиус>), который
генерируется при изменении радиуса размытия. Внутри обработчика через параметр дос
тупно новое значение радиуса в виде вещественного числа.
BHHMAHHEI
Вызов некоторых методов из метода itemChange () может привести к рекурсии. За подроб
ной информацией обращайтесь к документации по классу QGraphicsrtem.
ПРНМЕЧАННЕ
PyQt также поддерживает помещение на сцену видеозаписей в качестве отдельных графи
ческих объектов и создание анимации. За подробным описанием обращайтесь к докумен
тации по этой библиотеке.
ГЛАВА 27
Диалоговые окна
PyQt позволяет выводить как произвольные диалоговые окна, так и стандартные: открытия
файла, каталога, для ввода данных, для вывода сообщений и др.
Все рассмотренные в этой главе классы определены в модуле QtWidgets, если не указано
иное.
Первый формат добавляет стандартную кнопку или кнопки (в виде одного из рассмот
ренных ранее элементов перечисления standardБutton или их комбинации через опера
тор 1 ). Второй формат принимает в качестве первого параметра надпись для добавляе
мой кнопки, а в качестве второго - ее роль. Третий формат принимает добавляемую
кнопку в виде объекта одного из подклассов класса QAЬstractButton - как правило,
класса QPushButton, представляющего обычную кнопку.
В качестве роли указывается один из следующих элементов перечисления вuttonRole из
класса QDialogButtonВox:
• InvalidRole - ошибочная роль;
• AcceptRole - нажатие кнопки устанавливает код возврата равным значению элемен
та Acceptect;
• RejectRole - нажатие кнопки устанавливает код возврата равным значению элемен
та Rejectect;
• DestructiveRole - кнопка для отказа от изменений и закрытия диалогового окна;
• ActionRole - нажатие кнопки приводит к выполнению операции, которая не связана
с закрытием окна;
• HelpRole - кнопка для отображения справки;
• YesRole - кнопка Yes;
• NoRole ---: кнопка No;
• ResetRole - кнопка для установки значений по умолчанию;
• ApplyRole - кнопка для принятия изменений.
Если роль недействительна, кнопка добавлена не будет.
Первый и второй форматы возвращают ссылку на сгенерированную и добавленную
в контейнер кнопку, третий не возвращает ничего:
self.ЬtnYes = QtWidget.QPushButton("&Дa")
self.box.addБutton(self.ЬtnYes,
QtWidget.QDialogButtonBox.ButtonRole.AcceptRole)
self.ЬtnNo = self.box.addButton(QtWidget.QDialogButtonBox.StandardButton.No)
self.ЬtnCancel = self.box.addБutton("&Cancel",
QtWidget.QDialogButtonBox.StandardБutton.RejectRole)
♦ button(<Стандартная кнопка StandardБutton>) - возвращает ссылку на стандартную
кнопку, соответствующую указанному обозначению, или None, если такой стандартной
кнопки в контейнере нет;
♦ standardБutton(<Кнопка QAЬstractButton>) - возвращает обозначение стандартной
кнопки, переданной в качестве параметра, или значение элемента NoButton, если такой
кнопки в контейнере нет;
♦ standardButtons () - возвращает комбинацию обозначений всех стандартных кнопок,
добавленных в контейнер;
♦ buttonRole(<Кнопка QAЬstractButton>) - возвращает роль указанной в параметре
кнопки. Если такая кнопка отсутствует, возвращается значение э:ле�ента InvalidRole;
♦ buttons() - возвращает список со ссылками на все кнопки, которые бьши добавлены
в контейнер;
Глава 27. Диалоговые окна 639
о
iiJ Текст заг�ловка Х
Текст сообщения
Close
iL-----�
Yes iГ .t::!o ! 1 Cancel
В параметре <Родитель> указывается ссылка на родительское окно или значение None. Если
указана ссылка, диалоговое окно будет центрироваться относительно родительского окна,
а не относительно экрана. Необязательный параметр buttons позволяет указать вьmодящие
ся на экран стандартные кнопки (элементы перечисления, задающие стандартные кнопки,
указываются через оператор 1 ). По умолчанию отображается кнопка ОК. Параметр
defaultButton назначает кнопху по умолчанию. Метод critical() возвращает числовое
обозначение нажатой кнопки:
QtWidgets.QМessageBox.critical(window, "Текст заголовка",
"Программа вьmолнила недопустимую ошибку и будет закрыта",
defaultButton=QtWidgets.QМessageBox.StandardВutton.Ok)
Результат выполнения этого кода показан на рис. 27.4.
i] Текст заголовка х
.___о_к__.l
Рис. 27.4. Окно критического сообщения
!u о фреймворке Qt х
AboutQt
Qt provides single-source portabllity across all major desk\op operating systems. lt is also
availaЫe for embedded Linux and other embedded and moblle operating systems.
Qt is The Qt Company ltd product developed as an open source project. See _g!Jo for
more information.
ок
ШI
__о_к_�I [ СаП(еl 1
Рис. 27.7. Окно для ввода целого числа
if ok:
print("Bвeдeнo значение:", n)
ОК Сапсеl
dir QtWidgets.QFileDialog.getExistingDirectoryUrl(parent=window,
=
directory=QtCore.QUrl.fromLocalFile(QtCore.QDir.currentPath()))
dirName = dir.toLocalFile()
Результат выполнения этого кода показан на рис. 27.10.
l 27, Диал,
>
Папка: 11
Выбор папки,
■
_..,. Этот компьюте�
видео
� Документы
Dlls
Doc
include
.J, Загрузки i: lib
� Изображения libs
Scripts
J, Му з ыка
tcl
::J Объемные itf:.·
t Tools
8 Рабочий аол
Р python.exe 97 К
• System (С:)
5-' pythonw.exe 95 К
$WinREAgen1
§;) python3.dll 61 К
book l� python31O.dll 4367К
OneDrfVeTem L� vcruntime140.dll 95 К
J Program Files � vcruntime140_1.dll 37 К
1, Program Files
ProgramData
>
Имя файла: 1
�-------------------�
Qncpьm.
IJ Заголовок окна х
!!asic colors
нuо· Го�
�• c___L'!:.�
�
.Веd: 1255 L-:'Ji
�at: ��Ш lo__
§гееn: ЯJi
г-:
!:;ustom colors
г: · • ·.
!-,.....___,:.:;;;;-.;,;:_,
Дd� t� � ol rs ·- .· �· 1
Custo C o !:!TML: !#ffOooo-----�
if color.isValid():
print(color.red(), color.green(), color.Ыue(), color .alpha()).
Метод возвращает кортеж из двух элементов: (<Выбранный шрифт>, <Статус>). Если второй
элемент содержит значение True, первый элемент хранит объект класса QFont с выбра1rnым
шрифтом. Пример:
(font, ok) = QtWidgets.QFontDialog.getFont(QtGui.QFont("Tahoma", 16),
parent=window, сарtiоn= "Заголовок окна")
if ok:
print(font.family(), font.pointSize(), font.weight(),
font.italic(), font.underline())
Результат выполнения этого кода показан на рис. 27.14.
i] Заголовок окна х
F ont �---------- fon��txf_e ____ �ize
"" = = = �
1j 16
!,
.,,lli=
ft•=
j.= @j'-------------< обы_��ы ". .. . ••··
ii,= , ..
Sitka Subheading "
,., Q���':!!Й-� ·
1
8
Sitka Text Полужирный 9
Small Fonts 10
1
Sylfaen 11
12
gi!
1
��:::' �. f
Tahoma "!
------------�' �-------� �---�
�1 __ 1
Нfects Sample
О Stri!\eout
1
О !J.nderline
д_а_вь_v_у_2z.__
1 i Cancel j
Рис. 27 .14. Окно для выбора шрифта
(в виде строки), то не будут выводиться лишь сообщения того же типа. Оба формата метода
являются слотами. Пример:
ems = QtWidgets.QErrorMessage(parent=window)
ems.showMessage("Cooбщeниe об ошибке")
ems.showMessage("Cooбщeниe об ошибке типа warning", "warning")
iU pythonw Х
о Сообщение об ошибке
QK
iu Выполнение операции х
Выполняется операция
30%
1 Отмена
ВННМАННЕ!
Опции необходимо устанавливать до добавления прикрепляемых панелей. Исключением
ЯВЛЯЮТСЯ ОПЦИИ AnimatedDocks И VerticalTabs.
аddАсtiоn(<Действие QAction>)
addAction(<Haзвaниe>) -> QAction
addAction(<Haзвaниe>, <Обработчик>) -> QAction
Второй и третий форматы возвращают созданное действие;
♦ addSeparator() - добавляет в главное меmо разделитель и возвращает представляющее
его действие;
♦ insertSeparator(<Действие QAction>) - добавляет перед указанным действием разде-
литель и возвращает связанное с ним действие;
♦ clear() - удаляет все действия из главного меmо;
♦ setActiveAction(<Дейсоrвие QAction>) - делает активным указанное действие;
♦ activeAction() - возвращает активное действие или значение None;
♦ setDefaultUp(<Флaг>) - если в качестве параметра указано значение True, отдельные
меmо будут открываться над главным меmо, а не под ним;
♦ setVisiЫe(<Флаг>) - если в качестве параметра указано значение False, главное меmо
будет скрыто. Значение True отображает панель меmо.
Класс QMenuBar поддерживает следующие сигналь�:
♦ hovered(<Действие QAction>) - генерируется при наведении курсора мыши на какое-
либо действие. Последнее передается обработчику с параметром;
♦ triggered(<Дeйcтвиe QAction>) - генерируется при выборе какого-либо действия. По
следнее передается обработчику с параметром.
Внутри текста названия символ &, указанный перед буквой или цифрой, задает клавишу
быстрого доступа. Эта клавиша сработает только в том случае, если меmо, в котором на
ходится пункт, является активным.
Параметр shortcut задает комбин;щmо «горячих» клавиш, нажатие которых аналогично
выбору пункта в меmо. Нажатие комбинации «горячих» клавиш сработает даже в том
случае, если меmо не является активным. Примеры:
QtGui.QKeySequence("Ctrl +R")
QtGui.QKeySequence(QtCore.Qt.Key.Key_FS))
QtGui.QKeySequence .fromString("Ctrl+R")
Все форматы, за исключением первого, возвращают созданное действие;
♦ addSeparator() -добавляет в меmо действие-разделитель и возвращает ссылку на него;
♦ addSection() -добавляет в меmо действие-секцию, которая выглядит как разделитель
с заголовком и, возможно, значком, и возвращает ссылку на созданное действие. Форма
ты метода:
addSection(<Зaгoлoвoк>)
addSection(<Зaгoлoвoк>, <Значок,Qiсоn>)
♦ addМenu(<Меню QMenu>) - добавляет заданное меmо в качестве вложенного и возвращает
представляющее его действие (объект класса QAction);
♦ addМenu( [ <Значок Qicon>, J <Название>) - создает вложенное меmо, добавляет его
в текущее меmо и возвращает ссьmку на созданное меmо (объект класса QMenu);
♦ insertMenu(<Действие QAction>, <Меню QMenu>) - добавляет заданное меmо в качестве
вложенного перед указанным действием. Метод возвращает действие, соответствующее
добавленному меmо;
♦ insertseparator(<Действие QAction>) -добавляет действие-разделитель перед указан
ны� действием и возвращает ссьmку на добавленное действие;
♦ insertsection() -добавляет действие-секцию перед указанным действием и возвраща
ет ссылку на добавленное действие. Форматы метода:
insertSection(<Дe�cтвиe QAction>, <Заголовок>)
insertSection(<Дeйcтвиe QAction>, <Заголовок>, <Значок Qicon>)
♦ clear() -удаляет все действия из меmо;
♦ isEmpty() - возвращает значение True, если меmо не содержит видимых пунктов,
и False - в противном случае;
♦ menuAction() -возвращает действие, связанное с текущим меmо;
♦ setTitle(<Название>) -задает название меmо;
♦ title() -возвращает название меmо;
♦ seticon(<Значок Qicon>) - задает значок меmо;
♦ icon( J -возвращает значок меmо (объект :\<Ласса Qrcon);
♦ setActiveAction(<Действие QAction>) - делает активным пункт, соответствующий ука
занному действию;
♦ activeAction() - возвращает активный пункт (объект класса QAction) или значение
None;
♦ setDefaultAction(<Действие QAction>) -задает пункт по умолчанию;
678 Часть 11. Библиотека PyQt 6
self.setContextMenuPolicy(
QtCore.Qt.ContextMenuPolicy.ActionsContextMenu)
# Создаем действия actionl, action2 и actionЗ
self.addAction(self.actionl)
self.addAction(self.action2)
self.addAction(self.actionЗ)
Созданное таким образом контекстное меmо будет выводиться автоматически при щелчке
на компоненте правой кнопкой мыши.
Другой способ вывести контекстное меmо - переопределение у компонента метода
contextMenuEvent (self, <event>). Этот метод будет автоматически вызываться при щелчке
правой кнопкой мыши в области компонента. Внутри метода через параметр <event> досту
пен объект класса QContextMenuEvent, содержащий дополнительную информацию о собы
тии. Класс QContextMenuEvent поддерживает следующие основные методы:
♦ х() и у () - возвращают координаты по осям х и У соответственно в пределах области
компонента;
♦ pos () - возвращает объект класса QPoint с целочисленными координатами в пределах
области компонента;
680 Часть 11. Библиотека PyQt б
пункт,.является активным;
♦ text() - возвращает название действия;
♦ seticon(<Значок Qicon>) - устанавливает значок;
♦ icon() - возвращает значок (объект класса Qicon);
♦ seticonVisiЬleinМenu(<Флaг>) - если в качестве параметра указано значение False,
значок отображаться не будет;
♦ setSeparator(<Флаг>) - если в качестве параметра указано значение True, действие
станет разделителем;
Глава 28. Создание SDI- и МDl-программ 681
addAction(<Haзвaниe>)
addAction(<Знaчoк Qicon>, <Название>)
♦ removeAction(<Действие QAction>) -удаляет указанное действие из группы;
♦ actions() -возвращает список с действиями (объектами класса QAction), которые были
добавлены в группу, или пустой список;
♦ checkedAction() - возвращает ссьmку (объект класса QAction) на установленное дейст
вие-переюпочатель внутри группы при использовании эксклюзивного режима или зна
чение None;
684 Часть 11. Библиотека PyQt б
28.6. МDl-программы
Чтобы создать МDl-программу, следует в качестве центрального компонента главного окна
установить компонент QMdiArea вызовом метода setCentralWidget () класса QMainWindow.
Отдельное окно внутри МDI-области представляется классом QМdiSuЬWindow.
Мультимедиа
Библиотека PyQt предоставляет развитые средства воспроизведения звука и видео. Для это
го она задействует мультимедийную подсистему Windows Media Foundation, входящую
в состав Windows. Следовательно, все форматы, поддерживаемые операционной системой,
будут гарантированно поддерживаться и PyQt.
Все описанные в этой главе классы объявлены в модуле QtMultimedia, если не указано иное.
чанию. Однако звуковому выходу можно указать выводить звук на любое другое доступное
устройство.
Выяснить, какие устройства вывода звука существуют на текущей платформе, позволяют
следующие статические методы класса QMediaDevices:
♦ audioOUtputs() - возвращает список доступных устройств вывода звука, представляе
МЬIХ объектами класса QAudioDevice (описан далее) :
>>> dlist = QtMultimedia.QМediaDevices.audioOutputs()
>>> for d in dlist: print(d.description())
ii] Аудиопроиrрыватель Х
class MyWindow(QtWidgets.QWidget):
def _init_(self, parent=None):
QtWidgets.QWidget. init (self, parent,
flags=QtCore.Qt.WindowType.Window
QtCore.Qt.WindowType.MSWindowsFixedSizeDialogHint)
self.setWindowТitle("Ayдиoпpoигpывaтeль")
# Создаем устройство вывода звука
self.aOutput = QtMultimedia.QAudioOutput(parent=self)
seif.aOutput.setVolume(0.5)
# Создаем сам проигрыватель
self.mplPlayer = QtMultim�dia.QМediaPlayer(parent=self)
self.mplPlayer.setAudioOutput(self.aOutput)
self.mplPlayer.mediaStatusChanged.connect(self.initPlayer)
self.mplPlayer.playbackStateChanged.connect(self.setPlayerState)
vbox = QtWidgets:QvвoxLayout()
# Создаем кнопку открытия файла
btnOpen = QtWidgets.QPushВutton("&Oткpыть файл...")
btnOpen.clicked.connect(self.openFile)
vbox.addWidget(btnOpen)
# Создаем компоненты для управления воспроизведением.
# Делаем их изначально недоступными
self.sldPosition = QtWidgets.QSlider(QtCore.Qt.Orientation.Horizontal)
self.sldPosition.setMinimum(0)
self.sldPosition.valueChanged.connect(self.mplPlayer.setPosition)
self.mplPlayer.positionChanged.connect(self.sldPosition.setValue)
702 Часть 11. Библиотека PyQt 6
self.sldPosition.setEnaЫed(False)
vbox.addWidget(self.sldPosition)
hЬох = QtWidgets.QHBoxLayout()
self.btnPlay = QtWidgets.QPushButton("&Пycк")
self.btnPlay.clicked.connect(self.mplPlayer.play)
self.btnPlay.setEnaЫed(False)
hЬox.addWidget(self.btnPlay)
self.ЬtnPause = QtWidgets.QPushButton("П&ayзa")
self.ЬtnPause.clicked.connect(self.mplPlayer.pause)
self.ЬtnPause.setEnaЫed(False)
hЬox.addWidget(self.ЬtnPause)
self.ЬtnStop = QtWidgets.QPushButton("&Cтon")
self.ЬtnStop.clicked.connect(self.mplPlayer.stop)
self.ЬtnStop.setEnaЫed(False)
hЬox.addWidget(self.ЬtnStop)
vbox.addLayout(hЬox)
# Создаем компоненты для управления громкостью
hЬох = QtWidgets.QHBoxLayout()
lЫVolume = QtWidgets.QLabel("&Гpoмкocть")
hЬox.addWidget(lЫVolume)
self.sldVolume = QtWidgets.QSlider(QtCore.Qt.Orientation.Horizontal)
self.sldVolume.setRange(0, 100)
self.sldVolume.setTickPosition(
QtWidgets.QSlider.TickPosition.TicksAЬove)
self.sldVolume.setTickinterval(l0)
self.sldVolume.setValue(50)
lЫVolume.setBuddy(self.sldVolume)
self.sldVolume.valueChanged.connect(self.setVolume)
hЬox.addWidget(self.sldVolume)
ЬtnМute = QtWidgets.QPushButton("&Tиxo!")
ЬtnМute.setCheckaЫe(True)
ЬtnМute.toggled.connect(self.aOutput.setMuted)
hЬox.addWidget(ЬtnМute)
vbox.addLayout(hЬox)
· self.setLayout(vbox)
self.resize(З00, 100)
case QtMultimedia.QМediaPlayer.MediaStatus.LoadedМedia:
# После загрузки файла подготавливаем проигрыватель
# для его воспроизведения
self.mplPlayer.stop()
self.btnPlay.setEnaЫed(True)
self.bbnPause.setEnaЬled(False)
self.sldPosition.setEnaЫed(True)
self.sldPosition.setMaximum(self.mplPlayer.duration())
case QtMultimedia.QМediaPlayer.MediaStatus.EndOfМedia:
# По окончании воспроизведения файла возвращаем
# проигрыв�тель в изначальное состояние
self.mplPlayer.stop()
self.sldPosition.setValue(0)
self.sldPosition.setEnaЫed(False)
self.btnPlay.setEnaЫed(False)
self.btnPause.setEnaЫed(False)
self.btnStop.setEnaЫed(False)
case QtMultimedia.QМediaPlayer.MediaStatus.NoMedia 1 \
QtMultimedia.QМediaPlayer.MediaStatus.InvalidМedia:
# Если файл не был загружен, отключаем компоненты,
# управлякщие воспроизведением
self.sldPosition.setValue(0)
self.sldPosition.setEnaЫed(False)
self.btnPlay.setEnaЫed(False)
self.btnPause.setEnaЫed(False)
self.btnStop.setEnaЫed(False)
арр = QtWidgets.QApplication(sys.argv)
window = MyWindow()
window.show()
sys.exit(app.exec())
!J] Метаданные х
Г
l ... ����-Qткры! Файл_..._-�-------·J
1
ь
1181c'n1W� ..........�.
from PyQtб import QtCore, QtWidgets, QtMultirnedia
import sys
class MyWindow(QtWidgets.QWidget):
def init (self, parent=None):
QtWidgets.QWidget. init (self, parent,
flags=QtCore.Qt.WindowТype.Window 1 ·
QtCore.Qt.WindowТype.MSWindowsFixedSizeDialogHint)
self.setWindowТitle("Meтaдaнныe")
self.mplPlayer � QtMultimedia.()-lediaPlayer(parent=self)
self.mplPlayer.metaDataChanged.connect(self.showМetadata)
vbox = QtWidgets.QVВoxLayout()
btnOpen = QtWidgets.QPushButton("&Oткpыть файл...")
btnOpen.clicked.connect(self.openFile)
vbox.addWidget(btnOpen)
# Создаем доступную только для чтения область редактирования,
# в которую будет выводиться результат
self.txtOutput = QtWidgets.QТextEdit()
self.txtOutput.setReadOnly(True)
vЬox.addWidget(self.txtOutput)
self.setLayout(vbox)
self.resize(З00, 250)
def openFile(self):
file = QtWidgets.QFileDialog.getOpenFileUrl(parent=self,
сарtiоn="Быберите звуковой файл",
filtеr="Звуковые файлы (*.mрЗ *.асЗ)")
if file[1]:
self.mplPlayer.setSource(file[0])
def showМetadata(self):
# как только метаданные будут получены...
# •••очищаем область редактирования...
self.txtOutput.clear()
# ...извлекаем объект с метаданными...
md = self.mplPlayer.metaData()
s = ""
# ...перебираем их в цикле...
for k in list(QtMultimedia.()-lediaмetaData.Key):
v = md.value(k)
# ...проверяем, действительно ли существует значение
# с таким ключом...
if v:
# ... если значение представляет собой список,
# преобразуем.его в строку...
if v is list:
v = ", ".join(v)
Гnа(!а 29. Мультимедиа ·707
арр = QtWidgets.QApplication(sys.argv)
window = MyWindow()
window.show()
sys.exit(app.exec())
U Видеопроигрыватель· х
,----------·----··--
[ ---,
_ __ _ ______ Qткрыtь файл..._ ___ , ___ _· _ J
l, . :��� ______
' 'j [____ _ J:��----J
' [
[ромкость 1 Jихо!
_J
Рис. 29.3. Видеопроигрыватель
class MyWindow(QtWidgets.QWidget):
def init (self, parent=None):
vbox.addWidget(btnOpen)
vwg = QtМultimediaWidgets.QVideoWidget()
vwg.setAspectRatiat-юde(QtCore.Qt.AspectRatiat-юde.КeepAspectRatio)
self.mplPlayer.setVideoOutput(vwg)
vЬох.addWidget(vwg)
self.sldPosition = QtWidgets.QSlider(QtCore.Qt.Orientation.Horizontal)
self.setLayout(vbox)
self.resize(З00, 300)
Глава 29. Мультимедиа 709
def openFile(self):
file = QtWidgets.QFileDialog.getOpenFileUrl(parent=self,
сарtiоn=11 Выберите
видеофайп••·,
filtе="Видеофайпы (* .avi * .mp4) ")
if file[l]:
self.mplPlayer .setSource(file[0J)
Микрофон ( SGЗЗО)
Головной телефон (RВ-НХ220В Hands-Free AG Audio)
♦ defaultAudioinput() - возвращает устройство ввода звука, помеченное как используе-
мое по умолчанию (объект класса QAudioDevice):
>>> QtMultirnedia.QMediaDevices.defaultAudioinput().description()
'Микрофон (SGЗЗО)'
Назначить у звукового входа другое устройство ввода звука можно вызовом метода
setDevice(<Звуковой вход QAudioDevice>) класса QAudioinput:_
ai = QtMultirnedia.QAudioinput(parent=self)
dlist = QtMultirnedia.QMediaDevices.audioinputs()
ai.setDevice(dlist[l])
Соединить транспорт со звуковым входом можно вызовом метода setAudioinput(<Звуковой
вход QAudioinput>) класса QМediaCaptureSession.
MPEG4, AVI, QuickTime, WеЬМ, Mpeg4Audio, FIAC или UnspecifiedFormat (формат не задан -
значение по умолчанmо).
Второй формат вызова конструктора создает копию указанного объекта.
Класс поддерживает следующие полезные методы (их полный список приведен на странице
https://doc.qt.io/qt-6/qmediaformat.html):
♦ supportedFileFormats(<Операция>) - возвращает список обозначений форматов фай
лов, с которыми можно выполнить заданную операцию. Параметр <Операция> указыва
ется в виде следующего элемента перечисления ConversionМode из класса QmediaFormat:
Encode (кодирование, т. е. запись) или Decode (декодирование, т. е. воспроизведение).
Пример получения форматов файлов, запись в которые поддерживается на компьютере
одного из авторов:
>>> mf = QtMultimedia.QМediaFormat()
.>>> flist = mf.supportedFileFormats(
QtMultimedia.QМediaFormat.ConversionМode.Encode)
>>> for f in flist: pri.nt(f.name, end=" ")
Н264 WМV
self.ЬtnRecord = QtWidgets.QPushButton("&Зaпиcь")
self.ЬtnRecord.clicked.connect(self.ardRecorder.record)
hЬox.addWidget(self.ЬtnRecord)
self.ЬtnPause = QtWidgets.QPushButton("П&ayзa")
self.ЬtnPause.clicked.connect(self.ardRecorder.pause)
self.ЬtnPause.setEnaЫed(False)
hЬox.addWidget(self.ЬtnPause)
self.ЬtnStop = QtWidgets.QPushВutton("&Стоп")
self.ЬtnStop.clicked.connect(self.ardRecorder.stop)
self.ЬtnStop.setEnaЫed(False)
hЬox.addWidget(self.ЬtnStop)
vbox.addLayout(hЬox)
hЬох = QtWidgets.QHBoxLayout()
lЫVolume = QtWidgets.QLabe1("&Ypoвeнь записи")
hЬox.addWidget(lЫVolume)
self.sldVolume = QtWidgets.QSlider(QtCore.Qt.Orientation.Horizontal)
self.sldVolume.setRange(0, 100)
self.sldVolume.setTickPosition(
QtWidgets.QSlider.TickPosition.TicksAЬove)
self.sldVolume.setTickinterval(l0)
self.sldVolume.setValue(l00)
lЫVolume.setBuddy(self.sldVolume)
self.sldVolurne.valueChanged.connect(self.setVolume)
hЬox.addWidget(self.sldVolume)
vbox.addLayout(hЬox)
# Создаем надпись, в которую будет выводиться состояние программы
self.lЫStatus = QtWidgets.QLaЬel("Гoтoвo")
vbox.addWidget(self.lЫStatus)
self._setLayout(vbox)
self.resize(З00, 100)
арр = QtWidgets.QApplication(sys.argv)
window = MyWindow ()
window.show()
sys.exit(app.exec())
SGЗЗО
♦ defaultVideoinput() - возвращает устройство захвата видео, помеченное как исполь
зуемое по умолчанию (объект класса QCarneraDevice):
>>> QtМultimedia.QМediaDevices.defaultVideoinput().description()
'SGЗЗО'
Пример:
dlist = QtMultimedia.QМediaDevices.videoinputs()
carn = QtMultimedia.QCarnera(dlist[l])
Назначить у камеры другое устройство захвата видео можно вызовом метода
setCarneraDevice(<Камера QCarneraDevice>) класса QCarnera:
carn = QtMultimedia.QCarnera(parent=self)
dlist = QtMultimedia.QMediaDevices.videoinputs()
carn.setCarneraDevice(dlist[l])
Класс QCarneraDevice представляет доступное на текущей шштформе устройство для захвата
видео. Метод description() этого класса возвращает строковое название текущего устрой
ства. Полное описание класса QCarneraDevice приведено на странице https://doc.qt.io/qt-6/
qcameradevice.html.
Класс QCarnera поддерживает много методов, позволяющих, в частности, узнать характери
стики устройства захвата видео и указать некоторые его параметры, и ряд сигналов. Полное
описание этого класса приведено на странице https://doc.qt.io/qt-6/qcamera.html.
Соединить транспорт с камерой можно вызовом мето�а setCarnera (<Камера QCarnera>) клас
са QМediaCaptureSession.
После присоединения камеры к транспорту необходимо запустить захват видео этой каме
рой, вызвав метод start() класса QCarnera.
Напишем простую программу для записи видео
(листинг 29.5), которая станет сохранять записан- iil Запись видео Х
Записано 7 .секунд
Рис. 29.5. Программа для записи видео
718 Часть 11. Библиотека PyQt 6
class MyWindow(QtWidgets.QWidget):
def init (self, parent=None):
QtWidgets.QWidget. init (self, parent,
flags=QtCore.Qt.WindowType.Window
QtCore.Qt.WindowType.MSWindowsFixedSizeDialogHint)
self.setWindowТitle("Зaпиcь видео")
# Создаем транспорт, звуковой вход, камеру и кодировщик
mcs = QtMultimedia.QMediaCaptureSession(parent=self)
self.ainput = QtMultimedia.QAudioinput(parent=self)
self.ainput.setVolume(l.0)
mcs.setAudioinput(self.ainput)
cam = QtMultimedia.QCamera(parent=self)
mcs.setCamera(cam)
self.ardRecorder = QtMultimedia.QMediaRecorder(parent=self)
mcs.setRecorder(self.ardRecorder)
# Запускаем захват видео созданной ранее камерой
cam.start()
# Видео будет сохраняться в файле movie.rnp4, находящемся
# в той же папке, где хранится программа
fn = QtCore.QUrl.fromLocalFile(os.path.abspath("movie.rnp4"))
self.ardRecorder.setOutputLocation(fn)
mf = QtMultimedia.QМediaFormat(
QtMultimedia.QMediaFormat.FileFormat.MPEG4)
mf.setAudioCodec(QtMultimedia.QMediaFormat.AudioCodec.AAC)
mf.setVideoCodec(QtMultimedia.QMediaFormat.VideoCodec.H264)
self.ardRecorder.setMediaFormat(mf)
# Задаем параметры кодирования звука и видео
self.ardRecorder.setQuality(
QtMultimedia.QMediaRecorder.Quality.LowQuality)
self.ardRecorder.setEncodingMode(
QtMultimedia.QMediaRecorder.EncodingMode.ConstantQualityEncoding)
self.ardRecorder.setAudioChannelCount(l)
self.ardRecorder.setAudioSarnpleRate(-1)
self.ardRecorder.setVideoResolution(QtCore.QSize())
self.ardRecorder.recorderStateChanged.connect(self.initRecorder)
self.ardRecorder.durationChanged.connect(self.showDuration)
vbox = QtWidgets.QVВoxLayout()
# Создаем контрольную видеопанель и связываем ее с транспортом
vwg = QtMultimediaWidgets.QVideoWidget()
vwg.setAspectRatioMode(QtCore.Qt.AspectRatioMode.KeepAspectRatio)
mcs.setVideoOutput(vwg)
vbox.addWidget(vwg, stretch=l)
# Создаем компоненты для запуска, приост�новки и остановки
# записи видео
hЬох = QtWidgets.QHBoxLayout()
self.ЬtnRecord = QtWidgets.QPushButton("&Зaпиcь")
Глава 29. Мультимедиа 719
self.btnRecord.clicked.connect(self.ardRecorder.record)
hЬox.addWidget(self.btnRecord)
self.btnPause = QtWidgets.QPushButton("П&ауза")
self.btnPause.clicked.connect(self.ardRecorder.pause)
self.btnPause.setEnaЫed(False)
hЬox.addWidget(self.btnPause)
se1f.ЬtnStop = QtWidgets.QPushВutton("&Cтoп")
self.ЬtnStop.clicked.connect(self.ardRecorder.stop)
self.ЬtnStop.setEnaЫed(False)
hЬox.addWidget(self.ЬtnStop)
vbox.addLayout(hЬox)
vbox.addLayout(hЬox)
# Создаем надпись, в которую будет выводиться состояние программы
self.lЫStatus = QtWidgets.QLabel("Гoтoвo")
vbox.addWidget(self.lЫStatus)
self.setLayout(vbox)
self.resize(З00, 280)
арр = QtWidgets.QApplication(sys.argv)
window = MyWindow()
720 Часть 11. Библиотека PyQt 6
window.show()
sys.exit(app .exec())
♦ error {) - возвращает обозначение возникшей при захвате кадра ошибки в виде одного
из следующих элементов перечисления Error из класса QimageCapture:
• NoError - ошибки не возникло;
• NotReadyError - кодировщик не готов к работе, поскольку уже выполняет захват
и обработку статичного кадра;
• ResourceError - устройство захвата видео отсутствует или недоступно;
• FormatError - заданный формат файла не поддерживается на текущей платформе;
• Out0fSpaceError - не хватает места на диске для сохранения результирующего файла;
• NotSupportedFeatureError - выбранное устройство захвата видео не поддерживает
захват статичных изображений;
♦ errorstring {) - возвращает строковое описание возникшей ошибки.
Класс QimageCapture поддерживает следующие полезные сигналы (их полный список при
веден на странице https://doc.qt.io/qt-6/qimagecapture.html):
♦ imageCaptured {<Идентификатор>, <Изображение Qimage>) - генерируется сразу после
захвата статичного изображения и перед его сохранением в файле (если был выполнен
вызовом метода captureToFile() ). Через параметры передаются целочисленный иден
тификатор захваченного изображения и само это изображение;
♦ imаgеSаvеd{<Идентификатор>, <Путь к файлу>) - генерируется сразу после сохране�
захваченного кадра в файле. Через параметры передаются целочисленный идентифика
тор захваченного изображения и путь к созданному файлу;
♦ readyForCaptureChanged(<Флаг>) - генерируется при изменении состояния_ готовности
кодировщика к захвату кадра. В параметре передается значение тrue, если кодировщик
готов к захвату кадра, и False - если не готов;
♦ erroroccured {<Ошибка Error>, <Описание>) - генерируется при возникновении ошиб-
ки. В параметрах передаются обозначение возникшей ошибки и ее строковое описание.
Присоединить кодировщик статичных изображений к транспорту можно вызовом метода
setimageCapture(<Кодировщик QimageCapture>) класса QМediaCaptureSession.
В листинге 29.6 приведен код программы-«фотоаппараrа», которая захватывает статичные
кадры и сохраняет их в файлах формата JPEG, чьи имена представляют собой последова
тельно увеличивающиеся целые числа.
29.6. niюrpa
class MyWindow(QtWidgets.QWidget):
def init (self, parent=None)·:
QtWidgets.QWidget. init (self, parent,
flags=QtCore .Qt.WindowТype.Window
QtCore.Qt.WindowТype.MSWindowsFixedSizeDialogHint)
# Создаем счетчик захваченных изображений
self.counter = О
self.setWindowТitle("Зaxвaт фото")
722 Часть 11. Библиотека PyQt б
def captureimage(self):
self.counter += 1
self.icCapture.captureToFile(
os.path.abspath(str (self.counter) + ".jpg"))
арр = QtWidgets.QApplication(sys.argv)
window = MyWindow()
window.show()
sys.exit(app.exec())
Первый формат соединяет создаваемый эффект с устройством для вывода звука, исполь
зуемым по умолчанию. Второй формат позволяет явно указать используемое устройство
вывода звука.
В параметре parent может быть указана ссьmка на родительский компонент.
Класс QSoundEffect поддерживает следующие основные методы (полный их список можно
найти на странице https://doc.qt.io/qt-6/qsoundeffect.html):
♦ setsource(<Путь QUrl>) -задает файл-источник воспроизводимого звука;
♦ setVolume (<Громкость>) -задает громкость воспроизводимого звука в виде веществен
ного числа от о.о (звук отключен) до 1.о (максимальная громкость - значение по
умолчанию);
♦ setLoopCount(<Количество>) - задает количество повторений звука при воспроизведе
нии. Значение о или 1 предписывает воспроизвести звук однократно, а значение -2 -
воспроизводить его бесконечное количество раз;
♦ setMuted(<Флаг>) - если передать значение True, звук при воспроизведении будет от
ключен. Значение False вновь включает звук;
♦ setAudioDevice (<Устройство вывода звука QAudioDevice>) - связывает текущий эф-
фект с заданным устройсuюм вывода звука;
♦ play () - запускает воспроизведение звука. Метод является слотом;
♦ stop() - останавливает воспроизведение звука. Метод является слотом;
♦ isLoaded() -возвращает True, если звук загружен из указанного в методе setsource()
файла, и False - в противном случае;
♦ isPlaying() -возвращает True, если звук в текущий момент воспроизводится, и False -
в противном случае;
♦ loopsRernaining() - возвращает количество оставшихся повторов воспроизведения зву
ка или значение -2, если было задано бесконечное воспроизведение;
♦ volume() - возвращает текущее значение громкости звука в виде вещественного числа
от о.о до 1.0;
♦ isMuted() - возвращает True, если воспроизводящийся звук в текущий момент отклю
чен, и False - в противном случае;
♦ status() -возвращает текущее состояние звука в виде значения одного из следующих
элементов перечисления Status из класса QSoundEffect:
• Null - источник звука не задан;
• Loading - идет загрузка звука из файла-источника;
• Ready -звук загружен и готов к воспроизведенmо;
• Error -в процессе загрузки звука возникла ошибка;
♦ supportedМirneTypes() - статический, возвращает список МIМЕ-типов поддерживаемых
форматов звука:
>>> for f in QtMultirnedia .QSoundEffect .supportedМirneTypes():
print(f, end=" ")
cla55 MyWindow(QtWidget5.QWidget):
def init (5elf, parent=None):
QtWidget5.QWidget. init (5elf, parent,
flag5=QtCore.Qt.WindowТype.Window
QtCore._Qt.WindowТype.МSWindow5FixedSizeDialogHint)
5elf.5etWindowТitle("Звyкoвыe эффекты")
# Инициализируем подсистему вывода звуковых эффектов
5elf.5ndEffect = QtMultimedia.QSoundEffect()
# Задаем файл-источник
fn = QtCore.QUrl. fromLocalFile(05 .path.aЬ5path("effect.wav"))
5elf.5ndEffect.5etSource(fn)
5elf.5ndEffect.loop5RemainingChanged.coппect(5elf.5hoWCount)
5elf.5ndEffect.playingChanged.coппect(self.clearCount)
vbox = QtWidgets.QVВoxLayout()
# Создаем кнопки для заГ!Уска воспроизведения звука
lЫPlay = QtWidgets.QLaЬel("Bocпpoизвecти...")
vbox.addWidget(lЫPlay)
btnOnce = QtWidget5.QPu5hВutton("...&oдин раз")
btn0nce.clicked.coппect(5elf.play0nce)
vbox.addWidget(btnOnce)
btnTen = QtWidget5.QPu5hВutton("...&дecять раз")
btnTen.clicked.coппect(self.playТen)
vbox.addWidget(btnTen)'
btnihfinite = QtWidget5.QPu5hButton(
"...&бесконечное количество раз")
btninfinite.clicked.coппect(5elf.playinfinite)
vbox.addWidget(btninfinite)
ЬtnStop = QtWidgets.QPu5hВutton("&Cтoп")
Глава 29. Мультимедиа 725
btnStop.clicked.connect(self.sndEffect.stop)
vbox.addWidget(btnStop)
self.lЫStatus = QtWidgets.QLaЬel("")
vbox.addWidget(self.lЫStatus)
self.setLayout(vbox)
self.resize(З00, 100)
def playOnce(self):
self.sndEffect.setLoopCount(l)
self.sndEffect.play()
def playТen(self):
self.sndEffect.setLoopCount(l0)
self.sndEffect.play()
def playinfinite(self):
self.sndEffect.setLoopCount(-2)
self.sndEffect.play()
арр = QtWidget's.QApplication(sys.argv)
window = MyWindow()
window.show()
sys.exit(app.exec())
ПРИМЕЧАНИЕ
Помимо описанных здесь возможностей, PyQt поддерживает доступ к устройствам воспро
изведения звука на низком уровне, а также получение непосредственно массива звуковых
и видеоданных с целью их анализа и обработки. Соответствующие программные инстру
менты описаны в документации по этой библиотеке.
ГЛАВА зо·
Печать документов
600
ВНИМАНИЕ!
Для некоторых принтеров метод supportResolutions() по какой-то причине выводит пустой
список.
В листинге 30.2 приведен код класса PrintList, реализующий печать списков или содержи
мого таблиц баз данных в виде полноценного табличного отчета. Этот класс можно исполь
зовать для разработки бизнес-приложений.
class PrintList:
def init (self):
self.printer = QtPrintSupport.QPrinter()
self.headerFont = QtGui.QFont("Arial", pointSize=lO,
weight=QtGui.QFont.Weight.Bold)
self.bodyFont = QtGui.QFont("Arial",-pointSize=lO)
self.footerFont = QtGui.QFont("Arial"; pointSize=9, italic = True)
self.headerFlags = QtCore.Qt.AlignmentFlag.AlignнCenter 1 \
QtCore.Qt.TextFlag.TextWordWrap
self.bodyFlags = QtCore.Qt.TextFlag.TextWordWrap
self.footerFlags = QtCore.Qt.AlignmentFlag.AlignнCenter 1 \
QtCore.Qt.TextFlag.TextWordWrap
color = QtGui.QColor(QtCore.Qt.GlobalColor.Ыack)
self.headerPen = QtGui.QPen(color, 2)
self.bodyPen = QtGui.QPen(color, 1)
self.margin = 5
self._resetData()
def _resetData(self):
self.headers = None
self.columnWidths = None
self.data = None
self._brush = QtCore.Qt.BrushStyle.NoBrush
self._currentRowHeight = О
self._currentPageHeight О
self._headerRowHeight = О
self._footerRowHeight = О
self._currentPageNumЬer 1
self._painter = None
def printData(self):
self._painter =.QtGui.QPainter()
self._painter.begin(self.printer)
self._painter.setBrush(self._brush)
if self._headerRowHeight == О:
self._painter.setFont(self.headerFont)
self._headerRowHeight = self._calculateRowHeight(
self.columnWidths, self.headers)
if self._footerRowHeight == О:
self._painter.setFont(self.footerFont)
self._footerRowHeight = self._calculateRowHeight(
[ self.printer.width() ] , "Страница")
Глава 30. Печать документов 733
for i in range(len(self.data)):
height = self._calculateRowHeight(self.columnWidths, self.data[i])
if self._currentPageHeight + height > self.printer.height() - \
self._footerRowHeight - 2 * self.margin:
self._printFooterRow()
self._current.PageHeight = О
self._currentPageNumЬer += 1
self.printer.newPage()
if self._currentPageHeight == О:
self._painter.setPen(self.headerPen)
self._painter.setFont(self.headerFont)
self.printRow(self.columnWidths, self.headers,
self._headerRowHei�ht, self.headerFlags)
self._painter.setPen(self.bodyPen)
self._painter.setFont(self.bodyFont)
self.printRow(self.columnWidths, self.data[i], height,
self.bodyFlags}
self. _printFooterRow(}
self._pai!'lter.end()
self._resetData()
def _printFooterRow(self):
self._painter.setFont(self.footerFont)
self._painter.drawТext(self.margin, self.printer.height() -
self._footerRowHeight - self.margin, self.printer.width() -
2 * self.margin, self._footerRowHeight - 2 * self.margin,
self.footerFlags, "Страница " + str(self._currentPageNumЬer))
734 Часть 11. Библиотека PyQt 6
Пользоваться этим классом очень просто. Сначала нужно создать его объект, вызвав конст
руктор следующего формата:
PrintList ()
После чего задать параметры печатаемого табличного отчета, воспользовавшись следую
щими атрибутами класса PrintList:
♦ headers- заголовки для столбцов табличного отчета в виде списка строк;
♦ columnWidths - значения ширинъ1 для столбцов, измеряемые в пикселах, в виде списка
целочисленных величин;
♦ data - собственно выводимые даннъ1е. Они должны представлять собой �писок значе
ний, выводимых в отдельных ячейках, - каждый из элементов этого списка задает дан
ные для одной строки.
Следующие свойства являются необязательными для указания:
♦ printer - принтер (ссьшка на объект класса QPrinter), на котором будет печататься
отчет. Изначально хранит принтер, используемый по умолчанию;
♦ headerFont - шрифт, используемый для вывода текста «шапки» табличного отчета. По
умолчанию- полужирный шрифт Arial размером 10 пунктов;
♦ headerPen - параметры рамки, рисуемой вокруг ячеек «шапки». По умолчанию - чер
ная линия толщиной 2 пиксела;
♦ headerFlags - параметры вывода текста ячеек «шапки». По умолчанию - выравнива
ние по центру и перенос по словам;
♦ bodyFont - шрифт, используемый для вывода текста обычных строк. По умолчанию -
шрифт Arial размером 1О пунктов;
♦ bodyPen - параметры рамки, рисуемой вокруг ячеек обычных строк. По умолчанию -
черная линия толщиной 1 пиксел;
♦ bodyFlags - параметры вьmода текста ячеек обычных строк. По умолчанию - перенос
по словам;
♦ footerFont - шрифт, используемый для вывода текста «поддона» таблицы. По умолча
нию - курсивный шрифт Arial размером 9 пунктов;
♦ footerFlags - параметры вывода текста «поддона». По умолчанию - выравнивание по
центру и перенос по словам;
♦ margin - величина просвета между рамкой ячейки и ее содержимым. По умолчанию -
5 пикселов.
После задания всех необходимых параметров следует вызвать метод printData () класса
PrintList•, который и выполняет печать данных. Впоследствии, пользуясь тем же объектом
этого класса, мы можем распечатать другой набор данных.
В листинге 30.3 приведен код тестовой программы, вьmодящей на экран числа от 1 до 100,
их квадраты и кубы. Подразумевается, что код, приведенный в листинге 30.2, сохранен
в модуле Printlist.py.
Наиболее полезные методы класса QPageLayout приведены далее (полный их список можно
найти на странице https://doc.qt.io/qt-6/qpagelayout.html).
♦ setPageSize(<Размер QPageSize> [, minМargins= QMarginsF (О, О, О, О)]) - задает раз
меры бумаги. В необязательном параметре minМargins можно указать минимальные ве
личины отступов от краев страницы (объект класса QMarginsF);
♦ setOrientation (<Ориентация Orientation>) - задает ориентацию страницы;
♦ setMargins(<Отступы QMarginsF>) - задает величины отступов от краев страницы. Воз
вращает тrue, если операция прошла успешно, и False - в противном случае;
♦ setLeftMargin(<Oтcтyп>), setTopMargin(<Oтcтyп>), setRightMargin(<Oтcтyп>) и
setBottomМargin(<Отступ>) - задают величины отступов от левого, верхнего, правого
и нижнего краев страницы соответственно. _Возвращают тrue, если операция прошла
успешно, и False - в противном случае;
♦ setUnits (<Единица измерения Unit>) - задает единицу измерения размеров;
♦ setMinimumМargins(<Отступы QMarginsF>) - задает минимальные величины отступов от
краев страницы, которые может обеспечить принтер;
♦ swap(<Параметры QPageLayout>) - меняет параметры бумаги, хранящиеся в текущем
объекте класса, на заданные;
♦ fullRect( [<Единица измерения Unit> J ) - возвращает размеры страницы с учетом ори
ентации, но без учета отступов в виде объекта класса QRectF. Если единица измерения не
указана, выведенные значения будут исчисляться в единицах измерения, заданных
в конструкторе. Пример:
>>> lt = QtGui.QPageLayout(QtGui.QPageSize(QtGui.QPageSize.PageSizeid.AS),
QtGui.QPageLayout.Orientation.Landscape,
QtCore.QMarginsF(10, 10, 10, 10),
units=QtGui.QPageLayout.Unit.Millimeter)
>>> r = lt.fullRect()
>>> print(r.width(), "х", r.height())
210.0 х 148.0
♦ fullRectPixels(<Разрешение>) - возвращает размеры страницы в пикселах с учетом
ориентации, но без учета отступов для заданного разрешения, которое измеряется в точ
ках на дюйм. Результат представляет собой объект класса QRect;
♦ fullRectPoints() - возвращает размеры страницы в пунктах с учетом ориентации, но
без учета отступов в виде объекта класса QRect;
♦ paintRect( [<Единица измерения Unit> J ) - возвращает размеры страницы с учетом ори
ентации и отступов (фактически - доступной для вывода графики области страницы)
в виде объекта класса QRectF. Если единица измерения не указана, выведенные значения
будут исчисляться в единицах измерения, заданных в конструкторе;
♦ paintRectPixels(<Разрешение>) - возвращает размеры страницы в пикселах с учетом
ориентации и отступов для заданного разрешения, которое измеряется в точках на дюйм.
Результат представляет собой объект класса QRect;
♦ paintRectPoints() - возвращает размеры страницы в пунктах с учетом ориентации и
отступов в виде объекта класса QRect.
Класс QPageLayout также поддерживает операторы сравнения = = и ! =.
Глава 30. Печать документов 739
.,,;J Печать х
Общие
Выберите принтер
�-C_a_no_ n
_ _iP_2_800_ser_i
es
______ �I•
Microsoft XPS Document \
� Fax 8 OneNote for Windows 1 О
8 Microsoft Print to PDF 8 PDFCrea tor
< >
Состояние: Отключен О П�ать в файл ��-=·
i Н,IСТройк а ._.:_��,i
>. ___
Папка:
Коммент арий: f!:\аllти пр интер .1
Диаnаэон страниц
· ,Iекущая • Число 11опий:
@Все · страница
0fаэобрать по копиям
· _ , В!а!деление
тр н
О с а ицы: �10-_1___�
Введите номер.страницы или диапазон
страниц. Например: 5-12
Все настройки, заданные в диалоговом окне, будут применены к принтеру самой библиоте
кой PyQt. Так, если пользователь выберет другой принтер, печать будет выполнена на
выбранном им принтере. А если он укажет напечатать документ в нескольких копиях, все
эти копии будут напечатаны PyQt самостоятельно.
Класс QPrintDialog поддерживает следующие основные методы (полный их список можно
найти на страницах https://doc.qt.io/qt-6/qabstractprintdialog.html и https://doc.qt.io/qt-6/
qprintdialog.html):
♦ setOption(<Hacтpoйкa>[, on=True]) - активизирует указанную в первом параметре
настройку принтера, если в параметре on задано значение True, и сбрасывает, если пере
дано False. Настройка задается в виде одного из следующих элементов перечисления
PrintDialogOption из класса QPrintDialog или их комбинации через оператор 1:
• None - все настройки принтера сброшены;
• PrintToFile - доступна возможность печати в файл;
• PrintSelection - разрешен выбор принтера;
• PrintPageRange - доступно указание диапазона печатаемых страниц;
• PrintShowPageSize - вьmод размера страницы и отступов (только если это возможно);
• PrintCollateCopies - доступно указание режима печати копий документов (будет
ли каждая копия печататься полностью, или уначала будут отпечатаны все копии
первой страницы, потом - копии второй и т. д.);
• PrintCurrentPage __: доступно указание печати только текущей страницы;
♦ setOptions(<Настройки PrintDialogOptions>) - позволяет активизировать сразу не
сколько настроек принтера;
♦ testOption(<Настройка PrintDialogOptions>) - возвращает True, если заданная на
стройка принтера активизирована, и False - в противном случае;
♦ options() - возвращает через оператор I комбинацию элементов перечисления
PrintDialogOption, представляющих активизированные настройки;
♦ printer() - возвращает принтер (объект класса QPrinter), выбранный в диалоговом
окне.
Параметры страницы х
Бумага
Ра;}мер:
Пода�а:
П я(м
Ориентация ол м}
@Книжн я
а левое: г·-----,
��j правое �:=J
:
л мная
Qt, ьбо
j!ерхнее: ��96 ] нижнее: l�
, ОК Отмена
.
Принципы работы с диалоговым окном параметров страницы такие же, как и у его «колле
гю>, представляющего параметры принтера (см.разд. 30.2.1): мы задаем нужные параметры
страницы, вызывая соответствующие методы класса QPrinter, и выводим диалоговое окно
на экран вызовом метода ехес (). И, опять же, все зада�rnые в этом диалоговом окне на
стройки будут применены к принтеру автоматически.
Из поддерживаемых классом QPagesetupDialog методов нас может заинтересовать разве что
Он возвращает принтер (объект класса QPrinter), указанный в вызове конструк
printer ().
тора.
Напишем печатающую изображение из указанного графического файла утилиту, -позво
ляющую задать настройки принтера и параметры страницы в соответствующих диалоговых
окнах (листинг 30.4).
class MyWindow(QtWidgets.QWidget):
def �init (self, parent=None):
QtWidgets.QWidget. init (self, parent,
flags=QtCore.Qt.WindowТype.Window
QtCore.Qt.WindowТype.MSWindowsFixedSizeDialogHint)
self.setWindowТitle("Пeчaть изображений")
self.printer = QtPrintSupport.QPrinter()
742 Часть 11. Библиотека PyQt б
self.printer.setPageOrientation(
QtGui.QPageLayout.0rientation.Landscape)
self.file = None
vbox = QtWidgets.QVВoxLayout()
ЬtnOpen = QtWidgets.QPushButton("&Oткpыть файл...")
ЬtnOpen.clicked.connect(self.openFile)
vbox.addWidget(ЬtnOpen)
ЬtnPageOptions = QtWidgets.QPushButton("Настройка &страницы...")
ЬtnPageOptions.clicked.connect(self.showPageOptions)
vbox.addWidget(ЬtnPageOptions)
ЬtnPrint = QtWidgets.QPushButton("&Пeчaть ...")
ЬtnPrint.clicked.connect(self.print)
vbox.addWidget(ЬtnPrint)
self.setLayout(vbox)
self.resize(З00, 100)
def openFile(self):
self.file = QtWidgets.QFileDialog.getOpenFileNarne(parent=self,
сарtiоn="Выберите графический файл",
filtеr="Графические файлы (*.bmp *.jpg *.png)") [О]
def showPageOptions(self):
pd = QtPrintSupport.QPageSetupDialog(self.printer, parent=self)
pd.exec()
def print(s�lf):
pd = QtPrintSupport.QPrintDialog(self.printer, parent=self)
pd.setOptions(
QtPrintSupport.QAЬstractPrintDialog.PrintDialogOption.PrintToFile
QtPrintSupport.QAЬstractPrintDialog.PrintDialogOption.PrintSelection)
if pd.exec() == QtWidgets.QDialog.DialogCode.Accepted:
painter = _QtGui.QPainter()
painter.begin(self.printer)
pixmap = QtGui.QPixmap(self.file)
pixmap = pixmap.scaled(self.printer.width(), self.printer.height(),
aspectRatioMode=QtCore.Qt.AspectRatioMode.KeepAspectRatio)
painter.drawPixmap(0, О, pixmap)
painter.end()
арр = QtWidgets.QApplication(sys.argv)
window = MyWindow()
window.show()
sys.exit(app.exec())
бирования выведенного документа, указания режима его вывода (постранично, по две стра
ницы и т. п.), задания ориентации страницы, вызова диалогового окна настроек страницы, а
также отправки документа на печать.
class MyWindow(QtWidgets.QWidget):
def init (self, parent=None):
QtWidgets.QWidget. init (self, parent,
flags=QtCore.Qt.WindowТype.Window
QtCore.Qt.WindowТype.MSWindowsFixedSizeDialogHint)
self.setWindowТitle("Пeчaть изображений")
self.printer = QtPrintSupport.QPrinter'()
self.printer.setPageOrientation(
QtGui.QPageLayout.Orientation.Landscape)
self.file = None
vbox = QtWidgets.QVВoxLayout()
btnOpen = QtWidgets.QPushВutton("&Oткpыть файл ...")
btnOpen.clicked.connect(seif.openFile)
vbox.addWidget(btnOpen)
btnPageOptions = QtWidgets.QPushButton("Hacтpoйкa &страницы...")
btnPageOptions.clicked.connect(self.showPageOptions)
vbox.addWidget(btnPageOptions)
btnPreview = QtWidgets.QPushВutton("П&pocмoтp...")
btnPreview.clicked.connect(self.preview)
vbox.addWidget(btnPreview)
btnPrint = QtWidgets.QPushButton("&Пeчaть ...")
btnPrint.clicked.connect(self.print)
vbox.addWidget(btnPrint)
self.setLayout(vbox)
self.resizeГЗ00, 100)
def openFile(self):
self.file = QtWidgets.QFileDialog.getOpenFileName(parent=self,
сарtiоn="Выберите графический файл",
filtеr="Графические файлы (*.bmp *.jpg *.png)") [О]
def showPageOptions(self):
pd = QtPrintSupport.QPageSetupDialog(self.printer, parent=self)
pd.exec()
def preview(self):
рр = QtPrintSupport.QPrintPreviewDialog(self.printer, parent=self)
pp.paintRequested.connect(self.__printimage)
рр.ехес()
def print(self):
pd = QtPrintSupport.QPrintDialog(self.printer, parent=self)
pd.setOptions(
QtPrintSupport.QAЬstractPrintDialog.PrintDialogOption.PrintToFile
QtPrintSupport.QAЬstractPrintDialog.PrintDialogOption.PrintSelection)
Глава 30. Печать документов 745
if pd.exec() == QtWidgets.QDialog.DialogCode.Accepted:
self._printimage(self.printer)
арр = QtWidgets.QApplication(sys.argv)
window = MyWindow()
window.show ()
sys.exit(app.exec())
>�>.print(QtPrintSupport.QPrinterinfo.defaultPrinterName() )
Canon iP2800 series
748 Часть 11. Библиотека PyQt б
Сч,-�.тываем настройки
123 1 Python I PyQtб.QtCore.QSize(640, 480)
Значение 4 в хранилище отсутствует
Очищаем хранилище
Теперь прочитаем из реестра путь к системному каталогу Документы, для чего воспользу
емся пятым форматом конструктора класса QSettings (см.разд. 31. 1):
>» settings = QtCore.QSettings("HКEY_CURRENT_USER\\Software\\" +
"Microsoft\\Windows\\CurrentVersion\\" +
"Explorer\\Shell Folders",
QtCore.QSettings.Format.NativeFormat)
>>> settings.value("Personal")
'D:\\Dаtа\\Документы'
class MyWindow(QtWidgets.QWidget):
def init (self, parent=None):
QtWidgets.QWidget. init (self, parent,
flags=QtCore.Qt.WindowТype.Window)
self.setWindowТitle("Иcпoльзoвaниe ключей")
self.settings = QtCore.QSettings("Пpoxopeнoк и Дронов",
"Использование ключей")
vbox = QtWidgets.QVВoxLayout()
self.txtLine = QtWidgets.QLineEdit(parent=self)
vbox.addWidget(self.txtLine)
btnSave = QtWidgets.QPushВutton("&Coxpaнить текст")
btnSave.clicked.connect(self.saveText)
vbox.addWidget(btnSave)
self.setLayout(vbox)
if self.settings.contains("Oкнo/Mecтoпoлoжeниe"):
self.setGeometry(self.settings.value("Oкнo/Mecтoпoлoжeниe") )
Глава 31. Сохранение настроек программ 757
else:
self.resize(200, 50)
if self.settings.contains("Дaнныe/Teкcт"):
self.txtLine.setText(self.settings.value("Дaнныe/Teкcт"))
def saveтext(self):
self.settings.beginGroup.("Данные")
self.settings.setValue("Teкcт", self.txtLine.text())
self.settings.endGroup()
арр = QtWidgets.QApplication(sys.argv)
window = MyWindow()
window.show()
sys.exit(app.exec())
Программа «Судоку»
5 4 7 8 6 3 9 2 1
3 8 1 2 9 7 6 5 4
2 9 6 5 1 4 8 3 7
6 2 5 9 3 1 4 7 8
7 3 8 4 2 б 1 9 5
4 1 9 7 5 8 3 б 2
9 7 3 1 8 2 5 4 6
1 6 2 3 4 5 7 8 9
8 5 4 6 7 9 2 1 3
Рис. 32.1. Решенная судаку (группы выделены утолщенными рамками)
762 Часть 11. Библиотека PyQt б
Как правило, судоку решают вдвоем: один игрок произвольно расставляет цифры в некото
рых ячейках поля, а второй, собственно, решает головоломку.
fШ Судаку 2.0.0 Х
�айл [lравка �правка
2 1
{ ,. ""
4 5
,,
7 б
8
'
4
Чтобь1 случайно не занести в какую-либо ячейку другую цифру, есть возможность заблоки
ровать ее. Для этого следует сделать нужную ячейку активной и нажать клавишу <F2>.
В заблокированной ячейке цифра выводится красным шрифтом (на рис. 32.2 заблокирована
ячейка с цифрой 5). Отметим, что блокировать можно только ячейки, содержащие цифры.
Снять блокировку с ячейки можно, сделав ее активной и нажав клавишу <F4>.
Главное меню программы содержит следующие пункты:
♦ меmоФайл:
• Новый (с ним связана комбинация клавиш <Ctrl>+<N>)- очистка поля;
• Открыть (<Ctrl>+<O>)- загрузка сохраненной ранее головоломки из выбранного
пользователем файла;
• Сохранить (<Ctrl>+<S>)- сохранение головоломки. в файле с указанным пользова
телем именем.
При выборе этого пункта производится сохранение в полном формате - т. е. для
каждой ячейки, помимо находящейся в ней цифры, сохраняется признак того, забло
кирована ли она;
• Сохранить компактно- сохранение головоломки в файле в компактном формате.
Компактный формат не предусматривает хранения признака блокировки ячейки.
Вследствие этого файл, сохраненный в компактном формате, вдвое меньше полно
форматного.
При открытии файла, сохраненного в компактном формате, производится автомати
ческая блокировка всех ячеек, содержащих цифры;
• Печать (<Ctrl>+<P>) - вывод головоломки на печать;
• Предварительный просмотр- просмотр головоломки в том виде, в котором она
будет выведена на печать;
• Параметры страницы - настройка параметров печатаемой страницы;
• Выход (<Ctrl>+<Q>)- завершение работы программы;
♦ меmо Правка:
• Копировать (<Ctrl>+<C>)- копирование головоломки в буфер обмена в полном
формате (аналогичном тому, в котором сохраняется файл при выборе пункта Сохра
. нить меmо Файл);
• Копировать компактно - копирование головоломки в буфер обмена в компактном
формате (аналогичном тому, в котором сохраняется файл при выборе пункта Сохра
нить компактно меmо Файл);
• Копировать для Excel- копирование головоломки в буфер обмена в формате,
предназначенном для вставки в таблицу Microsoft Ехсе\;
• Вставить (<Ctrl>+<V>)- вставка головоломки, скопированной ранее в любом фор
мате.
Ес11и головоломка была скопирована в компактном формате, все ячейки, содержащие
цифры, будут автоматически заблокированы;
• Вставить из Excel- вставка из буфера обмена головоломки, скопированной из таб
лицы Microsoft Ехсе\;
• Блокировать (<F2>)- блокировка активной ячейки;
764 Часть 11. Библиотека PyQt б
• Блокировать все (<FЗ>)- блокировка всех ячеек. Блокируются только ячейки, со
держащие цифры;
• Разблокировать (<F4>)- разблокировка активной ячейки;
• Разблокировать все (<F5>)- разблокировка всех ячеек;
♦ меюо Справка:
• О программе - вывод окна со сведениями о программе «Судоку»;
• О Qt - вывод окна со сведениями о фреймворке Qt.
При наведении курсора мыши на любой пункт меюо в строке состояния появляется его раз
вернутое описание.
В панели J:fНСтрументов присутствуют кнопки (в порядке слева направо): Новый, Открыть,
Сохранить, Печать, Предварительный просмотр, Копировать, Вставить, Блокировать
все и Разблокировать все. Они выполняют те же действия, что и одноименные пункты
меюо. При наведении курсора мыши на кнопку панели инструментов в строке состояния
появляется ее развернутое описание, а рядом с кнопкой спустя небольшой промежуток
времени появляется всплывающая подсказка (на рис. 32.2 курсор мыши наведен на кнопку
Сохранить).
Сохранение головоломок выполняется в текстовых файлах с расширением svd. Форматы,
в которых хранятся головоломки, будут описаны позже..
Окно программы при закрытии сохраняет свое местоположение и впоследствии, после сле
дующего запуска, восстанавливает его.
ячейка должна иметь возможность принимать фоновый цвет- ведь именно разными цве
тами фона мы будем выделять группы ячеек на поле.
Визуально ячейка должна иметь размеры 30 х 30 пикселов и выводить цифру, выровненную
по середине без отступов от границ компонента.
Большую часть необходимой функциональности мы можем получить, просто сделав класс
MyLabel производным от класса надписи QLabel. Тогда, чтобы вывести на экран цифру,
можно просто задать ее в качестве содержимого надписи, воспользовавшись унаследован
ным методом setтext (), - также несложно будет указать необходимые размеры и вырав
нивание. А задать для текста и фона нужные цвета мы сможем, привязав к ячейке таблицу
стилей, для чего воспользуемся методом setstyleSheet (), опять же, унаследованным.
Теперь подумаем, какие атрибуты должен поддерживать класс MyLabel:
♦ colorYellow, colorOrange, colorGrey, colorBlack и colorRed- будут хранить RGВ-коды
соответственно желтого, оранжевого, светло-серого, черного и красного цветов. По
скольку значения этих атрибутов будут одинаковыми для всех объектов класса ячейки,
мы сделаем их атрибутами класса (если же сделать их обычными атрибутами, каждый
объект будет хранить свой собственный набор этих атрибутов, что приведет к избыточ
ному расходу оперативной памяти);
♦ isCellChange - значение тrue сообщит о том, что ячейка разблокирована, а значение
False - что она заблокирована;
♦ fontColorCurrent - текущий цвет текста: черный или красный;
♦ bgColorDefault - заданный при создании ячейки цвет фона: оранжевый или светло
серый. Он понадобится, чтобы перевести ячейку из активного в неактивное состояние;
♦ bgColorcurrent- текущий цвет фона: желтый, оранжевый или светло-серый;
♦ id- порядковый номер ячейки. Он понадобится, чтобы при щелчке мышью на ячейке
сообщить компоненту поля судоку, какая ячейка стала активной.
Компонент MyLabel будет входить в состав компонента поля, который мы напишем чуть
позже. Он должен уведомлять компонент поля, когда текущая ячейка становится активной
после щелчка мышью. Наилучший способ сделать это- объявить сигнал, который будет
генерироваться при щелчке. Обрабатывая этот сигнал, поле всегда будет «в курсе», на
какой ячейке был выполнен щелчок.
Мы объявим в .ячейке сигнал cellChangeFocus. Он будет передавать обработчику единст
венный параметр целочисленного типа - номер ячейки, на которой пользователь щелкнул
МЫШЬЮ.
Теперь определим набор необходимых методов нашего класса и представим в общих чер
тах, что должен делать каждый из них:
♦ ·mousePressEvent () - этот метод следует переопределить, чтобы получить возможность
обрабатывать щелчки мышью. Внутри него мы будем генерировать сигнал cellChangeFocus;
♦ shoWColorCurrent () - задаст для ячейки цвета текста и фона, взятые из атрибутов
fontco·lorcurrent и bgColorCurrent соответственно, и тем самым обновит визуальное
состояние ячейки, или, как говорят программисты, перерисует ее. Этот метод мы будем
вызывать после перевода ячейки из неактивного состояния в активное, из разблокиро
ванного - в заблокированное и наоборот;
♦ setCellFocus () - переведет ячейку из неактивного состояния в активное. Сделать это
очень просто - мы занесем в атрибут bgColorCurrent код желтого цвета (который хра-
766 Часть 11. Библиотека PyQt 6
ЭЛЕКТРОННЫЙ АРХИВ
Напомним, что файлы с кодами разрабатываемой в этой главе программы. находятся
в папке sudoku сопровождающего книгу электронного архива (см. приложение).
class MyLabel(QtWidgets.QLabel):
colorYellow = "#FFFF90"
colorOrange = "#F5D8Cl"
colorGrey = "#Е8Е8Е8"
colorBlack = "#000000"
colorRed = "#D77A38"
changeCellFocus = QtCore.pyqtSignal(int)
self.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.setFixedSize(З0, 30)
self.setMargin(0)
self.setText("")
if id < О or id > 80:
id = О
self.id = id
self.isCellChange = True
self.fontColorCurrent = self.colorBlack
self.bgColorDefault = bgColor
self.bgColorCurrent = bgColor
self.showColorCurrent()
def showColorCurrent(self):
self.setStyleSheet("background-color:" + self.bgColorCurrent +
";color:" + self.fontColorCurrent + ";")
def setCellFocus(self):
self.bgColorCurrent = self.colorYellow
self.showColorCurrent()
def clearCellFocus(self):
self.bgColorCurrent = self.bgColorDefault
self.showColorCurrent()
def setCellBlock(self):
self.isCellChange = False
self.fontColorCurrent = self.colorRed
.self.showColorCurrent()
def clearCellBlock(self):
self.isCellChange = True
self.fontColorCurrent = self.colorBlack
self.showColorCurrent()
фокус ввода (для чего вызовем у нее метод setFocusPolicy() с параметром NoFocus), - это
нужно для того, чтобы поле судоку при нажатии любой из этих кнопок не теряло фокус,
и пользователь смог продолжать манипулировать в нем с помощью клавиш.
Ьtn = QtWidgets.QPushButton( "Х")
Ьtn.setFixedSize(27, 27)
Ьtns.append(Ьtn)
Таким же образом создаем кнопку Х, которая уберет цифру из ячейки.
for Ьtn in Ьtns:
hЬox.addWidget(Ьtn)
Помещаем все кнопки в контейнер QHBoxLayout.
Ьtns[OJ .clicked.connect(self.onВtnOClicked)
Ьtns[l] .clicked.connect(self.onBtnlClicked)
Ьtns[2] .clicked.connect(self.onBtn2Clicked)
Ьtns[З] .clicked.connect(self.onBtnЗClicked)
Ьtns[4] .clicked.connect(self.onBtn4Clicked)
Ьtns[SJ .clicked.connect(self.onBtnSClicked)
Ьtns[б] .clicked.connect(self.onBtnбClicked)
Ьtns[7] .clicked.connect(self.onBtn7Clicked)
Ьtns[BJ .clicked.connect(self.onBtn8Clicked)
Ьtns[9] .clicked.connect(self.onBtnXClicked)
Привязываем к сигналам clicked всех этих кнопок соответствующие обработчики - мето
ды класса поля, которые объявим позже.
frame2.setLayout(hЬox)
vBoxМain.addWidget(frame2,
alignment=QtCore.Qt.AlignmentFlag.AlignHCenter)
Помещаем контейнер с кнопками в панель с рамкой, а ее - во «всеобъемлющий» контей
нер vвoxLayout, не забыв указать горизонтальное выравнивание посередине.
self.setLayout(vBoxМain)
И помещаем этот контейнер в компонент поля.
Если была нажата клавиша<j>, следует сделать активной ячейку, расположенную строкой
выше. Мь1 получим номер этой ячейки, вычтя из номера активной ячейки (он, как мы знаем,
хранится в атрибуте idCellinFocus) число 9- т.е. количество ячеек, помещающихся
в строке поля. Если получившаяся разность меньше о (фокус выделения вышел за пределы
верхней границы поля), мы прибавляем к разности 81 (количество ячеек в поле), в результа
те чего фокус окажется на самой последней строке поля, в том же столбце. И, наконец, вы
зываем метод onChangeCellFocus() класса поля, передав ему результирующий номер ячей
ки, чтобы сделать ее активной.
Если была нажата клавиша<--+>, нужно сделать активной ячейку, расположенную правее.
Понятно, что для получения ее номера нам следует прибавить к номеру активной ячейки
единицу. Если же полученная сумма оказалась больше 80 (фокус выделения вышел за пре
делы верхней границы диапазона имеющихся ячеек), мы вычтем из нее то же число 81-
тогда фокус окажется на самой первой ячейке в поле. И не забываем напоследок вызвать
метод onChangeCellFocus().
Обработка нажатия клавиш <l> и <<-> производится аналогично. Вы можете сами разо
браться, как работает выполняющий ее код.
Если была нажата клавиша<1>..<9>,
. мы формируем на основе ее кода соответствующий
символ, воспользовавшись функцией chr (), вызьmаем у активной ячейки метод
setNewТext() и передаем ему этот символ. Так мы занесем в активную ячейку цифру, соот
ветствующую нажатой клавише.
Случай нажатия клавиш <Backspace>, <Del> или <пробел> - самый простой. Мь1 вызыва
ем у активной ячейки метод setNewТext(), передав ему пустую строку, - так мы уберем
цифру с ячейки.
В самом конце, какая бы ни бьша нажата клавиша, мы в обязательном порядке вызьmаем
метод keyPressEvent() базового класса. Если этого не сделать, программа может повести
себя непредсказуемо.
def onВtnOClicked(self):
self.cells[self.idCellinFocus] .setNewText("l")
def onВtnlClicked(self):
self.cells[self.idCellinFocus] .setNewТext("2")
def onBtn2Clicked(self):
self.cells[self.idCellinFocus] ..setNewТext( "З")
def onВtnЗClicked(self):
self.cells[self.idCellinFocus] .setNewТext("4")
def onBtn4Clicked(self):
self.cells[self.idCellinFocus] .setNewТext("S")
def onBtnSClicked(self):
self.cells[self.idCellinFocus] .setNewText("б")
def onBtnбClicked(self):
self.cells[self.idCellinFocus] .setNewText("7")
774 Часть //. Библиотека PyQt б
def onBtn7Clicked(self):
self.cells[self.idCellinFocus] .setNewТext("B")
def onBtn8Clicked(self):
self.cells[self.idCellinFocus] .setNewТext("9")
def onBtnXClicked(self):
self.cells[self.idCellinFocus] .setNewТext("")
def onClearAllCells(self):
for cell in self.cells:
cell.setText("")
cell.clearCellBlock()
Метод onClearAllCells() очистит поле судоку (листинг 32.5). В нем мы перебираем все
имеющиеся в поле ячейки, каждую очищаем от занесенной в нее цифры и переводим в раз
блокированное состояние вызовом метода clearCellBlock() класса MyLabel.
def onBlockCell(self):
cell = self.cells[self.idCellinFocus]
if cell. text() ! = '"':
' if cell.isCellChange:
cell.setCellBlock()
def onBlockCells(self):
for cell in self.cells:
if cell.text() and cell.isCellChange:
cell.setCellBlo,ck()
Глава 32. Программа «Судоку» 775
Метод onBlockCells(), блокирующий все ячейки, выполняет перебор всех ячеек и блокиру
ет тобую из них, если она содержит цифру и еще не заблокирована (листинг 32. 7).
def onClearBlockCell(self):
cell = self.cells[self.idCellinFocus]'
if not cell.isCellChange:
cell.clearCellBlock()
def onClearBlockCells(self):
for cell in self.cells:
if not cell.isCellChange:
cell.clearCellBlock()
Метод onClearBlockCells(), разблокирующий все ячейки поля (листинг 32.9), очень прост,
и вы, уважаемые читатели, сами поймете, как он работает.
self.sudoku = Widget()
self.setCentralWidget(self.sudoku)
Создаем объект класса Widget, сохраняем его в атрибуте sudoku и помещаем в окно в каче
стве центрального.
menuBar self.menuВar()
toolBar = QtWidgets.QToolBar()
Получаем уже имеющееся в окне главное меню и создаем панель инструментов.
myМenuFile = menuBar.addМenu("&Фaйл")
Создаем меню Файл.
action = myMenuFile.addAction(QtGui.Qicon(r"images/new.png"),
"&Новый", self.sudoku.onClearAllCells,
QtGui.QKeySequence("Ctrl+N"))
Создаем пункт Новый меню Файл.
Для создания пунктов меню используем разновидность метода addAction() класса QMenu,
которая в качестве параметров принимает значок, название пункта, обработчик и комбина
цию клавиш. На основе всего этого метод формирует действие (объект класса QAction),
создает связанный с ним пункт меню и возвращает это действие в качестве результата
(подробности- в разд. 28.2.2). Мы сохраним полученное действие в переменной, чтобы
впоследствии создать на панели инструментов связанную с ним кнопку и задать текст под
сказки для строки состояния.
В качестве обработчика для этого действия мы указываем метод onClearAllCells () компо
нента поля судоку (класса Widget).
toolBar.addAction(action)
action.setStatusTip("Coздaниe новой, пустой головоломки")
Создаем на основе только что подготовленного действия кнопку Новый панели инструмен
тов и задаем для действия текст подсказки, выводимой в строке состояния.
myМenuFile.addSeparator()
toolBar.addSeparator()
actioп = myMenuFile.addAction("&Bыxoд",
QtWidgets.QApplication.instance().quit,
QtGui.QKeySequence("Ctrl+Q"))
action.setStatusTip("Зaвepшeниe работы программы")
Добавляем в меню и на панель инструментов разделители и точно таким же образом созда
ем пункт Выход меню Файл. В качестве обработчика указываем сигнал quit() объекта
программы.
myМenuEdit = menuBar.addМenu("&Пpaвкa")
action = myMenuEdit.addAction("&Блoкиpoвaть",
self.sudoku.onBlockCell, QtCore.Qt.Key.Key_F2)
action.setStatusTip("Блoкиpoвaниe активной ячейки")
action = myMenuEdit.addAction(QtGui.Qicon(r"images/lock.png"),
"Б&локировать все",
self.sudoku. onВlockCells, QtCore.Qt.Кеу·.Кеу_FЗ)
778 Часть 11. Библиотека PyQt 6
toolBar.addAction(action)
action.setStat:usTip("Блoкиpoвaниe всех ячеек")
action = myMenuEdit.addAction("&Paзблoкиpoвaть",
self.sudoku.onClearBlockCell, QtCore.Qt.Key.Key_F4)
action.setStatusTip("Paзблoкиpoвaниe активной ячейки")
action myMenuEdit.addAction(QtGui.Qicon(r"images/unlock.png"),
=
"Р&азблокировать все",
self.sudoku.onClearBlockCells,
QtCore.Qt.Key.Key_FS)
toolBar.addAction(action)
action.setStatusTip( "Разблокирование всех ячеек")
Создаем меню Правка, его пункты Блокировать, Блокировать все, Разблокировать
и Разблокировать все и соответствующие им кнопки панели инструментов. В качестве
обработчиков действий указываем соответственно методы onBlockCell(), onBlockCells(),
onClearBlockCell() и onClearBlockCells() компонента поля судоку.
myМenuAЬout = menuBar.addМenu("&Справка")
извлекаем эти значения и позиционируем окно по этим координатам (размеры окна хранить
не имеет смысла, поскольку они неизменны).
Метод closeEvent() будет автоматически вызван при закрытии окна (листинг 32.1О). В нем
мы выполняем сохранение текущих координат левого верхнего угла окна.
Выполним запуск программы, запустив модуль start.pyw любым знакомым нам способом:
щелчком мышью на самом файле или нажатием клавиши <F5> в окне IDLE, в котором от
крыт этот модуль. Проверим, работает ли активизация ячеек по щелчку мыши и посредст
вом клавиш-стрелок поставим в какие-либо ячейки цифры и удалим их. Попробуем забло
кировать и разблокировать ячейки. Наконец, очистим поле судоку, вызовем окна сведений
о программе и Qt и закроем программу.
1
def getDataAllCells(self):
listAllData = []
for cell in self.cells:
listAllData.append("0" if cell.isCellChange else " l")
s = cell.text ()
listAllData.append(s if len(s) == 1 else "0")
return "" .join(listAllData)
def getDataAllCellsMini(self):
listAllData = []
for cell in self.cells:
s = cell.text()
listAllData.append(s if len(s) l.else "0")
return "".join(listAllData)
def getDataAllCellsExcel(self):
nurnЬers = (9, 18, 27, 36, 45, 54, 63, 72)
listAllData = [self.cells[O] .text() ]
for i in range(l, 81):
listAllData.append("\r\n" if i in nurnЬers else "\t")
listAllData.append(self.cells[i] .text() )
listAllData.append("\r\n")
return "".join(listAllData)
Метод getDataAllCellsExcel(), что станет формировать данные для вставки в Excel, немно
гим сложнее (листинг 32.15). Мы создаем кортеж, содержащий номера ячеек, перед кото
рыми в результирующую строку вместо символа табуляции нужно вставить возврат каретки
и перевод строки, - как видим, это номера первых ячеек в каждой строке, кроме первой.
Затем создаем список из единственного элемента - содержимого самой первой ячейки:
цифры или пустой строки. Далее в цикле перебираем все ячейки от второй· до последней.
Если номер очередной ячейки входит в объявленный ранее кортеж (т. е. начинается новая
строка поля судоку), добавляем в список возврат каретки и перевод строки, в противном
случае - символ табуляции. Далее добавляем в список содержимое ячейки. Наконец, за
вершаем. формируемые данные возвратом каретки и переводом строки и формируем строку,
составленную из элементов списка.
elif 1 == 162:
for i in range(0, 162, 2):
if data[i] == "О":
self.cells[i // 2].clearCellBlock()
else:
self.cells[i // 2] .setCellBlock()
self.cells[i // 2] .setText('"' if data[i + 1] "О" \
else data[i + 1])
self.onChangeCellFocus(0)
Начнем с того, что в самое начало модуля, где находятся выражения импорта, добавим вы
ражение, импортирующее модуль для поддержки регулярных выражений:
import re
Регулярные выражения очень помогут нам реализовать проверку вставляемых данных на
корректность.
Доработаем конструктор. Все необходимые добавления показаны в листинге 32.17 (добав
ленный код выделен полужирным шрифтом).
myMenuEdit = menuBar.addМenu("&Пpaвкa")
action = myМenuEdit.addAction(QtGui.Qicon(r"images/copy.png"),
"К&опировать", self.onCopyData,
QtGui.QКeySequence("Ctrl+c"))
toolВar.a.c:ldAction(action)
action.setstatusTip("Кoпиpoв;iНИE! гоnоволомки в буфер обмена")
action = myМenuEdit.addAction(QtGui.Qicon(r"images/paste.png"),
"&Вставить", self.onPasteData,
QtGui.QКeySequence("Ctrl +V"))
toolВar.addAction(action)
action.setstatusTip("Вставка гоnоволомки из буфера обмена")
myМenuEdit. addSeparator ()
toolВar. addSeparator ()
action = myMenuEdit.addAction("&Блoкиpoвaть",
self.sudoku.onBlockCell, QtCore.Qt.Key_F2)
Здесь мы добавляем в начало меmо Правка пять пунктов: Копировать, Копировать ком
пактно, Копировать для Excel, Вета.вить и Вставить из Excel. Указываем для них в каче
стве обрабоtчиков приведенные ранее методы, а также добавляем нужные кнопки в панель
инструментов.
Глава 32. Программа «Судаку» 785
def onCopyData(self):
QtWidgets.QApplication.clipboard().setText(
self.sudoku.getDataAllCells())
def onCopyDataMini(self):
QtWidgets.QApplication.clipboard().setText(
self.sudoku.getDataAllCellsMini())
def onCopyDataExcel(self):
QtWidgets.QApplication.clipboard().setText(
self.sudoku.getDataAllCellsExcel())
Метод onPasteData() вставляет данные в полном или компактном формате (листинг 32.19).
Перед тем как вызвать метод setDataAllCells() класса поля судоку, передав ему данные
для вставки, он выполняет их проверку. Сначала он удостоверяется, что данные для вставки
вообще есть (не равны пустой строке), потом - что их длина равна 81 или 162 символам.
Далее он выполняет последнюю проверку - выясняет, не присутствует ли в строке символ,
отличный от цифр 0 ...9. Для этого он создает регулярное выражение, совпадающее с любым
из таких символов ([ л 0-9J), и выполняет поиск в строке с даннь1ми посредством быстро
выполняющегося метода rnatch() (более подробно о работе с регулярными выражениями
рассказьmалось в главе 7).
И только если все проверки выполнены, вызьmается метод setDataAllCells(), и ему пере
дается строка с даннь1ми для вставки. После чего сразу же выполняется выход из метода.
Если же какая-лдбо проверка завершилась неудачей, будет выполнено самое последнее вы
ражение - вызов метода dataErrorMsg(), выводящего сообщение об ошибке. Мы напишем
этот метод позже.
def onPasteDataExcel(self):
data = QtWidgets.QApplication.clipboard().text()
786 Часть 11. Библиотека PyQt б
if data:
data = data.replace ("\r", "")
r = re.compile(r"([0-9]?[\t\n]){81)")
if r.match(data):
result = []
if data[-1] == "\n":
data = data[:-1]
dl = data.split("\n")
for sl in dl:
dli = sl.split("\t")
for sli in dli;
if len(sli) ==·о:
result.append("00")
else:
result.append('�О" + sli[0J)
data = "".join(result)
self.sudoku.setDataAllCells(data)
return
self.dataErrorMsg()
Осталось написать этот метод. Его код приведен в листише 32.21 - как видим, он очень
прост.
def dataErrorMsg(self):
QtWidgets.QMessageBox.infoпnation(self, "Судаку",
"Данные имеют неправильный формат")
action = myMenuFile.addAction(QtGui.Qicon(r"images/new.png"),
"&Новый", self.sudoku.onClearAllCells,
QtCore.Qt.CTRL + QtCore.Qt.Key_N)
toolBar.addAction(action)
action.setStatusTip("Coздaниe новой, пустой головоломки")
action = myМenuFile.addAction(QtGui.Qicon(r"images/open.png"),
"&Открыть ...", self.onOpenFile,
QtGui. QКeySequence ( "Ctrl+о"))
toolВar.addAction(action)
action.setstatusTip("Зarpyзкa головаnомки из файnа")
action = myМenuFile.addAction(QtGui.Qicon(r"i.mages/save.png"),
"Со&хранить ...", self.onSave,
QtGui.QКeySequence("Ctrl+S"))
788 Часть 11. Библиотека PyQt б
toolВar.addAction(action)
action.setstatusTip("Сохранение гоnоваiiомки в файле")
action = myМenuFile.addAction("&Co:xpaнить компахтно . ..",
self.onSaveМ:i.ni) ·'
action.setstatusTip("Coxpaнeниe гоnоваnомки в компактном ФQРGте")
myМenuFile.addSeparator()
toolBar.addSeparator()
Этот код добавит в меюо Файл, между пунктом Новый и разделителем, пункты Открыть,
Сохранить и Сохранить компактно. В качестве обработчиков указаны описанные ранее
методы. Также выщшняется добавление еще двух кнопок в панель инструментов.
Листи
._ _,..1,-.,..
· def onOpenFile(self) :
fileName = QtWidgets.QFileDialog.getOpenFileName(self,
"Выберите файл", QtCore.QDir.homePath() ,
"Судаку (* .svd) ") [О]
if fileName:
data =· ""
try:
with open(fileName, newline="") as f:
data = f. read()
except:
QtWidgets.QМessageBox.information(self, "Судоку",
"Не удалось _открыть файл")
return
if len(data) > 2:
if data[-1] == "\n":
data = data[:-1]
if len(data) == 81 or len(data) 162:
r = re.compile(r"[л0-9]")
if not r.match(data) :
self.sudoku.setDataAllCells(data)
return
self.dataErrorMsg()
В методе onOpenFile (), загружающем данные из файла (листинг 32.23), мы выводим стан
дартное диалоговое окно открытия файла, указав в качестве начального каталог пользова
тельского профиля. Если пользователь выбрал файл и нажал кнопку Открыть, мы в блоке
обработки исключения открываем этот файл для чтения и читаем его содержимое. Если
файл прочитать не удалось и бьшо сгенерировано·исключение, выводим соответстзующее
сообщение и выполняем возврат из метода.
Если данные были прочитаны, мы проверяем, имеют ли они длину 81 или 162 символа и не
включают ли в себя символы, отличные от цифр 0... 9. Если это так, передаем загруженные
данные все тому же методу setDataAllCells() класса поля судоку и выполняем возврат.
Глава 32. Программа «Судоку» 789
Если же все эти проверки не увенчаются успехом, выполняется последнее выражение, ко
торое вызовет метод dataErrorMsg() класса MainWindow, написанный нами ранее.
def onSave(self):
self.saveSVDFile(self.sudoku.getDataAllCells() )
def onSaveMini(self):
self.saveSVDFile(self.sudoku.getDataAllCellsMini() )
Методы onSave() и onSaveMini(), сохраняющие данные в файл (листинг 32.24), очень про
сты - они лишь вызывают метод saveSVDFile(), передав ему результат, возвращеннь1й
методами соответственно getDataAllCells() и getDataAllCellsMini() класса поля судоку.
Вычисляем координаты левого верхнего угла печатаемой в настоящий момент ячейки. Го
ризонтальную координату мы можем получить, взяв номер текущей ячейки текущей строки
и умножив его на ширину ячейки (30 пикселов). Вертикальная координата вычисляется
аналогично на основе номера текущей строки и ее высоты (также 30 пикселов).
painter.setPen(penBorder)
Теперь нам нужно вывести сам квадратик, создающий ячейку. Задаем темно-серое перо для
печати рамки этого квадратика.
painter.setBrush(brushGrey if. \
self.cells[i] .bgColorDefault == MyLabel.colorGrey \
else brushOrange)
Если для фона ячейки задан светло-серый цвет, задаем светло-серое перо, в противном слу
чае - оранжевое.
Глава 32. Программа «Судоку» 791
1Ш Предварительный просмотр х
5
-t--+=+----+--+ 6
--+-=-i'-"+--< i
c-:-'t---+-+--+-+-t•--i'--i-.:..J
41
class PreviewDialog(QtWidgets.QDialog):
Окно предварительного просмотра мы делаем подклассом класса Dialog. Это позволит нам
без особых проблем сделать размеры окна неизменяемыми, а само окно - модальным.
def init (self, parent=None):
QtWidgets.QDialog. init (self, parent)
self.setWindowТitle("Пpeдвapитeльный просмотр")
self.resize(б00, 400)
vBox = QtWidgets.QVBoxLayout()
Интерфейс окна включает две горизонтальные группы элементов управления, расположен
ные друг над другом (см. рис. 32.3). Поэтому для размещения групп мы создадим верти
кальный контейнер QVBoxlayout.
hBoxl = QtWidgets.QHBoxLayout()
Верхняя группа будет содержать три кнопки: для увеличения, уменьшения и сброса мас
штаба. Поскольку элементы в группе должны располагаться по горизонтали, используем
для их расстановки контейнер QHBoxLayout.
ЬtnZoomin = QtWidgets.QPushButton("&+")
ЬtnZoomin.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus)
hBoxl.addWidget(ЬtnZoomin, alignment=QtCore.Qt.AlignmentFlag.AlignLeft)
ЬtnZoomOut = QtWidgets.QPushВutton("&-")
ЬtnZoomOut.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus)
hBoxl.addWidget(ЬtnZoomOut,
alignment=QtCore.Qt.AlignmentFlag.AlignLeft)
ЬtnZoomReset = QtWidgets.QPushButton("&Cбpoc")
ЬtnZoomReset.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus)
ЬtnZoomReset.clicked.connect(self.zoomReset)
hBoxl.addWidget(ЬtnZoomReset,
alignment=QtCore.Qt.AlignmentFlag.AlignLeft)
Создаем все эти три кнощш и добавляем их в контейнер. У каждой кнопки указываем, что
она не может принимать фокус ввода, вызвав у нее метод setFocusPolicy() с параметром
NoFocus, - таким образом, мы создадим в нашем диалоговом окне подобие панели инстру
ментов. Также у всех трех кнопок указываем выравнивание по левому краю.
У кнопки сброса масштаба мы сразу же указываем в качестве обработчика сигнала. clicked
метод zoomReset() класса PreviewDialog. У остальных кнопок мы пока не указьшаем обра
ботчики этого сигнала.
hBoxl.addStretch()
Добавляем в горизонтальный контейнер растягивающуюся область, чтобы все кнопки ока
зались прижатыми к левому краю контейнера.
vBox.addLayout(hBoxl)
Добавляем сам горизонтальный контейнер в вертикальный.
Глава 32. Программа «Судоку» 793
hBox2 = QtWidgets.QHBoxLayout()
Создаем еще один горизонтальный контейнер, в котором будут выводиться панель предва
рительного просмотра и кнопка Закрыть.
self.ppw = QtPrintSupport.QPrintPreviewWidget(parent.printer)
self.ppw.paintRequested.connect(parent.sudoku.print)
hBox2.addWidget(self.ppw)
myМenuFile.addSeparator()
toolВar. addSeparator ()
action = myМenuFile.addAction(QtGui.Qicon(r"illlages/print.pnq"),
11 &Печать..." , self.onPrint,
QtGui. QКeySequence("Ctrl+P") )
toolВar.addAction(action)
action.setStatusTip ("Печать гаnовоnсмки")
action = myМenuFile.addAction(QtGui.Qicon(r"illlages/preview.png"),
"П&редвар,rrельный просмотр...", self.onPreview)
toolВar.addAction(action)
action. setStatusTip ("Предвар,rrельный просмотр гаnовоnсмки•i)
myМenuFile.addSeparator()
toolBar.addSeparator()
action = myMenuFile.addAction("&Bыxoд",
QtWidgets.QApplication.instance().quit,
QtGui.QKeySequence("Ctrl+P"))
Глава 32. Пfjограмма «Судаку» 795
def onPrint(self):
pd = QtPrintSupport.QPrintDialog(self.printer, parent=self)
pd.setOptions(
QtPrintSupport.QPrintDialog.PrintDialogOption.PrintToFile
QtPrintSupport.QPrintDiqlog.PrintDialogOption.PrintSelection)
if pd.exec() == QtWidgets.QDialog.DialogCode.Accepted:
self.sudoku.print(self.printer)
def on.Preview(sеЦ) :
pd = PreviewDialog(self)
pd.exec()
def onPageSetup(self):
pd = QtPrintSupport.QPageSetupDialog(self.printer, parent=self)
pd.exec()
Вот и закончилось наше путешествие в мир Python 3 и PyQt 6. Материал книги описьmает
лишь базовые возможности этих замечательных программных платформ. А здесь мы рас
скажем, где найти дополнительную информацию, чтобы продолжить изучение.
Самыми важными источниками информации является официальный сайт Python https://
www.python.org/ - на нем вы найдете дистрибутив, новости, а также ссылки на все другие
ресурсы в Интернете. А на сайте https://docs.python.org/3/ имеется документация по Python 3,
которая обновляется в режиме реального времени.
Описание библиотеки PyQt можно найти на сайте https://www.riverbankcomputing.com/, но
от него мало толку. Поэтому лучше сразу наведаться на сайт https://doc.qt.io/, где приведена
полная документация по фреймворку Qt, лежащему в основе PyQt.
Описание всевозможных дополнительных библиотек, расширяющих возможности Python,
созданных сторонними разработчиками и доступных для свободного скачивания, вы найде
те на сайте https://pypi.python.org/pypi.
Следует отметить библиотеку Qt for Python (https://www.qt.io/qt-for-python), бывшую
PySide, созданную специалистами компании Nokia, а ныне поддерживаемую независимыми
разработчиками. Эта библиотека является полным аналогом PyQt и распространяется по
лицензии LGPL. Существуют другие библиотеки для создания оконных программ: wxPython
(https://www.wxpython.org/), PyGObject (https://pygobjectreadthedocs.io/enЛatest/indeLhtml,
бьmшая РуGТК), PyWin32 (https://mhammond.github.io/pywin32/) и pyFLTK (http://pyfltk.
sourceforge.net/). Обратите внимание и на библиотеку pygame (http://www.pygame.org/),
позволяющую разрабатывать игры, и на фреймворк Django (https://www.djangoprojectcom/),
с помощью которого можно создавать веб-сайтьr.
Если в процессе изучения у вас возникнут какие-либо вопросы, вспомните, что в Интернете
можно найти решения самых разнообразных проблем, - достаточно лишь набрать свой
вопрос в строке запроса того или J:ПIОГО поискового портала (например, https://www.
blng.com/ или https://www.google.ru/).
Засим авторы прощаются с вами, уважаемые читатели, и желают успехов в нелегком, но
таком увлекательном деле, как программирование!
ПРИЛОЖЕНИЕ
_hash_()268
@ _iadd_()269
@abstractmethod272,276 _iand_()270
@classmethod272 _ifloordiv_()269
@staticmethod271 _ilshift_()270
_imod_()269
_import_()245
_imul_()269.
_abs_()269 _index_()268
_add_()269 _init_()260
_all_248,253 _int_()268
_and_()270 _invert_()270
-annotations- 242 _ior_() 270
- bases- 264 _ipow_()269
_bool_()267,268 _irshift_() 270
_call_()266 _isub_()269
- cause- 288 _iter_()292
-class- 301 _itruediv_() 269
_complex_()268 _ixor_()270
_contains_()271,294 _le_()271
_debug_291 _len_()267
_del_()261 _lshift_()270
_delattr_()267 _lt_()271
_delitem_() 294 _mod_()269
_dict_246,267 - mro- 265
_dir_()249 _mul_()269
_ doc_ 43,44,94 _name_243,301
_enter_()284 _ne_()270
_eq_()270 _neg_()269
_ exit_()284 _next_()236,287,292; 310,317
-file- 307 _or_()270
_float_()268 _pos_()269
_floordiv_()269 _pow_()269
_ge_()271 _radd_()269
_getattr_()249,266 _rand_()270
_getattribute_()267 _repr_()268
_getitem_()292,293 _rfloordiv_()269
_gt_()271 _rlshift_()270
Предметный указатель 799
isRemote() 748
isRowHidden() 537,538,540
к
isRowSelected() 544 Кеу 435,704
isRunning() 366 key() 436,437
isSectionHidden() 542 КЕУ ALL ACCESS 339
isSeekaЫe() 697 КЕУ-CREAТЕ-SUВ-КЕУ 339
isSelect() 561 КEY_ENUMERATE_SUB_KEYS 339
isSelected() 544,616 КЕУ EXECUTE 339
isSeparator() 681 КЕУ NOТIFY 339
isShaded() 694 КEY_QUERY_VALUE 339
isSingleShot() 425 КЕУ READ 339
isSizeGripEnaЫed() 635 КЕУ SET VALUE 339
isslice() 181 КЕУ_WOW64_32КEY 339,343
isspace() 119 КЕУ_WOW64_64КEY 339,343
issubset() 173 КЕУ WRITE 339
issuperset() 173 Keyboardlnterrupt 286
isSystemTrayAvailaЫe() 694 KeyboardModifier 438
isTabEnaЫed() 468 КеуЕттоr 286
isTabVisiЫe() 468 keyPressEvent() 437, 627
isTearOffEnaЫed() 678 keyReleaseEvent() 437, 627
isTearQffМenuVisiЫe() 678
keys() 190,330,705
istitle() 119
killTimer() 423
isTristate() 480
isUndoAvailaЫe() 483,492
· isUndoRedoEnaЫed() 487,492 L
isupper() 118
isUserTristate() 531 lambda 234
isValid() 392,398,522,553,557,561,575, last() 561
727 lastВlock() 493
isValidColor() 576 lastEттor() 552,563,564
isVisiЫe() 383,616,682,684,695 lastgroup 143
isWritaЫe() 759 lastindex 142
italic() 584 lastlnsertld() 563
item() 526 lastPos() 628,629
itemAt() 605, 612 lastQuery() 563
itemChange() 631 lastScenePos() 628,629
itemChanged() 528 lastScreenPos() 628,629
itemData 517 LayoutDirection 454
ItemDataRole 520 LayoutMode 536
ItemFlag 522 LC ALL 113
itemFromlndex() 527 LC COLLATE 113
ItemlndexMethod 604 LC СТУРЕ 113
items() 190,330,606,612 LC MONETARY 113
itemsBoundingRect() 604 LC NUMERIC 113
ItemSelectionМode 606 LC ТIМЕ 113
ItemSelectionOperation 608 leapdays() 218
itemText() 470,517 leaveEvent() 441
left() 397
J len() 97,109,153,188,267
length() 555
join() 111,168,326,374 LifoQueue 372
JoinМode 571 lighter() 577
joinPreviousEditВlock() 497 line() 618
810 Предметный указатель
lineCount()493 mapToGlobal()440
LineWrapMode 489 mapToParent()441
linkActivated()477,622 mapToScene()612
linkНovered()477,622 marginsAdded() 395
list 39,47 marginsRemoved()395
list()55, 150,151 mask()596
listdir() 332 MaskМode 596
ljust()103 match 71
load()328, 329,512,593,595,599 Mat�h 142
loadedChanged()724 match()140
loadFinished()514 matches()438
loadFromData()595,599 MatchFlag 518
loadProgress()514 max 201,204,206, 212
loads()128 max()85
loadStarted()514 iµaximumВlockCount()493
loadUi()359 maximumНeight()386
loadUiType()360 maximumPhysicalPageSize()749
LOCALE 132 maximumSectionSize()542
localeconv()113 maximumSize()386
LocaleHTMLCalendar 215 maximumWidth()386
LocaleTextCalendar 213 МАХУЕАR 201
locals()227 md5()129
localtime()195 mdiArea()693
location()748 МDI-программа 670
lock()376 MediaStatus 698
log()88 mediaStatus()698
logl 0()88 mediaStatusChanged()699
log2()88 MemoryError 286
logicallndex() 543 memoryview()319
loops()697 menu()479,687
loopsRemaining()723 menuAction()677
loopsRemainingChanged()724 menuBar()671
lostFocus()434 menuWidget()671
lower()114 messageChanged()690
lseek()314 messageClicked()695
lstrip()11О Messagelcon 695
metaData() 698
м metaDataChanged()699
microsecond 205,207,209
magenta()578 microseconds 199
magentaF()578 milliseconds 199
make_shortcut() 346 mimeData()444,447,449,630
makeAndModel()748 MimeHtmlSaveFormat 515
makedirs()331 min 201,204,206,212
maketrans()117 min()86
manhattanLength()391 minimumНeight()386
map()158 minimumPhysicalPageSize()749
mapFrom() 441 minimumSectionSize()542
mapFromGlobal()441 minimumSize()386
mapFromParent()441 minimumSizeHint()386
mapFromScene()611 minimumWidth()386
mapTo()441 minute 204,205,207,209
Предметный указатель 811
minutes199
MINYEAR201
N
mirror() 601 name300,312,335
mirrored() 601 name() 555,556,576,737
mkdir() 331 NameError287
mktime() 195 NameFormat576
rnnemonic() 435 nativeErrorCode() 557
mode312 newPage() 730
Mode507,602 next() 561,664
model() 522,531,532 nextld() 664,669
modificationChanged() 494 NoDockWidgetArea672
modifiers() 438,440,628,629,631 None49
module49 NoneType49
ModuleNotFoundError287 nonlocal241
modules245 normalized() 398
MONDAY213 normcase()333
month202,209 normpath()326
month() 217 not67
month abbr220 not in61,66,173,187,300
month name219 NotADirectoryError337
monthcalendar() 217 Notation485
monthrange() 218 NotlmplementedError 287
monthShown() 505 now() 208
MouseButton 439 numberOfМatches() 513
mouseDouЬ!eClickEvent() 439,627 numRowsAffected() 563
MouseEventCreatedDouЬ!eClick440
MouseEventFlag440
mouseGrabberltem() 608
о
mouseMoveEvent() 440,627 О APPEND313
mousePressEvent()439,627 О BlNARY313
mouseReleaseEvent() 439,627 О CREAT313
move() 322,387 О EXCL313
moveBottom() 396 О RDONLY313
moveBottomLeft() 396 О RDWR313
moveBottomRight() 396 О-SHORT-LIVED313
moveBy() 615 О TEМPORARY313
moveCenter() 396 О ТЕХТ313
moveCursor() 494 О TRUNC313
moveEvent() 430 О WRONLY313
Movement536 objectName() 410
MoveMode495 oct() 84,268
MoveOperation494 offset() 620,624
movePosition() 495 offsetChanged() 624
moveRight() 396 oldPos() 430
moveSection() 543 oldSize() 430
moveTo() 395 oldState() 429
moveTop() 396 opacity() 615,626
moveTopLeft() 396 opacityChanged() 626
moveTopRight() 396 opacityMask() 626
msleep() 368 opacityMaskChanged() 626
МULТILINE131,133 open() 302,307,313,329,552,635
mutedChanged() 700,724 ОреnКеу() 339
812 Предметный указатель
QSizePolicy461 QToo!Box469
QS!ider508 QToo!Button686
QSortFilterProxyModel546 QtPrintSupport362
QSoundEffect722 QTransforrn 593,616
QSpinBox499 QTreeView539
QSplashScreen379 QtSql363
QSplitter471 QtSvg363
QSq!Database551 QtWebEngineCore362
QSq!Error 556 QtWebEngineWidgets362
QSq!Field 555 QtWidgets362
QSqllndex 556 QtXml363
QSq!Query557 QtXm!Pattems363
QSq!QueryModel563 Quality711,720
QSq!Record554,567 query() 564
QSq!Relation571 QuerylnfoKey() 343
QSq!Relationa!Delegate573 QueryValue() 341
QSqlRelationa!ТaЫeModel571 QueryValueEx() 340
QSq!TaЬleModel 565 question() 643
QStackedLayout460 Queue372
QStackedWidget460 quit() 363,370,417
QStandardltem524,528 QUrl497
QStandardltemМodel524 QValidator485
QStatusBar 689 QVariant423
QStatusTipEvent 682 QVBoxLayout452
QStringListModel523 QVideoWidget707
QStyledltemDelegate548 QWebEngineDownloadRequest515
QStyleOption548 QWebEngineFindTextResult513
QStyleOptionViewltem548 QWebEnginePage513,515
QSystemTraylcon 694 QWebEngineView511
Qt349 QWhee!Event441
Qt Designer 356 QWidget382,586
QT_VERSION_SТR349 QWindowStateChangeEvent429
QTaЫeView537 QWizard663
QTabWidget465 QWizardPage667
QtCore362
QTextВlock493
QTextBrowser497
R
QTextCharForrnat491, 506 R ОК320
QTextCursor494 radians() 88
QTextDocument491 raise287
QTextDocumentFragment496 raise_() 635
QTextEdit486 randint() 90
QTextOption489,590 random() 89
QtGui362 randrange() 90
QtHelp 363 range48
QThread365 range() 156,175
QTimeEdit501 rangeChanged()51 О
,QTimer424, 426 re142
QtMultimedia362 read() 308, 314,316
QtMultimediaWidgets362 readaЫe() 31 О
QtNetwork363 readline() 309,317
QToo!Вar685 readlines() 309,317
816 Предметный указатель
А Делегат521, 547
◊ связанный573
Абсолютное позиционирование451 Делетер275
Автоблокировщик378 Десериализация128
Аккордеон469 Дескриптор313
Аннотация242 Деструктор261
Атрибут87,256, 257 Диапазон48,175
◊ закрытый259, 273
◊ класса259
◊ общедоступный259
з
◊ экземпляра объекта258 Заставка379
Захват фрагмента137
Звуковой эффект722
Б
Блок29
Блокировщик376
и
Блокнот465 Импорт243
◊ идентификатора246, 251
в ◊ модуля243
◊ модуля из пакета252,253
Валидатор485 Индекс 150
Ветвь338 Индикатор процесса328
Взаимная блокировка379 Инструкция26
Временная отметка194 ◊ ветвления65,68,71
Временной промежуток 198 ◊ выбора65,71
Всплывающая подсказка408 ◊ закомментированная 30
Вхождение 66 ◊ проверочная290
Вызов функции223, 228 Интерактивный режим24, 26,28,32, 257
Выражение26 Интерпретатор 21
Выражение-генератор157 Исключение278
◊ вторичное288
г ◊ первичное288
◊ пользовательское289
Генератор Исполняющая среда21
◊ множества175 Источник
◊ словаря193 ◊ сигнала414
◊ списка156 ◊ события414
Геттер274 Итератор157,292
Горячая клавиша435 Итерация29, 77
Графическая сцена603
Графическое представление603
Группа137,462
к
◊ именованная138 Каталог текущий рабочий306
Квантификатор136
д Клавиша быстрого доступа435
Класс87,256
Двоичные данные92 ◊ базовый261
Действие675· ◊ встроенный256
Декоратор ◊ пользовательский256
◊ класса276 ◊ производный261
◊ функции237 ◊ символов136
830 Предметный указатель
Ключ185,755
Ключевое слово46
н
Комментарий30 Наследование261
Компиляция44 ◊ множественное263
Конкатенация60 Невхождение66
◊ неявная98
Консоль 23
Конструктор260
о
Контейнер293,451 Обработка
◊ отображение293 ◊ исключения278
◊ последовательность293 ◊ события414
Контекст283 Обработчик
Копия ◊ исключений279
◊ поверхностная151 ◊ контекста283
◊ полная151 ◊ сигнала352,414
Кортеж48,169 ◊ собьпия352,414
Куст338 Обратная ссьmка138
Объект49,256,257,261
л ◊ текущий87
Окно
Логическая величина47 ◊ вложенное670
Локаль112 ◊ главное670
Лямбда-функция234 ◊ модальное401
м
Операнд57
Оператор28,57
◊ присваивания46,50
Маска прав доступа320 ◊ присваивания в составе инструкции80
Мастер663 ◊ пустой63
Менеджер контекста283
◊ сравнения66
Меню
Определение
◊ · главное675 ◊ класса256,259
◊ контекстное675
◊. функции222,229
Метасимвол133 Отображение48, 185
Метод87,256 Очередь372
◊ абстрактный272 Ошибка
◊ закрытый259,273 ◊ времени выполнения278
◊ класса272 ◊ логическая278
◊ общедоступный 259 ◊ синтаксическая278
◊ специальный266
◊ статический271
Множество48,170 п
◊ неизменяемое175 Пакет252
Модель521 Палитра404
◊ вьщеления521 Параметр26,222
◊ промежуточная521 ◊ двоякий229
Модуль26,243 ◊ именованный228
◊ встроенный243 ◊ необязательный230
◊ главный243 ◊ ПОЗИЦИОННЫЙ 228
Мьютекс376 ◊ строго именованный229
◊ строго позиционный229
Предметный указатель 831
Перегрузка оператора269
Перезагрузка251
с
Перекрытие метода261 Сборщик мусора781
Переменная27,46 Свойство274
◊ глобальная226,235 Сегмент пути305
◊ локальная 222,225 Секция677
Переопределение метода262 Сериализация128
Перехват исключения278 Сетка455
Перечисление296 Сеттер275
Подкласс261 Сигнал352,414
Подключение243 ◊ перегруженный422
Подрежим открытия файла302 ◊ пользовательский421
◊ бинарный303 Словарь48,185
◊ ДВОИЧНЫЙ 303 Слот352,414
◊ текстовый303
◊ перегруженный417
Последовательность48,150
Событие352,414
◊ непронумерованная150
◊ пользовательское450
◊ пронумерованная150
Поток основной365 Специальный символ92,94, 132
Потомок351 Список47,150
Представление521 ◊ вложенный151
Приемник ◊ многомерный 155
◊ сигнала414 Срез 97,153
◊ события414 Ссьmка49
Примесь265 Стандартная библиотека35,243
Приоритет операторов63 Стек372,460
Присваивание46,50 · Стопка452
◊ групповое50 Строка47,92
◊ позиционное51 ◊ длина97
Программа ◊ документирования30,94
◊ главная243 ◊ необрабатываемая95
◊ многопоточная365 ◊ форматируемая108
◊ оконная349 Суперкласс261
Процесс365
Псевдоним244
Путь т
◊ абсолютный306 Тело
◊ относительный306 ◊ функции222
◊ цикла77
Тип данных47
◊ изменяемый49
Размер150,185
Распаковка233 ◊ неизменяемый 50
Расширенная подсказка 408 Точечная нотация 244, 257
Регулярное выражение131 Транспорт709
\ Редактор547
Режим открытия файла302
Результат35
Рекурсия239 Указатель302
◊ проход240 Условие65
Репозиторий37
Родитель351
832 Предметный указатель
ф ц
Файл в памяти 315 ЦИЮI65
Форма 356,457 ◊ бесконечный 78
Функция 26, 222 ◊ обработки собьfГий 363
◊ анонимная 234 ◊ перебора последовательности 77
◊ вложенная 240 ◊ с условием 78
◊ встроенная 222
◊ генератор 236
◊ обратного вызова 233
ч
◊ пользовательская 222 Число
◊ родительская 240 ◊ вещественное 47,82,83
◊ специальная 249 ◊ восьмеричное 82
◊ двоичное 82
х ◊ десятичное 82
◊ комплексное 82,84
Хранилище настроек 752 ◊ целое 82
◊ шестнадцатеричное 82
э
Элемент 47, 150, 185,296,338