How to pass and invoke lambda function at slot ?
-
wrote on 22 Aug 2019, 14:58 last edited by huseyinkozan
Hi,
I want to take lambda as function parameter and invoke it, but couldn't realize how to do that. For ex:
MyObject::ask(const QString & text, QObject * receiver, / * param type ? */ func) { // setup m_msgBox to ask question connect(m_msgBox, &QMessageBox::finished, this, [&](int result) { func(result); // how to call lambda function ? }); m_msgBox->open(); } MyObject::someFunction() { ask(tr("How?"), this, [&](int result) { // process result }); }
-
wrote on 22 Aug 2019, 15:08 last edited by
MyObject::ask(const QString & text, QObject * receiver, std::function<void(int)> func)
or
template<class T> MyObject::ask(const QString & text, QObject * receiver, T func)
The template one can get messy for class members, but is still doable.
-
MyObject::ask(const QString & text, QObject * receiver, std::function<void(int)> func)
or
template<class T> MyObject::ask(const QString & text, QObject * receiver, T func)
The template one can get messy for class members, but is still doable.
wrote on 22 Aug 2019, 15:21 last edited by huseyinkozan@fcarney Thanks.
I tried to did first way, and call like:
MyObject::ask(const QString & text, QObject * receiver, std::function<void(int)> func) { ... func(result);
But failed at
functional
header:__throw_bad_function_call();
Also, should I use
receiver
? -
wrote on 22 Aug 2019, 15:55 last edited by
Why are you passing this here?:
connect(m_msgBox, &QMessageBox::finished, this, [&](int result) {
Can you create a minimal project that shows the error? Something we can compile?
-
Why are you passing this here?:
connect(m_msgBox, &QMessageBox::finished, this, [&](int result) {
Can you create a minimal project that shows the error? Something we can compile?
wrote on 22 Aug 2019, 16:41 last edited by@fcarney Right, no need to pass
this
.
Found the problem, changing the capture from&
to=
solved the exception.#include <QApplication> #include <QMessageBox> #include <QPushButton> #include <QDebug> #include <QScreen> #include <QStyle> class Button : public QPushButton { Q_OBJECT public: Button(QWidget *parent = nullptr) : QPushButton(parent) { setText(tr("Click")); m_msgBox = new QMessageBox(this); connect(this, &QPushButton::clicked, [&](){ askProceed(); }); } virtual ~Button() {} void ask( const QString & text, const QStringList & buttons, int acceptButton, int rejectButton, std::function<void(int)> func) { m_msgBox->setText(text); m_msgBox->setIcon(QMessageBox::Question); m_msgBox->setStandardButtons(QMessageBox::NoButton); QList<QAbstractButton*> buttonList; for (int i = 0; i < buttons.count(); ++i) { if (i == acceptButton) { QPushButton * btn = m_msgBox->addButton(buttons.at(i), QMessageBox::AcceptRole); m_msgBox->setDefaultButton(btn); buttonList.append((QAbstractButton*)btn); } else if (i == rejectButton) { QPushButton * btn = m_msgBox->addButton(buttons.at(i), QMessageBox::RejectRole); m_msgBox->setEscapeButton((QAbstractButton*)btn); buttonList.append((QAbstractButton*)btn); } else { Q_ASSERT(false); } } connect(m_msgBox, &QMessageBox::finished, [=](int) { int res = rejectButton; for (int i = 0; i < buttonList.count(); ++i) { if (m_msgBox->clickedButton() == buttonList.at(i)) { res = i; break; } } QList<QAbstractButton*> listToDelete = m_msgBox->buttons(); foreach (QAbstractButton * btn, listToDelete) { m_msgBox->removeButton(btn); btn->deleteLater(); } m_msgBox->disconnect(); func(res); }); m_msgBox->open(); } void askProceed() { ask( tr("Which do you prefer?"), QStringList() << tr("Apple") << tr("Pineapple"), 0, 1, [&](int result) { if (result == 0) qDebug() << "Selected Apple"; else if (result == 1) qDebug() << "Selected Pineapple"; else qDebug() << "Unexpected result = " << result; }); } private: QMessageBox * m_msgBox = nullptr; }; int main(int argc, char *argv[]) { QApplication a(argc, argv); Button btn; btn.resize(100, 30); btn.setGeometry( QStyle::alignedRect( Qt::LeftToRight, Qt::AlignCenter, btn.size(), qApp->screens().first()->availableGeometry() ) ); btn.show(); return a.exec(); } #include "main.moc"
Also added to my snippets:
https://gitlab.com/snippets/1888176 -
wrote on 23 Aug 2019, 07:07 last edited by
@fcarney said in How to pass and invoke lambda function at slot ?:
Why are you passing this here?:
connect(m_msgBox, &QMessageBox::finished, this, [&](int result) {Should we check if the object and function exists before calling the callback ?
-
@fcarney said in How to pass and invoke lambda function at slot ?:
Why are you passing this here?:
connect(m_msgBox, &QMessageBox::finished, this, [&](int result) {Should we check if the object and function exists before calling the callback ?
wrote on 23 Aug 2019, 07:56 last edited by@huseyinkozan said in How to pass and invoke lambda function at slot ?:
Should we check if the object and function exists before calling the callback ?
When a QObject is deleted, all connection on this object are removed ==> Take a look at QObject::~QObject() for more details
-
@huseyinkozan said in How to pass and invoke lambda function at slot ?:
Should we check if the object and function exists before calling the callback ?
When a QObject is deleted, all connection on this object are removed ==> Take a look at QObject::~QObject() for more details
wrote on 23 Aug 2019, 08:14 last edited by@kromignon Thanks.
Then we can be sure that all childs of deleted QObject will disconnect.At my example,
m_msgBox
will be deleted afterbtn
delete, and lambda for&QMessageBox::finished
will not call. I prefer checkingfunc
is not a dangling pointer. But seems not possible with std::function::operator==.I tried to find at
QObject::connect()
code at woboq, but couldn't find how to handle lambdas. -
@kromignon Thanks.
Then we can be sure that all childs of deleted QObject will disconnect.At my example,
m_msgBox
will be deleted afterbtn
delete, and lambda for&QMessageBox::finished
will not call. I prefer checkingfunc
is not a dangling pointer. But seems not possible with std::function::operator==.I tried to find at
QObject::connect()
code at woboq, but couldn't find how to handle lambdas.wrote on 23 Aug 2019, 08:20 last edited by@huseyinkozan said in How to pass and invoke lambda function at slot ?:
but couldn't find how to handle lambdas.
Qt documentation is big, so find the right information is not that easy, but Google is very performant for "data mining".
A simple search with "connect functor" and you will find Making Connections to Lambda Expressions -
@huseyinkozan said in How to pass and invoke lambda function at slot ?:
but couldn't find how to handle lambdas.
Qt documentation is big, so find the right information is not that easy, but Google is very performant for "data mining".
A simple search with "connect functor" and you will find Making Connections to Lambda Expressionswrote on 23 Aug 2019, 08:43 last edited by huseyinkozan@kromignon Thanks.
I had found the page that you gave, but couldn't realise howQObject::connect()
accepts lambdas.I am using lambdas newly. Just read this article, and helped alot:
https://blog.feabhas.com/2014/03/demystifying-c-lambdas/ -
wrote on 23 Aug 2019, 09:07 last edited by
@huseyinkozan Thanks for the link, I've found this one https://medium.com/genymobile/how-c-lambda-expressions-can-improve-your-qt-code-8cd524f4ed9f
-
@huseyinkozan Thanks for the link, I've found this one https://medium.com/genymobile/how-c-lambda-expressions-can-improve-your-qt-code-8cd524f4ed9f
wrote on 23 Aug 2019, 09:41 last edited by huseyinkozan@kromignon Thanks.
I had also read that article, but that also not defines how Qt handle this stuation:
In this version, we pass monitor as a context to connect(). It won't affect the execution of our lambda, but when monitor is deleted, Qt will notice and will disconnect Worker::progress() from our lambda.
I tried with below, but could not find how to detect I have already disconnect:
connect(receiver, &QObject::destroyed, [this](){ qDebug() << "receiver destroyed"; m_msgBox->disconnect(); });
Full code with receiver (deletes
btn
after 10 sec!):#include <QApplication> #include <QMessageBox> #include <QPushButton> #include <QDebug> #include <QScreen> #include <QStyle> #include <QTimer> class Button : public QPushButton { Q_OBJECT public: Button(QWidget *parent = nullptr) : QPushButton(parent) { setText(tr("Click")); m_msgBox = new QMessageBox(this); connect(this, &QPushButton::clicked, [this](){ ask(tr("Which do you prefer?"), QStringList() << tr("Apple") << tr("Pineapple"), 0, 1, this, [](int result) { if (result == 0) qDebug() << "Selected Apple"; else if (result == 1) qDebug() << "Selected Pineapple"; else qDebug() << "Unexpected result = " << result; }); }); } virtual ~Button() {} void ask( const QString & text, const QStringList & buttons, int acceptButton, int rejectButton, const QObject * receiver, std::function<void(int)> func) { m_msgBox->setText(text); m_msgBox->setIcon(QMessageBox::Question); m_msgBox->setStandardButtons(QMessageBox::NoButton); QList<QAbstractButton*> buttonList; for (int i = 0; i < buttons.count(); ++i) { if (i == acceptButton) { QPushButton * btn = m_msgBox->addButton(buttons.at(i), QMessageBox::AcceptRole); m_msgBox->setDefaultButton(btn); buttonList.append((QAbstractButton*)btn); } else if (i == rejectButton) { QPushButton * btn = m_msgBox->addButton(buttons.at(i), QMessageBox::RejectRole); m_msgBox->setEscapeButton((QAbstractButton*)btn); buttonList.append((QAbstractButton*)btn); } else { Q_ASSERT(false); } } connect(m_msgBox, &QMessageBox::finished, [this, rejectButton, buttonList, func](int) { m_msgBox->disconnect(); int res = rejectButton; for (int i = 0; i < buttonList.count(); ++i) { if (m_msgBox->clickedButton() == buttonList.at(i)) { res = i; break; } } QList<QAbstractButton*> listToDelete = m_msgBox->buttons(); foreach (QAbstractButton * btn, listToDelete) { m_msgBox->removeButton(btn); btn->deleteLater(); } func(res); }); connect(receiver, &QObject::destroyed, [this](){ qDebug() << "receiver destroyed"; m_msgBox->disconnect(); }); m_msgBox->open(); } private: QMessageBox * m_msgBox = nullptr; }; int main(int argc, char *argv[]) { QApplication a(argc, argv); Button * btn = new Button; btn->resize(100, 30); btn->setGeometry( QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, btn->size(), qApp->screens().first()->availableGeometry())); btn->show(); QTimer::singleShot(10000, [&](){ delete btn; btn = nullptr; }); int res = a.exec(); if (btn) delete btn; return res; } #include "main.moc"
1/12