Issues with QScrollArea and custom widget
-
Hello folks,
I have a custom widget named
Display
that inherits fromQWidget
. For the sake of argument, let's just assume that that widget just paints aQPixmap
in itspaintEvent()
(because that's essentially what is happening).
I took that custom widget and put it into aQScrollArea
which I assigned as the central widget of myQMainWindow
.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 usessetFixedSize()
: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/LYYHT8QhFZWhat 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 modifyingDisplay::paintEvent()
to draw the pixmap with the correct aspect ratio.The
Display
class represents a virtual display of a given size in pixels. -
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
andheight
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 theQScrollArea
and add it as the central widget of theQMainWindow
:// 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 regularQToolBar
. I add it in that layout instead of usingQMainWindow::addToolbar()
because I want the toolbar to be between theQDockWidget
s 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
andheight
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 theQScrollArea
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 aQWidget
wrapper around theGDisplay
provided by the µGFX library. It's like a virtual display inside of aQWidget
. All it does is proxy stuff around. -
@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?
-
@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()
insideUGfxDisplay::paintEvent()
) and they (UGfxDisplay::_width()
andUGfxDisplay::width()
) are always the same value (and they don't change when zooming).
Keep in mind thatUGfxDisplay::width()
returns the width of my virtual display which is not supposed to change even if the person zooms in. This is not likeQWidget::width()
which would return_width * _scale
(which it does as I always callQWidget::setFixedSize(_scale*_width, _scale*_height)
.I assume that
QScrollArea
callsQWidget::width()
and notUGfxDisplay::width()
as it only has the pointer to theQWidget
anyway.Renaming my
width()
andheight()
methods would be quite an effort as they are used on many places. Is the QtCreator refactoring algorithm up to the task? ;) -
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
-
@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?
-
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?
-
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 theQPixmap
I draw in myUGfxDisplay::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. -
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 -
But why are you
redrawingreinitializing 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...