Qt - C++ - Function of the child accessed through parent with connect - c++

Sorry if the title isn't really explicit. Here is the example, a classA has several child, childA1, childA2 etc.. stored in a list of classA :
QList<ClassA> listA;
listA << childA1() << childA2();
The childA1 has the function start() which doesn't exist in classA. So if I try :
listA[0].start();
The compiler says that classA has no member start().
start() being a public slot, I've done :
connect(this, SIGNAL(signalStart()), listA[0], SLOT(start()));
And it's working. So my question is how can he know this function start() after being stored as classA in the list ? Is there a way to know the "original" type ?

Qt signals/slots work using introspection (the ability to list methods and properties of any QObject at runtime).
Qt utilizes the Meta-Object Compiler tool to have introspection capabilities in C++. You may want to have a look at this blog post for more information on the internals of Qt signals/slots.
You can use introspection without signals/slots. So, in your example, You don't need to define the signal signalStart() just to be able to invoke the start() method. Instead, You can do something like this:
QMetaObject::invokeMethod(listA[0], "start");
This will look for the function named start in the specified QObject (regardless of the pointer type), and invoke it.
Here is a fully working example of using introspection:
#include <QtCore>
QTextStream& qOut(){static QTextStream out(stdout); return out;}
class MyObject : public QObject{
Q_OBJECT
public:
explicit MyObject(QObject* parent= nullptr):QObject(parent){}
~MyObject(){}
//Object has a slot named func
Q_SLOT void func(){ qOut() << "Hello func!\n"; }
};
int main(int , char*[]){
QObject* object = new MyObject();
//print the object's class name (this is the real class name!)
qOut() << object->metaObject()->className() << "\n";
//look for a function named func in the object, and invoke it
QMetaObject::invokeMethod(object, "func");
return 0;
}
//run MOC on this CPP file
#include "main.moc"

Firstly, you should store pointers of your objects in QList.
Then you could use dynamic_cast to determine which child type your object is:
A *obj = listA[0];
if (A1 *a1 = dynamic_cast<A1 *>(obj)) {
// This is an A1 object, you can call start()
a1->start();
} else if (A2 *a2 = dynamic_cast<A2 *>(obj)) {
// This is an A2 object
}

Related

C++ QT Call C++ function in QJSEngine

I'm trying to call a function which is implemented in C++ to call in a JS function, which gets evaluated with the QT QJSEngine.
So far I have tried this
class Person : public QObject {
Q_OBJECT
public:
Q_INVOKABLE cppFunction(int a);
};
My function evuation looks like this
QJSValue formulaFunction = jsEngine->evaluate("(function(" + functionArgList.join(",") + "){ " + formula + "})");
In formula I have a call to cppFunction and functionArgList provides the arguments, these work fine, I have checked them.
So, but somehow it doesn't get called, I don't get an error too. Am I missing something or should it work this way?
So, after some research I got it running.
class MyObject : public QObject{
Q_OBJECT
public:
Q_INVOKABLE MyObject() {}
Q_INVOKABLE bool hasChannel(int id);
};
QJSEngine engine;
QJSValue injectedObject;
injectedObject= engine.newQMetaObject(&MyObject::staticMetaObject);
engine.globalObject().setProperty("MyObject", injectedObject);
So, here I'm creating a new class which is inheriting from QObject.
The class has two methods, every method I want to invoke in the QJSEngine has to provide the Q_INVOKABLE Label in front of the method declaration.
An example JS code run by the engine would look like this
let myObject = new MyObject();
myObject.hasChannel(1234);
I don't know if the injected prefix is appropriate, but I couldn't think of a better one

Qt widget destructor indirectly calls widget method via connected signal, and crashes

