Brainy Pi

Available to select audience (currently beta)

User interfaces (UIs) are an essential part of modern software applications, providing an interactive way for users to interact with the underlying functionality of the application. Qt Creator, a popular integrated development environment (IDE) for developing Qt applications, provides a graphical interface for designing UIs using a drag-and-drop approach. Once the UI is designed,  we can implement it in Python using the PySide2 library, which is a set of Python bindings for the Qt application framework. HMI is Human Machine Interface – The hardware or software through which an operator interacts with a controller. In this blog, we will explore how to design HMI on Brainy Pi using Python i.e. UI with Qt Creator and implement it in using PySide2.

Index

  1. Pre-requisites
  2. HMI Top-level Design
  3. QT Creator Overview
  4. Designing the HMI in Depth
    1. Button and Labels
    2. Sliders, Dials and LCD
    3. Graphs
  5. Coding
    1. Structure of the Code
    2. Button and Labels
    3. Sliders, Dials and LCD
    4. Graphs
  6. Run the ready to use code
  7. TLDR

Pre-requisites for HMI on Brainy Pi

  1. Installing Qt creator
    sudo apt update && sudo apt install -y qtbase5-dev qtcreator 
  2. Installing PySide2 (python for QT5)
    sudo apt update && sudo apt-get install -y python3-pyside2.qt3dcore python3-pyside2.qt3dinput python3-pyside2.qt3dlogic python3-pyside2.qt3drender python3-pyside2.qtcharts python3-pyside2.qtconcurrent python3-pyside2.qtcore python3-pyside2.qtgui python3-pyside2.qthelp python3-pyside2.qtlocation python3-pyside2.qtmultimedia python3-pyside2.qtmultimediawidgets python3-pyside2.qtnetwork python3-pyside2.qtopengl python3-pyside2.qtpositioning python3-pyside2.qtprintsupport python3-pyside2.qtqml python3-pyside2.qtquick python3-pyside2.qtquickwidgets python3-pyside2.qtscript python3-pyside2.qtscripttools python3-pyside2.qtsensors python3-pyside2.qtsql python3-pyside2.qtsvg python3-pyside2.qttest python3-pyside2.qttexttospeech python3-pyside2.qtuitools python3-pyside2.qtwebchannel python3-pyside2.qtwebsockets python3-pyside2.qtwidgets python3-pyside2.qtx11extras python3-pyside2.qtxml python3-pyside2.qtxmlpatterns
  3. Installing pyqtgraph (library for creating graphs)
    pip3 install pyqtgraph

HMI Top-level Design

  1. PushButton which will toggle a GPIO on or off and show the GPIO value.
  2. 2 LCD display, 1 Displays Temperature, 1 Displays Pressure
  3. 2 Sliders
    1. 1 Slider for setting Temperature
    2. 1 Slider for setting Pressure
    1. Dial for fine tuning the temperature setting.
  4. 2 Graphs
    1. 1 Graph which displays live CPU temperature of the system.
    2. 1 Graph which displays a random value.
Finally the HMI would look something like this.

2023-04-07-112802_1280x720_scrot

QT Creator Overview

Designing a UI with Qt Creator involves the following steps:
  1. Open QT Creator

    2023-04-06-143900_1280x720_scrot

  2. Create a New Qt Project: Launch Qt Creator and create a new Qt project by selecting “File” > “New File or Project” from the menu bar. 2023-04-06-143930_1280x720_scrot
  3. Choose “Application (QT for Python)” as the project template, then choose the “QT for Python – Window (UI file)” as the template and click “Next”. 2023-04-06-143941_1280x720_scrot
  4. To Configure the Project: Enter a Class name for the project, and then click “Next”.

    2023-04-06-143949_1280x720_scrot

    2023-04-06-143958_1280x720_scrot

  5. Finally, click “Finish” to create the project.
  6. Design the UI:
    • Once we create a project, Qt Creator will open the main window so click on the form.ui to open the Designer window

      2023-04-08-022702_1280x720_scrot

    • The main window consists of a central widget, where the UI can be designed using various UI elements such as buttons, labels, text boxes, etc., which can be dragged and dropped onto the canvas and hence it becomes easy for developers to create UI using QT.

      2023-04-08-022709_1280x720_scrot

  • The properties of each UI element can be customized using the “Properties” pane on the right side of the window.
  • We will see the designing the HMI in depth in next section.
  1. Save the UI: After making changes to the UI, save by selecting “File” > “Save” from the menu bar.

