QPushButton, Overwrite and StyleSheet



  • Hi, everyone,

    I have a QPushButton subclass cButton. I did that to add a new state for the StyleSheet and to forward mouse events to the parent widget.

    Seems simple enough:

    class cButton: public QPushButton
    {
        Q_OBJECT
        Q_PROPERTY(bool connected READ isConnected WRITE changeConnected)
    public:
        explicit cButton(QWidget *parent = 0): QPushButton(parent){}
        ~cButton(){}
        bool isConnected(){return connected;}
    
    public slots:
        void changeConnected(bool state){
            connected = state;
            style()->unpolish(this);
            style()->polish(this);
        }
    signals:
        void signalPressedEvent(QMouseEvent *e);
        void signalReleaseEvent(QMouseEvent *e);
    protected:
        bool connected = false;
        QPoint m_Origin;
    
        virtual void mousePressEvent(QMouseEvent *e) Q_DECL_OVERRIDE{
            m_Origin = mapToGlobal(e->pos());
            emit signalPressedEvent(e);
            emit pressed();
        }
        virtual void mouseReleaseEvent(QMouseEvent *e) Q_DECL_OVERRIDE{
            QPoint m_Dest = mapToGlobal(e->pos());
            emit signalReleaseEvent(e);
            if(abs(m_Dest.x()-m_Origin.x()) < this->width()/4 && abs(m_Dest.y()-m_Origin.y())<this->height()/4){
                emit released();
                emit clicked();
            }
        }
    };
    

    The problem is, the StyleSheet does not recognize the pressed state anymore.
    QPushButton:pressed{ //do Stuff} has no effect .
    QPushButton[connected = true]{//doStuff} works fine, so I could replace pressed with an other custom property, but I'm wondering, if there's an other way.

    I can't change the mousePressEvent function to this:

    virtual void mousePressEvent(QMouseEvent *e) Q_DECL_OVERRIDE{
            m_Origin = mapToGlobal(e->pos());
            emit signalPressedEvent(e);
            QPushButton::mousePressEvent(e);
        }
    

    It would enable the StyleSheet part, but than signalPressedEvent(e) would not be forwarded to the parent.


  • Lifetime Qt Champion

    Hi,

    Out of curiosity, why do you need to "forward" that event to the parent widget like that ?


  • Moderators

    You should call the base implementation. Doing otherwise is just a bug lurking to be uncovered when least expected. Also passing an event pointer via a signal is a bad idea. You're not in control of how someone connects to it, and if the connection is in any way deferred (i.e. to another thread or via queued connection) the pointer will be invalid, and that's another hard to track bug for the future.
    Events are not meant to be moved around via signals. If you want to pass an event to a parent you can always create a new event (by copying the one you got) and use sendEvent. That's yet another unusual thing to do though. Are you sure you need the signals to pass the event pointer? Wouldn't it be enough to pass any event data that you need (e.g. the cursor position or mouse buttons)?



  • Hi,
    So I totally missed that someone answered. Sorry about that.

    To answer some questions:

    @SGaist said in QPushButton, Overwrite and StyleSheet:

    Hi,

    Out of curiosity, why do you need to "forward" that event to the parent widget like that ?

    I have a custom widget that manages a series of buttons, labels and animations. <- That is working fine.

    Now was the idea to allow the user to "grab" the widget and move it around. To realize that, I need to catch when a mousebutton is pressed and the mose is moved. To calculate the new position.
    I overwrote the QMouseEvents in the main custom Widget to get this information. But whenever I lclick on a QPushButton, no Signal is passed to the main widget.

    @Chris-Kawa said in QPushButton, Overwrite and StyleSheet:

    Events are not meant to be moved around via signals. If you want to pass an event to a parent you can always create a new event (by copying the one you got) and use sendEvent. That's yet another unusual thing to do though. Are you sure you need the signals to pass the event pointer? Wouldn't it be enough to pass any event data that you need (e.g. the cursor position or mouse buttons)>

    A good point, I only need the pos() and the QMouseEvent::Type, I'll change that later for sure.

    So, over the weekend, I managed to find a solution. However, I'm not sure why I have to do shedule the signal into the next eventloop cycle.

    This way I get the Signal in my other class:

    virtual void mousePressEvent(QMouseEvent *e) Q_DECL_OVERRIDE{
            m_Origin = mapToGlobal(e->pos());
            QTimer::singleShot(0,this,[=]{emit signalMouseEvent(e);});
            QPushButton::mousePressEvent(e);
        }
    

    This way I don't get the Signal:

    virtual void mousePressEvent(QMouseEvent *e) Q_DECL_OVERRIDE{
            m_Origin = mapToGlobal(e->pos());
            emit signalMouseEvent(e);
            QPushButton::mousePressEvent(e);
        }
    


  • @Chris-Kawa turns out, you were spot on! I had some difficulties to compile my "fix" in release configuration.
    So I went and rewrote the class, with your comment in mind. I intoduced a new signal and changed the overrides accordingly.

    void signalEvent(QMouseEvent::Type t, QPoint p);
    

    now, I don't need the timer, it also works in debug as well as in release mode...

    virtual void mousePressEvent(QMouseEvent *e) Q_DECL_OVERRIDE{
            emit signalEvent(QMouseEvent::MouseButtonPress, e->pos());
            QPushButton::mousePressEvent(e);
        }
    

    works like a charm. Thanks!