Discussion:
Behaviour when sending QList<QObject*> as QVariant to QML?
Pris Matic
2011-10-08 23:21:12 UTC
Permalink
Hi all,

I have a question about what exactly happens when you have a QList<QObject*>
in your C++ code, and then you send it to QML with something like this:

QVariant Abc::GetObjectList()
{ return QVariant::fromValue(m_object_list); }

Where m_object_list is a QList<QObject*> where the QObjects have slots that
can be directly invoked from QML (imagine using the list for something like
a ListView).

Does the QML side of things create a copy of the list, or do I have to
maintain it in C++? Ie if m_object_list was cleared after 'GetObjectList'
was called from QML, then would the list still be valid? If I was using a
ListView, would the ListView need to access that QList if it needed to draw
offscreen delegates as they became visible, etc.


Regards,

-Pris
Pris Matic
2011-10-10 00:05:05 UTC
Permalink
I'll try to answer my own question, in the hopes that it might benefit
someone else.

You definitely need to maintain those QObjects in memory on the C++ side.
When I pushed a QList<QObject*> to the model of a ListView, and then later
deleted the QList (using qDeleteAll), I got an instant seg fault. There's
also some unclear behaviour as to what happens when you clear the ListView
of all elements, and then try to delete the QList. I ran into the same
problem (seg fault) even though the model was now pointing to something
else. So right now I keep track of everything I send to the declarative
engine in this manner and then delete it when I exit the program. It's not
ideal, but it's the only solution that works for me.


Regards,

-Pris
Post by Pris Matic
Hi all,
I have a question about what exactly happens when you have a
QList<QObject*> in your C++ code, and then you send it to QML with something
QVariant Abc::GetObjectList()
{ return QVariant::fromValue(m_object_list); }
Where m_object_list is a QList<QObject*> where the QObjects have slots that
can be directly invoked from QML (imagine using the list for something like
a ListView).
Does the QML side of things create a copy of the list, or do I have to
maintain it in C++? Ie if m_object_list was cleared after 'GetObjectList'
was called from QML, then would the list still be valid? If I was using a
ListView, would the ListView need to access that QList if it needed to draw
offscreen delegates as they became visible, etc.
Regards,
-Pris
Todd Rose
2011-10-10 01:32:24 UTC
Permalink
You're already on the right track here. One thing that might be
helpful is to do this in your QObject's constructor:

QDeclarativeEngine::setObjectOwnership (this, QDeclarativeEngine::CppOwnership);

We also had to implement reference counting because we shared our list
objects throughout the program.
Post by Pris Matic
I'll try to answer my own question, in the hopes that it might benefit
someone else.
You definitely need to maintain those QObjects in memory on the C++ side.
When I pushed a QList<QObject*> to the model of a ListView, and then later
deleted the QList (using qDeleteAll), I got an instant seg fault. There's
also some unclear behaviour as to what happens when you clear the ListView
of all elements, and then try to delete the QList. I ran into the same
problem (seg fault) even though the model was now pointing to something
else. So right now I keep track of everything I send to the declarative
engine in this manner and then delete it when I exit the program. It's not
ideal, but it's the only solution that works for me.
Regards,
-Pris
Post by Pris Matic
Hi all,
I have a question about what exactly happens when you have a
QList<QObject*> in your C++ code, and then you send it to QML with something
QVariant Abc::GetObjectList()
{  return QVariant::fromValue(m_object_list);  }
Where m_object_list is a QList<QObject*> where the QObjects have slots
that can be directly invoked from QML (imagine using the list for something
like a ListView).
Does the QML side of things create a copy of the list, or do I have to
maintain it in C++? Ie if m_object_list was cleared after 'GetObjectList'
was called from QML, then would the list still be valid? If I was using a
ListView, would the ListView need to access that QList if it needed to draw
offscreen delegates as they became visible, etc.
Regards,
-Pris
_______________________________________________
Qt-qml mailing list
http://lists.qt.nokia.com/mailman/listinfo/qt-qml
Johan Paul
2011-10-10 01:48:34 UTC
Permalink
Sounds like using a QAbstractListModel would be easier for you than
using the QList<QObject *> approach.

