PyQt5: the Python Quasar toolkit 5

Spyder 4 and PyQt5

It is recommended to change the settings in Spyder when running a PyQt Application. Go to Tools and then Preferences:

Change the Backend to Automatic. To do this select IPython console and then to the top select Graphics and change the Backend to Automatic:

Object Orientated Programming Fundamentals

Before looking at the PyQt5 library, let's look at some of the core concepts behind object orientated programming.

Variables

We use the = operator in python to denote assignment. In the following we assign the value 1 to the variable name a.

a is an object and can be accessed in the variable explorer.

Typing in the object name followed by a dot . and tab ↹ gives a list of methods and attributes.

The above can also be explicitly represented by defining the class of a which is int (which is taken by default if an integer number is typed in).

If we instead use the class str we will see a completely different list of attributes and methods available

These attributes and methods are defined within the class and the class can be thought of as a blueprint or set of instructions for a particular object, we will examine this in more detail later.

Functions

The keyword argument def is used to start the definition of a custom function.

It is then followed by the functions name which is in lower case.

Parenthesis are used to enclose keyword input arguments, in this case none are supplied.

Colon and Indentation

A colon is used in python as a block creation indicator.

Any code belonging to the block is indented by four spaces (line 2) while code not belonging to the block is not indented.

This behavior can be seen when using a custom function with conditional logic.

Line 2-10 belong to the code block within the customfunction and the colon at the end of line 1 begins the code block. Line 11 is independent from the customfunction.

The if statement on line 3, else if abbreviated elif on line 5 and else statement on line 7 are linked. Each condition ends with a colon to indicate a new block creation. Code on line 4 belongs to the if statement and will only be executed if the condition specified on line 3 is True. Code on line 6 belongs to the elif statement and will only be executed if the if statement is False and the elif statement is True. Code on lines 8-10 belongs to the else statement which will only be executed if code in the if statement and multiple elif statements (we only have a single one in this case) are False. In line 9 there is a nested if statement within the else block and this ends in a colon, code on line 10 belongs to this and is indented. It will only be executed if conditions are satisfied to launch the else block and the statement within the if condition is True.

Defining and Calling a Function

When this code is ran nothing happens because the function has not been called. We can call it by typing in the function within the console. Note to call the function we need to use parenthesis otherwise we just get information about the function and don't actually call it.

We can define and call the function within the script. In this case the conditions on line 3 and line 5 are False so the else block is executed. The condition on line 9 is also True so code on line 10 within the nested if statement of the else block is also executed.

In the following code the if condition on line 3 is True so the code belonging to this condition line 4 is executed. Although the conditions on line 5 and line 7 are also True they are not examined and code within them is not executed because the if branch has been executed and takes priority over these proceeding branches.

In the following code the if condition on line 3 is False so the code belonging to this condition line 4 is not executed. We then look at the next condition line 5 and see that this is true so execute the code on line 6. Although the condition on line 7 is also True it is not looked because the earlier elif branch (line 6) has been executed and takes priority over this proceeding elif branch (line 7).

Input Arguments: Position and Keyword

The function previously examined had no input arguments. We can add two positional input arguments and two keyword input arguments (line 1). The positional input arguments have to be supplied every time the function is called. On line 7 we can call customfunction and supply only the two positional input, implicitly specifying them. On line 8 we can supply these without typing in their actual name because they are positional they have an order to them. On line 9 we can explicitly supply 2 positional input arguments and only one of the keyword arguments in this case we need to explicitly specify that this keyword input argument is c. On line 10 we see that we only need to explicitly specify the keyword input argument. On line 11 we can explicitly specify all four input arguments and on line 12 we can specify the 2 positional input arguments and then only specify one of the keyword input arguments d.

Local Name Space

Note that functions have their own custom workspace. The four variables created on lines 1,2,3 and 4 are within the global workspace.

These are independent from the variables supplied on line 11 or keyword input arguments specified on line 5.

These variables can be supplied into the custom function using the inputs.

Return Statement

The return statement can be used to return a value into the global namespace:

In this case the function has been called but not assigned to an output variable name.

We can assign a new variable name e2.

When this code is ran custom function is called on line 5 supplying the local function variables a=3 and b=5. The additional local two variables c=1 and d=2 are supplied from line 1. The value e is calculated to be 3+5-1-2=5 within the local function workspace on line 2. This value is returned on line 3 and assigned to the variable name e2 (line 5) on the global namespace.

Custom Classes

A class can be thought of as a blueprint or set of instructions. Later we will look at PyQt5 and one of the first classes we will look at will be QMainWindow which gives instructions on making an instance or object mainWindow.

The keyword argument class is used to start the definition of a custom class.

CamelCaseCapitalization is used for the class name:

Parenthesis are used to enclose the parent class. In this case the parent class is object. object is an inbuilt class otherwise known as a python keyword and python keywords are not capitalized. In objected orientated programming is an object so if nothing is supplied between the parenthesis then an object is assumed.

A colon is once again used in python as a block creation indicator.

Any code belonging to the block is indented by four spaces (line 2) while code not belonging to the block is not indented as seen before.

The Special Initialization Method __init__

Methods can be be thought of as functions belonging to classes. Like a function we use the keyword def to start the definition of a method.

The special method __init__ (double underscores before and after) is ran once during the initialization of an instance of a class.

Parenthesis are used to enclose self followed by any input arguments (in this case there are no input arguments):

A colon is once again used in python as a block creation indicator.

Code is indented appropriately, line 3 belongs to the init method within the CustomClass, line 4 belongs to the CustomClass only and line 5 doesn't belong to the class.

Classes and Instances

Instances usually use camelCaseCapitlization starting with a lower case in the first word but each subsequent word begins with an upper case character. In this case we will call the instance, instanceOne.

Then call the custom class.

The parenthesis should contain all the input arguments supplied within the initialization method, in this case none are supplied.

The word self does not get supplied. The instance name in this case instanceOne is prescribed to self for that instance of the class.

In the following we proscribe 2 positional and one keyword input argument in the __init__ method (line 2) and we create an instance of the CustomClass, instanceOne (line 6) by calling the two positional input arguments, the default value for the keyword input argument c is supplied from line 2. The code within the special __init__ method (line 3) is executed and the value 6 is displayed on the console.

Attributes

Recall that functions have their own namespace, this is also true with classes. The input arguments a, b and c are local variables within the __init__ method and are lost to the main workspace and the class workspace once the value 6 has been computed. Attributes can be thought of as variables/objects that belong to another object, in this case belong to the instance of a class. We can create attributes with lower case names atta, attb and attc on line 3-5 and corresponding these to the input arguments a, b and c. We use self. within our class to proscribe these as attributes which belong to the instance of the class.

Once this code is run we can type in the instance name followed by a dot . and tab ↹ to access a list of methods and attributes.

Attributes act like variable names and like variable names these can be looked up and reassigned to a new value.

New attributes can be assigned to an instance of a class once it is created. In this case we can create an attribute attd of the CustomClass and this attribute can be looked up from the instance of the class CustomClass, instanceOne but returns an error when it is attempted to be looked up on instanceTwo as it was never assigned for instanceTwo.

Now instead of creating a new attribute for each input argument. Let's instead create an attribute that is a list of the three input arguments.

Once we run this code we can now look look up attributes and methods which belong to the instance instanceOne, select the attribute attd and then look up the attributes (none in this case) and methods which belong to this attribute. The attribute attd is a list and displays the list methods.

We can use a dot . and a tab ↹ to look up the list methods.

These are of course the same as if we had created a list directly.

Assigning an object such as a list or another class to an attribute opposed to an independent object is pretty common when we use object orientated programming. Late in the code we may want to call other attributes and methods. We use the dot . notation detailed below.

Methods

Methods can be thought of as functions that belong to a class, we have already seen the special method __init__ which is ran when an instance of a class is created. We can create additional custom methods for example customMethodOne (line 6) which like an instance is named using camel case capitalization with exception to the first word. This method has no input arguments besides the instance name self. In this case two attributes are accessed self.atta and self.attb which were created when the instance was created via the __init__ method. These are then assigned to a new attribute self.attd and this value is returned when customMethodOne is called. We can create a second method customMethodTwo which has self and then a single positional input argument x and a single keyword input argument y. This custom method accesses the first custom method self.customMethodOne() in line and sums the value of the input x to it assigning this new value to an attribute self.atte which is returned.

If we launch the code above, we can type in an instance name in the console followed by a dot . and a ↹. Here we can see three attributes and two methods. Only the code within the __init__ method is run when a function is created which is the reason there are no attributes instanceOne.attd or instanceOne.atte.

If we type the first custom method with open parenthesis we see details about the input arguments, there are none in this case. self is not required in its case as the method is called from the instance name.

In contrast if we call in the custom method from the class name we are prompted to specify an instance self as no instance is specified.

The method can thus be ran using both conventions:

Once this method has been run, the additional attribute instanceOne.attd is created.

Now we can look at the second custom method. Here we see that we need a positional input argument x and there is a keyword input argument y. If we instead called it from the class we would see the need to specify the instance self.

When launched we can see that the attribute instanceOne.atte is created.

The attributes were named self.atta, self.attb and self.attc to highlight the fact that they were attributes. It is more common to name them closer to the input arguments of the init function. However there should be no confusion in line 3 for example the right hand side is the input argument a and this is assigned to the attribute self.a on the left hand side. The attribute self.d is created directly when an instance is created because the __init__ calls customMethodOne. This type of modularity is common place when working with pyQt5.

Inheritance

Class inheritance allows the child class to inherit attributes and methods from a parent class. This is useful as it allows for the creation of a subclass that gets all the functionality from the parent class however it allows flexibility as new functionality can be added or functionality can be altered without altering the parent class. In line 14 the new CustomClassTwo child class is created and inherits all functionality from the parent class CustomClassOne.

We can see that instanceTwo an instance of customClassTwo inherits all the attributes and methods of parent class customClassOne.

If instead we create a new __init__ method. Then this will be used instead of the__init__ defined in the parent class and because this new __init__ has no input arguments, the instance must be created without supplying any:

As we can see we inherit the methods but not the attributes created within the special method __init__ as this was overwritten in the child class.

If we want the functionality of the parent special method __init__ and want to add additional functionality to the child class we can use super().__init__() to include the contents from the parent __init__ method as shown on line 16. Note that line 16 does end in a colon as it is not starting a new code block. Only the two positional arguments are supplied a and b. The instance self and keyword input argument c=3 are supplied in line 2. In line 15 the instance self, the two positional arguments a and b are supplied alongside a keyword argument f. The keyword input argument f is not passed onto the parent class as it is not in the parents __init__ but rather used to create a new attribute on line 17.

All the attributes and methods from the parent class are available in addition to the attribute defined in the child's own __init__ method.

Creating a MainWindow subclass of QMainWindow

To begin we will first need to import the QMainWindow class from the QtWidgets module of the PyQt5 library. Then we can create our own child custom class MainWindow based on the QMainWindow parent class. We want to create our own special __init__ method however we want to inherit the __init__ method from the parent class QMainWindow. In this case there are no positional or keyword input arguments. Our special method will call a custom method initWindow (which we will look at in a moment) and then call the method show() inherited from the parent class QMainWindow to make the window visible (QMainWindow instances are hidden by default).

The custom method initWindow defines five attributes four which are integers and one is a string. These will be used in two methods inherited from QMainWindow: setWindowTitle which has a string as an input argument and setGeometry which requires four numerical input arguments. Details about these input arguments can be seen if the method is called with open parenthesis.

On a screen these input arguments correspond to the following.

An instance of this MainWindow custom class can be created.

If this code is ran the window will be created and will show for less than a millisecond and then be closed. To make a window that we can actually see and interact with we need to create an application loop which is in essence an infinite loop that is broken when the last window of the application is closed. To do this we need to import the QApplication class from the QtWidgets module of the PyQt5 library (we can do this with QMainWindow on a single line) and we also need to import sys.

A new application can be created by starting an QApplication thread and then creating an instance of the MainWindow class. sys.argv is supplied as the input argument for the QApplication class which is a list of commandline arguments passed to python. The application should end automatically when all instances of the MainWindow class created (in this case just a single instance) are closed.

The Window should now show. It has the title and dimensions as specified in the initWindow method. This window is very basic, we can minimise, maximise, and close it using the three standard buttons on the right hand side as well as resize it like most other windows.

In Spyder 4 after the window has been closed the application appOne remains in the consoles namespace and this causes a conflict when attempting to run the code again as the application thread is already present so nothing appears to happen.

