Sunday, April 2, 2017

Design GUI in Python Using PyQt5

Hello! Everyone, in this post i will show you, how to design GUI's in Python using PyQt5.
PyQt5 is a set of Python Bindings for Qt v5, the Qt version at the time of writing this post is Qt 5.8.1 and the corresponding Python Binding version is also the same, you can download the Python Wheel of PyQt5 by clicking here.
Introduction
In this post, I will show how to make a GUI in Python using PyQt5 and for that I will take a real-world simple project example of measuring the distance using an Ultrasonic sensor and Arduino, and then display this distance value in GUI developed in Python using PyQt5 as shown below.
Setup and Python User Interface
In this project, Arduino will measure the distance using an Ultrasonic sensor and after that, it will transmit the distance values on the serial port, Python program will receive distance values from Arduino and update the text label on GUI with the recent distance values. We already have a few posts on Ultrasonic sensors and Serial Communication using Python, though it's not mandatory if you like, you can read those posts.
Ultrasonic Sensor Interfacing with Raspberry Pi
Ultrasonic Sensor Interfacing with Arduino & Displaying Data on MATLAB & LabVIEW GUI
Serial Communication using Python: Raspberry Pi
Serial Communication using Python: Windows

You can watch the following video or read the post below.

Now coming to this post, first, we will write Arduino Program for transmitting data over Serial Port (this part is simple and I am just sharing the source code, the main aim of this post is to show you guys how you can use PyQt5 with Python to develop GUI's), so the Arduino source code is as follow.
#include <NewPing.h>

#define TRIGGER_PIN   8     // Arduino pin tied to trigger pin on the ultrasonic sensor.
#define ECHO_PIN      9     // Arduino pin tied to echo pin on the ultrasonic sensor.
#define MAX_DISTANCE  200   // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm.

NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); // NewPing setup of pins and maximum distance.

void setup()
{
  Serial.begin(115200); // Open serial monitor at 115200 baud to see ping results.
}

void loop()
{
  Serial.println(sonar.ping_cm());
  delay(2000);
}

Configuring Python
I hope you all have Python 3. x and if not you can download Python 3 from the python website. The main step is to download and install PyQt5, I am going to use the PyQt5 wheels for windows which can be downloaded from this page.
Download PyQt5 Wheel for Python 3.x
Now install the wheel by using the following command.

pip3 install filename.whl


Install PyQt5 Wheel
Now open the Qt Designer and add the labels as shown in the image below, and after that save this file. Note this file will have the extension as *.ui.
Design GUI in Qt Designer
Now it's time to convert the *.ui file into *.py file, which can be done using the pyuic5.exe application, present in the Scripts directory of Python 3.x installation. use the following command to generate the Python file.

pyuic5.exe -x DistanceMeasurement.ui -o DistanceMeasurement.py

After issuing the above command, a python file will be generated, this file is just the GUI file and on running this you can see the GUI that you have developed, but it will not work as we haven't added the code to update the distance data from Arduino. So let's add this one by one, the first step is to import and create serial objects.

from PyQt5 import QtCore, QtGui, QtWidgets
import serial
import time, threading

global ser
ser = serial.Serial('COM11', baudrate=115200, timeout=10,
                    parity=serial.PARITY_NONE,
                    stopbits=serial.STOPBITS_ONE,
                    bytesize=serial.EIGHTBITS
                    )

and after that update the following code in the retranslateUi method present in the Python file, this code will update the data on the label.
    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "Distance Measurement"))
        self.label.setText(_translate("MainWindow", "Obsctacle Distance"))
        self.labelDistance.setText(_translate("MainWindow", "0"))
        self.labelcm.setText(_translate("MainWindow", "cm"))
        # User Code
        self.timeout = 0
        self.check_serial_event()

    def check_serial_event(self):
        self.timeout += 1
        # print (self.timeout)
        serial_thread = threading.Timer(1, self.check_serial_event)
        if ser.is_open == True:
            serial_thread.start()
            if ser.in_waiting:
                eol = b'\n'
                leneol = len(eol)
                line = bytearray()
                while True:
                    c = ser.read(1)
                    if c:
                        line += c
                        if line[-leneol:] == eol:
                            break
                    else:
                        break
                    # print (line)
                    # print (type(line))
                line = line.rstrip()
                distance = line.decode("utf-8")
                self.labelDistance.setText(distance)
                # print (distance)
                self.timeout = 0

        if self.timeout >= 10:
            ser.close()

The code shown above is added by me and the rest of the code is generated automatically by pyuic5.exe, I am also posting the complete code below.
# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'DistanceMeasurement.ui'
#
# Created by: PyQt5 UI code generator 5.8.1
#
# WARNING! All changes made in this file will be lost!

from PyQt5 import QtCore, QtGui, QtWidgets
import serial
import time, threading

global ser
ser = serial.Serial('COM11', baudrate=115200, timeout=10,
                    parity=serial.PARITY_NONE,
                    stopbits=serial.STOPBITS_ONE,
                    bytesize=serial.EIGHTBITS
                    )

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(259, 124)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setGeometry(QtCore.QRect(30, 10, 211, 41))
        font = QtGui.QFont()
        font.setFamily("Calibri")
        font.setPointSize(20)
        self.label.setFont(font)
        self.label.setAlignment(QtCore.Qt.AlignCenter)
        self.label.setObjectName("label")
        self.labelDistance = QtWidgets.QLabel(self.centralwidget)
        self.labelDistance.setGeometry(QtCore.QRect(60, 50, 71, 41))
        font = QtGui.QFont()
        font.setFamily("Calibri")
        font.setPointSize(20)
        self.labelDistance.setFont(font)
        self.labelDistance.setAlignment(QtCore.Qt.AlignCenter)
        self.labelDistance.setObjectName("labelDistance")
        self.labelcm = QtWidgets.QLabel(self.centralwidget)
        self.labelcm.setGeometry(QtCore.QRect(120, 50, 71, 41))
        font = QtGui.QFont()
        font.setFamily("Calibri")
        font.setPointSize(20)
        self.labelcm.setFont(font)
        self.labelcm.setAlignment(QtCore.Qt.AlignCenter)
        self.labelcm.setObjectName("labelcm")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 259, 21))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "Distance Measurement"))
        self.label.setText(_translate("MainWindow", "Obsctacle Distance"))
        self.labelDistance.setText(_translate("MainWindow", "0"))
        self.labelcm.setText(_translate("MainWindow", "cm"))
        # User Code
        self.timeout = 0
        self.check_serial_event()

    def check_serial_event(self):
        self.timeout += 1
        # print (self.timeout)
        serial_thread = threading.Timer(1, self.check_serial_event)
        if ser.is_open == True:
            serial_thread.start()
            if ser.in_waiting:
                eol = b'\n'
                leneol = len(eol)
                line = bytearray()
                while True:
                    c = ser.read(1)
                    if c:
                        line += c
                        if line[-leneol:] == eol:
                            break
                    else:
                        break
                    # print (line)
                    # print (type(line))
                line = line.rstrip()
                distance = line.decode("utf-8")
                self.labelDistance.setText(distance)
                # print (distance)
                self.timeout = 0

        if self.timeout >= 10:
            ser.close()


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

GUI after running the above script is shown below.

GUI After Running the script.

4 comments:

  1. Great Blog Post, Thanks For Sharing information.. if you Buy distance measuring wheels, 10.000 Meter in Christchurch (New Zealand) from souq. Compare prices and shop online now.

    ReplyDelete
  2. Great post!! I am searching these for over an hour and finally!! Thank you so much!

    ReplyDelete
  3. Thanks For sharing. It very helpful

    ReplyDelete