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

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


Скачать 7.92 Mb.
НазваниеНиколай Прохоренок Владимир Дронов
Дата05.05.2023
Размер7.92 Mb.
Формат файлаpdf
Имя файлаПрохоренок_Н_А__Дронов_В_А_Python_3_и_PyQt_5_Разработка_приложен.pdf
ТипДокументы
#1111379
страница31 из 83
1   ...   27   28   29   30   31   32   33   34   ...   83
 move(<Путь к файлу>, <Куда перемещаем>)
— перемещает файл в указанное место с уда- лением исходного файла. Если файл существует, он будет перезаписан. Если файл не удалось переместить, возбуждается исключение
OSError или одно из исключений, являющихся подклассом этого класса. В качестве результата возвращает путь переме- щенного файла.
Пример перемещения файла file4.txt в каталог
C:\book\test
:
>>> shutil.move(r"file4.txt", r"C:\book\test")
Для переименования и удаления файлов предназначены следующие функции из модуля os
:
 rename(<Старое имя>, <Новое имя>)
— переименовывает файл. Если файл не удалось переименовать, возбуждается исключение
OSError или одно из исключений, являющих- ся подклассом этого класса.
Пример переименования файла с обработкой исключений: import os # Подключаем модуль try: os.rename(r"file3.txt", "file4.txt") except OSError: print("Файл не удалось переименовать") else: print("Файл успешно переименован")
 remove(<Путь к файлу>)
и unlink(<Путь к файлу>)
— позволяют удалить файл. Если файл не удалось удалить, возбуждается исключение
OSError или одно из исключений, являющихся подклассом этого класса:
>>> os.remove(r"file2.txt")
>>> os.unlink(r"file4.txt")
Модуль os.path содержит дополнительные функции, позволяющие проверить наличие файла, получить размер файла и др. Опишем эти функции:
 exists(<Путь или дескриптор>)
— проверяет указанный путь на существование. В ка- честве параметра можно передать путь к файлу или целочисленный дескриптор откры- того файла, возвращенный функцией open()
из того же модуля os
. Возвращает
True
, если путь существует, и
False
— в противном случае:
>>> import os.path
>>> os.path.exists(r"file.txt"), os.path.exists(r"file2.txt")
(True, False)
>>> os.path.exists(r"C:\book"), os.path.exists(r"C:\book2")
(True, False)
 getsize(<Путь к файлу>)
— возвращает размер файла в байтах. Если файл не существу- ет, возбуждается исключение
OSError
:
>>> os.path.getsize(r"file.txt") # Файл существует
18

Глава 16. Работа с файлами и каталогами
309
>>> os.path.getsize(r"file2.txt") # Файл не существует
... Фрагмент опущен ...
OSError: [Error 2] Не удается найти указанный файл: 'file2.txt'
 getatime(<Путь к файлу>)
— возвращает время последнего доступа к файлу в виде ко- личества секунд, прошедших с начала эпохи. Если файл не существует, возбуждается исключение
OSError
:
>>> import time # Подключаем модуль time
>>> t = os.path.getatime(r"file.txt")
>>> t
1511773416.0529847
>>> time.strftime("%d.%m.%Y %H:%M:%S", time.localtime(t))
'27.11.2017 12:03:36'
 getctime(<Путь к файлу>)
— возвращает дату создания файла в виде количества секунд, прошедших с начала эпохи. Если файл не существует, возбуждается исключение
OSError
:
>>> t = os.path.getctime(r"file.txt")
>>> t
1511773416.0529847
>>> time.strftime("%d.%m.%Y %H:%M:%S", time.localtime(t))
'27.11.2017 12:03:36'
 getmtime(<Путь к файлу>)
— возвращает время последнего изменения файла в виде ко- личества секунд, прошедших с начала эпохи. Если файл не существует, возбуждается исключение
OSError
:
>>> t = os.path.getmtime(r"file.txt")
>>> t
1511773609.980973
>>> time.strftime("%d.%m.%Y %H:%M:%S", time.localtime(t))
'27.11.2017 12:06:49'
Получить размер файла и время создания, изменения и доступа к файлу, а также значения других метаданных, позволяет функция stat()
из модуля os
. В качестве значения функция возвращает объект stat_result
, содержащий десять атрибутов: st_mode
, st_ino
, st_dev
, st_nlink
, st_uid
, st_gid
, st_size
, st_atime
, st_mtime и st_ctime
Пример использования функции stat()
:
>>> import os, time
>>> s = os.stat(r"file.txt")
>>> s os.stat_result(st_mode=33206, st_ino=5910974511035376, st_dev=2086732993, st_nlink=1, st_uid=0, st_gid=0, st_size=15, st_atime=1511773416, st_mtime=1511773609, st_ctime=1511773416)
>>> s.st_size # Размер файла
15
>>> t = s.st_atime # Время последнего доступа к файлу
>>> time.strftime("%d.%m.%Y %H:%M:%S", time.localtime(t))
'27.11.2017 12:03:36'
>>> t = s.st_ctime # Время создания файла
>>> time.strftime("%d.%m.%Y %H:%M:%S", time.localtime(t))
'27.11.2017 12:03:36'

310
Часть I. Основы языка Python
>>> t = s.st_mtime # Время последнего изменения файла
>>> time.strftime("%d.%m.%Y %H:%M:%S", time.localtime(t))
'27.11.2017 12:06:49'
Обновить время последнего доступа и время изменения файла позволяет функция utime()
из модуля os
. Функция имеет два варианта формата: utime(<Путь к файлу или его дескриптор>, None) utime(<Путь к файлу или его дескриптор >, (<Последний доступ>, <Изменение файла>))
В качестве первого параметра можно указать как строковый путь, так и целочисленный дескриптор открытого файла, возвращенный функцией open()
из модуля os
. Если в качестве второго параметра указано значение
None
, то время доступа и изменения файла будет теку- щим. Во втором варианте формата функции utime()
указывается кортеж из новых значений в виде количества секунд, прошедших с начала эпохи. Если файл не существует, возбужда- ется исключение
OSError
Пример использования функции utime()
:
>>> import os, time
>>> os.stat(r"file.txt") # Первоначальные значения os.stat_result(st_mode=33206, st_ino=5910974511035376, st_dev=2086732993, st_nlink=1, st_uid=0, st_gid=0, st_size=15, st_atime=1511773416, st_mtime=1511773609, st_ctime=1511773416)
>>> t = time.time() - 600
>>> os.utime(r"file.txt", (t, t)) # Текущее время минус 600 сек
>>> os.stat(r"file.txt") os.stat_result(st_mode=33206, st_ino=5910974511035376, st_dev=2086732993, st_nlink=1, st_uid=0, st_gid=0, st_size=15, st_atime=1511790710, st_mtime=1511790710, st_ctime=1511773416)
>>> os.utime(r"file.txt", None) # Текущее время
>>> os.stat(r"file.txt") os.stat_result(st_mode=33206, st_ino=5910974511035376, st_dev=2086732993, st_nlink=1, st_uid=0, st_gid=0, st_size=15, st_atime=1511791343, st_mtime=1511791343, st_ctime=1511773416)
16.7. Преобразование пути к файлу или каталогу
Преобразовать путь к файлу или каталогу позволяют следующие функции из модуля os.path
:
 abspath(<Относительный путь>)
— преобразует относительный путь в абсолютный, учи- тывая местоположение текущего рабочего каталога:
>>> import os.path
>>> os.path.abspath(r"file.txt")
'C:\\book\\file.txt'
>>> os.path.abspath(r"folder1/file.txt")
'C:\\book\\folder1\\file.txt'
>>> os.path.abspath(r"../file.txt")
'C:\\file.txt'
Как уже отмечалось ранее, в относительном пути можно указать как прямые, так и об- ратные слэши. Все они будут автоматически преобразованы с учетом значения атрибута