To get around this we can select the QApplication instance within the console and use it instead of attempting to create a new one (only creating a new instance if one isn't present in the name space).

This code can be simplified:

from PyQt5.QtWidgets import QMainWindow,QApplication
import sys

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initWindow()
        self.show()
        
    def initWindow(self):
        self.setWindowTitle('PyQt5 Window')
        self.setGeometry(100,100,400,300)
        
if not QApplication.instance():
    appOne=QApplication(sys.argv)
else:
    appOne=QApplication.instance()
windowOne=MainWindow()

QIcon

We will now look at setting the window icon. For this we are going to extract all the icons from the system32 folder using a third party utility however it is possible to download icon files from the internet:

Launch iconsext:

In the location type:

C:\Windows\system32\*.*

The Search for Icons.

Select all the icons by pressing [Ctrl] + [a]:

Select save selected icons.

Select save icons.

Select an icon you like:

Then copy it to the same folder as your script:

In line 2 QIcon will be imported from the QtGui module of PyQt5. In line 12 a new attribute will be created referring to the location of the icon. In this case a relative string can be used to include the location of the file, the file name and the extension. In line 15 an instance of the QIcon class is created using the input argument self.icon which is the attribute that contains the location of the icon as a string and this instance is assigned to self.iconOne. This instance is then supplied as the input argument in line 16 for the method setWindowIcon (which is inherited from QMainWindow).

from PyQt5.QtWidgets import QMainWindow,QApplication
from PyQt5.QtGui import QIcon
import sys

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initWindow()
        self.show()
        
    def initWindow(self):
        self.setWindowTitle('PyQt5 Window')
        self.icon=r'C:\Users\Phili\Documents\PyQt5\setupapi_32.ico'
        self.setGeometry(100,100,400,300)
        self.iconOne=QIcon(self.icon)
        self.setWindowIcon(self.iconOne)

if not QApplication.instance():
    appOne=QApplication(sys.argv)
else:
    appOne=QApplication.instance()
windowOne=MainWindow()

The window now has the icon on the left hand side:

Since the icon is in the same folder as the script file, this can be simplified using the name of the icon and its extension opposed to the full file path and the three lines of code involving the icon can be merged into one as shown on line 14.

from PyQt5.QtWidgets import QMainWindow,QApplication
from PyQt5.QtGui import QIcon
import sys

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initWindow()
        self.show()
        
    def initWindow(self):
        self.setWindowTitle('PyQt5 Window')
        self.setGeometry(100,100,400,300)
        self.setWindowIcon(QIcon('setupapi_32.ico'))

if not QApplication.instance():
    appOne=QApplication(sys.argv)
else:
    appOne=QApplication.instance()
windowOne=MainWindow()

QWidget, QMainWindow and QDialog

So far we have looked at using the QMainWindow class and we created our own custom class MainWindow which is a child class of the QMainWindow class.

from PyQt5.QtWidgets import QMainWindow,QApplication
from PyQt5.QtGui import QIcon
import sys

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initWindow()
        self.show()
        
    def initWindow(self):
        self.setWindowTitle('QMainWindow')
        self.setGeometry(100,100,400,300)
        self.setWindowIcon(QIcon('setupapi_32.ico'))

if not QApplication.instance():
    appOne=QApplication(sys.argv)
else:
    appOne=QApplication.instance()
windowOne=MainWindow()

QMainWindow itself has a parent class QWidget and for our very basic code so far we can use the generic QWidget in place of the parent class. To use QWidget we will also need to import it from the QtWidgets module of the PyQt5 library.

from PyQt5.QtWidgets import QMainWindow,QApplication
from PyQt5.QtWidgets import QWidget
from PyQt5.QtGui import QIcon
import sys

class Widget(QWidget):
    def __init__(self):
        super().__init__()
        self.initWindow()
        self.show()
        
    def initWindow(self):
        self.setWindowTitle('QWidget')
        self.setGeometry(100,100,400,300)
        self.setWindowIcon(QIcon('setupapi_32.ico'))

if not QApplication.instance():
    appOne=QApplication(sys.argv)
else:
    appOne=QApplication.instance()
widgetOne=Widget()

The difference between the two may seem subtle at this stage as both display a TitleBar.

however the QMainWIndow class has additional methods available, in particular support for ToolBars, MenuBars and StatusBars. If we type in the instance followed by a dot . and tab ↹ we can get a list of methods and attributes available for each instance of the respective classes. We can see that windowOne has more methods than widgetOne.

If we look at Microsoft Paint (Windows XP Version before the Ribbon Interface), we can see the following Bars present on the MainWindow of Microsoft Paint.

We can also have a look at the menuBar to reveal a subMenu.

Another type of window is the dialog window. This window shows on top of the mainWindow and the mainWindow cannot be accessed until the dialog is closed.

We can create one of these using PyQt5. To use QDialog we will also need to import it from the QtWidgets module of the PyQt5 library.

from PyQt5.QtWidgets import QMainWindow,QApplication
from PyQt5.QtWidgets import QWidget,QDialog
from PyQt5.QtGui import QIcon
import sys

class Dialog(QDialog):
    def __init__(self):
        super().__init__()
        self.initWindow()
        self.show()
        
    def initWindow(self):
        self.setWindowTitle('QDialog')
        self.setGeometry(100,100,400,300)
        self.setWindowIcon(QIcon('setupapi_32.ico'))

if not QApplication.instance():
    appOne=QApplication(sys.argv)
else:
    appOne=QApplication.instance()
dialogOne=Dialog()

QPushButton

If we return to the QMainWindow.

from PyQt5.QtWidgets import QMainWindow,QApplication
from PyQt5.QtGui import QIcon
import sys

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initWindow()
        self.show()
        
    def initWindow(self):
        self.setWindowTitle('QMainWindow')
        self.setGeometry(100,100,400,300)
        self.setWindowIcon(QIcon('setupapi_32.ico'))

if not QApplication.instance():
    appOne=QApplication(sys.argv)
else:
    appOne=QApplication.instance()
windowOne=MainWindow()

We can see that we have a MainWindow with nothing in it.

This image has an empty alt attribute; its file name is image-78.png

We can add a button to the MainWindow by using the QPushButton class. This again has to be imported from the QtWidgets module of the PyQt5 library (line 2). We can then create a new method addPushButton (line 17). On line 18 we can create an instance of the QPushButton class and here we can see that we can specify an icon for the button, a string for the buttons text and also a parent widget.

We can use the same icon as the window icon, supply 'Click Me' as the string and then supply self as the parent widget (which will be the instance of MainWindow). This button can be assigned to an attribute of the MainWindow self.btn1 on the left hand side.

This method will not be ran by default, recall that only code present in the __init__ or called from the __init)) will run when an instance of the class is created. We can call this method from the initWindow method which is in turn called from the __init__ method.

from PyQt5.QtWidgets import QMainWindow,QApplication
from PyQt5.QtWidgets import QPushButton
from PyQt5.QtGui import QIcon
import sys

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initWindow()
        self.show()
        
    def initWindow(self):
        self.setWindowTitle('QMainWindow')
        self.setGeometry(100,100,400,300)
        self.setWindowIcon(QIcon('setupapi_32.ico'))
        self.addPushButton()

    def addPushButton(self):
        self.btn=QPushButton(QIcon('setupapi_32.ico'),
                              'Click Me',self)
        
if not QApplication.instance():
    appOne=QApplication(sys.argv)
else:
    appOne=QApplication.instance()
windowOne=MainWindow()

