QGIM: documentation update
Document that we support reference wrappers and smart pointers for the model. Turn the construction of the model into a dedicated section, and add relevant snippets. Rename and reorganize sections a bit to introduce basic concepts (rows, columns, multi-role items) first, before the rather advanced topic of trees and value/pointer trade-offs for rows. Change-Id: Ia622783f9cd7aa345217186b438e5e883b9c25b0 Reviewed-by: Mate Barany <mate.barany@qt.io>
This commit is contained in:
parent
2e5a881d34
commit
d139d22f7e
@ -24,6 +24,36 @@ listView.setModel(&model);
|
|||||||
//! [array]
|
//! [array]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void construct_by()
|
||||||
|
{
|
||||||
|
std::vector<int> numbers = {1, 2, 3, 4, 5};
|
||||||
|
|
||||||
|
{
|
||||||
|
//! [value]
|
||||||
|
QGenericItemModel model(numbers);
|
||||||
|
//! [value]
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
//! [pointer]
|
||||||
|
QGenericItemModel model(&numbers);
|
||||||
|
//! [pointer]
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
//! [reference_wrapper]
|
||||||
|
QGenericItemModel model(std::ref(numbers));
|
||||||
|
//! [reference_wrapper]
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
//! [smart_pointer]
|
||||||
|
auto shared_numbers = std::make_shared<std::vector<int>>(numbers);
|
||||||
|
QGenericItemModel model(shared_numbers);
|
||||||
|
//! [smart_pointer]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void const_array()
|
void const_array()
|
||||||
{
|
{
|
||||||
//! [const_array]
|
//! [const_array]
|
||||||
@ -40,6 +70,14 @@ std::array<const int, 5> numbers = {1, 2, 3, 4, 5};
|
|||||||
QGenericItemModel model(numbers);
|
QGenericItemModel model(numbers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void const_ref()
|
||||||
|
{
|
||||||
|
std::vector<int> numbers = {1, 2, 3, 4, 5};
|
||||||
|
//! [const_ref]
|
||||||
|
QGenericItemModel model(std::cref(numbers));
|
||||||
|
//! [const_ref]
|
||||||
|
}
|
||||||
|
|
||||||
void list_of_int()
|
void list_of_int()
|
||||||
{
|
{
|
||||||
//! [list_of_int]
|
//! [list_of_int]
|
||||||
|
@ -31,29 +31,67 @@ QT_BEGIN_NAMESPACE
|
|||||||
operations will perform better if \c{std::size} is available, and if the
|
operations will perform better if \c{std::size} is available, and if the
|
||||||
iterator satisfies \c{std::random_access_iterator}.
|
iterator satisfies \c{std::random_access_iterator}.
|
||||||
|
|
||||||
The range can be provided by pointer or by value, and has to be provided
|
\section1 Constructing the model
|
||||||
when constructing the model. If the range is provided by pointer, then
|
|
||||||
QAbstractItemModel APIs that modify the model, such as setData() or
|
|
||||||
insertRows(), modify the range. The caller must make sure that the
|
|
||||||
range's lifetime exceeds the lifetime of the model. Methods that modify
|
|
||||||
the structure of the range, such as insertRows() or removeColumns(), use
|
|
||||||
standard C++ container APIs \c{resize()}, \c{insert()}, \c{erase()}, in
|
|
||||||
addition to dereferencing a mutating iterator to set or clear the data.
|
|
||||||
|
|
||||||
There is no API to retrieve the range again, so constructing the model
|
The range must be provided when constructing the model; there is no API to
|
||||||
from a range by value is mostly only useful for displaying data.
|
set the range later, and there is no API to retrieve the range from the
|
||||||
|
model. The range can be provided by value, reference wrapper, or pointer.
|
||||||
|
How the model was constructed defines whether changes through the model API
|
||||||
|
will modify the original data.
|
||||||
|
|
||||||
|
When constructed by value, the model makes a copy of the range, and
|
||||||
|
QAbstractItemModel APIs that modify the model, such as setData() or
|
||||||
|
insertRows(), have no impact on the original range.
|
||||||
|
|
||||||
|
\snippet qgenericitemmodel/main.cpp value
|
||||||
|
|
||||||
|
As there is no API to retrieve the range again, constructing the model from
|
||||||
|
a range by value is mostly only useful for displaying read-only data.
|
||||||
Changes to the data can be monitored using the signals emitted by the
|
Changes to the data can be monitored using the signals emitted by the
|
||||||
model, such as \l{QAbstractItemModel}{dataChanged()}.
|
model, such as \l{QAbstractItemModel}{dataChanged()}.
|
||||||
|
|
||||||
|
However, if the range holds pointers to data, then the copy of the range
|
||||||
|
that the model operates on will typically point to the same data, so calls
|
||||||
|
to setData() will modify the original data, while calls to insertRows()
|
||||||
|
will not.
|
||||||
|
|
||||||
|
To make modifications of the model affect the original range, provide the
|
||||||
|
range either by reference wrapper or by pointer.
|
||||||
|
|
||||||
|
\snippet qgenericitemmodel/main.cpp pointer
|
||||||
|
\snippet qgenericitemmodel/main.cpp reference_wrapper
|
||||||
|
|
||||||
|
In this case, QAbstractItemModel APIs that modify the model also modify the
|
||||||
|
range. Methods that modify the structure of the range, such as insertRows()
|
||||||
|
or removeColumns(), use standard C++ container APIs \c{resize()},
|
||||||
|
\c{insert()}, \c{erase()}, in addition to dereferencing a mutating iterator
|
||||||
|
to set or clear the data.
|
||||||
|
|
||||||
|
\note Once the model has been constructed, the range the model operates on
|
||||||
|
must no longer be modified directly. Views on the model wouldn't be
|
||||||
|
informed about the changes, and structural changes are likely to corrupt
|
||||||
|
instances of QPersistentModelIndex that the model maintains.
|
||||||
|
|
||||||
|
The caller must make sure that the range's lifetime exceeds the lifetime of
|
||||||
|
the model.
|
||||||
|
|
||||||
|
Use smart pointers to make sure that the range is only deleted when all
|
||||||
|
clients are done with it.
|
||||||
|
|
||||||
|
\snippet qgenericitemmodel/main.cpp smart_pointer
|
||||||
|
|
||||||
|
QGenericItemModel supports both shared and unique pointers.
|
||||||
|
|
||||||
\section2 Read-only or mutable
|
\section2 Read-only or mutable
|
||||||
|
|
||||||
For ranges that are const objects, for which access always yields
|
For ranges that are const objects, for which access always yields constant
|
||||||
constant values, or where the required container APIs are not available,
|
values, or where the required container APIs are not available,
|
||||||
QGenericItemModel implements write-access APIs to do nothing and return
|
QGenericItemModel implements write-access APIs to do nothing and return
|
||||||
\c{false}. In the example above, the model cannot add or remove rows, as
|
\c{false}. In the example using \c{std::array}, the model cannot add or
|
||||||
the number of entries in a C++ array is fixed. But the values can be
|
remove rows, as the number of entries in a C++ array is fixed. But the
|
||||||
changed using setData(), and the user can trigger editing of the values in
|
values can be changed using setData(), and the user can trigger editing of
|
||||||
the list view. By making the array const, the values also become read-only.
|
the values in the list view. By making the array const, the values also
|
||||||
|
become read-only.
|
||||||
|
|
||||||
\snippet qgenericitemmodel/main.cpp const_array
|
\snippet qgenericitemmodel/main.cpp const_array
|
||||||
|
|
||||||
@ -61,31 +99,38 @@ QT_BEGIN_NAMESPACE
|
|||||||
|
|
||||||
\snippet qgenericitemmodel/main.cpp const_values
|
\snippet qgenericitemmodel/main.cpp const_values
|
||||||
|
|
||||||
|
In the above examples using \c{std::vector}, the model can add or remove
|
||||||
|
rows, and the data can be changed. Passing the range as a constant
|
||||||
|
reference will make the model read-only.
|
||||||
|
|
||||||
|
\snippet qgenericitemmodel/main.cpp const_ref
|
||||||
|
|
||||||
\note If the values in the range are const, then it's also not possible
|
\note If the values in the range are const, then it's also not possible
|
||||||
to remove or insert columns and rows through the QAbstractItemModel API.
|
to remove or insert columns and rows through the QAbstractItemModel API.
|
||||||
For more granular control, implement \l{the C++ tuple protocol}.
|
For more granular control, implement \l{the C++ tuple protocol}.
|
||||||
|
|
||||||
\section1 List, Table, or Tree
|
\section1 Rows and columns
|
||||||
|
|
||||||
The elements in the range are interpreted as rows of the model. Depending
|
The elements in the range are interpreted as rows of the model. Depending
|
||||||
on the type of these rows, QGenericItemModel exposes the range as a list,
|
on the type of these row elements, QGenericItemModel exposes the range as a
|
||||||
a table, or a tree.
|
list, a table, or a tree.
|
||||||
|
|
||||||
If the row type is not an iterable range, and does not implement the
|
If the row elements are simple values, then the range gets represented as a
|
||||||
C++ tuple protocol, then the range gets represented as a list.
|
list.
|
||||||
|
|
||||||
\snippet qgenericitemmodel/main.cpp list_of_int
|
\snippet qgenericitemmodel/main.cpp list_of_int
|
||||||
|
|
||||||
If the row type is an iterable range, then the range gets represented as a
|
If the type of the row elements is an iterable range, such as a vector,
|
||||||
table.
|
list, or array, then the range gets represented as a table.
|
||||||
|
|
||||||
\snippet qgenericitemmodel/main.cpp grid_of_numbers
|
\snippet qgenericitemmodel/main.cpp grid_of_numbers
|
||||||
|
|
||||||
With such a row type, the number of columns can be changed via
|
If the row type provides the standard C++ container APIs \c{resize()},
|
||||||
insertColumns() and removeColumns(). However, all rows are expected to have
|
\c{insert()}, \c{erase()}, then columns can be added and removed via
|
||||||
|
insertColumns() and removeColumns(). All rows are required to have
|
||||||
the same number of columns.
|
the same number of columns.
|
||||||
|
|
||||||
\section2 Fixed-size rows
|
\section2 Structs and gadgets as rows
|
||||||
|
|
||||||
If the row type implements \l{the C++ tuple protocol}, then the range gets
|
If the row type implements \l{the C++ tuple protocol}, then the range gets
|
||||||
represented as a table with a fixed number of columns.
|
represented as a table with a fixed number of columns.
|
||||||
@ -110,7 +155,48 @@ QT_BEGIN_NAMESPACE
|
|||||||
implementing the tuple protocol for compile-time generation of the access
|
implementing the tuple protocol for compile-time generation of the access
|
||||||
code.
|
code.
|
||||||
|
|
||||||
\section2 Trees of data
|
\section2 Multi-role items
|
||||||
|
|
||||||
|
The type of the items that the implementations of data(), setData(),
|
||||||
|
clearItemData() etc. operate on can be the same across the entire model -
|
||||||
|
like in the \c{gridOfNumbers} example above. But the range can also have
|
||||||
|
different item types for different columns, like in the \c{numberNames}
|
||||||
|
case.
|
||||||
|
|
||||||
|
By default, the value gets used for the Qt::DisplayRole and Qt::EditRole
|
||||||
|
roles. Most views expect the value to be
|
||||||
|
\l{QVariant::canConvert}{convertible to and from a QString} (but a custom
|
||||||
|
delegate might provide more flexibility).
|
||||||
|
|
||||||
|
If the item is an associative container that uses \c{int},
|
||||||
|
\l{Qt::ItemDataRole}, or QString as the key type, and QVariant as the
|
||||||
|
mapped type, then QGenericItemModel interprets that container as the storage
|
||||||
|
of the data for multiple roles. The data() and setData() functions return
|
||||||
|
and modify the mapped value in the container, and setItemData() modifies all
|
||||||
|
provided values, itemData() returns all stored values, and clearItemData()
|
||||||
|
clears the entire container.
|
||||||
|
|
||||||
|
\snippet qgenericitemmodel/main.cpp color_map
|
||||||
|
|
||||||
|
The most efficient data type to use as the key is Qt::ItemDataRole or
|
||||||
|
\c{int}. When using \c{int}, itemData() returns the container as is, and
|
||||||
|
doesn't have to create a copy of the data.
|
||||||
|
|
||||||
|
Gadgets and QObject types are also represented at multi-role items if they
|
||||||
|
are the item type in a table. The names of the properties have to match the
|
||||||
|
names of the roles.
|
||||||
|
|
||||||
|
\snippet qgenericitemmodel/main.cpp color_gadget_0
|
||||||
|
|
||||||
|
When used in a list, these types are ambiguous: they can be represented as
|
||||||
|
multi-column rows, with each property represented as a separate column. Or
|
||||||
|
they can be single items with each property being a role. To disambiguate,
|
||||||
|
use the QGenericItemModel::SingleColumn and QGenericItemModel::MultiColumn
|
||||||
|
wrappers.
|
||||||
|
|
||||||
|
\snippet qgenericitemmodel/main.cpp color_gadget_1
|
||||||
|
|
||||||
|
\section1 Trees of data
|
||||||
|
|
||||||
QGenericItemModel can represent a data structure as a tree model. Such a
|
QGenericItemModel can represent a data structure as a tree model. Such a
|
||||||
tree data structure needs to be homomorphic: on all levels of the tree, the
|
tree data structure needs to be homomorphic: on all levels of the tree, the
|
||||||
@ -241,55 +327,15 @@ QT_BEGIN_NAMESPACE
|
|||||||
row type. QGenericItemModel will never allocate new rows in lists and tables
|
row type. QGenericItemModel will never allocate new rows in lists and tables
|
||||||
using operator new, and will never free any rows.
|
using operator new, and will never free any rows.
|
||||||
|
|
||||||
So, using pointers at rows comes with some memory allocation and management
|
Using pointers at rows comes with some memory allocation and management
|
||||||
overhead. However, when using rows through pointers the references to the
|
overhead. However, when using rows through pointers the references to the
|
||||||
row items remain stable, even when they are moved around in the range,
|
row items remain stable, even when they are moved around in the range, or
|
||||||
or when the range reallocates. This can significantly reduce the cost
|
when the range reallocates. This can significantly reduce the cost of
|
||||||
of making modifications to the model's structure when using insertRows(),
|
making modifications to the model's structure when using insertRows(),
|
||||||
removeRows(), or moveRows().
|
removeRows(), or moveRows().
|
||||||
|
|
||||||
So, each choice has different performance and memory overhead trade-offs.
|
Each choice has different performance and memory overhead trade-offs. The
|
||||||
The best option depends on the exact use case and data structure used.
|
best option depends on the exact use case and data structure used.
|
||||||
|
|
||||||
\section2 Multi-role items
|
|
||||||
|
|
||||||
The type of the items that the implementations of data(), setData(),
|
|
||||||
clearItemData() etc. operate on can be the same across the entire model -
|
|
||||||
like in the \c{gridOfNumbers} example above. But the range can also have
|
|
||||||
different item types for different columns, like in the \c{numberNames}
|
|
||||||
case.
|
|
||||||
|
|
||||||
By default, the value gets used for the Qt::DisplayRole and Qt::EditRole
|
|
||||||
roles. Most views expect the value to be
|
|
||||||
\l{QVariant::canConvert}{convertible to and from a QString} (but a custom
|
|
||||||
delegate might provide more flexibility).
|
|
||||||
|
|
||||||
If the item is an associative container that uses \c{int},
|
|
||||||
\l{Qt::ItemDataRole}, or QString as the key type, and QVariant as the
|
|
||||||
mapped type, then QGenericItemModel interprets that container as the storage
|
|
||||||
of the data for multiple roles. The data() and setData() functions return
|
|
||||||
and modify the mapped value in the container, and setItemData() modifies all
|
|
||||||
provided values, itemData() returns all stored values, and clearItemData()
|
|
||||||
clears the entire container.
|
|
||||||
|
|
||||||
\snippet qgenericitemmodel/main.cpp color_map
|
|
||||||
|
|
||||||
The most efficient data type to use as the key is Qt::ItemDataRole or
|
|
||||||
\c{int}. When using \c{int}, itemData() returns the container as is, and
|
|
||||||
doesn't have to create a copy of the data.
|
|
||||||
|
|
||||||
Gadgets and QObject types are also represented at multi-role items if they
|
|
||||||
are the item type in a table. The names of the properties have to match the
|
|
||||||
names of the roles.
|
|
||||||
|
|
||||||
\snippet qgenericitemmodel/main.cpp color_gadget_0
|
|
||||||
|
|
||||||
When used in a list, these types are ambiguous: they can be represented as
|
|
||||||
multi-column rows, with each property represented as a separate column. Or
|
|
||||||
they can be single items with each property being a role. To disambiguate,
|
|
||||||
use the QGenericItemModel::SingleColumn wrapper.
|
|
||||||
|
|
||||||
\snippet qgenericitemmodel/main.cpp color_gadget_1
|
|
||||||
|
|
||||||
\section2 The C++ tuple protocol
|
\section2 The C++ tuple protocol
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user