Глава 16. Работа с файлами и каталогами
311 sep из модуля os.path
. Значение этого атрибута зависит от используемой операционной системы. Выведем значение атрибута sep в операционной системе Windows:
>>> os.path.sep
'\\'
При указании пути в Windows следует учитывать, что слэш является специальным сим- волом. По этой причине слэш необходимо удваивать (экранировать) или вместо обыч- ных строк использовать неформатированные строки:
>>> "C:\\temp\\new\\file.txt" # Правильно 'C:\\temp\\new\\file.txt'
>>> r"C:\temp\new\file.txt" # Правильно 'C:\\temp\\new\\file.txt'
>>> "C:\temp\new\file.txt" # Неправильно!!!
'C:\temp\new\x0cile.txt'
Кроме того, если слэш расположен в конце строки, то его необходимо удваивать даже при использовании неформатированных строк:
>>> r"C:\temp\new\" # Неправильно!!!
SyntaxError: EOL while scanning string literal
>>> r"C:\temp\new\\"
'C:\\temp\\new\\\\'
В первом случае последний слэш экранирует закрывающую кавычку, что приводит к синтаксической ошибке. Решить эту проблему можно, удвоив последний слэш. Однако посмотрите на результат — два слэша превратились в четыре. От одной проблемы ушли, а к другой пришли. Поэтому в данном случае лучше использовать обычные строки:
>>> "C:\\temp\\new\\" # Правильно 'C:\\temp\\new\\'
>>> r"C:\temp\new\\"[:-1] # Можно и удалить слэш 'C:\\temp\\new\\'
 isabs(<Путь>)
— возвращает
True
, если путь является абсолютным, и
False
— в против- ном случае:
>>> os.path.isabs(r"C:\book\file.txt")
True
>>> os.path.isabs("file.txt")
False
 basename(<Путь>)
— возвращает имя файла без пути к нему:
>>> os.path.basename(r"C:\book\folder1\file.txt")
'file.txt'
>>> os.path.basename(r"C:\book\folder")
'folder'
>>> os.path.basename("C:\\book\\folder\\")
''
 dirname(<Путь>)
— возвращает путь к папке, где хранится файл:
>>> os.path.dirname(r"C:\book\folder\file.txt")
'C:\\book\\folder'
>>> os.path.dirname(r"C:\book\folder")
'C:\\book'

312
Часть I. Основы языка Python
>>> os.path.dirname("C:\\book\\folder\\")
'C:\\book\\folder'
 split(<Путь>)
— возвращает кортеж из двух элементов: пути к папке, где хранится файл, и имени файла:
>>> os.path.split(r"C:\book\folder\file.txt")
('C:\\book\\folder', 'file.txt')
>>> os.path.split(r"C:\book\folder")
('C:\\book', 'folder')
>>> os.path.split("C:\\book\\folder\\")
('C:\\book\\folder', '')
 splitdrive(<Путь>)
— разделяет путь на имя диска и остальную часть пути. В качестве значения возвращается кортеж из двух элементов:
>>> os.path.splitdrive(r"C:\book\folder\file.txt")
('C:', '\\book\\folder\\file.txt')
 splitext(<Путь>)
— возвращает кортеж из двух элементов: пути с именем файла, но без расширения, и расширения файла (фрагмент после последней точки):
>>> os.path.splitext(r"C:\book\folder\file.tar.gz")
('C:\\book\\folder\\file.tar', '.gz')
 join(<Путь1>[, ..., <ПутьN>])
— соединяет указанные элементы пути, при необходи- мости вставляя между ними разделители:
>>> os.path.join("C:\\", "book\\folder", "file.txt")
'C:\\book\\folder\\file.txt'
>>> os.path.join(r"C:\\", "book/folder/", "file.txt")
'C:\\\\book/folder/file.txt'
Обратите внимание на последний пример — в пути используются разные слэши, и в ре- зультате получен некорректный путь. Чтобы этот путь сделать корректным, необходимо воспользоваться функцией normpath()
из того же модуля os.path
:
>>> p = os.path.join(r"C:\\", "book/folder/", "file.txt")
>>> os.path.normpath(p)
'C:\\book\\folder\\file.txt'
16.8. Перенаправление ввода/вывода
При рассмотрении методов для работы с файлами говорилось, что значение, возвращаемое методом fileno()
, всегда будет больше числа 2, т. к. число 0 закреплено за стандартным вводом stdin
, 1 — за стандартным выводом stdout
, а 2 — за стандартным выводом сооб- щений об ошибках stderr
. Все эти потоки имеют некоторое сходство с файловыми объек- тами. Например, потоки stdout и stderr поддерживают метод write()
, предназначенный для вывода сообщений, а поток stdin
— метод readline()
, служащий для получения вво- димых пользователем данных. Если этим потокам присвоить ссылку на объект, поддержи- вающий файловые методы, то можно перенаправить стандартные потоки в соответствую- щий файл. Для примера так и сделаем:
>>> import sys # Подключаем модуль sys
>>> tmp_out = sys.stdout # Сохраняем ссылку на sys.stdout

Глава 16. Работа с файлами и каталогами
313
>>> f = open(r"file.txt", "a") # Открываем файл на дозапись
>>> sys.stdout = f # Перенаправляем вывод в файл
>>> print("Пишем строку в файл")
>>> sys.stdout = tmp_out # Восстанавливаем стандартный вывод
>>> print("Пишем строку в стандартный вывод")
Пишем строку в стандартный вывод
>>> f.close() # Закрываем файл
В этом примере мы вначале сохранили ссылку на стандартный вывод в переменной tmp_out
С помощью этой переменной можно в дальнейшем восстановить вывод в стандартный поток.
Функция print()
напрямую поддерживает перенаправление вывода. Для этого использует- ся параметр file
, который по умолчанию ссылается на стандартный поток вывода. Напри- мер, записать строку в файл можно так:
>>> f = open(r"file.txt", "a")
>>> print("Пишем строку в файл", file=f)
>>> f.close()
Параметр flush позволяет указать, когда следует выполнять непосредственное сохранение данных из промежуточного буфера в файле. Если его значение равно
False
(это, кстати, значение по умолчанию), сохранение будет выполнено лишь после закрытия файла или по- сле вызова метода flush()
. Чтобы указать интерпретатору Python выполнять сохранение после каждого вызова функции print()
, следует присвоить этому параметру значение
True
:
>>> f = open(r"file.txt", "a")
>>> print("Пишем строку в файл", file = f, flush = True)
>>> print("Пишем другую строку в файл", file = f, flush = True)
>>> f.close()
Стандартный ввод stdin также можно перенаправить. В этом случае функция input()
будет читать одну строку из файла при каждом вызове. При достижении конца файла возбуждает- ся исключение
EOFError
. Для примера выведем содержимое файла с помощью перенаправ- ления потока ввода (листинг 16.4).
Листинг 16.4. Перенаправление потока ввода
# -*- coding: utf-8 -*- import sys tmp_in = sys.stdin # Сохраняем ссылку на sys.stdin f = open(r"file.txt", "r") # Открываем файл на чтение sys.stdin = f # Перенаправляем ввод while True: try: line = input() # Считываем строку из файла print(line) # Выводим строку except EOFError: # Если достигнут конец файла, break # выходим из цикла sys.stdin = tmp_in # Восстанавливаем стандартный ввод f.close() # Закрываем файл input()