The button is now present within the top left hand corner of the MainWindow and can be pressed (although nothing happens when its pressed because we haven't told it to do anything). We can access methods inherited from QPushButton by typing in the object name. In this case the object is actually an attribute self.btn1 and the self dot . indicates that it belongs to the MainWindow class. Once we type in the objects name we can followed it by a dot . and then call the method. In this case we are interested in the method setGeometry and we can see that it has four arguments, the relative x position from the start of the window in pixels, the relative y position from the start of the window in pixels and the width and heigh in pixels.

We can input the values 100,75,100 and 70/.

from PyQt5.QtWidgets import QMainWindow,QApplication
from PyQt5.QtWidgets import QPushButton
from PyQt5.QtGui import QIcon
import sys

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initWindow()
        self.show()
        
    def initWindow(self):
        self.setWindowTitle('QMainWindow')
        self.setGeometry(100,100,400,300)
        self.setWindowIcon(QIcon('setupapi_32.ico'))
        self.addPushButton()

    def addPushButton(self):
        self.btn=QPushButton(QIcon('setupapi_32.ico'),
                              'Click Me',self)
        self.btn.setGeometry(100,75,100,70)
        
if not QApplication.instance():
    appOne=QApplication(sys.argv)
else:
    appOne=QApplication.instance()
windowOne=MainWindow()

This gives the following windowOne.

In PyQt it is also common to specify a point using the class QPoint(ax,ay) and the size of an object using the class QSize(aw,ah). A rectangle is defined using a QRect(QPoint(ax,ay),QSize(aw,ah)) or QRect(ax,ay,aw,ah). Some of the methods will only accept a QPoint, QSize orQRect instance as their input argument.

We can import the three classes QSize, QPoint and QRect from the QtCore module of PyQt5 (line 4) and update line 15 and 17 to explicitly define the input argument as a rectangle made up using a point and a size. We can also call the method setIconSize called from the self.btn (where btn is an attribute of self, the instance of MainWindow) which requires a QSize instance as an input argument.

from PyQt5.QtWidgets import QMainWindow,QApplication
from PyQt5.QtWidgets import QPushButton
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import QSize,QPoint,QRect
import sys

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initWindow()
        self.show()
        
    def initWindow(self):
        self.setWindowTitle('QMainWindow')
        self.setGeometry(QRect(QPoint(100,100)
                               ,QSize(400,300)))
        self.setWindowIcon(QIcon('setupapi_32.ico'))
        self.addPushButton()

    def addPushButton(self):
        self.btn=QPushButton(QIcon('setupapi_32.ico'),
                              'Click Me',self)
        self.btn.setGeometry(QRect(QPoint(100,75),
                                   QSize(130,70)))
        self.btn.setIconSize(QSize(50,100))
        
if not QApplication.instance():
    appOne=QApplication(sys.argv)
else:
    appOne=QApplication.instance()
windowOne=MainWindow()

This gives the following.

We can create a second identical button that is positioned 100 pixels more in y.

from PyQt5.QtWidgets import QMainWindow,QApplication
from PyQt5.QtWidgets import QPushButton
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import QSize,QPoint,QRect
import sys

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initWindow()
        self.show()
        
    def initWindow(self):
        self.setWindowTitle('QMainWindow')
        self.setGeometry(QRect(QPoint(100,100)
                               ,QSize(400,300)))
        self.setWindowIcon(QIcon('setupapi_32.ico'))
        self.addPushButton()

    def addPushButton(self):
        self.btn1=QPushButton(QIcon('setupapi_32.ico'),
                              'Click Me',self)
        self.btn1.setGeometry(QRect(QPoint(100,75),
                                   QSize(130,70)))
        self.btn1.setIconSize(QSize(50,100))
        self.btn2=QPushButton(QIcon('setupapi_32.ico'),
                              'Click Me',self)
        self.btn2.setGeometry(QRect(QPoint(100,175),
                                   QSize(130,70)))
        self.btn2.setIconSize(QSize(50,100))
        
if not QApplication.instance():
    appOne=QApplication(sys.argv)
else:
    appOne=QApplication.instance()
windowOne=MainWindow()

QLabel

Instead of a second button we can add a QLabel. For this we will need to import the QLabel class from the QtWidgets module. We can then create a method addLabel (line 28) and within this create an instance of the QLabel class which has very similar input arguments to the QPushButton with exception to the QIcon instance (although the QPushButton can be called without a QICon instance). This instance is once again assigned to an attribute self.label1. This attribute also has the method setGeometry and we can leave the inputs for this unchanged for what we had when we had two QPushButton instances (line 30). Finally we need to call this method so it is invoked during initialization when an instance of a class is created (line 19).

from PyQt5.QtWidgets import QMainWindow,QApplication
from PyQt5.QtWidgets import QPushButton,QLabel
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import QSize,QPoint,QRect
import sys

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initWindow()
        self.show()
        
    def initWindow(self):
        self.setWindowTitle('QMainWindow')
        self.setGeometry(QRect(QPoint(100,100)
                               ,QSize(400,300)))
        self.setWindowIcon(QIcon('setupapi_32.ico'))
        self.addPushButton()
        self.addLabel()

    def addPushButton(self):
        self.btn1=QPushButton(QIcon('setupapi_32.ico'),
                              'Click Me',self)
        self.btn1.setGeometry(QRect(QPoint(100,75),
                                   QSize(130,70)))
        self.btn1.setIconSize(QSize(50,100))

    def addLabel(self):
        self.label1=QLabel('Hello',self)
        self.label1.setGeometry(QRect(QPoint(100,175),
                                   QSize(130,70)))

        
if not QApplication.instance():
    appOne=QApplication(sys.argv)
else:
    appOne=QApplication.instance()
windowOne=MainWindow()

The font is a bit small. We can change the font size and the font type. To do this we need to import the QFont class from the QtGui module of the PyQt5 library (line 3) We can then use the method setFont and supply the instance of QFont as an input argument (line 32).

We can also import the Qt class from the QtCore module of the PyQt5 library (line 4). This class contains a number of attributes which are useful for alignment. We can then use the method setAlignment and supply as an input argument Qt.AlignCenter for central alignment (line 33). We can also use the method setStyleSheet to change the color and background-color of the text. The input arguments for this is a similar to a python dictionary but unfortunately not a python dictionary (the code would be much more python friendly if it were a python dictionary). Each field is enclosed in quotations, the field name and the field value are separated using a colon and the field is ended using a semicolon for example 'color:#ff0000;' (lines 34-35)

from PyQt5.QtWidgets import QMainWindow,QApplication
from PyQt5.QtWidgets import QPushButton,QLabel
from PyQt5.QtGui import QIcon,QFont
from PyQt5.QtCore import QSize,QPoint,QRect,Qt
import sys

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initWindow()
        self.show()
        
    def initWindow(self):
        self.setWindowTitle('QMainWindow')
        self.setGeometry(QRect(QPoint(100,100)
                               ,QSize(400,300)))
        self.setWindowIcon(QIcon('setupapi_32.ico'))
        self.addPushButton()
        self.addLabel()

    def addPushButton(self):
        self.btn1=QPushButton(QIcon('setupapi_32.ico'),
                              'Click Me',self)
        self.btn1.setGeometry(QRect(QPoint(100,75),
                                   QSize(130,70)))
        self.btn1.setIconSize(QSize(50,100))

    def addLabel(self):
        self.label1=QLabel('Hello',self)
        self.label1.setGeometry(QRect(QPoint(100,175),
                                   QSize(130,70)))
        self.label1.setFont(QFont('Arial',18))
        self.label1.setAlignment(Qt.AlignCenter)
        self.label1.setStyleSheet('color:#ff0000;'
                                  'background-color:#00ffff;')
        
if not QApplication.instance():
    appOne=QApplication(sys.argv)
else:
    appOne=QApplication.instance()
windowOne=MainWindow()

Events, Signals and Slots

Let's now add the following icon to our folder:

We can then update the text to 'Close' and then specify this new icon

    def addPushButton(self):
        self.btn1=QPushButton(QIcon('aclui_126.ico'),
                              'Close',self)
        self.btn1.setGeometry(QRect(QPoint(100,75),
                                   QSize(130,70)))
        self.btn1.setIconSize(QSize(50,100))

When we interact with our windowOne and click the Close button it is highlighted blue and nothing happens. User interaction with a program, for example clicking this button is known as an event.

Recall that the btn1 is stored as an attribute, that is a object looked up with reference to another object. In this case we look up self, the instance of our class MainWindow and hence access it using self.btn1. This object also has an attribute clicked which is used to record a signal each time the event occurs where the user clicks the button.

    def addPushButton(self):
        self.btn1=QPushButton(QIcon('aclui_126.ico'),
                              'Close',self)
        self.btn1.setGeometry(QRect(QPoint(100,75),
                                   QSize(130,70)))
        self.btn1.setIconSize(QSize(50,100))
        self.btn1.clicked

In the above we have set up a signal which corresponds to an event but we haven't told the signal to do anything. We can use the method connect to as the name suggests "connect" an external method to the signal in this case self.btnOnePressed. This external function is known as a slot.

    def addPushButton(self):
        self.btn1=QPushButton(QIcon('aclui_126.ico'),
                              'Close',self)
        self.btn1.setGeometry(QRect(QPoint(100,75),
                                   QSize(130,70)))
        self.btn1.setIconSize(QSize(50,100))
        self.btn1.clicked.connect(self.btnOnePressed)

    def btnOnePressed(self):
        self.close()

We can now look at the full code. In line 22 (the icon and text for btn1 have been updated). In line 27, the signal and slot have been setup for an event (the button being pressed). The method being used for the slot is self.btnOnePressed which is defined on line 28 and acts to close the instance self of our custom class MainWindow.

from PyQt5.QtWidgets import QMainWindow,QApplication
from PyQt5.QtWidgets import QPushButton,QLabel
from PyQt5.QtGui import QIcon,QFont
from PyQt5.QtCore import QSize,QPoint,QRect,Qt
import sys

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initWindow()
        self.show()
        
    def initWindow(self):
        self.setWindowTitle('QMainWindow')
        self.setGeometry(QRect(QPoint(100,100)
                               ,QSize(400,300)))
        self.setWindowIcon(QIcon('setupapi_32.ico'))
        self.addPushButton()
        self.addLabel()

    def addPushButton(self):
        self.btn1=QPushButton(QIcon('aclui_126.ico'),
                              'Close',self)
        self.btn1.setGeometry(QRect(QPoint(100,75),
                                   QSize(130,70)))
        self.btn1.setIconSize(QSize(50,100))
        self.btn1.clicked.connect(self.btnOnePressed)

    def addLabel(self):
        self.label1=QLabel('Hello',self)
        self.label1.setGeometry(QRect(QPoint(100,175),
                                   QSize(130,70)))
        self.label1.setFont(QFont('Arial',18))
        self.label1.setAlignment(Qt.AlignCenter)
        self.label1.setStyleSheet('color:#ff0000;'
                                  'background-color:#00ffff;')

    def btnOnePressed(self):
        self.close()

if not QApplication.instance():
    appOne=QApplication(sys.argv)
else:
    appOne=QApplication.instance()
windowOne=MainWindow()

Pressing the Close button will now close windowOne.

This image has an empty alt attribute; its file name is image-101.png

We can return to our original code:

from PyQt5.QtWidgets import QMainWindow,QApplication
from PyQt5.QtWidgets import QPushButton,QLabel
from PyQt5.QtGui import QIcon,QFont
from PyQt5.QtCore import QSize,QPoint,QRect,Qt
import sys

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initWindow()
        self.show()
        
    def initWindow(self):
        self.setWindowTitle('QMainWindow')
        self.setGeometry(QRect(QPoint(100,100)
                               ,QSize(400,300)))
        self.setWindowIcon(QIcon('setupapi_32.ico'))
        self.addPushButton()
        self.addLabel()

    def addPushButton(self):
        self.btn1=QPushButton(QIcon('setupapi_32.ico'),
                              'Click Me',self)
        self.btn1.setGeometry(QRect(QPoint(100,75),
                                   QSize(130,70)))
        self.btn1.setIconSize(QSize(50,100))

    def addLabel(self):
        self.label1=QLabel('Hello',self)
        self.label1.setGeometry(QRect(QPoint(100,175),
                                   QSize(130,70)))
        self.label1.setFont(QFont('Arial',18))
        self.label1.setAlignment(Qt.AlignCenter)
        self.label1.setStyleSheet('color:#ff0000;'
                                  'background-color:#00ffff;')
        
if not QApplication.instance():
    appOne=QApplication(sys.argv)
else:
    appOne=QApplication.instance()
windowOne=MainWindow()

We can change Line 30 to create a label1 with no text. In line 27 we can create a signal and connect it to a method updateLabel which we define in line 38. This uses the code the label1 method (from the class QLabel) setText which can be used to set the text. We can make it a happy face.

from PyQt5.QtWidgets import QMainWindow,QApplication
from PyQt5.QtWidgets import QPushButton,QLabel
from PyQt5.QtGui import QIcon,QFont
from PyQt5.QtCore import QSize,QPoint,QRect,Qt
import sys

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initWindow()
        self.show()
        
    def initWindow(self):
        self.setWindowTitle('QMainWindow')
        self.setGeometry(QRect(QPoint(100,100)
                               ,QSize(400,300)))
        self.setWindowIcon(QIcon('setupapi_32.ico'))
        self.addPushButton()
        self.addLabel()

    def addPushButton(self):
        self.btn1=QPushButton(QIcon('setupapi_32.ico'),
                              'Click Me',self)
        self.btn1.setGeometry(QRect(QPoint(100,75),
                                   QSize(130,70)))
        self.btn1.setIconSize(QSize(50,100))
        self.btn1.clicked.connect(self.updateLabel)

    def addLabel(self):
        self.label1=QLabel('',self)
        self.label1.setGeometry(QRect(QPoint(100,175),
                                   QSize(130,70)))
        self.label1.setFont(QFont('Arial',18))
        self.label1.setAlignment(Qt.AlignCenter)
        self.label1.setStyleSheet('color:#ff0000;'
                                  'background-color:#00ffff;')
    
    def updateLabel(self):
        self.label1.setText('☺')
        
if not QApplication.instance():
    appOne=QApplication(sys.argv)
else:
    appOne=QApplication.instance()
windowOne=MainWindow()

The Window will display like the following when the btn1 has not been pressed.

When it has been pressed the following will show:

In other python libraries getters and setters are commonly used together. In the case of the QLabel class however the method text is used opposed to getText. We can use this to create a button that shows the smiley face when we press it and when we press it again returns the text to an empty string.

from PyQt5.QtWidgets import QMainWindow,QApplication
from PyQt5.QtWidgets import QPushButton,QLabel
from PyQt5.QtGui import QIcon,QFont
from PyQt5.QtCore import QSize,QPoint,QRect,Qt
import sys

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initWindow()
        self.show()
        
    def initWindow(self):
        self.setWindowTitle('QMainWindow')
        self.setGeometry(QRect(QPoint(100,100)
                               ,QSize(400,300)))
        self.setWindowIcon(QIcon('setupapi_32.ico'))
        self.addPushButton()
        self.addLabel()

    def addPushButton(self):
        self.btn1=QPushButton(QIcon('setupapi_32.ico'),
                              'Click Me',self)
        self.btn1.setGeometry(QRect(QPoint(100,75),
                                   QSize(130,70)))
        self.btn1.setIconSize(QSize(50,100))
        self.btn1.clicked.connect(self.updateLabel)

    def addLabel(self):
        self.label1=QLabel('',self)
        self.label1.setGeometry(QRect(QPoint(100,175),
                                   QSize(130,70)))
        self.label1.setFont(QFont('Arial',18))
        self.label1.setAlignment(Qt.AlignCenter)
        self.label1.setStyleSheet('color:#ff0000;'
                                  'background-color:#00ffff;')
    
    def updateLabel(self):
        if self.label1.text()=='':
            self.label1.setText('☺')
        else:
            self.label1.setText('')
        
if not QApplication.instance():
    appOne=QApplication(sys.argv)
else:
    appOne=QApplication.instance()
windowOne=MainWindow()

The QLabel can also be used to display an image. For this we need to import QPixmap from the QtGui module of the PyQt5 library (line 3) so we can make an instance of this class pixmap.

from PyQt5.QtGui import QIcon,QFont,QPixmap

In this example we will use the Union Flag UK.png which has 1920×960 pixels.

This is bit to large for the size specified for the MainWindow class which is 400×300 pixels.

    def initWindow(self):
        self.setGeometry(QRect(QPoint(100,100)
                               ,QSize(400,300)))

So for convenience we will downsize it to 10 % so specify 192×96 pixels for the size of label1. In line 38 we will change the code in the method updateLabel.

    def addLabel(self):
        self.label1=QLabel('',self)
        self.label1.setGeometry(QRect(QPoint(100,175),
                                   QSize(192,96)))

First we will create an instance of the class QPixmap called pixmap by loading in the the UK.png file found in the same folder as the script file and assigning it to the attribute self.pixmap. The pixmap will be saved as an attribute self.pixmap where once again self denotes the instance of MainWindow.

    def updateLabel(self):
        self.pixmap=QPixmap('UK.png')

Then we will create a scaled pixmap using the method scaled from the class QPixmap where we need to specify the QSize, aspectRatioMode and transformMode. For the aspectRatioMode we can select either Qt.IgnoreAspectRatio, Qt.KeepAspectRatio or Qt.KeepAspectRatioByExpanding and for the transformMode we can select Qt.FastTransformation or Qt.SmoothTransformation.

In this case we will use the following and pixmap2 is also an attribute of the instance of MainWindow.

    def updateLabel(self):
        self.pixmap=QPixmap('UK.png')
        self.pixmap2=self.pixmap.scaled(QSize(192,96),
                                        Qt.KeepAspectRatio,
                                        Qt.FastTransformation)

We can now look to see if the attribute label 1 of the MainWindow has a pixmap and if it doesn't (is equal to None), we can assign the pixmap to self.pixmap2 else we can clear the label.

    def updateLabel(self):
        self.pixmap=QPixmap('UK.png')
        self.pixmap2=self.pixmap.scaled(QSize(192,96),
                                        Qt.KeepAspectRatio,
                                        Qt.FastTransformation)
        if self.label1.pixmap()==None:
            self.label1.setPixmap(self.pixmap2)
        else:
            self.label1.clear()
  

The code in full.

from PyQt5.QtWidgets import QMainWindow,QApplication
from PyQt5.QtWidgets import QPushButton,QLabel
from PyQt5.QtGui import QIcon,QFont,QPixmap
from PyQt5.QtCore import QSize,QPoint,QRect,Qt
import sys

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initWindow()
        self.show()
        
    def initWindow(self):
        self.setWindowTitle('QMainWindow')
        self.setGeometry(QRect(QPoint(100,100)
                               ,QSize(400,300)))
        self.setWindowIcon(QIcon('setupapi_32.ico'))
        self.addPushButton()
        self.addLabel()

    def addPushButton(self):
        self.btn1=QPushButton(QIcon('setupapi_32.ico'),
                              'Click Me',self)
        self.btn1.setGeometry(QRect(QPoint(100,75),
                                   QSize(130,70)))
        self.btn1.setIconSize(QSize(50,100))
        self.btn1.clicked.connect(self.updateLabel)

    def addLabel(self):
        self.label1=QLabel('',self)
        self.label1.setGeometry(QRect(QPoint(100,175),
                                   QSize(192,96)))
        self.label1.setFont(QFont('Arial',18))
        self.label1.setAlignment(Qt.AlignCenter)
        self.label1.setStyleSheet('color:#ff0000;'
                                  'background-color:#00ffff;')
    
    def updateLabel(self):
        self.pixmap=QPixmap('UK.png')
        self.pixmap2=self.pixmap.scaled(QSize(192,96),
                                        Qt.KeepAspectRatio,
                                        Qt.FastTransformation)
        if self.label1.pixmap()==None:
            self.label1.setPixmap(self.pixmap2)
        else:
            self.label1.clear()
        
if not QApplication.instance():
    appOne=QApplication(sys.argv)
else:
    appOne=QApplication.instance()
windowOne=MainWindow()

Layouts

CentralWidget

So far we have used the setGeometry method to explicitly set the locations of each widget on the MainWindow. This becomes slightly cumbersome for multiple widgets and also as you can see does not scale well when the window is maximised.

We can modify our code so we create the MainWindow class where we have the methods addPushButton and addLabel defined but don't use their setGeometry methods and don't call these in the __init__ method. We will get an empty windowOne.

from PyQt5.QtWidgets import QMainWindow,QApplication,QWidget
from PyQt5.QtWidgets import QPushButton,QLabel
from PyQt5.QtGui import QIcon,QFont,QPixmap
from PyQt5.QtCore import QSize,QPoint,QRect,Qt
import sys

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initWindow()
        self.show()
        
    def initWindow(self):
        self.setWindowTitle('QMainWindow')
        self.setGeometry(QRect(QPoint(100,100)
                               ,QSize(400,300)))
        self.setWindowIcon(QIcon('setupapi_32.ico'))

    def addPushButton(self):
        self.btn1=QPushButton(QIcon('setupapi_32.ico'),
                              'Click Me',self)
        self.btn1.setIconSize(QSize(50,100))
        self.btn1.clicked.connect(self.updateLabel)

    def addLabel(self):
        self.label1=QLabel('',self)
        self.label1.setFont(QFont('Arial',18))
        self.label1.setAlignment(Qt.AlignCenter)
        self.label1.setStyleSheet('color:#ff0000;'
                                  'background-color:#00ffff;')
    
if not QApplication.instance():
    appOne=QApplication(sys.argv)
else:
    appOne=QApplication.instance()
windowOne=MainWindow()

We can then call our custom method addPushButton and then use the setCentralWidget method (from QMainWindow) to set our button to the CentralWidget.

    def initWindow(self):
        self.setWindowTitle('QMainWindow')
        self.setGeometry(QRect(QPoint(100,100)
                               ,QSize(400,300)))
        self.setWindowIcon(QIcon('setupapi_32.ico'))
        
        self.addPushButton()
        self.setCentralWidget(self.btn1)

The code in full.

from PyQt5.QtWidgets import QMainWindow,QApplication
from PyQt5.QtWidgets import QPushButton,QLabel
from PyQt5.QtGui import QIcon,QFont,QPixmap
from PyQt5.QtCore import QSize,QPoint,QRect,Qt
import sys

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initWindow()
        self.show()
        
    def initWindow(self):
        self.setWindowTitle('QMainWindow')
        self.setGeometry(QRect(QPoint(100,100)
                               ,QSize(400,300)))
        self.setWindowIcon(QIcon('setupapi_32.ico'))
        
        self.addPushButton()
        self.setCentralWidget(self.btn1)

    def addPushButton(self):
        self.btn1=QPushButton(QIcon('setupapi_32.ico'),
                              'Click Me',self)
        self.btn1.setIconSize(QSize(50,100))
        self.btn1.clicked.connect(self.updateLabel)

    def addLabel(self):
        self.label1=QLabel('',self)
        self.label1.setFont(QFont('Arial',18))
        self.label1.setAlignment(Qt.AlignCenter)
        self.label1.setStyleSheet('color:#ff0000;'
                                  'background-color:#00ffff;')
    
    def updateLabel(self):
        self.pixmap=QPixmap('UK.png')
        w=self.label1.width()
        h=self.label1.height()
        self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                        Qt.IgnoreAspectRatio,
                                        Qt.SmoothTransformation)
        self.label1.setScaledContents(True)
        
        if self.label1.pixmap()==None:
            self.label1.setPixmap(self.pixmap2)
        else:
            self.label1.clear()

        
if not QApplication.instance():
    appOne=QApplication(sys.argv)
else:
    appOne=QApplication.instance()
windowOne=MainWindow()

This centralWidget fits the entire window and resizes with it.

QVBoxLayout

Each QMainWindow instance can only have a single CentralWidget however this CentralWidget is often set to an instance of a blank or dummy QWidget and we can set this dummy QWidget to have a layout which contains multiple Widgets. To use QWidget we will need to import it from the QtWidgets of the PyQt5 library.

from PyQt5.QtWidgets import QMainWindow,QApplication,QWidget

Now we can create the dummyCentralWidget and set it to the CentalWidget.

    def initWindow(self):
        self.setWindowTitle('QMainWindow')
        self.setGeometry(QRect(QPoint(100,100)
                               ,QSize(400,300)))
        self.setWindowIcon(QIcon('setupapi_32.ico'))
        
        self.dummyCentralWidget=QWidget()
        self.setCentralWidget(self.dummyCentralWidget)

From the QtWidgets module of the PyQt5 library we can then import a QVBoxLayout class.

from PyQt5.QtWidgets import QVBoxLayout

We can then create an instance of the QVBoxLayout class and store it as an attribute self.vBoxLayout. Then call our custom method self.addPushButton. Then call the method addWidget from our instance of QVBoxLayout in this case the attribute self.vBoxLayout and specify the attribute self.btn1 (which is an instance of the class QPushButton) as an input. We can repeat a similar procedure for our attribute self.label1. Then we can use the method setLayout from self.dummyCentralWidget o set self.vBoxLayout as its layout before setting this to the CentralWidget.

    def initWindow(self):
        self.setWindowTitle('QMainWindow')
        self.setGeometry(QRect(QPoint(100,100)
                               ,QSize(400,300)))
        self.setWindowIcon(QIcon('setupapi_32.ico'))
        
        self.vBoxLayout=QVBoxLayout()
        self.addPushButton()
        self.vBoxLayout.addWidget(self.btn1)
        self.addLabel()
        self.vBoxLayout.addWidget(self.label1)
        
        self.dummyCentralWidget=QWidget()
        self.dummyCentralWidget.setLayout(self.vBoxLayout)
        self.setCentralWidget(self.dummyCentralWidget)

Pictorially this can be represented below.

We can modify the method updateLabel to get the width and height of the label and then feed this through these as a QSize when creating the scaled pixmap2.

    def updateLabel(self):
        self.pixmap=QPixmap('UK.png')
        w=self.label1.width()
        h=self.label1.height()
        self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                        Qt.IgnoreAspectRatio,
                                        Qt.SmoothTransformation)
        self.label1.setScaledContents(True)
        
        if self.label1.pixmap()==None:
            self.label1.setPixmap(self.pixmap2)
        else:
            self.label1.clear()

The code in full.

from PyQt5.QtWidgets import QMainWindow,QApplication,QWidget
from PyQt5.QtWidgets import QPushButton,QLabel
from PyQt5.QtWidgets import QVBoxLayout
from PyQt5.QtGui import QIcon,QFont,QPixmap
from PyQt5.QtCore import QSize,QPoint,QRect,Qt
import sys

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initWindow()
        self.show()
        
    def initWindow(self):
        self.setWindowTitle('QMainWindow')
        self.setGeometry(QRect(QPoint(100,100)
                               ,QSize(400,300)))
        self.setWindowIcon(QIcon('setupapi_32.ico'))
        
        self.vBoxLayout=QVBoxLayout()
        self.addPushButton()
        self.vBoxLayout.addWidget(self.btn1)
        self.addLabel()
        self.vBoxLayout.addWidget(self.label1)
        
        self.dummyCentralWidget=QWidget()
        self.dummyCentralWidget.setLayout(self.vBoxLayout)
        self.setCentralWidget(self.dummyCentralWidget)

    def addPushButton(self):
        self.btn1=QPushButton(QIcon('setupapi_32.ico'),
                              'Click Me',self)
        self.btn1.setIconSize(QSize(50,100))
        self.btn1.clicked.connect(self.updateLabel)

    def addLabel(self):
        self.label1=QLabel('',self)
        self.label1.setFont(QFont('Arial',18))
        self.label1.setAlignment(Qt.AlignCenter)
        self.label1.setStyleSheet('color:#ff0000;'
                                  'background-color:#00ffff;')
    
    def updateLabel(self):
        self.pixmap=QPixmap('UK.png')
        w=self.label1.width()
        h=self.label1.height()
        self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                        Qt.IgnoreAspectRatio,
                                        Qt.SmoothTransformation)
        self.label1.setScaledContents(True)
        
        if self.label1.pixmap()==None:
            self.label1.setPixmap(self.pixmap2)
        else:
            self.label1.clear()

        
if not QApplication.instance():
    appOne=QApplication(sys.argv)
else:
    appOne=QApplication.instance()
windowOne=MainWindow()

This generates the following. When the button is clicked the Union flag fills in the blue space and when clicked again the Union flag is removed. The Window can be resized when the Union flag is removed. Unfortunately PyQt5 doesn't handle pixmap scaling too well. When a pixmap is shown the window cannot be shrunk smaller than the size of the pixmap. It can however be stretched to a larger size but becomes pixelated.

QHBoxLayout

Now supposing instead of a button which toggles the pixmap2 of label1 on and off we want two separate buttons btn1 and btn2 which show and hide the label respectively. We may want these buttons arranged in an instance of QHBoxLayout, in our case hBoxLayout we would need to add each button to this layout. Then we would need to create a dummy widget dummyHWidget. We can then set the layout of the dummyHWidget to hBoxLayout. Next we would need to create an instance of the QVBoxLayout, in our case vBoxLayout. We could then add the dummyHWidget to it alongside the label1. We would then need to create a dummyCentralWidget then set the layout of this dummyCentralWidget to the vBoxLayout and finally set the CentralWindow layout to the dummyCentralWidget.

To use the HBoxLayout we will also need to import it from the QtWidgets module of the PyQt5 library.

from PyQt5.QtWidgets import QVBoxLayout, QHBoxLayout

We can then update our code tomatch the diagram above.

from PyQt5.QtWidgets import QMainWindow,QApplication,QWidget
from PyQt5.QtWidgets import QPushButton,QLabel
from PyQt5.QtWidgets import QVBoxLayout, QHBoxLayout
from PyQt5.QtGui import QIcon,QFont,QPixmap
from PyQt5.QtCore import QSize,QPoint,QRect,Qt
import sys

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initWindow()
        self.show()
        
    def initWindow(self):
        self.setWindowTitle('QMainWindow')
        self.setGeometry(QRect(QPoint(100,100)
                               ,QSize(400,300)))
        self.setWindowIcon(QIcon('setupapi_32.ico'))
        self.hBoxLayout=QHBoxLayout()
        self.addPushButton1()
        self.hBoxLayout.addWidget(self.btn1)
        self.addPushButton2()
        self.hBoxLayout.addWidget(self.btn2)
        
        self.vBoxLayout=QVBoxLayout()
        self.dummyHWidget=QWidget()
        self.dummyHWidget.setLayout(self.hBoxLayout)
        self.vBoxLayout.addWidget(self.dummyHWidget)
        self.addLabel()
        self.vBoxLayout.addWidget(self.label1)
        
        self.dummyCentralWidget=QWidget()
        self.dummyCentralWidget.setLayout(self.vBoxLayout)
        self.setCentralWidget(self.dummyCentralWidget)

    def addPushButton1(self):
        self.btn1=QPushButton(QIcon('setupapi_32.ico'),
                              'Show',self)
        self.btn1.setIconSize(QSize(50,100))
        self.btn1.clicked.connect(self.updateLabelBtn1)
        
    def addPushButton2(self):
        self.btn2=QPushButton(QIcon('setupapi_32.ico'),
                              'Hide',self)
        self.btn2.setIconSize(QSize(50,100))
        self.btn2.clicked.connect(self.updateLabelBtn2)

    def addLabel(self):
        self.label1=QLabel('',self)
        self.label1.setFont(QFont('Arial',18))
        self.label1.setAlignment(Qt.AlignCenter)
        self.label1.setStyleSheet('color:#ff0000;'
                                  'background-color:#00ffff;')
    
    def updateLabelBtn1(self):
        self.pixmap=QPixmap('UK.png')
        w=self.label1.width()
        h=self.label1.height()
        self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                        Qt.IgnoreAspectRatio,
                                        Qt.SmoothTransformation)
        self.label1.setScaledContents(True)
        self.label1.setPixmap(self.pixmap2)

    def updateLabelBtn2(self):
        self.label1.clear()
        
if not QApplication.instance():
    appOne=QApplication(sys.argv)
else:
    appOne=QApplication.instance()
windowOne=MainWindow()

QGridLayout

The above can also be created using a QGridLayout, for this we will need to import the QGridLayout class from the QtWidgets module from the PyQt5 library.

from PyQt5.QtWidgets import QVBoxLayout, QHBoxLayout,QGridLayout

We can now look at creating a gridLayout and adding the widgets to it using:

gridLayout=QGridLayout()
gridLayout.addWidget(

Here we see that the input arguments are the instance, the QWidget and either two int values or four int values. These correspond to the row and column location on a grid. The optional two indexes provide a means of stretching across an entire row or column when the value is 0 no stretching occurs and when the index is 1 the row or column will be stretched.

    def initWindow(self):
        self.setWindowTitle('QMainWindow')
        self.setGeometry(QRect(QPoint(100,100)
                               ,QSize(400,300)))
        self.setWindowIcon(QIcon('setupapi_32.ico'))
        self.gridLayout=QGridLayout()
        self.addPushButton1()
        self.gridLayout.addWidget(self.btn1,0,0)
        self.addPushButton2()
        self.gridLayout.addWidget(self.btn2,2,3)
        self.addLabel()
        self.gridLayout.addWidget(self.label1,0,1,0,2)

We can view this pictorially as.

The full code.

from PyQt5.QtWidgets import QMainWindow,QApplication,QWidget
from PyQt5.QtWidgets import QPushButton,QLabel
from PyQt5.QtWidgets import QVBoxLayout, QHBoxLayout,QGridLayout
from PyQt5.QtGui import QIcon,QFont,QPixmap
from PyQt5.QtCore import QSize,QPoint,QRect,Qt
import sys

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initWindow()
        self.show()
        
    def initWindow(self):
        self.setWindowTitle('QMainWindow')
        self.setGeometry(QRect(QPoint(100,100)
                               ,QSize(400,300)))
        self.setWindowIcon(QIcon('setupapi_32.ico'))
        self.gridLayout=QGridLayout()
        self.addPushButton1()
        self.gridLayout.addWidget(self.btn1,0,0)
        self.addPushButton2()
        self.gridLayout.addWidget(self.btn2,0,1)
        self.addLabel()
        self.gridLayout.addWidget(self.label1,1,0,1,0)

        
        self.dummyCentralWidget=QWidget()
        self.dummyCentralWidget.setLayout(self.gridLayout)
        self.setCentralWidget(self.dummyCentralWidget)

    def addPushButton1(self):
        self.btn1=QPushButton(QIcon('setupapi_32.ico'),
                              'Show',self)
        self.btn1.setIconSize(QSize(50,100))
        self.btn1.clicked.connect(self.updateLabelBtn1)
        
    def addPushButton2(self):
        self.btn2=QPushButton(QIcon('setupapi_32.ico'),
                              'Hide',self)
        self.btn2.setIconSize(QSize(50,100))
        self.btn2.clicked.connect(self.updateLabelBtn2)

    def addLabel(self):
        self.label1=QLabel('',self)
        self.label1.setFont(QFont('Arial',18))
        self.label1.setAlignment(Qt.AlignCenter)
        self.label1.setStyleSheet('color:#ff0000;'
                                  'background-color:#00ffff;')
    
    def updateLabelBtn1(self):
        self.pixmap=QPixmap('UK.png')
        w=self.label1.width()
        h=self.label1.height()
        self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                        Qt.IgnoreAspectRatio,
                                        Qt.SmoothTransformation)
        self.label1.setScaledContents(True)
        self.label1.setPixmap(self.pixmap2)

    def updateLabelBtn2(self):
        self.label1.clear()
        
if not QApplication.instance():
    appOne=QApplication(sys.argv)
else:
    appOne=QApplication.instance()
windowOne=MainWindow()

This gives a very similar user interface to before.

This image has an empty alt attribute; its file name is image-118.png
This image has an empty alt attribute; its file name is image-119.png
This image has an empty alt attribute; its file name is image-120.png

An integer value can be used for the row or column stretch. In this case a value of 2 carries out a column stretch that spans across the width of 2 columns.

    def initWindow(self):
        self.setWindowTitle('QMainWindow')
        self.setGeometry(QRect(QPoint(100,100)
                               ,QSize(400,300)))
        self.setWindowIcon(QIcon('setupapi_32.ico'))
        self.gridLayout=QGridLayout()
        self.addPushButton1()
        self.gridLayout.addWidget(self.btn1,0,0)
        self.addPushButton2()
        self.gridLayout.addWidget(self.btn2,2,3)
        self.addLabel()
        self.gridLayout.addWidget(self.label1,0,1,0,2)

QAction, QToolBar, QMenuBar and QStatusBar

QToolBar

Let's simply our code so that we only have a single QLabel within a CentralWidget.

from PyQt5.QtWidgets import QMainWindow,QApplication,QWidget
from PyQt5.QtWidgets import QPushButton,QLabel
from PyQt5.QtWidgets import QVBoxLayout, QHBoxLayout,QGridLayout
from PyQt5.QtGui import QIcon,QFont,QPixmap
from PyQt5.QtCore import QSize,QPoint,QRect,Qt
import sys

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initWindow()
        self.show()
        
    def initWindow(self):
        self.setWindowTitle('QMainWindow')
        self.setGeometry(QRect(QPoint(100,100)
                               ,QSize(400,300)))
        self.setWindowIcon(QIcon('setupapi_32.ico'))
        self.addLabel()
        self.setCentralWidget(self.label1)

    def addLabel(self):
        self.label1=QLabel('',self)
        self.label1.setFont(QFont('Arial',18))
        self.label1.setAlignment(Qt.AlignCenter)
        self.label1.setStyleSheet('color:#ff0000;'
                                  'background-color:#00ffff;')
    
if not QApplication.instance():
    appOne=QApplication(sys.argv)
else:
    appOne=QApplication.instance()
windowOne=MainWindow()

We are now going to create a toolbar. To do this we can import the QToolBar and the QAction classes from the QtWidgets module of the PyQt library. These classes allow us to create a toolbar with action buttons.

from PyQt5.QtWidgets import QToolBar,QAction

We can now create an empty toolBar using.

    def initWindow(self):
        self.setWindowTitle('QMainWindow')
        self.setGeometry(QRect(QPoint(100,100)
                               ,QSize(400,300)))
        self.setWindowIcon(QIcon('setupapi_32.ico'))
        self.addLabel()
        self.setCentralWidget(self.label1)
        
        self.toolBar=QToolBar("My main toolbar")
        self.addToolBar(self.toolBar)

QAction

Now we can create an instance of the QAction button class. As an input argument we will input a string of text in this case 'Show' and then we can assign this to an attribute self.showAction. In order to view showAction within the toolBar we need to call the method addAction from self.toolBar and specify the action button self.showAction as an input argument.

    def initWindow(self):
        self.setWindowTitle('QMainWindow')
        self.setGeometry(QRect(QPoint(100,100)
                               ,QSize(400,300)))
        self.setWindowIcon(QIcon('setupapi_32.ico'))
        self.addLabel()
        self.setCentralWidget(self.label1)
        
        self.toolBar=QToolBar("My main toolbar")
        self.addToolBar(self.toolBar)
        self.showAction=QAction('Show')
        self.toolBar.addAction(self.showAction)

Because this is a button we can add an icon using the optional QIcon as the first input argument (similar to how QPushButton works). This replaces the text with an icon.

self.showAction=QAction(QIcon('ActionCenterCPL_2.ico'),'Show')

It is however useful to set a status tip so the user can easily find out what an icon does.

self.showAction.setStatusTip('Show')