I made a widget (QDataflowCanvas) based on QGraphicsView, where I connect the signal QGraphicsScene::selectionChanged() to the slot MainWindow::onSelectionChanged of my main window:
void MainWindow::onSelectionChanged()
{
// canvas is ptr to QDataflowCanvas, subclass of QGraphicsView
auto selNodes = canvas->selectedNodes();
auto selConns = canvas->selectedConnections();
...
}
The problem happens when I close my MainWindow and there is some item selected in the QGraphicsView.
I don't think I need to provide the complete code (although it can be found here), as I already isolated the cause of the crash.
This is what will happen (in order of causality):
destructor of MainWindow is called
destructor of QDataflowCanvas is called
destructor of QGraphicsView is called
destructor of QGraphicsScene is called, which triggers the removal of all items (with clear())
destructor of a QGraphicsItem is called
that will trigger a selectionChange event
the MainWindow::onSelectionChanged slot is called
method QDataflowCanvas::selectedNodes() is called, but the object is destroyed
crash!
which can be seen more in detail from the stack trace of the crash:
I found this workaround: if I disconnect the signal in MainWindow::~MainWindow, it will of course not crash:
MainWindow::~MainWindow()
{
QObject::disconnect(canvas->scene(), &QGraphicsScene::selectionChanged, this, &MainWindow::onSelectionChanged);
}
But this seems a rather atypical practice: I never found myself having to manually severe signal-slot connections because the program would otherwise crash.
There must be a more proper solution to this.
First of all, the name of your project is a mistake. The Q-prefixed namespace is taken. You should not be having any Q-prefixed classes in any project that uses Qt. You should rename the project to DataflowCanvas, for example.
There are three solutions:
Hold all children by value, order the children according to their dependencies. The QWidgetPrivate::deleteChildren called from QDataFlowCanvas will be a no-op, or at least it won't be touching objects you care about.
Use the old connect syntax when connecting to the MainWindow::onSelectionChanged slot. Note that when your slot was invoked, the mainwindow object was of the QWidget dynamic type, not MainWindow type. The connections made using the old connect syntax respect the dynamic type of the object, and a connection made to a slot of given class will guarantee that the object is of that class dynamically, i.e. at runtime.
Clear the selection in the destructor - then no futher selection changes will be handled.
The first solution makes everything explicit and is the one I'd use:
class DataFlowCanvas : public QGraphicsView {
...
private:
QDataflowModel *model_;
QDataflowTextCompletion *completion_;
QSet<QDataflowNode*> ownedNodes_;
QSet<QDataflowConnection*> ownedConnections_;
QMap<QDataflowModelNode*, QDataflowNode*> nodes_;
QMap<QDataflowModelConnection*, QDataflowConnection*> connections_;
bool showIOletsTooltips_;
bool showObjectHoverFeedback_;
bool showConnectionHoverFeedback_;
qreal gridSize_;
bool drawGrid_;
QGraphicsSecene scene_;
};
The scene is destructed before any other fields. Problem solved. You should hold everything else by value as well. E.g. completion_, etc. The pointer indirections are not useful.
The second solution highlights an unfortunate Qt bug. To wit -- in the code below, the old connect syntax will never invoke Derived2::aSlot2, because at the time the slot is invoked, the object isn't of the Derived2 type anymore:
#include <QtCore>
int ctr1, ctr2;
struct Derived1 : QObject {
Q_SLOT void aSlot1() { ctr1++; qDebug() << __FUNCTION__; }
Q_SIGNAL void aSignal();
~Derived1() { Q_EMIT aSignal(); }
Q_OBJECT
};
struct Derived2 : Derived1 {
Q_SLOT void aSlot2() { ctr2++; qDebug() << __FUNCTION__ << qobject_cast<Derived2*>(this); }
Q_OBJECT
};
int main() {
{
Derived2 d;
QObject::connect(&d, &Derived2::aSignal, &d, &Derived2::aSlot2);
QObject::connect(&d, SIGNAL(aSignal()), &d, SLOT(aSlot2()));
QObject::connect(&d, SIGNAL(aSignal()), &d, SLOT(aSlot1()));
}
Q_ASSERT(ctr1 == 1);
Q_ASSERT(ctr2 == 1);
}
#include "main.moc"
The output clearly demonstrates the problem:
aSlot2 QObject(0x0) <-- aSlot2 called but `this` is of `Derived1*` type!
aSlot1
I was taking it so simple :) What about just checking canvas pointer:
void MainWindow::onSelectionChanged()
{
if (!qobject_cast<QGraphicsScene*>(canvas))
return;
auto selNodes = canvas->selectedNodes();
auto selConns = canvas->selectedConnections();
...
}
I used qobject_cast to check if pointer canvas still exists. You can check in other (better) way. The code works.

How to pass a signal as function parameter?