I've found that using the latter is easy to get started, maybe for
throwing a PoC or something, but if you really want to use model in QML
from C++, it's worth implementing it with a QAbstractListModel. In the
end it's not even that much more code.

Or was there a reason the model had to be a QList<QObject *> thingy?


Cheers,

Johan
Post by Todd Rose
You're already on the right track here. One thing that might be
QDeclarativeEngine::setObjectOwnership (this, QDeclarativeEngine::CppOwnership);
We also had to implement reference counting because we shared our list
objects throughout the program.
Post by Pris Matic
I'll try to answer my own question, in the hopes that it might benefit
someone else.
You definitely need to maintain those QObjects in memory on the C++ side.
When I pushed a QList<QObject*> to the model of a ListView, and then later
deleted the QList (using qDeleteAll), I got an instant seg fault. There's
also some unclear behaviour as to what happens when you clear the ListView
of all elements, and then try to delete the QList. I ran into the same
problem (seg fault) even though the model was now pointing to something
else. So right now I keep track of everything I send to the declarative
engine in this manner and then delete it when I exit the program. It's not
ideal, but it's the only solution that works for me.
Regards,
-Pris
Post by Pris Matic
Hi all,
I have a question about what exactly happens when you have a
QList<QObject*> in your C++ code, and then you send it to QML with something
QVariant Abc::GetObjectList()
{ return QVariant::fromValue(m_object_list); }
Where m_object_list is a QList<QObject*> where the QObjects have slots
that can be directly invoked from QML (imagine using the list for something
like a ListView).
Does the QML side of things create a copy of the list, or do I have to
maintain it in C++? Ie if m_object_list was cleared after 'GetObjectList'
was called from QML, then would the list still be valid? If I was using a
ListView, would the ListView need to access that QList if it needed to draw
offscreen delegates as they became visible, etc.
Regards,
-Pris
_______________________________________________
Qt-qml mailing list
http://lists.qt.nokia.com/mailman/listinfo/qt-qml
_______________________________________________
Qt-qml mailing list
http://lists.qt.nokia.com/mailman/listinfo/qt-qml
r***@public.gmane.org
2011-10-10 15:50:48 UTC
Permalink
Hi all,

I am trying to get access to some global objects from a QML WorkerScript, and whatever I do I cannot seem to access them. The key object is a C++ object that implements a time consuming function, so I thought this might be the way to go.

The first attempt was to pass the object as inside the workscript message:

MyQml.qml:

Player {
id: player
}

WorkerScript {
id: workerThread
source: "qrc:player.js"
}

workThread.sendMessage( { object: player } );

This did not work - the message.object is not an object.

The second attempt, declare global variables inside the JavaScript file:

player.js:

var myplayer;

MyQml:

import "player.js" as Player

....

Player.myplayer = player
workThread.sendMessage( {} );

The WorkScript.onMessage function cannot access player.

I am sure that I have missed some crucial piece of documentation somewhere, but any help solving this issue would help.

Ronan.
Sivan Greenberg
2011-10-10 17:16:03 UTC
Permalink
Hi Ronan!
Post by r***@public.gmane.org
Player.myplayer = player
workThread.sendMessage( {} );
The WorkScript.onMessage function cannot access player.
I am sure that I have missed some crucial piece of documentation somewhere, but any help solving this issue would help.
In http://doc.qt.nokia.com/4.7-snapshot/qml-workerscript.html#sendMessage-method
, it is said:
"The message object may only contain values of the following types:
boolean, number, string
JavaScript objects and arrays
ListModel objects (any other type of QObject* is not allowed)
All objects and arrays are copied to the message. With the exception
of ListModel objects, any modifications by the other thread to an
object passed in message will not be reflected in the original
object."

Could this be causing your problems? I have no experience myself with
WorkerScript, however I know from other similar systems there is quite
some constraints on the "messages" you can pass as objects to other
context to be run in parallel...

If this is not the issue, I will be also quite curious to know how to
do something like this, how to pass the instances properly for
processing, consider Python's Queue.Queue module and multiprocessing.

