PDA

View Full Version : Return QObject undefined



coderbob
5th November 2010, 11:19
If I pass an object to a QML and that object in turn grabs another object I error out with the following:

"code example/main.js:4: TypeError: Result of expression 'failedObject' [undefined] is not an object."

Now if I change line 16 in object1.h (and of course its matching function in object1.cpp) to QObject the script will convert it fine and I will not have any errors. The problem with this is I then have to qobject_cast when using the function internally in the c++ code. This casting internally just to get it to be recognized correctly in the script feels wrong to me.

Since my class inherits QObject I was under the impression I should not have to use qRegisterMetaType. It appears to be registered correctly since it knows how to cast it in the script when it is passed as a QObject.

I am confused on why I must use QObject and let the script auto cast it and I cannot reference my class directly if it is already registered. Also not sure if casting my class internally will have a performance hit or is just bad form?

Short Question: Why cant I reference a return object using my specific class identifier in a script?

I attached a very small code example that will show specifically what the problem is.

wysota
7th November 2010, 13:59
My impression is you need to call qmlRegisterType for qml to be aware of your object type.

coderbob
8th November 2010, 09:56
That will work for QML code but I am trying to get the script code to reconize it. I need something like qScriptRegisterMetaType but I do not have a QScriptEngine since the script is being handled by the QML.

My guess is it might not be possible right now or I am looking at it wrong.

wysota
8th November 2010, 10:23
Could you prepare an example that actually does something? When I run your code I only get a blank window and no messages to the console.

coderbob
9th November 2010, 08:56
Could you prepare an example that actually does something?

Just click anywhere in the box and you will see the error message in the console.



Now if I change line 16 in object1.h (and of course its matching function in object1.cpp) to QObject the script will convert it fine and I will not have any errors. The problem with this is I then have to qobject_cast when using the function internally in the c++ code. This casting internally just to get it to be recognized correctly in the script feels wrong to me.

wysota
9th November 2010, 11:02
It works (no error, object name is printed) if I use qmlRegisterType. Why haven't you tried it after I have given this solution to you?

coderbob
9th November 2010, 16:30
I still get the error, can you please post your code changes.

wysota
9th November 2010, 16:35
I added this to main().

qmlRegisterType<Object2>("com.test.object", 1, 0, "Object2");

coderbob
10th November 2010, 02:22
I error with:

In file included from object1.h:6,
from main.cpp:7:
object2.h:21: error: explicit specialization in non-namespace scope ‘class Object2’
object2.h:21: error: ‘struct QMetaTypeId<Object2*>’ redeclared with different access
object2.h:21: error: explicit specialization in non-namespace scope ‘class Object2’
object2.h:21: error: ‘struct QMetaTypeId<QDeclarativeListProperty<Object2> >’ redeclared with different access
make: *** [main.o] Error 1

wysota
10th November 2010, 03:31
I don't know what you did exactly to get this error but the line I gave you is the only change I made to the whole program (apart including headers for your classes, of course).

#include "object1.h"
#include "object2.h"

int main(int argc, char *argv[])
{
QApplication app(argc, argv);

QDeclarativeView view;
qmlRegisterType<Object2>("com.test.object", 1, 0, "Object2");
view.setResizeMode(QDeclarativeView::SizeRootObjec tToView);

Object1 *object1 = new Object1();

view.rootContext()->setContextProperty("myObject1",object1);

view.setSource(QUrl::fromLocalFile("main.qml"));
view.show();

return app.exec();
}

coderbob
12th November 2010, 01:17
Thank you Wysota, the problem I was having was experimenting with other possibilities and I did not back that code out before attempting the qmlRegisterType.

I have noticed that QML behaves differently when these objects are passed to the script as a signal. I have added to the previous code example that reproduces this.

The update adds a singleShot QTimer that passed the object1 twice to the script. Once declared class and the second as a QObject.

When passed as a class it is casted as a QVariant and I will not have access to any properties or slots.

When passed as a QObject I will have access to properties but is also casted differently then when it is passed using the doSomething() function. (Clickign anywhere in the window)

