Building an easy GUI (graphical user interface) with Python | by Brunna Torino | Towards Data Science

If you are not a software engineer, you may work with people that don’t know any programming. However, your Python solutions can increase the team’s efficiency and reduce workload tenfold. But what if you need to teach others how to execute it too? Using the terminal or other user interfaces may look too foreign and complicated for many.
In this tutorial, I will go through the process of creating a GUI for your Python script. We will use the Python package Gooey, which will make it extremely easy to design beautiful and simple user interfaces that look familiar to anyone with a computer. You will need:
Python and IDLEDesigning Skills (just kidding, this package really is that amazing)
Python and IDLE
Designing Skills (just kidding, this package really is that amazing)
We will run through an example where I want to open a GUI window where the end user can search for restaurants in his area and get a “recommendation” from the program.
It’s a simple idea, but it serves well to showcase the cool things Gooey can do. Aside from this, I have also found Gooey extremely useful for uploading files, running them through a script, and automatically downloading them again into a designated folder in your computer. You can do that by adding
widget="FileChooser"
into one your arguments!
Here’s how my GUI ended up looking:
Gooey is not designed to work with Jupyter, so I used IDLE for this project. If you have Python installed, you should already have IDLE as well. To install Gooey, you should run in your terminal (OS) /command prompt (Windows):
pip install Gooey
Depending on whether you have more Python 2 and 3 installed in your computer, you would need to run:
pip3 install Gooey ## if you want to use python 3
We also need to instal the wxPython package so that Gooey can use it to create our interfaces. Learning more about wxPython is a great idea if you want to build an interface from scratch.
pip install wxPython==4.0.7 ## type pip3 instead to use python 3
If you haven’t used IDLE before, you should also pip install all the other packages you will use in your script (such as pandas, numpy, xlsxwriter etc...)
The GUI script looks very much like a normal Python script, but there is some structure to be mindful of. We will start with imports:
import pandas as pdfrom gooey import Gooey, GooeyParserimport numpy as npfrom random import randint
We will start the script by defining a few parameters:
# the program name will show on your GUI window# you can also set the sizing of the window (default_size)# you can choose the color of your program using HEX codes # (header_bg_color) and (body_bg_color)# the (navigation) parameter refers to the design of the window@Gooey(program_name="robot restaurant recommendations",       default_size=(710, 700),       navigation='TABBED',        header_bg_color = '#FF0000',       body_bg_color = '#FFF900')
Our script will be structured in functions, starting with the function that will define how the window looks like and what information we want to collect from the user.
Our first function will define the parser and pass on the window arguments.
def parse_args():    parser = GooeyParser()
Our first argument group will be restaurant price category — “how fancy are we feeling today?” , and give the user five choices from $ to $$$$ and a “surprise me” option that will return a random choice.
# first we create a section of window called Priceprice_group = parser.add_argument_group('Price')price_group.add_argument("prices",        metavar='How fancy are we feeling today?',        action = 'store',        choices=['$', '$$','$$$','$$$$','surprise me'],        default='$')# we should set a default value if no option is chosen
Next, we need to know where they are so we can make recommendations for restaurants nearby. We’re calling that group postcode_group. We are also adding a validator in the end of the argument. The validator will evaluate the user input, and if the length of the postcode given is not 6 digits, it is not going to let the user start the program (very important feature!). It can even output a message in red as to what needs to be fixed.
# first we create a section of window called Postcodepostcode_group = parser.add_argument_group('Postcode')postcode_group.add_argument('postcode',                    metavar='What is your postcode?',                    action='store',                    help = "6 digit postcode",                    gooey_options={                         'validator': {                             'test': 'len(user_input) == 6',                             'message': 'robot needs a 6 digit postcode!'                             }                         })
Keep in mind that both the metavar and the .add_argument_group name will appear on your GUI window, so keep their titles as simple and explanative as possible.
The argument .add_argument_group will be shown in bold in the window as a section title i.e. Postcode
Metavar will appear immediately before the end user input and is used to explain what is wanted as an input i.e. What is your postcode?
And we end the function specifying what we want it to return:
args = parser.parse_args()return args
In general, the structure of the Gooey GUI will look somewhat like this:
import gooey#all other imports @Gooey(program_name="Restaurants",default_size=(710,700),navigation='TABBED', all other parameters...)def parse_args():    parser = GooeyParser() group1 = parser.add_argument_group('group 1') group1.add_argument(metavar='a message to the user',                    action='store',                    help = "help the user understand what you want")# add as many groups and arguments as necessary # specify the return of the function args = parser.parse_args() return argsdef my_second_function():# your python script here return something# as many functions as necessary# time to call the functions and execute the scriptif __name__ == '__main__':# execute the window    args = parse_args()    my_second_function(variables)    ## and so on...# THE END
There are many widgets, customisable elements and actions that are available on Gooey. With the restaurant examples, we’ll be applying a few of them but make sure to check their GitHub for all the endless possibilities!
As explained before, this is our parse_args function:
import pandas as pdfrom gooey import Gooey, GooeyParserimport numpy as npfrom random import randint@Gooey(program_name="robot restaurant recommendations",       default_size=(710, 700),       navigation='TABBED', header_bg_color = '#FF0000',       body_bg_color = '#FFF900')def parse_args():    parser = GooeyParser()    price_group = parser.add_argument_group('Price')    price_group.add_argument("prices",        metavar='How fancy are we feeling today?',        action = 'store',        choices=['$', '$$','$$$','$$$$','surprise me'],        default='$')    postcode_group = parser.add_argument_group('Postcode')    postcode_group.add_argument('postcode',                    metavar='What is your postcode?',                    action='store',                    help = "6 digit postcode",                    gooey_options={                         'validator': {                             'test': 'len(user_input) == 6',                             'message': '6 digit postcode!'                             }                         })args = parser.parse_args()    return argsif __name__ == '__main__':    args = parse_args()    prices = args.prices    postcode = args.postcode
But as of now, it’s not returning anything to the user. In order to do that, we need to create more functions. Let’s start by writing one for matching the postcode given with the postcodes we have on file (we will define these variables and import files on the if __name__ == ‘__main__’: function)
The match_postcode function will take in the postcode and a data frame, clean the column “zipcode” of the data frame from any whitespaces (in order to avoid any mismatches between user input and postcodes), filter the data frame by only the rows that match the user postcode, and return it so that we can use that dataframe in the next function!
def match_postcode(postcode,df):    df.zipcode = df.zipcode.str.replace("\s","")    restaurants = df[df['zipcode'] == postcode]    return restaurants
Remember we had choices for prices? Whenever you pass that argument on Gooey, you will probably need to make an if statement to define the different outcomes of each choice. For this program, we only have to generate a random price if the user picks “surprise me” so for this example, I managed to get away with only one if statement.
def match_price(prices, restaurants):    if prices == 'surprise me':        price_len = randint(1,4)    else:        price_len = len(prices)    print(price_len)    restaurants['length'] = restaurants['prices'].str.len()    restaurants = restaurants[restaurants['length'] == str(price_len)]    return restaurants
Next, we would like Gooey to show us the recommendations in a pop-up window that will call the end user’s attention. So we define the next two functions: one for when restaurants are found within the parameters, and another for when we can’t recommend anything. Here, we are importing wx locally to avoid it from interfering with Gooey.
This was a somewhat custom function that uses wxPython to customize Gooey, but it is also available on the Gooey repository.
def show_recommendations(message):    import wx    app = wx.App()    dlg = wx.MessageDialog(None, message, 'What about this one?', wx.ICON_INFORMATION)    dlg.ShowModal()def show_error_message(message):    import wx    app = wx.App()    dlg = wx.MessageDialog(None, message, 'Whoops', wx.ICON_INFORMATION)    dlg.ShowModal()
And last, but not least, we need to call all these functions into action:
First, we define the variables prices and postcode that we stored from the user in the start of the script. Next, we read (globally) a data frame which contains data from restaurants in Amsterdam obtained through the Yelp API (check out my other articles for more on that!). After, we have to call the match_ functions and assign their return value to a new data frame.
if __name__ == '__main__':    args = parse_args()    prices = args.prices    postcode = args.postcode        df = pd.read_excel("yelp-ams.xlsx")    df2 = match_postcode(postcode,df)    df3 = match_price(prices, df2)
And in order to show the recommendations to the user, we transform the column into a list called recommendations (and then to a dictionary to delete any duplicates) and then back into a list. If the list is not empty, we call the show_recommendations function, and if it is, we call the show_error_message function!
recommendations = df3.name.tolist()    mylist = list( dict.fromkeys(recommendations) )    if mylist != []:        for restaurant in mylist:            show_recommendations(restaurant)    else:        show_error_message('Sorry! No restaurants found')
If you have made it to here, thank you for reading! Make sure to check out the Gooey Github to learn more about all its functionalities, which I only touched upon with this tutorial. Hope this GUI can help you and your co-workers!