ListView doesn't update when its model data changes - qt

I have a QList<QObject*> that is read from within a ListView module into a model. At first the QList is an empty object. I want the indexBackup to be updated when I assign a new list to the QList object.
import QtQuick 2.5
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.2
import QtQuick.Controls.Styles 1.0
ListView {
id: layersListView
model: settingsList //QList<QObject*> object
int indexBackup: 0
onModelChanged: {
if(indexBackup && indexBackup < model.length)
{
indexBackup = model.length
}
else
{
indexBackup = - 1
}
}
}

For everybody with similar problem, the answer can be found here.

Related

How to create a "creatable" qml singleton?

I have a use case where I need to instantiate a singleton in qml (for property bindings).
i.e:
File: main.qml
Item{
MainQuery{id: mainquery
graphql: `query MainQuery{...}`
}
Text{
text: mainquery.data
}
}
This other file should use the query defined in main.qml
File: otherfile.qml
Item{
import MyLib 1.0 as Gql
Text{
text: Gql.MainQuery.data
}
}
However if you use QML_SINGLETON you can't initialize it in QML
and in order to bind to the graphql property above you would have to use
Connection.
Is there a better solution?
Note:
The object should be a singleton in CPP / Python side as well.
currently it is implemented in python like this:
class QSingleton(type(QObject)): # type: ignore
def __init__(cls, name, bases, dict):
super().__init__(name, bases, dict)
cls.instance = None
def __call__(cls, *args, **kw):
if cls.instance is None:
cls.instance = super().__call__(*args, **kw)
return cls.instance
class BaseQuery(QObjcet, QSingleton):
...
#QmlElement
class MainQuery(BaseQuery):
...
And gives this nice error when the qml engine tries to initiate the type:
QEventLoop: Cannot be used without QApplication
Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)
(I do have an eventloop running)
Whether the underlying Python object is or is not a singleton it doesn't matter. We can always declare a singleton wrapper in QML by defining a qmldir and using pragma Singleton in our source file. To demonstrate this approach, I wrap TextMetrics in my singleton object and expose TextMetrics properties as properties of my singleton so that I can use property binding with singleton syntax:
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Page {
Frame {
anchors.centerIn: parent
ColumnLayout {
TextField {
text: "Hello"
onTextChanged: MyWrapper.tmText = text
}
Text {
text: MyWrapper.tmWidth
}
}
}
}
// qmldir
module ExampleWrapper
singleton MyWrapper 1.0 MyWrapper.qml
// MyWrapper.qml
pragma Singleton
import QtQuick
import QtQuick.Controls
Item {
property alias tmText: tm.text
property alias tmWidth: tm.width
TextMetrics {
id: tm
text: "Hello"
}
}
You can Try it Online!
References:
https://doc.qt.io/qt-6/qtqml-modules-qmldir.html

declaring string in .qml files

I am trying to make a varaibles file in qml which declares several strings to be used by other qml files. for example: this would be Variables.qml
import QtQuick 2.0
Item {
property var mystring: qsTr("hello world")
}
and I want to reference it in another file tab1.ui.qml
import QtQuick 2.4
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.3
import "."
Item {
Rectangle {
id: rectangle
color: "#3e1edd"
anchors.fill: parent
Text {
id: text6
anchors.fill: parent
font.pixelSize: 12
text: Variables.mystring
visible: true
}
}
}
this gives an error: Unable to assign [undefined] to QString
please tell me if there is a better way to manage variables. I want them to be global so they can be used in multiple frames. Thanks
You must use a singleton, for it you must create a folder that contains the .qml with "pragma Singleton" at the top and a qmldir that indicate the files:
Global
├── qmldir
└── Variables.qml
qmldir
singleton Variables 1.0 Variables.qml
Variables.qml
pragma Singleton
import QtQuick 2.0
QtObject {
property var mystring1: qsTr("hello world1")
property var mystring2: qsTr("hello world2")
}
Then you must import and use it in the following way:
// others imports
import "Global"
// ...
Text{
// ...
text: Variables.mystring1
}
In this link you will find an example.

How to put multiple singletons in one qmldir file?

