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

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.

Related

Handling Qt C++ events in QML context

The C++ API has QEvent along with multiple other classes derived from it (QMouseEvent, QGestureEvent etc.). QML on the other hand has events too. However I am struggling to find an elegant way of directly processing C++ events in QML.
Usually what I do is I create a custom QQuickWidget (or similar including QQmlEngine), override the QWidget::event(QEvent* event) and upon receiving a specific C++ event I propagate it through signals to QML slots with the QML code being loaded through that widget. This seems like a lot of work and I'm wondering if there is some sort of QML built-in event handling for events that come from C++ context.
In particular I'm interested in handling QGestureEvents in QML but I guess what works for this type of events should also work for any other type of event.
There is no direct support for event handling in QML, even keyboard and mouse are accessible through auxiliary objects.
QEvent itself is not a QObject derived, and as such, the same applies to all the derived events as well. That means no meta-information and no easy way to use from QML.
Since you are interested in a particular type of events, it would be easiest to create your own auxiliary object for those type of events, implement it in C++ and interface it to QML via signals you can attach handlers to.
If QGestureEvent can be copy-constructed you could simply create a Q_GADGET based adapter:
class QmlGestureEvent : public QGestureEvent
{
Q_GADGET
Q_PROPERTY(...) // for the things you want to access from QML
public:
QmlGestureEvent(const QGestureEvent &other) : QGestureEvent(other) {}
};
If it is not copy-constructable you'll have to add data members to the adapter and copy the values from the event.

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);
}

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/

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.

Event Handling in Qt

I am new to Qt GUI programming and come from a .NET/Winforms background as far as GUI development goes. I am using the Qt Creator IDE.
How can I handle a button press event in Qt to do something like the following:
if (button.clicked = true)
{
startProgram();
}
Also, how would I browse for a configuration file which would populate values into all of my line edit textboxes when opened? I am programming this in C++.
EDIT: I am taking a console app that someone else wrote and building a GUI around it. I want to access one of the functions in there from a button click event. However, I can't figure out how to get the functions of the original app to be in scope of the GUI which I have created when I try to use SIGNALS and SLOTS.
A simple example could be something like this. Say you have a class like ProgramStarter:
#include <QObject>
class ProgramStarter : public QObject {
Q_OBJECT
public slots:
void startProgram() {
//Do stuff
}
};
and something like this where ProgramStarter is used:
#include <QtGui/QApplication>
#include <QPushButton>
#include <QObject>
#include "programstarter.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QPushButton *testButton = new QPushButton("Push me");
testButton->resize(100, 40);
testButton->show();
ProgramStarter *program = new ProgramStarter;
QObject::connect(testButton, SIGNAL(clicked()), program, SLOT(startProgram()));
return app.exec();
}
Qt uses signals and slots to communicate between objects. The core idea is that signals are emitted when events occur and these signals can be connected to slots. In this case the button emits a clicked() signal when the button is pressed and the clicked() signal is connected to the startProgram() slot. So when the button is pressed, startProgram() is called.
Qt has a meta-object system for enabling the use of signals and slots, among other things. This is the reason why ProgramStarter inherits QObject and why the Q_OBJECT macro is in the header. I highly suggest reading this if you want to understand how signals and slots and the meta-object system work in Qt: http://doc.qt.io/qt-5/signalsandslots.html
It's a little long to explain but you connect the signal from the button to the slot with the function.
connect(button, SIGNAL(triggered()), this, SLOT(doSomething()));
You really need to read and understand signals and slots before trying to write code
edit: You are trying to call functions in a compiled separate running program?
Are you confusing Qt Signals with C/Unix signals? You could start the separate app from within your Qt app, get its PID and then send it a 'c' signal.
Or if you have the source of the app you could include it's code into your Qt app and then call any of the functions by simply wrapping them with a Qt slot handler.
In order to access the functions defined in the console application, you need to include the headers (.h/.hpp) that declare these functions, and link their implementation. I you have the source code of the implementations (.cpp), you need to add them to the sources to be compiled (they will be compiled in object files (.o) that will be linked in the final executable). If the console application is a front-end to a set of functions defined in a library (.lib, .a), you need to link this library.
After that, you can use Qt's signal/slot mechanism to connect the "button clicked" signal to a slot calling the function you want to execute, as described in other answers.
It seems like your problem is not really to do with Qt or GUI programming at all, but rather a general design issue. Others have told you how to be notified when the button is pressed - it's up to you to hook that notification up to whatever code you want to call. Some possibilities:
If you just need to call a function then include the appropriate header file and call it.
If you have an object you want to call a method on, then you will need to provide a reference to that object to the class which receives the notification.
If your console app is a separate program, you can use QProcess to launch and communicate with it.
I guess your problem is that you cannot call the console app's function, because it hasn't been defined to be a slot. So here is how you might do it.
Let's assume you have a console app class which might look like this:
class ConsoleApp
{
public:
void run()
{
// Code which you want to trigger by a button in your UI.
}
}
Then we implement a Qt based wrapper class which inherits from QObject class and, hence, is able to send and receive signals. This class simply provides a custom slot which delegates execution to the ConsoleApp class.
class ConsoleAppWrapper : public QObject
{
Q_OBJECT
public slots:
void startProgram()
{
m_consoleApp.run();
}
private:
ConsoleApp m_consoleApp;
}
Ok, and now we want a button which, when pressed, will trigger a call of the 'run()' method.
int main(int argc, const char** argv)
{
QApplication app(argc, argv);
// Define wrapper for console app.
ConsoleAppWrapper wrapper;
// Define button and connect its 'clicked()' signal
// to the wrapper's 'startProgram()' slot.
QPushButton startProgramButton("Start Program");
connect(&startProgramButton, SIGNAL(clicked()), &wrapper, SLOT(startProgram()));
// Show the button.
startProgramButton.setVisible(true);
// Start Qt's event loop.
app.exec();
}
This should give you a 'Start Program' button which, when pressed, will call ConsoleApp::run() method.
While this example is not Qt Designer based I hope this example helps you hunderstand how signals and slots work in Qt.
Cheers,
Jonny
If you have a button created with the Designer you can use on_myButton_clicked() slot in the parent widget class, just like in WinForms.