Need some guidance on using signals to communicate between QML and C++
-
Thank you for the prompt reply. I was attempting to use the examples in the same document albeit referenced from the trolltech.com site; I just happened to find that version first in my search.
I will give your example a closer look as the casting problem I described happened precisely when following the "Receiving Signals" section.
I have also found another "example":http://www.developer.nokia.com/Community/Wiki/CS001625_-_Connecting_Qt_signal_to_QML_function where dynamic_cast was used:
@QObject rootObject = dynamic_cast<QObject>(view.rootObject());@
I'll report back when I try these various methods.
Thanks again.
-
Your method worked wonderfully once I figured out that I could use QmlApplicationViewer just like a QDeclarativeView:
@
QmlApplicationViewer viewer;
viewer.setMainQmlFile(QLatin1String("qml/calcXY/main.qml"));
viewer.rootContext()->setContextProperty("calcXYObject, &calcXYClass);
viewer.showExpanded();
@I was confused initially as to how to setContextProperty to the root of the "Page" that was relevant for my function invocations, but then I figured out that handing the root of the entire QML tree should suffice.
So I suppose that I can set up all the various Calc classes (for the different functions to be calculated) using separate setContextProperty calls in main.cpp.
-
With the signal approach, things are complicated by the fact that if you use Qt Components the delayed instantiation of the page makes it difficult to set the signal up in main.cpp. Or am I missing something? The above example with the signal approach would read something like:
@
CalcXY calcXY;QObject rootObject = dynamic_cast<QObject>(viewer.rootObject());
QObject::connect(rootObject, SIGNAL(calcXYSignal()),
&calcXY, SLOT(calcXYSlot()));
@but then I get the following error at runtime:
@
Object::connect: No such signal PageStackWindow_QMLTYPE_13::calcXYSignal()
@which makes sense since calcXYSignal isn't declared in main.qml, which is the start page of the PageStack. It is declared in another QtComponent, calcxy.qml. One can easily imagine a set of such qml files: calcxy.qml, calcab.qml, calccd.qml, etc., each dealing with a specific function.
Is there a way of making this signals example work with Qt Components which are instantiated as needed and so I am presuming one cannot search the QML tree to get the appropriate node?
-
I'm not sure what hoops would have to be jumped through to dynamically search through the QML as needed. It may be possible, but I'm much too tired to think through it at the moment. At any rate, that's partially why I prefer to expose the C++ code to the QML. In my mind, the QML is much more dynamic and prone to change (either in the short-term, like loading and unloading of pages/components, or in the long-term via page redesign and layout changes and such) whereas the business logic (written in C++) is typically more stable and less likely to change as much. This way, your program only has to rely on a looser coupling between the C++ and QML than you'd have to have in order to have the C++ code know more details about the interface. I prefer to think of it as the solid, structured C++ code presenting itself to the QML, which is much more dynamic, fluid, and arbitrary.
Granted, there are always other options and different designs and philosophies, but this is just my opinion based on my experience.
-
Your approach makes a lot of sense now that I have experimented a bit. The C++ object is a singleton and so the namespace will be consistent to the various Qt Components. I see now why you prefer to expose the C++ slots to the dynamic QML code.
This will be the approach that I will take for my project.
Thanks for your (late night) help. It certainly removed the logjam in me proceeding with development.
-
It was my pleasure! I'm glad to help. If you have any other issues, feel free to ask! (And welcome to the forums, btw!)
One more thing, if you feel your issue is solved, please edit the initial post and change the title to add [Solved] to the beginning. Thanks!
-
Okay, I thought that the flip side—that is getting and setting the values of QML components from C++—would be easier to get through given the link provided above, but I am still stuck at a basic conceptual level.
My problem is two-fold:
- Say I have a Button and its associated TextField as a child:
@
Button {
id: xButton
x: 45
y: 231
text: "x"
onClicked: calcXY.calcX()Item { property alias x: xTextField.text TextField { id: xTextField /* anchors { left: parent.right; leftMargin: 10; verticalCenter: parent.verticalCenter } */ y: 136 width: 126 height: 50 text: "120" } } }
@
I need to be able to access the value of the TextField in my C++ code. Again using the link given above, the example points to using a property. However, the example doesn't really go into how to access an existing property. So I have attempted to use the example given "here":http://stackoverflow.com/questions/9062189/how-to-modify-a-qml-text-from-c.
The second problem listed below remains, but my first question is whether an intermediate Item wrapper is required at all? Is there no way to directly manipulate the text "property" in the TextField?
And if the Item wrapper is required, then what is the best way to preserve the anchor relationship between xButton and xTextField? The code given above binds the anchors to Item, which isn't what I want.
- My second problem is more fundamental: how does one get the root of the QML stack anywhere other than in main where the viewer is created and set:
@
QmlApplicationViewer viewer;viewer.setMainQmlFile(QLatin1String("qml/CalcXY/main.qml")); QObject *rootObject = viewer.rootObject();
@
In my CalcXY class, I need the root object if I am to use the example to affect the property x:
@
void CalcXY::calcXSlot()
{
qDebug() << "Property value:" << QDeclarativeProperty::read(rootObject, "x").toInt();
}
@In other words I need to have rootObject to navigate the QML tree. Is this a global variable? Do I need to set it up as a global in main, or is there a better method? How do I make rootObject visible inside the CalcXY object?
Thanks.
-
It would be simpler to just let the calcX slot accept a parameter. Then you could just send the data into C++ when you call the slot. Again, this decouples the C++ code from the QML.
As in:
@
Button {
...
onClicked: calcXY.calcX(xTextField.text);
}
@The calcX slot could then emit its result via a signal which the QML code could reference and respond to.
Typically, if you find yourself needing your C++ code to dig down into the QML, there's probably some redesign that can be done.
Additionally, there's no need to wrap your TextField in an Item. TextField, itself, IS an Item.
Is there any particular reason you choose to have the TextField as a child of the Button? From the commented-out anchor code, it appears you intend them to be laid out next to each other on-screen. I would make them siblings:
@
Button {
id: xButton
...
}TextField {
id: xTextField
...
}
@(An item doesn't need to be a child in order to allow access to its properties.)
-
I had initially handcoded the QML with the TextField and Buttons as siblings, but in one of my manipulations with Designer, I think the TextFields (there are three Button/TextField combinations) adopted child relationships.
I'll give your approach a try right now.
Thanks.
-
Nearly there. I have the signal mechanism working from C++ being caught via Connections in the QML.
Just a quick question: how does one trap when the user has finished editing a TextField, i.e., by leaving the field. In my case such an event would indicate that the other values may be calculated based on this change. I have tried 'onActiveFocusChanged' but that triggers upon both entry and exit of the TextField. 'onTextChanged' triggers an event for every character stroke. My other guess 'onDataChanged' was reported as invalid by the compiler. I also cannot seem to find a comprehensive list of these for the Qt Components for Symbian. In the Docs they are listed for Qt classes.
Essentially I have a (linear) function of 3 variables. When one of the values changes (the event that I would like to trap for indicated by the user leaving the field), the other two values will get updated to keep the function consistent. It is this 'value change' that I am trying to trap inside QML:
@
onActiveFocusChanged: calcXYObject.setX(xTextField.text)/* If x has been recalculated because either y or z were changed. */ Connections { target: calcXYObject onXHasChanged: console.log("x has changed") }
@
This code triggers both on entry into the x TextField as well as exit from the same.
Thanks.