Designing the HMI on Brainy Pi in Depth

Since, we got basic understand of QT Creator lets move on to designing HMI for our application. Double click on the “form.ui” file in QT Creator, to open up the QT Designer window, this is where we will be designing the UI.

2023-04-08-022702_1280x720_scrot

1. Button and Labels

  1. Search for PushButton, Drag and drop it into the canvas.

    2023-04-08-022751_1280x720_scrot

  2. So now double Click on the PushButton to change the text inside the PushButton, to OFF.
  3. Search for Label, Drag and drop it into the canvas, just above the PushButton.
    2023-04-08-022736_1280x720_scrot
  4. So now double Click on the Label to change the text inside the label.

2. Sliders, Dials and LCD

  1. Search for Vertical Slider, Drag and drop it into the canvas. Make it 2 vertical sliders.
    2023-04-08-022743_1280x720_scrot
  2. Then search for Dial, Drag and drop it into the canvas.
    2023-04-08-022806_1280x720_scrot
  3. Then search for LCD Number, Drag and drop 2 LCD into the canvas.
    2023-04-08-022758_1280x720_scrot
  4. Label all the components that you dropped into the canvas and the end result should look like this
    2023-04-08-024000_1280x720_scrot

3. Graphs

  1. Search for Widget, Drag and drop 2 widgets into the canvas.
    2023-04-08-022813_1280x720_scrot
  2. Then resize both the widget as per your liking.
  3. Note Down the Width and the Height of the widgets from the Properties section.
    2023-04-08-022719_1280x720_scrot

Coding

Implementing the UI designed with Qt Creator in Python using PySide2, will the split into these parts

1. Structure of the code

import sys
from PySide2.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton, QSlider, QDial
from PySide2.QtCore import Qt

class hmi(QWidget):
    def __init__(self):
        super(hmi, self).__init__()
        self.load_ui()

    def load_ui(self):
        loader = QUiLoader()
        path = os.path.join(os.path.dirname(__file__), "form.ui")
        ui_file = QFile(path)
        ui_file.open(QFile.ReadOnly)
        self.ui = loader.load(ui_file, self)
        ui_file.close()

    # .....
    # More code will follow here

# Create QApplication instance
app = QApplication(sys.argv)

# Create main window
window = hmi()
window.show()

# Start event loop
sys.exit(app.exec_())
  1. Class Definition: The hmi class is defined, which inherits from the QWidget class, the base class for all GUI objects in PySide2. The hmi class represents the main window of the GUI and contains methods for initializing GUI widgets, handling signals and slots, and updating GUI elements.
  2. GUI Layout: The load_ui method loads the GUI layout from an external .ui file using the QUiLoader class. The .ui file contains the graphical design of the GUI created using Qt Creator.
  3. Application Definition: We usedQApplication class to create application, and used hmi class to create new window and this window is  the primary window of the application.

