Модель TableView предназначена для определения данных в виде таблицы. Она определена в пакете Qt.labs.qmlmodels. Она имеет относительно небольшой функционал. Она имеет три свойства:
columnCount: возвращает количество столбцов
rowCount: возвращает количество строк
rows: хранит все строки таблицы
Для манипуляции с данными TableModel предоставляет ряд методов:
appendRow(object row)
: добавляет строку
clear()
: удаляет все строки
variant data(QModelIndex index, string role)
: возвращает данные по определенному индексу с учетом роли
object getRow(int rowIndex)
: возвращает строку по индексу
QModelIndex index(int row, int column)
: возвращает индекс для определенной ячейки таблицы
insertRow(int rowIndex, object row)
: вставляет строку по определенному индексу
moveRow(int fromRowIndex, int toRowIndex, int rows)
: передвигает строку на новую позицию
removeRow(int rowIndex, int rows)
: удаляет строку/строки
bool setData(QModelIndex index, string role, variant value)
: устанавливает данные
setRow(int rowIndex, object row)
: устанавливает строку
Определение простейшей модели TableModel
TableModel{ TableModelColumn { display: "name" } TableModelColumn { display: "age" } rows:[ {name: "Tom", age: 39}, {name: "Bob", age: 43}, {name: "Sam", age: 28} ] }
Все строки таблицы помещаются в массив rows. В данном случае в этом массиве три строки-объекта, каждый из которых имеет два свойства - name и age. В
модели также надо установить определения столбцов с помощью типа TableModelColumn. Для каждого столбца устанавливается свойство display
, которое указывает,
какое именно свойство объектов-строк будет отображаться.
Стоит отметить, что количество столбцов - объектов TableModelColumn необязательно должно соответствовать количеству свойств в объектах-строках. Мы можем определить меньшее количество стобцов, если мы не хотим выводить в таблицу все свойства.
Для отображения табличных данных в Qt Quick есть представление TableView. Например, определим простейшую таблицу:
import QtQuick import Qt.labs.qmlmodels Window { width: 250 height: 150 visible: true title: "METANIT.COM" TableModel{ id: userTable TableModelColumn { display: "name" } TableModelColumn { display: "age" } rows:[ {name: "Tom", age: 39}, {name: "Bob", age: 43}, {name: "Sam", age: 28} ] } TableView { model: userTable anchors.fill: parent delegate: Text { text: model.display} } }
Здесь свойству model у TableView передается выше определенная модель TableModel. Для получения содержимого столбца внутри делегата можно использовать свойство
model.display
:
delegate: Text { text: model.display}
То есть поскольку у нас три строки с двумя свойствами, то делегат будет создавать 6 элементов Text.
Тип TableView предоставляет ряд свойств для управления внешним видом таблицы. Отмечу основные:
columnSpacing: устанавливает расстояние между столбцами. По умолчанию равно 0
rowSpacing: устанавливает расстояние между строками. По умолчанию равно 0
resizableColumns: при значении true
позволяет пользователю растягивать ширину столбцов. По умолчанию равно false
. Добавлено с версии Qt 6.5
resizableRows: при значении true
позволяет пользователю растягивать высоту строк. По умолчанию равно false
. Добавлено с версии Qt 6.5
По умолчанию TableView не включает заголовки. Тем не мнее можно вручную добавить элементы HorizontalHeaderView и VerticalHeaderView для создания заголовков для стобцов и строк соответственно. Например, определим заголовки:
import QtQuick import QtQuick.Controls import Qt.labs.qmlmodels Window { width: 250 height: 150 visible: true title: "METANIT.COM" TableModel{ id: userTable TableModelColumn { display: "name" } TableModelColumn { display: "age" } rows:[ {name: "Tom", age: 39}, {name: "Bob", age: 43}, {name: "Sam", age: 28} ] } HorizontalHeaderView { id: horizontalHeader anchors.left: tableView.left anchors.top: parent.top syncView: tableView } VerticalHeaderView { id: verticalHeader anchors.top: tableView.top anchors.left: parent.left syncView: tableView } TableView { id: tableView anchors.left: verticalHeader.right anchors.top: horizontalHeader.bottom anchors.right: parent.right anchors.bottom: parent.bottom model: userTable rowSpacing: 9 columnSpacing: 9 delegate: Text { text: model.display} } }
Для обоих заголовков здесь определена синхронизация с TableView:
syncView: tableView
Больше для связи заголовков с TableView ничего не требуется. В итоге мы получим следующий интерфейс:
Но, как видно из скриншота, в качестве текста по умолчанию заголовки получают индекс столбцов/строк. Настроим заголовки - уберем заголовки строк, а и исправим текст заголовков столбцов:
import QtQuick import QtQuick.Controls import Qt.labs.qmlmodels Window { width: 250 height: 150 visible: true title: "METANIT.COM" TableModel{ id: userTable TableModelColumn { display: "name" } TableModelColumn { display: "age" } rows:[ {name: "Tom", age: 39}, {name: "Bob", age: 43}, {name: "Sam", age: 28} ] } HorizontalHeaderView { id: horizontalHeader anchors.left: tableView.left anchors.top: parent.top syncView: tableView model: ["Имя", "Возраст"] delegate: Text { text: modelData font.bold: true } } TableView { id: tableView anchors.left: parent.left anchors.leftMargin: 5 anchors.top: horizontalHeader.bottom anchors.right: parent.right anchors.bottom: parent.bottom model: userTable rowSpacing: 9 columnSpacing: 9 delegate: Text { text: model.display} } }
HorizontalHeaderView/VerticalHeaderView представляют типы представлений, у которых можно задать данные с помощью свойства model и настроить отображение данных с помощью делегата. В данном случае HorizontalHeaderView в качестве модели получает массив текстов заголовков для обоих столбцов:
Когда новый столбец отображается в TableView, с помощью вызвова метода columnsWidthProvider у него определяется ширина. Если эта функция установлена, то она устанавливает ширину столбца. Если эта функция не установлена, то проверяется установка ширины с помощью метода setColumnWidth(). Если этот метод не был вызван, то для установки ширины применяется метод implicitColumnWidth(). Этот метод устанавливает в качестве ширины столбца неявную ширину, которая равна наибольшей неявной ширине элементов для данного столбце.
Та же логика применяется для установки высоты строк с помощью методов rowsHeightProvider, setRowHeight() и implicitRowHeight().
Пример установки ширины столбца:
TableView { id: tableView model: userTable delegate: Text { text: model.display} property var columnWidths: [80, 50] // два столбца шириной по 80 и 50 columnWidthProvider: function(column) { // получаем неявную длину const implicitWidth = implicitColumnWidth(column) // если неявная ширина больше, чем та, которую мы хотим установить if (implicitWidth > columnWidths[column]) return implicitWidth; return columnWidths[column] } }
Возьмем ситуацию, когда мы хотим, чтобы столбцы имели как минимм некоторую ширину. Однако содержимое столбца превышает эту ширину, то столбец должен принимать
ширину этого содержимого. И здесь на уровне элемента TableView определяется свойство columnWidths
, которое в виде массива хранит два значения - ширину двух столбцов. Это минимальная
ширина.
Свойству columnWidthProvider передается функция, которая в качестве параметра принимает индекс столбца, для которого устанавливается ширина. И данная функция должна возвратить
ширину столбца. Сначала с помощью функции implicitColumnWidth(column)
получаем неявную ширину столбца, которая вычисляет на основе содержимого (implicitColumnWidth()
- встроенная функция). Если
эта неявная ширина больше минимальной ширины для текущего столбца, то возвращаем неявную ширину. Иначе возвращаем минимальную ширину.
Можно таким образом также установить константные значения:
columnWidthProvider: function(column) { return 80;} // с помощью обычной функции rowHeightProvider: () => 25 // с помощью стрелочной функции
TableView позволяет добавить поддержку выделения строки. Для настройки выделения в таблице применяются три свойства:
selectionModel: модель выделения, принимает объект ItemSelectionModel
selectBehavior: что именно выделяет пользователь. Может принимать следующие значения:
TableView.SelectionDisabled
: выделение отключено
TableView.SelectCells
: можно выделять отдельные ячейки (значение по умолчанию)
TableView.SelectRows
: можно выделять отдельные строки
TableView.SelectColumns
: можно выделять отдельные столбцы
selectionMode: режим выделения, который указывает как и сколько объектов может выделять пользователь. Может принимать следующие значения:
TableView.SingleSelection
: пользователь может выбрать одну ячейку, строку или столбец.
TableView.ContigiousSelection
: пользователь может выбрать один непрерывный блок ячеек. Существующее выделение можно увеличить или уменьшить,
удерживая клавишу Shift во время выделения.
TableView.ExtendedSelection
: пользователь может выбрать несколько отдельных блоков ячеек (значение по умолчанию). Существующее выделение можно увеличить
или уменьшить, удерживая клавишу Shift во время выделения. Новый блок выбора можно запустить, не очищая текущий выбор, удерживая клавишу Control во время выбора.
Чтобы узнать, делегат применяется к текущей или выделенной ячейке, в делегат добавляются два свойства - selected (выделена ли ячейка) и current (является ли ячейка текущей):
delegate: Item { required property bool selected required property bool current // ... }
Оба свойства должны быть определены как required
Для рендеринга элемента также можно использовать свойства currentRow и currentColumn, который представляют соответственно текущую строку и текущий столбец.
Например, выделим текущую строку при нажатии на нее:
import QtQuick import QtQuick.Controls import Qt.labs.qmlmodels Window { width: 250 height: 150 visible: true title: "METANIT.COM" TableModel{ id: userTable TableModelColumn { display: "name"} TableModelColumn { display: "age" } rows:[ {name: "Tom", age: 39}, {name: "Bob", age: 43}, {name: "Sam", age: 28} ] } TableView { id: tableView anchors.fill: parent model: userTable selectionModel: ItemSelectionModel {} delegate: Rectangle { implicitHeight: 25 implicitWidth: 80 color: row==tableView.currentRow ? "lightgray": "white" Text { text: display } } } }
Здесь при установке свойства color проверяем на соответствие строки ячейки и текущей строки TableView. Строку, для которой создается ячейка, в делегате можно получить через
свойство row
color: row==tableView.currentRow ? "lightgray": "white"
Если надо выделить только одну ячейку, можно проверять свойство "current", которое устанавливается автоматически:
TableView { id: tableView anchors.fill: parent model: userTable selectionModel: ItemSelectionModel {} delegate: Rectangle { implicitHeight: 25 implicitWidth: 80 required property bool current color: current ? "lightgray": "white" Text { text: display } } }
Другой пример - выделим нескольких строк:
import QtQuick import QtQuick.Controls import Qt.labs.qmlmodels Window { width: 250 height: 150 visible: true title: "METANIT.COM" TableModel{ id: userTable TableModelColumn { display: "name"} TableModelColumn { display: "age" } rows:[ {name: "Tom", age: 39}, {name: "Bob", age: 43}, {name: "Sam", age: 28} ] } TableView { id: tableView anchors.fill: parent model: userTable selectionBehavior: TableView.SelectRows selectionModel: ItemSelectionModel {} delegate: Rectangle { implicitHeight: 25 implicitWidth: 80 required property bool selected color: selected ? "lightgray": "white" Text { text: display } } } SelectionRectangle { target: tableView } }
В данном случае аналогичным способом проверяем свойство "selected" и в зависимости от его значения устанавливаем свойство "color". Здесь надо отметить, что для отрисовки выделения применяется элемент SelectionRectangle, у которого свойство target
указывает на TableView. Выделим несколько строк: