gui zero – making Python GUIs really simple

The code in this post has been updated to reflect the changes in guizero version 0.4.

When I was a teacher, I found it frustrating that it was not at all easy for students to create GUIs using Python. I used tkinter a little bit with my GCSE class and they instantly loved creating GUIs, but became frustrated because simple things were so difficult to do. After talking with some developers about this at Pycon UK, I decided to do something about it, and so I wrote a library called guizero which is meant to be used by children and removes all the unnecessary nasty bits without removing the intellectual requirements. Another thing which is important to me is that guizero is very easy to install and get started with – so there’s a completely no faff installation process, which is a winner if you’re struggling with getting your school techies to install things.

Download guizero and read the documentation here

But, promo flannel aside – is it any good for use in the classroom? On the CAS resources site, Stuart Lucas posted a popular “Book of programming challenges” resource (9651 downloads at the time of writing) so I thought I’d put guizero to the test to see how easy it is to complete a few of his challenges with a GUI and let you see for yourself.

Challenge 1

Write a program that will display a joke. Don’t display the punchline until the reader hits the enter key.
Extension: display the punchline in a different colour.

from guizero import App, Text, PushButton

# Method to call when button pressed
def display_joke():
   punchline.value = "Poke him on"
   
# Set up the app
app = App("Joke teller")

joke = Text(app, "How do you get Pikachu on a bus?")
punchline = Text(app, text="", color="red")
button = PushButton(app, display_joke, text="Display punchline")

app.display()

challenge1

So this was pretty straightforward. I created two pieces of text and a button, with the second piece of text remaining blank until the button is pressed, at which point it fills in the punchline.

Challenge 2

Write a program that will ask you your name. It will then display ‘Hello Name’ where ‘Name’ is the name you have entered.

from guizero import App, Text, TextBox, PushButton

# Method to display the greeting
def display_greeting():
   text.value = "Hello " + name.value

# Set up the app
app = App("Hello machine")

text = Text(app, text="What is your name?")
name = TextBox(app)
button = PushButton(app, display_greeting, text="Greet me")

app.display()

challenge2a challenge2b

This challenge makes use of the get() and set() methods of the Text and TextBox widgets to easily get values typed in and display them elsewhere on the page.

Challenge 3

Write a program to work out the area of a rectangle. Collect the width and height of the rectangle from the keyboard. Calculate the area and display the result.

Extension: Display the volume of a cuboid. See what happens when you don’t type in numbers! Try to explain what has happened and why.

from guizero import App, Text, TextBox, PushButton, error

# Method to calculate the area
def calculate():

    # Validation to check whether they typed in numbers
    if not height.value.isdigit() or not width.value.isdigit():
        error("Input error", "You must type in numbers for height and width")
    # Depth is allowed to be a digit or blank
    elif not depth.value.isdigit() and depth.value != "":
        error("Input error", "You must type in a number for depth")

    # Perform the calculation
    else:
        area = int( height.value ) * int( width.value )
        if depth.value == "":        
            result.value = str(area) + "cm squared"
        else:
            volume = area * int(depth.value)
            result.value = str(volume) + "cm cubed"
        

# Set up the app
app = App("Area and Volume calculator", layout="grid")

width_label = Text(app, text="Width:", grid=[0,0], align="left")
width = TextBox(app, grid=[1,0], align="left", width=30)

height_label = Text(app, text="Height:", grid=[0,1], align="left")
height = TextBox(app, grid=[1,1], align="left", width=30)

depth_label = Text(app, text="Depth:", grid=[0,2], align="left")
depth = TextBox(app, grid=[1,2], align="left", width=30)

button = PushButton(app, calculate, text="Calculate", grid=[1,3])

result_label = Text(app, text="Result:", grid=[0,4], align="left")
result = TextBox(app, grid=[1,4], align="left", width=30)

app.display()

challenge3a challenge3b challenge3c