This icon will do nothing when pressed. We can access the attribute triggered and use it to set up a signal. This signal will be triggered for every event when our instance showAction of the class QAction is triggered.

self.showAction.triggered

The signal will now do nothing however we can use the method connect to connect it to our custom method slotShow

self.showAction.triggered.connect(self.slotShow)

We can define this method and reuse the code which we had for the updateLabelBtn1.

    def slotShow(self):
        self.pixmap=QPixmap('UK.png')
        w=self.label1.width()
        h=self.label1.height()
        self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                        Qt.IgnoreAspectRatio,
                                        Qt.SmoothTransformation)
        self.label1.setScaledContents(True)
        self.label1.setPixmap(self.pixmap2)

We can now add another button hide and because the code involving the toolbar is getting quite long, we can compartmental it by placing all the code involving the ToolBar within the custom method addToolBar and then calling this within the initWindow method. The full code is here.

from PyQt5.QtWidgets import QMainWindow,QApplication,QWidget
from PyQt5.QtWidgets import QPushButton,QLabel
from PyQt5.QtWidgets import QToolBar,QAction
from PyQt5.QtWidgets import QVBoxLayout, QHBoxLayout,QGridLayout
from PyQt5.QtGui import QIcon,QFont,QPixmap
from PyQt5.QtCore import QSize,QPoint,QRect,Qt
import sys

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initWindow()
        self.show()
        
    def initWindow(self):
        self.setWindowTitle('QMainWindow')
        self.setGeometry(QRect(QPoint(100,100)
                               ,QSize(400,300)))
        self.setWindowIcon(QIcon('setupapi_32.ico'))
        self.addLabel()
        self.setCentralWidget(self.label1)
        self.addFullToolBar()
        
    def addFullToolBar(self):
        self.toolBar=QToolBar("My main toolbar")
        self.addToolBar(self.toolBar)
        self.showAction=QAction(QIcon('ActionCenterCPL_2.ico'),
                                'Show')
        self.showAction.setStatusTip('Show')
        self.showAction.triggered.connect(self.slotShow)
        self.toolBar.addAction(self.showAction)
        self.hideAction=QAction(QIcon('ActionCenterCPL_4.ico'),
                                'Hide')
        self.hideAction.setStatusTip('Hide')
        self.hideAction.triggered.connect(self.slotHide)
        self.toolBar.addAction(self.hideAction)

    def addLabel(self):
        self.label1=QLabel('',self)
        self.label1.setFont(QFont('Arial',18))
        self.label1.setAlignment(Qt.AlignCenter)
        self.label1.setStyleSheet('color:#ff0000;'
                                  'background-color:#00ffff;')
    
    def slotShow(self):
        self.pixmap=QPixmap('UK.png')
        w=self.label1.width()
        h=self.label1.height()
        self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                        Qt.IgnoreAspectRatio,
                                        Qt.SmoothTransformation)
        self.label1.setScaledContents(True)
        self.label1.setPixmap(self.pixmap2)

    def slotHide(self):
        self.label1.clear()
        
if not QApplication.instance():
    appOne=QApplication(sys.argv)
else:
    appOne=QApplication.instance()
windowOne=MainWindow()

QMenuBar

The QMainWindow class by default has a menuBar so there is no need to import the QMenuBar class, create an instance and use a method from our MainWindow class to add the menuBar like we did for the toolBar. Instead we can access it directly using the method menuBar.

self.mainMenuBar=self.menuBar()

We can then create a subMenu on this mainMenu for instance a viewMenu. Note the string for the submenu begins with & as this helps with menu functionality in Windows 10 (particularly when setting up keyboard shortcuts).

self.viewMenu=self.mainMenuBar.addMenu('&View')

From either the mainMenu or the viewMenu submenu we can use the method addAction and add Actions like in the case of the toolBar.

self.viewMenu.addAction(self.showAction1)
    def addFullMenuBar(self):
        self.mainMenuBar=self.menuBar()
        self.viewMenu=self.mainMenuBar.addMenu('&View')
        self.showAction1=QAction(QIcon('ActionCenterCPL_2.ico'),
                                'Show')
        self.showAction1.setStatusTip('Show')
        self.showAction1.triggered.connect(self.slotShow)
        self.viewMenu.addAction(self.showAction1)
        self.hideAction1=QAction(QIcon('ActionCenterCPL_4.ico'),
                                'Hide')
        self.hideAction1.setStatusTip('Hide')
        self.hideAction1.triggered.connect(self.slotHide)
        self.viewMenu.addAction(self.hideAction1)

Let's now add a separator using the method addSeparator.

self.viewMenu.addSeparator()

We can then add another submenu viewMenu2 and add another 2 Actions to it.

    def addFullMenuBar(self):
        self.mainMenuBar=self.menuBar()
        self.viewMenu=self.mainMenuBar.addMenu('&View')
        self.showAction1=QAction(QIcon('ActionCenterCPL_2.ico'),
                                'Show')
        self.showAction1.setStatusTip('Show')
        self.showAction1.triggered.connect(self.slotShow)
        self.viewMenu.addAction(self.showAction1)
        self.hideAction1=QAction(QIcon('ActionCenterCPL_4.ico'),
                                'Hide')
        self.hideAction1.setStatusTip('Hide')
        self.hideAction1.triggered.connect(self.slotHide)
        self.viewMenu.addAction(self.hideAction1)
        self.viewMenu.addSeparator()
        self.viewMenu2=self.viewMenu.addMenu('&View')
        self.showAction2=QAction(QIcon('ActionCenterCPL_2.ico'),
                                'Show')
        self.showAction2.setStatusTip('Show')
        self.showAction2.triggered.connect(self.slotShow)
        self.viewMenu2.addAction(self.showAction2)
        self.hideAction2=QAction(QIcon('ActionCenterCPL_4.ico'),
                                'Hide')
        self.hideAction2.setStatusTip('Hide')
        self.hideAction2.triggered.connect(self.slotHide)
        self.viewMenu2.addAction(self.hideAction2)

This gives the user interface below.

Note by default when the mouse is clicked into the window, pressing v will access the viewMenu submenu and pressing v again will access the viewMenu2 submenu. These shortcut keys are setup by default.

We can also set keyboard shortcuts using the setShortcut method of an action. In this case we can use Alt+s as a keyboard for showAction1 and Ctrl+h for hideAction1.

    def addFullMenuBar(self):
        self.mainMenuBar=self.menuBar()
        self.viewMenu=self.mainMenuBar.addMenu('&View')
        self.showAction1=QAction(QIcon('ActionCenterCPL_2.ico'),
                                'Show')
        self.showAction1.setStatusTip('Show')
        self.showAction1.setShortcut('Alt+s')
        self.showAction1.triggered.connect(self.slotShow)
        self.viewMenu.addAction(self.showAction1)
        self.hideAction1=QAction(QIcon('ActionCenterCPL_4.ico'),
                                'Hide')
        self.hideAction1.setStatusTip('Hide')
        self.hideAction1.setShortcut('Ctrl+h')
        self.hideAction1.triggered.connect(self.slotHide)
        self.viewMenu.addAction(self.hideAction1)
        self.viewMenu.addSeparator()
        self.viewMenu2=self.viewMenu.addMenu('&View')
        self.showAction2=QAction(QIcon('ActionCenterCPL_2.ico'),
                                'Show')
        self.showAction2.setStatusTip('Show')
        self.showAction2.triggered.connect(self.slotShow)
        self.viewMenu2.addAction(self.showAction2)
        self.hideAction2=QAction(QIcon('ActionCenterCPL_4.ico'),
                                'Hide')
        self.hideAction2.setStatusTip('Hide')
        self.hideAction2.triggered.connect(self.slotHide)
        self.viewMenu2.addAction(self.hideAction2)

QStatusBar

The status tips for Actions within the mainMenuBar aren't shown by default unlike the Actions on the toolBar. To view these we will need to add a statusBar. To do this we will need to import the QStatusBar class from the QTWidgets module of the PyQt5 library.

from PyQt5.QtWidgets import QToolBar,QAction,QStatusBar

We can then create an instance of the class QStatusBar and assign it to an attribute of self, the instance of MainWindow. Then from self, the instance of MainWindow use the method setStatusBar and select self.statusBar as the input argument.

        self.statusBar=QStatusBar()
        self.setStatusBar(self.statusBar)

The code in full.

from PyQt5.QtWidgets import QMainWindow,QApplication,QWidget
from PyQt5.QtWidgets import QPushButton,QLabel
from PyQt5.QtWidgets import QToolBar,QAction,QStatusBar
from PyQt5.QtWidgets import QVBoxLayout, QHBoxLayout,QGridLayout
from PyQt5.QtGui import QIcon,QFont,QPixmap
from PyQt5.QtCore import QSize,QPoint,QRect,Qt
import sys

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initWindow()
        self.show()
        
    def initWindow(self):
        self.setWindowTitle('QMainWindow')
        self.setGeometry(QRect(QPoint(100,100)
                               ,QSize(400,300)))
        self.setWindowIcon(QIcon('setupapi_32.ico'))
        self.addLabel()
        self.setCentralWidget(self.label1)
        self.addFullToolBar()
        self.addFullMenuBar()
        self.statusBar=QStatusBar()
        self.setStatusBar(self.statusBar)
        
    def addFullToolBar(self):
        self.toolBar=QToolBar("My main toolbar")
        self.addToolBar(self.toolBar)
        self.showAction=QAction(QIcon('ActionCenterCPL_2.ico'),
                                'Show')
        self.showAction.setStatusTip('Show')
        self.showAction.triggered.connect(self.slotShow)
        self.toolBar.addAction(self.showAction)
        self.hideAction=QAction(QIcon('ActionCenterCPL_4.ico'),
                                'Hide')
        self.hideAction.setStatusTip('Hide')
        self.hideAction.triggered.connect(self.slotHide)
        self.toolBar.addAction(self.hideAction)

    def addFullMenuBar(self):
        self.mainMenuBar=self.menuBar()
        self.viewMenu=self.mainMenuBar.addMenu('&View')
        self.showAction1=QAction(QIcon('ActionCenterCPL_2.ico'),
                                'Show')
        self.showAction1.setStatusTip('Show')
        self.showAction1.setShortcut('Alt+s')
        self.showAction1.triggered.connect(self.slotShow)
        self.viewMenu.addAction(self.showAction1)
        self.hideAction1=QAction(QIcon('ActionCenterCPL_4.ico'),
                                'Hide')
        self.hideAction1.setStatusTip('Hide')
        self.hideAction1.setShortcut('Ctrl+h')
        self.hideAction1.triggered.connect(self.slotHide)
        self.viewMenu.addAction(self.hideAction1)
        self.viewMenu.addSeparator()
        self.viewMenu2=self.viewMenu.addMenu('&View')
        self.showAction2=QAction(QIcon('ActionCenterCPL_2.ico'),
                                'Show')
        self.showAction2.setStatusTip('Show')
        self.showAction2.triggered.connect(self.slotShow)
        self.viewMenu2.addAction(self.showAction2)
        self.hideAction2=QAction(QIcon('ActionCenterCPL_4.ico'),
                                'Hide')
        self.hideAction2.setStatusTip('Hide')
        self.hideAction2.triggered.connect(self.slotHide)
        self.viewMenu2.addAction(self.hideAction2)
        
    def addLabel(self):
        self.label1=QLabel('',self)
        self.label1.setFont(QFont('Arial',18))
        self.label1.setAlignment(Qt.AlignCenter)
        self.label1.setStyleSheet('color:#ff0000;'
                                  'background-color:#00ffff;')
    
    def slotShow(self):
        self.pixmap=QPixmap('UK.png')
        w=self.label1.width()
        h=self.label1.height()
        self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                        Qt.IgnoreAspectRatio,
                                        Qt.SmoothTransformation)
        self.label1.setScaledContents(True)
        self.label1.setPixmap(self.pixmap2)

    def slotHide(self):
        self.label1.clear()
        
if not QApplication.instance():
    appOne=QApplication(sys.argv)
else:
    appOne=QApplication.instance()
windowOne=MainWindow()

As you can see the status bar displays the tool tip for the highlighted button.

QButtonGroup

Let's return to the code which made the following.

We essentially have 2 QPushButtons and a QLabel in a QGridLayout.

from PyQt5.QtWidgets import QMainWindow,QApplication,QWidget
from PyQt5.QtWidgets import QPushButton,QLabel
from PyQt5.QtWidgets import QVBoxLayout,QHBoxLayout,QGridLayout
from PyQt5.QtGui import QIcon,QFont,QPixmap
from PyQt5.QtCore import QSize,QPoint,QRect,Qt
import sys
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initWindow()
        self.show()
        
    def initWindow(self):
        self.setWindowTitle('QMainWindow')
        self.setGeometry(QRect(QPoint(100,100)
                               ,QSize(400,300)))
        self.setWindowIcon(QIcon('setupapi_32.ico'))
        
        self.gridLayout=QGridLayout()
        self.addPushButton()
        self.gridLayout.addWidget(self.btn1,0,0)
        self.gridLayout.addWidget(self.btn2,0,1)
        
        self.addLabel()
        self.gridLayout.addWidget(self.label1,1,0,1,0)
        
        self.dummyCentralWidget=QWidget()
        self.dummyCentralWidget.setLayout(self.gridLayout)
        self.setCentralWidget(self.dummyCentralWidget)
        
    def addPushButton(self):
        self.btn1=QPushButton(QIcon('ActionCenterCPL_2.ico'),
                              'Show',self)
        self.btn1.setIconSize(QSize(50,100))
        self.btn1.clicked.connect(self.slotShow)
        
        self.btn2=QPushButton(QIcon('ActionCenterCPL_4.ico'),
                              'Hide',self)
        self.btn2.setIconSize(QSize(50,100))
        self.btn2.clicked.connect(self.slotHide)
        
    def addLabel(self):
        self.label1=QLabel('',self)
        self.label1.setFont(QFont('Arial',18))
        self.label1.setAlignment(Qt.AlignCenter)
        self.label1.setStyleSheet('color:#ff0000;'
                                  'background-color:#00ffff;')
    
    def slotShow(self):
        self.pixmap=QPixmap('UK.png')
        w=self.label1.width()
        h=self.label1.height()
        self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                        Qt.IgnoreAspectRatio,
                                        Qt.SmoothTransformation)
        self.label1.setScaledContents(True)
        self.label1.setPixmap(self.pixmap2)
        
    def slotHide(self):
        self.label1.clear()
        
if not QApplication.instance():
    appOne=QApplication(sys.argv)
else:
    appOne=QApplication.instance()
windowOne=MainWindow()

These buttons are not linked. We can link them together using a QButtonGroup. To do this we need to import QButtonGroup from the QtWidgets module of the PyQt5 library.

from PyQt5.QtWidgets import QButtonGroup

It is important to note that the button group does not have the behavior of a single QWidget so the method addWidget won't accept an instance of QButtonGroup as an input argument nor does it act as a layout so the method setLayout cannot accept an instance of QButtonGroup as an input argument.

