Python Forum
[PyQt] Size Policy, Laout and Official Documentation
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[PyQt] Size Policy, Laout and Official Documentation
#1
I am a very new to Python. I have no prior experience in this field, so in order to learn programming, I started to learn Python on my own. To create a simple GUI I am using PyQt5 module. I wrote this code:

from PyQt5 import QtWidgets
from PyQt5.QtWidgets import *


def button_clicked():
    global num
    num += 1
    button.setText("Click on me")
    label1.setText(f"Button is pressed {num} times.")
    label2.setText("You are welcome to my GUI. I am trying to stretch the label" + f"\nThis is {num} times" + "\nI am welcoming you.")


num = 0
app = QtWidgets.QApplication([])
label1 = QtWidgets.QLabel("Hello World.")
label2 = QtWidgets.QLabel("You are welcome to my GUI.")
button = QtWidgets.QPushButton("Click on me. I am waiting")
window = QWidget()
layout = QGridLayout(window)
layout.addWidget(label1, 1, 1)
layout.addWidget(label2, 2, 1)
layout.addWidget(button, 2, 4)
button.clicked.connect(button_clicked)
window.show()
window.setGeometry(100, 100, 280, 100)
app.exec()
Now I think the button is changing shape. I asked this on StackOvetflow, and they said it might be possible because of the size policy of the button. They shared this link

But when I read the document, I got that when the layout is present like the way it is in my code, the size policy of the button should not work, the resize of the button or similar behavior is taken care of by the layout. Though there is no size policy property in Qlayout or QGridlayout.

[Image: 41pwc.jpg]

Why is it contradicting with the official document? I am confused, and must be missing something. Please help me.
Reply
#2
Okay first I am not quite sure what you were asking about but it is always good to start from a good base and your program is lacking that base so in an effort to help you out here is a more solid base for you to work from. I assume eventually you will graduate to using QWindow but here is a basic version of what you implemented.
from sys import exit as sysExit

