QML ListView sections not showing - qt

I am trying to display a list of contacts in a ListView in QML. The list comes from the c++ side of my app and the contents are displaying just fine.
Now I want to be able to add sections from the name, but it doesn't work as I want.
What is weird is that the following code does work and shows the sections I want :
Working solution :
main.cpp :
QList<QObject*> contactList;
engine.rootContext()->setContextProperty("contactsModel", QVariant::fromValue(contactList));
contoller.cpp :
...
// Call setContextProperty every time the list is updated
m_qmlEngine->setContextProperty("contactsModel", QVariant::fromValue(m_currentUser->contactListObjects()));
...
qml file :
ListView {
id: contactListView
model: contactsModel
section {
property: "name"
criteria: ViewSection.FirstCharacter
delegate: Rectangle {
...
}
}
}
However, the way that i find more elegant only shows the data from the model, not the section headers :
main.cpp :
Controller ctrl(engine.rootContext());
engine.rootContext()->setContextProperty("Controller", &ctrl);
contoller.h :
class Controller : public QObject
{
Q_OBJECT
Q_PROPERTY(QList<QObject*> contactListModel READ contactListModel NOTIFY contactListModelChanged)
...
};
qml file :
ListView {
id: contactListView
model: Controller.contactListModel
section {
property: "name"
criteria: ViewSection.FirstCharacter
delegate: Rectangle {
...
}
}
}
I find the second method more elegant because the data will be updated automatically from the signal that is emitted, whereas the first solution means I need to call the setContextProperty method each time.
I found a very similar question here, but unfortunately there are no answers.
What difference am I missing between the 2 solutions ?

Related

Repater: Using C++ property as model

I have a C++ property
Q_PROPERTY(QList<qreal> XTickPos MEMBER _xTickPos);
which I want to use in a Repeater. In the same QML file, the c++ class has been given the id
id: pw
The repeater looks like this
Item {
anchors.fill: parent
visible: true
Repeater {
model: pw.XTickPos.length
Rectangle{
height: 50
width: 2
x: pw.XTickPos[index]
y:10
visible: true
color: "black"
border.width: 2
}
}
}
However, nothing is drawn on the screen. If instead I make property in the QML file:
var xTickPos = []
and set it via a Q_Invokable function in c++
Q_INVOKABLE QList<qreal> getXTickPositions();
and in QML
root.xTickPos=pw.getXTickPositions();
and use the QML property xTickPos as model in the above repeater it is working. I checked that pw.XTickPos is correctly filled via a console.log
What am I missing here?
This one is kinda tricky.
The documentation states that you can use a JS array as a model, and it does state that QList<qreal> is automatically converted to a JS array when returned to QML.
But it seems that you can't use a QList<qreal> that is automatically converted to a JS array as a model. Go figure...
Naturally, it is preferable to have a proper model with its proper notifications for top efficiency. But in case you really want to go for the list property, it appears you will have to do the conversion manually in a getter:
QVariantList model() {
QVariantList vl;
for (auto const & v : yourList) vl.append(v);
return vl;
}
Amazingly, although Qt supposedly makes that conversion automatically, it doesn't seem to be able to make a QVariantList from a QList<qreal>.
That's Qt for you...

pass data from one window to another (inside same QML file)

