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. Can someone explain how the new qt5 signals and slots work
Forum Updated to NodeBB v4.3 + New Features

Can someone explain how the new qt5 signals and slots work

Scheduled Pinned Locked Moved Solved General and Desktop
20 Posts 6 Posters 6.5k Views 4 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.
  • J.HilkJ J.Hilk

    @ambershark
    yes of course you can do it that way, In fact before I found the overload context object that is how I handled this kind of situations.

    But the problem is, the connection still existes and takes up a cycle of the cpu. Doesn't seem like much, but if one is not careful, it can explode pretty quickly.

    There's also the possibility of a manuel disconnect:

    QMetaObject::Connection m_connection;
    //…
    m_connection = QObject::connect(…);
    //…
    QObject::disconnect(m_connection);
    

    But that can mean lots of new member variables or at least many more lines of code.

    The best solution, is in deed - in my opinion - to pass the refered context object to the QObject::Connect:

    connect(sender, &Sender::updateLabels, lbl_1,[=](const QString &newValue) {
    lbl_1->setText(newValue);
    });

    and let Qt handle the potential disconnect internally.

    A Offline
    A Offline
    ambershark
    wrote on last edited by ambershark
    #7

    @J.Hilk Yea that sounds like the preferred way for me too. I would much rather have Qt handle it. I don't want to save connection variables and deal with disconnecting them later.

    And I definitely don't want to have all my signals build up and need to be checked even after they are no longer relevant. That is definitely a waste of CPU cycles and it all adds up. It will never be as slow as Java though. ;P

    My L-GPL'd C++ Logger github.com/ambershark-mike/sharklog

    1 Reply Last reply
    1
    • AsimovA Asimov

      I was wondering if someone could explain how the new qt5 signals and slots work I am new to the old ones, let alone the new ones. I know you have to have a signal and a slot, but they have even removed the keywords in the new version, and it doesn't seem so easy to read. I have read the documentation, but reading and understanding are two different things.

      For instance I have this

      QObject::connect(modelBox, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)),this,SLOT(updateBoxList(const QModelIndex & , const QModelIndex &)));
      

      And the new format is this.

      QObject::connect(modelBox, &QSlider::valueChanged,this, &QProgressBar::setValue);
      

      What I don't understand is where is the signal, and where is the slot.
      Why has QSlider replaced the Signal keyword, and there is no SLOT keyword. It is hard for me to grasp the concept of this new design.

      I know what a QSlider, and I know what QProgressBar does, but I don't know how to add my dataChanged part and then call my function.

      Don't get me wrong my code is working, but I am trying to get my head around the new syntax.

      Any help would be appreciated. I have only been using QT for just over a week, so I am very new.

      E Offline
      E Offline
      Eeli K
      wrote on last edited by
      #8

      @Asimov You seem to have too different connections - different signals and slots - in your example. The structure of both is

      sender_object, signal, receiver_object, slot

      In the old syntax you need to mark the signal and slot with macro markers. With the new one they are not needed, but you have to give the signal and slot in form of

      the_address_of_the_member_function

      which is

      a_pointer_to_the_member_function

      which is expressed as

      &class_name::function_name

      I don't know how much you know low-level C/C++ stuff. If you know how pointers to functions work you understand this. The member functions of a class reside somewhere in the memory. Each function is common to all objects of that class, as is all static data. Only the normal data members are per-object. So basically what you need to call a member function of an object is the object and the pointer to the member function. And you can get the pointer through the class. Therefore you use both the object name and the class name in the new syntax.

      On important aspect of the new syntax is that it checks compile time that the signal and slot are compatible. I sometimes have run in a situation where in the old syntax I got the arguments wrong and the connection didn't work but it doesn't give any error. In the case where there are no overloads I find the new syntax short and clear and easy to type after you have been accustomed to the syntax.

      In some cases you may run into problems if you don't know all the details. See for example QAbstractSocket::error.

      Note: Signal error is overloaded in this class. To connect to this one using the function pointer syntax, you must specify the signal type in a static cast, as shown in this example:
      
      connect(abstractSocket, static_cast<void(QAbstractSocket::*)(QAbstractSocket::SocketError)>(&QAbstractSocket::error),
          [=](QAbstractSocket::SocketError socketError){ /* ... */ });
      

      Unfortunately it wasn't documented in a place where I tried to look at (the signal/slot documentation) but the compiler gave errors which I didn't understand. The old syntax worked. No need to say how ugly and difficult this new syntax with static_cast is...

      J.HilkJ AsimovA 2 Replies Last reply
      3
      • E Eeli K

        @Asimov You seem to have too different connections - different signals and slots - in your example. The structure of both is

        sender_object, signal, receiver_object, slot

        In the old syntax you need to mark the signal and slot with macro markers. With the new one they are not needed, but you have to give the signal and slot in form of

        the_address_of_the_member_function

        which is

        a_pointer_to_the_member_function

        which is expressed as

        &class_name::function_name

        I don't know how much you know low-level C/C++ stuff. If you know how pointers to functions work you understand this. The member functions of a class reside somewhere in the memory. Each function is common to all objects of that class, as is all static data. Only the normal data members are per-object. So basically what you need to call a member function of an object is the object and the pointer to the member function. And you can get the pointer through the class. Therefore you use both the object name and the class name in the new syntax.

        On important aspect of the new syntax is that it checks compile time that the signal and slot are compatible. I sometimes have run in a situation where in the old syntax I got the arguments wrong and the connection didn't work but it doesn't give any error. In the case where there are no overloads I find the new syntax short and clear and easy to type after you have been accustomed to the syntax.

        In some cases you may run into problems if you don't know all the details. See for example QAbstractSocket::error.

        Note: Signal error is overloaded in this class. To connect to this one using the function pointer syntax, you must specify the signal type in a static cast, as shown in this example:
        
        connect(abstractSocket, static_cast<void(QAbstractSocket::*)(QAbstractSocket::SocketError)>(&QAbstractSocket::error),
            [=](QAbstractSocket::SocketError socketError){ /* ... */ });
        

        Unfortunately it wasn't documented in a place where I tried to look at (the signal/slot documentation) but the compiler gave errors which I didn't understand. The old syntax worked. No need to say how ugly and difficult this new syntax with static_cast is...

        J.HilkJ Online
        J.HilkJ Online
        J.Hilk
        Moderators
        wrote on last edited by
        #9

        @Eeli-K
        a quick addition to that with qt5.7 we canshorten overload /static_cast drastically:

        //static_cast of overloaded functions:
        QObject::connect(
                a,
                static_cast<void(MyObject::*)(int)>(&MyObject::signal),
                b,
                static_cast<void(MyObject::*)(int)>(&MyObject::slot));
        
        // Qt 5.7 + allows qOverload, but it requires C++14 to be enabled:
            QObject::connect(
                a,
                qOverload<int>(&MyObject::signal),
                b,
                qOverload<int>(&MyObject::slot));
        
        // slightly longer, but works in C++11:
            QObject::connect(
                a,
                QOverload<int>::of(&MyObject::signal),
                b,
                QOverload<int>::of(&MyObject::slot));
        

        Example taken from here:


        Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


        Q: What's that?
        A: It's blue light.
        Q: What does it do?
        A: It turns blue.

        1 Reply Last reply
        4
        • E Eeli K

          @Asimov You seem to have too different connections - different signals and slots - in your example. The structure of both is

          sender_object, signal, receiver_object, slot

          In the old syntax you need to mark the signal and slot with macro markers. With the new one they are not needed, but you have to give the signal and slot in form of

          the_address_of_the_member_function

          which is

          a_pointer_to_the_member_function

          which is expressed as

          &class_name::function_name

          I don't know how much you know low-level C/C++ stuff. If you know how pointers to functions work you understand this. The member functions of a class reside somewhere in the memory. Each function is common to all objects of that class, as is all static data. Only the normal data members are per-object. So basically what you need to call a member function of an object is the object and the pointer to the member function. And you can get the pointer through the class. Therefore you use both the object name and the class name in the new syntax.

          On important aspect of the new syntax is that it checks compile time that the signal and slot are compatible. I sometimes have run in a situation where in the old syntax I got the arguments wrong and the connection didn't work but it doesn't give any error. In the case where there are no overloads I find the new syntax short and clear and easy to type after you have been accustomed to the syntax.

          In some cases you may run into problems if you don't know all the details. See for example QAbstractSocket::error.

          Note: Signal error is overloaded in this class. To connect to this one using the function pointer syntax, you must specify the signal type in a static cast, as shown in this example:
          
          connect(abstractSocket, static_cast<void(QAbstractSocket::*)(QAbstractSocket::SocketError)>(&QAbstractSocket::error),
              [=](QAbstractSocket::SocketError socketError){ /* ... */ });
          

          Unfortunately it wasn't documented in a place where I tried to look at (the signal/slot documentation) but the compiler gave errors which I didn't understand. The old syntax worked. No need to say how ugly and difficult this new syntax with static_cast is...

          AsimovA Offline
          AsimovA Offline
          Asimov
          wrote on last edited by Asimov
          #10

          @Eeli-K
          I know a little bit about pointers a * denotes a pointer to a variable, and an & is the address of the variable at it's simplest term.

          The reason I want to learn the new format is that I am worried they will get rid of the old format which I have only just learnt.

          @Everyone
          If a signal is not marked with a Macro how do I do the datachanged. It is for a QTableView so would I do something like &QTableView::dataChanged or something?

          I haven't read all these posts properly yet, but going to have a good read now.

          Thanks for all the replies. I am not new to C++ but I have got a little rusty because I haven't done much for a few years, while 3D Modelling.
          I am however new to QT.

          PS. I just tried QTableView::dataChanged and it doesn't work LOL.
          PPS. I don't know what Lambda means.

          A E 2 Replies Last reply
          0
          • AsimovA Asimov

            @Eeli-K
            I know a little bit about pointers a * denotes a pointer to a variable, and an & is the address of the variable at it's simplest term.

            The reason I want to learn the new format is that I am worried they will get rid of the old format which I have only just learnt.

            @Everyone
            If a signal is not marked with a Macro how do I do the datachanged. It is for a QTableView so would I do something like &QTableView::dataChanged or something?

            I haven't read all these posts properly yet, but going to have a good read now.

            Thanks for all the replies. I am not new to C++ but I have got a little rusty because I haven't done much for a few years, while 3D Modelling.
            I am however new to QT.

            PS. I just tried QTableView::dataChanged and it doesn't work LOL.
            PPS. I don't know what Lambda means.

            A Offline
            A Offline
            ambershark
            wrote on last edited by
            #11

            @Asimov http://en.cppreference.com/w/cpp/language/lambda.

            Show us how you did the dataChanged signal.

            My L-GPL'd C++ Logger github.com/ambershark-mike/sharklog

            AsimovA 1 Reply Last reply
            0
            • AsimovA Asimov

              @Eeli-K
              I know a little bit about pointers a * denotes a pointer to a variable, and an & is the address of the variable at it's simplest term.

              The reason I want to learn the new format is that I am worried they will get rid of the old format which I have only just learnt.

              @Everyone
              If a signal is not marked with a Macro how do I do the datachanged. It is for a QTableView so would I do something like &QTableView::dataChanged or something?

              I haven't read all these posts properly yet, but going to have a good read now.

              Thanks for all the replies. I am not new to C++ but I have got a little rusty because I haven't done much for a few years, while 3D Modelling.
              I am however new to QT.

              PS. I just tried QTableView::dataChanged and it doesn't work LOL.
              PPS. I don't know what Lambda means.

              E Offline
              E Offline
              Eeli K
              wrote on last edited by
              #12

              @Asimov Yes, & takes the address of a variable. In this case is takes the address of a function. You can store the address to a variable which is a pointer to a function. In C++11, which is required anyways by the new signal/slot syntax, you can do

              auto sgnl = &QSlider::valueChanged;
              auto slt = &QProgressBar::setValue;
              QObject::connect(modelBox, sgnl, this, slt);
              

              sgnl and slt are pointers to functions (signals are also functions). I don't even try to find out their types explicitly - that's where the auto keyword is very handy - but you can see some details in http://www.cprogramming.com/tutorial/function-pointers.html. (See also "Related articles" there.)

              You don't have to be afraid of them getting rid of the old syntax. Maybe after 10 or 15 years, but they don't want to break existing working code and can't (yet) get rid of the MOC (Meta Object Compiler which creates C++ code out of Q_OBJECT, signals:, slots: etc.) even if they wanted to. You can read https://woboq.com/blog/reflection-in-cpp-and-qt-moc.html and https://woboq.com/blog/verdigris-qt-without-moc.html for some in-depth discussion about MOC.

              1 Reply Last reply
              0
              • A ambershark

                @Asimov http://en.cppreference.com/w/cpp/language/lambda.

                Show us how you did the dataChanged signal.

                AsimovA Offline
                AsimovA Offline
                Asimov
                wrote on last edited by Asimov
                #13

                @ambershark
                I didn't have to do this signal, it is part of the QAbstractItemModel, and I am using it on a QStandardItemModel in a QTableView
                ie
                http://doc.qt.io/qt-4.8/qabstractitemmodel.html#dataChanged

                My signal and slot are working fine. I just wanted to know how to do it the qt5 way.
                PS. I have already read that article, but found it as clear as mud LOL.

                @Eeli-K
                It's nice to know they are keeping the old system for a while, but I would like to understand the new system.
                @jsulm
                The second was just an example, and I kinda understand how that works, but I don't understand how to use my datachange example in the new format.

                PS. Just found out that Lambda's are like little functions with no name. Not heard of these before.
                PPS.
                Just tried this. I think I am close

                QObject::connect(modelBox,&QAbstractItemModel::dataChanged(const QModelIndex &),this,&DbManager::updateBoxList(const QModelIndex &));
                

                But it is giving me an error at the end. I don't think &DbManager is right. It is the name of my class which updateBoxList is in.

                E 1 Reply Last reply
                0
                • AsimovA Asimov

                  @ambershark
                  I didn't have to do this signal, it is part of the QAbstractItemModel, and I am using it on a QStandardItemModel in a QTableView
                  ie
                  http://doc.qt.io/qt-4.8/qabstractitemmodel.html#dataChanged

                  My signal and slot are working fine. I just wanted to know how to do it the qt5 way.
                  PS. I have already read that article, but found it as clear as mud LOL.

                  @Eeli-K
                  It's nice to know they are keeping the old system for a while, but I would like to understand the new system.
                  @jsulm
                  The second was just an example, and I kinda understand how that works, but I don't understand how to use my datachange example in the new format.

                  PS. Just found out that Lambda's are like little functions with no name. Not heard of these before.
                  PPS.
                  Just tried this. I think I am close

                  QObject::connect(modelBox,&QAbstractItemModel::dataChanged(const QModelIndex &),this,&DbManager::updateBoxList(const QModelIndex &));
                  

                  But it is giving me an error at the end. I don't think &DbManager is right. It is the name of my class which updateBoxList is in.

                  E Offline
                  E Offline
                  Eeli K
                  wrote on last edited by
                  #14

                  @Asimov Well, there is only

                  [signal] void QAbstractItemModel::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles = QVector<int> ())
                  

                  in QAbstractItemModel, so &QAbstractItemModel::dataChanged(const QModelIndex &) doesn't work. Don't keep teasing us, give us the error message. Have you tried

                  QObject::connect(modelBox,&QAbstractItemModel::dataChanged,this,&DbManager::updateBoxList);
                  

                  ? The niceness of this is that if neither signal nor slot are overloaded you should be able to leave off the arguments because the function names are unambiguous.

                  If you really want to understand the new system, look at https://woboq.com/blog/how-qt-signals-slots-work-part2-qt5.html. But it's quite techical, too.

                  1 Reply Last reply
                  1
                  • AsimovA Offline
                    AsimovA Offline
                    Asimov
                    wrote on last edited by Asimov
                    #15

                    @Eeli-K
                    Hey that worked, it called my function. That wasn't as bad as I thought.
                    I knew I was close, just couldn't get the syntax.

                    I wonder if there is any way to send the table index to my function?

                    But the good news is that my function is being called.

                    PS. I managed to get the index like this

                    void DbManager::updateBoxList(const QModelIndex &index){
                        qDebug() << "index: " << index.row();
                    }
                    

                    This is must better than the original signal and slots I originally used.

                    A 1 Reply Last reply
                    0
                    • AsimovA Asimov

                      @Eeli-K
                      Hey that worked, it called my function. That wasn't as bad as I thought.
                      I knew I was close, just couldn't get the syntax.

                      I wonder if there is any way to send the table index to my function?

                      But the good news is that my function is being called.

                      PS. I managed to get the index like this

                      void DbManager::updateBoxList(const QModelIndex &index){
                          qDebug() << "index: " << index.row();
                      }
                      

                      This is must better than the original signal and slots I originally used.

                      A Offline
                      A Offline
                      ambershark
                      wrote on last edited by
                      #16

                      @Asimov You already get the indexes with that call you are making to connect. Just so you know with that updateBoxList call you are only getting the topLeft index, so if you ever update more than 1 index, you don't handle that case... for that you would need:

                      void DbManager::updateBoxList(const QModelIndex &topLeft, const QModelIndex &buttomRight)
                      {
                      }
                      

                      My L-GPL'd C++ Logger github.com/ambershark-mike/sharklog

                      1 Reply Last reply
                      0
                      • AsimovA Offline
                        AsimovA Offline
                        Asimov
                        wrote on last edited by Asimov
                        #17

                        Yes ambershark &topleft gives me what my index was giving me. Really I think all I was doing was renaming &topleft to index if you think about it. Thanks. So the new format isn't so bad after all, just takes a bit of getting used to.
                        Thanks to everyone who answered as well. Been a great help.

                        BTW I keep looking at your avatar and thinking firefox, and then I look again and it looks like a fish.

                        A 1 Reply Last reply
                        0
                        • AsimovA Asimov

                          Yes ambershark &topleft gives me what my index was giving me. Really I think all I was doing was renaming &topleft to index if you think about it. Thanks. So the new format isn't so bad after all, just takes a bit of getting used to.
                          Thanks to everyone who answered as well. Been a great help.

                          BTW I keep looking at your avatar and thinking firefox, and then I look again and it looks like a fish.

                          A Offline
                          A Offline
                          ambershark
                          wrote on last edited by
                          #18

                          @Asimov Lol yea it's a shark... an amber one. ;) My company logo. I never thought about it but it does kind of share a flow with firefox. The whole circular animal logo thing.

                          Yea you were just renaming topLeft to index which is fine I was just pointing out that if you don't use topleft and bottomright and have multiple index changes than your "index" will not be what you expect it to be. If you control updates to always be a single index you'll be fine though.

                          My L-GPL'd C++ Logger github.com/ambershark-mike/sharklog

                          1 Reply Last reply
                          0
                          • AsimovA Offline
                            AsimovA Offline
                            Asimov
                            wrote on last edited by
                            #19

                            I know I marked this as solved, but I just wanted you to know I am still learning. I also had another couple of other signals and slots which used the old format, but seeing as they were still working I didn't change it straight away, and now I have managed to change it to the new format without any problems.

                            Old Format

                            connect(searchButton, SIGNAL(clicked()),this,SLOT(on_searchButton_clicked()));
                            connect(lineEdit, SIGNAL(returnPressed()),this,SLOT(on_searchButton_clicked()));
                            

                            And re-written in new format

                            connect(searchButton,&QPushButton::clicked,this,&MainWindow::on_searchButton_clicked);
                            connect(lineEdit,&QLineEdit::returnPressed,this,&MainWindow::on_searchButton_clicked);
                            

                            I probably only scratched the surface with signals and slots, but I am progressing slowly. Thanks all.

                            kshegunovK 1 Reply Last reply
                            0
                            • AsimovA Asimov

                              I know I marked this as solved, but I just wanted you to know I am still learning. I also had another couple of other signals and slots which used the old format, but seeing as they were still working I didn't change it straight away, and now I have managed to change it to the new format without any problems.

                              Old Format

                              connect(searchButton, SIGNAL(clicked()),this,SLOT(on_searchButton_clicked()));
                              connect(lineEdit, SIGNAL(returnPressed()),this,SLOT(on_searchButton_clicked()));
                              

                              And re-written in new format

                              connect(searchButton,&QPushButton::clicked,this,&MainWindow::on_searchButton_clicked);
                              connect(lineEdit,&QLineEdit::returnPressed,this,&MainWindow::on_searchButton_clicked);
                              

                              I probably only scratched the surface with signals and slots, but I am progressing slowly. Thanks all.

                              kshegunovK Offline
                              kshegunovK Offline
                              kshegunov
                              Moderators
                              wrote on last edited by
                              #20

                              @Asimov said in Can someone explain how the new qt5 signals and slots work:

                              I probably only scratched the surface with signals and slots, but I am progressing slowly. Thanks all.

                              Probably not. If you understand the old syntax and understand how pointer-to-member (and/or function pointers) work then you know it all. The new Qt syntax is nothing else but the old syntax modified to be a handy wrap around the pointer-to-member syntax C++ provides.

                              For completeness, a pointer to member is a function pointer, and a function pointer is a variable that stores the address of a function. Functions reside in the static part of the program (as Eeli K already noted). So you can get the address of that piece of memory. Incidentally the address of the function is exactly the function name in C++ syntax. :)
                              You can use function pointers to call functions, naturally, consider this:

                              void myCoolFunction(int x)
                              {
                              }
                              
                              void someOtherFunction
                              {
                                  void (*functionPointerVariable)(int) = myCoolFunction; // or &myCoolFunction
                                  // Now functionPointerVariable points to the address of myCoolFunction and we can call the function through that ponter
                                  functionPointerVariable(10); // Calls myCoolFunction, equivalent to myCoolFunction(10)
                              }
                              

                              Extending this to classes is somewhat trivial. The main thing to know is that (non-static) methods are the same as regular function, i.e. they reside in the static part of the program memory BUT they need an object to operate on. So one can get a pointer to a method in the same way, only the call is a bit different as you need to bind that function pointer to an object. Rewriting the above examples with member pointers is straightforward:

                              class A
                              {
                                  void someMethod(int)
                                  {
                                  }
                              };
                              
                              void someOtherFunction()
                              {
                                  void (A::*methodPointer)(int) = &A::someMethod; // methodPointer points to the function someMethod of the class A now
                                  A myObject; // We need an object to call the method, so let's create it.
                              
                                  // Finally we can call the method of myObject with our function pointer. Notice the parentheses around the .* operator, they are required for this because of operator priorities.
                                  (myObject.*methodPointer)(10); // Equivalent to myObject.someMethod(10);
                              }
                              

                              The whole point of this all is that you can store the pointer-to-member variable without actually specifying an object until the very last moment - when the call is done. And this is what Qt does, it stores the pointers and binds the object (with the .* or ->* operator only when the actual call is made.

                              Read and abide by the Qt Code of Conduct

                              1 Reply Last reply
                              3

                              • Login

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