-Sivan
Sivan Greenberg
2011-10-10 17:18:42 UTC
Permalink
Okay, re-reading this again, it seems you must serialize the object to
pass it to the worker script (the standard way of transmittable object
notation in js) which actually means you need to do something like
JSON.stringify or somesuch and JSON.parse it back to the object form
in your worker script.

Hope this helps and that it is correct :)

-Sivan
Post by Sivan Greenberg
Hi Ronan!
Post by r***@public.gmane.org
Player.myplayer = player
workThread.sendMessage( {} );
The WorkScript.onMessage function cannot access player.
I am sure that I have missed some crucial piece of documentation somewhere, but any help solving this issue would help.
In http://doc.qt.nokia.com/4.7-snapshot/qml-workerscript.html#sendMessage-method
boolean, number, string
JavaScript objects and arrays
ListModel objects (any other type of QObject* is not allowed)
All objects and arrays are copied to the message. With the exception
of ListModel objects, any modifications by the other thread to an
object passed in message will not be reflected in the original
object."
Could this be causing your problems? I have no experience myself with
WorkerScript, however I know from other similar systems there is quite
some constraints on the "messages" you can pass as objects to other
context to be run in parallel...
If this is not the issue, I will be also quite curious to know how to
do something like this, how to pass the instances properly for
processing, consider Python's Queue.Queue module and multiprocessing.
-Sivan
--
-Sivan
Bo Thorsen
2011-10-13 16:04:40 UTC
Permalink
I have used the idea to store objects in javascript for global access
before. You have to make sure you have ".pragma library" in the JS file,
though, or you will have each JS import declare it's own name scope. Not
what you want for a global variable :)

The JS file looks something like this:


.pragma library

var globalObject;

function getGlobalObject() {
return globalObject;
}

function setGlobalObject(object) {
return globalObject;
}

In each QML file, you import this one and then you have access to your
object.

I think this is a horrible piece of code, but it's the only way I have
found so far.

If possible, move the shared values you have to a C++ object instead and
declare this as a named global property. This is a cleaner way to access
shared values. But it only works on limited data types.

Bo.
Post by Pris Matic
Hi all,
I am trying to get access to some global objects from a QML WorkerScript, and whatever I do I cannot seem to access them. The key object is a C++ object that implements a time consuming function, so I thought this might be the way to go.
Player {
id: player
}
WorkerScript {
id: workerThread
source: "qrc:player.js"
}
workThread.sendMessage( { object: player } );
This did not work - the message.object is not an object.
var myplayer;
import "player.js" as Player
....
Player.myplayer = player
workThread.sendMessage( {} );
The WorkScript.onMessage function cannot access player.
I am sure that I have missed some crucial piece of documentation somewhere, but any help solving this issue would help.
Ronan.
_______________________________________________
Qt-qml mailing list
http://lists.qt.nokia.com/mailman/listinfo/qt-qml
Bo Thorsen,
Fionia Software.
--
Expert Qt and C++ developer for hire
Contact me if you need expert Qt help
http://www.fioniasoftware.dk
Sivan Greenberg
2011-10-13 16:18:05 UTC
Permalink
Post by Bo Thorsen
I have used the idea to store objects in javascript for global access
before. You have to make sure you have ".pragma library" in the JS file,
though, or you will have each JS import declare it's own name scope. Not
what you want for a global variable :)
.pragma library
var globalObject;
function getGlobalObject() {
  return globalObject;
}
function setGlobalObject(object) {
  return globalObject;
}
If you declare your object to be well, through the source location,
global, and using .pragma then why do you need the functions to access
it? Also, does you approach allows passing the whole object including
methods (so you can manipulate them for instance) as opposed to just
passing values and responses as strings as noted in WorkerScript qt
docs?
Post by Bo Thorsen
In each QML file, you import this one and then you have access to your
object.
I think this is a horrible piece of code, but it's the only way I have
found so far.
If possible, move the shared values you have to a C++ object instead and
declare this as a named global property. This is a cleaner way to access
shared values. But it only works on limited data types.
I was sure that due to the nature of objects serialization in JS, this
would actually be the only way to pass all of the types to the worker
script?


Thanks for the note!

-Sivan

Loading...