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

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


Скачать 7.92 Mb.
НазваниеНиколай Прохоренок Владимир Дронов
Дата05.05.2023
Размер7.92 Mb.
Формат файлаpdf
Имя файлаПрохоренок_Н_А__Дронов_В_А_Python_3_и_PyQt_5_Разработка_приложен.pdf
ТипДокументы
#1111379
страница53 из 83
1   ...   49   50   51   52   53   54   55   56   ...   83
) запрос переходит в активное состояние. Это значит, что выполнить любую другую SQL-команду с его по- мощью невозможно.
Метод isActive()
класса
QSqlQuery возвращает
True
, если запрос находится в активном со- стоянии. Если же запрос неактивен, метод возвращает
False
Если один и тот же экземпляр класса
QSqlQuery планируется использовать для выполнения нескольких SQL-команд, перед выполнением новой команды следует сбросить его, переве- дя тем самым в неактивное состояние и освободив занимаемые им системные ресурсы. Это выполняется вызовом метода clear()
: query = QtSql.QSqlQuery() query.exec("select * from good order by goodname")
# Обрабатываем результат запроса query.clear() query.exec("select count(*) as cnt from good")
# Работаем с новым запросом
23.3.4. Получение служебных сведений о запросе
Класс
QSqlQuery позволяет также получить всевозможные служебные сведения о запросе.
Для этого применяются следующие методы:
 numRowsAffected()
— возвращает количество записей, обработанных в процессе выпол- нения запроса, или
-1
, если это количество не удается определить. Для запросов выбор- ки данных возвращает
None
— в этом случае следует вызывать метод size()
;
 lastInsertId()
— возвращает идентификатор последней добавленной записи. Если за- прос не добавлял записи, или если формат базы данных не позволяет определить иден- тификатор последней добавленной записи, возвращает
None
;
 lastError()
— возвращает экземпляр объекта
QSqlError
, описывающий последнюю возникшую в базе данных ошибку;
 executedQuery()
— возвращает SQL-код последнего выполненного запроса или пустую строку, если никакой запрос еще не был выполнен;
 lastQuery()
— возвращает код последнего выполненного запроса или пустую строку, если никакой запрос еще не был выполнен. Отличается от метода executedQuery()
тем, что все именованные параметры (заданные символьными обозначениями) в возвращае- мом SQL-коде заменяются вопросительными знаками;
 boundValue(<Номер параметра>)
— возвращает значение параметра запроса с указанным номером;
 boundValue(<Обозначение параметра>)
— возвращает значение параметра запроса с ука- занным символьным обозначением;
 boundValues()
— возвращает словарь, ключами элементов которого служат символьные обозначения параметров, а значениями элементов — значения этих параметров. Если параметры обозначены вопросительными знаками, в качестве ключей используются произвольные строки вида
:a для первого параметра,
:bb для второго и т. д.