This challenge was a little tricker. I used a grid layout so make the interface look tidy and line up the boxes properly. However, it wasn’t the GUI part which generated most of the code here, it was the validation. Since the get() methods always return strings I had to cast them to integers to do calculations (but this should already be familiar to students). I also saw a good excuse to use the error() box function to pop up a message if the validation check didn’t pass.

What do you think?

I’d love to hear your thoughts on guizero – whether they are positive or negative. Is this useful for you and your students? Is there something you’d like it to do but it doesn’t currently do? Would you like to see a tutorial on how to do a particular thing? Is there a bug? You can find the project on GitHub and add issues/ideas here, or leave a comment on this post, or tweet me @codeboom with your thoughts.

37 thoughts on “gui zero – making Python GUIs really simple

  1. isdigit() suggests to me that you can only test for a single digit.

    Could you add isinteger(), isfloat(), and isnumber() methods to gui zero? I think they should return false on empty strings, so that you can simplify a lot of your validation.

  2. oh, yes! there needs to be a few more of these.

    as the author of an educational pl designed to be as easy as possible tp learn, the gui is something that is never simple enough– i know there are a few “easy guis” out there, and theyre usually limited but a few more and a good balance might be found. i havent had more than a moments look at yours, but fwiw i think you have the right idea.

  3. I bought an inexpensive 5″ touch screen display for the pi and am using guizero for the user interface. The system sends me an alert email when it sees one of the input pins active. I would like several windows, one for an overview, another for entering email information, another to display a log of the email alerts. The user clicks a PushButton to open the desired window and closes it when done. Did you try having more than one App displaying at the same time? It sort of works for me, but not well. For instance the second App is not able to set the text in a TextBox, and strangely I don’t have to Display() the second App for it to show on the screen.

  4. I have been looking at guizero for a couple of days. I am teaching a middle school python class and want kids to create a choose your own adventure. Can you show an example of using if statements with either dropboxes or textboxes? Thanks.

    1. Something simple. Do you want anything fancier?

      from guizero import *

      def greeting():
      name = tbxName.get()
      if name == “Eric”:
      txtGreeting.set(“Welcome Sir ” + name)
      else:
      txtGreeting.set(“Welcome Mr./Ms. ” + name)

      app = App(title=”Demo 1″)
      tbxName = TextBox(app)
      PushButton(app, text=”GREETING”, command=greeting)
      txtGreeting = Text(app)
      app.display()

  5. I am using guizero in a first semester Intro to Programming course with good success. I have a question that I believe is style and not syntax, because my programs work as expected. If I define a widget that is never referenced, I don’t assign it a name, like:
    Text(app, “A label”)
    Is there a reason to consider this bad style? It creates less clutter in the code.

    1. I never noticed this. I looks like you don’t need a variable if you don’t plan on referencing it later. I am not sure about style, but I think it l looks easier to read. It doesn’t make sense to have variables that don’t do anything.

      I have been teaching guizero for a week. Some of my students have asked if there is a quick way to clear the gui so a new menu doesn’t build off of the information that is already on the page. Is there some method/function that I am not seeing?

      Thanks.

      1. Hi Jeremy. Could you explain more about what you mean by “clear the gui”? By “new menu”, are you referring to the “MenuBar”?

  6. pip installed guizero

    Copied your Challenge 1 code above into 3.6, run gives error:

    NameError: name ‘app’ is not defined.

    Any ideas?

    1. Any errors on this line:
      from guizero import *

      Or try this instead:
      from guizero import App, Text, PushButton

      If there are no errors, then this should work:
      app = App(“Joke teller”)

  7. Thanks for responding Eric. By ‘clear the gui’, I mean that I have students that would like to make a function that would take off all of the previous widgets so they would have a blank/or semi blank GUI. This might be a choose your own adventure that describes a room and has some buttons on which rooms they can enter next. They would like the button to clear the previous information and make room for the information that the next situation would provide. I know how to modify the widgets using a text.set() or picture.set(), but that only replaces something that is already there. My students wanted a blank screen to start fresh.

    I hope this was more clear.

    1. Hi Jeremy,
      If you are using the grid manager, you could replace all the used grid elements with new ones. Would you like a new screen instead? How about this?

      from guizero import *

      def create2():
      window2 = App(title=”screen 2″)
      PushButton(window2, text=”QUIT”, command=window2.destroy)

      window1 = App(title=”screen 1″)
      PushButton(window1, text=”New Window”, command=create2)

      window1.display()

  8. Thank you for making this.

    I can now run the first example without a problem and the 3rd but the second reports eol while scanning string literal on line 5 – and I don’t know what that expression means, text.set(“helloname.get())

    It looks like it is missing a ” but putting it in doesn’t fix it.

    I will need far more # comments on this pieces of code to understand what is going on – but then I haven’t done much in tkinter or pygames etc.

  9. I think she wanted to write:
    text.set(“Hello”+name.get())
    This replaces what the text widget says with whatever the user typed in the textbox called name. She also has it say ‘Hello’ before the the name.

  10. I did about ten tests on an Asus Window10 machine Tkinter works fine, but is a bit cumbersome if you are used to Visual Studio. So I gave Guizero a try by copy-paste and some examples worked fine and others no a lot of errors. So I gave Apjar a try. Excellent so this 75 years old guy with a lot of programming experience decided to use Apjar. But I presume that you install Guizero on a Raspberry it will work fine. The winter I am in Tenerife, but when I am at home I give it a try.

  11. In the Challenge 3, you need to import “error” from guizero in addition to the items already being imported. The example works fine with that addition. (Using Thonny on a Raspberry Pi.)

  12. Hi

    With the new Guizero update finding it difficult to
    center the text to the middle of the GUI.

    I know by default it centers itself to the top of the Gui I am trying to ccenter the
    readout to the middle of the Gui

    Any help appreciated, Thanks.

    import time
    from guizero import App, Text
    def update_writing():

    T1 = (time.strftime(“%H:%M:%S”))
    writing.value = T1
    app.after(200, update_writing)
    app = App(height=800, width=800,bg=”black”)

    writing = Text(app,text=”T1″, color=”green”,size=90,bg=”black”)

    app.after(200, update_writing)
    app.display()

    1. This is my approach. Use a grid layout and put some blank text in grid [0, 0]. The number of blanks moves the display to the right and the size of the text move the display down. The actual display is then built from grid[1, 1].

      Also, if you use app.repeat instead of app.after, you can eliminate the recursive call in the function. Also, slow it down to one repeat per second since you are displaying seconds.

      Eric
      ————————————————————————————————————————————
      import time
      from guizero import App, Text

      def update_writing():
      t1 = time.strftime(‘%H:%M:%S’)
      writing.value = t1

      app = App(height=400, width=800, bg=’black’, layout=’grid’)

      left_margin = 5
      top_margin = 90

      Text(app, text=left_margin * ‘ ‘, size=top_margin, color=’black’, bg=’black’, grid=[0, 0])

      writing = Text(app, text=’T1′, color=’green’, size=90, bg=’black’, grid=[1, 1])

      app.repeat(1000, update_writing)
      app.display()

  13. Thanks Eric Mailman, that work well, I had been trying for ages looking for examples.
    I would imagine having a back ground knowledge in Tinker would go a long way.
    The basic program I had before was a listing from a site which encourage me to
    pursue Gui zero.
    Now I can manipulate the output.

    Thanks once again.

    1. You’re welcome Alan.

      Yes, you can get a greater understanding of guizero if you study tkinter, but I would not say it’s necessary. Check out the link at the top of this page for the most complete guizero documentation I have found. You can also search this forum for “guizero” for more information: https://www.raspberrypi.org/forums/viewforum.php?f=32

      The primary use of the grid layout is to build apps with rows and columns. Note that the grid spec is [column(x), row(y)], rather than [row, column] as you might expect, with [0, 0] in the upper left corner. Here’s a simple example:
      ———————————————————————————————————————————
      import time
      from guizero import App, Text

      def update_writing():
      t1 = time.strftime(‘%H:%M:%S’)
      d1 = time.strftime(‘%b %d %Y’)
      writing1.value = t1
      writing2.value = d1

      app = App(height=400, width=900, bg=’black’, layout=’grid’)

      left_margin = 5
      top_margin = 50

      Text(app, text=left_margin * ‘ ‘, size=top_margin, color=’black’, bg=’black’, grid=[0, 0])

      Text(app, text=’Time: ‘, color=’green’, size=60, bg=’black’, align=’right’, grid=[1, 1])
      writing1 = Text(app, text=”, color=’green’, size=60, bg=’black’, align=’left’, grid=[2, 1])

      Text(app, text=’Date: ‘, color=’green’, size=60, bg=’black’, align=’right’, grid=[1, 2])
      writing2 = Text(app, text=”, color=’green’, size=60, bg=’black’, align=’left’, grid=[2, 2])

      app.repeat(1000, update_writing)
      app.display()

      1. You read my thoughts, that was going to be my next project, having two displays running at
        the same time.
        I like the grid system it works well.
        Having examples to go buy opens up a whole new world of possibilities.

        Once again thanks.

  14. Hi,
    I find guizero very user friendly and useful. Thank you very much for developing this module.
    I need help to use the listbox with a search filter, which would update the listbox.items accordingly.
    Can you show a simple example how to use the “upate_command()” to update the listbox items.
    I was able to do this using PySimpleGui, but i prefer the clean guizero interface.
    Thank you.
    Ram

  15. @Eric Mailman, please can you help me, I am facing difficulties when it come to import images/ pictures from guizero. GIF, PNG, citing wrong path despite using this code: from guizero import App, Picture app = App() picture = Picture(app,image=”test.gif” app.display()

    1. Hi Simba. Ninapenda mswahili anatumia guizero.

      I am no expert. However, that error usually occurs when your file that your image is on isn’t in the same folder as your python program using guizero. So, make sure that your python program and image are both in the same file folder. If you are receiving this message and they are in the file folder, I am not sure what is happening. I hope this helps.

      1. I agree with Jeremy that this is the most likely cause. You can put the image in a different directory if you include path information in the file name. If you do, use “/” instead of “\” to make things easier.
        Here’s one other possibility. If you moved your python program around in your project directory structure, it’s possible that the Run Configuration settings are wrong. If you are using PyCharm, you can build a new Run Configuration by right-clicking on the program’s tab and selecting “Run ‘your program name'”.

  16. Hello,
    Is it possible to set a specific window position on startup by using gui-zero, in a similar way it is done with Tkinter?
    Thanks for your attention.
    Regards,

    1. Yes you can by using some tk magic. I needed to create an application with multiple windows which had to be aligned as a stack. The location and size were key to making that work.

      Below is an example which is derived from a guizero example. Both of the windows (app and window) have their size and position set by the .tk.geometry() function. The area to be careful of is the format of the geometry parameter which is a string in the form: : (‘width x height + X coordinate + Y coordinate’) but with no spaces.

      from guizero import App, Window, PushButton

      def open_window():
      window.show()

      def close_window():
      window.hide()

      app = App(title=”Main window”, width=300, height=300, bg = “honeydew” )
      app.tk.geometry(‘520×400+350+200’)

      window = Window(app, title=”Second window”, width=300, height=300, bg = “#ffff00″ )
      window.tk.geometry(‘520×400+350+200’)
      window.hide()

      open_button = PushButton(app, text=”Open”, command=open_window)
      close_button = PushButton(window, text=”Close”, command=close_window)

      app.display()

Leave a comment