I already have a working connection from SomeClass::somSignal using the SIGNAL() macro but I don't have a way of connecting it to a lambda using &SomeClass::someSignal mostly because someClass is inaccessible. I have the QObject *, though.
I tried to look into QMetaObject; it should have some sort of look-up structure where these details are kept.
We can have:
QMetaMethod::fromSignal(PointerToMemberFunction)
returning QMetaMethod
My question is, is it possible to have something like this?
fromMetaMethod(QMetaMethod)
returning PointerToMemberFunction
??
Thanks.
One way to create the connection you want does not require you to find any methods address.Simply:
Create a signal-to-signal connection, from SomeClass::someSignal to your own signal in your own class using the SIGNAL() connection style.
Connect your signal to the lambda.
You only have to make sure the parameters of the original signal are forwarded to your lambda.
Example:
QObject::connect(
pointerToSomeClass, SIGNAL(someSignal(int)),
pointerToMyClass , SIGNAL(mySignal(int)),
Qt::DirectConnection
);
QObject::connect(
pointerToMyClass, &MyClass::mySignal,
[](int i) { ... }
);
If you happen to need the sender of the original signal, then you will need a slot.
Example:
void myClass::mySlot(int i)
{
emit mySignal(sender(), i);
}
and
QObject::connect(
pointerToSomeClass, SIGNAL(someSignal(int)),
pointerToMyClass , SIGNAL(mySlot(int)),
Qt::DirectConnection
);
QObject::connect(
pointerToMyClass, &MyClass::mySignal,
[](QObject* sender, int i) { ... }
);
Related
I am trying to create a custom slot that can receive an int and pass it as a double to QDoubleSpinBox.SetValue(double);
But I have a bunch of spinboxes in my UI so I would like to have a single slot that can have a DoubleSpinBox pointer as a parameter like so:
void MainWindow::ConvertSetSpinBoxValue(int value, QDoubleSpinBox * spinbox)
{
double ValToDouble = value;
spinbox->setValue(ValToDouble);
}
and then I can connect a widget that emits a Int returning signal i.e. a QSlider:
connect(ui->horizontalSlider, SIGNAL(valueChanged(int)), this, SLOT(ConvertSetSpinBoxValue(int, ui->doubleSpinBox)));
This connect could be repeated and I would simply change the spinbox pointer. But this doesn't work and the connect is discarded at compilation.
Any idea? thanks!
Using the new Qt5 signal/slot syntax allows you to connect to a lambda that can capture the QDoubleSpinBox * (untested)...
auto *sb = ui->doubleSpinBox;
connect(ui->horizontalSlider, &QAbstractSlider::valueChanged, this,
[this, sb](int value)
{
this->ConvertSetSpinBoxValue(value, sb);
});
connect(ui->horizontalSlider, &QSlider::valueChanged,
[this] (int value) {
ConvertSetSpinBoxValue(value, ui->doubleSpinBox);
});
Please use modern syntax for connections
A lambda should help you
As the others pointed out using the (new) Qt5 signals and slots (using function pointers and lambda functions) should solve your problem.
Different methods exist:
Using lambda function:
connect(ui->horizontalSlider, &QSlider::valueChanged, this /*context*/, [this] (int value) {ui->doubleSpinBox->setValue(value);});
Note that the context is useful as it automatically destructs the connection when the context is destructed, but it is probably not needed here as the ui->horizontalSlider will probably destructed first.
Using std::bind:
connect(ui->horizontalSlider, &QSlider::valueChanged, this /*context*/, std::bind(&MainWindow::ConvertSetSpinBoxValue, this, value, ui->doubleSpinBox));
Using implicit conversion between signals/slots:
connect(ui->horizontalSlider, &QSlider::valueChanged, ui->doubleSpinBox, &QDoubleSpinBox::SetValue);
Note that the new signal and slot mechanism allows implicit conversion between the signals and slots.
References
Differences between String-Based and Functor-Based Connections
I want to kinda build a wrapper around QObject's connect using the new syntax that has type check at compilation time.
The main idea would be: "hey listener, connect this object's slots to my newData signal"
void Listener::_addClient(const QObject *object, const QMetaMethod& slot)
{
connect(this, &Listener::newData, object, slot);
}
And some Client class would simply do:
Listener myListener;
myListener._addClient(this, &Client::mySlot);
This of course does not compile.
Since all Listener objects will be owned by a manager class, I cannot access them directly, so its not possible to do the traditional connect.
How can I build a wrapper for this connect so I can assure the type check at compilation time?
You might use template:
template <typename Receiver, typename Slot>
void Listener::_addClient(const Receiver *object, const Slot& slot)
{
connect(this, &Listener::newData, object, slot);
}
I could not make Jarod42 solution work because I needed access to protected properties and the method needed to be public.
I ended up proceeding as follow
void Listener::_addClient(const QObject *receiver, const char *slot)
{
connect(this, SIGNAL(newData()), receiver, slot);
}
calling like so
Listener myListener;
myListener._addClient(this, SLOT(mySlot()));
It might not be the pretiest way to proceed but it works.
I have this code:
QObject::connect(lineEdit_1, SIGNAL(textChanged(const QString &)), MainWindow, SLOT(myMethod(const QString &, QLineEdit* )) );
This code works correctly when myMethod has only the first argument (equal to the SIGNAL) but I need to pass a pointer lo lineEdit_1 in order to allow myMethod to know on which LineEdit it has to operate.
What I should to do?
Thanks a lot.
It is not necessary that you send as an additional argument the object that emits the signal for it, the QObjects have the sender() method that allows us to obtain that object:
QObject::connect(lineEdit_1, &QLineEdit::textChanged, MainWindow, &Your_Class::myMethod);
void Your_Class::MyMethod(const QString & text){
if(QLineEdit *le = qobject_cast<QLineEdit *>(sender())){
qDebug() << le;
}
}
If you need to pass other arguments you can use the lambda functions but always take the time to see the limitations (how to use it depends on the context):
QObject::connect(lineEdit_1, &QLineEdit::textChanged, [ /* & or = */](const QString & text){
MainWindow->MyMethod(text, another_arguments);
});
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 use QNetworkAccessManager to do form POST.
I have connected signals and slots as:
connect(manager,SIGNAL(finished(QNetworkReply*)),this,SLOT(readCookies(QNetworkReply*)));
Now, I make a request by doing:
manager->post(request,postData);
Now readCookies(QNetworkReply *) will be run as soon as SIGNAL is emitted. Now, using the Cookies which I get in this slot, I have to make one more POST..
As signals & slots are asynchronous, I want to wait till I get the cookies from my first POST and then I again want to do another post using the cookies I got in first POST like
//Setting new request, headers etc...
manager->post(request2,postData2);
I want the later to always be executed after first one has executed (so that I get proper cookies value).
What is the way to go? I am new to all these SIGNALS & SLOTS so please bear with me.
You can do the post in your readCookies() slot:
void readCookies( QNetworkReply* reply ) {
if ( ...error? ) {
report error...
return;
}
...
manager->post(request2,postData2);
}
I will be called when the cookies is read, and you can then continue with your post. Connect that to a second slot, and so on.
Managing multiple, possibly parallely running asynchronous operations like this can become errorprone though, if you manage many of them in a single object. I would suggest to use the Command Pattern - here I described why I find it extremely useful in exactly this context. The sequence of request and asnychronous operations is encapsulated in a single object (abbreviated, with some pseudo-code):
class PostStuffOperation : public QObject {
Q_OBJECT
public:
enum Error {
NoError=0,
Error=1,
...
};
Error error() const; //operation successful or not?
QString errorString() const; //human-readable error description
... setters for all the information the operation needs
...
void start() {
...start your first request and connect it to cookiesRead
}
public Q_SLOTS:
void cookiesRead( QNetworkReply * ) {
if ( error ) {
// set error and errorString...
emit finished( this ); //couldn't read cookies, so the operation fails
return;
}
... do post
}
void postFinished( QNetworkReply* ) {
if ( error ) {
// set error and errorString...
}
emit finished( this ); //post finished - that means the whole operation finished
}
Q_SIGNALS:
void finished( PostStuffOperation* );
};
To start the operation, you do
PostStuffOperation op* = new PostStuffOperation( this );
... pass data like server, port etc. to the operation
connect( op, SIGNAL(finished()), this, SLOT(postOperationFinished()) );
op->start();
void postOperationFinished( PostStuffOperation* op ) {
if ( op->error != PostStuffOperation::NoError ) {
//handle error, e.g. show message box
}
}
It makes sense to have a common baseclass for such operations, see e.g. KDE's KJob.
You can connect a signal from this to a slot from your manager and emit the signal after reading the cookies. By example:
connect(this, SIGNAL(cookiesRead()), manager, SLOT(PostAgain());
So your readCookies function will be:
{
// Read cookies
emit cookiesRead();
}
Of course you can send all data you want form signal to slot.
Hope that helps
You can send a second signal connected to another slot (the resend slot), if you have finished the evaluation of your first cookie. You can do that directly in the slot. You can also call slots like a normal member function.