Hello everyone! It’s been a week and I have been working hard at designing and writing the trivia interface for this project, and I am proud to say that after an absence of more than five years from Python, I was successful in getting a decent looking GUI built. Unfortunately I have yet to figure out how I will be randomizing the questions that the device ask trick-or-treaters, but that is something I can work on later in the project. So let’s jump in and take a look at how I created the trivia interface.
In my first post, I mentioned that I had planned on using the Drupal CMS to build the trivia interface. After some conversation with another Drupal developer, I came to the conclusion that using a PHP-based framework and building a database was way overkill when I wanted to just display a few questions on a screen. So I decided to just write the whole trivia interface in Python, a language I have not touched in more than five years. Unfortunately my entire Python experience was in the command line, and I had never tried writing a GUI before.
After some research I found out that it is extremely easy to create a simple, low-level user interface in Python by using a GUI library named Tkinter. For those of you who have never heard of Tkinter (like me), it’s Python's de-facto standard GUI (Graphical User Interface) package. It is a thin object-oriented layer on top of Tcl/Tk. Using Tkinter I was easily able to create a nice full-screen application that would clearly present the question and its answer buttons to the user.
Installing The Raspberry Pi 7-InchTouch Screen
Before I show you the interface and the code that created it, let’s take a look at what we will be displaying the code on. Element14 sent me the brand new Raspberry Pi 7-inch touch-screen LCDRaspberry Pi 7-inch touch-screen LCD to use in this project, and after spending a couple of weeks with this screen, all I can say is WOW. I have used several different screens with the Raspberry PiRaspberry Pi, and Beaglebone BlackBeaglebone Black boards, and none has been as easy as this screen was to use.
Getting the Raspberry Pi 7-inch Touch Screen up and running is as simple as connecting the large ribbon cable to the back of the driver board, and then securing the driver board to the screen with the provided hardware.
With the driver board mounted to the LCD screen, connect the smaller ribbon cable to the driver board. I found it was easier to do it this way since I have larger fingers.
With thetwo LCD ribbon cables connected, mount the Raspberry Pi to the stand-offs, and then connect the DSI cable to both boards.
Now connect the two power jumper wires to both boards as shown in the above image. You will also want to insert the SD card into the Raspberry Pi. If you are running the latest version of raspbian, you can now plug a 5V 2A power source into the LCD screen’s driver board. If you do not have a 5V 2A power source, you can remove the power wires that connect the two boards, and then connect a single 5V 1A power source to the Raspberry Pi and One to the LCD screen’s driver board.
You will have to connect your Raspberry Pi to video source via the HDMI port for this process, or SSH into the Pi if you know it’s IP address. If you are using the NOOBs SD card that comes with the kit for this tutorial, you will have to update to the latest Raspbian, as the one installed on it is out of date.
To see which version of Raspbian your Pi is running, use the following command in the terminal:
uname -a
Which should return something that looks like this.
Linux raspberrypi 4.1.6-v7+ #810 SMP PREEMPT Tue Aug 18 15:32:12 BST 2015 armv7l GNU/Linux
If your version of Raspbian is out of date, run the following commands in the terminal:
sudo apt-get update
Then
sudo apt-get upgrade -y
With the update and upgrades ran, you can now restart your Raspberry Pi, and the LCD screen should now work and display the login screen.
Enter the desktop GUI by typing the following command after logging in.
startx
Now that you are on the desktop, let’s look at how I built the trivia GUI.
GUI Build UsingTkinter
As I mentioned above, I decided to write my own GUI instead of using a heavy framework like Drupal to create a simple quiz interface. I looked at a few options and finally decided on a GUI solution that was already built into Python called Tkinter. I chose this because it was very lightweight which would save loading times, and because it seemed very simple to use.
The Knowledge
One of the biggest sellers for me on Tkinter was the sheer amount of documentation that is available online. New Mexico Tech has a great Tkinter reference on their website which was very helpful when troubleshooting, but the best resource I found was a series of Videos on YouTube from a user named TheNewBoston. I would highly recommend heading to his channel and watching his tutorial series on using Tkinter.
TheNewBoston's first Tkinter Tutorial
I ended up watching the entire series, but if you only have time to watch a few, everything up to video six will give you the knowledge needed to complete this project. Watch the videos if you are following along at home, and check out my code below which I have broken out to better explain what each section does.
The Code
We need to import Tkinter, the Raspberry Pi GPIO as GPIO, Time, and system libraries.
from Tkinter import * import RPi.GPIO as GPIO import time import sys
Here we set up the GPIO pins. For the purpose of this tutorial I turn GPIO warnings to off. Then we need to set the GPIO Pinout to the BCM layout. Finally we need to define two output pins that will turn on a pair of LEDs.
GPIO.setwarnings(False) GPIO.setmode(GPIO.BCM) GPIO.setup(26, GPIO.OUT) GPIO.setup(19, GPIO.OUT)
For those of you who do not know, the Raspberry PI’s GPIO sins can be configured two different ways, GPIO.board and GPIO.bcm. When setting the GPIO pins mode, you are telling it what numbering scheme your code will be adhering too. Unfortunately the pin numbering between each of the different modes changes on various models and revisions of the Raspberry PI. So if you are not running a Model B+ or Raspberry Pi 2, you will need to search the internet for the proper pinouts for the mode you select.
- The GPIO.BOARD option specifies that you are referring to the pins by the number of the pin the the plug - i.e the numbers printed on the board (e.g. P1) and in the middle of the diagram below.
- The GPIO.BCM option means that you are referring to the pins by the "Broadcom SOC channel" number, these are the numbers after "GPIO" in the green rectangles around the outside of the diagram below.
Raspberry Pi B+ and Raspberry Pi 2 GPIO Pinout.
We need to set the state to True
state = True
This section defines a function called blink_led. At the end of this script I add a sys.exit line that is executed three seconds after I turn off the GPIO pin for the last time. This exits out of the quiz script, allowing us to reset for the next trick-or-treater.
def blink_led(): # endless loop, on/off for 1 second while True: GPIO.output(26,True) time.sleep(1) GPIO.output(26,False) time.sleep(1) GPIO.output(26,True) time.sleep(1) GPIO.output(26,False) time.sleep(1) GPIO.output(26,True) time.sleep(1) GPIO.output(26,False) time.sleep(1) GPIO.cleanup() time.sleep(3) sys.exit()
This section sets up another function that blinks the same pattern, but this time we name the function blink_led_2.
def blink_led_2(): # endless loop, on/off for 1 second while True: GPIO.output(19, True) time.sleep(1) GPIO.output(19, False) time.sleep(1) GPIO.output(19, True) time.sleep(1) GPIO.output(19, False) time.sleep(1) GPIO.output(19, True) time.sleep(1) GPIO.output(19, False) time.sleep(1) GPIO.cleanup() time.sleep(3) sys.exit()
Now we need to set up Tkinter. The first thing we need to do is set up a basic box that will hold the elements of our GUI. We do this by telling the program that root=Tk().
root = Tk()
Now we need to set the window to not have a border, title, or any of the normal minimize, maximize, and close buttons.
root.overrideredirect(True)
This line tells the program to set the window to full screen and auto size it to the screens resolution.
root.geometry("{0}x{1}+0+0".format(root.winfo_screenwidth(), root.winfo_screenheight()))
Now we need to tell the program to set the focused window to this one.
root.focus_set()
We also need to set the window’s background to black.
root.configure(background='black')
With the basic GUI window setup we can move on to setting up the elements that will appear in the z. This consist of three labels on top that span three rows of a grid, followed by two answer buttons on the next row spaced apart by two columns, and another row with two buttons and the same spacing below it. Finally we need to add two more labels to the bottom that describe what the rewards are for correct and incorrect answers. You will see that I have styled the elements inline with basic styling options, and placed the elements using Tkinter's .grid Geometry Manager.
Setting up the Labels.
In each of these three labels you can see that I have placed them in the root box, and included font, font size, background color, and foreground color attributes. and the set each of the labels to display using the .grid method followed by some placement style and padding attributes.
label_1 = Label(root, text="Welcome to Trick or Trivia", font=("Helvetica", 36), bg="black", fg="white") label_1.grid(columnspan=6,padx=(100, 10)) label_2 = Label(root, text="Answer the question for candy!", font=("Helvetica", 28), bg="black", fg="red") label_2.grid(columnspan=6, pady=5, padx=(100, 10)) label_3 = Label(root, text="Casper is a friendly ____!", font=("Helvetica", 32), bg="black", fg="green") label_3.grid(columnspan=6, pady=5, padx=(100, 10))
Setting up the Buttons
Just like the Label elements, I defined the buttons by placing them into the root window, adding text, and including attributes for font type and font size. Here is a reference (http://effbot.org/tkinterbook/button.htm). What makes the buttons special is the “command” attribute I included at the end of the button setup line. This calls the function it names, which in our case is one of the two blink_led functions we wrote earlier. Finally I displayed the button using the .grid method with some styling attributes.
button_1 = Button(root, text="Ghost", font=("Helvetica", 36), command=blink_led) button_1.grid(row=4, column=2, pady=5, padx=(100, 10)) button_2 = Button(root, text="Ghast", font=("Helvetica", 36), command=blink_led_2) button_2.grid(row=4, column=4, sticky=W, padx=(100, 10)) button_3 = Button(root, text="Ghoul", font=("Helvetica", 36), command=blink_led_2) button_3.grid(row=5, column=2, pady=5, padx=(100, 10)) button_4 = Button(root, text="Gremlin", font=("Helvetica", 36), command=blink_led_2) button_4.grid(row=5, column=4, sticky=W, padx=(100, 10))
Finally I added two more labels with the same styling and placement attributes as before.
label_4 = Label(root, text="Correct Answer = 3 Pieces", font=("Helvetica", 20), bg="black", fg="green") label_4.grid(columnspan=6, padx=(100, 10)) label_5 = Label(root, text="Incorrect Answer = 1 Piece", font=("Helvetica", 20), bg="black", fg="red") label_5.grid(columnspan=6, padx=(100, 10))
Finally we need to tell the program to stay in the event loop until we close the window.
root.mainloop()
Putting It All Together
Here is what the code looks like as a whole. You can download this code from my Github repo for this project or by clicking here.
from Tkinter import * import RPi.GPIO as GPIO import time import sys GPIO.setwarnings(False) GPIO.setmode(GPIO.BCM) GPIO.setup(26, GPIO.OUT) GPIO.setup(19, GPIO.OUT) state = True def blink_led(): # endless loop, on/off for 1 second while True: GPIO.output(26,True) time.sleep(1) GPIO.output(26,False) time.sleep(1) GPIO.output(26,True) time.sleep(1) GPIO.output(26,False) time.sleep(1) GPIO.output(26,True) time.sleep(1) GPIO.output(26,False) time.sleep(1) GPIO.cleanup() time.sleep(3) sys.exit() def blink_led_2(): # endless loop, on/off for 1 second while True: GPIO.output(19, True) time.sleep(1) GPIO.output(19, False) time.sleep(1) GPIO.output(19, True) time.sleep(1) GPIO.output(19, False) time.sleep(1) GPIO.output(19, True) time.sleep(1) GPIO.output(19, False) time.sleep(1) GPIO.cleanup() time.sleep(3) sys.exit() root = Tk() root.overrideredirect(True) root.geometry("{0}x{1}+0+0".format(root.winfo_screenwidth(), root.winfo_screenheight())) root.focus_set() # <-- move focus to this widget root.configure(background='black') root.config(cursor="none") label_1 = Label(root, text="Welcome to Trick or Trivia", font=("Helvetica", 36), bg="black", fg="white") label_1.grid(columnspan=6,padx=(100, 10)) label_2 = Label(root, text="Answer the question for candy!", font=("Helvetica", 28), bg="black", fg="red") label_2.grid(columnspan=6, pady=5, padx=(100, 10)) label_3 = Label(root, text="Casper is a friendly ____!", font=("Helvetica", 32), bg="black", fg="green") label_3.grid(columnspan=6, pady=5, padx=(100, 10)) button_1 = Button(root, text="Ghost", font=("Helvetica", 36), command=blink_led) button_1.grid(row=4, column=2, pady=5, padx=(100, 10)) button_2 = Button(root, text="Ghast", font=("Helvetica", 36), command=blink_led_2) button_2.grid(row=4, column=4, sticky=W, padx=(100, 10)) button_3 = Button(root, text="Ghoul", font=("Helvetica", 36), command=blink_led_2) button_3.grid(row=5, column=2, pady=5, padx=(100, 10)) button_4 = Button(root, text="Gremlin", font=("Helvetica", 36), command=blink_led_2) button_4.grid(row=5, column=4, sticky=W, padx=(100, 10)) label_4 = Label(root, text="Correct Answer = 3 Pieces", font=("Helvetica", 20), bg="black", fg="green") label_4.grid(columnspan=6, padx=(100, 10)) label_5 = Label(root, text="Incorrect Answer = 1 Piece", font=("Helvetica", 20), bg="black", fg="red") label_5.grid(columnspan=6, padx=(100, 10)) root.mainloop()
Copy and paste this code into a new file on your Raspberry Pi’s desktop, or from the command line enter the following commands.
cd Desktop
sudo nano TrickorTriviaQuiz.py
Then right click to paste the copied code into the new file, and exit nano.
Setting up the LEDs
Follow the diagram above and wire up your LEDs to GPIO pins 19 and 26 . Since the Pi’s GPIO pins output a 3.3v signal, you do not need resistors if you are using a single red, green LEDs.
Running the program
Now from the desktop, open the terminal (if you were not in the GUI or pasting the code remotely. With the terminal open, navigate to the desktop directory and enter the following command to run your new quiz program.
sudo nano TrickorTrivia.py
The trivia screen should pop up like in the video below. If you select a correct answer, then the green LED will illuminate and flash three times. If an incorrect answer is selected, then the red LED will illuminate and flash three times.
The LEDs used in this tutorial are just for troubleshooting purposes, and are not part of the kit. You can use any LEDs you have laying around, or two of the red 10mm LEDs included in the kit. In a future update, I will show you how to write a function that rotates a servo via the GPIO pins and we will replace the blink_led calls with this. For now this proves that the quiz script works, and that we are okay moving forward with the next step in the project. Check back next week for another update, and until then, Hack the World and Make Awesome!
Project Introduction
Building The Trivia Interface
- Interfacing Ambient and Triggered Audio Events
- Building The Candy Dispenser & Servo Coding
- Carve Foam Tombstone
- October 24th - Assembly and Testing
- October 28th - Project Wrap-up