How to get data from QML to C++? - c++

I try to write some application using FileDialog and I have to get file urls in my C++ class. I try to do this in this way:
FileDialog {
id: fileDialog
objectName: "fileDialog"
selectMultiple: true
signal getFiles(var urls)
title: qsTr("Open file")
nameFilters: [qsTr("MP3 files (*.mp3)"), qsTr("All files (*.*)")]
onAccepted: getFiles(fileDialog.fileUrls)
}
........................................
class MyClass : public QObject
{
Q_OBJECT
public slots:
void addToPlaylist (const QList<QUrl> & urls){
for(int i = 0; i < urls.length(); ++i)
qDebug() << "Get\n";
}
};
............................................
QObject *fileDialog = root->findChild<QObject *>("column")->findChild<QObject *>("row")->findChild<QObject *>("openButton")->findChild<QObject*>("fileDialog");
MyClass myClass;
QObject::connect(fileDialog, SIGNAL(getFiles(QVariant)), &myClass, SLOT(addToPlaylist(QList<QUrl>)));
I found solution but I do not understand. Can someone explain me in my example?
just a question (and maybe solution), why do you need to define a
signal in QML for this? can’t you simply call the c++ slot and pass
the URL list directly?

Well,you are trying to pass a list of urls from qml to c++. These are the ways.
1)Make sure that you make an object of MyClass available to QML by calling QQmlContext::setContextProperty() and registering that object.
Example:
QQuickView view;
MyClass myObj;
view.rootContext()->setContextProperty("myObj", &myObj);
Then you can call your MyClass::addToPlaylist() from QML like this.
FileDialog{onAccepted: myObj.addToPlaylist(fileDialog.fileUrls);}
Reference: Embedding C++ Objects into QML with Context Properties
2)Make the FileDialog instance emit a signal whenever user selects some files from QML. Now this signal needs to be connected to the slot of an object of MyClass. In order to do this, you need to have pointers to both FileDialog instance and object of MyClass object.
You are getting pointer to an instance of FileDialog by doing this.
QObject *fileDialog = root->findChild<QObject *>("column")->findChild<QObject *>("row")->findChild<QObject *>("openButton")->findChild<QObject*>("fileDialog");
Now you are connecting the signal and slot. Thats it.

Related

Invoking c++ slots from plasmoid qml

New question for you guys.
I have a simple kde (kf5) plasmoid, with a label and two buttons.
I have a C++ class behind the scenes, and I am currently able to send signals from C++ to qml.
The problem: I need to send signals from the qml buttons to the C++ class.
Usually this could be done by using the standard Qt/qml objects like QQuickView and so on, but in my case I have no main.cpp.
This is my C++ class header. Using a QTimer, I emit the textChanged_sig signal, which tells the qml to refresh the label's value:
class MyPlasmoid : public Plasma::Applet
{
Q_OBJECT
Q_PROPERTY(QString currentText READ currentText NOTIFY textChanged_sig)
public:
MyPlasmoid( QObject *parent, const QVariantList &args );
~MyPlasmoid();
QString currentText() const;
signals:
void textChanged_sig();
private:
QString m_currentText;
}
This is the plasmoid main.qml:
import QtQuick 2.1
import QtQuick.Layouts 1.1
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.plasmoid 2.0
import org.kde.plasma.components 2.0 as PlasmaComponents
Item {
Plasmoid.fullRepresentation: ColumnLayout {
anchors.fill: parent
PlasmaComponents.Label {
text: plasmoid.nativeInterface.currentText
}
PlasmaComponents.Button {
iconSource: Qt.resolvedUrl("../images/start")
onClicked: {
console.log("start!") *** HERE
}
}
}
}
The PlasmaComponents.Label item contains the correct value of the c++ field m_currentText.
*** HERE I need to emit some signal (or invoke a c++ method, would have the same effect).
Any hint?
Since you can access the currentText property through plasmoid.nativeInterface that object is almost certainly an instance of your C++ applet class, i.e. a MyPlasmoid instance.
So if your MyPlasmoid has a slot, it can be called as a function on the plasmoid.nativeInterface object
in C++
class MyPlasmoid : public Plasma::Applet
{
Q_OBJECT
public slots:
void doSomething();
};
in QML
onClicked: plasmoid.nativeInterface.doSomething()

How to use a custom Qt C++ type with a QML slot?

I want to use my C++ type registered in QML as parameter in QML slot. So I already have C++ signal which has QString type parameter:
emit newMessage(myString);
and QML slot:
onNewMessage: {
console.log(myString);
}
Now I want to pass C++ type registered in QML as parameter of this slot.
I found similar question, but I don't understand how I can use this vice versa. Any help will be useful.
So Lets say you wanted to pass a QObject derived file back and forth from C++ to QML via a signal/slot setup.
class MyClass : public QObject
{
Q_OBJECT
public:
explicit MyClass(QObject *parent = 0);
signals:
void sendSignal(MyClass* anotherMyClass)
public slots:
void send_signals() { emit sendSignal(this); }
};
If you have a C++ type called MyClass which is a QObject-based type, and you successfully did
qmlRegisterType<MyClass>("com.myapp", 1, 0, "MyClass");
Then In QML all you have to do is this:
import QtQuick 2.0
import com.myapp
Item {
MyClass {
id: myInstance
Component.OnCompleted: {
// connect signal to javascript function
myInstance.sendSignal.connect( myInstance.receiveSignal);
}
function receiveSignal(mySentObject) {
// here is your QObject sent via Signal
var receivedObject = mySentObject;
// Now to send it back
// (will cause an endless loop)
myInstance.sendSignal(receivedObject);
// or you could do this to perform
// the same endless loop
receivedObject.send_signals();
// or you could do this to perform
// the same endless loop
receivedObject.sendSignal(myInstance)
}
}
}
Summary:
You can just send any QObject-derived object as long as its registered with the QML engine as the actual object, you just treat it like any other type in QML
Hope this helps