314
Часть I. Основы языка Python
Если необходимо узнать, ссылается ли стандартный ввод на терминал или нет, можно вос- пользоваться методом isatty()
. Метод возвращает
True
, если объект ссылается на терми- нал, и
False
— в противном случае:
>>> tmp_in = sys.stdin # Сохраняем ссылку на sys.stdin
>>> f = open(r"file.txt", "r")
>>> sys.stdin = f # Перенаправляем ввод
>>> sys.stdin.isatty() # Не ссылается на терминал
False
>>> sys.stdin = tmp_in # Восстанавливаем стандартный ввод
>>> sys.stdin.isatty() # Ссылается на терминал
True
>>> f.close() # Закрываем файл
Перенаправить стандартный ввод/вывод можно также с помощью командной строки. Для примера создадим в папке
C:\book файл test3.py с кодом, приведенным в листинге 16.5.
Листинг 16.5. Содержимое файла test3.py
# -*- coding: utf-8 -*- while True: try: line = input() print(line) except EOFError: break
Запускаем командную строку и переходим в папку со скриптом, выполнив команду: cd
C:\book
. Теперь выведем содержимое созданного ранее текстового файла file.txt
(его содер- жимое может быть любым), выполнив команду:
C:\Python36\python.exe test3.py < file.txt
Перенаправить стандартный вывод в файл можно аналогичным образом. Только в этом случае символ
<
необходимо заменить символом
>
. Для примера создадим в папке
C:\book файл test4.py с кодом из листинга 16.6.
Листинг 16.6. Содержимое файла test4.py
# -*- coding: utf-8 -*- print("String") # Эта строка будет записана в файл
Теперь перенаправим вывод в файл file.txt
, выполнив команду:
C:\Python36\python.exe test4.py > file.txt
В этом режиме файл file.txt будет перезаписан. Если необходимо добавить результат в конец файла, следует использовать символы
>>
. Вот пример дозаписи в файл:
C:\Python36\python.exe test4.py >> file.txt
С помощью стандартного вывода stdout можно создать индикатор выполнения процесса непосредственно в окне консоли. Чтобы реализовать такой индикатор, нужно вспомнить, что

Глава 16. Работа с файлами и каталогами
315 символ перевода строки в Windows состоит из двух символов:
\r
(перевод каретки) и
\n
(перевод строки). Таким образом, используя только символ перевода каретки
\r
, можно перемещаться в начало строки и перезаписывать ранее выведенную информацию. Рассмот- рим вывод индикатора процесса на примере (листинг 16.7).
Листинг 16.7. Индикатор выполнения процесса
# -*- coding: utf-8 -*- import sys, time for i in range(5, 101, 5): sys.stdout.write("\r ... %s%%" % i) # Обновляем индикатор sys.stdout.flush() # Сбрасываем содержимое буфера time.sleep(1) # Засыпаем на 1 секунду sys.stdout.write("\rПроцесс завершен\n") input()
Сохраним код в файл и запустим его с помощью двойного щелчка. В окне консоли записи будут заменять друг друга на одной строке каждую секунду. Так как данные перед выводом будут помещаться в буфер, мы сбрасываем их на диск явным образом с помощью метода flush()
16.9. Сохранение объектов в файл
Сохранить объекты в файл и в дальнейшем восстановить объекты из файла позволяют модули pickle и shelve
. Модуль pickle предоставляет следующие функции:
 dump(<Объект>, <Файл>[, <Протокол>][, fix_imports=True])
— производит сериализа- цию объекта и записывает данные в указанный файл. В параметре
<Файл>
указывается файловый объект, открытый на запись в бинарном режиме.
Пример сохранения объекта в файл:
>>> import pickle
>>> f = open(r"file.txt", "wb")
>>> obj = ["Строка", (2, 3)]
>>> pickle.dump(obj, f)
>>> f.close()
 load()
— читает данные из файла и преобразует их в объект. Формат функции: load(<Файл>[, fix_imports=True][, encoding="ASCII"]
[, errors="strict"])
В параметре
<Файл>
указывается файловый объект, открытый на чтение в бинарном ре- жиме.
Пример восстановления объекта из файла:
>>> f = open(r"file.txt", "rb")
>>> obj = pickle.load(f)
>>> obj
['Строка', (2, 3)]
>>> f.close()

316
Часть I. Основы языка Python
В один файл можно сохранить сразу несколько объектов, последовательно вызывая функ- цию dump()
. Вот пример сохранения нескольких объектов:
>>> obj1 = ["Строка", (2, 3)]
>>> obj2 = (1, 2)
>>> f = open(r"file.txt", "wb")
>>> pickle.dump(obj1, f) # Сохраняем первый объект
>>> pickle.dump(obj2, f) # Сохраняем второй объект
>>> f.close()
Для восстановления объектов необходимо несколько раз вызвать функцию load()
:
>>> f = open(r"file.txt", "rb")
>>> obj1 = pickle.load(f) # Восстанавливаем первый объект
>>> obj2 = pickle.load(f) # Восстанавливаем второй объект
>>> obj1, obj2
(['Строка', (2, 3)], (1, 2))
>>> f.close()
Сохранить объект в файл можно также с помощью метода dump(<Объект>)
класса
Pickler
Конструктор класса имеет следующий формат:
Pickler(<Файл>[, <Протокол>][, fix_imports=True])
Пример сохранения объекта в файл:
>>> f = open(r"file.txt", "wb")
>>> obj = ["Строка", (2, 3)]
>>> pkl = pickle.Pickler(f)
>>> pkl.dump(obj)
>>> f.close()
Восстановить объект из файла позволяет метод load()
из класса
Unpickler
. Формат конст- руктора класса:
Unpickler(<Файл>[, fix_imports=True][, encoding="ASCII"]
[, errors="strict"])
Пример восстановления объекта из файла:
>>> f = open(r"file.txt", "rb")
>>> obj = pickle.Unpickler(f).load()
>>> obj
['Строка', (2, 3)]
>>> f.close()
Модуль pickle позволяет также преобразовать объект в последовательность байтов и вос- становить объект из таковой. Для этого предназначены две функции:
 dumps(<Объект>[, <Протокол>][, fix_imports=True])
— производит сериализацию объ- екта и возвращает последовательность байтов специального формата. Формат зависит от указанного протокола — числа от
0
до значения pickle.HIGHEST_PROTOCOL
в порядке от более старых к более новым и совершенным. По умолчанию в качестве номера про- токола используется значение: pickle.DEFAULT_PROTOCOL
(
3
).
Пример преобразования списка и кортежа:
>>> obj1 = [1, 2, 3, 4, 5] # Список
>>> obj2 = (6, 7, 8, 9, 10) # Кортеж

Глава 16. Работа с файлами и каталогами
317
>>> pickle.dumps(obj1) b'\x80\x03]q\x00(K\x01K\x02K\x03K\x04K\x05e.'
>>> pickle.dumps(obj2) b'\x80\x03(K\x06K\x07K\x08K\tK\ntq\x00.'
 loads(<Последовательность байтов>[, fix_imports=True][, encoding="ASCII"][, errors="strict"])
— преобразует последовательность байтов специального формата в объект.
Пример восстановления списка и кортежа:
>>> pickle.loads(b'\x80\x03]q\x00(K\x01K\x02K\x03K\x04K\x05e.')
[1, 2, 3, 4, 5]
>>> pickle.loads(b'\x80\x03(K\x06K\x07K\x08K\tK\ntq\x00.')
(6, 7, 8, 9, 10)
Модуль shelve позволяет сохранять объекты под заданным строковым ключом и предос- тавляет интерфейс доступа, сходный со словарями, позволяя тем самым создать нечто, по- добное базе данных. Для сериализации объекта используются возможности модуля pickle
, а для записи получившейся строки по ключу в файл — модуль dbm
. Все эти действия модуль shelve производит самостоятельно.
Открыть файл с набором объектов поможет функция open()
. Функция имеет следующий формат: open(<Путь к файлу>[, flag="c"][, protocol=None][, writeback=False])
В необязательном параметре flag можно указать один из режимов открытия файла:
 r
— только чтение;
 w
— чтение и запись;
 c
— чтение и запись (значение по умолчанию). Если файл не существует, он будет соз- дан;
 n
— чтение и запись. Если файл не существует, он будет создан. Если файл существует, он будет перезаписан.
Функция open()
возвращает объект, с помощью которого производится дальнейшая работа с базой данных. Этот объект имеет следующие методы:
 close()