We can modify the method addPushButton and rename it as addButtonGroup. Now we can create an instance of the class QButtonGroup and assign it to self.btnGroup. self.btn1 and self.btn2 can be created in an identical manner as before. We can access the method addButton from self.btnGroup which takes the button, QAbstractButton and an integer index id as input arguments. By default the value of id is -1 meaning a new button will be added to the buttonGroup.

    def addButtonGroup(self):
        self.btnGroup=QButtonGroup()

        self.btn1=QPushButton(QIcon('setupapi_32.ico'),
                              'Show',self)
        self.btn1.setIconSize(QSize(50,100))
        self.btnGroup.addButton(self.btn1,1)
        self.btn1.clicked.connect(self.slotShow)
        
        self.btn2=QPushButton(QIcon('setupapi_32.ico'),
                              'Hide',self)
        self.btn2.setIconSize(QSize(50,100))
        self.btnGroup.addButton(self.btn2,2)
        self.btn2.clicked.connect(self.slotHide)

For each QPushButton we have individually set up a signal in response to the button being clicked and then connected it to a slot.

self.btn1.clicked.connect(self.slotShow)
self.btn2.clicked.connect(self.slotHide)

Instead we can access the attribute buttonClicked from the QButtonGroup. As this is a QButtonGroup we can think of the buttonClicked attributes as being a list. To select our button we will need to index an integer. Recall that we index in Python using square brackets.

btnGroup.buttonClicked[int].connect(self.method)

This means the method begin connected will need to contain not only self but also a positional input argument which will correspond to the integer index id. For example:

    def addButtonGroup(self):
        self.btnGroup=QButtonGroup()
        self.btnGroup.buttonClicked[int].connect(self.buttonPressed)

        self.btn1=QPushButton(QIcon('setupapi_32.ico'),
                              'Show',self)
        self.btn1.setIconSize(QSize(50,100))
        self.btnGroup.addButton(self.btn1,1)
        
        self.btn2=QPushButton(QIcon('setupapi_32.ico'),
                              'Hide',self)
        self.btn2.setIconSize(QSize(50,100))
        self.btnGroup.addButton(self.btn2,2)

    def buttonPressed(self,id):
        if id==1:
            self.slotShow()
        if id==2:
            self.slotHide()

We can now simplify this and view the full code.

from PyQt5.QtWidgets import QMainWindow,QApplication,QWidget
from PyQt5.QtWidgets import QPushButton,QLabel
from PyQt5.QtWidgets import QVBoxLayout,QHBoxLayout,QGridLayout
from PyQt5.QtWidgets import QButtonGroup
from PyQt5.QtGui import QIcon,QFont,QPixmap
from PyQt5.QtCore import QSize,QPoint,QRect,Qt
import sys
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initWindow()
        self.show()
        
    def initWindow(self):
        self.setWindowTitle('QMainWindow')
        self.setGeometry(QRect(QPoint(100,100)
                               ,QSize(400,300)))
        self.setWindowIcon(QIcon('setupapi_32.ico'))
        
        self.gridLayout=QGridLayout()
        self.addButtonGroup()
        self.gridLayout.addWidget(self.btn1,0,0)
        self.gridLayout.addWidget(self.btn2,0,1)
        
        self.addLabel()
        self.gridLayout.addWidget(self.label1,1,0,1,0)
        
        self.dummyCentralWidget=QWidget()
        self.dummyCentralWidget.setLayout(self.gridLayout)
        self.setCentralWidget(self.dummyCentralWidget)
        
    def addButtonGroup(self):
        self.btnGroup=QButtonGroup()
        self.btnGroup.buttonClicked[int].connect(self.buttonPressed)

        self.btn1=QPushButton(QIcon('setupapi_32.ico'),
                              'Show',self)
        self.btn1.setIconSize(QSize(50,100))
        self.btnGroup.addButton(self.btn1,1)
        
        self.btn2=QPushButton(QIcon('setupapi_32.ico'),
                              'Hide',self)
        self.btn2.setIconSize(QSize(50,100))
        self.btnGroup.addButton(self.btn2,2)
        
    def addLabel(self):
        self.label1=QLabel('')
        self.label1.setFont(QFont('Arial',18))
        self.label1.setAlignment(Qt.AlignCenter)
        self.label1.setStyleSheet('color:#ff0000;'
                                  'background-color:#00ffff;')
    
    def buttonPressed(self,id):
        if id==1:
            self.pixmap=QPixmap('UK.png')
            w=self.label1.width()
            h=self.label1.height()
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if id==2:
            self.label1.clear()
        
if not QApplication.instance():
    appOne=QApplication(sys.argv)
else:
    appOne=QApplication.instance()
windowOne=MainWindow()

QRadioButton

A QRadioButton acts as a Boolean, it is either unselected (0) or selected (1). To use it it needs to be imported from the Qt5Widgets module of the PyQt5 library.

from PyQt5.QtWidgets import QPushButton,QLabel,QRadioButton

Creating a QRadioButton is similar to creating a QPushButton except there is no option to add a QIcon:

self.btn1=QRadioButton('Show',self)

We can update our code above to create Show and Hide QRadioButtons opposed to QPushButtons.

from PyQt5.QtWidgets import QMainWindow,QApplication,QWidget
from PyQt5.QtWidgets import QPushButton,QLabel,QRadioButton
from PyQt5.QtWidgets import QVBoxLayout,QHBoxLayout,QGridLayout
from PyQt5.QtWidgets import QButtonGroup
from PyQt5.QtGui import QIcon,QFont,QPixmap
from PyQt5.QtCore import QSize,QPoint,QRect,Qt
import sys
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initWindow()
        self.show()
        
    def initWindow(self):
        self.setWindowTitle('QMainWindow')
        self.setGeometry(QRect(QPoint(100,100)
                               ,QSize(400,300)))
        self.setWindowIcon(QIcon('setupapi_32.ico'))
        
        self.gridLayout=QGridLayout()
        self.addButtonGroup()
        self.gridLayout.addWidget(self.btn1,0,0)
        self.gridLayout.addWidget(self.btn2,0,1)
        
        self.addLabel()
        self.gridLayout.addWidget(self.label1,1,0,1,0)
        
        self.dummyCentralWidget=QWidget()
        self.dummyCentralWidget.setLayout(self.gridLayout)
        self.setCentralWidget(self.dummyCentralWidget)
        
    def addButtonGroup(self):
        self.btnGroup1=QButtonGroup()
        self.btnGroup1.buttonClicked[int].connect(self.buttonPressed)

        self.btn1=QRadioButton('Show')
        self.btnGroup1.addButton(self.btn1,1)
        
        self.btn2=QRadioButton('Hide')
        self.btnGroup1.addButton(self.btn2,2)
        
        
    def addLabel(self):
        self.label1=QLabel('')
        self.label1.setFont(QFont('Arial',18))
        self.label1.setAlignment(Qt.AlignCenter)
        self.label1.setStyleSheet('color:#ff0000;'
                                  'background-color:#00ffff;')
    
    def buttonPressed(self,id):
        if id==1:
            self.pixmap=QPixmap('UK.png')
            w=self.label1.width()
            h=self.label1.height()
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if id==2:
            self.label1.clear()
        
if not QApplication.instance():
    appOne=QApplication(sys.argv)
else:
    appOne=QApplication.instance()
windowOne=MainWindow()

Note that the QRadioButtons Show and Hide cannot be selected at the same time (when Hide gets selected, Show gets unselected). This is because they belong to the same instance of QButtonGroup.

We can update our code to create a second ButtonGroup with a Dummy QRadioButton.

from PyQt5.QtWidgets import QMainWindow,QApplication,QWidget
from PyQt5.QtWidgets import QPushButton,QLabel,QRadioButton
from PyQt5.QtWidgets import QVBoxLayout,QHBoxLayout,QGridLayout
from PyQt5.QtWidgets import QButtonGroup
from PyQt5.QtGui import QIcon,QFont,QPixmap
from PyQt5.QtCore import QSize,QPoint,QRect,Qt
import sys
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initWindow()
        self.show()
        
    def initWindow(self):
        self.setWindowTitle('QMainWindow')
        self.setGeometry(QRect(QPoint(100,100)
                               ,QSize(400,300)))
        self.setWindowIcon(QIcon('setupapi_32.ico'))
        
        self.gridLayout=QGridLayout()
        self.addButtonGroup()
        self.gridLayout.addWidget(self.btn1,0,0)
        self.gridLayout.addWidget(self.btn2,0,1)
        self.gridLayout.addWidget(self.btn3,0,3)
        
        self.addLabel()
        self.gridLayout.addWidget(self.label1,1,0,1,0)
        
        self.dummyCentralWidget=QWidget()
        self.dummyCentralWidget.setLayout(self.gridLayout)
        self.setCentralWidget(self.dummyCentralWidget)
        
    def addButtonGroup(self):
        self.btnGroup1=QButtonGroup()
        self.btnGroup1.buttonClicked[int].connect(self.buttonPressed)

        self.btn1=QRadioButton('Show')
        self.btnGroup1.addButton(self.btn1,1)
        
        self.btn2=QRadioButton('Hide')
        self.btnGroup1.addButton(self.btn2,2)
        
        self.btnGroup2=QButtonGroup()
        self.btn3=QRadioButton('Dummy')
        self.btnGroup2.addButton(self.btn3)
        
    def addLabel(self):
        self.label1=QLabel('',self)
        self.label1.setFont(QFont('Arial',18))
        self.label1.setAlignment(Qt.AlignCenter)
        self.label1.setStyleSheet('color:#ff0000;'
                                  'background-color:#00ffff;')
    
    def buttonPressed(self,id):
        if id==1:
            self.pixmap=QPixmap('UK.png')
            w=self.label1.width()
            h=self.label1.height()
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if id==2:
            self.label1.clear()
        
if not QApplication.instance():
    appOne=QApplication(sys.argv)
else:
    appOne=QApplication.instance()
windowOne=MainWindow()

This Dummy QRadioButton can be selected independently if Show or Hide are selected as it is in a separate instance of QButtonGroup.

QCheckBox

A similar class to the QRadioButton is the QCheckBox. To use this class, we need to import it from the QtWidgets module of the PyQt5 library:

from PyQt5.QtWidgets import QCheckBox

We can create an instance of it in the console to have a look through its available methods and attributes.

We can then create an instance of the class QCheckBox and use its methods checkState, setCheckState and setTristate to get the checked state, to set the checked state and to enable the tristate (disabled by default).

    def addCheckBox(self):
        self.checkBox1=QCheckBox('Show')
        self.checkBox1.setTristate(True)
        self.checkBox1.setCheckState(0)

Because the QCheckBox supports a tristate, it has three values 0 corresponds to unchecked, 1 corresponds to partially checked and state 2 corresponds to checked.

We can setup the attribute stateChanged which will setup a signal in response to the event when the check state is changed.

        self.checkBox1.stateChanged

We can then use the method connect to connect it to a slot.

        self.checkBox1.stateChanged.connect(self.checkState)