2. Button and Labels

  1. We will create a init_ui function to initialize the UI.
    def init_ui(self):
        """ Intialize the QT UI 
        """
        # Set Window Title
        self.setWindowTitle("HMI Display")
  2. We will also create a init_gpio function to initialize the GPIO which will be toggled by the button.
    def init_gpio(self):
    """ Intialize GPIO 
    
    Function initializes GPIO for UI usage later on in the code
    """
    GPIO.setmode(GPIO.BOARD)
    GPIO.setup(self.LED_GPIO_PIN, GPIO.OUT)
  3. Then we will create a function gpio_toggle which will toggle GPIO on or off on button press
    @Slot()
    def gpio_toggle(self):
        """Toggle GPIO on or off 
    
        Toggles GPIO on or off based on the current state of the GPIO.
        If on the function will turn it off. 
        If off the function will turn it on. 
        """
        if self.LED_value:
            GPIO.output(self.LED_GPIO_PIN, GPIO.LOW)
            self.LED_value = False
            self.ui.pushButton.setText("OFF")
            self.ui.label.setText("LED is OFF")
        else:
            GPIO.output(self.LED_GPIO_PIN, GPIO.HIGH)
            self.LED_value = True
            self.ui.pushButton.setText("ON")
            self.ui.label.setText("LED is ON")
    • The @Slot() decorator indicates that this method is a slot, which can be connected to signals emitted by GUI widgets.
    • The method first checks the current state of the LED, which is stored in a boolean variable self.LED_value.
    • If the LED is currently ON (self.LED_value is True), the method turns it OFF by setting the GPIO pin to GPIO.LOW, updating the button text to “OFF”, and changing a label text to “LED is OFF”.
    • If the LED is currently OFF (self.LED_value is False), the method turns it ON by setting the GPIO pin to GPIO.HIGH, updating the button text to “ON”, and changing the label text to “LED is ON”.
  4. Connect the gpio_toggle function to button click using the button click signal
    def init_ui(self):
        """ Intialize the QT UI 
        """
        # Set Window Title
        self.setWindowTitle("HMI Display")
    
        # Set the slot for the Pushbutton click
        self.ui.pushButton.clicked.connect(self.gpio_toggle)

3. Sliders, Dials and LCD

  1. Lets create a function for updating the LCD Display when the slider is moved.
    @Slot()
    def slider1Moved(self, value):
        """Update the LCD value when the slider1 moves
        """
        self.ui.lcdNumber.display((value/10)%20 + 0.01)
    
    @Slot()
    def slider2Moved(self, value):
        """Update the LCD value when the slider2 moves
        """
        self.TEMP_value = value + 0.01
        self.ui.lcdNumber_2.display(((value)) + 0.01)
    • slider1Moved(self, value): This method is a slot that is connected to a slider widget in the GUI. It is triggered when the slider is moved, and it updates the value displayed on an LCD widget. The value is calculated by taking the slider value (value) divided by 10, then taking the modulus of 20, and adding 0.01. The result is then displayed on the LCD widget using the display() method.
    • slider2Moved(self, value): Similar to slider1Moved, this method is a slot connected to a second slider widget. It updates the value displayed on a different LCD widget. We calculate this value by adding 0.01 to the slider value, and then displayed on the LCD widget using the display() method.
  2. Let’s create a function for updating the LCD Display when the Dial is moved, the code is similar to the slider
    @Slot()
    def dialMoved(self, value):
        """Update the LCD value when the dial moves
        """
        self.ui.lcdNumber_2.display(self.TEMP_value + (value/100))
    • dialMoved(self, value): This method is a slot connected to a dial widget. It updates the value displayed on the same LCD widget as slider2Moved. The value is calculated by adding the dial value divided by 100 to a variable self.TEMP_value, and then displayed on the LCD widget using the display() method.
  3. Now Let’s connect all the above functions to their respective UI elements, so that when the user interacts with those UI elements, those function get triggered.
    def init_ui(self):
        """ Intialize the QT UI 
        """
        # Set Window Title
        self.setWindowTitle("HMI Display")
    
        # Set the slot for the Pushbutton click
        self.ui.pushButton.clicked.connect(self.gpio_toggle)
        # Set the slot for the Vertical sliders moved
        self.ui.verticalSlider.sliderMoved.connect(self.slider1Moved)
        self.ui.verticalSlider_2.sliderMoved.connect(self.slider2Moved)
        # Set the slot for the Dial moved
        self.ui.dial.sliderMoved.connect(self.dialMoved)
    Hence, this is how init_ui function connects the UI  elements and functions.

4. Graphs

