qt5 qml c++ interaction - c++

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/

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.

Why do I get "QMetaObject::connectSlotsByName: No matching signal" with a custom dialog?

I am using Qt5.
I have a very simple dialog class that inherits from QDialog.
I have a class that uses this dialog, and also a QFileDialog:
NameDlg m_name_dlg;
QFileDialog m_file_dlg;
This class also has some slots to handle dialog closing:
private slots:
void on_dlgName_accepted();
void on_FileDlgClosed(int result);
In the constructor I do some connecting:
QObject::connect(&m_file_dlg, SIGNAL(finished(int)),
this, SLOT(on_FileDlgClosed(int)));
QObject::connect(&m_name_dlg, SIGNAL(accepted()),
this, SLOT(on_dlgName_accepted()));
The first call to connect is fine, but the second call generates the output:
QMetaObject::connectSlotsByName: No matching signal for on_dlgName_accepted()
Curiously, my slot is correctly called when the name dialog is accepted!
The documentation (and header file) for QDialog says:
Signals
void accepted()
So it isn't a parameter mismatch.
As I said above, the signal is correctly called so my code all works fine, I just would really really like to understand why I get this warning (as an educational exercise) and also get rid of it (for peace of mind).
I cannot post full code, but I do believe there should be enough for anyone to understand the problem.
Things I have tried:
Using finished() instead.
Casting &m_name_dialog to a QDialog *.
Changing the signal to QDialog::accepted().
Thanks in advance.
In some part of your code you are using the method connectSlotsByName, if you have created a design (.ui) this usually calls it since compiling generates a file ui_somefile.h, and this file is used.
According to the docs:
void QMetaObject::connectSlotsByName(QObject *object)
Searches recursively for all child objects of the given object, and
connects matching signals from them to slots of object that follow the
following form:
void on_<object name>_<signal name>(<signal parameters>);
From the above it is observed that this method will try to connect the slots that have that format, and in your case the second slot fulfills it, when trying to connect it looks for the objects and signals but in your case it does not find it since the object does not exist dlgName and generates the warning you see.
This method is created by the .ui file because through the design you can create slots by right clicking on the widget and selecting go to slot, choosing the signal and finally creating the slot.
Note:
If you are going to create your own slot, avoid using the underscores as this could cause you problems because Qt would try to connect it and if the objects do not exist it will send you several warnings.

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

Qt dynamic stylesheet with User Interface Compiler

I'm working with a Qt application that uses an XML file to generate the user interface, via the Qt User Interface Compiler.
I don't have access to the code that holds each widget (I do but the Qt UI Compiler re-generates it each time), so I'm not able to add another method to the class that it generates.
I'm trying to do a setStyleSheet on one of the QLineEdit widgets, but it gives me a QPixmap: It is not safe to use pixmaps outside the GUI thread warning and then eventually seg faults. After resigning myself to not being able to go this route, I decided to test having two copies of each widget, each with the stylesheet values needed. I would then trigger a QLineEdit::hide() and QLineEdit::show() on the widgets as needed, which I thought would work.
It didn't. The program now spits out QCoreApplication::sendPostedEvents: Cannot send posted events for objects in another thread whenever run.
What can I do to fix this? I need to change the stylesheet of the widget dynamically, but seem unable to do so in any manner.
You can't be invoking any QWidget methods from threads other than the main thread. It's fairly easy to indirectly invoke such methods safely from any thread, though. See this answer for details.
For example, suppose you wanted to call setStyleSheet on a widget, from code that runs in some other thread:
template <typename F>
static void postToMainThread(F && fun, QObject * object) {
QObject signalSource;
QObject::connect(&signalSource, &QObject::destroyed, object, std::forward(fun));
}
void threadCode(QWidget * widget) {
postToMainThread([widget]{
widget->setStyleSheet("color: black");
}, widget);
}

Access QML signal deep in the hierarchy

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.