I have ListModel which I manage in QML. After editing I want to send this model to C++ (as a QStringList for example, or something else I can work with in c++).
I know I can expose a list from C++ to qml and edit it from there, but for my app logic that does not really make sense. Can I somehow create a list like datatype in QML which I can send to c++ with a signal?
I think you can just send the QStringList to the C++ code and it ill be handled. See the docs linked below.
string QML Basic Type
When integrating with C++, note that any QString value passed into QML
from C++ is automatically converted into a string value, and
vice-versa.
Data Type Conversion Between QML and C++
Sequence Type to JavaScript Array
Certain C++ sequence types are supported transparently in QML as
JavaScript Array types.
In particular, QML currently supports:
QList<int>
QList<qreal>
QList<bool>
QList<QString> and QStringList
QList<QUrl>
If you for some reason QVariants to send data (as I do), you can convert it the following way:
// Test if the value is of the correct type (not nessary of one is sure that it is a list of strings)
if (val.canConvert<QStringList>() && val.convert(QVariant::StringList))
{
return val.toStringList();
}
Related
I am trying to connect a QML signal to a Qt slot with following parameter types:
in QML side:
signal Sig(var info)
in Qt side:
QObject::connect(topLevel, SIGNAL(Sig(QVariantMap)), &mObj, SLOT(mSlot(QVariantMap)));
Which gives me the following:
QObject::connect: No such signal QQuickWindowQmlImpl_QML_24::Sig(QVariantMap) in ...
So, I assume that types var and QVariantMap does not match. According to this document, QVariantMap types are converted to JavaScript objects. But I am not sure if it also does the other way around.
I have implemented an opposite type of connection(Qt signal with QVariantMap, QML handler with "Connections" element) which worked just fine. I was able to get the signal's argument as a JS object.
By the way, I have also tried the same connection with string argument types in my code, so I don't think that there is another unrelated mistake in my code.
How do I pass JS objects to Qt(C++) side using signal/slot mechanism? I haven't been able to find a solution. Not even an example that matches my case(signal and slot argument types), actually. Which makes me think that I am doing some sort of design mistake.
The parameters are QVariants on C++ side, so you need to do
QObject::connect(topLevel, SIGNAL(Sig(QVariant)), &mObj, SLOT(mSlot(QVariant)));
Note that you also need to change mSlot parameter type, because QVariant can't be implicitly converted to QVariantMap. To get the map in the slot, use QVariant::toMap() method, if it indeed is a map (if it isn't, this method returns empty map, and you need to do some debugging).
It works the other way around, because QVariantMap can be implicitly converted to QVariant.
I want to populate a QComboBox defined in QML from my C++ code. I have seen two possible ways to do this:
Define a list (as a QStringList for example) from the C++ code, and expose it as Q_ELEMENT. Then access that list from the C++, by saying model: backend.qlist assuming the list is defined in backend. Or
Find the QComboBox in the C++ code by using view.rootObject()->findChild(). Then use addItem() to populate the list.
What is best practice?
By far the first option!
QML stands for Qt Modeling Language, following the model-view architecture, in which the model (here C++) should not know anything about the view (QML).
The first option works very well. The implementation is easy. From C++ side create a method to return a list:
QVariantList getList()
{
QVariantList list;
list << "Op1";
list << "Op2";
list << "Op3";
return list;
}
And then call the method by QML like this:
comboBoxReader.model = backend.getList()
In C++ model, I have QAbstractListModelderived class called Cart which contains QList<void*>container.
In QML, I show a list of objects. When user clicks on any of them, it should create that object in C++ and add it to the cart. It will also set some properties of that object.
My question is how do I really do that in the best way?
Here is the code how this will look like in C++ alone:
Cart * cart = new Cart; // we have this object already created
// which we have exposed to QML as 'qmlcart'.
// When user clicks apple
Apple * apple = new Apple(1.99) ; // 1.99 is price
apple->setType("Red Delicious");
cart.add( apple );
// When user clicks orange
Orange * orange = new Orange(0.99) // price
orange->setType("Valencia")
cart.add( orange );
The above is straight forward in C++ world but it gets somewhat complex if the click is in QML. What is the best way create and pass information about the object? Assume we have more attributes about the object other than price and type.
Does there has to be a corresponding slot function for every type of object we create? If the object has more complex properties, say 5 various attributes, do we pass all 5 of them to slot function or there is a better way? Should we use some of factory design pattern to create object?
One particular ability I am missing with QML in use is the freedom to work with C++ created object. For example if it was all C++, I could have created the C++ object and than set all its attributes no matter how many are there and than simply add the object to the container.
But with QML, even though I have all information about the object but I can't really create and configure the class and than pass to C++ because it has to be created in C++ which can't return it and so all configuration parameters will have to be passed to the slot function as well! Is there any better way?
Unless the C++ object happens to be a QObject derived type, registered as a type to QML, you can't do that directly. Even in QML, you either need a component or a QML source wrapper to create a C++ object, for example:
\\Object.qml
import Object 1.0
Object {}
So now you have a QML source wrapper to create the C++ Object dynamically. Or using a component:
Component {
id: component
Object {}
}
You can however, have a C++ slot or Q_INVOKABLE you can call from QML, and do the object creation there.
You can set object properties in QML as well, for example:
Object {
someProperty: someValue
}
or
component.createObject(parentObj, {"someProperty" : somevalue})
it will work similarly to a constructor - all properties will be set before the object is considered to be "competed" by the runtime.
From your question it looks like you are getting ahead of yourself - do a little more learning before you rush into using QML... and there is really no need for those void * - this is not C. Don't make your life any harder.
My question is best clarified by an example. I have QML with a Text{} item. In C++ I can get to this item and I have no problem using qobject_cast to turn anything into a QQuickItem*. But how do I turn it into the closest corresponding item so that I can call more specific methods directly like setText() the same way I might call setWidth()? I realize I can use the generic setProperty() method but I'm after the compile time checking that casting offers.
I'm after a more general answer for finding the correspondence between QML and their C++ classes, so that I can find out how to do this for Rectangles, MenuBars etc. I can't seem to find this in the docs. For those that prefer code examples:
auto text_object = app_item->findChild<QObject*>("myTextArea");
text_object->setProperty("text","New Text set from Code"); //THIS WORKS BUT...
auto text_qitem = qobject_cast<QQuickItem*>(text_object);
text_qitem->setWidth(128);
auto text_quick_text = qobject_cast<WHATGOESHERE???*>(text_object);
text_quick_text->setText("new Text for qml item"); //I WANT TO DO THIS
Q: but I'm after the compile time checking that casting offers.
qobject_cast does not offer any compilation-time checking. It is all runtime and dynamic, thus this request is not plausible. The context property is fine, or you could also get the class name with QMetaObject. Then, you could build a static LUT, but the effort may not be worth it overall...
All QML properties and methods are exposed to the meta-object system and can be called from C++ using Object::setProperty and QMetaObject::invokeMethod() respectively. invokeMethod parameters and return values passed from QML are always translated into QVariant values in C++:
QString msg("That's it");
auto text_object = app_item->findChild<QObject*>("myTextArea");
if (text_object)
QMetaObject::invokeMethod(text_object, "append", Q_ARG(QString, msg));
I connected C++ and QML via a mediator-class and have everything working in both directions but this one puzzles me.
This is how I connect the mediator-class:
// Initialize Mediator between QML and C++
QmlCppMediator m_qmlCppMediator;
QDeclarativeContext *context = viewer.rootContext();
context->setContextProperty("cppInterface", &m_qmlCppMediator);
How to fire off an ordinary Property-Animation from within C++ ?
Ok I can answer this myself already.
I went for an approach described here http://qt-project.org/doc/qt-4.8/qdeclarativeanimation.html
I bind the “state” of the object which I try to animate to a Q_PROPERTY in the C++ interface.
The different states are linked to transitions (in QML) which do the animate the object.
Rather an easy way would be to define a JavaScript function inside the QML file itself, lie this:
function startAnimation() {
animationID.running = true;
}
Now call this code from C++, simple!