— закрывает файл с базой данных. Для примера создадим файл и сохраним в нем список и кортеж:
>>> import shelve # Подключаем модуль
>>> db = shelve.open("db1") # Открываем файл
>>> db["obj1"] = [1, 2, 3, 4, 5] # Сохраняем список
>>> db["obj2"] = (6, 7, 8, 9, 10) # Сохраняем кортеж
>>> db["obj1"], db["obj2"] # Вывод значений
([1, 2, 3, 4, 5], (6, 7, 8, 9, 10))
>>> db.close() # Закрываем файл
 keys()
— возвращает объект с ключами;
 values()
— возвращает объект со значениями;
 items()
— возвращает объект-итератор, который на каждой итерации генерирует кор- теж, содержащий ключ и значение:

318
Часть I. Основы языка Python
>>> db = shelve.open("db1")
>>> db.keys(), db.values()
(KeysView(),
ValuesView())
>>> list(db.keys()), list(db.values())
(['obj1', 'obj2'], [[1, 2, 3, 4, 5], (6, 7, 8, 9, 10)])
>>> db.items()
ItemsView()
>>> list(db.items())
[('obj1', [1, 2, 3, 4, 5]), ('obj2', (6, 7, 8, 9, 10))]
>>> db.close()
 get(<Ключ>[, <Значение по умолчанию>])
— если ключ присутствует, метод возвращает значение, соответствующее этому ключу. Если ключ отсутствует, возвращается значе- ние
None или значение, указанное во втором параметре;
 setdefault(<Ключ>[, <Значение по умолчанию>])
— если ключ присутствует, метод воз- вращает значение, соответствующее этому ключу. Если ключ отсутствует, создается новый элемент со значением, указанным во втором параметре, и в качестве результата возвращается это значение. Если второй параметр не указан, значением нового элемента будет
None
;
 pop(<Ключ>[, <Значение по умолчанию>])
— удаляет элемент с указанным ключом и возвращает его значение. Если ключ отсутствует, возвращается значение из второго параметра. Если ключ отсутствует, и второй параметр не указан, возбуждается исключе- ние
KeyError
;
 popitem()
— удаляет произвольный элемент и возвращает кортеж из ключа и значения.
Если файл пустой, возбуждается исключение
KeyError
;
 clear()
— удаляет все элементы. Метод ничего не возвращает в качестве значения;
 update()
— добавляет элементы. Метод изменяет текущий объект и ничего не возвра- щает. Если элемент с указанным ключом уже присутствует, то его значение будет пере- записано. Форматы метода: update(<Ключ1>=<Значение1>[, ..., <КлючN>=<ЗначениеN>]) update(<Словарь>) update(<Список кортежей с двумя элементами>) update(<Список списков с двумя элементами>)
Помимо этих методов можно воспользоваться функцией len()
для получения количества элементов и оператором del для удаления определенного элемента, а также операторами in и not in для проверки существования или несуществования ключа:
>>> db = shelve.open("db1")
>>> len(db) # Количество элементов
2
>>> "obj1" in db
True
>>> del db["obj1"] # Удаление элемента
>>> "obj1" in db
False
>>> "obj1" not in db
True
>>> db.close()

Глава 16. Работа с файлами и каталогами
319 16.10. Функции для работы с каталогами
Для работы с каталогами используются следующие функции из модуля os
:
 getcwd()
— возвращает текущий рабочий каталог. От значения, возвращаемого этой функцией, зависит преобразование относительного пути в абсолютный. Кроме того, важно помнить, что текущим рабочим каталогом будет каталог, из которого запускается файл, а не каталог с исполняемым файлом:
>>> import os
>>> os.getcwd() # Текущий рабочий каталог 'C:\\book'
 chdir(<Имя каталога>)
— делает указанный каталог текущим:
>>> os.chdir("C:\\book\\folder1\\")
>>> os.getcwd() # Текущий рабочий каталог 'C:\\book\\folder1'
 mkdir(<Имя каталога>[, <Права доступа>])
— создает новый каталог с правами досту- па, указанными во втором параметре. Права доступа задаются восьмеричным числом
(значение по умолчанию
0o777
). Пример создания нового каталога в текущем рабочем каталоге:
>>> os.mkdir("newfolder") # Создание каталога
 rmdir(<Имя каталога>)
— удаляет пустой каталог. Если в каталоге есть файлы или ука- занный каталог не существует, возбуждается исключение — подкласс класса
OSError
Удалим каталог newfolder
:
>>> os.rmdir("newfolder") # Удаление каталога
 listdir(<Путь>)
— возвращает список объектов в указанном каталоге:
>>> os.listdir("C:\\book\\folder1\\")
['file1.txt', 'file2.txt', 'file3.txt', 'folder1', 'folder2']
 walk()
— позволяет обойти дерево каталогов. Формат функции: walk(<Начальный каталог>[, topdown=True][, onerror=None]
[, followlinks=False])
В качестве значения функция walk()
возвращает объект. На каждой итерации через этот объект доступен кортеж из трех элементов: текущего каталога, списка каталогов и спи- ска файлов, находящихся в нем. Если произвести изменения в списке каталогов во время выполнения, это позволит изменить порядок обхода вложенных каталогов.
Необязательный параметр topdown задает последовательность обхода каталогов. Если в качестве значения указано
True
(значение по умолчанию), последовательность обхода будет такой:
>>> for (p, d, f) in os.walk("C:\\book\\folder1\\"): print(p)
C:\book\folder1\
C:\book\folder1\folder1_1
C:\book\folder1\folder1_1\folder1_1_1
C:\book\folder1\folder1_1\folder1_1_2
C:\book\folder1\folder1_2