So i am looking to make our own generic inherited checkbox class that will be able to take in some values in its constructor and pop out a widget that is fully connected to our model in the manner we need.
Currently we do something like this within our view
connect(checkboxWidget, &QCheckbox::Clicked, this, &VMyView::Signal);
Which emits the Signal from VMyView when the checkbox is clicked.
If i wanted to pass that signal as a parameter into my new inherited class to be hooked up in its own connect statement, how would I do so?
Research has shown me i can pass a const char* but i get compilation errors that the signal/slot do not match.
Example
CheckBox(View myView, const char* signal)
{
connect(this, &QCheckBox::Clicked, myView, signal);
}
Returns an error that Signal and slot arguments are not compatible. Ive also tried SIGNAL(signal) with the same result.
The solution ended up being fairly simple in the end
Instead of using this from within my View
connect(pCheckbox, &QCheckBox::clicked, this, &MyView::Signal);
I use
connect(this, &QCheckBox::clicked, View, signal);
Where signal and comes into my function via a function pointer
MyCheckBox::MyCheckBox(QWidget* parent, MyView* View, void(MyView::*signal)(bool))
The key takeaway is
void(MyView::*signal)(bool)
is equal too
&MyView::Signal
I think the major issue here is that signals are not static member functions. Thus they require a pointer to an instance of the class to be called correctly. So you cannot just pass in things like &VMyView::Signal, as there's no corresponding this pointer attached to the function. (This is why most of the QObject::connect() overloads require an instance to the sender/receiver objects.)
One way to solve this is to create a function object, which contains both the member function pointer and the pointer to the object on which to call it. This can be passed to the QObject::connect() function just fine.
Here's an example:
// objects.h
#include <QtCore>
class Receiver : public QObject
{
Q_OBJECT
public:
Receiver( QObject *parent = nullptr)
: QObject(parent)
{
}
~Receiver() { }
signals:
void sig(void);
};
class Sender : public QObject
{
Q_OBJECT
public:
Sender(std::function<void(void)> &bound_signal, QObject *parent = nullptr)
: QObject(parent)
{
// automatically emit `Sender::sig` on a timer, for testing.
timer = new QTimer(this);
timer->setInterval(1000);
QObject::connect(timer, &QTimer::timeout, this, &Sender::sig);
QObject::connect(this, &Sender::sig, bound_signal);
timer->start();
}
~Sender() { }
signals:
void sig(void);
private:
QTimer *timer;
};
And then a main function:
// main.cc
#include <QtCore>
#include "objects.h"
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
Receiver receiver; // object to receive the signal
// Bind the receiver's signal to the instance of the class
std::function<void(void)> signal = std::bind(&Receiver::sig, &receiver);
// Create a Sender, which will connect its own signal to the
// given bound signal
Sender sender(signal);
QObject::connect(&receiver, &Receiver::sig,
[]() -> void { qDebug() << "received"; });
return app.exec();
}
So, in your case, the Receiver and its signal would be replaced by VMyView and the signals you want to chain, and Sender would be the custom checkbox class you've implemented. Then in the constructor of the checkbox class, connect whatever signals you want to the given bound signals. You can also pass in a list of bound signals, e.g., std::list<std::function<void(void)>> &bound_signals.
I have to say, though, I'm not sure what this buys you. You'll need to write the connection logic somewhere, and I don't see why it needs to be in the constructor of the checkbox class. Wherever the checkbox and the VMyView class are created and used, that seems like a better place to put the connection code. It's more obvious, less convoluted, and there's better separation of concerns. The checkbox class shouldn't have to know or care what signals/slots its connected to. The application logic (i.e., where the objects are used) should define how the objects interact with one another.

Find the sender of the `destroyed (QObject*)` signal