I could not find anyway to cast it from a QVariant in the script so I could access it properly. Also a little confused why it is casting the same object type differently.

coderbob
8th December 2010, 11:47
Appears to be a bug. Filed as http://bugreports.qt.nokia.com/browse/QTBUG-15712

wysota
8th December 2010, 12:03
Appears to be a bug. Filed as http://bugreports.qt.nokia.com/browse/QTBUG-15712

Where do you see the bug? Your code works fine if you register the types properly - with qml, with qvariant and with the signal/slots system.

coderbob
8th December 2010, 16:31
Wysota, please review the second code example more closely. Also follow the link in the report to the thread on the dev site.

To put it simply all 3 returned objects are casted differently.

wysota
8th December 2010, 20:34
I don't know how much more closely can I review the code. I don't know what doesn't work for you, to be honest. For me your application simply works, I can print the object name of the object returned by Object1::getObject2() just fine. What exactly is the "bug" you see?

coderbob
8th December 2010, 22:58
2 Signals are emitted and pass a QObject to the QML/js code. Both of these signals are interpreted by a single js function doSomething().

This first signal is defined as void mySignalSpecific(Object1 *obj); and is passing as a defined class.

The second is defined as void mySignalQObject(QObject *obj); and is passing as a QObject.

Both of these produce different results inside the function doSomething();

void mySignalSpecific(Object1 *obj) outputs:

QVariant(Object1*)
undefined

here we do not have access to myObject1.getObject2(); Also I did not see anywhere in the docs of how I could cast a QVariant in QML/js to be able to access it correctly.

void mySignalQObject(QObject *obj) outputs:

Object1(name = "")
object1 name property

and all works as intended. Right here you can see that the class is being casted differently when it is the same object. This method forces qcasting inside the c++ code or a separate function.


The third output when using view.rootContext()->setContextProperty("myObject1",object1); to give access to the object outputs.

Object2(0x69fc60)


Now the third example is not signal based but still shows how the same object is being casted differently when it should not be.

wysota
9th December 2010, 01:23
And please show me where in your code did you tell QVariant how to handle Object1* and where did you tell the signal/slot system how to treat Object*. How should QVariant know it should cast its data to Object1* when passing it through signals and slots?

I completely don't understand what is wrong with what you refer to as "third example". What is cast differently than what?

coderbob
9th December 2010, 02:06
And please show me where in your code did you tell QVariant how to handle Object1* and where did you tell the signal/slot system how to treat Object*. How should QVariant know it should cast its data to Object1* when passing it through signals and slots?

I am not sure where we are miss communicating here but your reply is explaining the problem.

First Object1 is a descendant of QObject which already puts it in the meta system. Secondly it is registered with qmlRegisterType<Object1>("com.test.object1", 1, 0, "Object1"); for QML.

Thirdly there is no reason it should be casted as a QVariant. This has been acknowledged by nokia and it is a lack of code in the signal/slot system related to QML.

If you can alter the code and make it work properly in 3 of the 3 examples instead of 2 it might help bridge the gap of the misunderstanding.

wysota
9th December 2010, 10:56
I am not sure where we are miss communicating here but your reply is explaining the problem.

First Object1 is a descendant of QObject which already puts it in the meta system.
Meta-system - yes. Signals and slots - no.


Secondly it is registered with qmlRegisterType<Object1>("com.test.object1", 1, 0, "Object1"); for QML.
QML - yes. Signals and slots - no.


Thirdly there is no reason it should be casted as a QVariant.
Sure there is. All custom things in Qt go through QVariant and signals and slots use QVariant as means for data storage with queued connections.


If you can alter the code and make it work properly in 3 of the 3 examples instead of 2 it might help bridge the gap of the misunderstanding.
I spent half an hour trying to understand your "simple" example so I'm definitely not going to alter it now. I still don't understand what your problem is. Your program outputs something but how should I know what you consider "correct" or not. If you want a simple example, have your program output three strings in the format "is: %1, should be %2". Then we can work on fixing the problem.

coderbob
9th December 2010, 13:09
According to what you said above Object1 must be registered with the signal/slot system.