320
Часть I. Основы языка Python
Если в параметре topdown указано значение
False
, последовательность обхода будет другой:
>>> for (p, d, f) in os.walk("C:\\book\\folder1\\", False): print(p)
C:\book\folder1\folder1_1\folder1_1_1
C:\book\folder1\folder1_1\folder1_1_2
C:\book\folder1\folder1_1
C:\book\folder1\folder1_2
C:\book\folder1\
Благодаря такой последовательности обхода каталогов можно удалить все вложенные файлы и каталоги. Это особенно важно при удалении каталога, т. к. функция rmdir()
по- зволяет удалить только пустой каталог.
Пример очистки дерева каталогов: import os for (p, d, f) in os.walk("C:\\book\\folder1\\", 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(<Путь>[, <Обработка ошибок>[, <Обработчик ошибок>]])
Если в параметре
<Обработка ошибок>
указано значение
True
, ошибки будут проигнори- рованы. Если указано значение
False
(значение по умолчанию), в третьем параметре можно задать ссылку на функцию, которая будет вызываться при возникновении исклю- чения.
Пример удаления дерева каталогов вместе с начальным каталогом: import shutil shutil.rmtree("C:\\book\\folder1\\")
 normcase(<Каталог>)
— преобразует заданный к каталогу путь к виду, подходящему для использования в текущей операционной системе. В Windows преобразует все прямые слэши в обратные. Также во всех системах приводит все буквы пути к нижнему ре- гистру:
>>> from os.path import normcase
>>> normcase(r"c:/BoOk/fIlE.TxT")
'c:\\book\\file.txt'
Как вы уже знаете, функция listdir()
возвращает список объектов в указанном каталоге.
Проверить, на какой тип объекта ссылается элемент этого списка, можно с помощью сле- дующих функций из модуля os.path
:

Глава 16. Работа с файлами и каталогами
321
 isdir(<Объект>)
— возвращает
True
, если объект является каталогом, и
False
— в про- тивном случае:
>>> import os.path
>>> os.path.isdir(r"C:\book\file.txt")
False
>>> os.path.isdir("C:\\book\\")
True
 isfile(<Объект>)
— возвращает
True
, если объект является файлом, и
False
— в про- тивном случае:
>>> os.path.isfile(r"C:\book\file.txt")
True
>>> os.path.isfile("C:\\book\\")
False
 islink(<Объект>)
— возвращает
True
, если объект является символической ссылкой, и
False
— в противном случае. Если символические ссылки не поддерживаются, функция возвращает
False
Функция listdir()
возвращает список всех объектов в указанном каталоге. Если необхо- димо ограничить список определенными критериями, следует воспользоваться функцией glob(<Путь>)
из модуля glob
. Функция glob()
позволяет указать в пути следующие специ- альные символы:

?
— любой одиночный символ;

*
— любое количество символов;

[<Символы>]
— позволяет указать символы, которые должны быть на этом месте в пути.
Можно задать символы или определить их диапазон через дефис.
В качестве значения функция возвращает список путей к объектам, совпадающим с шабло- ном. Вот пример использования функции glob()
:
>>> import os, glob
>>> os.listdir("C:\\book\\folder1\\")
['file.txt', 'file1.txt', 'file2.txt', 'folder1_1', 'folder1_2',
'index.html']
>>> glob.glob("C:\\book\\folder1\\*.txt")
['C:\\book\\folder1\\file.txt', 'C:\\book\\folder1\\file1.txt',
'C:\\book\\folder1\\file2.txt']
>>> glob.glob("C:\\book\\folder1\\*.html") # Абсолютный путь
['C:\\book\\folder1\\index.html']
>>> glob.glob("folder1/*.html") # Относительный путь
['folder1\\index.html']
>>> glob.glob("C:\\book\\folder1\\*[0-9].txt")
['C:\\book\\folder1\\file1.txt', 'C:\\book\\folder1\\file2.txt']
>>> glob.glob("C:\\book\\folder1\\*\\*.html")
['C:\\book\\folder1\\folder1_1\\index.html',
'C:\\book\\folder1\\folder1_2\\test.html']
Обратите внимание на последний пример. Специальные символы могут быть указаны не только в названии файла, но и в именах каталогов в пути. Это позволяет просматривать сра- зу несколько каталогов в поисках объектов, соответствующих шаблону.

322
Часть I. Основы языка Python
16.10.1. Функция scandir()
Начиная с Python 3.5, в модуле os появилась поддержка функции scandir()
— более быст- рого и развитого инструмента для просмотра содержимого каталогов. Формат функции: os.scandir(<Путь>)
<Путь>
можно указать как относительный, так и абсолютный. Если он не задан, будет ис- пользовано строковое значение
(точка), т. е. путь к текущему каталогу.
Функция scandir()
возвращает итератор, на каждом проходе возвращающий очередной элемент — файл или каталог, что присутствует по указанному пути. Этот файл или каталог представляется экземпляром класса
DirEntry
, определенного в том же модуле os
, который хранит всевозможные сведения о файле (каталоге).
Класс
DirEntry поддерживает атрибуты:
 name
— возвращает имя файла (каталога);
 path
— возвращает путь к файлу (каталогу), составленный из пути, что был указан в вы- зове функции scandir()
, и имени файла (каталога), хранящегося в свойстве name
Для примера выведем список путей всех файлов и каталогов, находящихся в текущем ката- логе (при вводе команд в Python Shell текущим станет каталог, где установлен Python):
>>> import os
>>> for entry in os.scandir(): print(entry.name)
.\DLLs
.\Doc
.\include
# Часть вывода пропущена
.\python.exe
.\python3.dll
.\vcruntime140.dll
Видно, что путь, возвращаемый свойством path
, составляется из пути, заданного в вызове функции scandir()
(в нашем случае это используемый по умолчанию путь
), и имени фай- ла (каталога). Теперь попробуем указать путь явно:
>>> for entry in os.scandir("c:\python36"): print(entry.path) c:\python36\DLLs c:\python36\Doc c:\python36\include
# Часть вывода пропущена c:\python36\python.exe c:\python36\python3.dll c:\python36\vcruntime140.dll
Помимо описанных ранее атрибутов, класс
DirEntry поддерживает следующие методы:
 is_file(follow_symlinks=True)
— возвращает
True
, если текущий элемент — файл, и
False в противном случае. Если элемент представляет собой символическую ссылку, и для параметра follow_symlinks указано значение
True
(или если параметр вообще

Глава 16. Работа с файлами и каталогами
323 опущен), проверяется элемент, на который указывает эта символическая ссылка. Если же для параметра follow_symlinks задано значение
False
, всегда возвращается
False
;
 is_dir(follow_symlinks=True)
— возвращает
True
, если текущий элемент — каталог, и
False в противном случае. Если элемент представляет собой символическую ссылку, и для параметра follow_symlinks указано значение
True
(или если параметр вообще опущен), проверяется элемент, на который указывает эта символическая ссылка. Если же для параметра follow_symlinks задано значение
False
, всегда возвращается
False
;
 is_symlink()
— возвращает
True
, если текущий элемент — символическая ссылка, и
False в противном случае;
 stat(follow_symlinks=True)
— возвращает объект stat_result
, хранящий сведения о файле (более подробно он был описан в разд. 16.6). Если элемент представляет собой символическую ссылку, и для параметра follow_symlinks указано значение
True
(или если параметр вообще опущен), возвращаются сведения об элементе, на который указы- вает эта символическая ссылка. Если же для параметра follow_symlinks задано значение
False
, возвращаются сведения о самой символической ссылке. В Windows атрибуты st_ino
, st_dev и st_nlink объекта stat_result
, возвращенного методом stat()
, всегда хранят
0
, и для получения их значений следует воспользоваться функцией stat()
из мо- дуля os
, описанной в разд. 16.6.
Рассмотрим пару примеров:
 для начала выведем список всех каталогов, что находятся в каталоге, где установлен
Python, разделив их запятыми:
>>> for entry in os.scandir(): if entry.is_dir(): print(entry.name, end=", ")
DLLs, Doc, include, Lib, libs, Scripts, tcl, Tools,
 выведем список всех DLL-файлов, хранящихся в каталоге Windows, без обработки сим- волических ссылок:
>>> for entry in os.scandir("c:\windows"): if entry.is_file(follow_symlinks=False) and entry.name.endswith(".dll"): print(entry.name, end=", ")
В Python 3.6 итератор, возвращаемый функцией scandir()
, получил поддержку протокола менеджеров контекста (см. разд. 16.2). Так что мы можем выполнить просмотр содержимо- го какого-либо пути следующим способом:
>>> with os.scandir() as it: for entry in it: print(entry.name)
В том же Python 3.6 для Windows появилась возможность указывать путь в вызове функции scandir()
в виде объекта bytes
. Однако нужно иметь в виду, что в таком случае значения атрибутов name и path класса
DirEntry также будут представлять собой объекты bytes
, а не строки:
>>> with os.scandir(b"c:\python36") as it: for entry in it: print(entry.name)

324
Часть I. Основы языка Python b'DLLs' b'Doc' b'include'
# Часть вывода пропущена b'python.exe' b'python3.dll' b'vcruntime140.dll'
16.11. Исключения, возбуждаемые файловыми операциями
В этой главе неоднократно говорилось, что функции и методы, осуществляющие файловые операции, при возникновении нештатных ситуаций возбуждают исключение класса
OSError или одно из исключений, являющихся его подклассами. Настало время познакомиться с ними.
Исключений-подклассов класса
OSError довольно много. Вот те из них, что затрагивают именно операции с файлами и каталогами:

BlockingIOError
— не удалось заблокировать объект (файл или поток ввода/вывода);

ConnectionError
— ошибка сетевого соединения. Может возникнуть при открытии фай- ла по сети. Является базовым классом для ряда других исключений более высокого уровня, описанных в документации по Python;

FileExistsError
— файл или каталог с заданным именем уже существуют;

FileNotFoundError
— файл или каталог с заданным именем не обнаружены;

InterruptedError
— файловая операция неожиданно прервана по какой-либо причине;

IsADirectoryError
— вместо пути к файлу указан путь к каталогу;

NotADirectoryError
— вместо пути к каталогу указан путь к файлу;

PermissionError
— отсутствуют права на доступ к указанному файлу или каталогу;

TimeoutError
— истекло время, отведенное системой на выполнение операции.
Вот пример кода, обрабатывающего некоторые из указанных исключений: try open("C:\temp\new\file.txt") except FileNotFoundError: print("Файл отсутствует") except IsADirectoryError: print("Это не файл, а каталог") except PermissionError: print("Отсутствуют права на доступ к файлу") except OSError: print("Неустановленная ошибка открытия файла")

ЧАСТЬ
II
Библиотека PyQt 5
Глава 17. Знакомство с PyQt 5
Глава 18. Управление окном приложения
Глава 19. Обработка сигналов и событий
Глава 20. Размещение компонентов в окнах
Глава 21. Основные компоненты
Глава 22. Списки и таблицы
Глава 23. Работа с базами данных
Глава 24. Работа с графикой
Глава 25. Графическая сцена
Глава 26. Диалоговые окна
Глава 27. Создание SDI- и MDI-приложений
Глава 28. Мультимедиа
Глава 29. Печать документов
Глава 30. Взаимодействие с Windows
Глава 31. Сохранение настроек приложений
Глава 32. Приложение «Судоку»


ГЛ А В А
17
Знакомство с PyQt 5
Итак, изучение основ языка Python закончено, и мы можем перейти к рассмотрению биб- лиотеки PyQt, позволяющей разрабатывать приложения с графическим интерфейсом. Пер- вые три главы второй части книги можно считать основными, поскольку в них описывают- ся базовые возможности библиотеки и методы, которые наследуют все компоненты, так что материал этих глав нужно знать обязательно. Остальные главы содержат дополнительный справочный материал. В сопровождающий книгу электронный архив (см. приложение) включен файл
PyQt.doc
, который содержит более 750 дополнительных листингов, пояс- няющих материал второй части книги, — что позволило уменьшить ее объем, поскольку с этими листингами страниц книги было бы вдвое больше, чем всех страниц ее второй части.
17.1. Установка PyQt 5
Библиотека PyQt 5 не входит в комплект поставки Python, и прежде чем начать изучение ее основ, необходимо установить эту библиотеку на компьютер.
В настоящее время установка библиотеки PyQt 5 выполняется исключительно просто. Для этого достаточно запустить командную строку и отдать в ней команду: pip3 install PyQt5
Утилита pip3, поставляемая в составе Python и предназначенная для установки дополни- тельных библиотек, самостоятельно загрузит последнюю версию PyQt 5 и установит ее по пути
<каталог, в котором установлен Python>\lib\site-packages\PyQt5
В
НИМАНИЕ
!
При установке PyQt таким способом устанавливаются только компоненты библиотеки, не- обходимые для запуска программ. Средства разработчика (такие как программа Designer) и дополнительные компоненты, в частности клиентские части серверных СУБД, должны быть установлены отдельно.
Чтобы проверить правильность установки, выведем версии PyQt и Qt:
>>> from PyQt5 import QtCore
>>> QtCore.PYQT_VERSION_STR
'5.9.2'
>>> QtCore.QT_VERSION_STR
'5.9.3'

328
Часть II. Библиотека PyQt 5 17.2. Первая программа
При изучении языков и технологий принято начинать с программы, выводящей надпись
«Привет, мир!». Не станем нарушать традицию и напишем программу (листинг 17.1), соз- дающую окно с приветствием и кнопкой для закрытия окна (рис. 17.1).
Рис. 17.1. Результат выполнения листинга 17.1
Листинг 17.1. Первая программа на PyQt
# -*- coding: utf-8 -*- from PyQt5 import QtWidgets import sys app = QtWidgets.QApplication(sys.argv) window = QtWidgets.QWidget() window.setWindowTitle("Первая программа на PyQt") window.resize(300, 70) label = QtWidgets.QLabel("
Привет, мир!
") btnQuit = QtWidgets.QPushButton("&Закрыть окно") vbox = QtWidgets.QVBoxLayout() vbox.addWidget(label) vbox.addWidget(btnQuit) window.setLayout(vbox) btnQuit.clicked.connect(app.quit) window.show() sys.exit(app.exec_())
Для создания файла с программой можно по-прежнему пользоваться редактором IDLE.
Однако запуск оконного приложения из IDLE нажатием клавиши приводит к очень неприятным артефактам (в частности, при завершении программы ее главное окно остается на экране) и даже аварийному завершению работы редактора. Поэтому запускать оконные приложения следует двойным щелчком на значке файла.
До сих пор мы создавали файлы с расширением py и все результаты выполнения программы выводили в консоль. Оконное приложение также можно сохранить с расширением py
, но тогда при его запуске, помимо основного окна, также будет выведено окно консоли, что, впрочем, на этапе разработки дает возможность вывести отладочную информацию (таким способом мы будем пользоваться в дальнейших примерах). Чтобы избавиться от окна кон- соли, следует сохранять файл с расширением pyw
Попробуйте создать два файла с различными расширениями и запустить двойным щелчком каждый из них.

Глава 17. Знакомство с PyQt 5 329 17.3. Структура PyQt-программы
Запускать программу мы научились. Теперь рассмотрим код из листинга 17.1 построчно.
В первой строке указывается кодировка файла. Поскольку в Python 3 по умолчанию для сохранения исходного кода используется кодировка UTF-8, эту строку можно и не указы- вать. Во второй строке импортируется модуль
QtWidgets
— он содержит классы, реали- зующие компоненты графического интерфейса: окна, надписи, кнопки, текстовые поля и др. В третьей строке импортируется модуль sys
, из которого нам потребуется список па- раметров, переданных в командной строке (
argv
), а также функция exit()
, позволяющая завершить выполнение программы.
Выражение: app = QtWidgets.QApplication(sys.argv) создает объект приложения в виде экземпляра класса
QApplication
. Конструктор этого класса принимает список параметров, переданных в командной строке. Следует помнить, что в программе всегда должен быть объект приложения, причем обязательно только один.
Может показаться, что после создания объекта он в программе больше нигде не использу- ется, однако надо понимать, что с его помощью осуществляется управление приложением незаметно для нас. Получить доступ к этому объекту из любого места в программе можно через атрибут qApp из модуля
QtWidgets
. Например, вывести список параметров, передан- ных в командной строке, можно так: print(QtWidgets.qApp.argv())
Следующее выражение: window = QtWidgets.QWidget() создает объект окна в виде экземпляра класса
QWidget
. Этот класс наследуют практически все классы, реализующие компоненты графического интерфейса. И любой компонент, не имеющий родителя, обладает своим собственным окном.
Выражение: window.setWindowTitle("Первая программа на PyQt") задает текст, который будет выводиться в заголовке окна, для чего используется метод setWindowTitle()
Очередное выражение: window.resize(300, 70) задает минимальные размеры окна. В первом параметре метода resize()
указывается ши- рина окна, а во втором параметре — его высота. При этом надо учитывать, что метод resize()
устанавливает размеры не самого окна, а его клиентской области, при этом разме- ры заголовка и ширина границ окна не учитываются. Также следует помнить, что эти раз- меры являются рекомендацией, — т. е., если компоненты не помещаются в окне, оно будет увеличено.
Выражение: label = QtWidgets.QLabel("
Привет, мир!
") создает объект надписи. Текст надписи задается в качестве параметра в конструкторе класса
QLabel
. Обратите внимание, что внутри строки мы указали HTML-теги, — а именно: с по- мощью тега

произвели выравнивание текста по центру компонента. Возможность

330
Часть II. Библиотека PyQt 5 использования HTML-тегов и CSS-атрибутов является отличительной чертой библиотеки
PyQt — например, внутри надписи можно вывести таблицу или отобразить изображение.
Это очень удобно.
Следующее выражение: btnQuit = QtWidgets.QPushButton("&Закрыть окно") создает объект кнопки. Текст, который будет отображен на кнопке, задается в качестве па- раметра в конструкторе класса
QPushButton
. Обратите внимание на символ
&
перед буквой
З
— таким образом задаются клавиши быстрого доступа. Если нажать одновременно кла- вишу и клавишу с буквой, перед которой в строке указан символ
&
, то кнопка срабо- тает.
Выражение: vbox = QtWidgets.QVBoxLayout() создает вертикальный контейнер. Все компоненты, добавляемые в этот контейнер, будут располагаться по вертикали сверху вниз в порядке добавления, при этом размеры добавлен- ных компонентов будут подогнаны под размеры контейнера. При изменении размеров кон- тейнера будет произведено изменение размеров всех компонентов.
В следующих двух выражениях: vbox.addWidget(label) vbox.addWidget(btnQuit) с помощью метода addWidget()
производится добавление созданных ранее объектов надпи- си и кнопки в вертикальный контейнер. Так как объект надписи добавляется первым, он будет расположен над кнопкой. При добавлении компонентов в контейнер они автомати- чески становятся потомками контейнера.
Новое выражение: window.setLayout(vbox) добавляет контейнер в основное окно с помощью метода setLayout()
. Таким образом, кон- тейнер становится потомком основного окна.
Выражение: btnQuit.clicked.connect(app.quit) назначает обработчик сигнала clicked()
кнопки, который генерируется при ее нажатии.
Этот сигнал доступен через одноименный атрибут класса кнопки и поддерживает метод connect()
, который и назначает для него обработчик, передаваемый первым параметром.
Обработчик представляет собой метод quit()
объекта приложения, выполняющий немед- ленное завершение его работы. Такой метод принято называть слотом.
П
ОЯСНЕНИЕ
Сигналом в PyQt называется особое уведомление, генерируемое при наступлении какого- либо события в приложении: нажатия кнопки, ввода символа в текстовое поле, закрытия окна и пр.
Очередное выражение: window.show() выводит на экран окно и все компоненты, которые мы ранее в него добавили.

Глава 17. Знакомство с PyQt 5 331
И, наконец, последнее выражение: sys.exit(app.exec_()) запускает бесконечный цикл обработки событий в приложении.
Код, расположенный после вызова метода exec_()
, будет выполнен только после заверше- ния работы приложения, — поскольку результат выполнения метода exec_()
мы передаем функции exit()
, дальнейшее выполнение программы будет прекращено, а код возврата передан операционной системе.
17.4. ООП-стиль создания окна
Библиотека PyQt написана в объектно-ориентированном стиле (ООП-стиле) и содержит несколько сотен классов. Иерархия наследования всех классов имеет слишком большой размер, и приводить ее в книге возможности нет. Тем не менее, чтобы показать зависимо- сти, при описании компонентов иерархия наследования конкретного класса будет показы- ваться. В качестве примера выведем базовые классы класса
QWidget
:
>>> from PyQt5 import QtWidgets
>>> QtWidgets.QWidget.__bases__
(, )
Как видно из примера, класс
QWidget наследует два класса:
QObject и
QPaintDevice
. Класс
QObject является классом верхнего уровня, и его в PyQt наследуют большинство классов.
В свою очередь, класс
QWidget является базовым классом для всех визуальных компонентов.
В
НИМАНИЕ
!
В описании каждого класса PyQt приводятся лишь атрибуты, методы, сигналы и слоты, оп- ределенные непосредственно в описываемом классе. Атрибуты, методы, сигналы и слоты базовых классов там не описываются — присутствуют лишь ссылки на соответствующие страницы документации.
В своих программах вы можете наследовать стандартные классы и добавлять новую функ- циональность. В качестве примера переделаем соответствующим образом код из листин- га 17.1 и создадим окно в ООП-стиле (листинг 17.2).
Листинг 17.2. ООП-стиль создания окна
# -*- coding: utf-8 -*- from PyQt5 import QtCore, QtWidgets class MyWindow(QtWidgets.QWidget): def __init__(self, parent=None):
QtWidgets.QWidget.__init__(self, parent) self.label = QtWidgets.QLabel("Привет, мир!") self.label.setAlignment(QtCore.Qt.AlignHCenter) self.btnQuit = QtWidgets.QPushButton("&Закрыть окно") self.vbox = QtWidgets.QVBoxLayout() self.vbox.addWidget(self.label) self.vbox.addWidget(self.btnQuit) self.setLayout(self.vbox) self.btnQuit.clicked.connect(QtWidgets.qApp.quit)

332
Часть II. Библиотека PyQt 5 if __name__ == "__main__": import sys app = QtWidgets.QApplication(sys.argv) window = MyWindow() # Создаем экземпляр класса window.setWindowTitle("ООП-стиль создания окна") window.resize(300, 70) window.show() # Отображаем окно sys.exit(app.exec_()) # Запускаем цикл обработки событий
В первых двух строках кода, как обычно, указывается кодировка файла и импортируются необходимые модули. На этот раз, помимо уже знакомого модуля
QtWidgets
, нам понадо- бится модуль
QtCore
, в котором объявлены атрибуты, задающие, в том числе, и режим вы- равнивания текста в объекте надписи.
Далее мы определяем класс
MyWindow
, который наследует класс
QWidget
: class MyWindow(QtWidgets.QWidget):
Можно наследовать и другие классы, являющиеся наследниками
QWidget
, — например,
QFrame
(окно с рамкой) или
QDialog
(диалоговое окно). При наследовании класса
QDialog окно будет выравниваться по центру экрана (или по центру родительского окна) и иметь в заголовке окна только две кнопки: Справка и Закрыть. Кроме того, можно наследовать класс
QMainWindow
, который представляет главное окно приложения с меню, панелями инст- рументов и строкой состояния. Наследование класса
QMainWindow имеет свои отличия, кото- рые мы рассмотрим в главе 27.
Выражение: def __init__(self, parent=None): определяет конструктор класса. В качестве параметров он принимает ссылки на экземпляр класса (
self
) и на родительский компонент (
parent
). Родительский компонент может отсут- ствовать, поэтому в определении конструктора параметру присваивается значение по умол- чанию (
None
). Внутри метода
__init__()
вызывается конструктор базового класса, и ему передается ссылка на родительский компонент:
QtWidgets.QWidget.__init__(self, parent)
Следующие выражения внутри конструктора создают объекты надписи, кнопки и контей- нера, затем добавляют компоненты в контейнер, а сам контейнер — в основное окно. Сле- дует обратить внимание на то, что объекты надписи и кнопки сохраняются в атрибутах экземпляра класса. В дальнейшем из методов класса можно управлять этими объектами — например, изменять текст надписи. Если объекты не сохранить, то получить к ним доступ будет не так просто.
В предыдущем примере (см. листинг 17.1) мы выравнивали надпись с помощью HTML- тегов. Однако выравнивание можно задать и вызовом метода setAlignment()
, которому следует передать атрибут
AlignHCenter из модуля
QtCore
: self.label.setAlignment(QtCore.Qt.AlignHCenter)
В выражении, назначающем обработчик сигнала: self.btnQuit.clicked.connect(QtWidgets.qApp.quit) мы получаем доступ к объекту приложения через рассмотренный ранее атрибут qApp моду- ля
QtWidgets

Глава 17. Знакомство с PyQt 5 333
Создание объекта приложения и экземпляра класса
MyWindow производится внутри условия: if __name__ == "__main__":
Если вы внимательно читали первую часть книги, то уже знаете, что атрибут модуля
__name__
будет содержать значение
__main__
только в случае запуска модуля как главной программы. Если модуль импортировать, этот атрибут будет содержать другое значение.
Поэтому весь последующий код создания объекта приложения и объекта окна выполняется только при запуске программы двойным щелчком на значке файла. Может возникнуть во- прос, зачем это нужно? Дело в том, что одним из преимуществ ООП-стиля программирова- ния является повторное использование кода. Следовательно, можно импортировать модуль и использовать класс
MyWindow в другом приложении.
Рассмотрим эту возможность на примере, для чего сохраним код из листинга 17.2 в файле с именем
MyWindow.py
, а затем создадим в той же папке еще один файл (например, с име- нем test.pyw
) и вставим в него код из листинга 17.3.
Листинг 17.3. Повторное использование кода при ООП-стиле
# -*- coding: utf-8 -*- from PyQt5 import QtCore, QtWidgets import MyWindow class MyDialog(QtWidgets.QDialog): def __init__(self, parent=None):
QtWidgets.QDialog.__init__(self, parent) self.myWidget = MyWindow.MyWindow() self.myWidget.vbox.setContentsMargins(0, 0, 0, 0) self.button = QtWidgets.QPushButton("&Изменить надпись") mainBox = QtWidgets.QVBoxLayout() mainBox.addWidget(self.myWidget) mainBox.addWidget(self.button) self.setLayout(mainBox) self.button.clicked.connect(self.on_clicked) def on_clicked(self): self.myWidget.label.setText("Новая надпись") self.button.setDisabled(True) if __name__ == "__main__": import sys app = QtWidgets.QApplication(sys.argv) window = MyDialog() window.setWindowTitle("Преимущество ООП-стиля") window.resize(300, 100) window.show() sys.exit(app.exec_())
Теперь запустим файл test.pyw двойным щелчком — на экране откроется окно с надписью и двумя кнопками (рис. 17.2). По нажатию на кнопку Изменить надпись производится изме-

334
Часть II. Библиотека PyQt 5
Рис. 17.2. Результат выполнения листинга 17.3 нение текста надписи, и кнопка делается неактивной. Нажатие кнопки Закрыть окно будет по-прежнему завершать выполнение приложения.
В этом примере мы создали класс
MyDialog
, который наследует класс
QDialog
. Поэтому при выводе окно автоматически выравнивается по центру экрана, а в заголовке окна выводятся только две кнопки: Справка и Закрыть. Внутри конструктора мы создаем экземпляр клас- са
MyWindow и сохраняем его в атрибуте myWidget
: self.myWidget = MyWindow.MyWindow()
С его помощью позже мы получим доступ ко всем атрибутам класса
MyWindow
. Например, в следующей строке произведем изменение отступа между границами контейнера и грани- цами соседних элементов: self.myWidget.vbox.setContentsMargins(0, 0, 0, 0)
В следующих инструкциях внутри конструктора создаются кнопки и контейнер, затем экземпляр класса
MyWindow и кнопка добавляются в контейнер, а сам контейнер помещается в основное окно.
Выражение: self.button.clicked.connect(self.on_clicked) назначает обработчик нажатия кнопки. В качестве параметра указывается ссылка на метод on_clicked()
, внутри которого производится изменение текста надписи (с помощью метода setText()
), и кнопка делается неактивной (с помощью метода setDisabled()
). Внутри ме- тода on_clicked()
доступен указатель self
, через который можно получить доступ к атри- бутам классов
MyDialog и
MyWindow
Вот так и производится повторное использование ранее написанного кода: мы создаем класс и сохраняем его внутри отдельного модуля, а чтобы протестировать модуль или ис- пользовать его как отдельное приложение, размещаем код создания объекта приложения и объекта окна внутри условия: if __name__ == "__main__":
Тогда при запуске с помощью двойного щелчка на значке файла производится выполнение кода как отдельного приложения. Если модуль импортируется, то создание объекта прило- жения не производится, и мы можем использовать класс в других приложениях. Например, так, как это было сделано в листинге 17.3, или путем наследования класса и добавления или переопределения методов.
В некоторых случаях использование ООП-стиля является обязательным. Например, чтобы обработать нажатие клавиши на клавиатуре, необходимо наследовать какой-либо класс и переопределить в нем метод с предопределенным названием. Какие методы необходимо переопределять, мы рассмотрим при изучении обработки событий.

Глава 17. Знакомство с PyQt 5 335 17.5. Создание окна с помощью программы Qt Designer
Если вы ранее пользовались Visual Studio или Delphi, то вспомните, как с помощью мыши размещали на форме компоненты: щелкали левой кнопкой мыши на нужной кнопке в пане- ли инструментов и перетаскивали компонент на форму, затем с помощью инспектора свойств производили настройку значений некоторых свойств, а остальные свойства получа- ли значения по умолчанию. При этом весь код генерировался автоматически. Произвести аналогичную операцию в PyQt позволяет программа Qt Designer, которая входит в состав этой библиотеки.
К огромному сожалению, в составе последних версий PyQt эта полезная программа отсут- ствует. Однако ее можно установить отдельно в составе программного пакета PyQt 5 Tools, отдав в командной строке команду: pip3 install pyqt5-tools
17.5.1. Создание формы
Запустить Qt Designer можно щелчком на исполняемом файле designer.exe
, который рас- полагается по пути
<путь, по которому установлен Python>\Lib\site-packages\pyqt5-tools
К сожалению, через меню Пуск это сделать не получится.
В окне New Form открывшегося окна (рис. 17.3) выбираем пункт Widget и нажимаем кноп- ку Create — откроется окно с пустой формой, на которую с помощью мыши можно пере- таскивать компоненты из панели Widget Box.
Рис. 17.3. Программа Qt Designer

336
Часть II. Библиотека PyQt 5
В качестве примера добавим на форму надпись и кнопку. Для этого на панели Widget Box в группе Display Widgets щелкнем левой кнопкой мыши на пункте Label и, не отпуская кнопку мыши, перетащим компонент на форму. Затем проделаем аналогичную операцию с компонентом Push Button, находящимся в группе Buttons, и разместим его ниже надписи.
Теперь выделим одновременно надпись и кнопку, щелкнем правой кнопкой мыши на лю- бом компоненте и в контекстном меню выберем пункт Lay out | Lay Out Vertically. Чтобы компоненты занимали всю область формы, щелкнем правой кнопкой мыши на свободном месте формы и в контекстном меню выберем пункт Lay out | Lay Out Horizontally.
Теперь изменим некоторые свойства окна. Для этого в панели Object Inspector (рис. 17.4) выделим первый пункт (Form), перейдем в панель Property Editor, найдем свойство objectName и справа от свойства введем значение
MyForm
. Затем найдем свойство geometry, щелкнем мышью на значке уголка слева, чтобы отобразить скрытые свойства, и зададим ширину равной
300
, а высоту равной
70
(рис. 17.5), — размеры формы автоматически изме- нятся. Указать текст, который будет отображаться в заголовке окна, позволяет свойство windowTitle.
Чтобы изменить свойства надписи, следует выделить компонент с помощью мыши или вы- брать соответствующий ему пункт в панели Object Inspector. Для примера изменим значе- ние свойства text (оно задает текст надписи). После чего найдем свойство alignment, щелк- нем мышью на значке уголка слева, чтобы отобразить скрытые свойства, и укажем для свойства Horizontal значение
AlignHCenter
. Теперь выделим кнопку и изменим значение свойства objectName на btnQuit
, а в свойстве text укажем текст надписи, которая будет вы- водиться на кнопке. (Кстати, изменить текст надписи или кнопки также можно, выполнив двойной щелчок мышью на компоненте.)
Рис. 17.4. Панель Object Inspector
Рис. 17.5. Панель Property Editor

Глава 17. Знакомство с PyQt 5 337
Закончив, выберем в меню File пункт Save и сохраним готовую форму в файл
MyForm.ui
При необходимости внести в этот файл какие-либо изменения, его можно открыть в про- грамме Qt Designer, выбрав в меню File пункт Open.
17.5.2. Использование UI-файла в программе
Как вы можете убедиться, внутри UI-файла содержится текст в XML-формате, а не про- граммный код на языке Python. Следовательно, подключить файл с помощью инструкции import не получится. Чтобы использовать UI-файл внутри программы, следует воспользо- ваться модулем uic
, который входит в состав библиотеки PyQt. Прежде чем использовать функции из этого модуля, необходимо подключить модуль с помощью инструкции: from PyQt5 import uic
Для загрузки UI-файла предназначена функция loadUi()
. Формат функции: loadUi(
1   ...   27   28   29   30   31   32   33   34   ...   83


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