I want to use multiple singletons in one qmldir file but it doesn't seem to work, I don't have an error but the program doesn't launch.
qmldir:
singleton File1 1.0 File.qml
singleton File2 1.0 File2.qml
main:
import QtQuick 2.7
import QtQuick.Controls 2.1
Window {
visible: true
width: 640
height: 480
title: qstr("hello world!")
Button {
onCliked: File2.test();
}
}
File2.qml:
pragma Singleton
import QtQuick 2.7
Item {
signal test;
onTest: console.log("File2 received signal");
}
File1.qml:
pragma Singleton
import QtQuick 2.7
Item {
signal test;
onTest: console.log("File1 received signal");
}
The code works if I delete the second line in qmldir, but then File2 will be undefined.
If your qmldir file is exactly as you have written:
Singleton File1 1.0 File.qml
Singleton File2 1.0 File2.qml
The error might reside within that, as the keyword is singleton, not Singleton.
Try that:
singleton File1 1.0 File.qml
singleton File2 1.0 File2.qml
But that error should not fail silently. There should be some errors:
[main.qml] a component declaration requires two or three arguments, but 4 were provided
[qmldir] a component declaration requires two or three arguments, but 4 were provided
main.qml:
import QtQuick 2.7
import QtQuick.Controls 2.0
import '.'
ApplicationWindow {
id: window
width: 800
height: 600
visible: true
Row {
spacing: 3
Button {
text: 'single1'
onClicked: Single.sig()
}
Button {
text: 'signle2'
onClicked: Single2.sig()
}
}
}
singleton.qml
pragma Singleton
import QtQuick 2.0
QtObject {
signal sig
onSig: console.log('Singleton1 Received')
}
singleton2.qml
pragma Singleton
import QtQuick 2.0
QtObject {
signal sig
onSig: console.log('Singelton2 Received')
}
qmldir
singleton Single 1.0 singleton.qml
singleton Single2 1.0 singleton2.qml
Works like a charm.

QML - How to insert an Item at any position in StackLayout?

I need to add a component at a particular position in stack layout and I am not getting how to insert, following is the code.
import QtQuick 2.6
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
Component{
id:comp
Rectangle{
id:rect
anchors.fill:parent
}
}
StackLayout
{
id:stack
anchors.fill:parent
currentIndex:index //spin box will update this property
}
Button
{
id:insert
onClicked:
{
var res = comp.createObject(stack) // Should insert a rect at a position not at the end
}
}
Got the answer and also may be duplicate of:
https://stackoverflow.com/a/43225476/6336374
I used insert(index,item) of ObjectModel
Thanks to #derM it helped a lot.

List delegates driven live by JavaScript data model

I have a QML application where I'm creating lists of elements received from JavaScript. Using the details from this answer I'm populating the model as a JS array, and it works great. However, I'd like it so that when properties of the JavaScript objects change that ListView items driven from them update live.
Here's a simple test app showing the problem. The ListView is properly populated with MyRow instances showing the correct id/title, but when the rand property is changed by the timer, the List rows are unchanged (they show 0 for the last item).
MyRow.qml
import QtQuick 2.0
import QtQuick.Layouts 1.3
Rectangle {
property var obj
color:'#eeeeff'; height:20
RowLayout {
anchors.fill:parent
Text { text:obj.id }
Text { text:obj.title; Layout.fillWidth:true }
Text { text:obj.rand }
}
}
main.qml
import QtQuick 2.7
import QtQuick.Window 2.2
Window {
id:app; visible:true; width:200; height:100
property var database: ({"17":"World","42":"Hello"})
property var objById: ({})
function getObj(id){
if (!objById[id]) objById[id] = { id:id, title:database[id], rand:0 };
return objById[id];
}
ListView {
id:mylist
anchors.fill:parent
model: [42,17] // object ids
delegate: MyRow {
width:parent.width
obj:getObj(mylist.model[index])
}
}
Timer { // Update every object's rand value every second
interval:1000; running:true; repeat:true
onTriggered: {
Object.keys(objById).forEach(function(id){
objById[id].rand = Math.random()*100<<0;
})
}
}
}
How can I get the delegate's Text items to update their text when properties of the objects change?
The easiest (only?) way to get property bindings to work properly is to create real Qt objects to hook the values to. If you don't want to use a ListModel (because you want to quickly populate a model with items from a master library), then you can use createObject() to generate objects and pass them to your delegate.
Here's an updated main.qml that works as desired:
Window {
// ...same as above...
Component { // Creates real Qt objects with bindable properties
id:objFactory
QtObject {
property int id
property string title
property int rand:0
}
}
function getObj(id){
if (!objById[id])
objById[id] = objFactory.createObject( app, {id:id,title:database[id]} );
return objById[id];
}
// ...same as above...
}
Additionally, you may wish to change the property var obj in MyRow.qml to a more specific property QtObject obj (or a more specific object type, depending on what you pass in).
Finally, note that it's slightly cleaner/simpler to use modelData instead of mylist.model[index]:
ListView {
anchors.fill:parent
model: [42,17] // object ids
delegate: MyRow {
width:parent.width
obj:getObj(modelData)
}
}

Resources