Python Graphical User Interface
Mastering widget placement in PyQt5 is the key to building clean, intuitive and highly functional user interfaces. Whether you are crafting a sleek dashboard or a data rich control panel understanding how to position widgets precisely can make or break the user experience. By using absolute positioning, you gain total control over the layout, while layout classes offer flexibility and adaptability for various screen sizes and orientations. The QBox layout simplifies alignment and the horizontal (QHBox) and vertical (QVBox) layouts ensure that widgets flow smoothly. As for the QGrid layout, it offers structured, grid like precision with features like spanning and stretching that give you even more control. Basic grid layouts help you get started quickly, while advanced span and stretch functions allow for dynamic adjustments and more complex designs. Finally, the QForm layout is perfect for organizing input fields and labels in a structured, easy-to-read manner. Dive into these techniques and elevate your PyQt5 applications to professional grade quality.
Table of Content
- Introduction
- Widgets placement using absolute positioning
- Widgets placement using layout classes
- QBox layout
- QH box layout
- QV box layout
- Q grid layout
- Basic Qgrid layout
- Q grid layout span
- Q grid layout stretch
- Q form layout
Introduction
Any application can be created by just dragging and dropping the
widgets in our GUI form from the widget box of Qt Designer. It
may look good when we are using multiple widgets on the same
GUI form and creating an application. We can place the widgets
in any location on the GUI form without giving a notion of their
appearance. However, it may look shabby if we do not place it in
a proper location on the GUI form with any arrangement of
widgets. Such an arrangement leads to the concept of layout
management an approach to place the widgets on our GUI form.
All professional GUI application forms creation uses the concept
of layout managers as it is important for the user to create an
application with a nice look. If we want to organize our widgets,
the preferred approach is to manage the layout with layout
managers. Therefore, whenever there is a requirement to
arrange widgets on a GUI form, we will use the methods present
inside the class of layout manager. The space between parent
and child widgets can be properly utilized in a GUI form and is
useful for communicating between them.
Let us start learning the different ways for layout management in
PyQt5. We will be creating the same application user credentials
app appearance only but now with the usage of the concept of
layouts.
Widgets Placement Using Absolute Positioning
Without using a layout manager, the first approach of creating
layouts in a GUI form that we will learn is, using the absolute
positioning. We can place the widgets in any position in a GUI
form by specifying explicitly the size or position values for each
widget in pixels. It is mainly used in the applications where there
is a requirement to size values and set positions of the widgets
which have to be accommodated within other widgets. Widgets
can be positioned in a GUI form using the absolute positioning
approach, with the help of move(x,y) method.
Here, x and y will be the coordinates of the respective widget
which starts from the upper top left corner (0,0).
From Figure 1.1, we can see that the x position will increase from
left to right and the y position from top to bottom.
Now, we shall create the same GUI form, similar to
run_user_credential_app.py using absolute positioning.
import sys
from PyQt5.QtWidgets import QWidget, QApplication,
QLabel, QLineEdit, QPushButton # RAP1
from PyQt5 import QtGui
class Absolute_Position(QWidget): # RAP2
def __init__(self):
super().__init__()
self.display_widgets() # RAP3
self.setGeometry(0, 0, 398, 229) # RAP4
self.setWindowTitle('User Credentials App') #
RAP5
self.show()
def display_widgets(self):
mylbl1 = QLabel('Enter your username:', self)
# RAP6
myfont = QtGui.QFont() # RAP7
myfont.setFamily("Calibri")
myfont.setPointSize(10)
myfont.setBold(True)
myfont.setWeight(75)
mylbl1.setFont(myfont)
mylbl1.move(14, 50) # RAP8
mylbl2 = QLabel('Enter your password:', self)
# RAP9
myfont = QtGui.QFont() # RAP10
myfont.setFamily("Calibri")
myfont.setPointSize(10)
myfont.setBold(True)
myfont.setWeight(75)
mylbl2.setFont(myfont)
mylbl2.move(14, 120) # RAP11
mylineedit1 = QLineEdit(self) # RAP12
mylineedit1.move(210, 50) # RAP13
mylineedit2 = QLineEdit(self) # RAP14
mylineedit2.move(210, 120) # RAP15
mybtn = QPushButton(self) # RAP16
mybtn.setText('Confirm') # RAP17
mybtn.move(140, 180) # RAP18
if __name__ == '__main__':
myapp = QApplication(sys.argv)
mywindow = Absolute_Position()
sys.exit(myapp.exec_())
Output:
On comparing with the original user credentials app, refer to
Figure 1.2, we can see that there is hardly any difference
between the original code and the one we wrote using absolute
positioning. Now, let us understand the code.
- In RAP1, first import the sys module for accessing the
command line arguments. Then we will be importing from
the class which creates desktop style UIs of PyQt5 package. QtWidgets, for creating an empty GUI and an
application handler, that is, QApplication. In addition to it,
we are also importing other widgets like QLabel, QLineEdit
and QPushButton. - In RAP2, we are creating a class named Absolute_Position
inheriting from the base class QWidget. - In RAP3, we are calling the display_widgets method present
inside the class QWidget. - In RAP4, we are displaying the GUI form of 398 (width) by
229 (height) pixels at position (0,0) on the screen. It can be
displayed anywhere on the screen by changing the xpos
and ypos which is at present set at (0,0). - In RAP5, we are setting the title of our GUI form, that is,
window as User Credentials App. - In RAP6, we are creating a QLabel object mylbl1 for using in
our GUI form and passing the text as Enter your username and
self as parameters, whose default display will be at the top
left position. - In RAP7, we are creating an instance of QFont class and
setting the QLabel object mylbl1 font family as Calibri, font
size as 10 and font-weight as Bold. For boldness control to
be fine, we have set weight as 75. - In RAP8, the QLabel object mylbl1 is placed at position 14
pixels towards the right and 50 pixels below from (0,0). - In RAP12, we are creating a QLineEdit object mylineedit1
whose default display will be at the top left position. - In RAP13, the QLineEdit object mylineedit1 is placed at
position 210 pixels towards the right and 50 pixels below
from (0,0). - In RAP14, we are creating a QLineEdit object mylineedit2
whose default display will be at the top left position. - In RAP15, the QLineEdit object mylineedit2 is placed at
position 210 pixels towards the right and 120 pixels below
from (0,0). - In RAP16, we are creating an instance of QPushButton object
mybtn whose default display will be at the top left position and passing a parameter self indicates that the pushbutton
is part of the GUI form. - In RAP17, the text for the QPushButton object mybtn is set as
confirm and will get displayed on the GUI form. - In RAP18, the QPushButton object mybtn is placed at position 140
pixels towards the right and 180 pixels below from (0,0).
Absolute positioning is quite simple but there are some limitations, such as the follows:
- The widget’s size and position will not be changing even if
the GUI form will be resized.
We can see from Figure 1.3, that even on resizing the GUI
form, the widget’s size and position are still the same.
- If we are creating any application using absolute
positioning, the layout may be different on various
platforms. - In some cases, there may be requirements of redesigning
the form. In such cases, layout modification will be tedious
and time consuming.
The reader should understand the concepts of absolute
positioning, before knowing about widgets placement using
layout classes.
Widget Placement Using Layout Classes
API of PyQt5 has a more elegant way of widgets positioning by
providing layout classes which we will learn in subsequent
sections.
QBox Layout
Suppose in our application, there is a requirement to organize or
arrange widgets either horizontally or vertically. In such cases,
we will use QBoxLayout class. The two basic layout management
classes for arranging the widgets either horizontally or vertically
are QHBoxLayout and QVBoxLayout. First, we will see the code for
arranging the widgets in a row, side-by-side using QHBoxLayout
with the usage of methods viz addStretch, addWidget, addLayout.
QHBox Layout
In this section, we will see the code to arrange the widgets in a
row, side-by-side using QHBoxLayout without addstretch.
import sys
from PyQt5.QtWidgets import QWidget, QApplication,
QLabel, QHBoxLayout, QDesktopWidget # QHBEG1
from PyQt5 import QtGui
class HBox_without_stretch(QWidget):
def __init__(self):
super().__init__()
self.display_widgets()
self.setGeometry(0, 0, 398, 229)
self.setWindowTitle('QHBoxLayout withoutstretch')# QHBEG2
self.movecenter()
self.show()
def display_widgets(self):
mylbl1 = QLabel('Label1:', self)
myfont = QtGui.QFont()
myfont.setFamily("Calibri")
myfont.setPointSize(10)
myfont.setBold(True)
myfont.setWeight(75)
mylbl1.setFont(myfont)
mylbl2 = QLabel('Label2', self)
myfont = QtGui.QFont()
myfont.setFamily("Calibri")
myfont.setPointSize(10)
myfont.setBold(True)
myfont.setWeight(75)
mylbl2.setFont(myfont)
# QHBoxlayout
myhbox = QHBoxLayout()# QHBEG7
myhbox.addWidget(mylbl1)# QHBEG8
myhbox.addWidget(mylbl2)# QHBEG9
self.setLayout(myhbox)# QHBEG10
def movecenter(self):
myfrm_gmtry = self.frameGeometry()# QHBEG3
mycenter =
QDesktopWidget().availableGeometry().center()# QHBEG4
myfrm_gmtry.moveCenter(mycenter)# QHBEG5
self.move(myfrm_gmtry.topLeft())# QHBEG6
if __name__ == '__main__':
myapp = QApplication(sys.argv)
mywindow = HBox_without_stretch()
sys.exit(myapp.exec_())
Output:
The preceding example depicts the example of QHBoxLayout with
usage of addWidget and setLayout methods. Here, we have not used
addStretch() method. In the above example, we created two Label
widgets and arranged them horizontally. Moreover, a method is being called to place the GUI form in the center of the screen.
Let us learn these new concepts.
- In QHBEG1, first import the sys module for accessing the
command line arguments. Then we will be importing from
the class which creates desktop style UIs of PyQt5
package, that is, QtWidgets, for creating an empty GUI and
an application handler, that is, QApplication. In addition to it,
we are also importing other widgets such as QLabel,
QHBoxLayout and QDesktopWidget. - In QHBEG2, we are setting the title of our GUI form, that is,
window, as QHBoxLayout without stretch. - From QHBEG3 to QHBEG6, we are displaying the GUI form in the
center of the screen. - In QHBEG3, we are trying to get the location and size
information of the GUI form using frameGeometry method that
are stored in the myfrm_gmtry object. - In QHBEG4, the monitor’s screen center position is getting
determined. - In QHBEG5, the rectangular position of the GUI form is moved
to the screen center. - In QHBEG6, the current GUI form will move to the rectangle
position (myfrm_gmtry) which is moved to the screen center
resulting in the matching of the current GUI form center
with the screen center, such that the GUI form will appear
in the center. Just try to comment this line only. You will find that the
GUI form will be placed at the top left of monitor screen. - In QHBEG7, an instance of QHBoxLayout myhbox is created.
- In QHBEG8, label object mylbl1 will be added to the box layout
object myhbox. - In QHBEG9, label object mylbl2 will be added to the box layout
object myhbox. - In QHBEG10, we are setting the main layout of the GUI form.
In other words, we are setting the horizontal layout to the
GUI form.
We can see that the label widgets are centered horizontally and
vertically in the GUI frame.
Moreover, in the above example, we have not used the
addStretch() method, at all. So, the whole width of the QHBoxLayout
will be averaged by the widgets. For example, if the QHBoxLayout
width is ‘a’ and the number of widgets are ‘x’, then each widget
width will be ‘a/x’.
One important thing to observe is that on resizing the GUI form
the widgets size will also be re-sized.
Now, we will look at another example of QHBoxLayout, but this time
with addStretch method.
QHBoxLayout.addStretch(size)
Here, size can be an integer number say -1,0 or 1. So, an empty stretchable box can be created using addStretch and
is a nice way for filling up the blank space.
We will only be adding the addStretch() method to QHBoxLayout
instance object, changing the class name to HBox_with_stretch and
changing the GUI form title as shown below from the previous
code example.
self.setWindowTitle('QHBoxLayout with addstretch')
# QHBoxlayout
myhbox = QHBoxLayout()
myhbox.addStretch() # added
myhbox.addWidget(mylbl1)
myhbox.addWidget(mylbl2)
self.setLayout(myhbox)
Output:
Refer to Figure 1.6 where we are indicating a double-sided arrow
for the display of an empty box when compared with Figure 1.7.
Output:
We can conclude the following points from above:
- The width of these widgets, that is, Label widget’s width
will be the original width and will not be the average whole
width of QHBoxLayout. - An empty stretchable box will be added in QHBoxLayout and
stretched as long as it fills up the whole QHBoxLayout.
So, from the preceding figures, we understand that the width of
the Label widgets is original and an empty box fills the remaining
space.
Now, we shall addStretch before and after these two label widgets
and see what output we shall see.
# QHBoxlayout
myhbox = QHBoxLayout()
myhbox.addStretch() # added
myhbox.addWidget(mylbl1)
myhbox.addWidget(mylbl2)
self.setLayout(myhbox)
myhbox.addStretch() # added
Output:
From Figure 1.9, we can conclude that the width of the two label
widgets are original and the remaining space is averaged by two
empty boxes as depicted in the following figure.
Output:
Now, we shall see the difference between addStretch(1) and
addStretch(2).
import sys
from PyQt5.QtWidgets import QWidget, QApplication,
QLabel, QHBoxLayout, QDesktopWidget
from PyQt5 import QtGui
class HBox_stretch1_2(QWidget):
def __init__(self):
super().__init__()
self.display_widgets()
self.setGeometry(0, 0, 398, 229)
self.setWindowTitle('QHBoxLayout stretch1_2')
self.movecenter()
self.show()
def display_widgets(self):
mylbl1 = QLabel('Label1:', self)
myfont = QtGui.QFont()
myfont.setFamily("Calibri")
myfont.setPointSize(10)
myfont.setBold(True)
myfont.setWeight(75)
mylbl1.setFont(myfont)
mylbl2 = QLabel('Label2', self)
myfont = QtGui.QFont()
myfont.setFamily("Calibri")
myfont.setPointSize(10)
myfont.setBold(True)
myfont.setWeight(75)
mylbl2.setFont(myfont)
# QHBoxlayout
myhbox = QHBoxLayout()
myhbox.addStretch(1) # addstretch_1
myhbox.addWidget(mylbl1)
myhbox.addStretch(2) # addstretch_2
myhbox.addWidget(mylbl2)
self.setLayout(myhbox)
def movecenter(self):
myfrm_gmtry = self.frameGeometry()
mycenter =
QDesktopWidget().availableGeometry().center()
myfrm_gmtry.moveCenter(mycenter)
self.move(myfrm_gmtry.topLeft())
if __name__ == '__main__':
myapp = QApplication(sys.argv)
mywindow = HBox_stretch1_2()
sys.exit(myapp.exec_())
Output:
a) Output displaying the difference between addstretch(1) and addstretch(2) of
Section 2_Example5.py.
b) Output displaying with stretch factor ‘x’ between Label1: and Label2 of
Section 2_Example5.py.
- myhbox.addStretch(1): An empty stretchable box will grow
horizontally from the layout’s left side moving rightwards.
In the right image of Figure 1.10, it is denoted as x. - myhbox.addStretch(2): An empty stretchable box will be grown
horizontally twice from the layout’s left side moving
rightwards. In the right image of Figure 1.10, it is denoted
as 2*x.
Now, observe the output for the following piece of code:
# QHBoxlayout
myhbox = QHBoxLayout()
myhbox.addStretch() # addstretch_0
myhbox.addWidget(mylbl1)
myhbox.addStretch(2) # addstretch_2
myhbox.addWidget(mylbl2)
self.setLayout(myhbox)
Output:
We can clearly see from the output that when the first stretch is
0, that is, addStretch() and then addstretch(2), then the second
empty box would not grow horizontally at all.
Now, we shall change the code as shown below:
# QHBoxlayout
myhbox = QHBoxLayout()
myhbox.addStretch(2) # addstretch_2
myhbox.addWidget(mylbl1)
myhbox.addStretch(1) # addstretch_1
myhbox.addWidget(mylbl2)
self.setLayout(myhbox)
Output:
The first empty stretchable box will grow horizontally twice (2*x)
from the layout’s left side moving rightwards and then ‘x’ times.
QVBox Layout
Now, let us learn how to arrange the same two label widgets
vertically using QVBoxLayout.
import sys
from PyQt5.QtWidgets import QWidget, QApplication,
QLabel, QVBoxLayout, QDesktopWidget # QVBEG1
from PyQt5 import QtGui
class VBox_with_stretch(QWidget):
def __init__(self):
super().__init__()
self.display_widgets()
self.setGeometry(0, 0, 398, 229)
self.setWindowTitle('QVBoxLayout with
addstretch') # QVBEG2
self.movecenter()
self.show()
def display_widgets(self):
mylbl1 = QLabel('Label1:', self)
myfont = QtGui.QFont()
myfont.setFamily("Calibri")
myfont.setPointSize(10)
myfont.setBold(True)
myfont.setWeight(75)
mylbl1.setFont(myfont)
mylbl2 = QLabel('Label2', self)
myfont = QtGui.QFont()
myfont.setFamily("Calibri")
myfont.setPointSize(10)
myfont.setBold(True)
myfont.setWeight(75)
mylbl2.setFont(myfont)
# QHBoxlayout
myvbox = QVBoxLayout() # QVBEG3
myvbox.addStretch() # QVBEG4
myvbox.addWidget(mylbl1) # QVBEG5
myvbox.addWidget(mylbl2) # QVBEG6
self.setLayout(myvbox) # QVBEG7
myvbox.addStretch() # QVBEG8
def movecenter(self):
myfrm_gmtry = self.frameGeometry()
mycenter =
QDesktopWidget().availableGeometry().center()
myfrm_gmtry.moveCenter(mycenter)
self.move(myfrm_gmtry.topLeft())
if __name__ == '__main__':
myapp = QApplication(sys.argv)
mywindow = VBox_with_stretch()
sys.exit(myapp.exec_())
Output:
- In QVBEG1, first import the sys module for accessing the
command line arguments. Then we will be importing from
the class which creates desktop-style UIs of PyQt5 package. QtWidgets, for creating an empty GUI and an application
handler, that is, QApplication. In addition to it, we are also
importing other widgets such as QLabel, QVBoxLayout and
QDesktopWidget. - In QVBEG2, we are setting the title of our GUI form, that is,
window as QVBoxLayout with addstretch. - In QVBEG3, an instance of QVBoxLayout myvbox is created.
- In QVBEG4, we are adding the addStretch() method to QVBoxLayout
instance object myvbox. - In QVBEG5, label object mylbl1 will be added to the box layout
object myvbox. - In QVBEG7, we are setting the main layout of the GUI form. In
other words, we are setting the vertical layout to the GUI
form. - In QVBEG8, we are again adding the addStretch() method to
QVBoxLayout instance object myvbox.
From Figure 1.14, we can conclude that the width of the two
label widgets is original and the remaining space is averaged by
two empty boxes highlighted in the figure of QVBoxLayout.
After learning the concepts of QHBoxLayout and QVBoxLayout, let us
make an application similar to run_user_credential_app.py.
import sys
from PyQt5.QtWidgets import QWidget, QApplication,
QLabel, QHBoxLayout, QVBoxLayout, QDesktopWidget,
QLineEdit, QPushButton # QBX1
from PyQt5 import QtGui
class BoxLayout_user_credential_App(QWidget):
def __init__(self):
super().__init__()
self.display_widgets()
self.setGeometry(0, 0, 398, 229)
self.setWindowTitle('BoxLayout User Credential App') # QBX2
self.movecenter()
self.show()
def display_widgets(self):
mylbl1 = QLabel('Enter your username:', self)
myfont = QtGui.QFont()
myfont.setFamily("Calibri")
myfont.setPointSize(10)
myfont.setBold(True)
myfont.setWeight(75)
mylbl1.setFont(myfont)
mylbl2 = QLabel('Enter your password:', self)
myfont = QtGui.QFont()
myfont.setFamily("Calibri")
myfont.setPointSize(10)
myfont.setBold(True)
myfont.setWeight(75)
mylbl2.setFont(myfont)
mylineedit1 = QLineEdit(self)
mylineedit2 = QLineEdit(self)
mybtn = QPushButton(self)
mybtn.setText('Confirm')
myhbox1 = QHBoxLayout() # QBX3
myhbox1.setSpacing(60) # QBX4
myhbox1.addStretch() # QBX5
myhbox1.addWidget(mylbl1) # QBX6
myhbox1.addWidget(mylineedit1) # QBX7
myhbox1.addStretch() # QBX8
# QBX9
myhbox2 = QHBoxLayout()
myhbox2.setSpacing(60)
myhbox2.addStretch()
myhbox2.addWidget(mylbl2)
myhbox2.addWidget(mylineedit2)
myhbox2.addStretch()
# QBX10
myhbox3 = QHBoxLayout()
myhbox3.addStretch()
myhbox3.addWidget(mybtn)
myhbox3.addStretch()
# QHBoxlayout
myvbox = QVBoxLayout() # QBX11
myvbox.addStretch() # QBX12
myvbox.addLayout(myhbox1) # QBX13
myvbox.addStretch() # QBX14
myvbox.addLayout(myhbox2) # QBX15
myvbox.addStretch() # QBX16
myvbox.addLayout(myhbox3) # QBX17
self.setLayout(myvbox) # QBX18
def movecenter(self):
myfrm_gmtry = self.frameGeometry()
mycenter =
QDesktopWidget().availableGeometry().center()
myfrm_gmtry.moveCenter(mycenter)
self.move(myfrm_gmtry.topLeft())
if __name__ == '__main__':
myapp = QApplication(sys.argv)
mywindow = BoxLayout_user_credential_App()
sys.exit(myapp.exec_())
Output:
- In QBX1, first import the sys module for accessing the
command line arguments. Then we will be importing from
the class which creates desktop-style UIs of PyQt5
package, that is, QtWidgets , for creating an empty GUI and
an application handler, that is, QApplication . In addition to it,
we are also importing other widgets like QLabel, QHBoxLayout,QVBoxLayout,QDesktopWidget, QLineEdit and QPushButton. - In QBX2, we are setting the title of our GUI form, that is,
window as BoxLayout User Credential App. - In QBX3, an instance of QHBoxLayout is being created with an
object name myhbox1. - In QBX4, the spacing between the items when arranging
horizontally is 60. It will take 60 as an argument. We are
using the setSpacing method with the QHBoxlayout object. - In QBX5, QBX8, QBX12, QBX14 and QBX16, an empty stretchable box
is created using addStretch for filling up the blank space. - In QBX6, label object mylbl1 will be added to the box layout
object myhbox1. - In QBX7, lineEdit object mylineedit1 will be added to the box
layout object myhbox1. - In QBX9, an instance of QHBoxLayout is created with the object
name myhbox2. The spacing between the items when
arranged horizontally is 60. An empty stretchable box is
created using addStretch for filling up the blank space. Label
object mylbl2 will be added to the box layout object myhbox2.
LineEdit object mylineedit2 will be added to the box layout
object myhbox2. - In QBX10, an instance of QHBoxLayout is created with an object
name myhbox3. An empty stretchable box is created using
addStretch for filling up the blank space. Push button object
mylbl2 will be added to the box layout object myhbox2. - In QBX11, an instance of QVBoxLayout is being created with the
object name myvbox. It will be acting as a container for
arranging the layouts QHBoxLayout vertically from top to
bottom. - In QBX13, we are adding a layout myhbox1 to the QVBoxLayout
object myvbox. - In QBX15, we are adding a layout myhbox2 to the QVBoxLayout
object myvbox. - In QBX17, we are adding a layout myhbox3 to the QVBoxLayout
object myvbox. - In QBX18, we are setting the main layout of the GUI form. In
other words, we are setting the vertical layout to the GUI
form. This is all about layout management using QBoxLayout.
QGrid Layout
Suppose in our application, there is a requirement to arrange
widgets in a grid of rows and columns. In that case, we must
know about QGridLayout. It is one of the most universal layout
classes for arranging the widgets in pairs, where each widget will
have a relative position, and will be arranged in a grid of rows
and columns. Its application ranges from adding buttons while
creating a calculator app or making a virtual keypad and so on.
The pair of coordinates for arranging the widgets and placing them on the cell should be zero based integer numbers.
QGridLayout will leave the cell empty if no widgets are added to a
given cell. We will use overloaded implementations of the
addWidget method if we are adding widgets to a grid layout.
- addWidget(self,QWidget, int row, int column): This method will
add a widget at the defined row and column. - addWidget(self,QWidget, int row, int column, int rowSpan, int
columnSpan, alignment): This method will add a widget at
defined row and column spanning multiple rows or columns
or both. Alignment is an optional argument whose default
value is 0, meaning that the entire cell will be filled by the
widget.
Let us see some of the examples using QGridLayout.
Basic QGrid Layout
import sys
from PyQt5.QtWidgets import QWidget, QApplication,
QLabel, QGridLayout # GLEG1_1
from PyQt5 import QtGui
class GridLayout_Eg1(QWidget):
def __init__(self):
super().__init__()
self.display_widgets()
self.setGeometry(0, 0, 398, 229)
self.setWindowTitle('Basic GridLayout
example') #GLEG1_2
self.show()
def display_widgets(self):
# writing the grid portion
mygrid_layout = QGridLayout()# GLEG1_3
self.setLayout(mygrid_layout)# GLEG1_4
for outer in range(4):
for lower in range(3):
mylbl = QLabel('Label' + str(outer) +
str(lower),self)# GLEG1_5
myfont = QtGui.QFont()
myfont.setFamily("Calibri")
myfont.setPointSize(10)
myfont.setBold(True)
myfont.setWeight(75)
mylbl.setFont(myfont)
mygrid_layout.addWidget(mylbl, outer,
lower)# GLEG1_6
if __name__ == '__main__':
myapp = QApplication(sys.argv)
mywindow = GridLayout_Eg1()
sys.exit(myapp.exec_())
Output:
- In GLEG1_1, first import the sys module for accessing the
command line arguments. Then we will be importing from
the class which creates desktop style UIs of PyQt5
package, that is, QtWidgets, for creating an empty GUI and
an application handler, that is, QApplication. In addition to it,
we are also importing other widgets such as QLabel and
QGridLayout. - In GLEG1_2, we are setting the title of our GUI form, that is,
window, as Basic GridLayout example. - In GLEG1_3, an instance of QGridLayout is created with the
object name as mygrid_layout. - In GLEG1_4, we are setting the main layout of the GUI form.
In other words, we are setting the Grid layout to the GUI
form. - In GLEG1_5, an instance of QLabel object mylbl is created, and
setting QLabel object font family is Calibri, font size is 10,
and font-weight is Bold. For boldness control to be fine, we
have set weight as 75. - In GLEG1_6, we are adding a QLabel widget at the defined row
and column. The first row will be 0 and then the widget will
be added to columns 0, 1, and 2. That is why we have appended the Label text with rows and columns for better
identification to the reader. - GLEG1_5 and GLEG1_6 will be repeated 11 times more than the
initial count. Now, we will see another example of spanning
in GridLayout.
QGrid Layout Span
In QGridLayout span, the default value of rowspan and columnspan
is 1. If a positive value is mentioned, then the cell widget will
extend to that value. If it is -1, then the extension will be done
either to the right or bottom edge.
import sys
from PyQt5.QtWidgets import QWidget, QApplication,
QLabel, QGridLayout, QPushButton # GLEG2_1
from PyQt5 import QtGui
class GridLayout_Eg2(QWidget):
def __init__(self):
super().__init__()
self.display_widgets()
self.setGeometry(0, 0, 398, 229)
self.setWindowTitle('GridLayout with span')#
GLEG2_2
self.show()
def display_widgets(self):
# writing the grid portion
mygrid_layout = QGridLayout()
self.setLayout(mygrid_layout)
mybtn1 = QPushButton("Row Spanning merging 2
rows")
mygrid_layout.addWidget(mybtn1, 0,0, 2,1)#
GLEG2_3
mybtn2 = QPushButton("Column Spanning")
mygrid_layout.addWidget(mybtn2, 2,0, 1,3)#
GLEG2_4
for outer in range(3,5):
for lower in range(3,5):
mylbl = QLabel('Label' + str(outer) +
str(lower),self)# GLEG2_5
myfont = QtGui.QFont()
myfont.setFamily("Calibri")
myfont.setPointSize(10)
myfont.setBold(True)
myfont.setWeight(75)
mylbl.setFont(myfont)
mygrid_layout.addWidget(mylbl, outer,
lower)# GLEG2_6
if __name__ == '__main__':
myapp = QApplication(sys.argv)
mywindow = GridLayout_Eg2()
sys.exit(myapp.exec_())
Output:
- In GLEG2_1, first import the sys module for accessing the
command line arguments. Then, we will be importing from
the class which creates desktop-style UIs of PyQt5
package, that is, QtWidgets, for creating an empty GUI and
an application handler, that is, QApplication . In addition to it,
we are also importing other widgets such as QLabel,
QGridLayout, QPushButton. - In GLEG2_2, we are setting the title of our GUI form, that is,
window as GridLayout with span. - In GLEG2_3, the QPushButton object mybtn1 will be added at a
defined row = 0 and column as 0 with rowSpan value as 2 and
columnSpan value as 1. - In GLEG2_4, the QPushButton object mybtn2 will be added at a
defined row = 2 and column as 0 with rowSpan value as 1 and
columnSpan value as 3. - In GLEG2_5, an instance of QLabel object mylbl is created and
setting QLabel object font family as Calibri, font size as 10
font weight as Bold. For boldness control to be fine, we
have set weight as 75. - In GLEG2_6, we are adding QLabel widget at the defined row
and column. The first row will be 3 and then the widget will
be added to columns 3 and 4. That is why we have appended the Label text with rows and columns for better
identification to the reader.
QGrid Layout Stretch
Before writing the code, we will see methods, setColumnStretch and
setRowStretch. These two methods generally focus on the stretch
factor of column/row. More available space will be taken, based
on the higher value of the stretch factor.
import sys
from PyQt5.QtWidgets import QWidget, QApplication,
QTextEdit, QGridLayout # GLEG3_1
from PyQt5 import QtGui
class GridLayout_Eg3(QWidget):
def __init__(self):
super().__init__()
self.display_widgets()
self.setGeometry(0, 0, 398, 229)
self.setWindowTitle('GridLayout with
stretch')# GLEG3_2
self.show()
def display_widgets(self):
# writing the grid portion
mygrid_layout = QGridLayout()
self.setLayout(mygrid_layout)
for outer in range(1,4):
for lower in range(1,4):
mytextedit = QTextEdit(self)# GLEG3_3
mytextedit.setPlaceholderText(str(outer) +
str(lower))# GLEG3_4
mygrid_layout.addWidget(mytextedit,
outer, lower)# GLEG3_5
mygrid_layout.setColumnStretch(outer,outer+1)# GLEG3_6
if __name__ == '__main__':
myapp = QApplication(sys.argv)
mywindow = GridLayout_Eg3()
sys.exit(myapp.exec_())
Output:
- In GLEG3_1 , first import the sys module for accessing the
command line arguments. Then we will be importing from
the class which creates desktop-style UIs of PyQt5
package, that is, QtWidgets , for creating an empty GUI and
an application handler, that is, QApplication. In addition to it,
we are also importing other widgets such as QTextEdit and
QGridLayout. - In GLEG3_2, we are setting the title of our GUI form, that is,
window, as GridLayout with stretch. - In GLEG3_3, an instance of QTextEdit is created with the object
name as mytextedit. - In GLEG3_4, the texteditor placeholder text will be a
concatenation of outer and lower and will be displayed as
grayed out text. - In GLEG3_5, we are adding QTextEdit widget at a defined row
and column. The first row will be 1 and then the widget will
be added to columns 1,2 and 3. - In GLEG3_6, the setColumnStretch factor will take the first
argument as outer and 2nd as outer+1. So, value will be
(1,2). Therefore, column width order will be more for 3rd
column, then 2nd and finally the 1st one as shown in the
output. The rows stretch factor will be set to 1, 2 and 3.
Now, let us learn how to develop the same User Credential App
using QGridLayout.
import sys
from PyQt5.QtWidgets import QWidget, QApplication,
QLabel, QGridLayout, QPushButton, QLineEdit ,
QHBoxLayout
from PyQt5 import QtGui
class GridLayout_User_Credential_App(QWidget):
def __init__(self):
super().__init__()
self.display_widgets()
self.setGeometry(0, 0, 398, 229)
self.setWindowTitle('GridLayout with User
Credential App')
self.show()
def display_widgets(self):
# writing the grid portion
mygrid_layout = QGridLayout()
self.setLayout(mygrid_layout)
mylbl1 = QLabel('Enter your username:',self)
mylbl1.setMinimumWidth(161)
myfont = QtGui.QFont()
myfont.setFamily("Calibri")
myfont.setPointSize(10)
myfont.setBold(True)
myfont.setWeight(75)
mylbl1.setFont(myfont)
mygrid_layout.addWidget(mylbl1, 1, 0, 1, 2)
mylbl2 = QLabel('Enter your password:',self)
mylbl2.setMinimumWidth(161)
myfont = QtGui.QFont()
myfont.setFamily("Calibri")
myfont.setPointSize(10)
myfont.setBold(True)
myfont.setWeight(75)
mylbl2.setFont(myfont)
mygrid_layout.addWidget(mylbl2, 3, 0, 1, 2)
mylineEdit1 = QLineEdit(self)
mylineEdit1.setMinimumWidth(161)
mygrid_layout.addWidget(mylineEdit1, 1, 2,1,2)
mygrid_layout.setColumnStretch(1,2)
mylineEdit2 = QLineEdit(self)
mylineEdit2.setMinimumWidth(161)
mygrid_layout.addWidget(mylineEdit2, 3, 2,1,2)
myhbox = QHBoxLayout()
myhbox.addStretch()
mybtn1 = QPushButton("Confirm")
myhbox.addWidget(mybtn1)
myhbox.addStretch()
mygrid_layout.addLayout(myhbox,4,1,1,3)
if __name__ == '__main__':
myapp = QApplication(sys.argv)
mywindow = GridLayout_User_Credential_App()
sys.exit(myapp.exec_())
Output:
QForm Layout
Whenever there is a requirement to create an application in 2-
column form where each row consists of a label (first column)
associated with an entry field (second column), then we can use
QFormLayout. An important overloaded method which is most
commonly used in QFormLayout, will be the addRow() method, which
is described as follows.
- addRow(QLabel, QWidget): This method will add a row
containing the label and its widget in the second column. - addRow(QWidget): This method will add a row containing the
widget spanning 2 columns and stretching it to a GUI form. - addRow(QLayout): This method will add a specified layout
spanning both columns, or we can say that it will be used
for nesting the layouts. - addRow(QLabel, QLayout): This method will add a row
containing the label and its child layout in the second
column.
Let us create the same user credential app using QFormLayout this
time.
import sys
from PyQt5.QtWidgets import QWidget, QApplication,
QLabel, QFormLayout, QPushButton,QHBoxLayout,
QLineEdit , QHBoxLayout # QFL_1
from PyQt5 import QtGui
class FormLayout_User_Credential_App(QWidget):
def __init__(self):
super().__init__()
self.display_widgets()
self.setGeometry(0, 0, 398, 229)
self.setWindowTitle('FormLayout with User
Credential App')# QFL_2
self.show()
def display_widgets(self):
# writing the grid portion
myfrm_layout = QFormLayout()
mylbl1 = QLabel('Enter your username:',self)
mylbl1.setMinimumWidth(161)
myfont = QtGui.QFont()
myfont.setFamily("Calibri")
myfont.setPointSize(10)
myfont.setBold(True)
myfont.setWeight(75)
mylbl1.setFont(myfont)
mylbl2 = QLabel('Enter your password:',self)
mylbl2.setMinimumWidth(161)
myfont = QtGui.QFont()
myfont.setFamily("Calibri")
myfont.setPointSize(10)
myfont.setBold(True)
myfont.setWeight(75)
mylbl2.setFont(myfont)
mylineEdit1 = QLineEdit(self)
mylineEdit1.setMinimumWidth(161)
mylineEdit2 = QLineEdit(self)
mylineEdit2.setMinimumWidth(161)
myhbox = QHBoxLayout()
myhbox.addStretch()
mybtn1 = QPushButton("Confirm")
myhbox.addWidget(mybtn1)
myhbox.addStretch()
myfrm_layout.addRow(mylbl1, mylineEdit1)#
QFL_3
myfrm_layout.addRow(mylbl2, mylineEdit2)#
QFL_4
myfrm_layout.addRow(myhbox)# QFL_5
self.setLayout(myfrm_layout)# QFL_6
if __name__ == '__main__':
myapp = QApplication(sys.argv)
mywindow = FormLayout_User_Credential_App()
sys.exit(myapp.exec_())
Output:
- In QFL_1, first import the sys module for accessing the
command line arguments. Then we will be importing from
the class which creates desktop style UIs of PyQt5
package, that is, QtWidgets, for creating an empty GUI and
an application handler, that is, QApplication. In addition to it, we are also importing other widgets such as QLabel,
QFormLayout, QPushButton, QLineEdit. - In QFL_2, we are setting the title of our GUI form, that is,
window, as Form Layout with User Credential App. - In QFL_3, we are adding a row containing label object mylbl1
in the first column and QLineEdit object mylineEdit1 in 2nd
column respectively. - In QFL_4, we are adding a row containing label object mylbl2
in the first column and QLineEdit object mylineEdit2 in 2nd
column respectively. - In QFL_5, we are adding a row containing QHBoxLayout object.
- In QFL_6, we are setting the main layout of GUI form. In
other words, we are setting the QFormLayout to the GUI form.
Now, from QFormLayout code, we can see that the space
between the 2 QLabel or 2 QLineEdit widgets is very less. The
same happens between QLabel or QLineEdit widgets too. So, in such
cases, we need to provide spacing. We will be providing vertical
spacing between the widgets using the SetVerticalSpacing method
and horizontal spacing using the setHorizontalSpacing method.
We will modify the previous code by introducing the concept of
Vertical and Horizontal spacer.
myhbox1 = QHBoxLayout() # addition1
myhbox1.addStretch() # addition2
myfrm_layout.addRow(myhbox1)
myfrm_layout.addRow(mylbl1, mylineEdit1)
myfrm_layout.setHorizontalSpacing(50) #
addition3
myfrm_layout.setVerticalSpacing(50) #
addition4
myfrm_layout.addRow(mylbl2, mylineEdit2)
myfrm_layout.setVerticalSpacing(50) #
addition5
myfrm_layout.addRow(myhbox1)
Output:
credentials app
- In addition1, an instance of QHBoxLayout object myhbox1 is
created. - In addition2, an empty stretchable box is created for the
myhbox1 object using addStretch for filling up the blank space. - In addition3, the space between the widgets lying side by
side (horizontally only) will be 50. setHorizontalSpacing takes
an integer value as an argument. - In addition4 and addition5, the space between the widgets
lying out vertically (only) will be 50. setVerticalSpacing takes
an integer value as an argument.
Now, the most important observation of all these concepts about
widgets placement which we saw till now, is that we can easily
manage using Qt Designer.
Let us show their usage with the help of figures, by playing with
default GUI form. This is inarguably one of the best ways to learn.
Now, we will be selecting in pairs say Btn1 and Btn2 as one and
Btn3 and Btn4 as another. As displayed in Figure 1.24 with arrow
sign which is Layout Horizontally in Qt Designer, we will be first
selecting the first pair and then click the button with arrow sign.
The same methodology will be repeated for second pair too. Just
observe the rectangle portion which are treated as pairs. The
result after performing these activities are displayed in the
figure below.
Now, we will be selecting in pairs say Btn1 and Btn3 as one and
Btn2 and Btn4 as another. As displayed in Figure 1.25 with arrow
sign which is Layout Vertically in Qt Designer, we will be first
selecting the first pair and then click the button with arrow sign.
The same methodology will be repeated for second pair too. Just
observe the rectangle portion which are treated as pairs. The
result after performing these activities are displayed in the
figure below.
Just observe the outputs when QPushButton widget, in pairs, are
being selected and corresponding Toolbar icons are being
pressed highlighted with arrow sign against each feature as
shown in Figure 1.26.
form
In Figure 1.27, the QPushButton widgets are selected in pairs and
stretched horizontally by clicking Lay Out Horizontally in splitter
icon.
form
GUI form
Layout in a Grid button as displayed with arrow sign
Layout in a Grid button as displayed with arrow sign
displayed with arrow sign
From Figure 1.28 to Figure 1.32 we are transforming a default
GUI form by arranging the widgets in a Grid layout using Qt Designer toolbar Lay Out in a Grid. Just observe the steps that we
have described in the preceding figures, for explaining how the
widgets are used in pairs and then laying out these widgets in a
grid of rows and columns. The figure with arrow marks are self-
explanatory.
Let us now go over Figure 1.33 containing the default GUI form.
default GUI form
Lay Out in a Form Layout button as displayed with arrow sign
Lay Out in a Form Layout button as displayed with arrow sign
button as displayed with arrow sign
Designer
From Figure 1.33 to Figure 1.37, we are transforming a default
GUI form by arranging the widgets in a Form layout using Qt
Designer toolbar Lay Out in a Form Layout. Just observe the steps
that we have described in the preceding figure, for explaining
how the widgets are used in pairs and then laying out these
widgets in a 2-column form. The figure with arrow marks are self explanatory.
Now, we will see how to use vertical and horizontal spacer in Qt
Designer. Refer to Figure 1.38 containing the default GUI form
with horizontal spacer usage.
form
first pair of Label1 and Line Edit1 widget after performing Lay Out in a Grid
transformation
second pair of Label1 and Line Edit1 widget after performing Lay Out in a Grid
transformation
pressing Ctrl + R
From Figure 1.38 to Figure 1.41, we can see the usage of a
horizontal spacer from the widget box on a default GUI form. By
default, the widgets pair QLabel and QLineEdit are arranged in a
grid of rows and columns. Then, a horizontal spacer is introduced
between the widgets thus indicating the usage of a horizontal
spacer. Finally, by pressing Ctrl + R, we can view the final output
of the GUI form.
Refer to Figure 1.42 containing the default GUI form with vertical
spacer usage.
Form
first pair of Label1 and Label2 widget after performing Lay Out in a Grid
transformation
arranged in a vertical layout
introducing vertical spacer between them as shown using arrow
Designer by pressing Ctrl + R
From Figure 1.42 to Figure 1.46, the widgets pair of QLabel and
QLineEdit are arranged by introducing a vertical spacer between
them and are stretched to the heights mentioned. Finally, by
pressing Ctrl + R, we can see the final output of the GUI form.
Conclusion
In this section, we will learned how to arrange the widgets using
absolute positioning and layout classes. We saw different
approaches of creating User Credential App application using
absolute positioning, QBoxLayout, QGridLayout and QFormLayout class. This section is important to understand, since in forthcoming
sections, we will be displaying multiple applications using all
these concepts. We will write the code on how to arrange the
widgets by applying drag and drop of widgets from Widget Box
and using toolbar icons to arrange the layouts for creating our
application.