Access QML signal deep in the hierarchy - c++

I am trying to emit a signal on completion of creating an object. Like this:
Component.onCompleted: mySignal()
This is in a QML file deep in the hierarchy. I now see two solutions for accessing this signal in C++.
First is passing the signal up the hierarchy until main.qml and then in C++ do this:
//Create a Quick View object.
QQuickView *view = new QQuickView();
//Object to access QML properties and childs.
QObject *container = (QObject *) view->rootObject();
//Connect signal and slots
QObject::connect(container, SIGNAL(mySignal()), this, SLOT(onMySignal()));
This I have tried and for some reason the slot is not called. It works for all the other signals I send and emit from main.qml, but not the one emitted from Component.onComplete. I can verify that the signal is emitted from the QML side, but never received on the C++ side.
The second thing I tried was instead of passing the signal to main.qml, I would get a reference to the QML file emitting the signal I want. I tried that doing so:
//Create a Quick View object.
QQuickView *view = new QQuickView();
//Object to access QML properties and childs.
QObject *container = (QObject *) view->rootObject();
//Connect signal and slots
QObject::connect(container->findChild<QObject*>("mySignalQmlFile"), SIGNAL(mySignal()), this, SLOT(onMySignal()));
where mySignalQmlFile is the ID of the main rectangle that has the signals defined within it.
and I get the error:
QObject::connect: No such signal QQuickRectangle_QML_54::mySignal() in ..\GC\mainwindow.cpp:62
I am now not sure how to proceed.

For your first try, I think it is possible that your signal is emitted before you even connect your signal and slot so that it is never received the signal.
For your second try, you need to use objectName from your qml file, not "mySignalQmlFile".
I recommend you to read this following tutorial.
http://developer.nokia.com/community/wiki/Using_objectName_to_find_QML_elements_from_Qt
Also, your qml file needs to be called or used before you connect signals and slot. Otherwise, it will not be able to find the object so that you would get the same error.

Related

How to execute QML slot from C++ without a signal?

I got a QML object in the QObject shape. With ->setProperty(..., ...) I can change properties, but how can I execute slots without signals?
Currently I declare a signal in my C++ class:
signals:
void testSignal();
and signal/slot in QML object:
signal executeTestFunc(); onExecuteTestFunc: {
testAnimation.start();
}
Then I connect these two:
QObject::connect(this, SIGNAL(testSignal()),
QmlObject, SIGNAL(executeTestFunc()));
But this is not clean, as I can see:
De facto this is strange SIGNAL/SIGNAL connection.
I do not want to use SIGNAL/SLOT mechanism, unless I have to, due to performance and long code.
Is there a way to execute QML onExecuteTestFunc(); from QObject directly?
You can create a C++ class that will emit a signal. That signal will be caught in QML. No explicit connection with SIGNAL/SLOT is needed. Example:
C++:
class Presenter : public QObject
{
Q_OBJECT
public:
explicit Presenter()
{
QTimer *timer = new QTimer();
timer->setInterval(500);
connect(timer, &QTimer::timeout,
this, &Presenter::timeout);
timer->start();
}
Q_SIGNAL void timeout();
};
QML:
Window {
...
Presenter {
onTimeout: {
console.log("called from c++")
}
}
}
Result:
qml: called from c++
qml: called from c++
qml: called from c++
qml: called from c++
...
#Thomenson has already given a good answer on how you can connect to a signal from C++ and act on it in QML. His solution works if you can create the C++ from QML, which may not always be the case.
Connections element
There are two other options you might consider. First, if you have an object that was not created from QML but was put into it in some other way (QML context, static object, returned from an invokable...) you may use the Connections element:
Connections {
target: theObjectWithTheSignal
onSignalName: {doSomething();}
}
This is a flexible way to react to signals from object that you did not create in QML, or even objects that were created elsewhere in QML.
Callback using JSValue
If you really insist on avoiding signal/slot (though I don't understand why; Qt and QML are build around it and trying to avoid it is fighting against the framework instead of using its strenghts), there is another way. You can, on the C++ object that is exposed to QML, create a property of type QJSValue. Then, in C++ on setting, check that whatever was set is callable (using QJSValue::isCallable()). Then as the point you wish to trigger whatever you want to execute in your QML, call it using QJSValue::call.
On the QML side, you can simply assign or bind something callable, just like you'd do for a signal handler.
Anti pattern: QMetaObject::invokeMethod
There is another way, which I will only include as a warning against an anti-pattern. You can call into the QML from C++ using Qt's introspection mechanism. You can find an object by it's set objectName and call any (invokable) method on it using QMetaObject::invokeMethod, read and write any property and listen to all signals. That includes calling methods you defined in QML. Using this, you can manipulate your QML from your C++. Don't do this. It leads to inflexible and unmaintainable code.

connecting QGraphicsview signal to UI slot

I am new to QT and C++ and i have legacy qt-c++ code here which i cant get to work.
Probably its something about the lifetime of the calling objects but hey, but please tell me what i am missing.
In a QT .ui i have various Frames and Widgets containing Frames and Widgets containing a QVBoxLayout which we shall call "myLayout"
On click in the .ui file i use
myWidget = new mywidget(some params);
myLayout->addWidget(myWidget);
where myWidget is declared as mywidget *myWidget; in the header file
myWidget is a QWidget which internally adds a QVBoxLayout to itself and adds a QGraphicsView. Using the MouseReleaseEvent i emit a signal from the QWidget.
now when i try to connect the signal slot (which i do in cpp file from the ui)
connect(myWidget, SIGNAL(mySignal(QString)), this, SLOT(mySlot(QString)));
the signals never catch the slot. the slot is public, the signal isnt.
What did i do wrong? Can somebody help. Feel free to ask further questions since i dont really know whats important in c++ questions;)
edit:
the signal gets emitted by QGraphicsObjects which themselfs connect to a slot in the QGraphicsView. This Slot is called and debuggable. at the end of the Routine an emit mySignal("..."); is called.
Maybe you forgot to add Q_OBJECT macro in your widget declaration. Qt documentation: http://doc.qt.io/qt-5/qobject.html#Q_OBJECT