Receiving C++ signal in QML

I have the signal being emitted and then caught in QML; however, when I try to read the parameters attached to the signal I get "undefined." Following are some code snippets. Thanks for the help ahead of time!
mymodel.h
class MyModel : public QObject
{
Q_OBJECT
...
signals:
void mySignal(float a, some::enum b)
...
}
mymodel.cpp
Do something to emit the signal (this isn't a problem, simply emit mySignal(1.0, 2.0);)
someotherclass.cpp
void SomeOtherClass::setupQML()
{
...
QQuickView *view = new QQuickView();
QWidget *container = QWidget::createWindowContainer(view);
...
QmlRootData = new RootData();
view->rootContext()->setContextObject(QmlRootData);
view->rootContext()->setContextProperty("MyModel", model);
view->setSource(QUrl("main.qml"));
view->setResizeMode(QQuickView::SizeRootObjectToView);
QObject* rootObj = view->rootObject();
...
}
main.qml
Rectangle {
Connections {
target: MyModel
onMySignal: console.log(a)
}
}
The above console.log(a) gets called when expected; however, I would expect the output to be "1.0" but it simply says "undefined" and I'm not sure why. I am using Qt 5.1 and Qt Quick 2.0.
It may be that the enum param is causing an error in the process that binds the parameters into the QML signal handler's context. Since it doesn't appear that this enum is exposed as a type to QML I don't believe it can correctly translate it into qml and this might break the whole process.
Given that you are passing two floats when emitting the signal, is it actually supposed to be two float inputs instead of a float and an enum?

How to set event listner on a QML object received say from QDeclarativeView?

We had an object that we instatniated in our main programm and passed it into QML View. There were defined a Q_PROPERTY. I wonder how to set event listner on to NOTIFY signal?
If I understand correctly, this Qt DevNet forum post has a setup similar to yours. Only in that case they are defining the READ function which in turn emits the somethingHappened signal. Be sure to scroll all the way down to the last comments for the working example.
In summary, you have something like this defined in your C++ header file:
class SomeClass : public QObject {
Q_OBJECT
Q_PROPERTY(sometype someProperty READ getSomeProperty NOTIFY somePropertyChanged)
signals:
void somePropertyChanged();
};
QML_DECLARE_TYPE(SomeClass)
Something like this in your C++ main method:
qmlRegisterType<SomeClass>("SomeModule", 1, 0, "SomeClass");
SomeClass myObj;
QDeclarativeView view;
view.rootContext()->setContextProperty("rootItem", (SomeClass *)&myObj);
Then on the QML side you would handle it like this:
import SomeModule 1.0
SomeClass {
onSomePropertyChanged: {
// do stuff
}
}

Communication between C++ and QML

This page shows how to call C++ functions from within QML.
What I want to do is change the image on a Button via a C++ function (trigger a state-change or however it is done).
How can I achieve this?
UPDATE
I tried the approach by Radon, but immediately when I insert this line:
QObject *test = dynamic_cast<QObject *>(viewer.rootObject());
Compiler complains like this:
error: cannot dynamic_cast '((QMLCppBinder*)this)->QMLCppBinder::viewer.QDeclarativeView::rootObject()' (of type 'struct QGraphicsObject*') to type 'class QObject*' (source is a pointer to incomplete type)
In case it is relevant, QMLCppBinder is a class that I try to build to encapsulate the connections from several QML pages to C++ code. Which seems to be trickier than one might expect.
Here is a skeleton class to give some context for this:
class QMLCppBinder : public QObject
{
Q_OBJECT
public:
QDeclarativeView viewer;
QMLCppBinder() {
viewer.setSource(QUrl("qml/Connect/main.qml"));
viewer.showFullScreen();
// ERROR
QObject *test = dynamic_cast<QObject *>(viewer.rootObject());
}
}
If you set an objectName for the image, you can access it from C++ quite easy:
main.qml
import QtQuick 1.0
Rectangle {
height: 100; width: 100
Image {
objectName: "theImage"
}
}
in C++:
// [...]
QDeclarativeView view(QUrl("main.qml"));
view.show();
// get root object
QObject *rootObject = dynamic_cast<QObject *>(view.rootObject());
// find element by name
QObject *image = rootObject->findChild<QObject *>(QString("theImage"));
if (image) { // element found
image->setProperty("source", QString("path/to/image"));
} else {
qDebug() << "'theImage' not found";
}
// [...]
→ QObject.findChild(), QObject.setProperty()
So, you could set your C++ object as a context property on the QDeclarativeView in C++, like so:
QDeclarativeView canvas;
ImageChanger i; // this is the class containing the function which should change the image
canvas.rootContext()->setContextProperty("imgChanger", &i);
In your ImageChanger class, declare a signal like:
void updateImage(QVariant imgSrc);
Then when you want to change the image, call emit updateImage(imgSrc);.
Now in your QML, listen for this signal as follows:
Image {
id: imgToUpdate;
}
Connections {
target: imgChanger;
onUpdateImage: {
imgToUpdate.source = imgSrc;
}
}
Hope this helps.