Many embedded linux platforms provide a graphical output port such as HDMI or MIPI-DSI to render a desktop environment. In situations where a display isn't available or your board doesn't support graphics output, you may still need a GUI to interact with the hardware. For example, monitoring a video stream or collecting and visualizing data.
In this project we're going to look at rapidly prototyping a web user interface on the MaaXBoard 8ULP using the PySimpleGuiWeb Python framework. The MaaXBoard 8ULP does not provide graphics output via HDMI, but an external display can be connected to its MIPI DSI port. For the purposes of this article however, we will not be connecting a display to the MIPI DSI port. Rather, the intention is to quickly architect and deploy a UI and use it to interact with hardware.
While there are many frameworks and technologies to help users develop a user interface, PySimpleGuiWeb allows you to do so with very few lines of code and doesn't require custom classes like other frameworks do.
First, we'll cover installing the PySimpleGuiWeb library using Python Pip. Then we'll create a web server, adding buttons & features to interface with the hardware.
Prerequisites & ResourcesIt's assumed the user has installed an image onto the board via eMMC. If not, there are existing articles that cover this in detail such as Getting Started with MaaXBoard. Below are also helpful links for getting started with the MaaXBoard 8ULP.
MaaXBoard 8ULP Reference Material:
- MaaXBoard 8ULP Product Page
- MaaXBoard 8ULP HUB Page
- MaaXBoard 8ULP Github
- MaaXBoard 8ULP Linux/Yocto User Manual
PySimpleGui Library References:
Other References:
Setup & Install LibraryAfter an image has successfully been installed on the 8ULP, connect an ethernet cable and a USB-C cable to the debug port.
Initiate a serial connection to the board using PuTTY or another serial terminal program. If using PuTTY, select the appropriate comPort (refer to the Linux/Yocto user manual in the section above) and set baud rate to 115200, data bits as 8, and stop bits as 1. An example of this is shown below. Note that the port number may be different on your system.
Now use ifconfig to retrieve the IP address of the board. Note that the IP address on your board may be different.
Next, we'll install PySimpleGuiWeb using Pip3.
pip3 install pysimpleguiweb
With that completed we'll now go onto creating a Hello World UI example.
Hello World UI ExampleThrough either the debug port or using SSH with the IP address from the previous section, connect to the board and create a file called Web_UI_demo.py
I've created this file in the /root/ directory and will be accessing the board via VSCode & SSH. If you're not familiar with VSCode, see the resources section.
In the newly created python file, paste the following code inside:
import PySimpleGUIWeb as sg
# Set theme color
sg.theme('Black')
# MAIN GUI LAYOUT
###########################################################################
layout = [
# Title label
[sg.Text('PySimpleGuiWeb Demo', size=(40, 2), justification='left', font='Helvetica 26')],
# Container:
# Text Label
# Hello World Button
[sg.Text('Button Test', justification='center', font='Helvetica 14')],
[sg.Button('Hello World', size=(10, 1), font='Helvetica 14', key='-HelloWorld-'),
],
# Empty label text place holder
[sg.Text('', size=(10, 3), font='Helvetica 14', key='-HWText-')],
# Button to exit application
[sg.Button('Exit', size=(10, 1), font='Helvetica 14', key='-EXIT-')]
]
###########################################################################
# Create Window Object & reference layout variable
# web_port will append to IP address (example 192.168.1.1:8080)
window = sg.Window('PySimpleGuiWeb Demo', layout, location=(800, 400), web_port=8080, web_start_browser=False)
# bool to toggle empty label text
updateText = True
while True:
try:
# Loop Timeout = 500ms
event, values = window.read(timeout=500)
# execute when exit button tapped
if event == '-EXIT-' or event == sg.WIN_CLOSED:
# Break from while loop & exit application
break
# execute when hello world button tapped
elif event == '-HelloWorld-':
# toggle updateText flag
if updateText:
# set text label
window['-HWText-'].update("Hello World Pressed")
updateText = not updateText
else:
# clear text label
window['-HWText-'].update("")
updateText = not updateText
# if any errors, print & exit application
except:
print("Error. Closing Application.")
window.close()
# Terminate UI server
window.close()
Run the python file using python3 Web_UI_Demo.py in the directory where the file is located. Open a browser of your choice and enter the IP address found in the earlier section for the board with port 8080 appended to the end. This should connect you to the new server running on the board and display the user interface over the browser.
**Important Note: PySimpleGuiWeb relies on Remi to render it's UI through a web browser. You may have noticed PySimpleGuiWeb installed Remi version 2020.3.10. This version of Remi is referenced by PySimpleGuiWeb as a dependency, however you may encounter an error AttributeError: 'HTMLParser' object has no attribute 'unescape'.
If that's the case, update the Remi library to 2022.7.27 using the following command and re-run the python script.
pip3 install remi==2022.7.27
Code WalkthroughPySimpleGuiWeb is built on top of the PySimpleGui library. So Many of visual elements found in the base library, such as buttons, text, columns, tabs, etc are also supported in the web framework. In this section, I'll explain some of these features and the main event loop.
- [1] - First we import the PySimpleGuiWeb as sg to alias the library's methods and functions. Then we create a layout variable (list) that contains text labels and two buttons. Although a simple example, hopefully this demonstrates the ease of rapidly prototyping a UI.
- Size, font, and justification can also be set as needed. For more details please reference the library methods in the resources section.
- [2] - In this area we define an element stack as another list. The text label will appear above a button called Hello World Button. If you were to add another button in this list, it would appear adjacent to the existing button.
# [1]
import PySimpleGUIWeb as sg
sg.theme('Black')
layout = [
# title
[sg.Text('PySimpleGuiWeb Demo', size=(40, 2), justification='left', font='Helvetica 26')],
# [2]
# Container:
# Text Label
# Hello World Button
[sg.Text('This is a text label', justification='center', font='Helvetica 14')],
[
sg.Button('Hello World Button', size=(10, 2), font='Helvetica 14', key='-HelloWorld-'),
],
# Place holder for empty text label
[sg.Text('', size=(10, 3), font='Helvetica 14', key='-HWText-')],
# Button to exit application
[sg.Button('Exit Button', size=(10, 1), font='Helvetica 14', key='-EXIT-')]
]
- [3] - Next, we create a window variable and assign to it a Window object that uses the `layout` we created above. The web_port number can be set to a different number, but for this example I chose 8080.
- If web_start_browswer is defined as True, the application will launch the UI in your selected default browser, however it may not open with the correct IP address. It's possible it opens as a local address (i.e 127.0.0.1) rather than the IP address of the device.
# [3]
# Create Window Object
window = sg.Window('PySimpleGuiWeb Demo', layout, location=(800, 400), web_port=8080, web_start_browser=False)
- [4] - Following the Window variable, we create an updateText flag which will be used to detect if the Hello World button is pressed. Then we setup the application to run in a while loop.
- [5] - Events and values are read at the timeout interval, which in this case is set to 500mS. The interval can be reduced, but shouldn't go below 100mS due to resourcing. This section is wrapped in a try/except block to catch errors associated with events & values.
- [6] - PySimpleGui uses keys to assign events to elements. For example, the `Hello World` button has a key -HelloWorld-. So if `Hello World` is tapped by the user, events will contain the key -HelloWorld- and the updateText flag will be toggled, and the text label with key -HWText- will be updated.
# [4]
# bool to toggle empty label text
updateText = True
while True:
try:
# [5]
# Loop Timeout = 500ms
event, values = window.read(timeout=500)
# execute when exit button tapped
if event == '-EXIT-' or event == sg.WIN_CLOSED:
# Break from while loop & exit application
break
# execute when hello world button tapped
elif event == '-HelloWorld-':
# [6]
# toggle updateText flag
if updateText:
# set text label
window['-HWText-'].update("Hello World Pressed")
updateText = not updateText
else:
# clear text label
window['-HWText-'].update("")
updateText = not updateText
# if any errors, print & exit application
except:
print("Error. Closing Application.")
window.close()
# Terminate UI server
window.close()
SummaryIn this article we showed how to setup PySimpleGuiWeb on a MaaXBoard 8ULP and quickly generate a UI. More features can easily be added, such as videos streaming via webcam and OpenCV or plots showing power consumption or processor activity.
Comments
Please log in or sign up to comment.