Issues with QScrollArea and custom widget



  • Hello folks,

    I have a custom widget named Display that inherits from QWidget. For the sake of argument, let's just assume that that widget just paints a QPixmap in its paintEvent() (because that's essentially what is happening).
    I took that custom widget and put it into a QScrollArea which I assigned as the central widget of my QMainWindow.

    My goal is to give the user the ability to zoom in and out of that custom widget (pixmap). For this I added a setScale(qreal scale) slot to my custom widget. It is essential that the aspect ratio of the custom widget doesn't change. To ensure this, my custom widget uses setFixedSize():

    Display::Display(int width, int height, QWidget* parent) : QWidget(parent), _width(width), _height(height), _scale(1.0)
    {
       ...
       // Configuration
       setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
       setFixedSize(width, height);
       ...
    }
    
    void Display::setScale(qreal scale)
    {
        setFixedSize(scale*_width, scale*_height);
    
        _scale = scale;
    }
    
    QSize Display::sizeHint() const
    {
        return QSize(_scale*_width, _scale*_height);
    }
    

    However, the QScrollArea still changes the aspect ratio of my widget if I zoom in so far that the scroll bars appear. Furthermore, even then the panning doesn't actually work.
    I made a video that demonstrates this problem: https://www.screencast.com/t/LYYHT8QhFZ

    What am I doing wrong? I'd appreciate any help on this.



  • So you want to keep the relation of the width and height of the pixmap?

    Kind Regards,

    Enrique Hernandez



  • Yes, but in this case the size of the Display (QWidget subclass) must keep the aspect ratio as well. This is not just about modifying Display::paintEvent() to draw the pixmap with the correct aspect ratio.

    The Display class represents a virtual display of a given size in pixels.


  • Qt Champions 2016

    Hey, long time no see!

    How do you build the scroll area, do you have a layout there? Mind showing the code/designer form (screenshot's fine)?

    It seems to me your initial values for width and height are wrong for some reason. You could try painting the widget background red (or a bright color) so you can get a glimpse of what it covers (beside the image itself).

    PS.
    The size hint doesn't do much here, as you've set all things to be fixed size, there's nothing to hint really. ;)



  • Hey man! Glad to see you alive :)

    How do you build the scroll area, do you have a layout there? Mind showing the code/designer form (screenshot's fine)?

    Sure, I have nothing to hide. But then again, there's is nothing really to show.
    Here's how I create the QScrollArea and add it as the central widget of the QMainWindow:

        // Central scroll area
        _centralScrollArea = new QScrollArea(this);
        _centralScrollArea->setFrameShape(QFrame::NoFrame);
        _centralScrollArea->setAlignment(Qt::AlignCenter);
        _centralScrollArea->setWidgetResizable(false);
    
        // Central widget
        QVBoxLayout* centralWidgetLayout = new QVBoxLayout;
        centralWidgetLayout->addWidget(_toolbar);
        centralWidgetLayout->addWidget(_centralScrollArea);
        QWidget* centralWidget = new QWidget(this);
        centralWidget->setLayout(centralWidgetLayout);
        setCentralWidget(centralWidget);
    

    _toolBar is just a regular QToolBar. I add it in that layout instead of using QMainWindow::addToolbar() because I want the toolbar to be between the QDockWidgets that I have left and right of the central widget.

    It seems to me your initial values for width and height are wrong for some reason. You could try painting the widget background red (or a bright color) so you can get a glimpse of what it covers (beside the image itself).

    What makes you think that the initial values for width and height of my custom widget are wrong? I checked them and they are fine. The widget is exactly the size I expect it to be. There's really no issue with my custom widget as long as I don't zoom into the QScrollArea deep enough to make it screw up the aspect ration (and make it fail the panning).

    PS. The size hint doesn't do much here, as you've set all things to be fixed size, there's nothing to hint really. ;)

    I was getting desperate ;)

    If it helps, here's the source of my custom widget (the one that I put into the QScrollArea): ugfxdisplay.cpp
    It's a QWidget wrapper around the GDisplay provided by the µGFX library. It's like a virtual display inside of a QWidget. All it does is proxy stuff around.


  • Qt Champions 2016

    @Joel-Bodenmann said in Issues with QScrollArea and custom widget:

    Hey man! Glad to see you alive :)

    Surprising, right? ;)

    What makes you think that the initial values for width and height of my custom widget are wrong?

    We old people often get confused. I noticed something strange about the scrollbars but perhaps that's nothing.

    QImage img((const uchar*)pixmapSurface, width(), height(), width()*sizeof(color_t), QImage::Format_RGB32);
    painter.drawImage(event->rect(), img, img.rect());
    

    Can you check how width() relates to _width and the same for the height here? I'm pretty sure you'd find them to differ.



  • Surprising, right? ;)

    Not really, given all the "healthy" food you eat ;)

    Can you check how width() relates to _width and the same for the height here? I'm pretty sure you'd find them to differ.
    Well... how can I say this but:

    int UGfxDisplay::width() const
    {
        return _width;
    }
    

    I don't see how that could possibly go wrong. Unless... is this a shadowing problem?


  • Qt Champions 2016

    @Joel-Bodenmann said in Issues with QScrollArea and custom widget:

    I don't see how that could possibly go wrong. Unless... is this a shadowing problem?

    Possibly, yes, try to rename those two methods to something. To be honest I don't really know what width() should return for a widget inside a scroll area, but my spidy sense is tingling. Notice that you get the scrolls when one of the dimensions reaches the scroll area border. That makes me think it's a "bad drawing" on your part, some mismatch of image/widget rects.



  • I just checked (using qDebug() inside UGfxDisplay::paintEvent()) and they (UGfxDisplay::_width() and UGfxDisplay::width()) are always the same value (and they don't change when zooming).
    Keep in mind that UGfxDisplay::width() returns the width of my virtual display which is not supposed to change even if the person zooms in. This is not like QWidget::width() which would return _width * _scale (which it does as I always call QWidget::setFixedSize(_scale*_width, _scale*_height).

    I assume that QScrollArea calls QWidget::width() and not UGfxDisplay::width() as it only has the pointer to the QWidget anyway.

    Renaming my width() and height() methods would be quite an effort as they are used on many places. Is the QtCreator refactoring algorithm up to the task? ;)


  • Qt Champions 2016

    No idea, my code is usually so bad it doesn't need refactoring but plain rewriting. ;)

    Anyway, forget that for a moment, can you try something like this:

    painter.drawImage(event->rect(), img, img.rect().intersected(event->rect()));
    


  • @kshegunov said in Issues with QScrollArea and custom widget:

    No idea, my code is usually so bad it doesn't need refactoring but plain rewriting. ;)

    Still waiting on my QAbstractModel merging thingy ;)

    Anyway, forget that for a moment, can you try something like this:

    Here you go: https://www.screencast.com/t/nNbHckWi


  • Qt Champions 2016

    @Joel-Bodenmann said in Issues with QScrollArea and custom widget:

    Still waiting on my QAbstractModel merging thingy ;)

    I know, I know.

    Here you go: https://www.screencast.com/t/nNbHckWi

    Well, something moved at least. Can you get the paint rects you get when zoomin in/out and when you scroll up/down/left/right along with the image rects and dump them all into a file?


  • Lifetime Qt Champion

    Hi,

    Might be a silly question but how are you handling UGfxDisplay ?



  • @kshegunov said in Issues with QScrollArea and custom widget:

    Well, something moved at least. Can you get the paint rects you get when zoomin in/out and when you scroll up/down/left/right along with the image rects and dump them all into a file?

    Sure, I'll get that done.

    @SGaist said in Issues with QScrollArea and custom widget:

    Might be a silly question but how are you handling UGfxDisplay ?

    I'm sorry, I don't understand what handling means in this context. Can you be more specific?


  • Lifetime Qt Champion

    Are you calling something like _centralScrollArea->setWidget(_myGDisplay); ?

    Then, are you sure that you should handle the scaling in GDisplay ?

    When zooming, what about having something like _myGDisplay->resize(_scale * _sourceSize); So you only handle painting in that widget.



  • @kshegunov said in Issues with QScrollArea and custom widget:

    Well, something moved at least. Can you get the paint rects you get when zoomin in/out and when you scroll up/down/left/right along with the image rects and dump them all into a file?

    Here you go: http://paste.ugfx.io/show/a5113bcc08

    @SGaist said in Issues with QScrollArea and custom widget:

    Are you calling something like _centralScrollArea->setWidget(_myGDisplay); ?

    Yep, that's what I am doing.

    @SGaist said in Issues with QScrollArea and custom widget:

    When zooming, what about having something like _myGDisplay->resize(_scale * _sourceSize); So you only handle painting in that widget.

    In my opinion that won't work as it will not scale up the contents inside of my UGfxDisplay. Keep in mind that the QPixmap I draw in my UGfxDisplay::paintEvent() is the content of a virtual display. That virtual display has a given size (width and height) in pixels. When zooming in I need to scale up everything. I don't display more of the widget, I just display the same content bigger.


  • Qt Champions 2016

    Have you noticed your image doesn't grow (or shrink)?



  • The image is not supposed to grow. The image represents the contents of the virtual display. It will always have the same amount of pixels no matter how much I zoom into the UGfxDisplay widget. My virtual display does not grow by zooming into it :p


  • Qt Champions 2016

    But why are you redrawing reinitializing it at each paint event then? It got me confused, now I believe I know what you want. Should be something like this (I *think*):

    painter.drawImage(event->rect(), img, event->rect() / _scale);
    

    PS. Or not. I need to think.
    PS. After thinking:

    QRect paintRect = event->rect();
    QRect sourceRect(paintRect.topLeft() / _scale, paintRect.size() / _scale);
    painter.drawImage(paintRect, img, sourceRect);
    


  • Wooo, I love you \o/ (still, not again, don't worry honey)

    This is with your "After thinking" code: https://www.screencast.com/t/cEAa2z4Zd

    In retrospect this seems somewhat obvious...


  • Qt Champions 2016

    @Joel-Bodenmann said in Issues with QScrollArea and custom widget:

    Wooo, I love you

    Nice! Love and world peace and such things. ;)

    In retrospect this seems somewhat obvious...

    Yes, quantum mechanics also seems rather obvious to me ... after a few people thought and invented it. ;)



  • @kshegunov said in Issues with QScrollArea and custom widget:

    Yes, quantum mechanics also seems rather obvious to me ... after a few people thought and invented it. ;)

    invented? ;)
    Too bad those people didn't teach you how to write nice code though :p

    Thank you for your help guys, much appreciated!


  • Qt Champions 2016

    @Joel-Bodenmann said in Issues with QScrollArea and custom widget:

    Too bad those people didn't teach you how to write nice code though :p

    This is all a fault of my own, sadly, but hey life's not all roses ... :)

    Thank you for your help guys, much appreciated!

    You are welcome. Now go and make us proud! ;D


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.