So you have a Raspberry Pi and a cool idea, but how do you make it as easy as a smartphone for your user to interact with? Add a nice intuitive interface! Building a Graphical User Interface is actually quite easy, and with some patience you can produce amazing results.
You have probably seen "GUI" somewhere, no matter how new to programming you are. GUI stands for Graphical User Interface. That actually means our title should just be RPi GUI; since we've effectively just wrote "Graphical User Interface Interface". But like many acronyms in English, phrases such as "ATM Machine", "LCD Display", and "PIN Number", it helps with identification of acronyms. While technically we're repeating ourselves, it's an odd quirk of the English language (and human nature).
Once you have dealt with GUI more, referring to it as simply GUI (often pronounced like "gooey") will make sense, and you can walk around talking like a code pro!
Once you gain an understanding of GUI, your Raspberry Pi projects can start to take on a whole new life. To keep the code simple we're using a standard screen and mouse. This keeps the theory simple and with the least additional support required, however the same principles will work on a touchscreen appropriately setup too.
You may already own, or have at least looked at, the Raspberry Pi touchscreens. Once you have been through this article and learnt how to create interactive elements, you'll no doubt want to move to touchscreen. This can serve as a springboard to home automation, digitisation of older hardware, and so much more. So let's dive in and take a look at the Raspberry Pi GUI!
The Broad Overview
One of the default programming languages on your Raspberry Pi (RPi) is Python. We assume you have had a good play with your RPi and even hooked up some sensors, HATs or LEDs to the GPIO port. One of the most powerful tools that RPi provides over other micros, is the rapid rate and ease you can create a Graphical User Interface (GUI) for your project.
For the purpose of this article, we’ll be using Python 3, which is quite different in style and syntax to Python 2. Some legacy functions hold over from 2 to 3, but there are substantial new functions available in 3. For the purpose of keeping up with current technology, we will usually be using Python 3. When you journey beyond the ideas presented in this article, just remember to include “Python 3” in search terms if you’re hunting down code snippets on the internet.
How It Works
Some time ago we interfaced with machines via knobs and dials. With the introduction of computers, we started to type our commands. As technology developed, we saw the mouse, voice recognition and the power glove. All these technologies have culminated into the fantastic interfaces in things such as smartphones.
Steve Jobs stated that interface should be so intuitive that the person picking it up should just be able to use it. When we build our projects the final user experience can be lacking, but this is the wonderful thing about the Raspberry Pi. We can quickly connect a touchscreen and straight from Python, design a user interface that will directly access and use the GPIO.
Python has the option to use several different GUIs, each with unique strengths and weaknesses. For this article, we will be using Tkinter, which is Python’s de-facto standard GUI package; it’s a thin object-oriented layer on top of Tcl/Tk. Tkinter is probably the most commonly used with Python, and plenty of resources exist on the internet.
At this stage, we are going to assume you have a fundamental knowledge of Python. So open idle, complete code, and run it.
We will start by producing the GUI equivalent of “Hello World”. Using Tkinter is quite simple but you need to be aware of how the code structure works. We start by importing the Tkinter object. Please be mindful here of the case; we are using Python 3, and there is some variation compared to Python 2. The general process of starting the GUI is to create the Window object (also known as the form). Set up the window as required, build any display parts such as grids or buttons, bind any functions if required then start the main loop.
Hello World In Tkinter
#!/usr/bin/python
from tkinter import *
root = Tk()
root.wm_title("Hello World")
root.mainloop()
TO BREAK THIS DOWN:
from Tkinter import *
Imports the Tkinter library.
root = Tk()
This creates the root window object.
root.wm_title("Hello World")
root.mainloop()
Sets title of the window, then starts the GUI loop.
Circuit 1 - LED Control
As we progress and add buttons into the form, we need to bind them to the functions they will be performing. In this case, we are going to create a form and add two buttons to it. One to turn the LED on/off, and the other to exit the program.
We have included this code in the resources - PiGUI_LED.py, so you can simply load and go. However we'll take you through the code to explain it below.
We start by importing the Tkinter and the GPIO libraries into the program. Next we setup the GPIO port to be output on pin 17. As with the Hello World example above, we set the root object, then the window title. The next lines are to set up how the GUI will function on your Pi.
root.attributes(“-fullscreen”,True) will make the GUI cover the entire screen, which is ultimately what we want to do. If you wish to be more specific, then comment this line out with a # then uncomment the next line (remove the #), and make sure that the resolution is set correctly. Our GUI/window is set.
Next, we create any functions that we need to bind to our buttons. This project is using two buttons: one to turn the LED on/off, and a second button to exit the program.
def btnClicked() has in “if” statement that checks the current state of the GPIO pin 17. If it’s high then it sends it low; if it’s low then it goes high. This is a Toggle function on pin 17.
defbtnExit() has a single line: root.destroy(). This terminates the program and provides a clean way to "close" the programme (especially useful if working fullscreen without a keyboard).
The final four lines of code are to create the buttons. Firstly we create the button. In doing this we set a number of attributes. Firstly, “root” is the window we are binding. Second is the text string that will be displayed on the button; and third is the command or function we want to bind it to. Finally, we will specify the height and width of the button.
#!/usr/bin/python
from tkinter import *
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(17,GPIO.OUT)
root = Tk() # the tkinter is case sensitive.
root.wm_title("PI Gui")
root.attributes("-fullscreen",True) #Makes the window full screen
#root.geometry(‘800x480’)
def btnClicked():
if GPIO.input(17):
print ("LED off")
GPIO.output(17,GPIO.LOW)
ledButton["text"]="LED OFF"
else:
print ("LED on")
GPIO.output(17,GPIO.HIGH)
ledButton["text"]="LED ON "
def btnExit():
root.destroy()
exitButton = Button(root, text="Exit",
command=btnExit, height=20, width=80)
exitButton.pack(side = BOTTOM)
ledButton = Button(root, text="LED OFF",
command=btnClicked, height=20, width=80)
ledButton.pack()
root.mainloop()
A NOTE ABOUT THE CODE: Make sure you have updated your RPi before starting, and that you have the GPIO library install. Open the command window and enter the following:
$ sudo apt-get update
$ sudo apt-get install python-rpi.gpio python3-rpi.gpio
WHAT UI ELEMENTS ARE AVAILABLE?
Tkinter includes many different widgets to help you create the most appropriate user interface. Widgets you can use include:
• message boxes | • menu items | • text box |
• buttons | • check button | • spin box |
• text labels | • input box (entry) | • slider |
• list box | • radio button |
THE BUILD
Parts Required: | JAYCAR | ALTRONICS |
---|---|---|
1 x Raspberry Pi 3 (NOOBS installed) | - | Z6302B |
1 x GPIO Extension Board + Ribbon Connector | - | Z6344 |
1 x Red or Green LED | ZD0170 | Z0801 |
1 x 330Ω Resistor | RR0560 | R7040 |
BUILDING THE CIRCUIT
Follow the diagram as shown. This is a fairly rudimentary circuit and doesn't require much instruction. Of course pay attention to the LED orientation (flat side to negative).
THE CODE & SETUP
We have provided the code file PiGUI_LED.py in the online resources.
Please note the lines:
root.attributes("-fullscreen",True)
#root.geometry('800x480')
Use one or the other. If you elect to use the root.geometry (‘800x480’), please replace the resolution as appropriate.
Once you have made any changes, save and run. Provided you have connected the circuit correctly your LED should switch on and off.
If you examine the partial code, you’ll see we do three things with each button press. The print (“LED off”) will send the text output to the console, indicating that the function has taken place. Similarly, the ledButton[“text”]=”LED OFF” changes the text of the button that the user is clicking on, so that it alternates.
The line GPIO.output(17,GPIO.LOW) addresses the RPi’s GPIO port, and tells pin 17 to go high or low, depending on the “if” statement.
CIRCUIT 2 - SERVO MOTOR CONTROLLER
Moving along to something other than a button, we can also use various inputs to control the PWM outputs from the Raspberry Pi. The applications for this are vast, so we'll look at a very popular one in robotics - servo motor control.
A servo motor is a great choice for robotics because it translates a PWM signal into an angle, providing you with a predictable and repeatable angle to control all sorts of things.
THE BUILD
Parts Required: | JAYCAR | ALTRONICS |
---|---|---|
1 x Mini Servo Motor | YM2758 | Z6392 |
BUILDING THE CIRCUIT
Follow the diagram shown below. This is a fairly rudimentary circuit and doesn't require much instruction. Of course pay attention to the LED orientation (flat side to negative). For high current servos (and indeed most applications) it's advisable to power the servo from a power supply without going through the microcontroller. This removes the strain of handling supply currents through the microcontroller itself. For our demonstration here it's rarely an issue, but not best-practice for production.
THE CODE & SETUP
We have provided the code file PiGUI_Dimmer.py. This code is not highly optimised, but is designed to demonstrate the use of multiple elements within the one system.
We won't go through each line, but will explain some important parts. You'll notice we have setup pin 18 as a PWM output, as well as providing a starting value. You can change this between 0 and 100 (the range of our PWM output) to your preferred start position.
pwm = GPIO.PWM(18, 100)
pwm.start(5)
You will notice that we do some math on the 180° range of the input slider, to convert it to within the bounds of our PWM output. We could just use a 0-100 range on our slider, but 0-180, corresponding with our servo angles, is much more user-friendly.
duty = float(angle) / 10.0 + 2.5
pwm.ChangeDutyCycle(duty)
Move the slider on screen and watch the servo move!
EXTRA CREDIT : 4-WAY LED DIMMER
With relative ease, we can expand on this principle and effectively create a 4-way dimmer. Of course, 4 is an arbitrary number we're using since it's enough for a demonstration. You can keep going until you run out of I/O, and even then there are breakout solutions.
THE BUILD
Parts Required: | JAYCAR | ALTRONICS |
---|---|---|
4 x Red or Green LEDs | ZD0170 | Z0801 |
4 x 330Ω Resistor | RR0560 | R7040 |
BUILDING THE CIRCUIT
Follow the diagram shown below. We are using GPIO pins 12,16,20, and 21 for our circuit. These are selected for no reason other than convenient breadboarding.
Insert the resistors and LEDs as shown, taking care to orient the flat side of the LED to the resistor side. Then load the code from the resources, PiGPIO_Dimmer.py.
THE CODE & SETUP
We have provided PiGUI_Dimmer.py code in the resources. For the purposes of clarity and not introducing too many new code principles at once, we are using rather rudimentary code here. When you have multiple lines of code doing similar things, there are numerous ways to enhance it, making it more efficient and using less memory. But we'll cover that another time.
LED4.grid(row=0,column=3)
Effectively we have setup four PWM outputs, one for each LED. We then create a corresponding slider for each output. This effectively provides you with a slider for each LED's brightness. This can easily become the foundation of some home automation controls. You can also step-up the PWM outputs (we provided PWM driver circuits in our 555 Timer article in Issue #001 which would be useful here also) to drive fans, motors, or much larger lights.
You'll also notice we've started populating position coordinates on the Tk grid. This is a dynamic positioning grid. We effectively assign elements to areas on the grid. If you don't do this, you risk an unpredictable layout.
There you have it! Go forth and conquer some amazing UI ideas!