I have the following C++ model structure:
Manager // QAbstractListModel
↪ Slot // QAbstractListModel
↪ Processor // QAbstractListModel
↪ name // Q_PROPERTY
I pass only the Manager reference to QML on instantiation. I need to fill the ComboBox with Processor names but I don't know how to fill up with this nested structure.
Here is the code I plan to have (but is not working now):
ComboBox {
model: Repeater {
model: manager
delegate: Repeater {
model: slot
delegate:Repeater {
model: processor
delegate: ListModel {
ListElement {text: name}
}
}
}
}
}
I know that delegates are for specifying how to display data (and that's why ComboBox doesn't have this one), but I'm out of ideas how to implement this correctly.
So my question is: how to fill up a ListModel recursively?
I came up with the following solution to recursively fill a ComboBox:
ComboBox {
id: comboBox
model: ListModel {}
textRole: "processorName"
Repeater {
model: manager
delegate: Repeater {
model: slot
delegate: Repeater {
model: processor
Component.onCompleted:
comboBox.model.append(
{"processorName": model.Processor.Name}
);
}
}
}
}
Add to your QAbstractListModel role that returns another QAbstractListModel.
Related
I have a model that works well with QTreeView. In the model I implemented a sort that looks like this:
void SimpleTreeModel::sort(Node* sortedNode)
{
emit layoutAboutToBeChanged(QList<QPersistentModelIndex>(), VerticalSortHint);
QModelIndexList oldIndices = persistentIndexList();
Node::SortType sortType = Node::Down;
//sort starting node
sortedNode->sortChildren(sortType);
QModelIndexList newIndices;
newIndices.reserve(oldIndices.size());
for(const auto &i : oldIndices)
{
Node* node = const_cast<Node*>(nodeFromIndex(i));
QModelIndex index = indexFromNode(node);
newIndices.push_back(index);
}
changePersistentIndexList(oldIndices, newIndices);
QModelIndex startingIndex = indexFromNode(sortedNode);
emit layoutChanged({ QPersistentModelIndex(startingIndex) }, VerticalSortHint);
}
when I call this function, QTreeView updates the view, but TreeView in QML don't do this.
QML TreeView usage:
TreeView
{
model: treeModel
TableViewColumn
{
title: "Title"
role: "title"
width: 700
}
}
What am I doing wrong? Why the view does not update the layout of the elements after sorting?
I think you need to delegate the tree view item. Data is provided to delegate.
Try changing your QML TreeView as shown below by adding itemDelegate
TreeView
{
model: treeModel
itemDelegate: Item {
Text {
color: styleData.textColor
text: styleData.value
}
}
TableViewColumn
{
title: "Title"
role: "title"
width: 700
}
}
Look into below link to understand the importance of delegate, between model and QML view. There is an image which easily explains.
http://doc.qt.io/qt-5/qtquick-modelviewsdata-modelview.html
Delegate - dictates how the data should appear in the view. The
delegate takes each data in the model and encapsulates it. The data is
accessible through the delegate.
I have a custom model that inherits from QAbstractItemModel. It implements a two-deep list (first layer has n elements, each element has m sub-elements). I can successfully bind a GridView's model to the first layer. How would I go about binding a nested ListView to display the second layer of elements?
GridView {
model: myModel
delegate: ColumnLayout {
Text { text: "First layer" }
ListView {
model: // What do I put here?
delegate: Text { text: "Second layer" }
}
}
}
You could use a DelegateModel.
The DelegateModel has a property called rootIndex which you can use to tell the ListView to shift the root of the model to the start of one of the sub-lists.
ListView {
model: DelegateModel {
model: myModel
delegate: {...}
rootIndex: index
}
}
Where index is the attached propery from your GridView which points to the parent entry for your sub-entries.
I created a dummy ListModel and loaded it to the TableView:
ListModel {
id: testModel
ListElement {
status: false
}
}
function testFunction() {
for (var i = 0; i < 45; i++) {
testModel.append({});
}
}
TableView {
model: testModel
TableViewColumn {
role: "status"
title: "Activation On/Off"
delegate: Checkbox {
id: idDelegatedCheckbox
}
}
}
I pretend to use the Checkbox as delegate component to select the items in the TableView. I check the first item in the table in the following way:
First item selected
Then I scroll down and return to the top and the items that I had selected disappear and other item is selected:
Different item selected
Do you know why?
Thanks in advance.
TableView delegates are instantiated as needed and may be recycled or destroyed at any time. As with any Qt Quick item views (ListView, GridView, PathView, TableView...), state should never be stored in a delegate. Hence, your CheckBox delegate must store its "checked" state in the model:
delegate: Checkbox {
id: idDelegatedCheckbox
checked: model.checked // read from the model when created or recycled
onCheckedChanged: model.checked = checked // write to the model when checked or unchecked
}
Currently some students and I are programming a little application with QtQuick.
We have the following TableView:
TableView {
model: ListModel {
id: orderListModel
Component.onCompleted: {
var tOrderList = orderController.getOrderList();
for(var i = 0; i < tTicketList.length; ++i){
orderListModel.append(tOrderList[i]);
}
}
}
TableViewColumn {
role: "orderId"
title: "Auftragsnummer"
width: 100
}
TableViewColumn {
role: "customer.name"
title: "Kunde"
width: 100
}
}
getOrderList returns a QList<Object*> with all the orders.
The Order class has a property customer
Q_PROPERTY(Customer* customer READ getCustomer NOTIFY customerChanged)
which in turns has a property called name.
We would like to show the latter property inside the TableView but unfortunately only the orderId property of Order does works.
What value should have the second role? How can we access data of the nested object?
According to the documentation you can use QList<QObject*> as a model for a view directly, without manually copying the data as you did. In particular:
The QObject* is available as the modelData property. As a convenience, the properties of the object are also made available directly in the delegate's context.
Considering the example linked in the documentation we have that a property of the QObject can be used as role in the following way:
ListView {
width: 100; height: 100
model: myModel // injected QList<QObject*> context property
delegate: Rectangle {
height: 25
width: 100
color: model.modelData.color // without "modelData" it would not work
Text { text: name }
}
}
The same applies for nested properties. If our QObject-derived object has a QObject property, like in your case, we can retrieve it via modelData and then access it's properties. So, for instance with the customer property, we would have something like this in the delegate:
Text { text: model.modelData.customer.name }
That's true for ListView et similia whereas I'm not sure it could be done directly with TableView. One possible workaround would be to combine the usage of the QObject-derived role with styleData.value. You can define the usage of the role in the role property and access the inner property inside the styleData.value. A solution for your case would look like the following (assuming myModel is a context property as the example above):
TableView {
width: 100; height: 100
model: myModel
TableViewColumn {
role: "customer"
title: "Kunde"
width: 100
delegate: Text {
text: styleData.value.name // accessing the property
}
}
itemDelegate: Item { } // should be set to empty to avoid warnings
}
A downside of this approach is that you should implement a delegate for each column.
I have an application that stores and edits notes. The list of notes is displayed in a listview like this:
Page {
id: noteList
title: i18n.tr("QNote")
visible: false
Column {
anchors.fill: parent
ListView {
anchors.fill: parent
model: notes
delegate: ListItem.Standard {
text: Title
onClicked: editNote(NoteText, Title, modelData);
progression: true
}
}
}
}
function editNote(text, title, item) {
pageStack.push(noteEdit, {title: title, text: text});
handler.setActiveItem(item);
}
The notes item is a NoteListModel that subclasses the QAbstractListModel and contains NoteListItems. What I would like to do is to store the currently selected NoteListItem so I could easily access the Note object inside when the user wants to save the modified note. However, I don't know how to access the backing NoteListItem from the qml delegate. the modelData seems to be something else. Is there any way to do so? If i could wrap the Note object in a QVariant I could access it easily through roles but when I tried it like this
QVariant NoteListItem::data(int role) {
switch (role) {
case Title:
return note.getTitle();
case NoteText:
return note.getText();
case NoteObject:
return QVariant::fromValue(note);
default:
return QVariant();
}
}
it resulted in a compiler error saying
qmetatype.h:642: error: invalid application of 'sizeof' to incomplete type 'QStaticAssertFailure'
Or should i try to access the selected list item from the backing code? Is there any way for that? Dou you have any ideas?
Thanks for your time. Regards,
Peter
This took me a very long time to find, as there are many incorrect solutions on Stackoverflow.
The pure QML way is to use a DelegateModel and access it from QML as follows:
import QtQuick 2.4
import QtQml.Models 2.1
ListView {
property var currentSelectedItem
onCurrentItemChanged{
// Update the currently-selected item
currentSelectedItem = myDelegateModel.items.get(currentIndex).model;
// Log the Display Role
console.log(currentSelectedItem.display);
}
model: DelegateModel {
id: myDelegateModel
model: myAbstractItemModel
delegate: {
// Define delegates here
}
}
}
This line returns an object (var) that you can access in the same way as within a delegate:
myDelegateModel.items.get(currentIndex).model
This example assumes you are only using the default DelegateModelGroup.
See http://doc.qt.io/qt-5/qml-qtqml-models-delegatemodel.html
and http://doc.qt.io/qt-5/qml-qtqml-models-delegatemodelgroup.html#get-method method