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. QPushButton geometry issue

QPushButton geometry issue

Scheduled Pinned Locked Moved General and Desktop
geometryrectpaint
8 Posts 2 Posters 4.4k 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.
  • T Offline
    T Offline
    Tannz0rz
    wrote on 12 Oct 2015, 17:52 last edited by Tannz0rz 11 Feb 2015, 17:21
    #1

    I'm creating my own click effect, rather than the default emphasized by the border. It simply fades into a white rect over top of the QPushButton. You can see it in the upper-left in this image:
    http://i.imgur.com/buqYXyn.png

    As you can tell, it doesn't fully cover the button as I was hoping it would and I'm not sure why.

    EDIT: Here is the working code folks. The gentleman in the comments was less than helpful in providing a meaningful solution, but getting a grips with the workings of Qt isn't terribly difficult provided the excellent documentation. Perhaps this will assist others when coming across a similar situation.

    pushbutton.h:

    #ifndef PUSHBUTTON
    #define PUSHBUTTON
    
    #include <QtCore>
    #include <QPushButton>
    #include <QPainter>
    
    class PushButton : public QPushButton
    {
        Q_OBJECT
    
    public:
        explicit PushButton(QString text, QWidget *parent = 0);
    
    protected:
        virtual void paintEvent(QPaintEvent *);
    
    public slots:
        void OnClick();
        void OnTick();
    
    private:
        enum States
        {
            NONE = 0,
            RESET,
            DRAW
        } state = NONE;
    
        QTimer *timer;
    
        float delta = -1.0f;
    };
    
    #endif
    

    pushbutton.cpp:

    #include "pushbutton.h"
    
    PushButton::PushButton(QString text, QWidget *parent) : QPushButton(text, parent)
    {
        timer = new QTimer(this);
    
        QObject::connect(this, SIGNAL(clicked(bool)), this, SLOT(OnClick()));
        QObject::connect(timer, SIGNAL(timeout()), this, SLOT(OnTick()));
    }
    
    void PushButton::paintEvent(QPaintEvent *e)
    {
        QPushButton::paintEvent(e);
    
        if(state == States::NONE)
        {
            return;
        }
    
        QPainter painter(this);
    
        if(state == States::RESET)
        {
            state = States::NONE;
    
            painter.eraseRect(rect());
    
            update();
        }
        else
        {
            painter.fillRect(rect(), QColor(200, 200, 200, 200 * (delta < 1.0f ? delta : 2.0f - delta)));
        }
    }
    
    void PushButton::OnClick()
    {
        state = States::DRAW;
    
        delta = 0.0f;
    
        timer->start(20);
    }
    
    void PushButton::OnTick()
    {
        delta += 0.2f;
    
        if(delta > 2.0f)
        {
            state = States::RESET;
    
            timer->stop();
        }
    
        update();
    }
    
    

    Regards,
    Tannz0rz

    1 Reply Last reply
    0
    • C Offline
      C Offline
      Chris Kawa
      Lifetime Qt Champion
      wrote on 12 Oct 2015, 20:14 last edited by Chris Kawa 10 Dec 2015, 20:15
      #2

      Hi, welcome to devnet.

      Ok, it's gonna be hard to explain but I'll try...
      There are a couple of fundamental mistakes here:

      ButtonClickEffect inherits from QWidget. This is wrong to start with. It's an effect applied to a widget. It shouldn't be a widget itself.

      Then you cast QWidget* parent to QPushButton with a C cast...ugh, don't do that. If I pass a QLineEdit as a parent this code will crash and burn.

      Then there's a paint event. It's a paint event of the effect widget, not the button widget, so passing button->geometry() to fillRect is wrong, as its a geometry that possibly (and in this case does) span outside the clip rectangle of the widget being painted.

      You're giving your ButtonClickEffect widget a parent, but it doesn't place it in any layout or anything. So it will just have some default size (what you see in the picture). It will not resize to the size of it's parent on its own, and you can't paint outside of the rectangle of that widget (like you try to do).

      This whole approach is not really good. Just install an event filter on the button and hijack its paint event there to do your custom drawing.

      T 1 Reply Last reply 12 Oct 2015, 20:59
      0
      • C Chris Kawa
        12 Oct 2015, 20:14

        Hi, welcome to devnet.

        Ok, it's gonna be hard to explain but I'll try...
        There are a couple of fundamental mistakes here:

        ButtonClickEffect inherits from QWidget. This is wrong to start with. It's an effect applied to a widget. It shouldn't be a widget itself.

        Then you cast QWidget* parent to QPushButton with a C cast...ugh, don't do that. If I pass a QLineEdit as a parent this code will crash and burn.

        Then there's a paint event. It's a paint event of the effect widget, not the button widget, so passing button->geometry() to fillRect is wrong, as its a geometry that possibly (and in this case does) span outside the clip rectangle of the widget being painted.

        You're giving your ButtonClickEffect widget a parent, but it doesn't place it in any layout or anything. So it will just have some default size (what you see in the picture). It will not resize to the size of it's parent on its own, and you can't paint outside of the rectangle of that widget (like you try to do).

        This whole approach is not really good. Just install an event filter on the button and hijack its paint event there to do your custom drawing.

        T Offline
        T Offline
        Tannz0rz
        wrote on 12 Oct 2015, 20:59 last edited by
        #3

        @Chris-Kawa Thanks. How do you force a paint update in that case? It needs to be updated within the timer.

        1 Reply Last reply
        0
        • C Offline
          C Offline
          Chris Kawa
          Lifetime Qt Champion
          wrote on 12 Oct 2015, 21:03 last edited by
          #4

          You can call either update() or more directly repaint() on it in response to timer event and then do the painting in the event filter..

          T 1 Reply Last reply 12 Oct 2015, 21:24
          0
          • C Chris Kawa
            12 Oct 2015, 21:03

            You can call either update() or more directly repaint() on it in response to timer event and then do the painting in the event filter..

            T Offline
            T Offline
            Tannz0rz
            wrote on 12 Oct 2015, 21:24 last edited by
            #5

            @Chris-Kawa I've tried the following, but the results are unsavory. It flashes white over top of the button and thereafter the text becomes hidden until the next paintEvent call. The update seems to work, just that it doesn't render as it ought to.

            Header: http://pastebin.com/cW72y2ns
            Source: http://pastebin.com/H7vQgEy6

            Initialization:

            pushButton->installEventFilter(new ButtonClickEffect(pushButton));
            
            1 Reply Last reply
            0
            • C Offline
              C Offline
              Chris Kawa
              Lifetime Qt Champion
              wrote on 12 Oct 2015, 21:38 last edited by
              #6

              You're discarding the button default paint event so yes, you will not see any text (or a button) with that code.
              What is the result you're trying to get?

              T 1 Reply Last reply 12 Oct 2015, 21:46
              0
              • C Chris Kawa
                12 Oct 2015, 21:38

                You're discarding the button default paint event so yes, you will not see any text (or a button) with that code.
                What is the result you're trying to get?

                T Offline
                T Offline
                Tannz0rz
                wrote on 12 Oct 2015, 21:46 last edited by
                #7

                @Chris-Kawa A quick flash of white overlaid on the button. As in my fillRect, I set the alpha to

                200 * (delta < 1.0f ? delta : 2.0f - delta)
                

                So it scales up to 1 from 0 and down to 0 from 1.

                1 Reply Last reply
                0
                • C Offline
                  C Offline
                  Chris Kawa
                  Lifetime Qt Champion
                  wrote on 12 Oct 2015, 22:06 last edited by
                  #8

                  Well yes, but you're painting that color with alpha over whatever widget is under the button, since the event filter is called before paint event of the button and you return true from it so the button's paint event is never actually called. To see the button painted underneath you need to actually call the paint event of the button manually before you overpaint it with your color.

                  Unrelated comments:

                  You seem to have a very bad habit of just casting types with a C cast to whatever you think you need at the moment. I'm referring to the QWidget *widget = (QWidget *)object; cast in the eventFilter. If I used any non-widget QObject derived type with your class it would compile successfully and crash at runtime. There's a isWidgetType() method on a QObject that you can test with before casting. Also please don't use C style casts. It's just plain evil and you will cause bugs with it. Either to yourself or other people that use your classes. There's a lot safer qobject_cast for QObject derived types that will return nullptr that you can test for if the type can't be cast.

                  Painting in response to a timer with interval of 10ms makes no sense on almost any of user's machines. The typical display is ticking at 60Hz (i.e. little over 16ms) so anything lower than that is just giving Qt more work in filtering out the update requests you are flooding it with. Displays with a higher refresh rate are still very rare and even on them there's not gonna be a noticeable difference if you stick to 60Hz animation.

                  Using == operator on a floating point numbers is on borderline of being a bug. You should practically never do that unless you're actually testing floating number stability, which is like super rare. Use non-strict comparisons (< and >) or just stick to fixed point integers for steps like that.

                  1 Reply Last reply
                  0

                  1/8

                  12 Oct 2015, 17:52

                  • Login

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