To create 2 Graphs, we need these functions for each graph
  1. Initialize function – Which initializes the Graph
  2. Timer function – Which triggers the update function after set time.
  3. Update Function – Which updates the Graph values
  4. Now that we understood what these functions are let’s create the Initialize function
    def initialize_graph1(self):
        """Initialize the Graph1
        """
        pen = pg.mkPen(color=(0, 255, 0), width=2)
        self.ui.graphWidget1 = pg.PlotWidget(self.ui.widget)
        # Set Background colour to white
        self.ui.graphWidget1.setBackground('w')
        # Set Graph Title
        self.ui.graphWidget1.setTitle("CPU Temperature", color="b", size="20pt")
        # Set Graph Axis Labels
        self.ui.graphWidget1.setLabel('left', "Temperature (C)")
        self.ui.graphWidget1.setLabel('bottom', "Time (s)")
        # Set Graph size
        self.ui.graphWidget1.setFixedSize(461, 201)
        # Set Initial Graph points
        self.graph1x = list(range(100))  # 100 time points
        self.graph1y = [0 for _ in range(100)]  # 100 data points
        self.graph1data_line = self.ui.graphWidget1.plot(self.graph1x, self.graph1y, pen=pen)
    
    def initialize_graph2(self):
        """Initialize the Graph2
        """
        pen = pg.mkPen(color=(255, 0, 0), width=2)
        self.ui.graphWidget2 = pg.PlotWidget(self.ui.widget_2)
        # Set Background colour to white
        self.ui.graphWidget2.setBackground('w')
        # Set Graph Title
        self.ui.graphWidget2.setTitle("Random Value - Pressure", color="b", size="20pt")
        # Set Graph Axis Labels
        self.ui.graphWidget2.setLabel('left', "Pressure (ba)")
        self.ui.graphWidget2.setLabel('bottom', "Hour (H)")
        # Set Graph size
        self.ui.graphWidget2.setFixedSize(461, 201)
        # Set Initial Graph points
        self.graph2x = list(range(100))  # 100 time points
        self.graph2y = [randint(0, 100) for _ in range(100)]  # 100 data points
        self.graph2data_line = self.ui.graphWidget2.plot(self.graph2x, self.graph2y, pen=pen)
    
    • Both these functions:
      • Creates a PlotWidget object from pyqtgraph library to create a graph in the GUI.
      • Sets properties of the graph such as background color, title, axis labels, and size.
      • Creates lists to store x and y values of the graph and fills these list with initial values
      • Creates a plot on the graph using the plot() method with the specified pen.
  5. Let’s create and update function
    def update_graph1_data(self):
        """Update the Graph1
        """
        self.graph1x = self.graph1x[1:]  # Remove the first x element.
        self.graph1x.append(self.graph1x[-1] + 1)  # Add a new value 1 higher than the last.
    
        self.graph1y = self.graph1y[1:]  # Remove the first
        temperature = self.get_cpu_temperature()
        self.graph1y.append(temperature)  # Add a new temperature value.
    
        self.graph1data_line.setData(self.graph1x, self.graph1y)  # Update the data.
    
    def update_graph2_data(self):
        """Update the Graph2
        """
        self.graph2x = self.graph2x[1:]  # Remove the first x element.
        self.graph2x.append(self.graph2x[-1] + 1)  # Add a new value 1 higher than the last.
    
        self.graph2y = self.graph2y[1:]  # Remove the first
        self.graph2y.append(randint(0, 100))  # Add a new random value.
    
        self.graph2data_line.setData(self.graph2x, self.graph2y)  # Update the data.
    
    def get_cpu_temperature(self):
        """Helper function which gets the live system CPU Temperature value
        """
        file1 = open("/sys/devices/virtual/thermal/thermal_zone0/temp", "r")
        CPU_temp = str(file1.readline())
        file1.close()
        CPU_temp = float(int(CPU_temp)/1000)
        return CPU_temp
    • update_graph1_data(self): Updates the data of Graph1 by removing the oldest data point, adding a new data point for CPU temperature obtained from get_cpu_temperature() function, and updating the plot with the new data using setData() method.
    • update_graph2_data(self): Updates the data of Graph2 by removing the oldest data point, adding a new random data point, and updating the plot with the new data using setData() method.
    • get_cpu_temperature(self): Helper function that reads the live system CPU temperature from a file, converts it to Celsius, and returns the temperature as a float value.
  6. Let’s move on to create the Timer functions
    def initialize_timer_for_graph1(self, updateTime, updateFunction):
        """Initiallize the timer for graph
    
        Args:
            1.  updateTime - Time interval in ms for updating the graph value
            2.  updateFunction - The update data function for the graph
    
        """
        self.timer1 = QTimer()
        self.timer1.setInterval(updateTime)
        self.timer1.timeout.connect(updateFunction)
        self.timer1.start()
    
    def initialize_timer_for_graph2(self, updateTime, updateFunction):
        """Initiallize the timer for graph
    
        Args:
            1.  updateTime - Time interval in ms for updating the graph value
            2.  updateFunction - The update data function for the graph
    
        """
        self.timer2 = QTimer()
        self.timer2.setInterval(updateTime)
        self.timer2.timeout.connect(updateFunction)
        self.timer2.start()
    • initialize_timer_for_graph1(self, updateTime, updateFunction): Initializes a timer for Graph1 with the given time interval updateTime in milliseconds. The timer is connected to the updateFunction which is responsible for updating the data of Graph1. The timer is started using start() method.
    • initialize_timer_for_graph2(self, updateTime, updateFunction): Initializes a timer for Graph2 with the given time interval updateTime in milliseconds. The timer is connected to the updateFunction which is responsible for updating the data of Graph2. The timer is started using start() method.
  7. Now that we explored all the factors lets put it all together
    def init_ui(self):
        """ Intialize the QT UI 
        """
        # Set Window Title
        self.setWindowTitle("HMI Display")
    
        # Set the slot for the Pushbutton click
        self.ui.pushButton.clicked.connect(self.gpio_toggle)
        # Set the slot for the Vertical sliders moved
        self.ui.verticalSlider.sliderMoved.connect(self.slider1Moved)
        self.ui.verticalSlider_2.sliderMoved.connect(self.slider2Moved)
        # Set the slot for the Dial moved
        self.ui.dial.sliderMoved.connect(self.dialMoved)
        # Initialize the Graph
        self.initialize_graph1()
        self.initialize_graph2()
        # Set the timers for graph
        self.initialize_timer_for_graph1(100, self.update_graph1_data)
        self.initialize_timer_for_graph2(500, self.update_graph2_data)
    This is how we initialize the graphs and set the timer in the init_ui function.
  8. Now the code should be ready
  9. Your code should look something like this – https://github.com/brainypi/brainypi-hmi-example.git

