TableView QML Type▲
-
Import Statement: import QtQuick
-
Since:: Qt 5.12
-
Inherited By:: HorizontalHeaderView, TreeView, and VerticalHeaderView
-
Inherits:: Flickable
-
Group: TableView is part of qtquick-views
Detailed Description▲
A TableView has a model that defines the data to be displayed, and a delegate that defines how the data should be displayed.
TableView inherits Flickable. This means that while the model can have any number of rows and columns, only a subsection of the table is usually visible inside the viewport. As soon as you flick, new rows and columns enter the viewport, while old ones exit and are removed from the viewport. The rows and columns that move out are reused for building the rows and columns that move into the viewport. As such, the TableView support models of any size without affecting performance.
A TableView displays data from models created from built-in QML types such as ListModel and XmlListModel, which populates the first column only in a TableView. To create models with multiple columns, either use TableModel or a C++ model that inherits QAbstractItemModel.
Example Usage▲
C++ Models▲
The following example shows how to create a model from C++ with multiple columns:
#include <qqml.h>
#include <QAbstractTableModel>
class
TableModel : public
QAbstractTableModel
{
Q_OBJECT
QML_ELEMENT
QML_ADDED_IN_MINOR_VERSION(1
)
public
:
int
rowCount(const
QModelIndex &
amp; =
QModelIndex()) const
override
{
return
200
;
}
int
columnCount(const
QModelIndex &
amp; =
QModelIndex()) const
override
{
return
200
;
}
QVariant data(const
QModelIndex &
amp;index, int
role) const
override
{
switch
(role) {
case
Qt::
DisplayRole:
return
QString("%1, %2"
).arg(index.column()).arg(index.row());
default
:
break
;
}
return
QVariant();
}
QHash&
lt;int
, QByteArray&
gt; roleNames() const
override
{
return
{
{
Qt::
DisplayRole, "display"
}
}
;
}
}
;
And then how to use it from QML:
import
QtQuick 2.12
import
TableModel 0.1
TableView
{
anchors.fill
:
parent
columnSpacing
:
1
rowSpacing
:
1
clip
:
true
model
:
TableModel {}
delegate
:
Rectangle
{
implicitWidth
:
100
implicitHeight
:
50
Text
{
text
:
display
}
}
}
QML Models▲
For prototyping and displaying very simple data (from a web API, for example), TableModel can be used:
import
QtQuick 2.14
import
Qt.labs.qmlmodels 1.0
TableView
{
anchors.fill
:
parent
columnSpacing
:
1
rowSpacing
:
1
clip
:
true
model
:
TableModel {
TableModelColumn {
display
:
"name"
}
TableModelColumn {
display
:
"color"
}
rows
:
[
{
"name"
:
"cat"
,
"color"
:
"black"
}
,
{
"name"
:
"dog"
,
"color"
:
"brown"
}
,
{
"name"
:
"bird"
,
"color"
:
"white"
}
]
}
delegate
:
Rectangle
{
implicitWidth
:
100
implicitHeight
:
50
border.width
:
1
Text
{
text
:
display
anchors.centerIn
:
parent
}
}
}
Reusing items▲
TableView recycles delegate items by default, instead of instantiating from the delegate whenever new rows and columns are flicked into view. This approach gives a huge performance boost, depending on the complexity of the delegate.
When an item is flicked out, it moves to the reuse pool, which is an internal cache of unused items. When this happens, the TableView::pooled signal is emitted to inform the item about it. Likewise, when the item is moved back from the pool, the TableView::reused signal is emitted.
Any item properties that come from the model are updated when the item is reused. This includes index, row, and column, but also any model roles.
Avoid storing any state inside a delegate. If you do, reset it manually on receiving the TableView::reused signal.
If an item has timers or animations, consider pausing them on receiving the TableView::pooled signal. That way you avoid using the CPU resources for items that are not visible. Likewise, if an item has resources that cannot be reused, they could be freed up.
If you don't want to reuse items or if the delegate cannot support it, you can set the reuseItems property to false.
While an item is in the pool, it might still be alive and respond to connected signals and bindings.
The following example shows a delegate that animates a spinning rectangle. When it is pooled, the animation is temporarily paused:
Component
{
id
:
tableViewDelegate
Rectangle
{
implicitWidth
:
100
implicitHeight
:
50
TableView.onPooled
:
rotationAnimation.pause()
TableView.onReused
:
rotationAnimation.resume()
Rectangle
{
id
:
rect
anchors.centerIn
:
parent
width
:
40
height
:
5
color
:
"green"
RotationAnimation
{
id
:
rotationAnimation
target
:
rect
duration
:
(Math.random() *
2000
) +
200
from
:
0
to
:
359
running
:
true
loops
:
Animation.Infinite
}
}
}
}
Row heights and column widths▲
When a new column is flicked into view, TableView will determine its width by calling the columnWidthProvider function. TableView does not store row height or column width, as it's designed to support large models containing any number of rows and columns. Instead, it will ask the application whenever it needs to know.
TableView uses the largest implicitWidth among the items as the column width, unless the columnWidthProvider property is explicitly set. Once the column width is found, all other items in the same column are resized to this width, even if new items that are flicked in later have larger implicitWidth. Setting an explicit width on an item is ignored and overwritten.
The calculated width of a column is discarded when it is flicked out of the viewport, and is recalculated if the column is flicked back in. The calculation is always based on the items that are visible when the column is flicked in. This means that column width can be different each time, depending on which row you're at when the column enters. You should therefore have the same implicitWidth for all items in a column, or set columnWidthProvider. The same logic applies for the row height calculation.
If you change the values that a rowHeightProvider or a columnWidthProvider return for rows and columns inside the viewport, you must call forceLayout. This informs TableView that it needs to use the provider functions again to recalculate and update the layout.
Since Qt 5.13, if you want to hide a specific column, you can return 0 from the columnWidthProvider for that column. Likewise, you can return 0 from the rowHeightProvider to hide a row. If you return a negative number, TableView will fall back to calculate the size based on the delegate items.
The size of a row or column should be a whole number to avoid sub-pixel alignment of items.
The following example shows how to set a simple columnWidthProvider together with a timer that modifies the values the function returns. When the array is modified, forceLayout is called to let the changes take effect:
TableView
{
id
:
tableView
property
var columnWidths
:
[100
, 50
, 80
, 150
]
columnWidthProvider
:
function
(column) {
return columnWidths[
column]
}
Timer
{
running
:
true
interval
:
2000
onTriggered
: {
tableView.
columnWidths[
2
]
=
150
tableView.forceLayout
(
);
}
}
}
Overlays and underlays▲
All new items that are instantiated from the delegate are parented to the contentItem with the z value, 1. You can add your own items inside the Tableview, as child items of the Flickable. By controlling their z value, you can make them be on top of or underneath the table items.
Here is an example that shows how to add some text on top of the table, that moves together with the table as you flick:
TableView
{
id
:
tableView
topMargin
:
header.implicitHeight
Text
{
id
:
header
text
:
"A table header"
}
}
Selecting items▲
You can add selection support to TableView by assigning an ItemSelectionModel to the selectionModel property. It will then use this model to control which delegate items should be shown as selected, and which item should be shown as current. You can set selectionBehavior to control if the user should be allowed to select individual cells, rows, or columns.
To find out whether a delegate is selected or current, declare the following properties:
delegate
:
Item {
required property bool
selected
required property bool
current
// ...
}
the selected and current properties must be defined as required. This will inform TableView that it should take responsibility for updating their values. If not, they will simply be ignored. See also Required Properties.
The following snippet shows how an application can render the delegate differently depending on the selected property:
TableView
{
id
:
tableView
anchors.fill
:
parent
clip
:
true
model
:
TableModel {
TableModelColumn {
display
:
"name"
}
rows
:
[ {
"name"
:
"Harry"
}
, {
"name"
:
"Hedwig"
}
]
}
selectionModel
:
ItemSelectionModel {}
delegate
:
Rectangle
{
implicitWidth
:
100
implicitHeight
:
30
color
:
selected ? "blue"
:
"lightgray"
required property
bool
selected
Text
{
text
:
display }
}
}
The currentRow and currentColumn properties can also be useful if you need to render a delegate differently depending on if it lies on the same row or column as the current item.
Qt Quick Controls offers a SelectionRectangle that can be used to let the user select cells.
By default, a cell will become current, and any selections will be removed, when the user taps on it. If such default tap behavior is not wanted (e.g if you use custom pointer handlers inside your delegate), you can set pointerNavigationEnabled to false.
Keyboard navigation▲
Copy and paste▲
Implementing copy and paste operations for a TableView usually also includes using a QUndoStack (or some other undo/redo framework). The QUndoStack can be used to store the different operations done on the model, like adding or removing rows, or pasting data from the clipboard, with a way to undo it again later. However, an accompanying QUndoStack that describes the possible operations, and how to undo them, should be designed according to the needs of the model and the application. As such, TableView doesn't offer a built-in API for handling copy and paste.
The following snippet can be used as a reference for how to add copy and paste support to your model and TableView. It uses the existing mime data API in QAbstractItemModel, together with QClipboard. The snippet will work as it is, but can also be extended to use a QUndoStack.