i got two Windows inside the same .qml file.
Window1 has a textinput1 and a button, and when I press the button, i'd like to send the string value from that textinput to the Window2 textInput2.
I'm new to Qt and QML, been reading a lot on signals, Loaders, properties and can't materialize this kind of transfer. Can anyone do a simple 10-line example of such transfer please?
Window {
id:window
TextInput {
id:text1
text: "This value is needed in the second Window!"
}
Button {
onClicked: window2.open()
}
Window {
id.window2
function open(){
visible = true
}
Text {
text: text1.text
}
}
}
If I do this it gives me ReferenceError: text1 is not defined, how can I reference the text1 from the first Window?
I would prefer to use signals in such case:
Window {
id: window1
title: "window 1"
visible: true
width: 600
height: 600
signal someSignal()
Button {
anchors.centerIn: parent
text: "Open window"
onClicked: window1.someSignal()
}
Window {
id: window2
title: "window 2"
width: 400
height: 400
// you can use this instead of Connections
//Component.onCompleted: {
// window1.someSignal.connect(show);
//}
}
Connections {
target: window1
onSomeSignal: {
window2.show();
}
}
}
I think this is more ... how do you say? ... more imperative -)
i got two Windows inside the same .qml file.
If you did then your code will work. Since it doesn't work, I will assume each window is defined in its own qml file, and you only instantiate the two windows in the same qml file.
If I do this it gives me ReferenceError: text1 is not defined, how can
I reference the text1 from the first Window?
You will have to be able to reference the window first, and it should provide an interface to reference the text.
Keep in mind that ideally ids should only be used to reference stuff in the same source. On rare occasions you could go further, and reference ids down the object tree, but only parents, and none of their out-of-line children, it will however work for in-line children that are given ids in that same source. Meaning that if window2 is created inside window then you will be able to reference window from inside window2. But if both windows are siblings in another object, the id won't resolve.
Obj1
Obj2
Obj4
Obj3
In this example object tree, Obj1 will resolve from any of the objects. However, Obj3 will not be able to resolve Obj2 if the id is given inside Obj2, but will resolve if the id for Obj2 is given inside Obj1. But there is no way to resolve Obj4 from Obj3. because the id doesn't act like a property, you can't do someId.someOtherId, that's only possible for properties. You cannot do somePropertyObject.someId neither. You can only begin with either an id or a property, and continue only with sub-properties.
When the id is not applicable, can expose objects or properties either as properties or property aliases. The first is useful when you want to expose a whole object, the second when you want to expose a particular property of an object:
Item {
property Item innerItem: inner // gives you access to the "entire" inner object
property alias innerWidth: inner.width // gives you access to a property of inner
Item {
id: inner
}
}
You can also have aliases to aliases.

How to display correctly Treeview with QML Qt 5.5

