Using QGraphicsView.fitInView( ... ), how can I predict whether scrollbars will be needed? - python

I have a PyQt5 project where I create complex geometrical objects in a QGraphicsScene, and them show them in a QGraphicsView - standard stuff.
When I run my code on a large (1920x1080) monitor, the scene is displayed nicely with no scrollbars. However when I run it on a 1366x768 device, the viewport is too small, and I only see a part of the object, with scrollbars.
(In both cases, the window starts maximised.)
To fix this, I added the following code when the window is initialised:
my_view.fitInView(my_scene.sceneRect(), Qt.KeepAspectRatio)
and this works perfectly on the small monitor. However, on the large monitor, the viewport is now made smaller, and the scene is smaller than before.
So I try to predict in advance whether the scene is going to fit in the view or not, and print out some sizes to try to work out how:
print("A", my_scene.sceneRect().size())
print("B", my_view.size())
print("C", my_view.viewport().size())
And I get this:
A PyQt5.QtCore.QSizeF(227.0, 702.0)
B PyQt5.QtCore.QSize(640, 480)
C PyQt5.QtCore.QSize(621, 478)
I get the same set of numbers before and after fitInView(), on both displays.
How can I predict whether or not the scene will fit in the view without scrollbars, so that I don't make the graphic smaller when it doesn't need to be, but still is rescaled when it is not going to fit?

Related

PyQt QScrollBar.setInvertedControls() has no effect

I a making PyQt5 GUI application where I need to show a vertical QScrollBar in an inverted shape (zero below and max above).
Calling setInvertedAppearance(True) is enough to show it properly. However, the controls still behave as usual (clicking the down arrow increases the value, which goes UP, and vise versa).
Calling setInvertedControls() does not seem to have any effect!

PyQt5 Pixmap in Label dynamic resize via Splitter

I am currently trying to solve a problem with resizing Pixmap dynamically, I have a QLabel in one corner of my QMainWindow, from both sides surrounded by two different QSplitters when I change picture it is scaled by the size of the label with KeepAspectRatio. Both splitters also are connected with signal to a function that once again scales the pixmap to fit as much as it can.
The problem I ran into is, that I cannot figure out how to be able to decrease the size when the pixmap already fits all the space available, because at that moment the splitters just stop working.
This is the pixmap setup:
self.picture_field.setStyleSheet("border: 4px solid")
self.pixmap = QPixmap('dandelion.jpg')
self.picture_field.setAlignment(Qt.AlignCenter)
self.picture_field.setPixmap(self.pixmap.scaled(self.picture_field.width()-8,
self.picture_field.height()-8, Qt.KeepAspectRatio))
The -8 has to be there because of the border, if it was missing, the widget gets bigger with every change
One of the Qsplitters:
right_splitter = QSplitter(Qt.Vertical)
right_splitter.addWidget(self.picture_field)
right_splitter.addWidget(self.person_field)
right_splitter.splitterMoved.connect(self.dynamic_scaling)
Person_field is simple QTreeView
The dynamic scaling function:
def dynamic_scaling(self):
self.picture_field.setPixmap(self.pixmap.scaled(self.picture_field.width()-8, self.picture_field.height()-8, Qt.KeepAspectRatio))
EDIT: I tested it a little bit more, and it seems, that the pixmap reacts, but only once either the width or height is halved.
Judging by this statement, "I cannot figure out how to be able to decrease the size when the pixmap already fits all the space available, because at that moment the splitters just stop working", I think I know what the problem is. Every QWidget has a minimum size, available through the function QWidget.minimumSize. From experience I know that a QSplitter honors the minimum sizes of its children, and it will not allow the user to move the sash to a position that would force one of the children to take a size less than its minimum.
You say that one of the splitter's children is a QLabel. By design, QLabels are pretty smart about their own size requirements. If you change the text of a QLabel, for example, it will automatically recompute its size values and trigger a re-layout. I assume it does the same thing if you change the Pixmap, although I have not tried this myself. The point is that the QLabel's minimum size changes as its contents change.
Because of the dynamic resizing of the QLabel, and the refusal of a QSplitter to allow a child widget to violate its minimum size, the splitter may appear to "get stuck" and prevent you from making it smaller.
To fix this you need to make the QLabel have a minimum size that's very small, say (1,1). There are a few ways to do this. Since I don't have your complete application I can't be sure which one will work best for you.
Subclass QLabel and override minimumSize to return (0,0) This is pretty straightforward.
Set the size policy on the QLabel object to QSizePolicy.Ignore. See the docs for the function QWidget.SetSizePolicy.
Use the function QWidget.SetMimimumSize. I'm not sure this will work for QLabels since any change you try to make may get undone the next time you change the pixmap.
Replace your QLabel with a custom widget subclassed directly from QWidget. A generic QWidget has no minimum size, so your problem goes away. You will need to write a small function to set a new pixmap into your control, which will entail a paint event handler.
I hope that one of these methods will work for you (and that I have understood the problem correctly).
try that:
w = QtGui.Qlabel.width();
h = QtGui.Qlabel.height();
#set a scaled pixmap to a w x h window keeping its aspect ratio
QtGui.Qlabel.setPixmap(p.scaled(w, h, Qt.KeepAspectRatio))
now try to change h and w.
If you get any issues comment below.
πŸ…ΏπŸ†ˆπŸ†€πŸ†ƒ

