Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. How to pass and invoke lambda function at slot ?
Forum Updated to NodeBB v4.3 + New Features

How to pass and invoke lambda function at slot ?

Scheduled Pinned Locked Moved Solved General and Desktop
12 Posts 3 Posters 2.9k Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • fcarneyF Offline
    fcarneyF Offline
    fcarney
    wrote on last edited by
    #2

    std::function

    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.

    C++ is a perfectly valid school of magic.

    H 1 Reply Last reply
    0
    • fcarneyF fcarney

      std::function

      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.

      H Offline
      H Offline
      huseyinkozan
      wrote on last edited by huseyinkozan
      #3

      @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 ?

      1 Reply Last reply
      0
      • fcarneyF Offline
        fcarneyF Offline
        fcarney
        wrote on last edited by
        #4

        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?

        C++ is a perfectly valid school of magic.

        H 1 Reply Last reply
        0
        • fcarneyF fcarney

          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?

          H Offline
          H Offline
          huseyinkozan
          wrote on last edited by
          #5

          @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

          1 Reply Last reply
          1
          • H Offline
            H Offline
            huseyinkozan
            wrote on last edited by
            #6

            @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 ?

            KroMignonK 1 Reply Last reply
            0
            • H huseyinkozan

              @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 ?

              KroMignonK Offline
              KroMignonK Offline
              KroMignon
              wrote on last edited by
              #7

              @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

              It is an old maxim of mine that when you have excluded the impossible, whatever remains, however improbable, must be the truth. (Sherlock Holmes)

              H 1 Reply Last reply
              0
              • KroMignonK KroMignon

                @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

                H Offline
                H Offline
                huseyinkozan
                wrote on last edited by
                #8

                @kromignon Thanks.
                Then we can be sure that all childs of deleted QObject will disconnect.

                At my example, m_msgBox will be deleted after btn delete, and lambda for &QMessageBox::finished will not call. I prefer checking func 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.

                KroMignonK 1 Reply Last reply
                0
                • H huseyinkozan

                  @kromignon Thanks.
                  Then we can be sure that all childs of deleted QObject will disconnect.

                  At my example, m_msgBox will be deleted after btn delete, and lambda for &QMessageBox::finished will not call. I prefer checking func 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.

                  KroMignonK Offline
                  KroMignonK Offline
                  KroMignon
                  wrote on last edited by
                  #9

                  @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

                  It is an old maxim of mine that when you have excluded the impossible, whatever remains, however improbable, must be the truth. (Sherlock Holmes)

                  H 1 Reply Last reply
                  0
                  • KroMignonK KroMignon

                    @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

                    H Offline
                    H Offline
                    huseyinkozan
                    wrote on last edited by huseyinkozan
                    #10

                    @kromignon Thanks.
                    I had found the page that you gave, but couldn't realise how QObject::connect() accepts lambdas.

                    I am using lambdas newly. Just read this article, and helped alot:
                    https://blog.feabhas.com/2014/03/demystifying-c-lambdas/

                    1 Reply Last reply
                    0
                    • KroMignonK Offline
                      KroMignonK Offline
                      KroMignon
                      wrote on last edited by
                      #11

                      @huseyinkozan Thanks for the link, I've found this one https://medium.com/genymobile/how-c-lambda-expressions-can-improve-your-qt-code-8cd524f4ed9f

                      It is an old maxim of mine that when you have excluded the impossible, whatever remains, however improbable, must be the truth. (Sherlock Holmes)

                      H 1 Reply Last reply
                      0
                      • KroMignonK KroMignon

                        @huseyinkozan Thanks for the link, I've found this one https://medium.com/genymobile/how-c-lambda-expressions-can-improve-your-qt-code-8cd524f4ed9f

                        H Offline
                        H Offline
                        huseyinkozan
                        wrote on last edited by huseyinkozan
                        #12

                        @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 Reply Last reply
                        0

                        • Login

                        • Login or register to search.
                        • First post
                          Last post
                        0
                        • Categories
                        • Recent
                        • Tags
                        • Popular
                        • Users
                        • Groups
                        • Search
                        • Get Qt Extensions
                        • Unsolved