I'm trying to create a correct Treeview with Qml Qt 5.5.
I succeed to have a Treeview with a global root.
But impossible to find how to add child for row item.
For the moment I got something like that :
TreeView {
id:listTree
anchors.fill: parent
anchors.leftMargin: 1
headerVisible: false
backgroundVisible: false
selection: ItemSelectionModel {
model: myModel
}
TableViewColumn {
role: "name"
}
itemDelegate: Item {
Text {
anchors.verticalCenter: parent.verticalCenter
color: styleData.textColor
elide: styleData.elideMode
text: styleData.value
}
}
Component.onCompleted: {
model.append({"name":"Never"})
model.append({"name":"gonna"})
model.append({"name":"give"})
model.append({"name":"you"})
model.append({"name":"up"})
model.append({"name":"Never"})
model.append({"name":"gonna"})
model.append({"name":"let"})
model.append({"name":"you"})
model.append({"name":"dow"})
}
}
And I would like something like that :
How can I do it ?
You can also create a TreeModel class that extends QStandardItemModel and overrides roleNames(), like done here. To add children to nodes in your tree, just use appendRow().
TreeModel::TreeModel(QObject *parent) : QStandardItemModel(parent)
{
QStandardItem *root = new QStandardItem("root");
QStandardItem *child = new QStandardItem("child");
this->appendRow(root);
root->appendRow(child);
}
Your model doesn't have any parent child relationships which is why its displayed like a list.
You'll want your "TreeModel" to be a collection of TreeItems. Each TreeItem will have knowledge of their own children and their parent item.
You can follow a fully implemented Qt example found here http://doc.qt.io/qt-5/qtwidgets-itemviews-simpletreemodel-example.html. You'll want to (in C++) make a class for TreeItem, and a separate class for your TreeModel.
That example is working code, you can just copy and paste it and get a working model for your TreeView.
The part you'll be particularly interested in is the implementation of the method setupModelData(). That's where you'll want to parse through your wonderful dataset of 80's lyrics and assign each of them a TreeItem.
Each TreeItem (one for every row of data) should be given knowledge of its parent upon creation (in its constructor). Then as soon as its children are created, call parentTreeItem.appendChild(childTreeItem)
When your model is completed, you can assign it to your qml view in a few ways, registering it with qmlRegisterType is what I prefer (http://doc.qt.io/qt-5/qqmlengine.html#qmlRegisterType)
Once registered, it can be created in qml as though it were a ListView or any other qml object.
NOTE: You'll have this rootItem. This is something that isn't usable by the view, but all your "first indentation" parents are children of the rootItem.
Good luck!
Can you provide a code snippet of what line is causing your about failing to make a shortcut for QAbstractItemModel?

Does qml supports object oriented programming

I'm using QML for my project, I want to know if am instantiating a file in another file, is it like instantiating object for a c++ class?
File.qml
Rectangle {
id: idRect1
.
.
}
File2.qml
Rectangle {
id: idRect2
File1 {
id:idFile1
.
.
}
}
In File2.qml i have initialized File1, does it means i have created an object of type File1? Please share some knowledge(links) on how all this mechanism works. Thanks in Advance
In QML when creating a file with first letter uppercase, you're creating a component. Components are implemented using OOP aggregation (not subclassing). That mean if I write
// MyButton.qml
import QtQuick 2.0;
Rectangle {
id: base;
width: 120;
height: 40;
color: "lightgray";
Text {
text: "foobar";
anchors.centerIn: parent;
}
}
... I haven't subclassed Rectangle, I just created a component that contains a Rectangle as root object, and configurates it in a certain way, and adds a Text object in it.
As soon as a component is created, it can be instanciated by simply writing :
MyComponent { id: myNewInstance; }
Because that's a way it works in QML.
The component name is a kind of class (but not in the C++ or JS way to define it) and it can also be used as a type for a property :
property MyComponent theComponent : myNewInstance;
Then it can hold the ID of an object created with the given component, acting somewhat like a C/C++ pointer : the property holds a link to the actual object.
But because of the way QML was designed, even if it's more aggregating than subclassing, a property of the type of the root object of a custom component can also hold ID of a derived component, in my case :
property Rectangle theComponent : myNewInstance;
Will work, but if I try to put an ID of an Image or Text or something else, QML engine will throw incompatible types error.
I hope it helps you.

How to make state-based model change in QML

I'm trying to make a QML-based dictionary application. It fetches the word definition via an XML RESTful API and displays them in a ListView. I have it working in this rudimentary mode. But now I'm trying to implement two states for the ListView: standard view with the definitions and a "did you mean" type suggestions list for when the search failed.
My current code for the ListView is like this:
ListView
{
SuggestionModel{id:suggestionModel; currentWord : "test"}
SuggestionDelegate{id:suggestionDelegate}
model : XmlModel{id: standardModel; currentWord : "test"}
delegate : ListDelegate{id:standardDelegate}
clip : true
anchors.top : hbox.bottom
y : hbox.height + 3
width : parent.width
height : parent.height - hbox.height
id : list
states :
State { name: "suggestion"; when: list.model == suggestionModel ||
list.model.status == XmlListModel.Ready && list.count == 0
PropertyChanges {
target: list
model : suggestionModel
delegate : suggestionDelegate
}
}
focus : true
keyNavigationWraps : true
}
which gives this error:
Unable to assign QObject* to QDeclarativeComponent*
for the PropertyChanges declaration. There is also a binding loop but that's not really an issue I couldn't fix. My problem is how do I define the states. I can't instantiate the model and delegate inside the State declaration either as the interpreter will complain about creating a state-specific object.
SuggestionDelegate is being instantiated. The delegate property requires a Component which it will instantiate itself for each item it displays. So to provide a Component rather than an instance you need to wrap the SuggestionDelegate in a Component and use the Component id in the PropertyChanges:
Component {
id: suggestionDelegate
SuggestionDelegate { }
}
Though Martin's solution fixed the problem I was having, I came up with a better design for the UI. Since the definitions and suggestions view are mutually exclusive, I implemented each as its own item which have the same geometry and are displayed or hidden according to the current state. This also allows for nice transition animations.

Resources