In this custom method we can use the method checkState from self.checkBox1 and assign it to idx. We can choose to clear the label if the idx is 0, show a part of the UK (Scotland) if it is partially checked 1 or show the entire UK if it is checked 2.

    def checkState(self):
        idx=self.checkBox1.checkState()
        w=self.label1.width()
        h=self.label1.height()
        if idx==0:
            self.label1.clear()
        if idx==1:
            self.pixmap=QPixmap('Scot.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if idx==2:
            self.pixmap=QPixmap('UK.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)

The full code is available here.

from PyQt5.QtWidgets import QMainWindow,QApplication,QWidget
from PyQt5.QtWidgets import QPushButton,QLabel,QRadioButton
from PyQt5.QtWidgets import QVBoxLayout,QHBoxLayout,QGridLayout
from PyQt5.QtWidgets import QButtonGroup,QCheckBox
from PyQt5.QtGui import QIcon,QFont,QPixmap
from PyQt5.QtCore import QSize,QPoint,QRect,Qt
import sys
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initWindow()
        self.show()
        
    def initWindow(self):
        self.setWindowTitle('QMainWindow')
        self.setGeometry(QRect(QPoint(100,100)
                               ,QSize(400,300)))
        self.setWindowIcon(QIcon('setupapi_32.ico'))
        
        self.gridLayout=QGridLayout()
        self.addCheckBox()
        self.gridLayout.addWidget(self.checkBox1,0,0)
        
        self.addLabel()
        self.gridLayout.addWidget(self.label1,1,0)
        
        self.dummyCentralWidget=QWidget()
        self.dummyCentralWidget.setLayout(self.gridLayout)
        self.setCentralWidget(self.dummyCentralWidget)
        
    def addCheckBox(self):
        self.checkBox1=QCheckBox('Show')
        self.checkBox1.setTristate(True)
        self.checkBox1.setCheckState(0)
        self.checkBox1.stateChanged.connect(self.checkState)
        
    def addLabel(self):
        self.label1=QLabel('')
        self.label1.setFont(QFont('Arial',18))
        self.label1.setAlignment(Qt.AlignCenter)
        self.label1.setStyleSheet('color:#ff0000;'
                                  'background-color:#00ffff;')
    
    def checkState(self):
        idx=self.checkBox1.checkState()
        w=self.label1.width()
        h=self.label1.height()
        if idx==0:
            self.label1.clear()
        if idx==1:
            self.pixmap=QPixmap('Scot.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if idx==2:
            self.pixmap=QPixmap('UK.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)    
            
if not QApplication.instance():
    appOne=QApplication(sys.argv)
else:
    appOne=QApplication.instance()
windowOne=MainWindow()

The GUI looks like the following.

QDial

So far we have only looked at QActions, QPushButton and QRadioButton which are all either on or off and QCheckBox which has three states (0,1 and 2). In some cases we may want discrete levels of control for example in a volume control or a brightness control. This can be done using a QDial. To use the QDial class we need to import it from the QtWidgets of the PyQt5 library.

from PyQt5.QtWidgets import QButtonGroup,QDial

We can create an instance of it in the console to have a look through its available methods and attributes.

We can then create an instance of the class QDial and use its methods setMinimum, setMaximum and setValue to set the minimum value, the maximum value and the default value.

    def addQDial(self):
        self.dial1=QDial()
        self.dial1.setMinimum(0)
        self.dial1.setMaximum(5)
        self.dial1.setValue(0)

We can then use the method value to read the currently set value.

self.dial1.value()

And we can setup the attribute valueChanged which will setup a signal in response to the event when the dial is changed.

self.dial1.valueChanged

We can then use the method connect to connect it to a slot.

self.dial1.valueChanged.connect(self.method)

In this example we will look at the three crosses which make the UK Union Flag.

  • 0 – No Pixmap
  • 1 – Scot.png St Andrew's Cross
  • 2 – Eng.png St George's Cross
  • 3 – GB.png King's Colours
  • 4 – NI.png St Patrick's Cross
  • 5 – UK.png Union Flag

The full code is:

from PyQt5.QtWidgets import QMainWindow,QApplication,QWidget
from PyQt5.QtWidgets import QPushButton,QLabel,QRadioButton
from PyQt5.QtWidgets import QVBoxLayout,QHBoxLayout,QGridLayout
from PyQt5.QtWidgets import QButtonGroup,QCheckBox,QDial
from PyQt5.QtGui import QIcon,QFont,QPixmap
from PyQt5.QtCore import QSize,QPoint,QRect,Qt
import sys
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initWindow()
        self.show()
        
    def initWindow(self):
        self.setWindowTitle('QMainWindow')
        self.setGeometry(QRect(QPoint(100,100)
                               ,QSize(400,300)))
        self.setWindowIcon(QIcon('setupapi_32.ico'))
        
        self.gridLayout=QGridLayout()
        self.addQDial()
        self.gridLayout.addWidget(self.dial1,0,0)
        
        self.addLabel()
        self.gridLayout.addWidget(self.label1,1,0,1,0)
        
        self.dummyCentralWidget=QWidget()
        self.dummyCentralWidget.setLayout(self.gridLayout)
        self.setCentralWidget(self.dummyCentralWidget)
        
    def addQDial(self):
        self.dial1=QDial()
        self.dial1.setMinimum(0)
        self.dial1.setMaximum(5)
        self.dial1.setValue(0)
        self.dial1.valueChanged.connect(self.dialValue)
        
    def addLabel(self):
        self.label1=QLabel('',self)
        self.label1.setFont(QFont('Arial',18))
        self.label1.setAlignment(Qt.AlignCenter)
        self.label1.setStyleSheet('color:#ff0000;'
                                  'background-color:#00ffff;')
    
    def dialValue(self):
        idx=self.dial1.value()
        w=self.label1.width()
        h=self.label1.height()
        if idx==0:
            self.label1.clear()
        if idx==1:
            self.pixmap=QPixmap('Scot.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if idx==2:
            self.pixmap=QPixmap('Eng.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if idx==3:
            self.pixmap=QPixmap('GB.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if idx==4:
            self.pixmap=QPixmap('NI.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if idx==5:
            self.pixmap=QPixmap('UK.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        
if not QApplication.instance():
    appOne=QApplication(sys.argv)
else:
    appOne=QApplication.instance()
appOne.setStyle('Fusion')
windowOne=MainWindow()

We can also optionally add notches to the QDial.

self.dial1.setNotchesVisible(True)

QComboBox, QSpinBox and QDoubleSpinBox

QComboBox

We can use a QComboBox instead of a dial in order to expose a dropdown menu of text. To access the QComboBox class we need to import it from the QtWidgets module of the PyQt5 library.

from PyQt5.QtWidgets import QComboBox

We can create an instance of it in the console to have a look through its available methods and attributes.

The methods of most interest are addItem which allows a string to be input and addItems which allows a list of strings to be input. The currently selected strng can be read out using the method currentText and each string when added will also have an index, which can be accessed using the method currentIndex. The value can be set by use of the string via the method setCurrentText or the index via the method setCurrent Text.

    def addQComboBox(self):
        self.comboBox1=QComboBox()
        self.comboBox1.addItems(['0 - None',
                                 '1 - Scotland',
                                 '2 - England',
                                 '3 - Great Britain',
                                 '4 - Northern Ireland',
                                 '5 - United Kingdom'])

We can also setup a signal which will be triggered when the index is changed.

        self.comboBox1.currentIndexChanged

We can connect this to a method using connect.

        self.comboBox1.currentIndexChanged.connect(self.comboBoxValue)

In this case we can alter the pixmap in accordance to the value selected within the QComboBox.

    def comboBoxValue(self):
        idx=self.comboBox1.currentIndex()
        w=self.label1.width()
        h=self.label1.height()
        if idx==0:
            self.label1.clear()
        if idx==1:
            self.pixmap=QPixmap('Scot.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if idx==2:
            self.pixmap=QPixmap('Eng.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if idx==3:
            self.pixmap=QPixmap('GB.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if idx==4:
            self.pixmap=QPixmap('NI.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if idx==5:
            self.pixmap=QPixmap('UK.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)

This gives a user interface like the following.

The full code is here.

from PyQt5.QtWidgets import QMainWindow,QApplication,QWidget
from PyQt5.QtWidgets import QPushButton,QLabel,QRadioButton
from PyQt5.QtWidgets import QVBoxLayout,QHBoxLayout,QGridLayout
from PyQt5.QtWidgets import QButtonGroup,QCheckBox,QDial
from PyQt5.QtWidgets import QComboBox
from PyQt5.QtGui import QIcon,QFont,QPixmap
from PyQt5.QtCore import QSize,QPoint,QRect,Qt
import sys
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initWindow()
        self.show()
        
    def initWindow(self):
        self.setWindowTitle('QMainWindow')
        self.setGeometry(QRect(QPoint(100,100)
                               ,QSize(400,300)))
        self.setWindowIcon(QIcon('setupapi_32.ico'))
        
        self.gridLayout=QGridLayout()
        self.addQComboBox()
        self.gridLayout.addWidget(self.comboBox1,0,0)
        
        self.addLabel()
        self.gridLayout.addWidget(self.label1,1,0,1,0)
        
        self.dummyCentralWidget=QWidget()
        self.dummyCentralWidget.setLayout(self.gridLayout)
        self.setCentralWidget(self.dummyCentralWidget)
        
    def addQComboBox(self):
        self.comboBox1=QComboBox()
        self.comboBox1.addItems(['0 - None',
                                 '1 - Scotland',
                                 '2 - England',
                                 '3 - Great Britain',
                                 '4 - Northern Ireland',
                                 '5 - United Kingdom'])
        self.comboBox1.currentIndexChanged.connect(self.comboBoxValue)
        
    def addLabel(self):
        self.label1=QLabel('',self)
        self.label1.setFont(QFont('Arial',18))
        self.label1.setAlignment(Qt.AlignCenter)
        self.label1.setStyleSheet('color:#ff0000;'
                                  'background-color:#00ffff;')
    
    def comboBoxValue(self):
        idx=self.comboBox1.currentIndex()
        w=self.label1.width()
        h=self.label1.height()
        if idx==0:
            self.label1.clear()
        if idx==1:
            self.pixmap=QPixmap('Scot.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if idx==2:
            self.pixmap=QPixmap('Eng.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if idx==3:
            self.pixmap=QPixmap('GB.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if idx==4:
            self.pixmap=QPixmap('NI.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if idx==5:
            self.pixmap=QPixmap('UK.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        
if not QApplication.instance():
    appOne=QApplication(sys.argv)
else:
    appOne=QApplication.instance()
appOne.setStyle('Fusion')
windowOne=MainWindow()

QSpinBox

The QSpinBox class is similar to the QComboBox class except it can only contain numeric integer values. To use the QSpinBox class we need to import it from the QtWidgets module of the PyQt5 library.

from PyQt5.QtWidgets import QSpinBox

We can the create an instance of the QSpinBox class

        self.spinBox1=QSpinBox()

Because it is numeric, the methods available are more similar to those of the QDial. We can use setMinimum, setMaximum, setSingleStep and setValue with an integer input. In this case we will set the minimum to 0, the maximum to 5, the value to 0 and the single step to 1. We could also optional read the set value off using the method value.

    def addQSpinBox(self):
        self.spinBox1=QSpinBox()
        self.spinBox1.setMinimum(0)
        self.spinBox1.setMaximum(5)
        self.spinBox1.setValue(0)
        self.spinBox1.setSingleStep(1)

We could also use the attribute valueChanged to setup a signal in response to the event where the value has been changed.

        self.spinBox1.valueChanged

We can connect this to a custom method (slot) using connect.

        self.spinBox1.valueChanged.connect(self.spinBoxValue)

This is very similar to the comboBoxValue method used earlier, only the function name and the 1st line differ.

    def spinBoxValue(self):
        idx=self.spinBox1.value()
        w=self.label1.width()
        h=self.label1.height()
        if idx==0:
            self.label1.clear()
        if idx==1:
            self.pixmap=QPixmap('Scot.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if idx==2:
            self.pixmap=QPixmap('Eng.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if idx==3:
            self.pixmap=QPixmap('GB.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if idx==4:
            self.pixmap=QPixmap('NI.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if idx==5:
            self.pixmap=QPixmap('UK.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
from PyQt5.QtWidgets import QMainWindow,QApplication,QWidget
from PyQt5.QtWidgets import QPushButton,QLabel,QRadioButton
from PyQt5.QtWidgets import QVBoxLayout,QHBoxLayout,QGridLayout
from PyQt5.QtWidgets import QButtonGroup,QCheckBox,QDial
from PyQt5.QtWidgets import QComboBox,QSpinBox
from PyQt5.QtGui import QIcon,QFont,QPixmap
from PyQt5.QtCore import QSize,QPoint,QRect,Qt
import sys
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initWindow()
        self.show()
        
    def initWindow(self):
        self.setWindowTitle('QMainWindow')
        self.setGeometry(QRect(QPoint(100,100)
                               ,QSize(400,300)))
        self.setWindowIcon(QIcon('setupapi_32.ico'))
        
        self.gridLayout=QGridLayout()
        self.addQSpinBox()
        self.gridLayout.addWidget(self.spinBox1,0,0)
        
        self.addLabel()
        self.gridLayout.addWidget(self.label1,1,0,1,0)
        
        self.dummyCentralWidget=QWidget()
        self.dummyCentralWidget.setLayout(self.gridLayout)
        self.setCentralWidget(self.dummyCentralWidget)
        
    def addQSpinBox(self):
        self.spinBox1=QSpinBox()
        self.spinBox1.setMinimum(0)
        self.spinBox1.setMaximum(5)
        self.spinBox1.setSingleStep(1)
        self.spinBox1.valueChanged.connect(self.spinBoxValue)
        
    def addLabel(self):
        self.label1=QLabel('',self)
        self.label1.setFont(QFont('Arial',18))
        self.label1.setAlignment(Qt.AlignCenter)
        self.label1.setStyleSheet('color:#ff0000;'
                                  'background-color:#00ffff;')
    
    def spinBoxValue(self):
        idx=self.spinBox1.value()
        w=self.label1.width()
        h=self.label1.height()
        if idx==0:
            self.label1.clear()
        if idx==1:
            self.pixmap=QPixmap('Scot.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if idx==2:
            self.pixmap=QPixmap('Eng.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if idx==3:
            self.pixmap=QPixmap('GB.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if idx==4:
            self.pixmap=QPixmap('NI.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if idx==5:
            self.pixmap=QPixmap('UK.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        
if not QApplication.instance():
    appOne=QApplication(sys.argv)
else:
    appOne=QApplication.instance()
appOne.setStyle('Fusion')
windowOne=MainWindow()

An optional prefix and suffix can be created.

        self.spinBox1.setPrefix('£')
        self.spinBox1.setSuffix('£')

QDoubleSpinBox

The QDoubleSpinBox is almost identical to the QSpinBox except for the fact that it allows floats. To use the QDoubleSpinBox class we need to import it from theQtWidgets module of the PyQt5 library.

from PyQt5.QtWidgets import QDoubleSpinBox

By default it displays data with 2 decimal places. We can change this using the method setDecimals and providing an itneger number of decimals in this case 3.

        self.spinBox1.setDecimals(3)

Otherwise the code is almost identical to before.

from PyQt5.QtWidgets import QMainWindow,QApplication,QWidget
from PyQt5.QtWidgets import QPushButton,QLabel,QRadioButton
from PyQt5.QtWidgets import QVBoxLayout,QHBoxLayout,QGridLayout
from PyQt5.QtWidgets import QButtonGroup,QCheckBox,QDial
from PyQt5.QtWidgets import QComboBox,QSpinBox,QDoubleSpinBox
from PyQt5.QtGui import QIcon,QFont,QPixmap
from PyQt5.QtCore import QSize,QPoint,QRect,Qt
import sys
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initWindow()
        self.show()
        
    def initWindow(self):
        self.setWindowTitle('QMainWindow')
        self.setGeometry(QRect(QPoint(100,100)
                               ,QSize(400,300)))
        self.setWindowIcon(QIcon('setupapi_32.ico'))
        
        self.gridLayout=QGridLayout()
        self.addQSpinBox()
        self.gridLayout.addWidget(self.spinBox1,0,0)
        
        self.addLabel()
        self.gridLayout.addWidget(self.label1,1,0,1,0)
        
        self.dummyCentralWidget=QWidget()
        self.dummyCentralWidget.setLayout(self.gridLayout)
        self.setCentralWidget(self.dummyCentralWidget)
        
    def addQSpinBox(self):
        self.spinBox1=QDoubleSpinBox()
        self.spinBox1.setMinimum(0)
        self.spinBox1.setMaximum(0.005)
        self.spinBox1.setValue(0)
        self.spinBox1.setSingleStep(0.001)
        self.spinBox1.setDecimals(3)
        self.spinBox1.setPrefix('£')
        self.spinBox1.setSuffix('£')
        self.spinBox1.valueChanged.connect(self.spinBoxValue)
        
    def addLabel(self):
        self.label1=QLabel('',self)
        self.label1.setFont(QFont('Arial',18))
        self.label1.setAlignment(Qt.AlignCenter)
        self.label1.setStyleSheet('color:#ff0000;'
                                  'background-color:#00ffff;')
    
    def spinBoxValue(self):
        idx=self.spinBox1.value()
        w=self.label1.width()
        h=self.label1.height()
        if idx==0.000:
            self.label1.clear()
        if idx==0.001:
            self.pixmap=QPixmap('Scot.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if idx==0.002:
            self.pixmap=QPixmap('Eng.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if idx==0.003:
            self.pixmap=QPixmap('GB.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if idx==0.004:
            self.pixmap=QPixmap('NI.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if idx==0.005:
            self.pixmap=QPixmap('UK.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        
if not QApplication.instance():
    appOne=QApplication(sys.argv)
else:
    appOne=QApplication.instance()
appOne.setStyle('Fusion')
windowOne=MainWindow()

QSlider

We can also use a QSlider. To use the QSlider class we need to import it from the QtWidgets of the PyQt5 library.

from PyQt5.QtWidgets import QSlider

We can create an instance of it in the console to have a look through its available methods and attributes.

We can then create an instance of the class QSlider and use its methods setMinimum, setMaximum and setValue to set the minimum value, the maximum value and the default value.

    def addQSlider(self):
        self.slider1=QSlider()
        self.slider1.setMinimum(0)
        self.slider1.setMaximum(5)
        self.slider1.setValue(0)
        self.slider1.setTickPosition(QSlider.TicksRight)

We can then use the method value to read the currently set value.

self.slider1.value()

By default the QSlider is vertical and without ticks.

We can then use the method value to read the currently set value.

self.dial1.value()

And we can setup the attribute valueChanged which will setup a signal in response to the event when the dial is changed.

self.slider1.valueChanged

We can then use the method connect to connect it to a slot.

self.slider1.valueChanged.connect(self.method)

These methods follow a similar behavior to the methods of the QDial. By default however the QSlider is vertical so we will arrange the QGridLayout to better accommodate it. Otherwise we will look at the same example, the make up of the UK Union Flag.

from PyQt5.QtWidgets import QMainWindow,QApplication,QWidget
from PyQt5.QtWidgets import QPushButton,QLabel,QRadioButton
from PyQt5.QtWidgets import QVBoxLayout,QHBoxLayout,QGridLayout
from PyQt5.QtWidgets import QButtonGroup,QCheckBox,QDial
from PyQt5.QtWidgets import QComboBox,QSpinBox,QDoubleSpinBox
from PyQt5.QtWidgets import QSlider
from PyQt5.QtGui import QIcon,QFont,QPixmap
from PyQt5.QtCore import QSize,QPoint,QRect,Qt
import sys

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initWindow()
        self.show()
        
    def initWindow(self):
        self.setWindowTitle('QMainWindow')
        self.setGeometry(QRect(QPoint(100,100)
                               ,QSize(400,300)))
        self.setWindowIcon(QIcon('setupapi_32.ico'))
        
        self.gridLayout=QGridLayout()
        self.addQSlider()
        self.gridLayout.addWidget(self.slider1,0,0)
        
        self.addLabel()
        self.gridLayout.addWidget(self.label1,0,1)
        
        self.dummyCentralWidget=QWidget()
        self.dummyCentralWidget.setLayout(self.gridLayout)
        self.setCentralWidget(self.dummyCentralWidget)
        
    def addQSlider(self):
        self.slider1=QSlider()
        self.slider1.setMinimum(0)
        self.slider1.setMaximum(5)
        self.slider1.setValue(0)
        self.slider1.valueChanged.connect(self.sliderValue)
        
    def addLabel(self):
        self.label1=QLabel('',self)
        self.label1.setFont(QFont('Arial',18))
        self.label1.setAlignment(Qt.AlignCenter)
        self.label1.setStyleSheet('color:#ff0000;'
                                  'background-color:#00ffff;')
    
    def sliderValue(self):
        idx=self.slider1.value()
        w=self.label1.width()
        h=self.label1.height()
        if idx==0:
            self.label1.clear()
        if idx==1:
            self.pixmap=QPixmap('Scot.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if idx==2:
            self.pixmap=QPixmap('Eng.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if idx==3:
            self.pixmap=QPixmap('GB.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if idx==4:
            self.pixmap=QPixmap('NI.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if idx==5:
            self.pixmap=QPixmap('UK.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        
if not QApplication.instance():
    appOne=QApplication(sys.argv)
else:
    appOne=QApplication.instance()
windowOne=MainWindow()

Tick positions can be added to the slider using the method setTickPosition.

This takes as an input argument another instance of QSlider with the attribute TicksLeft, TicksRight or TicksBothSides.

In this case we can add the ticks to the right hand side using TicksRight.

        self.slider1.setTickPosition(QSlider.TicksRight)

We can also change the orientation of the slider (by default it is vertical). This requires an instance of the Qt class with an attribute corresponding to the orientation.

In this case, we will use Qt.Horizontal.

        self.slider1.setOrientation(Qt.Horizontal)

This looks like the following, note that the QGridLayout and the tickPosition have also been modified to accommodate a horizontal QSlider.

from PyQt5.QtWidgets import QMainWindow,QApplication,QWidget
from PyQt5.QtWidgets import QPushButton,QLabel,QRadioButton
from PyQt5.QtWidgets import QVBoxLayout,QHBoxLayout,QGridLayout
from PyQt5.QtWidgets import QButtonGroup,QCheckBox,QDial
from PyQt5.QtWidgets import QComboBox,QSpinBox,QDoubleSpinBox
from PyQt5.QtWidgets import QSlider
from PyQt5.QtGui import QIcon,QFont,QPixmap
from PyQt5.QtCore import QSize,QPoint,QRect,Qt
import sys

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initWindow()
        self.show()
        
    def initWindow(self):
        self.setWindowTitle('QMainWindow')
        self.setGeometry(QRect(QPoint(100,100)
                               ,QSize(400,300)))
        self.setWindowIcon(QIcon('setupapi_32.ico'))
        
        self.gridLayout=QGridLayout()
        self.addQSlider()
        self.gridLayout.addWidget(self.slider1,0,0)
        
        self.addLabel()
        self.gridLayout.addWidget(self.label1,1,0)
        
        self.dummyCentralWidget=QWidget()
        self.dummyCentralWidget.setLayout(self.gridLayout)
        self.setCentralWidget(self.dummyCentralWidget)
        
    def addQSlider(self):
        self.slider1=QSlider()
        self.slider1.setMinimum(0)
        self.slider1.setMaximum(5)
        self.slider1.setValue(0)
        self.slider1.valueChanged.connect(self.sliderValue)
        self.slider1.setTickPosition(QSlider.TicksBelow)
        self.slider1.setOrientation(Qt.Horizontal)
        
    def addLabel(self):
        self.label1=QLabel('',self)
        self.label1.setFont(QFont('Arial',18))
        self.label1.setAlignment(Qt.AlignCenter)
        self.label1.setStyleSheet('color:#ff0000;'
                                  'background-color:#00ffff;')
    
    def sliderValue(self):
        idx=self.slider1.value()
        w=self.label1.width()
        h=self.label1.height()
        if idx==0:
            self.label1.clear()
        if idx==1:
            self.pixmap=QPixmap('Scot.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if idx==2:
            self.pixmap=QPixmap('Eng.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if idx==3:
            self.pixmap=QPixmap('GB.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if idx==4:
            self.pixmap=QPixmap('NI.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if idx==5:
            self.pixmap=QPixmap('UK.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        
if not QApplication.instance():
    appOne=QApplication(sys.argv)
else:
    appOne=QApplication.instance()
windowOne=MainWindow()

QProgressBar

We can create a QProgressBar to indicate the progress of a process. To create a ProgressBar we need to import the QProgressBar class from the QtWidgets module of the PyQt5 library.

from PyQt5.QtWidgets import QProgressBar

We can create an instance of it in the console to have a look through its available methods and attributes.

def addProgressBar(self):
    self.progressBar1=QProgressBar()

The most important methods are value and setValue to read and set the Values respectively of the QProgressBar in percent. We can in this very simple case set the value to correspond to the position of the slider. The slider goes from 0 to 5 so we can prescribe each value 20 percent. The full code is below.

from PyQt5.QtWidgets import QMainWindow,QApplication,QWidget
from PyQt5.QtWidgets import QPushButton,QLabel,QRadioButton
from PyQt5.QtWidgets import QVBoxLayout,QHBoxLayout,QGridLayout
from PyQt5.QtWidgets import QButtonGroup,QCheckBox,QDial
from PyQt5.QtWidgets import QComboBox,QSpinBox,QDoubleSpinBox
from PyQt5.QtWidgets import QSlider, QProgressBar
from PyQt5.QtGui import QIcon,QFont,QPixmap
from PyQt5.QtCore import QSize,QPoint,QRect,Qt
import sys

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initWindow()
        self.show()
        
    def initWindow(self):
        self.setWindowTitle('QMainWindow')
        self.setGeometry(QRect(QPoint(100,100)
                               ,QSize(400,300)))
        self.setWindowIcon(QIcon('setupapi_32.ico'))
        
        self.gridLayout=QGridLayout()
        self.addQSlider()
        self.gridLayout.addWidget(self.slider1,0,0)
        
        self.addLabel()
        self.gridLayout.addWidget(self.label1,1,0)
        
        self.addProgressBar()
        self.gridLayout.addWidget(self.progressBar1,2,0)
        
        self.dummyCentralWidget=QWidget()
        self.dummyCentralWidget.setLayout(self.gridLayout)
        self.setCentralWidget(self.dummyCentralWidget)
        
    def addProgressBar(self):
        self.progressBar1=QProgressBar()
        
    def addQSlider(self):
        self.slider1=QSlider()
        self.slider1.setMinimum(0)
        self.slider1.setMaximum(5)
        self.slider1.setValue(0)
        self.slider1.valueChanged.connect(self.sliderValue)
        self.slider1.valueChanged.connect(self.progressValue)
        self.slider1.setTickPosition(QSlider.TicksBelow)
        self.slider1.setOrientation(Qt.Horizontal)
        
    def addLabel(self):
        self.label1=QLabel('',self)
        self.label1.setFont(QFont('Arial',18))
        self.label1.setAlignment(Qt.AlignCenter)
        self.label1.setStyleSheet('color:#ff0000;'
                                  'background-color:#00ffff;')
    
    def sliderValue(self):
        idx=self.slider1.value()
        w=self.label1.width()
        h=self.label1.height()
        if idx==0:
            self.label1.clear()
        if idx==1:
            self.pixmap=QPixmap('Scot.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if idx==2:
            self.pixmap=QPixmap('Eng.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if idx==3:
            self.pixmap=QPixmap('GB.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if idx==4:
            self.pixmap=QPixmap('NI.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if idx==5:
            self.pixmap=QPixmap('UK.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
    
    def progressValue(self):
        idx=self.slider1.value()
        if idx==0:
            self.progressBar.setValue(0)
        if idx==1:
            self.progressBar1.setValue(20)
        if idx==2:
            self.progressBar1.setValue(40)
        if idx==3:
            self.progressBar1.setValue(60)
        if idx==4:
            self.progressBar1.setValue(80)
        if idx==5:
            self.progressBar1.setValue(100)
        
if not QApplication.instance():
    appOne=QApplication(sys.argv)
else:
    appOne=QApplication.instance()
windowOne=MainWindow()

We can optionally remove the text to get a ProgressBar which looks like the following:

    self.progressBar1.setTextVisible(False)

QTimer

In the example above we had to manually select the position on the QSlider. We can automate this however need to use the QTimer class to create a timeHandler. To use the QTimer class we need to import it from the QtCore module of the PyQt5 library.

from PyQt5.QtCore import QTimer

Once we've done this we can create an instance of QTimer. Then create a signal which corresponds to the timing event using the attribute timeout. Finally we can start the timer and as an input specify a time step in ms, in this case 2 s (2000 ms).

        self.timer=QTimer()
        self.timer.timeout
        self.timer.start(2000)

Now we can connect this to a slot.

        self.timer=QTimer()
        self.timer.timeout.connect(self.timeHandler)
        self.timer.start(2000)

We can then create the method timeHandler that contains a if, else condition. Once the code has checked the if, else condition and carried out the code within the specific branch, the timer will increment by the specified interval 2000 ms before looking at the if, else condition again. It will do this until the code tells the timer to stop which in this case is the code within the else branch.

    def timeHandler(self):
        value=self.slider1.value()
        if value<5:
            value=value+1
            self.slider1.setValue(value)
        else:
            self.timer.stop()
from PyQt5.QtWidgets import QMainWindow,QApplication,QWidget
from PyQt5.QtWidgets import QPushButton,QLabel,QRadioButton
from PyQt5.QtWidgets import QVBoxLayout,QHBoxLayout,QGridLayout
from PyQt5.QtWidgets import QButtonGroup,QCheckBox,QDial
from PyQt5.QtWidgets import QComboBox,QSpinBox,QDoubleSpinBox
from PyQt5.QtWidgets import QSlider, QProgressBar
from PyQt5.QtGui import QIcon,QFont,QPixmap
from PyQt5.QtCore import QSize,QPoint,QRect,Qt
from PyQt5.QtCore import QTimer
import sys
import time

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initWindow()
        self.show()
        
    def initWindow(self):
        self.setWindowTitle('QMainWindow')
        self.setGeometry(QRect(QPoint(100,100)
                               ,QSize(400,300)))
        self.setWindowIcon(QIcon('setupapi_32.ico'))
        
        self.gridLayout=QGridLayout()
        self.addQSlider()
        self.gridLayout.addWidget(self.slider1,0,0)
        
        self.addLabel()
        self.gridLayout.addWidget(self.label1,1,0)
        
        self.addProgressBar()
        self.gridLayout.addWidget(self.progressBar1,2,0)
        
        self.dummyCentralWidget=QWidget()
        self.dummyCentralWidget.setLayout(self.gridLayout)
        self.setCentralWidget(self.dummyCentralWidget)
        
        self.timer=QTimer()
        self.timer.timeout.connect(self.timeHandler)
        self.timer.start(2000)

    def addProgressBar(self):
        self.progressBar1=QProgressBar()
        self.progressBar1.setTextVisible(False)
        
    def addQSlider(self):
        self.slider1=QSlider()
        self.slider1.setMinimum(0)
        self.slider1.setMaximum(5)
        self.slider1.setValue(0)
        self.slider1.valueChanged.connect(self.sliderValue)
        self.slider1.valueChanged.connect(self.progressValue)
        self.slider1.setTickPosition(QSlider.TicksBelow)
        self.slider1.setOrientation(Qt.Horizontal)
        
    def addLabel(self):
        self.label1=QLabel('',self)
        self.label1.setFont(QFont('Arial',18))
        self.label1.setAlignment(Qt.AlignCenter)
        self.label1.setStyleSheet('color:#ff0000;'
                                  'background-color:#00ffff;')
    
    def sliderValue(self):
        idx=self.slider1.value()
        w=self.label1.width()
        h=self.label1.height()
        if idx==0:
            self.label1.clear()
        if idx==1:
            self.pixmap=QPixmap('Scot.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if idx==2:
            self.pixmap=QPixmap('Eng.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if idx==3:
            self.pixmap=QPixmap('GB.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if idx==4:
            self.pixmap=QPixmap('NI.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
        if idx==5:
            self.pixmap=QPixmap('UK.png')
            self.pixmap2=self.pixmap.scaled(QSize(w,h),
                                            Qt.IgnoreAspectRatio,
                                            Qt.SmoothTransformation)
            self.label1.setScaledContents(True)
            self.label1.setPixmap(self.pixmap2)
    
    def progressValue(self):
        idx=self.slider1.value()
        if idx==0:
            self.progressBar.setValue(0)
        if idx==1:
            self.progressBar1.setValue(20)
        if idx==2:
            self.progressBar1.setValue(40)
        if idx==3:
            self.progressBar1.setValue(60)
        if idx==4:
            self.progressBar1.setValue(80)
        if idx==5:
            self.progressBar1.setValue(100)       

    def timeHandler(self):
        value = self.slider1.value()
        if value<5:
            value=value+1
            self.slider1.setValue(value)
        else:
            self.timer.stop()


if not QApplication.instance():
    appOne=QApplication(sys.argv)
else:
    appOne=QApplication.instance()
windowOne=MainWindow()

If we instead want this to continuously loop we can change this to the following:

    def timeHandler(self):
        value = self.slider1.value()
        if value<5:
            value=value+1
            self.slider1.setValue(value)
        else:
            value=0
            self.slider1.setValue(value)

QStyleFactory

from PyQt5.QtWidgets import QStyleFactory
QStyleFactory.keys()

appOne.setStyle('Fusion')

Add

LineEdit

TextEdit

Tab

Context Menu