[Qt-qml] Behaviour when sending QList<QObject*> as QVariant to QML?

636 views
Skip to first unread message

Pris Matic

unread,
Oct 8, 2011, 7:21:12 PM10/8/11
to qt-...@qt.nokia.com
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

unread,
Oct 9, 2011, 8:05:05 PM10/9/11
to qt-...@qt.nokia.com
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

Todd Rose

unread,
Oct 9, 2011, 9:32:24 PM10/9/11
to Pris Matic, qt-...@qt.nokia.com
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.

> _______________________________________________
> Qt-qml mailing list
> Qt-...@qt.nokia.com
> http://lists.qt.nokia.com/mailman/listinfo/qt-qml
>
>
_______________________________________________
Qt-qml mailing list
Qt-...@qt.nokia.com
http://lists.qt.nokia.com/mailman/listinfo/qt-qml

Johan Paul

unread,
Oct 9, 2011, 9:48:34 PM10/9/11
to Todd Rose, qt-...@qt.nokia.com
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

ronan.ma...@nokia.com

unread,
Oct 10, 2011, 11:50:48 AM10/10/11
to qt-...@qt.nokia.com
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

unread,
Oct 10, 2011, 1:16:03 PM10/10/11
to ronan.ma...@nokia.com, qt-...@qt.nokia.com
Hi Ronan!

On Mon, Oct 10, 2011 at 5:50 PM, <ronan.ma...@nokia.com> wrote:

> 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

unread,
Oct 10, 2011, 1:18:42 PM10/10/11
to ronan.ma...@nokia.com, qt-...@qt.nokia.com
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

--

Bo Thorsen

unread,
Oct 13, 2011, 12:04:40 PM10/13/11
to ronan.ma...@nokia.com, qt-...@qt.nokia.com
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.


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

unread,
Oct 13, 2011, 12:18:05 PM10/13/11
to Bo Thorsen, qt-...@qt.nokia.com
On Thu, Oct 13, 2011 at 6:04 PM, Bo Thorsen <b...@fioniasoftware.dk> wrote:
> 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;
> }
>

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?

> 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

Reply all
Reply to author
Forward
0 new messages