Send widgets content scaled to printer

I'm using QWidget.grab to get a pixmap containing what a component currently displays. It works perfectly apart from the size of the component not suitable for my printing requirements.
Is there a way so I can grab the pixmap of the component in a particular size?
QPixMap.grab() in Qt5 and QPixMap.grabWidget() in Qt4 get a pixmap of a painted widget in the screen resolution which is the natural painting resolution. Using the rectangle parameter you can even get parts of widgets.
The natural output resolution is the widgets own screen resolution. But afterwards you can scale it to anything you like via QPixMap.scaled() so that it fits your printing requirements.
It would actually be nice to change (simple scale) the painting resolution when grabing. But I don't know of any way to achieve this.

Wxpython, Force window size to be bigger than screen size

How do I make WXPython disregard monitor sizes, when it resizes?
I want to make a really large window, so I can get a bitmap of all elements within the window. This program is not meant to be interactive in any way. I am using wxpython to set up a poster, using sizers, pictures and text, and then getting a bitmap via the DC.
All I found on this issue was a similar question, asking about the same problem, using the WinAPI.

How to show/hide a child QWidget with a motion animation?

I am working on an application with two children. One's a widget that functions as a toolbar, the second, below, functions as dashboard, on which information would appear. The latter can be shown/hidden with buttons on the former. Here's a screen-cast of the prototype.
Now I am looking at doing the same but with a motion animation whilst showing/hiding the lower widget.
In short: the effect should be giving the impression the entire application rises or falls progressively when toggling the dashboard.
In details: I would like the height of the lower widget to decrease until it is reduced to 0 and then hidden completely. Likewise it would increase slowly when showing the widget again. In the meanwhile the position of the application should change accordingly so it stays at the bottom of the screen.
How can I do that? I've never done animations on Qt before. If you don't have an answer, do you know of a quality tutorial that could lead me there.
NB: I am using PyQt.
I think you can get what you want by using a QPropertyAnimation that animates the geometry property of your widget.
But IMHO this is the window manager's role to do what you want. Maybe you will have some headaches bypassing it (but I'm maybe wrong).
After better reading of your question, it seems that you want to use your own components to trigger the hiding/showing so the WM shouldn't be a problem.
As a start here is some code that animate a minimizing of a widget (assuming tbw is an instance of the widget you want to animate):
formerGeometry = QtCore.QRect(tbw.geometry()) # storing previous geometry in order to be able to restore it later
hideAnimation = QtCore.QPropertyAnimation(tbw, "geometry")
hideAnimation.setDuration(2000) # chose the value that fits you
hideAnimation.setStartValue(formerGeometry)
#computing final geometry
endTopLeftCorner = QtCore.QPoint(tbw.pos() + QtCore.QPoint(0, tbw.height()))
finalGeometry = QtCore.QRect(endTopLeftCorner, QtCore.QSize(tbw.width(), 0))
hideAnimation.setEndValue(finalGeometry)
hideAnimation.start()

Categories