qt5 qml c++ interaction

http://doc.qt.io/qt-5/qtqml-cppintegration-interactqmlfromcpp.html#connecting-to-qml-signals
I've read most of that article, and seem to understand everything except for the part in c++. QObject::connect(item, SIGNAL(qmlSignal(QString)),
&myClass, SLOT(cppSlot(QString)));
It is clear where item, SIGNAL, myClass, SLOT, cppSlot and QString come from, but where does qmlSignal come from? Of course it comes from the .qml file, but then how does the compiler find out about it if it's loaded via runtime?
It is clear where item, SIGNAL, myClass, SLOT, cppSlot and QString
come from, but where does qmlSignal come from? Of course it comes from
the .qml file, but then how does the compiler find out about it if
it's loaded via runtime?
qmlSignal is emitted by QML code and caught on C++ side as you noted. And compiler knows nothing of what is happening at run time except C++ types the code deals with.
The root QML item is reflected to QObject that has a nested list of signals and slots very much like pure C++ QObject. Every signal and slot has a test string signature and slots also have the mapping to certain class member.
QQuickView view(QUrl::fromLocalFile("MyItem.qml"));
QObject *item = view.rootObject(); // get QObject from QML root
MyClass myClass;
QObject::connect(item, SIGNAL(qmlSignal(QString)), // find "qmlSignal(QString)" in the list of signals of 'item'
&myClass, SLOT(cppSlot(QString))); // connect that signal entry to the found cppSlot(QString) entry of myClass object
For better understanding of signal-slot internals there is a good article: http://woboq.com/blog/how-qt-signals-slots-work.html
The above is of course is about string-based connections: http://doc.qt.io/qt-5/signalsandslots-syntaxes.html
Not so many articles on QML signal/slot bindings but some: http://www.kdab.com/qml-engine-internals-part-2-bindings/

connect() seems to prefix signal with wrong namespace

I'm trying to use signals and slots to pass information to the GUI thread from another thread, as I can't modify a pixmap from any other thread. I'm encountering a runtime error:
Object::connect: No such signal QThread::image_change(std::string) in visualiser.cpp:33
Judging from this, though I may be wrong, it looks like the signal is being searched for in the wrong namespace, as it is actually defined in Visualiser::image_change().
My code is as follows:
Visualiser.cpp:
QFutureWatcher<void> watcher;
connect(watcher.thread(), SIGNAL(image_change(std::string)), QCoreApplication::instance()->thread(), SLOT(update_image(std::string)), Qt::QueuedConnection);
QFuture<void> update_thread = QtConcurrent::run(this, &Visualiser::update_state);
watcher.setFuture(update_thread);
...
emit(image_change(imageSrc));
...
void Visualiser::update_image(std::string src)
{
QImage image;
image.load(src.c_str());
ui->visualContainer->setPixmap(QPixmap::fromImage(image));
}
visualiser.h:
signals:
void image_change(std::string src);
public slots:
void update_image(std::string src);
Don't pass thread pointers into connect - pass pointers to the sender and receiver of the event (like this). Because you're giving it QThread pointers instead, Qt is looking for those signals and slots in QThread, where they don't exist. If you give it Visualizer pointers instead, Qt will look for those functions in Visualizer, where they really are, and everything will work.
Hope that helps!
The source and the target of the connection are the same object, this, so the connect call should be:
connect(this, SIGNAL(image_change(std::string)), this, SLOT(update_image(std::string)));
Since the signal will be emitted from another thread than the one the Visualizer has an affinity with (see QObject::moveToThread()), the connection with the slot will automatically be queued, and the slot will be executed by the correct thread.
But for queued connection to work, Qt has to store temporarily the parameter until it can actually call the slot, which is done by converting it to QVariant, storing it somewhere, and then reconverting it to the actual type when the receiving thread is ready to execute the slot.
So you need to register std::string to Qt's metatype system with Q_DECLARE_METATYPE or change the signal and slot parameter type to one that is already registered to (like QString or QByteArray).

How to simulate a QTreeWidget itemClicked signal without making a derived class?

I am unable to find a proper simulation for ItemClicked() SIGNAL for QTreeWidget.
Is there a way to simulate it so that ItemClicked Signal is generated ?
e.g: we can emit ItemClicked in a derived class of QTreeWidget but cannot (as a QT rule) outside of it.
You can't use the emit call for class A to emit class B's signals. But note that the documentation for signals and slots says:
"You can connect as many signals as you want to a single slot, and a signal can be connected to as many slots as you need. It is even possible to connect a signal directly to another signal. (This will emit the second signal immediately whenever the first is emitted.)"
So you can work around this by declaring a signal in class A of the same signature as the one you want class B to emit, and connecting the signals together:
connect(
myclass, SIGNAL(itemClicked(QTreeWidgetItem*, int)),
treewidget, SIGNAL(itemClicked(QTreeWidgetItem*, int))
);
Then emit itemClicked from myclass. If I'm not mistaken, it will work for this case...and fire the treewidget's itemClicked signal for you.