544
Часть II. Библиотека PyQt 5 23.4. Модели, связанные с данными
Очень часто данные, хранящиеся в базе, выводятся на экран с применением таких компо- нентов, как списки или таблицы (подробно списки и таблицы описаны в главе 22). Для этих случаев PyQt предоставляет два класса-модели, извлекающие данные напрямую из базы.
23.4.1. Модель, связанная с SQL-запросом
Если требуется вывести на экран данные, извлеченные в результате выполнения SQL- запроса, и эти данные не требуется редактировать, имеет смысл использовать класс
QSqlQueryModel
. Он представляет модель, связанную с SQL-запросом. Иерархия наследова- ния этого класса:
QObject – QAbstractItemModel – QAbstractTableModel – QSqlQueryModel
Конструктор класса:
<Объект> = QSqlQueryModel([parent=None])
Класс
QSqlQueryModel поддерживает следующие методы (здесь приведен их сокращенный список, а полный список методов этого класса доступен на страницах https://doc.qt.io/ qt-5/qsqlquerymodel.html и https://doc.qt.io/qt-5/qabstractitemmodel.html):
 setQuery(<Код запроса>[, db=QSqlDatabase()])
— задает код запроса для модели. Не- обязательный параметр db задает соединение с базой данных, запрос к которой следует выполнить, — если он не указан, будет использоваться соединение по умолчанию;
 query()
— возвращает код запроса, заданного для модели;
 record()
— возвращает экземпляр класса
QSqlRecord
, представляющий сведения о струк- туре результата запроса;
 record(<Индекс строки>)
— возвращает экземпляр класса
QSqlRecord
, представляющий сведения о записи, которая соответствует строке модели с указанным индексом;
 lastError()
— возвращает экземпляр объекта
QSqlError
, описывающий последнюю возникшую в базе данных ошибку;
 index(<Строка>, <Столбец>[, parent=QModelIndex()])
— возвращает индекс (экземпляр класса
QModelIndex
) элемента модели, находящегося на пересечении строки и столбца с указанными индексами. Необязательный параметр parent позволяет задать элемент верхнего уровня для искомого элемента — если таковой не задан, будет выполнен поиск элемента на самом верхнем уровне иерархии;
 data([, role=DisplayRole])
— возвращает данные, хранимые в указанной в параметре role роли элемента, на который ссылается индекс

;
 rowCount([parent=QModelIndex()])
— возвращает количество элементов в модели. Не- обязательный параметр parent указывает элемент верхнего уровня, при этом будет воз- вращено количество вложенных в него элементов. Если параметр не задан, возвращается количество элементов верхнего уровня иерархии;
 sort(<Индекс столбца>[, order=AscendingOrder])
— производит сортировку. Если во втором параметре указан атрибут
AscendingOrder класса
QtCore.Qt
, сортировка произво- дится в прямом порядке, а если
DescendingOrder
— в обратном;
 setHeaderData(<Индекс>, <Ориентация>, <Значение>[, role=EditRole])
— задает зна- чение для указанной роли заголовка. В первом параметре указывается индекс строки или

Глава 23. Работа с базами данных
545 столбца, а во втором — ориентация (атрибут
Horizontal или
Vertical класса
QtCore.Qt
).
Метод возвращает значение
True
, если операция успешно выполнена;
 headerData(<Индекс>, <Ориентация>[, role=DisplayRole])
— возвращает значение, со- ответствующее указанной роли заголовка. В первом параметре указывается индекс стро- ки или столбца, а во втором — ориентация.
Рассмотрим пример, выводящий данные из созданной нами ранее базы с помощью компо- нента таблицы (листинг 23.8).
Листинг 23.8. Использование модели, привязанной к SQL-запросу from PyQt5 import QtCore, QtWidgets, QtSql import sys app = QtWidgets.QApplication(sys.argv) window = QtWidgets.QTableView() window.setWindowTitle("QSqlQueryModel")
# Устанавливаем соединение с базой данных con = QtSql.QSqlDatabase.addDatabase('QSQLITE') con.setDatabaseName('data.sqlite') con.open()
# Создаем модель sqm = QtSql.QSqlQueryModel(parent=window) sqm.setQuery('select * from good order by goodname')
# Задаем заголовки для столбцов модели sqm.setHeaderData(1, QtCore.Qt.Horizontal, 'Название') sqm.setHeaderData(2, QtCore.Qt.Horizontal, 'Кол-во')
# Задаем для таблицы только что созданную модель window.setModel(sqm)
# Скрываем первый столбец, в котором выводится идентификатор window.hideColumn(0) window.setColumnWidth(1, 150) window.setColumnWidth(2, 60) window.resize(260, 160) window.show() sys.exit(app.exec_())
23.4.2. Модель, связанная с таблицей
Если необходимо дать пользователю возможность редактировать данные, хранящиеся в базе, следует использовать класс
QSqlTableModel
. Он представляет модель, связанную не- посредственно с указанной таблицей базы данных. Иерархия наследования:
QObject – QAbstractItemModel – QAbstractTableModel – QSqlQueryModel –
QSqlTableModel
Конструктор класса:
<Объект> = QSqlTableModel([parent=None][, db=QSqlDatabase()])
Необязательный параметр db задает соединение с базой данных, запрос к которой следует выполнить, — если он не указан, будет использоваться соединение по умолчанию.

546
Часть II. Библиотека PyQt 5
Класс
QSqlTableModel наследует все методы из класса
QSqlQueryModel
(см. разд. 23.4.1) и в дополнение к ним определяет следующие наиболее полезные для нас методы (полный их список приведен на странице https://doc.qt.io/qt-5/qsqltablemodel.html):
 setTable(<Имя таблицы>)
— задает таблицу, данные из которой будут представлены в модели. Отметим, что этот метод лишь выполняет получение из базы данных структу- ры указанной таблицы, но не загружает сами эти данные;
 tableName()
— возвращает имя таблицы, заданной для модели;
 setSort(<Индекс столбца>, <Порядок сортировки>)
— задает сортировку данных. Если во втором параметре указан атрибут
AscendingOrder класса
QtCore.Qt
, сортировка про- изводится в прямом порядке, а если
DescendingOrder
— в обратном;
 setFilter(<Условие фильтрации>)
— задает условие для фильтрации данных в виде строки в том формате, который применяется в SQL-команде
WHERE
;
 filter()
— возвращает строку с фильтром, заданным для модели;
 select()
— считывает в модель данные из заданной ранее таблицы с учетом указанных параметров сортировки и фильтрации. Возвращает
True
, если считывание данных про- шло успешно, и
False
— в противном случае: stm = QtSql.QSqlTableModel(parent=window) stm.setTable('good') stm.setSort(1, QtCore.Qt.DescendingOrder) stm.setFilter('goodcount > 2') stm.select()
Метод является слотом;
 setEditStrategy(<Режим редактирования>)
— указывает режим редактирования данных в модели. В качестве параметра используется один из атрибутов класса
QSqlTableModel
:

OnFieldChange

0
— все изменения переносятся в базу данных немедленно;

OnRowChange

1
— изменения переносятся в базу лишь после того, как пользователь перейдет на другую строку;

OnManualSubmit

2
— изменения переносятся в базу только после вызова метода submit()
или submitAll()
;
 insertRow(<Индекс>[, parent=QModelIndex()])
— вставляет пустую запись в позицию, заданную первым параметром. Возвращает значение
True
, если запись была успешно добавлена, и
False
— в противном случае;
 insertRows(<Индекс>, <Количество>[, parent=QModelIndex()])
— вставляет указанное количество пустых записей в позицию, заданную первым параметром. Возвращает зна- чение
True
, если записи были успешно добавлены, и
False
— в противном случае;
 setData(, <Значение>[, role=EditRole])
— задает значение для роли role поля записи, на которое указывает индекс

. Возвращает значение
True
, если данные были успешно занесены в запись, и
False
— в противном случае;
 removeRow(<Индекс>[, parent=QModelIndex()])
— удаляет запись с указанным индексом.
Возвращает значение
True
, если запись была успешно удалена, и
False
— в противном случае;
 removeRows(<Индекс>, <Количество>[, parent=QModelIndex()])
— удаляет указанное количество записей, начиная с записи с указанным индексом. Возвращает значение
True
, если записи были успешно удалены, и
False
— в противном случае;

Глава 23. Работа с базами данных
547
П
РИМЕЧАНИЕ
Нужно отметить, что после удаления записи вызовом метода removeRow() или removeRows()
в модели останется пустая запись, реально не представляющая никакой записи из таблицы. Чтобы убрать ее, достаточно выполнить повторное считывание данных в модель вызовом метода select().
 insertRecord(<Индекс>, )
— добавляет в модель новую запись в позицию, указанную первым параметром. Если значение первого параметра отрицательное, запись добавляется в конец модели. Добавляемая запись представляется экземпляром объекта
QSqlRecord
, уже заполненным необходимыми данными. Возвращает
True
, если запись была успешно добавлена, и
False
— в противном случае;
 setRecord(<Индекс>, )
— заменяет запись в позиции, указанной первым параметром, новой записью, которая передается вторым параметром в виде экземпляра объекта
QSqlRecord
, уже заполненного необходимыми данными. Возвращает
True
, если запись была успешно изменена, и
False
— в противном случае;
 submit()
— переносит в базу данных изменения, сделанные в текущей записи, если был задан режим редактирования
OnManualSubmit
. Возвращает
True
, если изменения были успешно перенесены, и
False
— в противном случае. Метод является слотом;
 submitAll()
— переносит в базу данных изменения, сделанные во всех записях, если был задан режим редактирования
OnManualSubmit
. Возвращает
True
, если изменения были успешно перенесены, и
False
— в противном случае. Метод является слотом;
 revert()
— отменяет изменения, сделанные в текущей записи, если был задан режим редактирования
OnManualSubmit
. Возвращает
True
, если изменения были успешно отме- нены, и
False
— в противном случае. Метод является слотом;
 revertRow(<Индекс записи>)
— отменяет изменения, сделанные в записи с заданным индексом, если был задан режим редактирования
OnManualSubmit
;
 revertAll()
— отменяет изменения, сделанные во всех записях, если был задан режим редактирования
OnManualSubmit
. Возвращает
True
, если изменения были успешно отме- нены, и
False
— в противном случае. Метод является слотом;
 selectRow(<Индекс строки>)
— обновляет содержимое строки с указанным индексом.
Возвращает
True
, если запись была успешно обновлена, и
False
— в противном случае.
Метод является слотом;
 isDirty()
— возвращает
True
, если запись с указанным индексом (экземп- ляр класса
QModelIndex
) была изменена, но эти изменения еще не были перенесены в ба- зу данных, и
False
— в противном случае;
 isDirty()
— возвращает
True
, если хотя бы одна запись в модели была изменена, но эти изменения еще не были перенесены в базу данных, и
False
— в противном случае;
 fieldIndex(<Имя поля>)
— возвращает индекс поля с указанным именем или
-1
, если такого поля нет;
 primaryKey()
— возвращает сведения о ключевом индексе таблицы, представленные экземпляром класса
QSqlIndex
, или пустой экземпляр этого класса, если таблица не со- держит ключевого индекса.
Методы insertRecord()
и setRecord()
, предназначенные, соответственно, для добавления и изменения записи, принимают в качестве второго параметра экземпляр класса
QSqlRecord

548
Часть II. Библиотека PyQt 5
Чтобы создать этот экземпляр, нам следует знать формат вызова конструктора класса.
Вот он:
<Объект> = QSqlRecord([])
Если в параметре указать экземпляр класса
QSqlRecord
, будет создана его копия. Обычно при создании новой записи здесь указывают значение, возвращенное методом record()
класса
QSqlDatabase
(оно хранит сведения о структуре таблицы и, следовательно, представ- ляет пустую запись), а при правке существующей записи — значение, возвращенное мето- дом record()
, который унаследован классом
QSqlTableModel от класса
QSqlQueryModel
(оно представляет запись, которую нужно отредактировать).
Класс
QSqlRecord
, в дополнение к методам, рассмотренным нами в разд. 23.2.1, поддержи- вает следующие методы:
 value(<Индекс поля>)
— возвращает значение поля текущей записи с заданным индек- сом;
 value(<Имя поля>)
— возвращает значение поля текущей записи с заданным именем;
 setValue(<Индекс поля>, <Значение>)
— заносит в поле с указанным индексом новое значение;
 setValue(<Имя поля>, <Значение>)
— заносит в поле с указанным именем новое значе- ние;
 isNull(<Индекс поля>)
— возвращает
True
, если в поле с указанным индексом нет зна- чения, и
False
— в противном случае;
 isNull(<Имя поля>)
— возвращает
True
, если в поле с указанным именем нет значения, и
False
— в противном случае;
 setNull(<Индекс поля>)
— удаляет значение из поля с указанным индексом;
 setNull(<Имя поля>)
— удаляет значение из поля с указанным именем;
 clearValues()
— удаляет значения из всех полей записи;
 setGenerated(<Индекс поля>, <Флаг>)
— если вторым параметром передано
False
, поле с указанным индексом помечается как неактуальное, и хранящееся в нем значение не будет перенесено в таблицу;
 setGenerated(<Имя поля>, <Флаг>)
— если вторым параметром передано
False
, поле с указанным именем помечается как неактуальное, и хранящееся в нем значение не бу- дет перенесено в таблицу;
 isGenerated(<Индекс поля>)
— возвращает
False
, если поле с указанным индексом по- мечено как неактуальное, и
True
— в противном случае;
 isGenerated(<Имя поля>)
— возвращает
False
, если поле с указанным именем помечено как неактуальное, и
True
— в противном случае.
Вот пример кода, добавляющего новую запись в модель: con = QtSql.QSqlDatabase.addDatabase('QSQLITE') con.setDatabaseName('data.sqlite') con.open() stm = QtSql.QSqlTableModel() stm.setTable('good') stm.select() rec = con.record('good')

Глава 23. Работа с базами данных
549 rec.setValue('goodname', 'Коврик для мыши') rec.setValue('goodcount', 2) stm.insertRecord(-1, rec)
А вот пример кода, редактирующего существующую запись с индексом
3
: rec = stm.record(3) rec.setValue('goodcount', 5) stm.setRecord(3, rec)
Класс
QSqlTableModel поддерживает такие сигналы:
 primeInsert(<Индекс записи>, )
— генерируется перед добавлением запи- си в модель. В первом параметре доступен целочисленный индекс добавляемой записи, а во втором — сама добавляемая запись, обычно пустая, в которую можно занести какие- либо изначальные данные;
 beforeInsert()
— генерируется перед добавлением новой записи в табли- цу. В параметре доступна добавляемая запись;
 beforeUpdate(<Индекс записи>, )
— генерируется перед изменением запи- си в таблице. В параметрах доступны целочисленный индекс изменяемой записи и сама изменяемая запись;
 beforeDelete(<Индекс записи>)
— генерируется перед удалением записи из таблицы.
В параметре доступен целочисленный индекс удаляемой записи;
 dataChanged(, , roles=[])
— генерируется при изменении данных в модели пользователем. Первым параметром передается индекс верхней левой из набора измененных записей, вторым — индекс правой нижней. Необязательный па- раметр roles хранит список ролей, данные которых изменились. Если указан пустой список, значит, изменились данные во всех ролях.
Сигнал dataChanged
— идеальное место для вызова методов submit()
или submitAll() в случае, если для модели был задан режим редактирования
OnManualSubmit
. Как мы зна- ем, эти методы выполняют сохранение отредактированных данных в базе.
В листинге 23.9 представлен код тестового складского приложения, позволяющего не толь- ко править, но и добавлять и удалять записи нажатием соответствующих кнопок. А на рис. 23.1 можно увидеть само это приложение в работе.
Листинг 23.9. Использование модели, привязанной к таблице from PyQt5 import QtCore, QtWidgets, QtSql import sys def addRecord():
# Вставляем пустую запись, в которую пользователь сможет
# ввести нужные данные stm.insertRow(stm.rowCount()) def delRecord():
# Удаляем запись из модели stm.removeRow(tv.currentIndex().row())
# Выполняем повторное считывание данных в модель,
# чтобы убрать пустую "мусорную" запись stm.select()

550
Часть II. Библиотека PyQt 5 app = QtWidgets.QApplication(sys.argv) window = QtWidgets.QWidget() window.setWindowTitle("QSqlTableModel")
# Устанавливаем соединение с базой данных con = QtSql.QSqlDatabase.addDatabase('QSQLITE') con.setDatabaseName('data.sqlite') con.open()
# Создаем модель stm = QtSql.QSqlTableModel(parent=window) stm.setTable('good') stm.setSort(1, QtCore.Qt.AscendingOrder) stm.select()
# Задаем заголовки для столбцов модели stm.setHeaderData(1, QtCore.Qt.Horizontal, 'Название') stm.setHeaderData(2, QtCore.Qt.Horizontal, 'Кол-во')
# Задаем для таблицы только что созданную модель vbox = QtWidgets.QVBoxLayout() tv = QtWidgets.QTableView() tv.setModel(stm)
# Скрываем первый столбец, в котором выводится идентификатор tv.hideColumn(0) tv.setColumnWidth(1, 150) tv.setColumnWidth(2, 60) vbox.addWidget(tv) btnAdd = QtWidgets.QPushButton("&Добавить запись") btnAdd.clicked.connect(addRecord) vbox.addWidget(btnAdd) btnDel = QtWidgets.QPushButton("&Удалить запись") btnDel.clicked.connect(delRecord) vbox.addWidget(btnDel) window.setLayout(vbox) window.resize(300, 250) window.show() sys.exit(app.exec_())
Рис. 23.1. Пример складского приложения, использующего модель
QSqlTableModel

Глава 23. Работа с базами данных
551 23.4.3. Модель, поддерживающая межтабличные связи
Предположим, что мы решили расширить наше простенькое складское приложение, введя разбиение товаров на категории. В базе данных data.sqlite мы создали таблицу category с полями id и catname
, а в таблицу good добавили поле category
, где будут храниться иден- тификаторы категорий.
Теперь попытаемся вывести содержимое таблицы good на экран с помощью модели
QSqlTableModel и компонента таблицы
QTableView
. И сразу увидим, что в колонке, где пока- зывается содержимое поля category
, выводятся числовые идентификаторы категорий
(рис. 23.2). А нам хотелось бы видеть там наименования категорий вместо непонятной ци- фири.
Рис. 23.2. Пример складского приложения после доработки: вместо названий категорий выводятся их числовые идентификаторы
Сделать это поможет класс
QSqlRelationalTableModel
, добавляющий уже известной нам модели
QSqlTableModel возможность связывать таблицы. Мы указываем поле внешнего ключа, первичную таблицу и в ней — поле первичного ключа и поле, откуда будет взято значение для вывода на экран.
Иерархия наследования класса
QSqlRelationalTableModel
:
QObject – QAbstractItemModel – QAbstractTableModel – QSqlQueryModel –
QSqlTableModel - QSqlRelationalTableModel
Конструктор класса:
<Объект> = QSqlRelationalQueryModel([parent=None][, db=QSqlDatabase()])
Необязательный параметр db задает соединение с базой данных, запрос к которой следует выполнить, — если он не указан, будет использоваться соединение по умолчанию.
Класс
QSqlRelationalTableModel наследует все методы класса
QSqlTableModel
(см. разд. 23.4.2) и в дополнение к ним определяет следующие полезные для нас методы (полный их список приведен на странице https://doc.qt.io/qt-5/qsqlrelationaltablemodel.html):
 setRelation(<Индекс столбца>, )
— задает связь для поля с указанным индексом. Сведения об устанавливаемой связи представляются экземпляром класса
QSqlRelation
, о котором мы поговорим чуть позже;

552
Часть II. Библиотека PyQt 5
 setJoinMode(<Режим связывания>)
— задает режим связывания для всей модели. В каче- стве параметра указывается один из атрибутов класса
QSqlRelationalTableModel
:

InnerJoin

0
— каждой записи вторичной таблицы должна соответствовать связан- ная с ней запись первичной таблицы. Используется по умолчанию;

LeftJoin

1
— записи вторичной таблицы не обязательно должна соответствовать связанная запись первичной таблицы.
Теперь о классе
QSqlRelation
. Он представляет связь, устанавливаемую между полями таб- лиц. Конструктор этого класса имеет такой формат:
<Объект> = QSqlRelation(<Имя первичной таблицы>,
<Имя поля первичного ключа>,
<Имя поля, выводящегося на экран>)
Поля, чьи имена указываются во втором и третьем параметрах, относятся к первичной таб- лице.
Класс
QSqlRelation поддерживает несколько методов, но они не очень нам интересны (пол- ное описание этого класса доступно на странице https://doc.qt.io/qt-5/qsqlrelation.html).
В листинге 23.10 приведен код исправленного складского приложения, а на рис. 23.3 пока- зан его интерфейс.
Листинг 23.10. Использование модели QSqlRelationalTableModel from PyQt5 import QtCore, QtWidgets, QtSql import sys def addRecord(): stm.insertRow(stm.rowCount()) def delRecord(): stm.removeRow(tv.currentIndex().row()) stm.select() app = QtWidgets.QApplication(sys.argv) window = QtWidgets.QWidget() window.setWindowTitle("QRelationalSqlTableModel") con = QtSql.QSqlDatabase.addDatabase('QSQLITE') con.setDatabaseName('data.sqlite') con.open() stm = QtSql.QSqlRelationalTableModel(parent=window) stm.setTable('good') stm.setSort(1, QtCore.Qt.AscendingOrder)
# Задаем для поля категории связь с таблицей списка категорий stm.setRelation(3, QtSql.QSqlRelation('category', 'id', 'catname')) stm.select() stm.setHeaderData(1, QtCore.Qt.Horizontal, 'Название') stm.setHeaderData(2, QtCore.Qt.Horizontal, 'Кол-во') stm.setHeaderData(3, QtCore.Qt.Horizontal, 'Категория') vbox = QtWidgets.QVBoxLayout() tv = QtWidgets.QTableView()

Глава 23. Работа с базами данных
553 tv.setModel(stm) tv.hideColumn(0) tv.setColumnWidth(1, 150) tv.setColumnWidth(2, 60) tv.setColumnWidth(3, 150) vbox.addWidget(tv) btnAdd = QtWidgets.QPushButton("&Добавить запись") btnAdd.clicked.connect(addRecord) vbox.addWidget(btnAdd) btnDel = QtWidgets.QPushButton("&Удалить запись") btnDel.clicked.connect(delRecord) vbox.addWidget(btnDel) window.setLayout(vbox) window.resize(420, 250) window.show() sys.exit(app.exec_())
Рис. 23.3. Пример складского приложения, использующего модель
QSqlRelationalTableModel
: на экран выводятся названия категорий
23.4.4. Использование связанных делегатов
К сожалению, наше новое приложение имеет один существеннейший недостаток — как только мы решим добавить новую запись или даже исправить уже существующую, то столкнемся с тем, что все сделанные нами изменения не сохраняются. Почему?
Дело в том, что модель
QSqlRelationalTableModel
«не знает», как перевести введенное нами название категории в ее идентификатор, который и хранится в поле category таблицы good
Она лишь выполняет попытку занести строковое название категории в поле целочисленного типа, что вполне ожидаемо вызывает ошибку, и запись в таблице не сохраняется.
Исправить такое положение дел нам позволит особый делегат, называемый связанным
(о делегатах рассказывалось в разд. 22.8). Он способен выполнить поиск в первичной таб- лице нужной записи, извлечь ее идентификатор и сохранить его в поле вторичной таблицы.
А, кроме того, он представляет все доступные для занесения в поле значения, взятые из первичной таблицы, в виде раскрывающегося списка — очень удобно!

554
Часть II. Библиотека PyQt 5
Функциональность связанного делегата реализует класс
QSqlRelationalDelegate
. Иерархия наследования:
QObject – QAbstractItemDelegate – QItemDelegate - QSqlRelationalDelegate
Использовать связанный делегат очень просто — нужно лишь создать его экземпляр, пере- дав конструктору класса ссылку на компонент-представление (в нашем случае — таблицу), и вызвать у представления метод setItemDelegate()
, setItemDelegateForColumn()
или setItemDelegateForRow()
, указав в нем только что созданный делегат.
Исходя из этого, давайте, наконец, доделаем до конца наше складское приложение, дав пользователю возможность выбирать категории товаров из списка. Для этого нам потребу- ется лишь вставить в код листинга 23.10 всего одно новое выражение (в приведенном далее листинге 23.11 оно выделено полужирным шрифтом):
Листинг 23.11. Использование связанного делегата
(фрагмент исправленного кода из листинга 23.10) tv = QtWidgets.QTableView() tv.setModel(stm) tv.setItemDelegateForColumn(3, QtSql.QSqlRelationalDelegate(tv)) tv.hideColumn(0)
Интерфейс законченного приложения показан на рис. 23.4.
Рис. 23.4. Окончательный вариант складского приложения, использующего связанный делегат

ГЛ А В А
24
Работа с графикой
Все компоненты, которые мы рассматривали в предыдущих главах, на самом деле нарисо- ваны. То есть, каждый раз, когда компонент становится видимым (в первый раз, при ото- бражении части компонента, ранее перекрытой другим окном, или после изменения его па- раметров), вызывается метод paintEvent()
(см. разд. 19.7.3). Вызвать событие перерисовки компонента можно и искусственно — с помощью методов repaint()
и update()
класса
QWidget
. Внутри метода paintEvent()
выполняется рисование компонента с помощью мето- дов класса
QPainter
Класс
QPainter поддерживает все необходимые средства, позволяющие выполнять рисова- ние геометрических фигур и вывод текста на поверхности, которая реализуется классом
QPaintDevice
. Класс
QWidget наследует класс
QPaintDevice
. В свою очередь класс
QWidget наследуют все компоненты, поэтому мы можем рисовать на поверхности любого ком- понента. Класс
QPaintDevice наследуют также классы
QPicture
,
QPixmap
,
QImage
,
QPagedPaintDevice и некоторые другие.
Класс
QPicture позволяет сохранить команды рисования в метафайл, а затем считать их из файла и воспроизвести на какой-либо поверхности. Классы
QPixmap и
QImage позволяют об- рабатывать изображения. Основные методы этих классов мы рассмотрим далее в этой главе.
Класс
QPagedPaintDevice является базовым для классов
QPrinter
(позволяет выводить документы на печать) и
QPdfWriter
(используется для экспорта документов в PDF-файлы).
Мы рассмотрим их в главе 29.
Все описанные в этой главе классы объявлены в модуле
QtGui
, если не указано обратное.
Библиотека PyQt 5 также позволяет работать с SVG-графикой и включает в свой состав поддержку технологии OpenGL, предназначенной для обработки двумерной и трехмерной графики. Рассмотрение этих возможностей выходит за рамки нашей книги, поэтому за под- робной информацией о них вам следует обратиться к соответствующей документации.
24.1. Вспомогательные классы
Прежде чем изучать работу с графикой, необходимо рассмотреть несколько вспомогатель- ных классов, с помощью которых производится настройка различных параметров: цвета, характеристик шрифта, стиля пера и кисти. Кроме того, мы рассмотрим классы, описываю- щие геометрические фигуры (например, линию и многоугольник).

556
Часть II. Библиотека PyQt 5 24.1.1. Класс QColor: цвет
Класс
QColor описывает цвет в цветовых моделях RGB, CMYK, HSV или HSL. Форматы конструктора класса
QColor
:
<Объект> = QColor()
<Объект> = QColor(<Красный>, <Зеленый>, <Синий>[, alpha=255])
<Объект> = QColor(<Строка>)
<Объект> = QColor(<Атрибут цвета>)
<Объект> = QColor(<Число>)
<Объект> = QColor()
Первый конструктор создает невалидный объект. Проверить объект на валидность можно с помощью метода isValid()
. Метод возвращает значение
True
, если объект является валидным, и
False
— в противном случае.
Второй конструктор позволяет указать целочисленные значения красной, зеленой и синей составляющих цвета модели RGB. В качестве параметров указываются числа от
0
до
255
Необязательный параметр alpha задает степень прозрачности цвета. Значение
0
соответст- вует прозрачному цвету, а значение
255
— полностью непрозрачному.
Вот пример указания красного цвета: red = QtGui.QColor(255, 0, 0)
В третьем конструкторе цвет указывается в виде строки в форматах "#RGB"
,
"#RRGGBB"
,
"#AARRGGBB"
(здесь
AA
обозначает степень прозрачности цвета),
"#RRRGGGBBB"
,
"#RRRRGGGGBBBB"
,
"Название цвета"
или "transparent"
(для прозрачного цвета): red = QtGui.QColor("#f00") darkBlue = QtGui.QColor("#000080") semiTransparentDarkBlue = QtGui.QColor("#7F000080") white = QtGui.QColor("white")
Получить список всех поддерживаемых названий цветов позволяет статический метод colorNames()
. Проверить правильность строки с названием цвета можно с помощью стати- ческого метода isValidColor(<Строка>)
, который возвращает значение
True
, если строка является правильным наименованием цвета, и
False
— в противном случае: print(QtGui.QColor.colorNames()) # ['aliceblue', 'antiquewhite', ...] print(QtGui.QColor.isValidColor("lightcyan")) # True
В четвертом конструкторе указываются следующие атрибуты из класса
QtCore.Qt
: white
, black
, red
, darkRed
, green
, darkGreen
, blue
, darkBlue
, cyan
, darkCyan
, magenta
, darkMagenta
, yellow
, darkYellow
, gray
, darkGray
, lightGray
, color0
, color1
или transparent
(прозрачный цвет). Атрибуты color0
(прозрачный цвет) и color1
(непрозрачный цвет) используются в двухцветных изображениях: black = QtCore.Qt.black
В пятом конструкторе указывается целочисленное значение цвета, а шестой конструктор создает новый объект на основе указанного в параметре.
Задать или получить значения в цветовой модели RGB (Red, Green, Blue — красный, зеле- ный, синий) позволяют следующие методы:
 setNamedColor(<Строка>)
— задает название цвета в виде строки в форматах "#RGB"
,
"#RRGGBB"
,
"#AARRGGBB"
,
"#RRRGGGBBB"
,
"#RRRRGGGGBBBB"
,
"Название цвета"
или "transparent"
(для прозрачного цвета);

Глава 24. Работа с графикой
557
 name()
— возвращает строковое представление цвета в формате "#RRGGBB"
;
 name(<Формат>)
— возвращает строковое представление цвета в заданном формате.
В качестве формата указывается один из атрибутов класса
QColor
:
HexRgb
(
0
, формат "#RRGGBB"
) или
HexArgb
(
1
, формат "#AARRGGBB"
);
 setRgb(<Красный>, <Зеленый>, <Синий>[, alpha=255])
— задает целочисленные значе- ния красной, зеленой и синей составляющих цвета модели RGB. В качестве параметров указываются числа от
0
до
255
. Необязательный параметр alpha задает степень прозрач- ности цвета: значение
0
соответствует прозрачному цвету, а значение
255
— полностью непрозрачному;
 setRgb(<Число>)
и setRgba(<Число>)
— задают целочисленное значение цвета, второй метод — со степенью прозрачности;
 setRed(<Красный>)
, setGreen(<Зеленый>)
, setBlue(<Синий>)
и setAlpha(<Прозрачность>)
— задают значения отдельных составляющих цвета. В качестве параметров указываются числа от
0
до
255
;
 fromRgb(<Красный>, <Зеленый>, <Синий>[, alpha=255])
— возвращает экземпляр класса
QColor с указанными значениями. В качестве параметров указываются числа от
0
до
255
: white = QtGui.QColor.fromRgb(255, 255, 255, 255)
Метод является статическим;
 fromRgb(<Число>)
и fromRgba(<Число>)
— возвращают экземпляр класса
QColor со значе- ниями, соответствующими целым числам, которые указаны в параметрах: white = QtGui.QColor.fromRgba(4294967295)
Метод является статическим;
 getRgb()
— возвращает кортеж из четырех целочисленных значений
(<Красный>, <Зеле- ный>, <Синий>, <Прозрачность>)
;
 red()
, green()
, blue()
и alpha()
— возвращают целочисленные значения отдельных со- ставляющих цвета;
 rgb()
и rgba()
— возвращают целочисленное значение цвета;
 setRgbF(<Красный>, <Зеленый>, <Синий>[, alpha=1.0])
— задает значения красной, зе- леной и синей составляющих цвета модели RGB. В качестве параметров указываются вещественные числа от
0.0
до
1.0
. Необязательный параметр alpha задает степень про- зрачности цвета: значение
0.0
соответствует прозрачному цвету, а значение
1.0
— пол- ностью непрозрачному;
 setRedF(<Красный>)
, setGreenF(<Зеленый>)
, setBlueF(<Синий>)
и setAlphaF(<Прозрачность>)
— задают значения отдельных составляющих цвета. В качестве параметров указываются вещественные числа от
0.0
до
1.0
;
 fromRgbF(<Красный>, <Зеленый>, <Синий>[, alpha=1.0])
— возвращает экземпляр клас- са
QColor с указанными значениями. В качестве параметров указываются вещественные числа от
0.0
до
1.0
: white = QtGui.QColor.fromRgbF(1.0, 1.0, 1.0, 1.0)
Метод является статическим;
 getRgbF()
— возвращает кортеж из четырех вещественных значений
(<Красный>, <Зеле- ный>, <Синий>, <Прозрачность>)
;

558
Часть II. Библиотека PyQt 5
 redF()
, greenF()
, blueF()
и alphaF()
— возвращают вещественные значения отдельных составляющих цвета;
 lighter([factor=150])
— если параметр имеет значение больше
100
, то возвращает новый объект с более светлым цветом, а если меньше
100
— то с более темным;
 darker([factor=200])
— если параметр имеет значение больше
100
, то возвращает новый объект с более темным цветом, а если меньше
100
— то с более светлым.
Задать или получить значения в цветовой модели CMYK (Cyan, Magenta, Yellow, Key — голубой, пурпурный, желтый, «ключевой», он же черный) позволяют следующие методы:
 setCmyk(<Голубой>, <Пурпурный>, <Желтый>, <Черный>[, alpha=255])
— задает цело- численные значения составляющих цвета модели CMYK. В качестве параметров указы- ваются числа от
0
до
255
. Необязательный параметр alpha задает степень прозрачности цвета: значение
0
соответствует прозрачному цвету, а значение
255
— полностью непро- зрачному;
 fromCmyk(<Голубой>, <Пурпурный>, <Желтый>, <Черный>[, alpha=255])
— возвращает экземпляр класса
QColor с указанными значениями. В качестве параметров указываются числа от
0
до
255
: white = QtGui.QColor.fromCmyk(0, 0, 0, 0, 255)
Метод является статическим;
 getCmyk()
— возвращает кортеж из пяти целочисленных значений
(<Голубой>, <Пурпур- ный>, <Желтый>, <Черный>, <Прозрачность>)
;
 cyan()
, magenta()
, yellow()
, black()
и alpha()
— возвращают целочисленные значения отдельных составляющих цвета;
 setCmykF(<Голубой>, <Пурпурный>, <Желтый>, <Черный>[, alpha=1.0])
— задает значе- ния составляющих цвета модели CMYK. В качестве параметров указываются вещест- венные числа от
0.0
до
1.0
. Необязательный параметр alpha задает степень прозрачно- сти цвета: значение
0.0
соответствует прозрачному цвету, а значение
1.0
— полностью непрозрачному;
 fromCmykF(<Голубой>, <Пурпурный>, <Желтый>, <Черный>[, alpha=1.0])
— возвращает экземпляр класса
QColor с указанными значениями. В качестве параметров указываются вещественные числа от
0.0
до
1.0
: white = QtGui.QColor.fromCmykF(0.0, 0.0, 0.0, 0.0, 1.0)
Метод является статическим;
 getCmykF()
— возвращает кортеж из пяти вещественных значений
(<Голубой>, <Пурпур- ный>, <Желтый>, <Черный>, <Прозрачность>)
;
 cyanF()
, magentaF()
, yellowF()
, blackF()
и alphaF()
— возвращают вещественные зна- чения отдельных составляющих цвета.
Задать или получить значения в цветовой модели HSV (Hue, Saturation, Value — оттенок, насыщенность, значение, она же яркость) позволяют следующие методы:
 setHsv(<Оттенок>, <Насыщенность>, <Значение>[, alpha=255])
— задает целочислен- ные значения составляющих цвета модели HSV. В первом параметре указывается число от
0
до
359
, а в остальных параметрах — числа от
0
до
255
;

Глава 24. Работа с графикой
559
 fromHsv(<Оттенок>, <Насыщенность>, <Значение>[, alpha=255])
— возвращает экземп- ляр класса
QColor с указанными значениями: white = QtGui.QColor.fromHsv(0, 0, 255, 255)
Метод является статическим;
 getHsv()
— возвращает кортеж из четырех целочисленных значений
(<Оттенок>, <Насы- щенность>, <Значение>, <Прозрачность>)
;
 hsvHue()
, hsvSaturation()
, value()
и alpha()
— возвращают целочисленные значения отдельных составляющих цвета;
 setHsvF(<Оттенок>, <Насыщенность>, <Значение>[, alpha=1.0])
— задает значения со- ставляющих цвета модели HSV. В качестве параметров указываются вещественные чис- ла от
0.0
до
1.0
;
 fromHsvF(<Оттенок>, <Насыщенность>, <Значение>[, alpha=1.0])
— возвращает экземп- ляр класса
QColor с указанными значениями. В качестве параметров указываются веще- ственные числа от
0.0
до
1.0
: white = QtGui.QColor.fromHsvF(0.0, 0.0, 1.0, 1.0)
Метод является статическим;
 getHsvF()
— возвращает кортеж из четырех вещественных значений
(<Оттенок>, <Насы- щенность>, <Значение>, <Прозрачность>)
;
 hsvHueF()
, hsvSaturationF()
, valueF()
и alphaF()
— возвращают вещественные значе- ния отдельных составляющих цвета.
Цветовая модель HSL (Hue, Saturation, Lightness — оттенок, насыщенность, яркость) отли- чается от модели HSV только последней составляющей. Описание этой модели и полный перечень методов для установки и получения значений вы найдете в соответствующей документации.
Для получения типа используемой модели и преобразования между моделями предназначе- ны следующие методы:
 spec()
— позволяет узнать тип используемой модели. Возвращает значение одного из следующих атрибутов, определенных в классе
QColor
:
Invalid
(
0
),
Rgb
(
1
),
Hsv
(
2
),
Cmyk
(
3
) или
Hsl
(
4
);
 convertTo(<Тип модели>)
— преобразует тип модели. В качестве параметра указываются атрибуты, которые приведены в описании метода spec()
. Метод возвращает новый объ- ект. Пример преобразования: whiteHSV = QtGui.QColor.fromHsv(0, 0, 255) whiteRGB = whiteHSV.convertTo(QtGui.QColor.Rgb)
Вместо метода convertTo()
удобнее воспользоваться методами toRgb()
, toCmyk()
, toHsv()
или toHsl()
, которые возвращают новый объект: whiteHSV = QtGui.QColor.fromHsv(0, 0, 255) whiteRGB = whiteHSV.toRgb()
24.1.2. Класс QPen: перо
Класс
QPen описывает виртуальное перо, с помощью которого производится рисование то- чек, линий и контуров фигур. Форматы конструктора класса:

560
Часть II. Библиотека PyQt 5
<Объект> = QPen()
<Объект> = QPen()
<Объект> = QPen(<Стиль>)
<Объект> = QPen(, <Ширина>[, style=SolidLine][, cap=SquareCap][, join=BevelJoin])
<Объект> = QPen()
Первый конструктор создает перо черного цвета с настройками по умолчанию. Второй кон- структор задает только цвет пера с помощью экземпляра класса
QColor
. Третий конструктор позволяет указать стиль линии — в качестве значения указываются следующие атрибуты класса
QtCore.Qt
:

NoPen

0
— линия не выводится;

SolidLine

1
— сплошная линия;

DashLine

2
— штриховая линия;

DotLine

3
— точечная линия;

DashDotLine

4
— штрих и точка, штрих и точка и т. д.;

DashDotDotLine

5
— штрих и две точки, штрих и две точки и т. д.;

CustomDashLine

6
— пользовательский стиль.
Четвертый конструктор позволяет задать все характеристики пера за один раз: в первом параметре указывается экземпляр класса
QBrush или
QColor
, ширина линии передается во втором параметре, стиль линии — в необязательном параметре style
, а необязательный параметр cap задает стиль концов линии, где в качестве значения указываются следующие атрибуты класса
QtCore.Qt
:

FlatCap

0
— квадратный конец линии. Длина линии не превышает указанных гранич- ных точек;

SquareCap

16
— квадратный конец линии. Длина линии увеличивается с обоих концов на половину ширины линии;

RoundCap

32
— скругленные концы. Длина линии увеличивается с обоих концов на половину ширины линии.
Необязательный параметр join задает стиль перехода одной линии в другую — в качестве значения указываются следующие атрибуты класса
QtCore.Qt
:

MiterJoin

0
— линии соединяются под острым углом;

BevelJoin

64
— пространство между концами линий заполняется цветом линии;

RoundJoin

128
— скругленные углы;

SvgMiterJoin

256
— линии соединяются под острым углом, как определено в специ- фикации SVG 1.2 Tiny.
Последний конструктор создает новый объект на основе указанного в параметре.
Класс
QPen поддерживает следующие методы (здесь приведены только основные — полный их список можно найти на странице https://doc.qt.io/qt-5/qpen.html):
 setColor()
— задает цвет линии;
 setBrush()
— задает кисть;
 setWidth(<Ширина типа int>)
и setWidthF(<Ширина типа float>)
— задают ширину ли- нии целым числом или числом с плавающей точкой соответственно;

Глава 24. Работа с графикой
561
 setStyle(<Стиль>)
— задает стиль линии (см. значения параметра style в четвертом формате конструктора класса
QPen
);
 setCapStyle(<Стиль>)
— задает стиль концов линии (см. значения параметра cap в чет- вертом формате конструктора класса
QPen
);
 setJoinStyle(<Стиль>)
— задает стиль перехода одной линии в другую (см. значения параметра join в четвертом формате конструктора класса
QPen
).
24.1.3. Класс QBrush: кисть
Класс
QBrush описывает виртуальную кисть, с помощью которой производится заливка фи- гур. Форматы конструктора класса:
<Объект> = QBrush()
<Объект> = QBrush([, style=SolidPattern])
<Объект> = QBrush(<Атрибут цвета>[, style=SolidPattern])
<Объект> = QBrush(<Стиль кисти>)
<Объект> = QBrush()
<Объект> = QBrush(, )
<Объект> = QBrush(<Атрибут цвета>, )
<Объект> = QBrush()
<Объект> = QBrush()
<Объект> = QBrush()
Параметр

задает цвет кисти в виде экземпляра класса
QColor
, а параметр
<Атрибут цвета>
— в виде атрибута класса
QtCore.Qt
(например, black
).
В параметрах
<Стиль кисти>
и style указываются атрибуты класса
QtCore.Qt
, задающие стиль кисти:
NoBrush
,
SolidPattern
,
Dense1Pattern
,
Dense2Pattern
,
Dense3Pattern
,
Dense4Pattern
,
Dense5Pattern
,
Dense6Pattern
,
Dense7Pattern
,
CrossPattern и др. С по- мощью этого параметра можно сделать цвет сплошным (
SolidPattern
) или имеющим тек- стуру (например, атрибут
CrossPattern задает текстуру в виде сетки).
Параметр

позволяет установить градиентную заливку. В качестве значения указываются экземпляры классов, порожденных от класса
QGradient
:
QLinearGradient
(линейный градиент),
QConicalGradient
(конический градиент) или
QRadialGradient
(ра- диальный градиент). За подробной информацией по этим классам обращайтесь к соответст- вующей документации.
Параметры

и

предназначены для установки изображения в качестве тек- стуры, которой будут заливаться рисуемые фигуры.
Класс
QBrush поддерживает следующие полезные для нас методы (полный их список приве- ден на странице https://doc.qt.io/qt-5/qbrush.html):
 setColor()
и setColor(<Атрибут цвета>)
— задают цвет кисти;
 setStyle(<Стиль>)
— задает стиль кисти (см. значения параметра style в конструкторе класса
QBrush
);
 setTexture()
— устанавливает растровое изображение в качестве текстуры.
Можно указать экземпляр класса
QPixmap или
QBitmap
;
 setTextureImage()
— устанавливает изображение в качестве текстуры.

562
Часть II. Библиотека PyQt 5 24.1.4. Класс QLine: линия
Класс
QLine из модуля
QtCore описывает координаты линии. Форматы конструктора класса:
<Объект> = QLine()
<Объект> = QLine(, )
<Объект> = QLine(, , , )
Первый конструктор создает линию, имеющую неустановленные местоположение и разме- ры. Во втором и третьем конструкторах указываются координаты начальной и конечной точек в виде экземпляров класса
QPoint или целочисленных значений через запятую.
Класс
QLine поддерживает следующие основные методы (полный их список приведен на странице https://doc.qt.io/qt-5/qline.html):
 isNull()
— возвращает значение
True
, если начальная или конечная точка не установле- ны, и
False
— в противном случае;
 setPoints(, )
— задает координаты начальной и конечной точек в виде экземпляров класса
QPoint
;
 setLine(, , , )
— задает координаты начальной и конечной точек в виде целочисленных значений через запятую;
 setP1()
— задает координаты начальной точки;
 setP2()
— задает координаты конечной точки;
 p1()
— возвращает координаты (экземпляр класса
QPoint
) начальной точки;
 p2()
— возвращает координаты (экземпляр класса
QPoint
) конечной точки;
 center()
— возвращает координаты (экземпляр класса
QPoint
) центральной точки. Под- держка этого метода появилась в PyQt 5.8;
 x1()
, y1()
, x2()
и y2()
— возвращают значения отдельных составляющих координат начальной и конечной точек в виде целых чисел;
 dx()
— возвращает горизонтальную составляющую вектора линии;
 dy()
— возвращает вертикальную составляющую вектора линии.
П
РИМЕЧАНИЕ
Класс QLine предназначен для работы с целыми числами. Чтобы работать с веществен- ными числами, необходимо использовать класс QLineF.
24.1.5. Класс QPolygon: многоугольник
Класс
QPolygon описывает координаты вершин многоугольника. Форматы конструктора класса:
<Объект> = QPolygon()
<Объект> = QPolygon(<Список с экземплярами класса QPoint>)
<Объект> = QPolygon([, closed=False])
<Объект> = QPolygon(<Количество вершин>)
<Объект> = QPolygon()
Первый конструктор создает пустой объект. Заполнить объект координатами вершин мож- но с помощью оператора
<<
. Вот пример добавления координат вершин треугольника:

Глава 24. Работа с графикой
563 polygon = QtGui.QPolygon() polygon << QtCore.QPoint(20, 50) << QtCore.QPoint(280, 50) polygon << QtCore.QPoint(150, 280)
Во втором конструкторе указывается список с экземплярами класса
QPoint
, которые задают координаты отдельных вершин: polygon = QtGui.QPolygon([QtCore.QPoint(20, 50), QtCore.QPoint(280, 50),
QtCore.QPoint(150, 280)])
Третий конструктор создает многоугольник на основе экземпляра класса
QRect
. Если пара- метр closed имеет значение
False
, то будут созданы четыре вершины, а если значение
True
— то пять вершин.
В четвертом конструкторе можно указать количество вершин, а затем задать координаты путем присваивания значения по индексу: polygon = QtGui.QPolygon(3) polygon[0] = QtCore.QPoint(20, 50) polygon[1] = QtCore.QPoint(280, 50) polygon[2] = QtCore.QPoint(150, 280)
Пятый конструктор создает новый объект на основе другого объекта.
Класс
QPolygon поддерживает следующие методы (здесь приведены только основные — полный их список можно найти на странице https://doc.qt.io/qt-5/qpolygon.html):
 setPoints()
— устанавливает координаты вершин. Ранее установленные значения уда- ляются. Форматы метода: setPoints(<Список с координатами>) setPoints(, [, ..., , ])
Пример указания значений: polygon = QtGui.QPolygon() polygon.setPoints([20,50, 280,50, 150,280])
 prepend()
— добавляет новую вершину в начало объекта;
 append()
— добавляет новую вершину в конец объекта. Добавить вершину можно также с помощью операторов
<<
и
+=
;
 insert(<Индекс>, )
— добавляет новую вершину в указанную позицию;
 setPoint()
— задает координаты для вершины с указанным индексом. Форматы метода: setPoint(<Индекс>, ) setPoint(<Индекс>, , )
Можно также задать координаты путем присваивания значения по индексу: polygon = QtGui.QPolygon(3) polygon.setPoint(0, QtCore.QPoint(20, 50)) polygon.setPoint(1, 280, 50) polygon[2] = QtCore.QPoint(150, 280)
 point(<Индекс>)
— возвращает экземпляр класса
QPoint с координатами вершины, ин- декс которой указан в параметре. Получить значение можно также с помощью операции доступа по индексу:

564
Часть II. Библиотека PyQt 5 polygon = QtGui.QPolygon([20,50, 280,50, 150,280]) print(polygon.point(0)) # PyQt5.QtCore.QPoint(20, 50) print(polygon[1]) # PyQt5.QtCore.QPoint(280, 50)
 remove(<Индекс>[, <Количество>])
— удаляет указанное количество вершин, начиная с индекса
<Индекс>
. Если второй параметр не указан, удаляется одна вершина. Удалить вершину можно также с помощью оператора del по индексу или срезу;
 clear()
— удаляет все вершины;
 size()
и count([])
— возвращают количество вершин. Если в методе count()
указан параметр, возвращается только количество вершин с указанными координатами.
Получить количество вершин можно также с помощью функции len()
;
 isEmpty()
— возвращает значение
True
, если объект пустой (многоугольник не содержит ни одной вершины), и
False
— в противном случае;
 boundingRect()
— возвращает экземпляр класса
QRect с координатами и размерами пря- моугольной области, в которую вписан многоугольник.
П
РИМЕЧАНИЕ
Класс QPolygon предназначен для работы с целыми числами. Чтобы работать с вещест- венными числами, необходимо использовать класс QPolygonF.
24.1.6. Класс QFont: шрифт
Класс
QFont описывает характеристики шрифта. Форматы конструктора класса:
<Объект> = QFont()
<Объект> = QFont(<Название шрифта>[, pointSize=-1][, weight=-1][, italic=False])
<Объект> = QFont()
Первый конструктор создает объект шрифта с настройками, используемыми приложением по умолчанию. Установить шрифт приложения по умолчанию позволяет статический метод setFont()
класса
QApplication
Второй конструктор позволяет указать основные характеристики шрифта. В первом пара- метре указывается название шрифта или семейства в виде строки. Необязательный пара- метр pointSize задает размер шрифта. В параметре weight можно указать степень жирности шрифта: число от
0
до
99
или значение атрибута
Light
(
25
),
Normal
(
50
),
DemiBold
(
63
),
Bold
(
75
) или
Black
(
87
) класса
QFont
. Если в параметре italic указано значение
True
, шрифт будет курсивным.
Третий конструктор создает новый объект на основе другого объекта.
Класс
QFont поддерживает следующие методы (здесь приведены только основные — пол- ный их список можно найти по адресу https://doc.qt.io/qt-5/qfont.html):
 setFamily(<Название шрифта>)
— задает название шрифта или семейства шрифтов;
 family()
— возвращает название шрифта;
 setPointSize(<Размер типа int>)
и setPointSizeF(<Размер типа float>)
— задают размер шрифта в пунктах;
 pointSize()
— возвращает размер шрифта в пунктах в виде целого числа или значение
-1
, если размер шрифта был установлен в пикселах;

Глава 24. Работа с графикой
565
 pointSizeF()
— возвращает размер шрифта в пунктах в виде вещественного числа или значение
-1
, если размер шрифта был установлен в пикселах;
 setPixelSize(<Размер>)
— задает размер шрифта в пикселах;
 pixelSize()
— возвращает размер шрифта в пикселах или
-1
, если размер шрифта был установлен в пунктах;
 setWeight(<Жирность>)
— задает степень жирности шрифта (см. описание параметра weight во втором конструкторе класса
QFont
);
 weight()
— возвращает степень жирности шрифта;
 setBold(<Флаг>)
— если в качестве параметра указано значение
True
, то жирность шрифта устанавливается равной значению атрибута
Bold
, а если
False
— то равной зна- чению атрибута
Normal класса
QFont
;
 bold()
— возвращает значение
True
, если степень жирности шрифта больше значения атрибута
Normal класса
QFont
, и
False
— в противном случае;
 setItalic(<Флаг>)
— если в качестве параметра указано значение
True
, шрифт будет курсивным, а если
False
— обычного начертания;
 italic()
— возвращает значение
True
, если шрифт курсивный, и
False
— в противном случае;
 setUnderline(<Флаг>)
— если в качестве параметра указано значение
True
, текст будет подчеркнутым, а если
False
— не подчеркнутым;
 underline()
— возвращает значение
True
, если текст подчеркнут, и
False
— в против- ном случае;
 setOverline(<Флаг>)
— если в качестве параметра указано значение
True
, над текстом будет выводиться черта;
 overline()
— возвращает значение
True
, если над текстом будет выводиться черта, и
False
— в противном случае;
 setStrikeOut(<Флаг>)
— если в качестве параметра указано значение
True
, текст будет зачеркнутым;
 strikeOut()
— возвращает значение
True
, если текст будет зачеркнутым, и
False
— в противном случае.
Получить список всех доступных шрифтов позволяет метод families()
класса
QFontDatabase
. Метод возвращает список строк. Отметим, что перед его вызовом следует создать экземпляр класса
QApplication
, в противном случае мы получим ошибку испол- нения: from PyQt5 import QtGui, QtWidgets app = QtWidgets.QApplication(list()) fdb = QtGui.QFontDatabase() print(fdb.families())
Чтобы получить список доступных стилей для указанного шрифта, следует воспользоваться методом styles(<Название шрифта>)
класса
QFontDatabase
: print(fdb.styles("Arial"))
# ['Обычный', 'Полужирный', 'Полужирный Курсив', 'Курсив']
Получить допустимые размеры для указанного стиля можно с помощью метода smoothSizes(<Название шрифта>, <Стиль>)
класса
QFontDatabase
:

566
Часть II. Библиотека PyQt 5 print(fdb.smoothSizes("Arial", "Обычный"))
# [6, 7, 8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 36, 48, 72]
Очень часто необходимо произвести выравнивание выводимого текста внутри некоторой области. Чтобы это сделать, нужно знать размеры области, в которую вписан текст. Полу- чить эти значения позволяют следующие методы класса
QFontMetrics
:
 width(<Текст>[, length=-1])
— возвращает расстояние от начала текста
<Текст>
до по- зиции, в которой должен начаться другой текст. Параметр length позволяет ограничить количество символов;
 height()
возвращает высоту шрифта;
 boundingRect(<Текст>)
— возвращает экземпляр класса
QRect с координатами и разме- рами прямоугольной области, в которую вписан текст.
Вот пример получения размеров области: font = QtGui.QFont("Tahoma", 16) fm = QtGui.QFontMetrics(font) print(fm.width("Строка")) # 67 print(fm.height()) # 25 print(fm.boundingRect("Строка")) # PyQt5.QtCore.QRect(0, -21, 65, 25)
Обратите внимание, что значения, возвращаемые методами width()
и
QRect.width()
, разли- чаются.
П
РИМЕЧАНИЕ
Класс QFontMetrics предназначен для работы с целыми числами. Чтобы работать с веще- ственными числами, необходимо использовать класс QFontMetricsF.
24.2. Класс QPainter
Класс
QPainter содержит все необходимые средства, позволяющие выполнять рисование геометрических фигур и вывод текста на поверхности, которая реализуется классом
QPaintDevice
. Класс
QPaintDevice наследуют классы
QWidget
,
QPicture
,
QPixmap
,
QImage
,
QPagedPaintDevice и некоторые другие. Таким образом, мы можем рисовать на поверхности любого компонента, на изображении или на печатаемой странице. Форматы конструктора класса:
<Объект> = QPainter()
<Объект> = QPainter()
Первый конструктор создает объект, который не подключен ни к одному устройству. Чтобы подключиться к устройству и захватить контекст рисования, необходимо вызвать метод begin()
и передать ему ссылку на экземпляр класса, являющегося наследни- ком класса
QPaintDevice
. Метод возвращает значение
True
, если контекст успешно захва- чен, и
False
— в противном случае. В один момент времени только один объект может ри- совать на устройстве, поэтому после окончания рисования необходимо освободить контекст рисования с помощью метода end()
. С учетом сказанного код, позволяющий рисовать на компоненте, будет выглядеть так: def paintEvent(self, e):
# Компонент, на котором выполняется рисование, передается в параметре self painter = QtGui.QPainter() painter.begin(self)

Глава 24. Работа с графикой
567
# Здесь производим рисование на компоненте painter.end()
Второй конструктор принимает ссылку на экземпляр класса, являющегося наследником класса
QPaintDevice
, подключается к этому устройству и сразу захватывает контекст рисо- вания. Контекст рисования автоматически освобождается внутри деструктора класса
QPainter при уничтожении объекта. Так как объект автоматически уничтожается при выхо- де из метода paintEvent()
, то метод end()
можно и не вызывать.
Вот пример рисования на компоненте: def paintEvent(self, e): painter = QtGui.QPainter(self)
# Здесь производим рисование на компоненте
Проверить успешность захвата контекста рисования можно с помощью метода isActive()
: он возвращает значение
True
, если контекст захвачен, и
False
— в противном случае.
24.2.1. Рисование линий и фигур
После захвата контекста рисования следует установить перо и кисть. С помощью пера про- изводится рисование точек, линий и контуров фигур, а с помощью кисти — заполнение фона фигур. Установить перо позволяет метод setPen()
класса
QPainter
. Форматы метода: setPen() setPen() setPen(<Стиль пера>)
Для установки кисти предназначен метод setBrush()
. Форматы метода: setBrush() setBrush(<Стиль кисти>)
Устанавливать перо или кисть необходимо перед каждой операцией рисования, требующей изменения цвета или стиля. Если перо или кисть не установлены, будут использоваться объекты с настройками по умолчанию. После установки пера и кисти можно приступать к рисованию точек, линий, фигур, текста и др.
Для рисования точек, линий и фигур класс
QPainter предоставляет следующие наиболее часто употребляемые методы (полный их список приведен на странице https://doc.qt.io/ qt-5/qpainter.html):
 drawPoint()
— рисует точку. Форматы метода: drawPoint(, ) drawPoint() drawPoint()
 drawPoints()
— рисует несколько точек. Форматы метода: drawPoints([, ..., ]) drawPoints([, ..., ]) drawPoints() drawPoints()
 drawLine()
— рисует линию. Форматы метода: drawLine() drawLine()

568
Часть II. Библиотека PyQt 5 drawLine(, ) drawLine(, ) drawLine(, , , )
 drawLines()
— рисует несколько отдельных линий. Форматы метода: drawLines([, ..., ]) drawLines([, ..., ]) drawLines(<Список с экземплярами класса QLineF>) drawLines([, ..., ]) drawLines([, ..., ])
 drawPolyline()
— рисует несколько линий, которые соединяют указанные точки. Пер- вая и последняя точки не соединяются. Форматы метода: drawPolyline([, ..., ]) drawPolyline([, ..., ]) drawPolyline() drawPolyline()
 drawRect()
— рисует прямоугольник с границей и заливкой. Чтобы убрать границу, сле- дует использовать перо со стилем
NoPen
, а чтобы убрать заливку — кисть со стилем
NoBrush
. Форматы метода: drawRect(, , <Ширина>, <Высота>) drawRect() drawRect()
 fillRect()
— рисует прямоугольник с заливкой без границы. Форматы метода: fillRect(, , <Ширина>, <Высота>, <Заливка>) fillRect(, <Заливка>) fillRect(, <Заливка>)
<Заливка>
может быть задана экземплярами классов

,

в виде стиля кисти или атрибута цвета;
 drawRoundedRect()
— рисует прямоугольник с границей, заливкой и скругленными краями. Форматы метода: drawRoundedRect(, , <Ширина>, <Высота>,
<Скругление по горизонтали>, <Скругление по вертикали>[, mode = Qt::AbsoluteSize]) drawRoundedRect(, <Скругление по горизонтали>,
<Скругление по вертикали>[, mode = Qt::AbsoluteSize]) drawRoundedRect(, <Скругление по горизонтали>,
<Скругление по вертикали>[, mode = Qt::AbsoluteSize])
Параметры
<Скругление по горизонтали>
и
<Скругление по вертикали>
задают радиусы скругления углов по горизонтали и вертикали. Необязательный параметр mode указыва- ет, в каких единицах измеряются радиусы скругления углов, и задается одним из сле- дующих атрибутов класса
QtCore.Qt
:

AbsoluteSize

0
— радиусы указываются в пикселах;

RelativeSize

1
— радиусы указываются в процентах от соответствующего разме- ра рисуемого прямоугольника;

Глава 24. Работа с графикой
569
 drawPolygon()
— рисует многоугольник с границей и заливкой. Форматы метода: drawPolygon([, ..., ]) drawPolygon([, ..., ]) drawPolygon([, fillRule=OddEvenFill]) drawPolygon([, fillRule=OddEvenFill])
Необязательный параметр fillRule задает алгоритм определения, находится ли какая- либо точка внутри нарисованного прямоугольника или вне его. В качестве его значения указывается атрибут
OddEvenFill
(
0
) или
WindingFill
(
1
) класса
QtCore.Qt
;
 drawEllipse()
— рисует эллипс с границей и заливкой. Форматы метода: drawEllipse(, , <Ширина>, <Высота>) drawEllipse() drawEllipse() drawEllipse(,
1   ...   49   50   51   52   53   54   55   56   ...   83


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