I have a class handling file transfers.
One of the emits is a finished() signal
on the parent side, I'd like to connect this finished() signal to a fileTransferFinished() Slot
But how can I know which instance is finished? as there are plenty of them running at the same time..
I know I can make use of the QObject::sender() method to return the emitter, but this way, I can't acces a method of my instance..
qDebug() << "finished " << QObject::sender()->getID();
it says no member named getID in QObject
I'd like to have my pointer inside the Slot function, is that possible?
Look at the prototype of Object::sender() in the documentation:
QObject * QObject::sender() const
It returns a pointer to QObject instance, hence you will not be able to call getID() method on that return value.
Cast the return value of Object::sender() to pointer of your class before using it :
YourClass * sender_obj = qobject_cast<YourClass*>(QObject::sender());
qDebug() << "finished " << sender_obj->getID();
Using sender() is not a clean solution. It breaks modularity which normally is the purpose of using signals and slots.
Documentation says:
Warning: This function violates the object-oriented principle of modularity.
It is recommended to use QSignalMapper.
In addition to previous solutions, and given you're using Qt 5, another option is to use a lambda that passes the object to your slot:
connect(object, &YourClass::finished, [this, object]() {
fileTransferFinished(object);
});
Related
Is there a way to connect signal and slot without using connect function?
If a way exists, please give some examples.
Nope, there is no other way, not in the public API at least. In Qt4 there is only the connect() function with the SIGNAL() and SLOT macro().
In Qt5 you have another, type-safe connection syntax, but it still uses the connect() function. And in QML you can use "attached handlers" - onSignal: doStuff() - but that's just for QML.
There is a way to use the meta-call interface to avoid using the connect() function. Depending on what you precisely need to do though this may not be the solution you are looking for, or it may be.
You essentially define a callback slot function in your QOBJECT class with a certain syntax and let the MOC sort the 'connection' out.
So say you wanted to use:
connect(ui->actionButton, SIGNAL(triggered()), this, SLOT(someCallbackSlot()));
but without having to use the callback function...
You can do it implicitly with a particular syntax of a member function in the QOBJECT who receives the signal:
class MyQtObj : public QWidget
{
QObject
private slots:
void on_actionButton_triggered();
}
Where on_actionButton_triggered is the functional equivalent of someCallbackSlot(), actionButton is the name of the button/action/signal emitter and triggered() is the signal emitted.
So any of these functions are valid providing the signal emitter is correct:
void on_minimizedButton_clicked();
void on_closeButton_released();
etc
When you run the Qt Meta-Object-Compiler and generate your moc_.cpp file of this class there will be a function called qt_static_metacall; this contains a switch statement that looks like :
void MyQtObj::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
if (_c == QMetaObject::InvokeMetaMethod) {
MyQtObj*_t = static_cast<MyQtObj*>(_o);
Q_UNUSED(_t)
switch (_id) {
case 0: _t->on_actionButton_triggered(); break;
default: ;
}
}
}
If you put a break point in this and trigger your action/button/signal you should see your function get executed.
I'm not too sure of the details of how this works though and I noticed it when I used this frame-less Qt window code and have now adopted it in my own Qt C++ projects.
What is the difference between a slot (a method declared in slots section) and a method in Qt (a method declared with Q_INVOKABLE keyword)? They both can be invoked using QMetaObject::invokeMethod, they are both accepted when connecting to a slot using SLOT macro, however when getting type of metamethod it can be returned either QMetaMethod::Method or QMetaMethod::Slot, so it seems that there is some difference for Qt?
The only difference is whether the method is listed as a slot or as not-a-slot in the class's metadata. In both Qt 4 and Qt 5, connection to either a slot or an invokable succeeds:
#include <QObject>
struct Test : public QObject {
Q_SLOT void slot() {}
Q_INVOKABLE void invokable() {}
Q_OBJECT
};
int main() {
Test test;
auto c1 = QObject::connect(&test, SIGNAL(destroyed(QObject*)), &test, SLOT(slot()));
auto c2 = QObject::connect(&test, SIGNAL(destroyed(QObject*)), &test, SLOT(invokable()));
Q_ASSERT(c1);
Q_ASSERT(c2);
}
#include "main.moc"
It's up to the user to decide how the difference between a slot and an invokable is interpreted. E.g. if you're exposing the slot list to the user in some way, you won't be exposing the invokable method list unless you choose to do so.
The practical differences I know of:
Q_INVOKABLE can have a return value, slot cannot.
Q_INVOKABLE is called on the GUI thread, and blocks the GUI thread. slot's thread depends on which thread the QObject was created on, and therefore can be non-blocking.
So my rule of thumb is, use slot if there are no return values, otherwise use Q_INVOKABLE.
How would I connect a signal and slot of 2 objects when the object names, signals, and slots are all specified in a text file?
Getting the right objects names isn't an issue, since I can easily loop through an array and compare the names to the spot in the file, but there has to be some sort of way where I can return the signal and slot from the file and use it in the connect function, like:
connect(rtnObj1(line),SIGNAL(rtnSignal(line)),rtnObj2(line),SLOT(rtnSlot(line)));
where rtn functions return the object name/signal/slot, and "line" is the current QString line from the file.
The only way I know of is by literally coding in every single combination and comparing QStrings with if statements, but that would be incredibly tedious, as the amount of combinations would be incredibly high.
Note:
Here's a simplified example demonstrating essentially how this issue exists.
Frame 1:
4 QComboBoxes. The first and third hold object names, the second holds signals, the fourth holds slots. Every item is of course a QString within these lists. Hitting a button appends a new line to a file, writing the text selected from each box.
Frame 2: Has already has the required objects. Reading the file, it would match the objects defined in the list against the ones already created, and connect them as the file describes.
It's easy enough to create an object based on the data a file holds, but how would one create/pull a signal and a slot from a file?
Edit:
Unless, is one able to connect like this?
connect(objectA, "", objectB, "");
Because I just found out that my code will compile like that, however whenever I try putting in the slot or signal names I just get an error like:
QObject::connect: Use the SIGNAL macro to bind Tile::clicked
Your problem is easily solvable with one of the following static QObject::connect() method:
QMetaObject::Connection QObject::connect(
const QObject *sender, const QMetaMethod &signal,
const QObject *receiver, const QMetaMethod &method,
Qt::ConnectionType type = Qt::AutoConnection)
First of all, you need pointers to sender and receiver objects. There are several ways how you can store an object pool. I would suggest to keep all objects in QHash:
QHash<QString, QObject *> m_objects; // I use QObject as a generic example
Now, it's possible to find a pointer to any object for connecting in an efficient way.
The next step would be obtaining QMetaMethod objects for sender's signal and receiver's slot from corresponding QMetaObject objects. Use QObject::metaObject() QMetaObject instances.
Here's the complete code of a function which connects two object using only string parameters:
void dynamicConnect(const QString &senderName, const QString &signalName,
const QString &receiverName, const QString &slotName)
{
QObject *emitter = m_objects.value(senderName);
int index = emitter->metaObject()
->indexOfSignal(QMetaObject::normalizedSignature(qPrintable(signalName)));
if (index == -1) {
qWarning("Wrong signal name!");
return;
}
QMetaMethod signal = emitter->metaObject()->method(index);
QObject *receiver = m_objects.value(receiverName);
index = receiver->metaObject()
->indexOfSlot(QMetaObject::normalizedSignature(qPrintable(slotName)));
if (index == -1) {
qWarning("Wrong slot name!");
return;
}
QMetaMethod slot = receiver->metaObject()->method(index);
QObject::connect(emitter, signal, receiver, slot);
}
I have a data model that I want other objects to be able to watch for updates, but I don't want to give anyone control of the update signal itself. I've come up with a something that makes sense to me conceptually, but it doesn't seem to work. I'm wondering if anyone could explain why I will never get it to work, or if i'm missing something that could make this work. Effectively I have a Client class (QObject) that has an arbitrary slot and Model class that has a private signal.
important Client class code (public SLOT):
void client::doUpdate()
{
std::cout << "HELLO UPDATE" <<std::endl;
}
Model code:
void Model::unRegisterForUpdates(const char* qt_slot_handle, QObject* o)
{
QObject::disconnect (this, SIGNAL( updateHandles() ),
o, qt_slot_handle);
}
void Model::registerForUpdates(const char* qt_slot_handle, QObject* o)
{
QObject::connect( this, SIGNAL( updateHandles() )
, o, qt_slot_handle
, Qt::UniqueConnection);
}
Main func:
Model foo;
client * cl = new client();
client * cl2 = new client();
std::cout << SLOT(cl->doUpdate()) << std::endl;
std::cout << SLOT(cl2->doUpdate()) << std::endl;
foo.registerForUpdates( SLOT(cl->doUpdate()) , cl);
foo.registerForUpdates( SLOT(cl2->doUpdate()) , cl2);
Output:
1cl->doUpdate()
1cl2->doUpdate()
Object::connect: No such slot client::cl->doUpdate() in .../main.cpp:14
Object::connect: No such slot client::cl2->doUpdate() in .../main.cpp:15
It will probably come down to the amount of introspection I can get into the signal/slot system.I'm not sure how to interpret the connect error message. It tells me that connect is concerned with the static information for the class Client, but the slot string indicates the specific instance name - I'm wondering if by the time I get to Model::connectHandle() this name loses its meaning.
It's a simple case of typo:
In class, you have doUpdate() slot.
In main func, you're passing onUpdate() to SLOT() macro.
Also, you shouldn't include the instance in the SLOT() macro, just the slot name (and parameters). Exactly the same syntax you'd use in connect(). Qt's signal-slot connection mechanism is based on string comparison. In other words, your main should do this:
foo.registerForUpdates(SLOT(doUpdate()), cl);
foo.registerForUpdates(SLOT(doUpdate()), cl2);
I can't seem to pass an argument to a slot. If I don't pass an argument, the function rolls through fine. If I pass an argument (integer), I get the errors "No such name type" and "No such slot" when I compile.
In my header, I declare:
private slots:
void addButton(int);
signals:
void clicked(int)
in my Main.cpp, I do:
int count;
int count = 0;
QPushButton* button = new QPushButton("Button");
_layout->addWidget(button);
connect(button, SIGNAL(clicked(count), this, SLOT(addButton(count)));
....
void Main::addButton(int count) {
//do stuff with count
}
Sebastian is correct that you cannot do this in the way you're trying, however Qt does provide a class that gives you the functionality you want.
Check out the QSignalMapper. It allows you to associate an integer with an object/signal pair. You then connect to its signals instead of directly to the button.
The signal and the slot must have the same number and type(s) of argument(s), and you can only pass the argument(s) of the signal to the slot, not any variable or value that you want.
I can see three problems with this.
Firstly, the clicked() signal is emitted by QPushButton (with no parameters), but you're trying to redefine it in your own class (with an int parameter). If you want to do this:
SignalClass* objectWithSignals = new SignalClass;
SlotClass* objectWithSlots = new SlotClass;
connect(objectWithSignals, SIGNAL(a()), objectWithSlots, SLOT(b()));
then you can only connect to the signals already defined in SignalClass. In other words, the signal a() must belong to SignalClass, not SlotClass.
(In fact, clicked() is defined in QPushButton's base class QAbstractButton.)
Secondly, inside the connect() function, you need to specify the signal and slot signatures with their parameter types. Where you have count inside the connect() function, it should be int.
And thirdly, there's a bracket missing in your call to connect: SIGNAL(clicked(count)).
Hope that helps.