Run the ready to use code

  1. Open Terminal and Navigate to your project folder
    cd /path/to/project/folder
    
    # For example
    # cd /home/pi/hmi
  2. Then run the program
    python3 main.py

    2023-04-07-112802_1280x720_scrot

TLDR;

Because there are so many steps above in order to create an application so here is easy way to run the HMI on brainy pi, follow these steps.
sudo apt update && sudo apt install -y qtbase5-dev qtcreator 
sudo apt update && sudo apt-get install -y python3-pyside2.qt3dcore python3-pyside2.qt3dinput python3-pyside2.qt3dlogic python3-pyside2.qt3drender python3-pyside2.qtcharts python3-pyside2.qtconcurrent python3-pyside2.qtcore python3-pyside2.qtgui python3-pyside2.qthelp python3-pyside2.qtlocation python3-pyside2.qtmultimedia python3-pyside2.qtmultimediawidgets python3-pyside2.qtnetwork python3-pyside2.qtopengl python3-pyside2.qtpositioning python3-pyside2.qtprintsupport python3-pyside2.qtqml python3-pyside2.qtquick python3-pyside2.qtquickwidgets python3-pyside2.qtscript python3-pyside2.qtscripttools python3-pyside2.qtsensors python3-pyside2.qtsql python3-pyside2.qtsvg python3-pyside2.qttest python3-pyside2.qttexttospeech python3-pyside2.qtuitools python3-pyside2.qtwebchannel python3-pyside2.qtwebsockets python3-pyside2.qtwidgets python3-pyside2.qtx11extras python3-pyside2.qtxml python3-pyside2.qtxmlpatterns
pip3 install pyqtgraph
git clone https://github.com/brainypi/brainypi-hmi-example.git
cd brainypi-hmi-example
python3 main.py
Original reference link – https://programmingdigest.com/raspberry-pi-hmi-project-using-pyqt5-software-tutorial/

Conclusion

Hence, in this blog, we explored the method to create HMI on Brainy Pi using python. Stay tuned for more such blogs.
0 Comments

Leave a reply

Your email address will not be published. Required fields are marked *

*