I am not sure how I can make the code any simpler. Code to register Object1 with the signal slot system according to what you are saying is all that is needed.

Looking at the output you can see it is cast as a QVariant when the signaled using Object1 *.

It needs to be cast as a Object1* as being sent by the signal. I have no found anywhere in the docs for registering type specifically for the signal/slot mechanism.





Object1(name = "")
object1 name property


is the proper output in this example. If you look at the code example you can see void mySignalSpecific(Object1 *obj) is cast as a QVariant and you are unable to access the name property but you can when you use void mySignalQObject(QObject *obj).

wysota
9th December 2010, 14:10
Looking at the output you can see it is cast as a QVariant when the signaled using Object1 *.
What does the following mean? If that's an example of clear description of a problem then it's likely we understand differently what "clear" is.


QVariant(Object1*)
undefined
Object1(name = "")
object1 name property
Object2(0x17392c0)

coderbob
9th December 2010, 17:05
I am at a loss as to what to say wysota. The code cannot become much simpler and I am not sure how to phrase this so you can understand it. I will attempt one last time and then I will just leave it to the trolls since it has already been addressed as a known issue to be resolved.


Sure there is. All custom things in Qt go through QVariant and signals and slots use QVariant as means for data storage with queued connections.

If this is true then please explain why when a class that inherits QObject is passed by defined type you cannot access any properties in QML/JS (The code example clearly shows this) it will be cast as a QVariant with no way to access its properties in QML/JS.


QVariant(Object1*)
undefined

That is the output when passed as a defined type

If this SAME OBJECT is passed in a signal/slot but is passed as a QOBJECT instead of it's defined type it will be casted correctly and the name PROPERTY will be accessible in the QML/JS code.



Object1(name = "")
object1 name property

That is the output when passed as a QObject



function displayObject(obj1)
{
print(obj1);
print(obj1.name);
}

All that does it attempt to access the property of object signaled. The only difference is how the signal is formated. The SAME EXACT OBJECT is being passed to the function and should be casted the same. This is the whole reason behind inheriting QObject and the meta system.


The output is as clear as day, I am not sure how this is not making sense to you.

All the code example does is pass the same object twice to the QML code which executes a script that attempts to read the name property of the object passed to it. In one case it is casted correctly and in the other it is casted as a QVARIANT. When it is casted as a QVARIANT the name property is not readable by the script code. This is the two seperate outputs you quoted. If you run the program look at the console window this is where the output is going.

You are saying it must be made known to the signal/slot mechanism. If this is true then please explain how a class that inherits QObject is made known to this mechanism.

wysota
9th December 2010, 19:22
I am at a loss as to what to say wysota. The code cannot become much simpler
Of course it can become much simpler. You only need to show three statements in three different situations and it doesn't require a bunch of files and having to click some area in a window.


If this is true then please explain why when a class that inherits QObject is passed by defined type you cannot access any properties in QML/JS
That's simple - because the scripting environment doesn't know your QObject subclass is really a QObject subclass so it doesn't try to extract the proper type from QVariant. This is a deficiency of QScriptEngine or QVariant, I guess.


it will be cast as a QVariant with no way to access its properties in QML/JS.
There is a trivial way to access your properties. Simply export a method that will perform the conversion in C++. Something like:

Object1* argToObject1(QVariant v) {
if(!v.canConvert<QObject*>()) return 0;
QObject *o = v.convert<QObject*>();
return qobject_cast<Object1*>(o);
}


The SAME EXACT OBJECT is being passed to the function and should be casted the same.
Well... "it depends". According to javascript you can never rely on the type of arguments passed to a function, it's not a strongly-typed language.


This is the whole reason behind inheriting QObject and the meta system.
And that's the only thing that makes this problematic.


The output is as clear as day, I am not sure how this is not making sense to you.
It's not that it doesn't make sense - I can analyze code and understand it but if you are providing a potential bug sample, you shouldn't force anyone to have to analyze your code for half an hour each time like I had to be doing from the very beginning of this thread. In my opinion if something is larger than 30 LOC, it's not a test-case anymore.