# Normally I restrict this to just the pieces I am using but when testing and such
# I do this in order to speed things up a smidge
from PyQt5.QtCore import *
from PyQt5.QtGui  import *
from PyQt5.QtWidgets import *
# Of course every GUI needs a MainWindow that first thing you show off if your not using a splash screen
# this is a basic Widget window fairly easy to deal with and it contains everything until you get to big for it
# note QWindow is a slightly different beast that gives a more robust base window but this one works for now
class MainWindow(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        # These first three lines are a must have in just about every class you create

        # Setting the windows specifics
        title = "Main Window"
        self.setWindowTitle(title)

        # I do the following because it helps remind me of what these
        # 4 values represent and its really simple to understand
        left=100;top=100;width=280;height=100
        self.setGeometry(left,top,width,height)

        # This variable is now global to this Class
        self.num = 1

        # These objects are now available to this Class and will
        # stick around for as long as the class does which you definitely
        # want to occur -- oh the magic of "self."
        self.Label1 = QLabel('Hello World')

        self.Label2 = QLabel('You are welcome to my GUI')

        self.Button1 = QPushButton('Click on me, I am waiting')
        self.Button1.clicked.connect(self.MyButtonClicked)

#  Here is what your QGridLayout would have looked like but
#  a QGridLayout was over kill for what you were doing so I
#  put it in a QVBoxLayout instead
#        self.Grid1 = QGridLayout(window)
#        self.Grid1.addWidget(label1, 1, 1)
#        self.Grid1.addWidget(label2, 2, 1)
#        self.Grid1.addWidget(button, 2, 4)

        # Creating the Simple Layout
        self.Simple1 = QVBoxLayout()
        self.Simple1.addWidget(self.Label1)
        self.Simple1.addWidget(self.Label2)
        self.Simple1.addWidget(self.Button1)
        # Adding this so when you stretch your window
        # the above objects stay in place note stetching
        # a Grid Layout is done differently
        self.Simple1.addStretch(1)

        # Okay now we add our layout to our window that is assuming
        # you want to see it that is
        self.setLayout(self.Simple1)

    def MyButtonClicked(self):
        # Notice what happens with the "self." since this method
        # is within the class it has access to all of these self.objects
        self.num += 1

        self.Button1.setText("Click Me Again")

        # I have changed your Text in two different ways just to show you
        # 2 fairly simple and easy to understand ways of doing it

        Label1Text = ('You have entered my World ' + str(self.num) + ' times')
        self.Label1.setText(Label1Text)

        Label2Text = 'You are welcome to my GUI,\nI am trying to stretch the label here by welcoming you '
        Label2Text += str(self.num)
        Label2Text += ' times\nSo you are welcome here once again'
        self.Label2.setText(Label2Text)

# Every Python program needs a main routine and this is the standard pythonic version of that
# with the very basics of pyqt implemented within it -- even in my more robust programs unless I am dealing with
# command line arguments or passing in a handle to the Desktop this is about all I have in my main -- 
# its very lean and very mean little function
if __name__ == "__main__":
    MainThred = QApplication([])

    MainGUI = MainWindow()
    MainGUI.show()

    sysExit(MainThred.exec_())
Well I hope that helps you understand what you might have done wrong as well as help you move in the right direction with learning Python as for button sizing if you could be more specific about the issue you were having I might be able to address it -- that is assuming this new code base does not fix it for you. Oh and lastly I reserve Stackoverflow for the really difficult questions as those guys really know their stuff and so I kind of hate to pass anything easy by them as I do not want to waste their time. This forum and this one (https://forum.qt.io/category/58/qt-for-python) are both good places for beginners to get a better understanding of things as they allow for more discussion and such than stackoverflow does.

I just realized something perhaps you were talking about your button stretching as the window stretched and in my above example the button does stretch horizontally while not stretching vertically now let us say you did not want your button to stretch any bigger than its text then here is how you would change the previous code:
        self.HBox = QHBoxLayout()
        self.HBox.addWidget(self.Button1)
        self.HBox.addStretch(1)

        self.Simple1 = QVBoxLayout()
        self.Simple1.addWidget(self.Label1)
        self.Simple1.addWidget(self.Label2)
        self.Simple1.addLayout(self.HBox)
        self.Simple1.addStretch(1)
This puts the button in a horizontal box with a stretch on its right so it will stay to the left and of course the vertical stretch keeps everything above it in place as well.
Reply
#3
(Jul-12-2019, 09:04 PM)Denni Wrote: Okay first I am not quite sure what you were asking about but it is always good to start from a good base and your program is lacking that base so in an effort to help you out here is a more solid base for you to work from. I assume eventually you will graduate to using QWindow but here is a basic version of what you implemented.
from sys import exit as sysExit

# Normally I restrict this to just the pieces I am using but when testing and such
# I do this in order to speed things up a smidge
from PyQt5.QtCore import *
from PyQt5.QtGui  import *
from PyQt5.QtWidgets import *
# Of course every GUI needs a MainWindow that first thing you show off if your not using a splash screen
# this is a basic Widget window fairly easy to deal with and it contains everything until you get to big for it
# note QWindow is a slightly different beast that gives a more robust base window but this one works for now
class MainWindow(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        # These first three lines are a must have in just about every class you create

        # Setting the windows specifics
        title = "Main Window"
        self.setWindowTitle(title)

        # I do the following because it helps remind me of what these
        # 4 values represent and its really simple to understand
        left=100;top=100;width=280;height=100
        self.setGeometry(left,top,width,height)

        # This variable is now global to this Class
        self.num = 1

        # These objects are now available to this Class and will
        # stick around for as long as the class does which you definitely
        # want to occur -- oh the magic of "self."
        self.Label1 = QLabel('Hello World')

        self.Label2 = QLabel('You are welcome to my GUI')

        self.Button1 = QPushButton('Click on me, I am waiting')
        self.Button1.clicked.connect(self.MyButtonClicked)

#  Here is what your QGridLayout would have looked like but
#  a QGridLayout was over kill for what you were doing so I
#  put it in a QVBoxLayout instead
#        self.Grid1 = QGridLayout(window)
#        self.Grid1.addWidget(label1, 1, 1)
#        self.Grid1.addWidget(label2, 2, 1)
#        self.Grid1.addWidget(button, 2, 4)

        # Creating the Simple Layout
        self.Simple1 = QVBoxLayout()
        self.Simple1.addWidget(self.Label1)
        self.Simple1.addWidget(self.Label2)
        self.Simple1.addWidget(self.Button1)
        # Adding this so when you stretch your window
        # the above objects stay in place note stetching
        # a Grid Layout is done differently
        self.Simple1.addStretch(1)

        # Okay now we add our layout to our window that is assuming
        # you want to see it that is
        self.setLayout(self.Simple1)

    def MyButtonClicked(self):
        # Notice what happens with the "self." since this method
        # is within the class it has access to all of these self.objects
        self.num += 1

        self.Button1.setText("Click Me Again")

        # I have changed your Text in two different ways just to show you
        # 2 fairly simple and easy to understand ways of doing it

        Label1Text = ('You have entered my World ' + str(self.num) + ' times')
        self.Label1.setText(Label1Text)

        Label2Text = 'You are welcome to my GUI,\nI am trying to stretch the label here by welcoming you '
        Label2Text += str(self.num)
        Label2Text += ' times\nSo you are welcome here once again'
        self.Label2.setText(Label2Text)

# Every Python program needs a main routine and this is the standard pythonic version of that
# with the very basics of pyqt implemented within it -- even in my more robust programs unless I am dealing with
# command line arguments or passing in a handle to the Desktop this is about all I have in my main -- 
# its very lean and very mean little function
if __name__ == "__main__":
    MainThred = QApplication([])

    MainGUI = MainWindow()
    MainGUI.show()

    sysExit(MainThred.exec_())
Well I hope that helps you understand what you might have done wrong as well as help you move in the right direction with learning Python as for button sizing if you could be more specific about the issue you were having I might be able to address it -- that is assuming this new code base does not fix it for you. Oh and lastly I reserve Stackoverflow for the really difficult questions as those guys really know their stuff and so I kind of hate to pass anything easy by them as I do not want to waste their time. This forum and this one (https://forum.qt.io/category/58/qt-for-python) are both good places for beginners to get a better understanding of things as they allow for more discussion and such than stackoverflow does.

I just realized something perhaps you were talking about your button stretching as the window stretched and in my above example the button does stretch horizontally while not stretching vertically now let us say you did not want your button to stretch any bigger than its text then here is how you would change the previous code:
        self.HBox = QHBoxLayout()
        self.HBox.addWidget(self.Button1)
        self.HBox.addStretch(1)

        self.Simple1 = QVBoxLayout()
        self.Simple1.addWidget(self.Label1)
        self.Simple1.addWidget(self.Label2)
        self.Simple1.addLayout(self.HBox)
        self.Simple1.addStretch(1)
This puts the button in a horizontal box with a stretch on its right so it will stay to the left and of course the vertical stretch keeps everything above it in place as well.

Sorry for not being so clear and thank you for showing me the better way of doing it. Actually I knew how to stop the button from resizing. I was looking for the reason why the button resize. From stackoverflow link I shared I learned why it resizes. That answer there says it resizes because the button sizepolicy property is set in way a way where it facilitates expanding horizontally and not vertically. To prove their point they shared the official documentation link (I shared the link as well). And when I visited that that made me confused (I shared the image of the particular part which is my source of confusion).

From the documentation I learned this: There is a widget and then a layout is added on top of that and then the layout holds some more widgets. In this case the layout of the widgets hold by the layout is controlled by the property of the layout. But if there is no layout attached to the parent widget, then the sizepolicy of the child widgets controls the layout.

I tried to create the similar situation: I have a parent widget "window", and on top of that I have a GridLayout. And inside that layout it holds three widgets - two labels and one button. But then why the sizepolicy of the button is controlling the layout behaviour of the button? Isn't it deviating from the official documentation statement?
Reply
#4
Another example is this:

from PyQt5 import QtWidgets

app = QtWidgets.QApplication([])
window = QtWidgets.QWidget()
window.setGeometry(50, 50, 500, 300)
menu_bar = QtWidgets.QMenuBar(window)
menu_bar.setGeometry(0, 0, 500, 22)
size_policy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
menu_bar.setSizePolicy(size_policy)
menu_bar.addMenu("File")
menu_bar.addMenu("Edit")
lay = QtWidgets.QHBoxLayout()
lay.addWidget(menu_bar)
window.setLayout(lay)
window.show()
app.exec()
If we remove the QVBoxLayout the sizepolicy is not working. Strange!
Reply
#5
Okay I have to apologize that I am having difficulty in understanding what your problem is. In your second example you are using a SizePolicy and it works as it should, while in your first example you put a button within a Grid Layout so the default size policy of that Grid Layout is governing how the button within it it will resize to some degree. Now to determine the default size policy of a layout you can reference the sizePolicy( ) function. For which it states the following:

The size policy holds the default layout behavior of the widget. If there is a QLayout that manages this widget's children, the size policy specified by that layout is used. If there is no such QLayout, the result of this function is used. The default policy is Preferred/Preferred, which means that the widget can be freely resized, but prefers to be the size sizeHint() returns. Button-like widgets set the size policy to specify that they may stretch horizontally, but are fixed vertically. The same applies to line edit controls (such as QLineEdit, QSpinBox or an editable QComboBox) and other horizontally orientated widgets (such as QProgressBar). QToolButton's are normally square, so they allow growth in both directions. Widgets that support different directions (such as QSlider, QScrollBar or QHeader) specify stretching in the respective direction only. Widgets that can provide scroll bars (usually subclasses of QScrollArea) tend to specify that they can use additional space, and that they can make do with less than sizeHint().

So if you read that carefully you can see that the SizePolicy is never without a value and that the value may change based on the particular widget it is being applied to -- without any manual SizePolicy being set -- further even if you set a specific policy I have encountered features that state -- unless you specifically turn this off it will dictate part of the SizePolicy -- granted those are not the exact words but that was the gist of what it was saying.

In short -- yes there are aspects in play almost all the time based off the widgets and layouts you are using. The key -- if you wish to dig that deep -- is to fully understand each of the pieces you are using and what the default rules are to how they interact with one another. Either that or you can just get a solid general idea of how they interact and then deal with any oddities that pop up when you are implementing something new. Make adjustments for the oddity, make note of that oddity and then move on.

The former has the benefit of giving you solid idea of what to expect but is going to take quite a bit of time. The latter has the benefit that you get up and running much faster but it means you do not necessarily know exactly how something is going to respond beyond what you have already experienced with it. However the latter is also more practical in that you are moving forward while you are learning rather than sitting stagnant until you have learned. Neither is wrong, it just depends on the situation you are in.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  [Tkinter] Trying to change font size w/o changing button size python63 3 9,737 Aug-05-2020, 01:04 AM
Last Post: Larz60+
  PyGtk3 why is Locale Folder Size Half of Module Size ? harun2525 1 3,584 Mar-09-2017, 03:46 AM
Last Post: Analyser

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020