Filling QML ListView with data from std::map - c++

I'm having trouble finding a good way to display the items of a std::map in QML. We're using a MVVM pattern in our application. The std::map contains file paths and is a member of a configuration class in the model.
Now I'm trying to show all entries of the map in QML, probably using a ListView item.
Currently we just have a couple of file paths in the configuration, so these are exposed as Q_PROPERTYies to the view model and then further to QML. But, of course, the number of paths can and will grow, thus my idea using a std::map for this. It won't be necessary to have it 'growable' at runtime, at least not in the forseeable future. But writing lots and lots of Q_PROPERTYies doesn't seem the right way for me.
Furhter question: How would I access/display the items of the map in a QML ListView - I can't figure it out and I can't find anything helpful online.

Another option than #ddriver suggested would be to create a list of QObject-derived types like:
class ConfigObject: public QObject
{
Q_PROPERTY(QString key READ key)
Q_PROPERTY(QString value READ value)
// ... getters and key and value members
};
fill a QList with them and provide them as a model for ListView either via
QVariantList configModel;
// ... fill it with ConfigObjects from std::map
engine.rootContext()->setContextProperty("configModel", &configModel); //QQmlApplicationEngine here
or a property of some class:
Q_PROPERTY(QVariant configModel READ configModel NOTIFY configModelChanged)
Then you should be able to use it via modelData.key and modelData.value in your ListView delegate.
ListView {
model: configModel // in case of using context
delegate: Item {
...
Text {
text: modelData.key
}
Text {
text: modelData.value
}
}
}

You should implement a model adapter for std::map by extending a QAbstractListModel, then you can use that as the model for a QML ListView. Implementing the right set of model roles will allow you to access the map element members without the need to use Q_PROPERTY which also requires a QObject derived datatype. Then you only need to expose the model to QML, as a context property for example.
Keep in mind the map is a sorted container, so if you modify the model you should properly reflect the index of insertion and deletion.
So you will have:
std::map -> YourCustomListModel -> ListView

Related

How are parameters passed from Qml to C++?

I would like to use call a C++ function from Qml in the following way:
Item{
id: qmlItem
function loadModel(dataModel)
{
// setup my listview
}
MyDataModel{
id: model
}
Button{
onClicked: {
cpp.addValues(model)
loadModel(model)
}
}
}
Now in C++ I have the following function:
void myCpp::addValues(MyDataModel* model)
{
// here I would like to add items to my data model
model->addItem();
}
Now this would be possible if the model was passed as a pointer, but I am not sure if this is the behaviour of C++/Qml interaction. Would what I wrote work or there is another approach I need to use?
EDIT: I have compiled the program and it works fine. However, I would like to know what is going under the hood. I mean does QML sends parameters by pointer? Would the same approach work with a JavaScript array (instead of MyDataModel)?
Actually, if you want to use model for ListView which is passed from C++ you should look at the QAbstractItemModel or QAbstractListModel(maybe better and easier for your case) class from which you inherit and create your own model to use in the qml. To subclass QAbstractListModel from you'll at least need to reimplement rowCount() , data() methods, also you can reimplement insertRows() and removeRows() methods to dynamically add/remove data from model (which will also update the view automatically).
You should look up the http://doc.qt.io/qt-4.8/qabstractlistmodel.html or http://doc.qt.io/qt-4.8/qabstractitemmodel.html
. Also, there were some easy examples in qtcreator on how to implement this kind of model

A dynamic C++ model and a QML ListView

I'm using a QML list view which displays one element at a time
ListView
{
model: cppobj.list
...
}
cppobj is a C++ object which can be modified, i.e. items can be removed, appended, etc. If an element is appended, the ListView goes back to the first element. What's more ListView.onRemove is not called. Any ideas how to cope with it?
Thanks
/edit: the append function of the C++ object looks like that:
void append (QString str) { m_list.append(str); emit listChanged(m_list); }
You need to use QAbstractListModel. See documentation here.
In case you want to have a ListModel for variant JSON data that you can use directly in QML, you can have a look at the JsonListModel. It can synchronize JSON data to a ListModel so you do not lose the current scroll position of the list. You can also apply transition animations and have the full ListView/ListModel features available.
ListView
{
model: JsonListModel {
source: myJsonData
keyField: "id"
}
...
}
You can find a detailed guide how to use the JsonListModel here:

Is there a Dojo list container that sorts child widgets automatically?

