Introduction Perhaps you just started learning Python and have built one or two simple applications. Congratulations! But, now what? Well, how about building a graphical user interface ("GUI") to interact with your shiny, new Python app? There are several options to build GUI applications with Python, including and . This guide, however, will introduce you to . PyQt wxPython Tkinter , which stands for "Tk interface", is Python's standard for building GUIs and is included with the . It is a binding to the , a free, open-source library of GUI that can be used to build graphical interfaces in a variety of programming languages. Tkinter Python Standard Library Tk GUI toolkit widgets This tutorial walks through the design of a basic GUI application that displays the local time in a timezone selected by the user. The steps build the application progressively and describe some of the key concepts when working with , including the layout of GUI elements, capturing user input, and binding GUI elements to callback methods. Tkinter Prerequisites To complete this guide, you will need to have: . Installed Python Installed . pip Installed the timezone library. The library can be installed using . pytz pip pip install pytz This guide will use the terminology and interchangeably. root window main window Step 1 - Create a New Python Application and Set Required Imports Create a new Python application named and add the following statements to import the , , and modules. timezone.py import Tkinter datetime pytz import tkinter as tk import datetime import pytz Step 2 - Add Timezones This guide incorporates the set of United States timezones which are a small fraction of the timezones supported by . The functionality of the application can be extended by adding additional timezone names. A complete list of timezones available with can be output by running the following command: pytz pytz pytz print(pytz.all_timezones) The set of United States timezones available via is specified as a list. pytz import tkinter as tk import datetime import pytz timezones = ["US/Alaska", "US/Aleutian", "US/Arizona", "US/Central", "US/East-Indiana", "US/Eastern", "US/Hawaii", "US/Indiana-Starke", "US/Michigan", "US/Mountain", "US/Pacific", "US/Samoa"] Step 3 - Create an Instance of the Tk Class Instantiating the class creates a which will serve as the main window of the timezone application's GUI. Tk root window import tkinter as tk import datetime import pytz timezones = ["US/Alaska", "US/Aleutian", "US/Arizona", "US/Central", "US/East-Indiana", "US/Eastern", "US/Hawaii", "US/Indiana-Starke", "US/Michigan", "US/Mountain", "US/Pacific", "US/Samoa"] root = tk.Tk() Step 4 - Set Up the Main Window This step configures the root window, particularly its title, geometry, and resizeability. Step 4a - Setting the Main Window Title The window title is set using the method. title import tkinter as tk import datetime import pytz timezones = ["US/Alaska", "US/Aleutian", "US/Arizona", "US/Central", "US/East-Indiana", "US/Eastern", "US/Hawaii", "US/Indiana-Starke", "US/Michigan", "US/Mountain", "US/Pacific", "US/Samoa"] root = tk.Tk() root.title("Simple Timezone Application") Step 4b - Setting Main Window Width and Height The window width and height values, expressed in pixels, can be assigned to variables to make the code easier to follow. import tkinter as tk import datetime import pytz timezones = ["US/Alaska", "US/Aleutian", "US/Arizona", "US/Central", "US/East-Indiana", "US/Eastern", "US/Hawaii", "US/Indiana-Starke", "US/Michigan", "US/Mountain", "US/Pacific", "US/Samoa"] root = tk.Tk() root.title("Simple Timezone Application") window_width = 450 window_height = 175 Step 4c - Calculating Main Window Center Position The main window can be placed anywhere on the screen, e.g. in the center, in the upper-left corner, etc. A centered window provides a nice "look" and the center position for the main window can be determined using the root window's and methods along with some simple math. These two methods return the screen width and screen height which are used to calculate the appropriate (x,y) coordinates to center the main window. As with the window width and height, the center position values can also be assigned to variables to make the code more readable. winfo_screenwidth() winfo_screenheight() import tkinter as tk import datetime import pytz timezones = ["US/Alaska", "US/Aleutian", "US/Arizona", "US/Central", "US/East-Indiana", "US/Eastern", "US/Hawaii", "US/Indiana-Starke", "US/Michigan", "US/Mountain", "US/Pacific", "US/Samoa"] root = tk.Tk() root.title("Simple Timezone Application") window_width = 450 window_height = 175 screen_width = root.winfo_screenwidth() screen_height = root.winfo_screenheight() center_x = int((screen_width - window_width)/2) center_y = int((screen_height - window_height)/2) Note that the and values are also expressed in pixels and is used to ensure both calculated values are integers. center_x center_y int Step 4d - Setting Main Window Geometry The window geometry, which specifies the size and position of the main window, is set using the root window's method. To make things simple, you can use a formatted sting literal, or , that will interpolate the geometry variable expressions at runtime. As per the following code block, the literal is passed to the method as a parameter. root geometry f-string f-string geometry import tkinter as tk import datetime import pytz timezones = ["US/Alaska", "US/Aleutian", "US/Arizona", "US/Central", "US/East-Indiana", "US/Eastern", "US/Hawaii", "US/Indiana-Starke", "US/Michigan", "US/Mountain", "US/Pacific", "US/Samoa"] root = tk.Tk() root.title("Simple Timezone Application") window_width = 450 window_height = 175 screen_width = root.winfo_screenwidth() screen_height = root.winfo_screenheight() center_x = int((screen_width - window_width)/2) center_y = int((screen_height - window_height)/2) root.geometry(f"{window_width}x{window_height}+{center_x}+{center_y}") Step 4e - Setting Main Window Resizability The resizability of the root window along its x and y axes can be set via the root window's method. The method accepts and parameters, in that order. A or non-zero value for either parameter specifies that the main window is resizable along the associated axis. A or value for either parameter specifies that the main window is not resizable along the given axis. The timer application will use a value of for both height and width to prevent the main window from being resized. resizable resizable height width True False 0 False import tkinter as tk import datetime import pytz timezones = ["US/Alaska", "US/Aleutian", "US/Arizona", "US/Central", "US/East-Indiana", "US/Eastern", "US/Hawaii", "US/Indiana-Starke", "US/Michigan", "US/Mountain", "US/Pacific", "US/Samoa"] root = tk.Tk() root.title("Simple Timezone Application") window_width = 450 window_height = 175 screen_width = root.winfo_screenwidth() screen_height = root.winfo_screenheight() center_x = int((screen_width - window_width)/2) center_y = int((screen_height - window_height)/2) root.geometry(f"{window_width}x{window_height}+{center_x}+{center_y}") root.resizable(False, False) Step 5 - Specify a Grid for Widget Layout can be arranged on top of the main window in different ways using . Generally, widgets can be arranged in 3 ways: Widgets geometry managers using the method. The geometry manager organizes widgets into blocks before they are added to a root window or parent widget. An imperfect but perhaps useful way to think about is to think about adding grocery items to a grocery bag. The items aren't necessarily added to pre-defined locations in the bag. Rather, they are one after another, using up available space until all items have been put in the bag. The geometry manager works in a similar way. Packed pack() pack() packing packed pack() using the method. The geometry manager widgets into specific, pre-defined positions in the root window or parent widget. This geometry manager is obviously useful when building precise GUI layouts. Placed place() place() places Arranged as a using the method. Grids are 2-dimensional row/column tables. Widgets are added to a grid by specifying the particular row and column where the widget should be placed. Grids are setup using the root window's and methods. Each of these methods has an and a attribute. The attribute specifies the position of the column or row using a starting basis of . The attribute specifies the size of a particular column or row relative to other columns. For example, if has weight and has weight , then will be 3 times as large as . grid grid() columnconfigure rowconfigure index weight index 0 weight column 0 1 column 1 3 column 1 column 0 Grids are handy when placing elements next to each other and they are relatively simple to implement. The timezone application will use a main window grid divided into 4 equal columns. import tkinter as tk import datetime import pytz timezones = ["US/Alaska", "US/Aleutian", "US/Arizona", "US/Central", "US/East-Indiana", "US/Eastern", "US/Hawaii", "US/Indiana-Starke", "US/Michigan", "US/Mountain", "US/Pacific", "US/Samoa"] root = tk.Tk() root.title("Simple Timezone Application") window_width = 450 window_height = 175 screen_width = root.winfo_screenwidth() screen_height = root.winfo_screenheight() center_x = int((screen_width - window_width)/2) center_y = int((screen_height - window_height)/2) root.geometry(f"{window_width}x{window_height}+{center_x}+{center_y}") root.resizable(False, False) root.columnconfigure(0, weight=1) root.columnconfigure(1, weight=1) root.columnconfigure(2, weight=1) root.columnconfigure(3, weight=1) Step 6 - Add Widgets In this step, widgets are added to the main window grid defined in Step 4. The timezone application will use 3 widgets: TKinter Label Listbox Button Tkinter's and widgets are exactly as they are named. The widget allows the programmer to display text (i.e. a label), and the widget is used to display buttons. The widget is used to select a value (or values) from a list of options. Label Button Label Button Listbox Of course, there are other widgets beyond the 3 that are listed above, such as the , , and widgets. The formal is a useful resource to learn more about the widgets available through the Tk toolkit. SpinBox Entry Message Tk commands documentation Step 6a - Create Widgets An instance of each widget class is created for a given widget used with the GUI design. The timezone application uses 4 widget instances: 2 widgets, 1 widget, and 1 widget. Label Listbox Button import tkinter as tk import datetime import pytz timezones = ["US/Alaska", "US/Aleutian", "US/Arizona", "US/Central", "US/East-Indiana", "US/Eastern", "US/Hawaii", "US/Indiana-Starke", "US/Michigan", "US/Mountain", "US/Pacific", "US/Samoa"] root = tk.Tk() root.title("Simple Timezone Application") window_width = 450 window_height = 175 screen_width = root.winfo_screenwidth() screen_height = root.winfo_screenheight() center_x = int((screen_width - window_width)/2) center_y = int((screen_height - window_height)/2) root.geometry(f"{window_width}x{window_height}+{center_x}+{center_y}") root.resizable(False, False) root.columnconfigure(0, weight=1) root.columnconfigure(1, weight=1) root.columnconfigure(2, weight=1) root.columnconfigure(3, weight=1) # Instance of Label widget class for selection prompt. select_timezone_label = tk.Label(root, text="Please select a timezone.") # Instance of Listbox class for selection of timezone from list. list_var = tk.Variable(value=timezones) select_timezone_listbox = tk.Listbox(root, listvariable=list_var, height=1) # Instance of Button class to get the local time in the selected timezone. select_timezone_button = tk.Button(root, text="Get Time") # Second instance of the Label class to display the local time in the selected timezone. time_label = tk.Label(root, text="") As seen in the code block above, each widget instance is "attached" to the window. The widget also requires instantiation of a special Tkinter class which is used to supply the widget with the list of timezone options that the user can select from via the attribute. The text displayed by each widget can be configured using the attribute. The text value for is left blank since it will be set dynamically each time the user "gets" the time for a selected timezone. root Listbox Variable() Listbox listvariable Label text time_label Step 6b - Place Widgets on the Grid The widget instances created in Step 5a can be placed on the grid defined in Step 4 using the method. The timezone application will use the following method attributes to define the layout: grid() grid() : Specifies the particular column where the widget will be placed using a basis. column 0 : Specifies the particular row where the widget will be placed using a basis. row 0 : Specifies that widget should span the specified number of columns. For example, a widget with a value will span 3 columns even if the widget itself is smaller than 3 columns. columnspan columnspan=3 : Tkinter's default behavior is to place a widget in the horizontal and vertical center of the cell (i.e. particular row/column position) where it is placed. This default behavior can be overridden using the attribute which uses compass-like values, including , , , , , , , and , to align the widget at a particular location the widget's cell. For example, specifies that the widget should be aligned to the west corner of its grid cell. Analogously, specifies that the widget should be aligned to the east corner of its grid cell. sticky sticky NW N NE W E SW S SE within sticky=tK.W sticky=tK.E , : The and attributes are used to add x-axis and y-axis padding respectively with values specified in pixels. Naturally, padding can provide a more professional looking GUI ensuring that widgets do not "bump up" directly against the edges of the root window or other widgets. padx pady padx pady import tkinter as tk import datetime import pytz timezones = ["US/Alaska", "US/Aleutian", "US/Arizona", "US/Central", "US/East-Indiana", "US/Eastern", "US/Hawaii", "US/Indiana-Starke", "US/Michigan", "US/Mountain", "US/Pacific", "US/Samoa"] root = tk.Tk() root.title("Simple Timezone Application") window_width = 450 window_height = 175 screen_width = root.winfo_screenwidth() screen_height = root.winfo_screenheight() center_x = int((screen_width - window_width)/2) center_y = int((screen_height - window_height)/2) root.geometry(f"{window_width}x{window_height}+{center_x}+{center_y}") root.resizable(False, False) root.columnconfigure(0, weight=1) root.columnconfigure(1, weight=1) root.columnconfigure(2, weight=1) root.columnconfigure(3, weight=1) # Instance of Label widget class for selection prompt. select_timezone_label = tk.Label(root, text="Please select a timezone.") # Instance of Listbox class for selection of timezone from list. list_var = tk.Variable(value=timezones) select_timezone_listbox = tk.Listbox(root, listvariable=list_var, height=1) # Instance of Button class to get the local time in the selected timezone. select_timezone_button = tk.Button(root, text="Get Time") # Second instance of the Label class to display the local time in the selected timezone. time_label = tk.Label(root, text="") # Place widgets on grid. select_timezone_label.grid(column=0, row=0, columnspan=4, sticky=tk.W, padx=10, pady=10) select_timezone_listbox.grid(column=0, row=1, columnspan=3, sticky=tk.EW, padx=10, pady=10) select_timezone_button.grid(column=4, row=1, sticky=tk.E, padx=10, pady=10) time_label.grid(column=0, row=4, columnspan=4, sticky=tk.W, padx=10, pady=10) Step 7 - Create Get Time Button Callback Method A callback method needs to be defined to handle events when the user click on the button created in Step 5. select_timezone_button Step 7a - Binding the Get Time Button Before defining the callback logic, it is helpful to first bind the button to the method name that will eventually encompass the callback code. The method can be used in conjunction with a function to bind the to the specified callback method. Also, note that the function is used to pass references to the and widgets as callback parameters. These callback parameters are actually necessary since and are within the global scope. However, it is arguably useful to demonstrate how arguments can be passed to the callback function. bind() lambda select_timezone_button lambda select_timezone_listbox time_label not select_timezone_listbox time_label import tkinter as tk import datetime import pytz timezones = ["US/Alaska", "US/Aleutian", "US/Arizona", "US/Central", "US/East-Indiana", "US/Eastern", "US/Hawaii", "US/Indiana-Starke", "US/Michigan", "US/Mountain", "US/Pacific", "US/Samoa"] root = tk.Tk() root.title("Simple Timezone Application") window_width = 450 window_height = 175 screen_width = root.winfo_screenwidth() screen_height = root.winfo_screenheight() center_x = int((screen_width - window_width)/2) center_y = int((screen_height - window_height)/2) root.geometry(f"{window_width}x{window_height}+{center_x}+{center_y}") root.resizable(False, False) root.columnconfigure(0, weight=1) root.columnconfigure(1, weight=1) root.columnconfigure(2, weight=1) root.columnconfigure(3, weight=1) # Instance of Label widget class for selection prompt. select_timezone_label = tk.Label(root, text="Please select a timezone.") # Instance of Listbox class for selection of timezone from list. list_var = tk.Variable(value=timezones) select_timezone_listbox = tk.Listbox(root, listvariable=list_var, height=1) # Instance of Button class to get the local time in the selected timezone. select_timezone_button = tk.Button(root, text="Get Time") # Second instance of the Label class to display the local time in the selected timezone. time_label = tk.Label(root, text="") # Place widgets on grid. select_timezone_label.grid(column=0, row=0, columnspan=4, sticky=tk.W, padx=10, pady=10) select_timezone_listbox.grid(column=0, row=1, columnspan=3, sticky=tk.EW, padx=10, pady=10) select_timezone_button.grid(column=4, row=1, sticky=tk.E, padx=10, pady=10) time_label.grid(column=0, row=4, columnspan=4, sticky=tk.W, padx=10, pady=10) select_timezone_button.bind("<Button>", lambda e, args=[select_timezone_listbox, time_label]: get_timezone_time(e, args)) Step 7b - Defining Callback Logic The callback logic to handle button click events is defined below. def get_timezone_time(e, args): select_timezone_listbox = args[0] time_label = args[1] selection_index = select_timezone_listbox.curselection() selected_timezone = select_timezone_listbox.get(selection_index) now_time = datetime.datetime.now() tz_time = now_time.astimezone(pytz.timezone(selected_timezone)) tz_formatted = tz_time.strftime("%H:%M:%S") time_label.configure({"text": f"The time in {selected_timezone} is {tz_formatted}."}) time_label.update() The and methods are used to retrieve the user's selected timezone from the reference to the widget. The user's current time is then converted into the selected timezone time. Finally, the method is used to change the attribute of the reference to the with the local time in the selected timezone. Note that the method is used to "force" the widget to update itself with the new text value. curselection() get() select_timezone_listbox configure text time_label update() time_label Step 8 - Complete and Run the Application The root window's method is applied as the last line of code. The method will run the GUI in an infinite loop until the user exits. mainloop() mainloop() The completed code is as follows: import tkinter as tk import datetime import pytz timezones = ["US/Alaska", "US/Aleutian", "US/Arizona", "US/Central", "US/East-Indiana", "US/Eastern", "US/Hawaii", "US/Indiana-Starke", "US/Michigan", "US/Mountain", "US/Pacific", "US/Samoa"] def get_timezone_time(e, args): select_timezone_listbox = args[0] time_label = args[1] selection_index = select_timezone_listbox.curselection() selected_timezone = select_timezone_listbox.get(selection_index) now_time = datetime.datetime.now() tz_time = now_time.astimezone(pytz.timezone(selected_timezone)) tz_formatted = tz_time.strftime("%H:%M:%S") time_label.configure({"text": f"The time in {selected_timezone} is {tz_formatted}."}) time_label.update() root = tk.Tk() root.title("Simple Timezone Application") window_width = 450 window_height = 175 screen_width = root.winfo_screenwidth() screen_height = root.winfo_screenheight() center_x = int((screen_width - window_width)/2) center_y = int((screen_height - window_height)/2) root.geometry(f"{window_width}x{window_height}+{center_x}+{center_y}") root.resizable(False, False) root.columnconfigure(0, weight=1) root.columnconfigure(1, weight=1) root.columnconfigure(2, weight=1) root.columnconfigure(3, weight=1) # Instance of Label widget class for selection prompt. select_timezone_label = tk.Label(root, text="Please select a timezone.") # Instance of Listbox class for selection of timezone from list. list_var = tk.Variable(value=timezones) select_timezone_listbox = tk.Listbox(root, listvariable=list_var, height=1) # Instance of Button class to get the local time in the selected timezone. select_timezone_button = tk.Button(root, text="Get Time") # Second instance of the Label class to display the local time in the selected timezone. time_label = tk.Label(root, text="") # Place widgets on grid. select_timezone_label.grid(column=0, row=0, columnspan=4, sticky=tk.W, padx=10, pady=10) select_timezone_listbox.grid(column=0, row=1, columnspan=3, sticky=tk.EW, padx=10, pady=10) select_timezone_button.grid(column=4, row=1, sticky=tk.E, padx=10, pady=10) time_label.grid(column=0, row=4, columnspan=4, sticky=tk.W, padx=10, pady=10) # Bind button to callback. select_timezone_button.bind("<Button>", lambda e, args=[select_timezone_listbox, time_label]: get_timezone_time(e, args)) root.mainloop() When the application is launched, the GUI should look like: To use the application, click the list box and use your keyboard’s up/down cursor keys to scroll through the timezone options. Click on the button to display the current time in your selected timezone. Get Time Next Steps Congratulations on building your first Python GUI application with ! As mentioned in the Introduction, this guide was designed to introduce you to a few basic concepts. The and the references mentioned earlier are two fantastic resources to help you learn about more advanced Tkinter features and functionality. Tkinter Python Standard Library documentation Tk commands documentation Tkinter GUI applications are sometimes criticized as having a non-native look-and-feel. That may be true. But the toolkit widgets are highly configurable and Tkinter GUI applications can be built relatively quickly without the need to install any external Python packages. Beyond formal documentation, there are innumerable tutorials available via the Internet to learn about building more sophisticated GUI applications with Tkinter.