I am currently wondering how to reasonably use the QObject::destroyed(QObject*) signal.
An observation
I noticed that QWidget-derived objects are treated slightly different. Consider the following small self-contained and compiling example:
/* sscce.pro:
QT += core gui widgets
CONFIG += c++11
TARGET = sscce
TEMPLATE = app
SOURCES += main.cpp
*/
#include <QApplication>
#include <QPushButton>
#include <QTimer>
#include <QtDebug>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QPushButton *button = new QPushButton;
QObject::connect(button, &QPushButton::destroyed,
[=](QObject *o) { qDebug() << o; });
delete button;
QTimer *timer = new QTimer;
QObject::connect(timer, &QTimer::destroyed,
[=](QObject *o) { qDebug() << o; });
delete timer;
return app.exec();
}
This is its output:
QWidget(0x1e9e1e0)
QObject(0x1e5c530)
So presumably, the signal is emitted from QObject's d-tor, so only the QObject base remains when the slot is called for the QTimer. However, QWidget's d-tor seems to intercept as it still identifies itself as a QWidget from the slot.
And the problem
Let's assume we have a timer pool that organizes a couple of timers in a QList<QTimer *>:
struct Pool {
QTimer *getTimer() {
return timers.at(/* some clever logic here */);
}
QList<QTimer *> timers;
};
Now an incautious user might delete the timer that was borrowed to him/her. Well, we can react, and simply remove that timer from the list. A slot will do the trick:
Pool::Pool() {
/* for each timer created */
connect(theTimer, SIGNAL(destroyed(QObject*),
this, SLOT(timerDestroyed(QObject*));
}
void Pool::timerDeleted(QObject *object) {
QTimer *theTimer = /* hrm. */
timers.removeOne(theTimer);
}
But what now? Hrm. When the slot is called, the QTimer already is in destruction and partially destroyed - only its QObject base remains. So I objously cannot qobject_cast<QTimer *>(object).
To resolve this issue, I could think of the following tricks:
Store QObjects in the list. Then I'd have to downcast every time I use an item from the list. This could be done using static_cast, though, as I know there will only be QTimers in the list, so no need for dynamic_cast or qobject_cast.
Insteat of removeOne traverse the list using an iterator and then compare each QTimer item directly to the QObject. Then use QList::erase or such.
static_cast or even reinterpret_cast the QObject to a Qtimer nonetheless.
What should I do?
If you're looking for tricks, you could simply use the base QObject objectName and remove the destroyed timer based on that.
It seems clear that your problem is one of object ownership; in particular, how to convey who is responsible for destroying an object. If your Pool object owns the QTimer objects (and thus the user should not delete them), make it clear through the interface, for example returning a QTimer& instead of a QTimer* from your getTimer method. I'm not really well versed in Qt, but if you actually wanted to transmit ownership of the object returned from a method and thus make the user responsible of its deletion, you'd likely return a std::unique_ptr<QTimer>.
Just do a direct cast:
void Pool::timerDeleted(QObject *object) {
QTimer *theTimer = (QTimer*)object; //qobject_cast doesn't work here
//we are sure that only a timer can be a sender
timers.removeOne(theTimer);
}
You could base your list on QPointer instead of raw pointers. I.e. write
QList<QPointer<QTimer>> timers;
Now when one of the timers in the list goes away, the corresponding entry in the list will automagically be cleared. It will not be removed, though! But when you access the timer via your getTimer() method, an entry whose timer has been deleted will now return a nullptr (and not a dangling pointer).
And yes, QWidget emits destroyed() in its own destructor. This is why you see a real QWidget in that case. Everybody else uses QObject's implementation.
The other way around is safe. Cast QTimer * to QObject * instead:
void Pool::timerDeleted(QObject *object) {
const auto it = std::find_if(timers.begin(), timers.end(), [object](QTimer *timer) {
return static_cast<QObject *>(timer) == object;
});
Q_ASSERT(it != timers.end());
timers.erase(it);
}
Or use erase_if(QList &list, Predicate pred) introduced in Qt 6.1.

Can Qt signals return a value?

Boost.Signals allows various strategies of using the return values of slots to form the return value of the signal. E.g. adding them, forming a vector out of them, or returning the last one.
The common wisdom (expressed in the Qt documentation [EDIT: as well as some answers to this question ]) is that no such thing is possible with Qt signals.
However, when I run the moc on the following class definition:
class Object : public QObject {
Q_OBJECT
public:
explicit Object( QObject * parent=0 )
: QObject( parent ) {}
public Q_SLOTS:
void voidSlot();
int intSlot();
Q_SIGNALS:
void voidSignal();
int intSignal();
};
Not only doesn't moc complain about the signal with the non-void return type, it seems to actively implement it in such a way as to allow a return value to pass:
// SIGNAL 1
int Object::intSignal()
{
int _t0;
void *_a[] = { const_cast<void*>(reinterpret_cast<const void*>(&_t0)) };
QMetaObject::activate(this, &staticMetaObject, 1, _a);
return _t0;
}
So: according to the docs, this thing isn't possible. Then what is moc doing here?
Slots can have return values, so can we connect a slot with a return value to a signal with a return value now? May that be possible, after all? If so, is it useful?
EDIT: I'm not asking for workarounds, so please don't provide any.
EDIT: It obviously isn't useful in Qt::QueuedConnection mode (neither is the QPrintPreviewWidget API, though, and still it exists and is useful). But what about Qt::DirectConnection and Qt::BlockingQueuedConnection (or Qt::AutoConnection, when it resolves to Qt::DirectConnection).
OK. So, I did a little more investigating. Seems this is possible. I was able to emit a signal, and receive value from the slot the signal was connected to. But, the problem was that it only returned the last return value from the multiple connected slots:
Here's a simple class definition (main.cpp):
#include <QObject>
#include <QDebug>
class TestClass : public QObject
{
Q_OBJECT
public:
TestClass();
Q_SIGNALS:
QString testSignal();
public Q_SLOTS:
QString testSlot1() {
return QLatin1String("testSlot1");
}
QString testSlot2() {
return QLatin1String("testSlot2");
}
};
TestClass::TestClass() {
connect(this, SIGNAL(testSignal()), this, SLOT(testSlot1()));
connect(this, SIGNAL(testSignal()), this, SLOT(testSlot2()));
QString a = emit testSignal();
qDebug() << a;
}
int main() {
TestClass a;
}
#include "main.moc"
When main runs, it constructs one of the test classes. The constructor wires up two slots to the testSignal signal, and then emits the signal. It captures the return value from the slot(s) invoked.
Unfortunately, you only get the last return value. If you evaluate the code above, you'll get: "testSlot2", the last return value from the connected slots of the signal.
Here's why. Qt Signals are a syntax sugared interface to the signaling pattern. Slots are the recipients of a signal. In a direct connected signal-slot relationship, you could think of it similar to (pseudo-code):
foreach slot in connectedSlotsForSignal(signal):
value = invoke slot with parameters from signal
return value
Obviously the moc does a little more to help in this process (rudimentary type checking, etc), but this helps paint the picture.
No, they can't.
Boost::signals are quite different from those in Qt. The former provide an advanced callback mechanism, whereas the latter implement the signaling idiom. In the context of multithreading, Qt's (cross-threaded) signals depend on message queues, so they are called asynchronously at some (unknown to the emitter's thread) point in time.
Qt's qt_metacall function returns an integer status code. Because of this, I believe this makes an actual return value impossible (unless you fudge around with the meta object system and moc files after precompilation).
You do, however, have normal function parameters at your disposal. It should be possible to modify your code in such a way to use "out" parameters that act as your "return".
void ClassObj::method(return_type * return_)
{
...
if(return_) *return_ = ...;
}
// somewhere else in the code...
return_type ret;
emit this->method(&ret);
You may get a return value from Qt signal with the following code:
My example shows how to use a Qt signal to read the text of a QLineEdit.
I'm just extending what #jordan has proposed:
It should be possible to modify your code in such a way to use "out" parameters that act as your "return".
#include <QtCore>
#include <QtGui>
class SignalsRet : public QObject
{
Q_OBJECT
public:
SignalsRet()
{
connect(this, SIGNAL(Get(QString*)), SLOT(GetCurrentThread(QString*)), Qt::DirectConnection);
connect(this, SIGNAL(GetFromAnotherThread(QString*)), SLOT(ReadObject(QString*)), Qt::BlockingQueuedConnection);
edit.setText("This is a test");
}
public slots:
QString call()
{
QString text;
emit Get(&text);
return text;
}
signals:
void Get(QString *value);
void GetFromAnotherThread(QString *value);
private slots:
void GetCurrentThread(QString *value)
{
QThread *thread = QThread::currentThread();
QThread *mainthread = this->thread();
if(thread == mainthread) //Signal called from the same thread that SignalsRet class was living
ReadObject(value);
else //Signal called from another thread
emit GetFromAnotherThread(value);
}
void ReadObject(QString *value)
{
QString text = edit.text();
*value = text;
}
private:
QLineEdit edit;
};
To use this, just request call();.
You can try to workaround this with following:
All your connected slots must save their results in some place (container) accessible from signaling object
The last connected slot should somehow (select max or last value) process collected values and expose the only one
The emitting object can try to access this result
Just as an idea.