I'd like a graphical container that I can add and remove my custom widgets to where I can set a sort function that is automatically applied when these operations take place.
Is there a suitable object in Dojo already that I've missed? Or maybe I'm not thinking about the problem correctly?
If not, are there any examples etc. of a minimal working custom container widget out there?
Dont think there is really - how would a standard component's sort functionality know, with which parameters it should weight the order, when containers can contain any widget type?
Using a layout widget extension would be your best option imho. They each have a function to add children, following this prototype:
addChild(/*Object*/ dijit, /*Integer?*/ insertIndex)
The dijit.layout.StackContainer is a good starting point, it inherits from dijit._Container (and dijit.layout._LayoutWidget). So you choose when to call the extension functionality of your override.
dojo.declare("my.Container", [dijit._Container], {
getSortOrder : function(newDijit) {
var newIndex = -1; ??
// something to work with
var currentChildren = this.getChildren();
var currentDescendants = this.getDescendants();
return newIndex;
},
addChild: function(dijit, index) {
// figure out index
arguments[1] = this.getSortOrder(dijit);
this.inherited(arguments);
}
});
But be aware, that layoutwidgets has more to it then choosing order, also positioning like with bordercontainer's region parameter.
Use SitePen's dgrid, then define a List widget with a column of type Editor. Send your custom widget to the Editor's parameter. dgrid's List widget should be able to sort as if it were a grid based on your data, and the Editor column should be able to display anything you want as part of a List's item's content.
If you need anything I'll be around. Luck,

Change C++ model with QML

I want to extend the example called "Object ListModel Example" from Qt documentation
(you can get it on http://qt-project.org/doc/qt-4.8/declarative-modelviews-objectlistmodel.html).
I am trying to add a simple GUI functionality: a menu item that changes the content
(i.e. name) of the first data item in the model. Something like this:
MenuItem {
text: "Item 123"
onClicked: {
myModel.setProperty(0,"name","Item 123") //this gives me error
}
}
I am able to create a menu in QML but I cannot find the correct way to make changes in the model.
Btw, what is a difference between setContextProperty and qmlRegisterType (only the first one is used in this example but many other examples include the second one).
That kind of model is really not suitable for modification. There is no way for the view to be notified of changes. A better option is to use a QAbstractItemModel: http://qt-project.org/doc/qt-4.8/declarative-modelviews-abstractitemmodel.html
A simpler way to use a QAbstractItemModel is via QStandardItemModel: http://qt-project.org/doc/qt-4.8/qstandarditemmodel.html
setContextProperty() adds a single named property to the context. qmlRegisterType() registers a QObject-derived type with the QML engine, allowing it to instantiate that type. For example, the QDeclarativeItem type is registered with the engine as "Item", which is how the engine knows what to create when Item {} appears in QML code.

Do I need to implement my own QAbstractTableModel?

I found this question: How to change the background color for a QTreeView Header (aka QHeaderView)?
I want to be able to set the color for each header section. So the question seen above seems to be my solution!
The solution says "the easiest way to do that is probably to derive a new model from QAbstractItemModel or another model class and reimplement the headerData()". I went and looked at the Qt source tree for QTableWidget, QTableModel, QTableWidgetItem... these classes are supposedly "default models" so I thought they would be a good example and then I would go implement my own QAbstractTableModel.
The 3 files I saw are a whopping 3300 lines of code. That is definitely NOT "the easiest way" IMO!!!
I would like the functionality of QTableWidget but then I want to add the following ability:
horizontalHeader.setSectionColor(index,color)
verticalHeader.setSectionColor(index,color)
Do I really need to inherit/implement QAbstractTableModel if all I want is to change the color of a section header?
Update:
I am not using my own custom view and model classes. I am using the convenience class QTableWidget right now (it is called a convenience class b/c it implements the view and model). The function headerData() is part of the model. The model class, QTableModel, is not accessible via Qt lib/headers so I can't inherit from it.
Update:
I tried creating a new item with background brush QBrush(QColor(Qt::red)) and then setting the table's header with the new item (using QTableWidget::setHorizontalHeaderItem(int column, QTableWidgetItem *item). I also tried inheriting QTableWidgetItem and overriding the virtual data() method with:
QVariant HeaderItem::data(int role) const
{
if(role==Qt::BackgroundRole) {
return QVariant(QBrush(QColor(Qt::red)));
} else if(role==Qt::ForegroundRole) {
return QVariant(QBrush(QColor(Qt::green)));
} else {
return QTableWidgetItem::data(role);
}
}
I can change the header sections foreground. But when I try to change the header's background brush... nothing happens... it's like the QTableWidgetItem's background brush that I set for the header section is ignored.
Instead of creating model with custom headerData() from scratch create subclass of QTableWidgetItem with desired implementation of QTableWidgetItem::data() and use the instances of this class for QTableWidget::setHorizontalHeaderItem.