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. Connection signals for dynamically created widgets
Forum Updated to NodeBB v4.3 + New Features

Connection signals for dynamically created widgets

Scheduled Pinned Locked Moved Solved General and Desktop
c++signal & slotdesktop
11 Posts 3 Posters 2.7k Views
  • 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.
  • D Deneguil
    2 Jan 2025, 17:36

    Hello,

    I am making a graphical automata editor. I have a custom graphics view that I use as the canvas for adding the nodes and later on the edges. I want to display the parameters of the clicked node in the sidebar. To do so, I thought of having the node widgets emit a clicked signal with a pointer to the underlying information. That signal would them be caught by a StateParams slot in the main application class.

    The relevant classes are as such :

    StateWidget.h

    constexpr float R = 50;
    
    class StateWidget : public QGraphicsEllipseItem {
    Q_OBJECT
        public:
            float x, y;
            State* m_State;
    
            inline StateWidget(QPointF p, QGraphicsItem* parent = nullptr): QGraphicsEllipseItem(p.x()-R/2,p.y()-R/2,R,R)
            {
                setBrush(QBrush(Qt::white));
                setZValue(10);
            }
    
        protected:
            inline void mousePressEvent(QGraphicsSceneMouseEvent *event) override {
                event->accept();
                std::cout << "State " << m_State->m_ID << " pressed\n";
                emit clicked(m_State);
            }
        
        signals:
            void clicked(State* s);
    };
    

    AutomatLab.h

    class AutomatLab : public QMainWindow {
        Q_OBJECT
    
    private:
        Ui::AutomatLab m_UI;
        GraphicView m_GV;
        Automata m_Automata;
    
        void ToggleBtn(QPushButton* btn, bool toggled);
    
    public:
        static EClickState s_State;
    
        AutomatLab();
        ~AutomatLab();
    
    public slots:
        void StateParams(State* s);
        void InsertStateMode(bool toggled);
        void InsertTransitionMode(bool toggled);
        void ButtonUsed();
    };
    

    The StateWidgets are instanciated in the graphics view through the list of states the automata has. It's a simple for each that creates a StateWidget on the heap, saves its reference in a vector so that it's deallocated properly when destroying the graphics view and adds it to the scene. I know it's quite inefficient as everything is rerendered every time a modification is done but I haven't managed to think of another way to so it yet.

    void GraphicView::mousePressEvent(QMouseEvent* event) {
        if(itemAt(event->pos()) != nullptr) {
            QGraphicsView::mousePressEvent(event);
            return;
        }
    
        std::cout << "APP MODE : " << AutomatLab::s_State << std::endl;
        
        switch(AutomatLab::s_State) {
            case None: break;
            case Node: {
                a->AddState(EStateKind::None, event->pos().x(), event->pos().y()); 
                Render();   // This rerenders the entire canva on every new State :xdd:
            }
            case Transition: break;
        }
    
        emit clicked(event->pos());
    }
    
    
    void GraphicView::Render() {
        for(auto& s : a->m_States) {
            StateWidget* sw = new StateWidget(mapToScene(s.x, s.y));
            sw->m_State = &s;
            connect(sw, &StateWidget::clicked, &AutomatLab::StateParams);
            items.push_back(sw);
            scene->addItem(sw);
        }
    }
    

    The issue is that when I try to build I get this error

    candidate function [with Func1 = void (StateWidget::*)(Automatlab::State *), Func2 = void (Automatlab::AutomatLab::*)(Automatlab::State *)] not viable: no known conversion from 'const typename QtPrivate::FunctionPointer<void (StateWidget::*)(State *)>::Object *' (aka 'const StateWidget *') to 'const typename QtPrivate::ContextTypeForFunctor<void (AutomatLab::*)(State *)>::ContextType *' (aka 'const Automatlab::AutomatLab *') for 3rd argument
    

    When I've double checked that the overload exists for connect.

    How can I achieve what I'm looking for in a clean way? I've thought of making the StateParams function static as there'd only be one instance of the UI at a time but I'm not a fan of that

    C Online
    C Online
    Christian Ehrlicher
    Lifetime Qt Champion
    wrote on 2 Jan 2025, 17:59 last edited by
    #2

    @Deneguil said in Connection signals for dynamically created widgets:

    connect(sw, &StateWidget::clicked, &AutomatLab::StateParams);

    You should pass a receiver object as third argument

    Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
    Visit the Qt Academy at https://academy.qt.io/catalog

    D 1 Reply Last reply 2 Jan 2025, 18:12
    1
    • C Christian Ehrlicher
      2 Jan 2025, 17:59

      @Deneguil said in Connection signals for dynamically created widgets:

      connect(sw, &StateWidget::clicked, &AutomatLab::StateParams);

      You should pass a receiver object as third argument

      D Offline
      D Offline
      Deneguil
      wrote on 2 Jan 2025, 18:12 last edited by
      #3

      @Christian-Ehrlicher said in Connection signals for dynamically created widgets:

      You should pass a receiver object as third argument

      Normally I would, but this overload was proposed by the LSP and it looked so convenient that I wanted to use it. I'll add a reference to the main app class to the graphics view then

      1 Reply Last reply
      0
      • D Deneguil
        2 Jan 2025, 17:36

        Hello,

        I am making a graphical automata editor. I have a custom graphics view that I use as the canvas for adding the nodes and later on the edges. I want to display the parameters of the clicked node in the sidebar. To do so, I thought of having the node widgets emit a clicked signal with a pointer to the underlying information. That signal would them be caught by a StateParams slot in the main application class.

        The relevant classes are as such :

        StateWidget.h

        constexpr float R = 50;
        
        class StateWidget : public QGraphicsEllipseItem {
        Q_OBJECT
            public:
                float x, y;
                State* m_State;
        
                inline StateWidget(QPointF p, QGraphicsItem* parent = nullptr): QGraphicsEllipseItem(p.x()-R/2,p.y()-R/2,R,R)
                {
                    setBrush(QBrush(Qt::white));
                    setZValue(10);
                }
        
            protected:
                inline void mousePressEvent(QGraphicsSceneMouseEvent *event) override {
                    event->accept();
                    std::cout << "State " << m_State->m_ID << " pressed\n";
                    emit clicked(m_State);
                }
            
            signals:
                void clicked(State* s);
        };
        

        AutomatLab.h

        class AutomatLab : public QMainWindow {
            Q_OBJECT
        
        private:
            Ui::AutomatLab m_UI;
            GraphicView m_GV;
            Automata m_Automata;
        
            void ToggleBtn(QPushButton* btn, bool toggled);
        
        public:
            static EClickState s_State;
        
            AutomatLab();
            ~AutomatLab();
        
        public slots:
            void StateParams(State* s);
            void InsertStateMode(bool toggled);
            void InsertTransitionMode(bool toggled);
            void ButtonUsed();
        };
        

        The StateWidgets are instanciated in the graphics view through the list of states the automata has. It's a simple for each that creates a StateWidget on the heap, saves its reference in a vector so that it's deallocated properly when destroying the graphics view and adds it to the scene. I know it's quite inefficient as everything is rerendered every time a modification is done but I haven't managed to think of another way to so it yet.

        void GraphicView::mousePressEvent(QMouseEvent* event) {
            if(itemAt(event->pos()) != nullptr) {
                QGraphicsView::mousePressEvent(event);
                return;
            }
        
            std::cout << "APP MODE : " << AutomatLab::s_State << std::endl;
            
            switch(AutomatLab::s_State) {
                case None: break;
                case Node: {
                    a->AddState(EStateKind::None, event->pos().x(), event->pos().y()); 
                    Render();   // This rerenders the entire canva on every new State :xdd:
                }
                case Transition: break;
            }
        
            emit clicked(event->pos());
        }
        
        
        void GraphicView::Render() {
            for(auto& s : a->m_States) {
                StateWidget* sw = new StateWidget(mapToScene(s.x, s.y));
                sw->m_State = &s;
                connect(sw, &StateWidget::clicked, &AutomatLab::StateParams);
                items.push_back(sw);
                scene->addItem(sw);
            }
        }
        

        The issue is that when I try to build I get this error

        candidate function [with Func1 = void (StateWidget::*)(Automatlab::State *), Func2 = void (Automatlab::AutomatLab::*)(Automatlab::State *)] not viable: no known conversion from 'const typename QtPrivate::FunctionPointer<void (StateWidget::*)(State *)>::Object *' (aka 'const StateWidget *') to 'const typename QtPrivate::ContextTypeForFunctor<void (AutomatLab::*)(State *)>::ContextType *' (aka 'const Automatlab::AutomatLab *') for 3rd argument
        

        When I've double checked that the overload exists for connect.

        How can I achieve what I'm looking for in a clean way? I've thought of making the StateParams function static as there'd only be one instance of the UI at a time but I'm not a fan of that

        J Offline
        J Offline
        JonB
        wrote on 2 Jan 2025, 18:33 last edited by JonB 1 Feb 2025, 18:35
        #4

        @Deneguil said in Connection signals for dynamically created widgets:

        When I've double checked that the overload exists for connect.

        Normally I would, but this overload was proposed by the LSP and it looked so convenient that I wanted to use it.

        [Btw, what's "LSP"?] As @Christian-Ehrlicher says for the solution. But OOI what overload of connect() do you think your call matches? If you think it's
        template <typename PointerToMemberFunction, typename Functor> QMetaObject::Connection QObject::connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
        that does not match on &AutomatLab::StateParams for Functor functor.

        D 1 Reply Last reply 2 Jan 2025, 18:43
        0
        • J JonB
          2 Jan 2025, 18:33

          @Deneguil said in Connection signals for dynamically created widgets:

          When I've double checked that the overload exists for connect.

          Normally I would, but this overload was proposed by the LSP and it looked so convenient that I wanted to use it.

          [Btw, what's "LSP"?] As @Christian-Ehrlicher says for the solution. But OOI what overload of connect() do you think your call matches? If you think it's
          template <typename PointerToMemberFunction, typename Functor> QMetaObject::Connection QObject::connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
          that does not match on &AutomatLab::StateParams for Functor functor.

          D Offline
          D Offline
          Deneguil
          wrote on 2 Jan 2025, 18:43 last edited by
          #5

          @JonB said in Connection signals for dynamically created widgets:

          [Btw, what's "LSP"?]

          LSP stands for Language Server Protocol, it's basically intellisense it tells you what functions are available and can detect some simple syntax errors before building

          As @Christian-Ehrlicher says for the solution. But OOI what overload of connect() do you think your call matches? If you think it's
          template <typename PointerToMemberFunction, typename Functor> QMetaObject::Connection QObject::connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
          that does not match on &AutomatLab::StateParams for Functor functor.

          Oh good catch, I thought it was saying Function instead.

          I'm unable to pass a reference to my AutomatLab class to GraphicsView as it'd make for a circular dependency so I've been trying a workaround by having another signal and basically forwarding the signal to the AutomatLab class as such

          void GraphicView::Render() {
              for(auto& s : a->m_States) {
                  StateWidget* sw = new StateWidget(mapToScene(s.x, s.y));
                  sw->m_State = &s;
                  emit connectStateWidget(sw);
                  items.push_back(sw);
                  scene->addItem(sw);
              }
          }
          
          // AutomatLab.cpp constructor
          connect(&m_GV, &GraphicView::connectStateWidget, this, [&] (StateWidget* sw) {
              connect(sw, &StateWidget::clicked, this, &AutomatLab::StateParams);
          });
          

          But now I'm getting a different error being

          error: no member named 'staticMetaObject' in 'QGraphicsEllipseItem'; did you mean simply 'staticMetaObject'?
          

          Even though my StateWidget class does include the Q_OBJECT macro. I'm thinking that it might be CMake breaking stuff or maybe it's because everything in inlined in that widget class as I orginally created it to test stuff before keeping it.

          J C 2 Replies Last reply 2 Jan 2025, 19:12
          0
          • D Deneguil
            2 Jan 2025, 18:43

            @JonB said in Connection signals for dynamically created widgets:

            [Btw, what's "LSP"?]

            LSP stands for Language Server Protocol, it's basically intellisense it tells you what functions are available and can detect some simple syntax errors before building

            As @Christian-Ehrlicher says for the solution. But OOI what overload of connect() do you think your call matches? If you think it's
            template <typename PointerToMemberFunction, typename Functor> QMetaObject::Connection QObject::connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
            that does not match on &AutomatLab::StateParams for Functor functor.

            Oh good catch, I thought it was saying Function instead.

            I'm unable to pass a reference to my AutomatLab class to GraphicsView as it'd make for a circular dependency so I've been trying a workaround by having another signal and basically forwarding the signal to the AutomatLab class as such

            void GraphicView::Render() {
                for(auto& s : a->m_States) {
                    StateWidget* sw = new StateWidget(mapToScene(s.x, s.y));
                    sw->m_State = &s;
                    emit connectStateWidget(sw);
                    items.push_back(sw);
                    scene->addItem(sw);
                }
            }
            
            // AutomatLab.cpp constructor
            connect(&m_GV, &GraphicView::connectStateWidget, this, [&] (StateWidget* sw) {
                connect(sw, &StateWidget::clicked, this, &AutomatLab::StateParams);
            });
            

            But now I'm getting a different error being

            error: no member named 'staticMetaObject' in 'QGraphicsEllipseItem'; did you mean simply 'staticMetaObject'?
            

            Even though my StateWidget class does include the Q_OBJECT macro. I'm thinking that it might be CMake breaking stuff or maybe it's because everything in inlined in that widget class as I orginally created it to test stuff before keeping it.

            J Offline
            J Offline
            JonB
            wrote on 2 Jan 2025, 19:12 last edited by JonB 1 Feb 2025, 19:17
            #6

            @Deneguil
            Do one complete, really clean rebuild (when Q_OBJECT is involved)?

            Just for the future

            Oh good catch, I thought it was saying Function instead.

            &AutomatLab::StateParams isn't even a (plain) function. Per the other param it's a PointerToMemberFunction, the member-ness makes it different. The 3 parameter overload would require a free function (or nowadays also a lambda, which is what you would use it for), or similar.

            1 Reply Last reply
            1
            • D Deneguil
              2 Jan 2025, 18:43

              @JonB said in Connection signals for dynamically created widgets:

              [Btw, what's "LSP"?]

              LSP stands for Language Server Protocol, it's basically intellisense it tells you what functions are available and can detect some simple syntax errors before building

              As @Christian-Ehrlicher says for the solution. But OOI what overload of connect() do you think your call matches? If you think it's
              template <typename PointerToMemberFunction, typename Functor> QMetaObject::Connection QObject::connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
              that does not match on &AutomatLab::StateParams for Functor functor.

              Oh good catch, I thought it was saying Function instead.

              I'm unable to pass a reference to my AutomatLab class to GraphicsView as it'd make for a circular dependency so I've been trying a workaround by having another signal and basically forwarding the signal to the AutomatLab class as such

              void GraphicView::Render() {
                  for(auto& s : a->m_States) {
                      StateWidget* sw = new StateWidget(mapToScene(s.x, s.y));
                      sw->m_State = &s;
                      emit connectStateWidget(sw);
                      items.push_back(sw);
                      scene->addItem(sw);
                  }
              }
              
              // AutomatLab.cpp constructor
              connect(&m_GV, &GraphicView::connectStateWidget, this, [&] (StateWidget* sw) {
                  connect(sw, &StateWidget::clicked, this, &AutomatLab::StateParams);
              });
              

              But now I'm getting a different error being

              error: no member named 'staticMetaObject' in 'QGraphicsEllipseItem'; did you mean simply 'staticMetaObject'?
              

              Even though my StateWidget class does include the Q_OBJECT macro. I'm thinking that it might be CMake breaking stuff or maybe it's because everything in inlined in that widget class as I orginally created it to test stuff before keeping it.

              C Online
              C Online
              Christian Ehrlicher
              Lifetime Qt Champion
              wrote on 2 Jan 2025, 19:14 last edited by
              #7

              @Deneguil said in Connection signals for dynamically created widgets:

              connect(sw, &StateWidget::clicked, this, &AutomatLab::StateParams);

              I doubt 'this' aka GraphicView is derived from AutomatLab ... please read the documentation on how signals and slots work and what you have to pass to the connect statement... https://doc.qt.io/qt-6/signalsandslots.html

              Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
              Visit the Qt Academy at https://academy.qt.io/catalog

              J D 2 Replies Last reply 2 Jan 2025, 19:17
              1
              • C Christian Ehrlicher
                2 Jan 2025, 19:14

                @Deneguil said in Connection signals for dynamically created widgets:

                connect(sw, &StateWidget::clicked, this, &AutomatLab::StateParams);

                I doubt 'this' aka GraphicView is derived from AutomatLab ... please read the documentation on how signals and slots work and what you have to pass to the connect statement... https://doc.qt.io/qt-6/signalsandslots.html

                J Offline
                J Offline
                JonB
                wrote on 2 Jan 2025, 19:17 last edited by
                #8

                @Christian-Ehrlicher said in Connection signals for dynamically created widgets:

                I doubt 'this' aka GraphicView is derived from AutomatLab

                Oh!!!

                1 Reply Last reply
                0
                • C Christian Ehrlicher
                  2 Jan 2025, 19:14

                  @Deneguil said in Connection signals for dynamically created widgets:

                  connect(sw, &StateWidget::clicked, this, &AutomatLab::StateParams);

                  I doubt 'this' aka GraphicView is derived from AutomatLab ... please read the documentation on how signals and slots work and what you have to pass to the connect statement... https://doc.qt.io/qt-6/signalsandslots.html

                  D Offline
                  D Offline
                  Deneguil
                  wrote on 2 Jan 2025, 20:20 last edited by
                  #9

                  @Christian-Ehrlicher said in Connection signals for dynamically created widgets:

                  I doubt 'this' aka GraphicView is derived from AutomatLab ... please read the documentation on how signals and slots work and what you have to pass to the connect statement... https://doc.qt.io/qt-6/signalsandslots.html

                  The this in this case is the instance of AutomatLab in this case! I tried to use the comment to indicate it was in the constructor of the AutomatLab.cpp after forwarding it but I should've used a different code block that's my bad

                  @JonB said in Connection signals for dynamically created widgets:

                  Do one complete, really clean rebuild (when Q_OBJECT is involved)?

                  I deleted the build folder, restarted vscode as it's not the first time CMake got a bit confused and sometimes restarting it fixed it and did a clean rebuild and still the same error. Which is weird to me as QGraphicsEllipseItem inherits from QObject so the macro should work

                  J 1 Reply Last reply 2 Jan 2025, 20:59
                  0
                  • D Deneguil
                    2 Jan 2025, 20:20

                    @Christian-Ehrlicher said in Connection signals for dynamically created widgets:

                    I doubt 'this' aka GraphicView is derived from AutomatLab ... please read the documentation on how signals and slots work and what you have to pass to the connect statement... https://doc.qt.io/qt-6/signalsandslots.html

                    The this in this case is the instance of AutomatLab in this case! I tried to use the comment to indicate it was in the constructor of the AutomatLab.cpp after forwarding it but I should've used a different code block that's my bad

                    @JonB said in Connection signals for dynamically created widgets:

                    Do one complete, really clean rebuild (when Q_OBJECT is involved)?

                    I deleted the build folder, restarted vscode as it's not the first time CMake got a bit confused and sometimes restarting it fixed it and did a clean rebuild and still the same error. Which is weird to me as QGraphicsEllipseItem inherits from QObject so the macro should work

                    J Offline
                    J Offline
                    JonB
                    wrote on 2 Jan 2025, 20:59 last edited by JonB 1 Mar 2025, 10:01
                    #10

                    @Deneguil said in Connection signals for dynamically created widgets:

                    as QGraphicsEllipseItem inherits from QObject

                    But it does not! QGraphicsItems do not inherit QObject, only QGraphicsObjects do. If StateWidget is a QGraphicsElipseItem (you never said what it is) you will have to multi-inherit to add QObject if you want to send signals from it.

                    D 1 Reply Last reply 2 Jan 2025, 21:28
                    1
                    • J JonB
                      2 Jan 2025, 20:59

                      @Deneguil said in Connection signals for dynamically created widgets:

                      as QGraphicsEllipseItem inherits from QObject

                      But it does not! QGraphicsItems do not inherit QObject, only QGraphicsObjects do. If StateWidget is a QGraphicsElipseItem (you never said what it is) you will have to multi-inherit to add QObject if you want to send signals from it.

                      D Offline
                      D Offline
                      Deneguil
                      wrote on 2 Jan 2025, 21:28 last edited by
                      #11

                      @JonB said in Connection signals for dynamically created widgets:

                      Bit it does not! QGraphicsItems do not inherit QObject, only QGraphicsObjects do.

                      You're right! I got confused when going up the inheritance tree in the documentation, I clicked on "inherited by QGraphicsObject" at some point thinking it was "inherits" instead.

                      I didn't specify in a written manner that my StateWidget was a QGraphicsEllipseItem as I had added the declaration of the class in the first message. I changed the class to inherit QObject as well though and it worked!

                      class StateWidget : public QObject, public QGraphicsEllipseItem { ... }
                      

                      The order of inheritance is important too.

                      The "Test n" below comes from the AutomatLab::StateParams function so the signal is properly forwarded to the main UI instance!
                      76d99a60-a567-4fff-9e5f-02f7a87ab787-image.png

                      Thank you for your help!

                      1 Reply Last reply
                      1
                      • D Deneguil has marked this topic as solved on 2 Jan 2025, 21:35

                      11/11

                      2 Jan 2025, 21:28

                      • Login

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