Создание таблиц. TableModel и TableView

Последнее обновление: 12.01.2024

TableModel

Модель 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 необязательно должно соответствовать количеству свойств в объектах-строках. Мы можем определить меньшее количество стобцов, если мы не хотим выводить в таблицу все свойства.

TableView

Для отображения табличных данных в 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.

TableModel и TableView в QML и Qt Quick

Настройка внешнего вида таблицы

Тип 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 ничего не требуется. В итоге мы получим следующий интерфейс:

Установка заголовков HorizontalHeaderView и VerticalHeaderView для TableView в QML и Qt Quick

Но, как видно из скриншота, в качестве текста по умолчанию заголовки получают индекс столбцов/строк. Настроим заголовки - уберем заголовки строк, а и исправим текст заголовков столбцов:

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 в качестве модели получает массив текстов заголовков для обоих столбцов:

Установка заголовков для TableModel и TableView в QML и Qt Quick

Настройка размеров строки столбцов

Когда новый столбец отображается в 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"
выделение строк в TableView в QML и Qt Quick

Если надо выделить только одну ячейку, можно проверять свойство "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. Выделим несколько строк:

Множественное выделение в TableView в QML и Qt Quick
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850