ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 05.12.2023
Просмотров: 848
Скачиваний: 3
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
СОДЕРЖАНИЕ
скрыта, и
False
— в противном случае;
isIndexHidden(
— возвращает значение
True
, если элемент с указанным индексом (экземпляр класса
QModelIndex
) скрыт, и
False
— в противном случае;
setExpanded(
— если во втором параметре указано значение
True
, то элементы, которые являются дочерними для элемента с указанным в первом па- раметре индексом, будут отображены, а если
False
— то скрыты. В первом параметре указывается экземпляр класса
QModelIndex
;
expand(
— отображает элементы, которые являются дочерними для эле- мента с указанным индексом. В качестве параметра указывается экземпляр класса
QModelIndex
. Метод является слотом;
522
Часть II. Библиотека PyQt 5
expandToDepth(<Уровень>)
— отображает все дочерние элементы до указанного уровня.
Метод является слотом;
expandAll()
— отображает все дочерние элементы. Метод является слотом;
collapse(
— скрывает элементы, которые являются дочерними для эле- мента с указанным индексом. В качестве параметра указывается экземпляр класса
QModelIndex
. Метод является слотом;
collapseAll()
— скрывает все дочерние элементы. Метод является слотом;
isExpanded(
— возвращает значение
True
, если элементы, которые явля- ются дочерними для элемента с указанным индексом, отображены, и
False
— в против- ном случае. В качестве параметра указывается экземпляр класса
QModelIndex
;
setItemsExpandable(<Флаг>)
— если в качестве параметра указано значение
False
, поль- зователь не сможет отображать или скрывать дочерние элементы;
setAnimated(<Флаг>)
— если в качестве параметра указано значение
True
, отображение и сокрытие дочерних элементов будет производиться с анимацией;
setIndentation(<Оступ>)
— задает отступ для дочерних элементов;
setRootIsDecorated(<Флаг>)
— если в качестве параметра указано значение
False
, для элементов верхнего уровня не будут показываться элементы управления, с помощью которых производится отображение и сокрытие дочерних элементов;
setFirstColumnSpanned(<Индекс строки>,
— если третьим па- раметром передано значение
True
, содержимое первого столбца строки с указанным в первом параметре индексом и родителем, заданным во втором параметре (как экземп- ляр класса
QModelIndex
), займет всю ширину списка;
setExpandsOnDoubleClick(<Флаг>)
— если передать в параметре значение
False
, свора- чивание и разворачивание пунктов списка будет выполняться по двойному щелчку мыши;
setSortingEnabled(<Флаг>)
— если в качестве параметра указано значение
True
, столб- цы можно сортировать с помощью щелчка мышью на заголовке столбца. При этом в за- головке показывается текущее направление сортировки;
sortByColumn(<Индекс столбца>[, AscendingOrder])
— производит сортировку. Если во втором параметре указан атрибут
AscendingOrder класса
QtCore.Qt
, сортировка произво- дится в прямом порядке, а если
DescendingOrder
— в обратном;
setWordWrap(<Флаг>)
— если в качестве параметра указано значение
True
, текст элемен- тов при необходимости будет переноситься по строкам.
Класс
QTreeView поддерживает сигналы:
expanded(
— генерируется при отображении дочерних элементов. Внутри обработчика через параметр доступен индекс (экземпляр класса
QModelIndex
) элемента;
collapsed(
— генерируется при сокрытии дочерних элементов. Внутри обработчика через параметр доступен индекс (экземпляр класса
QModelIndex
) элемента.
22.5.5. Управление заголовками строк и столбцов
Класс
QHeaderView представляет заголовки строк и столбцов в компонентах
QTableView и
QTreeView
. Получить ссылки на заголовки в классе
QTableView позволяют методы horizontalHeader()
и verticalHeader()
, а для установки заголовков предназначены методы
Глава 22. Списки и таблицы
523 setHorizontalHeader(
и setVerticalHeader(
. Получить ссыл- ку на заголовок в классе
QTreeView позволяет метод header()
, а для установки заголовка предназначен метод setHeader(
. Иерархия наследования:
(QObject, QPaintDevice) — QWidget — QFrame — QAbstractScrollArea —
QAbstractItemView — QHeaderView
Формат конструктора класса
QHeaderView
:
<Объект> = QHeaderView(<Ориентация>[, parent=<Родитель>])
Параметр
<Ориентация>
позволяет задать ориентацию заголовка. В качестве значения ука- зываются атрибуты
Horizontal или
Vertical класса
QtCore.Qt
Класс
QHeaderView наследует все методы и сигналы класса
QAbstractItemView
(см. разд. 22.5.1) и дополнительно определяет следующие основные методы (полный их список можно найти на странице https://doc.qt.io/qt-5/qheaderview.html):
count()
— возвращает количество секций в заголовке. Получить количество секций можно также с помощью функции len()
;
setDefaultSectionSize(<Размер>)
— задает размер секций по умолчанию;
defaultSectionSize()
— возвращает размер секций по умолчанию;
setMinimumSectionSize(<Размер>)
— задает минимальный размер секций;
minimumSectionSize()
— возвращает минимальный размер секций;
setMaximumSectionSize(<Размер>)
— задает максимальный размер секций;
maximumSectionSize()
— возвращает максимальный размер секций;
resizeSection(<Индекс>, <Размер>)
— изменяет размер секции с указанным индексом;
sectionSize(<Индекс>)
— возвращает размер секции с указанным индексом;
setSectionResizeMode(<Режим>)
— задает режим изменения размеров для всех секций.
В качестве параметра могут быть указаны следующие атрибуты класса
QHeaderView
:
•
Interactive
—
0
— размер может быть изменен пользователем или программно;
•
Stretch
—
1
— секции автоматически равномерно распределяют свободное про- странство между собой. Размер не может быть изменен ни пользователем, ни про- граммно;
•
Fixed
—
2
— размер может быть изменен только программно;
•
ResizeToContents
—
3
— размер определяется автоматически по содержимому сек- ции. Размер не может быть изменен ни пользователем, ни программно;
setSectionResizeMode(<Индекс>, <Режим>)
— задает режим изменения размеров для секции с указанным индексом;
setStretchLastSection(<Флаг>)
— если в качестве параметра указано значение
True
, последняя секция будет занимать все свободное пространство;
setCascadingSectionResizes(<Флаг>)
— если в качестве параметра указано значение
True
, изменение размеров одной секции может привести к изменению размеров других секций;
setSectionHidden(<Индекс>, <Флаг>)
— если во втором параметре указано значение
True
, секция с индексом, указанным в первом параметре, будет скрыта. Значение
False отображает секцию;
524
Часть II. Библиотека PyQt 5
hideSection(<Индекс>)
— скрывает секцию с указанным индексом;
showSection(<Индекс>)
— отображает секцию с указанным индексом;
isSectionHidden(<Индекс>)
— возвращает значение
True
, если секция с указанным ин- дексом скрыта, и
False
— в противном случае;
sectionsHidden()
— возвращает значение
True
, если существует хотя бы одна скрытая секция, и
False
— в противном случае;
hiddenSectionCount()
— возвращает количество скрытых секций;
setDefaultAlignment(<Выравнивание>)
— задает выравнивание текста внутри заголов- ков;
setHighlightSections(<Флаг>)
— если в качестве параметра указано значение
True
, то текст заголовка текущей секции будет выделен;
setSectionsClickable(<Флаг>)
— если в качестве параметра указано значение
True
, за- головок будет реагировать на щелчок мышью, при этом выделяя все элементы секции;
setSectionsMovable(<Флаг>)
— если в качестве параметра указано значение
True
, поль- зователь может перемещать секции с помощью мыши;
sectionsMovable()
— возвращает значение
True
, если пользователь может перемещать секции с помощью мыши, и
False
— в противном случае;
moveSection(<Откуда>, <Куда>)
— позволяет переместить секцию. В параметрах указы- ваются визуальные индексы;
swapSections(<Секция1>, <Секция2>)
— меняет две секции местами. В параметрах ука- зываются визуальные индексы;
visualIndex(<Логический индекс>)
— преобразует логический индекс (первоначальный порядок следования) в визуальный (отображаемый в настоящее время порядок следова- ния). Если преобразование прошло неудачно, возвращается значение
-1
;
logicalIndex(<Визуальный индекс>)
— преобразует визуальный индекс (отображаемый в настоящее время порядок следования) в логический (первоначальный порядок следо- вания. Если преобразование прошло неудачно, возвращается значение
-1
;
saveState()
— возвращает экземпляр класса
QByteArray с текущими размерами и поло- жением секций;
restoreState(
— восстанавливает размеры и положение секций на основе экземпляра класса
QByteArray
, возвращаемого методом saveState()
Класс
QHeaderView поддерживает следующие сигналы (здесь приведены только основные — полный их список можно найти на странице https://doc.qt.io/qt-5/qheaderview.html):
sectionPressed(<Логический индекс>)
— генерируется при нажатии левой кнопки мыши над заголовком секции. Внутри обработчика через параметр доступен целочисленный логический индекс секции;
sectionClicked(<Логический индекс>)
— генерируется при нажатии и отпускании левой кнопки мыши над заголовком секции. Внутри обработчика через параметр доступен це- лочисленный логический индекс секции;
sectionDoubleClicked(<Логический индекс>)
— генерируется при двойном щелчке мышью на заголовке секции. Внутри обработчика через параметр доступен целочислен- ный логический индекс секции;
Глава 22. Списки и таблицы
525
sectionMoved(<Логический индекс>, <Старый визуальный индекс>, <Новый визуальный индекс>)
— генерируется при изменении положения секции. Все параметры целочис- ленные;
sectionResized(<Логический индекс>, <Старый размер>, <Новый размер>)
— генериру- ется непрерывно при изменении размера секции. Все параметры целочисленные.
22.6. Управление выделением элементов
Класс
QItemSelectionModel
, объявленный в модуле
QtCore
, реализует модель, позволяющую централизованно управлять выделением сразу в нескольких представлениях. Установить модель выделения позволяет метод setSelectionModel(
класса
QAbstractItemView
, а получить ссылку на модель можно с помощью метода selectionModel()
. Если одна модель выделения установлена сразу в нескольких представ- лениях, то выделение элемента в одном представлении приведет к выделению соответст- вующего элемента в другом представлении. Иерархия наследования выглядит так:
QObject — QItemSelectionModel
Форматы конструктора класса
QItemSelectionModel
:
<Объект> = QItemSelectionModel(<Модель>)
<Объект> = QItemSelectionModel(<Модель>, <Родитель>)
Класс
QItemSelectionModel поддерживает следующие полезные методы (полный их список можно найти на странице https://doc.qt.io/qt-5/qitemselectionmodel.html):
hasSelection()
— возвращает значение
True
, если существует выделенный элемент, и
False
— в противном случае;
isSelected(
— возвращает значение
True
, если элемент с указанным ин- дексом (экземпляр класса
QModelIndex
) выделен, и
False
— в противном случае;
isRowSelected(<Индекс>,
— возвращает значение
True
, если строка с индексом
<Индекс>
и родителем
выделена, и
False
— в противном случае;
isColumnSelected(<Индекс>,
— возвращает значение
True
, если столбец с индексом
<Индекс>
и родителем
выделен, и
False
— в противном слу- чае;
rowIntersectsSelection(<Индекс>,
— возвращает значение
True
, если строка с индексом
<Индекс>
и родителем
содержит выделенный элемент, и
False
— в противном случае;
columnIntersectsSelection(<Индекс>,
— возвращает значение
True
, если столбец с индексом
<Индекс>
и родителем
содержит выделенный элемент, и
False
— в противном случае;
selectedIndexes()
— возвращает список индексов (экземпляров класса
QModelIndex
) выделенных элементов или пустой список, если выделенных элементов нет;
selectedRows([<Индекс столбца>=0])
— возвращает список индексов (экземпляров клас- са
QModelIndex
) выделенных элементов из указанного столбца. Элемент попадет в спи- сок только в том случае, если строка выделена полностью;
selectedColumns([<Индекс строки>=0])
— возвращает список индексов (экземпляров класса
QModelIndex
) выделенных элементов из указанной строки. Элемент попадет в список только в том случае, если столбец выделен полностью;
526
Часть II. Библиотека PyQt 5
selection()
— возвращает ссылку на экземпляр класса
QItemSelection
;
select(
— изменяет выделение элемента с указанным индек- сом. Во втором параметре указываются следующие атрибуты (или их комбинация через оператор
|
) класса
QItemSelectionModel
:
•
NoUpdate
— без изменений;
•
Clear
— снимает выделение всех элементов;
•
Select
— выделяет элемент;
•
Deselect
— снимает выделение с элемента;
•
Toggle
— выделяет элемент, если он не выделен, или снимает выделение, если эле- мент был выделен;
•
Current
— обновляет выделение текущего элемента;
•
Rows
— индекс будет расширен так, чтобы охватить всю строку;
•
Columns
— индекс будет расширен так, чтобы охватить весь столбец;
•
SelectCurrent
— комбинация
Select | Current
;
•
ToggleCurrent
— комбинация
Toggle | Current
;
•
ClearAndSelect
— комбинация
Clear | Select
Метод является слотом;
select(
— изменяет выделение элементов. Метод является слотом;
setCurrentIndex(
— делает элемент текущим и изменяет ре- жим выделения. Метод является слотом;
currentIndex()
— возвращает индекс (экземпляр класса
QModelIndex
) текущего элемента;
clearSelection()
— снимает все выделения. Метод является слотом.
Класс
QItemSelectionModel поддерживает следующие сигналы:
currentChanged(
— генерируется при изменении индекса текущего элемента. Внутри обработчика через первый параметр доступен индекс пре- дыдущего элемента, а через второй — индекс нового элемента;
currentRowChanged(
— генерируется при выделении эле- мента из другой строки. Внутри обработчика через первый параметр доступен индекс предыдущего элемента, а через второй — индекс нового элемента;
currentColumnChanged(
— генерируется при выделении элемента из другого столбца. Внутри обработчика через первый параметр доступен ин- декс предыдущего элемента, а через второй — индекс нового элемента;
selectionChanged(
— генерируется при изменении выделения. Внутри обработчика через первый параметр доступно предыдущее выделе- ние, а через второй — новое выделение.
22.7. Промежуточные модели
Как вы уже знаете, одну модель можно установить в нескольких представлениях. При этом изменение порядка следования элементов в одном представлении повлечет за собой изме- нение порядка следования элементов в другом. Чтобы предотвратить изменение порядка
Глава 22. Списки и таблицы
527 следования элементов в базовой модели, следует создать промежуточную модель с по- мощью класса
QSortFilterProxyModel
, объявленного в модуле
QtCore
, и установить ее в представлении. Иерархия наследования для класса
QSortFilterProxyModel выглядит так:
QObject — QAbstractItemModel — QAbstractProxyModel —
QSortFilterProxyModel
Формат конструктора класса
QSortFilterProxyModel
:
<Объект> = QSortFilterProxyModel([parent=<Родитель>])
Класс
QSortFilterProxyModel наследует следующие методы из класса
QAbstractProxyModel
(здесь приведены только основные — полный их список можно найти на странице https://doc.qt.io/qt-5/qabstractproxymodel.html):
setSourceModel(<Модель>)
— устанавливает базовую модель;
sourceModel()
— возвращает ссылку на базовую модель.
Класс
QSortFilterProxyModel поддерживает основные методы обычных моделей и допол- нительно определяет следующие основные методы (полный их список можно найти на странице https://doc.qt.io/qt-5/qsortfilterproxymodel.html):
sort(<Индекс столбца>[, order=AscendingOrder])
— производит сортировку. Если во втором параметре указан атрибут
AscendingOrder класса
QtCore.Qt
, сортировка произво- дится в прямом порядке, а если
DescendingOrder
— в обратном. Если в параметре
<Индекс столбца>
указать значение
-1
, будет использован порядок следования элементов из базовой модели.
П
РИМЕЧАНИЕ
Чтобы включить сортировку столбцов пользователем, следует передать значение True в метод setSortingEnabled() объекта представления.
setSortRole(<Роль>)
— задает роль (см. разд. 22.3), по которой производится сортиров- ка. По умолчанию сортировка производится по роли
DisplayRole
;
setSortCaseSensitivity(<Режим>)
— если в качестве параметра указать атрибут
CaseInsensitive класса
1 ... 46 47 48 49 50 51 52 53 ... 83
QtCore.Qt
, при сортировке регистр символов учитываться не будет, а если
CaseSensitive
— то будет;
setSortLocaleAware(<Флаг>)
— если в качестве параметра указать значение
True
, при сортировке будут учитываться настройки локали;
setFilterFixedString(<Фрагмент>)
— выбор из модели элементов, которые содержат заданный фрагмент. Если указать пустую строку, в результат попадут все строки из базовой модели. Метод является слотом;
setFilterRegExp()
— выбор из модели элементов, соответствующих указанному регу- лярному выражению. Если указать пустую строку, в результат попадут все строки из базовой модели. Форматы метода: setFilterRegExp(
В первом формате указывается экземпляр класса
QRegExp
, а во втором — строка с шаб- лоном регулярного выражения. Второй формат метода является слотом;
setFilterWildcard(<Шаблон>)
— выбор из модели элементов, соответствующих указан- ной строке, которая может содержать подстановочные знаки:
528
Часть II. Библиотека PyQt 5
•
?
— один любой символ;
•
*
— нуль или более любых символов;
•
[...]
— диапазон значений.
Остальные символы трактуются как есть. Если в качестве параметра указать пустую строку, в результат попадут все элементы из базовой модели. Метод является слотом;
setFilterKeyColumn(<Индекс>)
— задает индекс столбца, по которому будет произво- диться фильтрация. Если в качестве параметра указать значение
-1
, будут просматри- ваться элементы во всех столбцах. По умолчанию фильтрация производится по первому столбцу;
setFilterRole(<Роль>)
— задает роль (см. разд. 22.3), по которой производится фильт- рация. По умолчанию сортировка производится по роли
DisplayRole
;
setFilterCaseSensitivity(<Режим>)
— если в качестве параметра указать атрибут
CaseInsensitive класса
QtCore.Qt
, при фильтрации регистр символов учитываться не будет, а если
CaseSensitive
— то будет;
setDynamicSortFilter(<Флаг>)
— если в качестве параметра указано значение
False
, при изменении базовой модели не будет производиться повторная сортировка или фильт- рация.
22.8. Использование делегатов
Все три представления, рассмотренные в разд. 22.5, дают возможность редактирования тек- ста их элементов. Например, в таблице (класс
QTableView
) мы можем дважды щелкнуть мышью на любом элементе, после чего в нем появится поле ввода. Введем в это поле новый текст и нажмем клавишу
За редактирование данных в представлении отвечает особый класс, называемый делегатом.
Он создает компонент, в котором будет выполняться редактирование значения (
редактор), задает его параметры, заносит в него само редактируемое значение, а по окончании редак- тирования переносит его назад, в модель.
По умолчанию в качестве делегата используется класс
QItemDelegate из модуля
QtWidgets
А в качестве компонента-редактора применяется однострочное поле ввода (класс
QLineEdit
, рассмотренный нами в главе 21).
Если мы хотим использовать для редактирования значения в каком-либо столбце или стро- ке другой редактор — например, многострочное поле ввода, поле ввода даты или целого числа, мы создадим другой делегат и назначим его представлению. Класс, представляющий делегат, должен быть унаследован от класса
QStyledItemDelegate
Иерархия наследования классов
QItemDelegate и
QStyledItemDelegate
:
QObject – QAbstractItemDelegate - QItemDelegate
QObject – QAbstractItemDelegate - QStyledItemDelegate
В новом классе-делегате нам следует переопределить следующие методы:
createEditor()
— создает компонент, который будет использоваться для редактирова- ния данных, и задает его параметры. Формат метода: createEditor(self, <Родитель>, <Настройки>, <Индекс>)
Глава 22. Списки и таблицы
529
Вторым параметром передается ссылка на компонент-представление, который станет родителем создаваемого редактора (список, таблица или иерархический список). Треть- им параметром передается экземпляр класса
QStyleOptionViewItem
, хранящий дополни- тельные настройки делегата. Четвертым параметром можно получить индекс текущего элемента модели, представленный экземпляром класса
QModelIndex
Метод createEditor()
должен создать компонент-редактор, задать для него в качестве родителя компонент-представление (он передается вторым параметром) и вернуть соз- данный компонент в качестве результата.
Чтобы отказаться от использования собственного делегата и указать представлению ис- пользовать делегат по умолчанию, в методе createEditor()
следует вернуть значение
None
;
setEditorData()
— заносит в компонент-редактор, созданный в методе createEditor()
, данные из текущего элемента модели, тем самым подготавливая редактор для редакти- рования этих данных. Формат метода: setEditorData(self, <Редактор>, <Индекс>)
Вторым параметром передается компонент-редактор, а третьим — индекс текущего эле- мента модели в виде экземпляра класса
QModelIndex
;
updateEditorGeometry()
— задает размеры редактора соответственно размерам области, отведенной под него в компоненте-представлении. Формат: updateEditorGeometry(self, <Редактор>, <Настройки>, <Индекс>)
Вторым параметром передается ссылка на компонент-редактор, третьим — ссылка на экземпляр класса
QStyleOptionViewItem
, хранящий настройки делегата, четвертым — индекс текущего элемента модели, представленный экземпляром класса
QModelIndex
Размеры отведенной под редактор области мы можем получить из атрибута rect экземп- ляра класса
QStyleOptionViewItem
, переданного третьим параметром (полное описание класса
QStyleOptionViewItem приведено на странице https://doc.qt.io/qt-5/qstyleoption viewitem.html, а описание класса
QStyleOption
, от которого он порожден, — на страни- це https://doc.qt.io/qt-5/qstyleoption.html);
setModelData()
— по окончании редактирования переносит значение из редактора в те- кущий элемент модели. Формат: setModelData(self, <Редактор>, <Модель>, <Индекс>)
Вторым параметром передается ссылка на компонент-редактор, третьим — ссылка на модель, четвертым — индекс текущего элемента модели, представленный экземпляром класса
QModelIndex
Полное описание класса
QAbstractItemDelegate можно найти на странице https://doc.qt.io/ qt-5/qabstractitemdelegate.html, класса
QitemDelegate
— на странице https://doc.qt.io/qt-5/ qitemdelegate.html, а класса
QStyledItemDelegate
— на странице https://doc.qt.io/qt-5/ qstyleditemdelegate.html.
Для назначения делегатов представлению следует применять следующие методы, унаследо- ванные от класса
QAbstractItemView
:
setItemDelegate(
— назначает делегат для всего представления.
В параметре передается класс делегата, унаследованный от класса
QAbstractItemDelegate
;
530
Часть II. Библиотека PyQt 5
setItemDelegateForColumn(<Индекс столбца>,
— назначает делегат для столбца представления с указанным индексом. В параметре передается класс делегата, унаследованный от класса
QAbstractItemDelegate
;
setItemDelegateForRow(<Индекс строки>,
— назначает деле- гат для строки представления с указанным индексом. В параметре передается класс де- легата, унаследованный от класса
QAbstractItemDelegate
Если в какой-либо ячейке представления действуют одновременно два делегата, задан- ные для столбца и для строки, будет использоваться делегат, заданный для строки.
В качестве примера рассмотрим небольшое складское приложение (листинг 22.4), позво- ляющее править количество каких-либо имеющихся на складе позиций с применением поля ввода целочисленных значений (класс
QSpinBox
).
Листинг 22.4. Использование делегата from PyQt5 import QtCore, QtWidgets, QtGui import sys
# Создаем класс делегата class SpinBoxDelegate(QtWidgets.QStyledItemDelegate): def createEditor(self, parent, options, index):
# Создаем компонент-редактор, используемый для правки значений
# количества позиций editor = QtWidgets.QSpinBox(parent) editor.setFrame(False) editor.setMinimum(0) editor.setSingleStep(1) return editor def setEditorData(self, editor, index):
# Заносим в компонент-редактор значение количества value = int(index.model().data(index, QtCore.Qt.EditRole)) editor.setValue(value) def updateEditorGeometry(self, editor, options, index):
# Указываем размеры компонента-редактора editor.setGeometry(options.rect) def setModelData(self, editor, model, index):
# Заносим исправленное значение количества в модель value = str(editor.value()) model.setData(index, value, QtCore.Qt.EditRole); app = QtWidgets.QApplication(sys.argv) window = QtWidgets.QTableView() window.setWindowTitle("Использование делегата") sti = QtGui.QStandardItemModel(parent = window) lst1 = ['Дискета', 'Бумага для принтера', 'Барабан для принтера'] lst2 = ["10", "3", "8"] for row in range(0, 3): item1 = QtGui.QStandardItem(lst1[row]) item2 = QtGui.QStandardItem(lst2[row]) sti.appendRow([item1, item2])
Глава 22. Списки и таблицы
531 sti.setHorizontalHeaderLabels(['Товар', 'Кол-во']) window.setModel(sti)
# Назначаем делегат второму столбцу таблицы window.setItemDelegateForColumn(1, SpinBoxDelegate()) window.setColumnWidth(0, 150) window.resize(300, 150) window.show() sys.exit(app.exec_())
Результат выполнения кода из листинга 22.4 показан на рис. 22.4.
Рис. 22.4. Использование делегата
ГЛ А В А
23
Работа с базами данных
PyQt 5 включает в свой состав средства для работы с базами данных формата SQLite,
MySQL, Oracle, PostgreSQL и др., не требующие установки никаких дополнительных
Python-библиотек. С помощью этих средств мы можем выполнять любые SQL-запросы и обрабатывать их результаты, получать доступ к отдельным таблицам базы, работать с тран- закциями, а также использовать особые модели для вывода содержимого таблиц или запро- сов в любом из компонентов-представлений, рассмотренных в главе 22.
В
НИМАНИЕ
!
Для успешного доступа к базам данных всех форматов, кроме SQLite и ODBC, требуется установить соответствующий клиент, поскольку в комплект поставки PyQt он не входит.
Все классы, обеспечивающие работу с базами данных и рассмотренные в этой главе, опре- делены в модуле
QtSql
23.1. Соединение с базой данных
За соединение с базой данных и обработку транзакций отвечает класс
QSqlDatabase
Чтобы установить соединение с базой, следует вызвать статический метод addDatabase()
этого класса. Формат вызова: addDatabase(<Формат базы данных>[, connectionName=""])
Первым параметром указывается строка, обозначающая формат открываемой базы данных.
Поддерживаются следующие форматы:
QMYSQL
(MySQL),
QODBC
(ODBC),
QPSQL
(PostgreSQL),
QSQLITE2
(SQLite версии 2) и
QSQLITE
(SQLite версии 3).
Вторым параметром можно задать имя соединения, что может оказаться полезным, если приложение работает сразу с несколькими базами. Если имя соединения не указано, уста- навливаемое соединение будет помечено как используемое по умолчанию.
Метод addDatabase()
возвращает экземпляр класса
QSqlDatabase
, представляющий базу данных, с которой установлено соединение. Теперь мы можем задать параметры базы, вос- пользовавшись одним из приведенных далее методов
QSqlDatabase
:
setHostName(<Хост>)
— задает хост, на котором расположена база данных. Используется только для серверов данных наподобие MySQL;
setPort(<Номер порта>)
— задает номер порта, через который будет выполнено под- ключение к хосту. Используется только для серверов данных и лишь в том случае, если сервер настроен на использование порта, отличного от порта по умолчанию;
Глава 23. Работа с базами данных
533
setDatabaseName(<Имя или путь к базе данных>)
— задает имя базы данных (для серве- ров данных), путь к ней (для «настольных» баз данных, таких как SQLite) или полный набор параметров подключения (если используется ODBC);
setUserName(<Имя>)
— задает имя для подключения к базе. Используется только для серверов данных;
setPassword(<Пароль>)
— задает пароль для подключения к базе. Используется только для серверов данных;
setConnectOptions(<Параметры>)
— задает набор дополнительных параметров для под- ключения к базе в виде строки. Набор поддерживаемых дополнительных параметров различен в зависимости от выбранного формата и приведен в документации по классу
QSqlDatabase
Для работы с базой предназначены следующие методы класса
QSqlDatabase
:
open()
— открывает базу данных. Возвращает
True
, если база была успешно открыта, и
False
— в противном случае;
В
НИМАНИЕ
!
Перед созданием соединения с базой данных обязательно следует создать объект прило- жения (экземпляр класса QApplication). Если этого не сделать, PyQt не сможет загрузить драйвер указанного формата баз данных, и соединение не будет создано.
Открываемая база данных уже должна существовать на диске или сервере. Единственное исключение — база формата SQLite, которая, в случае ее отсутствия, будет создана авто- матически.
open(<Имя>, <Пароль>)
— открывает базу данных с указанными именем и паролем. Воз- вращает
True
, если база была успешно открыта, и
False
— в противном случае;
isOpen()
— возвращает
True
, если база данных в настоящее время открыта, и
False
— в противном случае;
isOpenError()
— возвращает
True
, если при попытке открытия базы данных возникли ошибки, и
False
— в противном случае;
transaction()
— запускает транзакцию, если формат базы поддерживает таковые. Если же формат базы не поддерживает транзакции, то не делает ничего. Возвращает
True
, если транзакция была успешно запущена, и
False
— в противном случае;
commit()
— завершает транзакцию, если формат базы поддерживает таковые. Если же формат базы не поддерживает транзакции, то не делает ничего. Возвращает
True
, если транзакция была успешно завершена, и
False
— в противном случае;
rollback()
— отменяет транзакцию, если формат базы поддерживает таковые. Если же формат базы не поддерживает транзакции, то не делает ничего. Возвращает
True
, если транзакция была успешно отменена, и
False
— в противном случае;
lastError()
— возвращает сведения о последней возникшей при работе с базой ошибке в виде экземпляра класса
QSqlError
;
connectionName()
— возвращает строку с именем соединения с базой или пустую строку для соединения по умолчанию;
tables([type=Tables])
— возвращает список таблиц, хранящихся в базе. В параметре type можно указать тип таблиц в виде одного из атрибутов класса
QSql или их комбина- ции через оператор
|
:
534
Часть II. Библиотека PyQt 5
•
Tables
—
1
— обычные таблицы;
•
SystemTables
—
2
— служебные таблицы;
•
Views
—
4
— представления;
•
AllTables
—
255
— все здесь указанное;
record(<Имя таблицы>)
— возвращает сведения о структуре таблицы с переданным име- нем, представленные экземпляром класса
QSqlRecord
, или пустой экземпляр этого клас- са, если таблицы с таким именем нет;
primaryIndex(<Имя таблицы>)
— возвращает сведения о ключевом индексе таблицы с переданным именем, представленные экземпляром класса
QSqlIndex
, или пустой эк- земпляр этого класса, если таблицы с таким именем нет;
close()
— закрывает базу данных.
Также нам могут пригодиться следующие статические методы класса
QSqlDatabase
:
contains([connectionName=""])
— возвращает
True
, если имеется соединение с базой данных с указанным именем, и
False
— в противном случае;
connectionNames()
— возвращает список имен всех созданных соединений с базами данных. Соединение по умолчанию обозначается пустой строкой;
database([connectionName=""][, ][open=True])
— возвращает сведения о соединении с базой данных, имеющем указанное имя, в виде экземпляра класса
QSqlDatabase
. Если в параметре open указано значение
True
, база данных будет открыта. Если такового соеди- нения нет, возвращается некорректно сформированный экземпляр класса
QSqlDatabase
;
cloneDatabase(
— создает копию указанного в пер- вом параметре соединения с базой и дает ему имя, заданное во втором параметре. Воз- вращаемый результат — экземпляр класса
QSqlDatabase
, представляющий созданную копию соединения;
removeDatabase(<Имя соединения>)
— удаляет соединение с указанным именем. Соеди- нение по умолчанию обозначается пустой строкой;
isDriverAvailable(<Формат>)
— возвращает
True
, если указанный в виде строки формат баз данных поддерживается PyQt, и
False
— в противном случае;
drivers()
— возвращает список всех поддерживаемых PyQt форматов баз данных.
В листинге 23.1 показан код, выполняющий соединение с базами данных различных форма- тов и их открытие.
Листинг 23.1. Соединение с базами данных различных форматов from PyQt5 import QtWidgets, QtSql import sys
# Создаем объект приложения, иначе поддержка баз данных не будет работать app = QtWidgets.QApplication(sys.argv)
# Открываем базу данных SQLite, находящуюся в той же папке, что и файл
# с этой программой con1 = QtSql.QSqlDatabase.addDatabase('QSQLITE') con1.setDatabaseName('data.sqlite')
Глава 23. Работа с базами данных
535 con1.open() con1.close()
# Открываем базу данных MySQL con2 = QtSql.QSqlDatabase.addDatabase('QMYSQL') con2.setHostName("somehost"); con2.setDatabaseName("somedb"); con2.setUserName("someuser"); con2.setPassword("password"); con2.open(); con2.close()
# Открываем базу данных Microsoft Access через ODBC con3 = QtSql.QSqlDatabase.addDatabase("QODBC"); con3.setDatabaseName("DRIVER={Microsoft Access Driver (*.mdb)};
1 ... 47 48 49 50 51 52 53 54 ... 83
FIL={MS Access};DBQ=c:/work/data.mdb"); con3.open() con3.close()
Полное описание класса
QSqlDatabase приведено на странице https://doc.qt.io/qt-5/qsql database.html.
23.2. Получение сведений о структуре таблицы
PyQt позволяет получить некоторые сведения о структуре таблиц, хранящихся в базе: списки полей таблицы, параметры отдельного поля, индекса и ошибки, возникшей при ра- боте с базой.
23.2.1. Получение сведений о таблице
Сведения о структуре таблицы можно получить вызовом метода record()
класса
QSqlDatabase
. Эти сведения представляются экземпляром класса
QSqlRecord
Для получения сведений о полях таблицы используются следующие методы этого класса:
count()
— возвращает количество полей в таблице;
fieldName(<Индекс поля>)
— возвращает имя поля, имеющее заданный индекс, или пус- тую строку, если индекс некорректен;
field(<Индекс поля>)
— возвращает сведения о поле (экземпляр класса
QSqlField
), чей индекс задан в качестве параметра;
field(<Имя поля>)
— возвращает сведения о поле (экземпляр класса
QSqlField
), чье имя задано в качестве параметра;
indexOf(<Имя поля>)
— возвращает индекс поля с указанным именем или
-1
, если тако- го поля нет. При поиске поля не учитывается регистр символов;
contains(<Имя поля>)
— возвращает
True
, если поле с указанным именем существует, и
False
— в противном случае;
isEmpty()
— возвращает
True
, если в таблице нет полей, и
False
— в противном случае.
536
Часть II. Библиотека PyQt 5
Полное описание класса
QSqlRecord приведено на странице https://doc.qt.io/qt-5/qsql record.html.
23.2.2. Получение сведений об отдельном поле
Сведения об отдельном поле таблицы возвращаются методом field()
класса
QSqlRecord
. Их представляет экземпляр класса
QSqlField
, поддерживающий следующие методы:
name()
— возвращает имя поля;
type()
— возвращает тип поля в виде одного из следующих атрибутов класса
QVariant
, объявленного в модуле
QtCore
(здесь приведен список лишь наиболее часто употребляе- мых типов — полный их список можно найти по адресу https://doc.qt.io/qt-5/qvariant- obsolete.html#Type-enum):
•
Invalid
— неизвестный тип;
•
Bool
— логический (
bool
);
•
ByteArray
— массив байтов (
QByteArray
, bytes
);
•
Char
— строка из одного символа (
str
);
•
Date
— значение даты (
QDate или datetime.date
);
•
DateTime
— значение даты и времени (
QDateTime или datetime.datetime
);
•
Double
— вещественное число (
float
);
•
Int и
LongLong
— целое число (
int
);
•
String
— строка (
str
);
•
Time
— значение времени (
QTime или datetime.time
);
•
UInt и
ULongLong
— положительное целое число (
int
);
length()
— возвращает длину поля;
precision()
— возвращает количество знаков после запятой для полей, хранящих веще- ственные числа;
defaultValue()
— возвращает значение поля по умолчанию;
requiredStatus()
— возвращает признак, является ли поле обязательным к заполнению, в виде одного из атрибутов класса
QSqlField
:
•
Required
—
1
— поле является обязательным к заполнению;
•
Optional
—
0
— поле не является обязательным к заполнению;
•
Unknown
—
-1
— определить признак обязательности заполнения поля не представля- ется возможным;
isAutoValue()
— возвращает
True
, если значение в поле заносится автоматически (что может быть, например, у поля автоинкремента), и
False
— в противном случае;
isReadOnly()
— возвращает
True
, если поле доступно только для чтения, и
False
— в противном случае.
Полное описание класса
QSqlField приведено на странице https://doc.qt.io/qt-5/qsqlfield.html.
Глава 23. Работа с базами данных
537 23.2.3. Получение сведений об индексе
Сведения о ключевом индексе, возвращаемые методом primaryIndex()
класса
QSqlDatabase
, представлены экземпляром класса
QSqlIndex
. Он наследует все методы класса
QSqlRecord
, тем самым позволяя узнать, в частности, список полей, на основе которых создан индекс.
Также он определяет следующие методы:
name()
— возвращает имя индекса или пустую строку для ключевого индекса;
isDescending(<Номер поля>)
— возвращает
True
, если поле с указанным номером в ин- дексе отсортировано по убыванию, и
False
— в противном случае.
Полное описание класса
QSqlIndex приведено на странице https://doc.qt.io/qt-5/qsqlindex.html.
23.2.4. Получение сведений об ошибке
Сведения об ошибке, возникшей при работе с базой данных, представляются экземпляром класса
QSqlError
. Выяснить, что за ошибка произошла и каковы ее причины, позволят сле- дующие методы вышеупомянутого класса:
type()
— возвращает код ошибки в виде одного из следующих атрибутов класса
QSqlError
:
•
NoError
—
0
— никакой ошибки не возникло;
•
ConnectionError
—
1
— ошибка соединения с базой данных;
•
StatementError
—
2
— ошибка в коде SQL-запроса;
•
TransactionError
—
3
— ошибка в обработке транзакции;
•
UnknownError
—
4
— ошибка неустановленной природы.
Если код ошибки не удается определить, возвращается
-1
;
text()
— возвращает полное текстовое описание ошибки (фактически — значения, воз- вращаемые методами databaseText()
и driverText()
, объединенные в одну строку);
databaseText()
— возвращает текстовое описание ошибки, сгенерированное базой дан- ных;
driverText()
— возвращает текстовое описание ошибки, сгенерированное драйвером базы данных, который входит в состав PyQt;
nativeErrorCode()
— возвращает строковый код ошибки, специфический для выбранно- го формата баз данных.
Пример: con = QtSql.QSqlDatabase.addDatabase('QSQLITE') con.setDatabaseName('data.sqlite') if con.open():
# Работаем с базой данных else:
# Выводим текст описания ошибки print(con.lastError().text())
Полное описание класса
QSqlError можно найти на странице https://doc.qt.io/qt-5/qsql error.html.
538
Часть II. Библиотека PyQt 5 23.3. Выполнение SQL-запросов и получение их результатов
Класс
QSqlQuery позволяет выполнять SQL-запросы любого назначения: создания необхо- димых таблиц и индексов, добавления, изменения, удаления и, разумеется, выборки запи- сей. Это один из наиболее развитых механизмов работы с данными, предоставляемых PyQt.
П
РИМЕЧАНИЕ
Далее будут рассмотрены лишь наиболее часто используемые возможности класса
QSqlQuery
. Полное его описание приведено на странице https://doc.qt.io/qt-5/qsql query.html.
23.3.1. Выполнение запросов
Чтобы выполнить запрос к базе, сначала следует создать экземпляр класса
QSqlQuery
. Для этого используется один из следующих форматов вызова его конструктора:
QSqlQuery([
QSqlQuery(
QSqlQuery(
Первый формат позволяет сразу задать SQL-код, который следует выполнить, и немедленно запустить его на исполнение. Необязательный параметр db задает соединение с базой дан- ных, запрос к которой следует выполнить, — если он не указан, будет использоваться со- единение по умолчанию.
Второй формат создает пустой запрос, не содержащий ни SQL-кода, ни каких-либо прочих параметров, но позволяющий указать соединение к нужной базе данных. Третий запрос создает копию запроса, переданного в параметре.
Для выполнения запросов используются следующие методы класса
QSqlQuery
:
exec(
— немедленно выполняет переданный в параметре SQL-код. Если по- следний был успешно выполнен, возвращает
True и переводит запрос в активное состоя- ние, в противном случае возвращает
False
. Пример использования этого метода показан в листинге 23.2.
Листинг 23.2. Использование метода exec() from PyQt5 import QtWidgets, QtSql import sys app = QtWidgets.QApplication(sys.argv) con = QtSql.QSqlDatabase.addDatabase('QSQLITE') con.setDatabaseName('data.sqlite') con.open()
# Проверяем, есть ли в базе данных таблица good, и, если таковой нет,
# создаем ее SQL-командой CREATE TABLE if 'good' not in con.tables(): query = QtSql.QSqlQuery() query.exec("create table good(id integer primary key autoincrement,
goodname text, goodcount integer) ") con.close()
Глава 23. Работа с базами данных
539
С
ОВЕТ
Метод exec() следует использовать в тех случаях, если SQL-запрос не принимает пара- метров. В противном случае рекомендуется применять методы, рассмотренные далее.
prepare(
— подготавливает SQL-запрос к выполнению. Применяется, если
SQL-запрос содержит параметры. Параметры в коде запроса могут быть заданы либо в стиле ODBC (вопросительными знаками), либо в стиле Oracle (символьными обозна- чениями, предваренными знаком двоеточия). Метод возвращает
True
, если SQL-запрос был успешно подготовлен, и
False
— в противном случае;
exec_()
— выполняет подготовленный ранее запрос. Возвращает
True
, если запрос был успешно выполнен, и
False
— в противном случае;
addBindValue(<Значение параметра>[, paramType=In])
— задает значение очередного по счету параметра: так, первый вызов этого метода задает значение для первого параметра, второй вызов — для второго и т. д. Необязательный параметр paramType указывает тип параметра — здесь практически всегда используется атрибут
In класса
QSql
, означаю- щий, что этот параметр служит для занесения значения в базу.
В листинге 23.3 приведен пример использования методов prepare()
, addBindValue()
и exec_()
Листинг 23.3. Использование методов prepare(), addBindValue() и exec_() from PyQt5 import QtWidgets, QtSql import sys app = QtWidgets.QApplication(sys.argv) con = QtSql.QSqlDatabase.addDatabase('QSQLITE') con.setDatabaseName('data.sqlite') con.open() query = QtSql.QSqlQuery()
# Добавляем в только что созданную таблицу good запись,
# используя SQL-команду INSERT query.prepare("insert into good values(null, ?, ?)") query.addBindValue('Дискета') query.addBindValue(10) query.exec_() con.close()
bindValue(<Номер параметра>, <Значение параметра>[, paramType=In])
— задает зна- чение для параметра с указанным порядковым номером (листинг 23.4).
Листинг 23.4. Использование метода bindValue() для задания параметров по их порядковым номерам from PyQt5 import QtWidgets, QtSql import sys app = QtWidgets.QApplication(sys.argv) con = QtSql.QSqlDatabase.addDatabase('QSQLITE') con.setDatabaseName('data.sqlite') con.open() query = QtSql.QSqlQuery()
540
Часть II. Библиотека PyQt 5 query.prepare("insert into good values(null, ?, ?)") query.bindValue(0, 'Компакт-диск') query.bindValue(1, 5) query.exec_() con.close()
bindValue(<Обозначение параметра>, <Значение параметра>[, paramType=In])
— задает значение для параметра с указанным символьным обозначением (листинг 23.5).
Листинг 23.5. Использование метода bindValue() для задания параметров по их символьным обозначениям from PyQt5 import QtWidgets, QtSql import sys app = QtWidgets.QApplication(sys.argv) con = QtSql.QSqlDatabase.addDatabase('QSQLITE') con.setDatabaseName('data.sqlite') con.open() query = QtSql.QSqlQuery() query.prepare("insert into good values(null, :name, :count)") query.bindValue(':name', 'Флеш-накопитель') query.bindValue(':count', 20) query.exec_() con.close()
execBatch([mode=ValuesAsRows])
— если в вызове метода addBindValue()
или bindValue()
в качестве значения параметра был указан список, выполнит подготовленный запрос.
Необязательный параметр mode позволяет указать, как будут интерпретироваться от- дельные элементы списка. В настоящее время в качестве его значения для всех форматов баз данных поддерживается лишь атрибут
ValuesAsRows класса
QSqlQuery
, говорящий, что подготовленный запрос должен быть выполнен столько раз, сколько элементов при- сутствует в списке, при этом на каждом выполнении запроса в его код подставляется очередной элемент списка.
Метод возвращает
True
, если запрос был успешно выполнен, и
False
— в противном случае.
Листинг 23.6 представляет пример добавления в таблицу сразу нескольких записей с применением метода execBatch()
Листинг 23.6. Использование метода execBatch() для добавления в таблицу сразу нескольких записей from PyQt5 import QtWidgets, QtSql import sys app = QtWidgets.QApplication(sys.argv) con = QtSql.QSqlDatabase.addDatabase('QSQLITE') con.setDatabaseName('data.sqlite') con.open() query = QtSql.QSqlQuery() query.prepare("insert into good values(null, :name, :count)")