In the past when I could only see sharp, I built applications that ran on the web, mobile phones, and desktop with C# using mono for cross-platform compatibility. In my current role, I still use C# but I have added python and typescript to my developer toolkit. Python is my personal favorite language because it's powerfully concise and very easy to learn. I use python to automate lots of tasks at work. I have a script that is used internally to simulate internet of things devices, this script is necessary for the business team to demonstrate functionality during sales pitch meetings. I decided to wrap the functionality of this script with a GUI and then distribute it as an application so it can be used by anyone with access to the application. In this post, I want to share how fun and easy building a simple desktop application with Python is. We will build a simple tool using tkinter for the UI and then use pyinstaller to deploy the application as an executable.
Why Tkinter GUI
Python has a wide range of GUI frameworks but the most widely used is Tkinter or Tk interface. The module offers a wide range of widgets that can be used to develop GUI applications much faster and easier than other python GUI modules. It also has a modern ttk module which has GUI improvements to the existing tk modules.
Understanding How Tkinter Wraps Tcl/Tk
The Tkinter package is the standard Python interface to the Tcl GUI toolkit. When the application uses Tkinter’s classes and methods, internally Tkinter is assembling strings representing Tcl/Tk commands and executing these commands in the Tcl interpreter attached to the application’s Tk instance. The Tcl interpreter will then call into the Tk and/or Ttk packages, which will, in turn, make calls to the facilities of the underlying operating system, i.e., Xlib on Unix/X11, Cocoa on macOS, GDI on Windows.
GUI Design
If you were to sketch the application out, it would look have a top-level app page and other pages or views to handle other functionalities. The Tkinter module has many widgets like frames, labels, buttons, entries, etc we will use a couple of these widgets in our application GUI.
Frame
A frame is a widget that displays as a simple rectangle. Frames help to organize your user interface, often both visually and at the coding level. Frames often act as parent widgets for a geometry manager-like grid, which manages the child widgets contained within the frame.
Label
A label is the widget typically used to display text or images, users will view but not interact with it. Labels are used to identify controls or other parts of the user interface, provide textual feedback or results, etc.
Button
A button, is a bit different from a frame, or label. Buttons are for user interactions. Users press a button to perform an action. Like labels, they can display text or images but accept additional options to change their behavior.
Entry
An entry widget presents users with a single-line text field where they can type in a string value. A string value can be used to represent things like: a name, a city, a password, social security number, etc.
Grid
Tkinter has geometry manager that arranges widgets. The grid geometry manager arranges widgets in columns and rows inside another window. widgets are assigned a column
number and a row
number. These indicate each widget's position relative to other widgets. Widgets in the same column are above or below each other. Those in the same row are to the left or right of each other.
Column and row numbers must be positive integers (i.e., 0, 1, 2, ...). You don't have to start at 0 and can leave gaps in column and row numbers (e.g., column 1, 2, 10, 11, 12, 20, 21). This is useful if you plan to add more widgets in the middle of the user interface later.
The width of each column will vary depending on the width of the widgets contained within the column. Ditto for the height of each row. This means when sketching out your user interface and dividing it into rows and columns, you don't need to worry about each column or row being equal width.
TK Event Loop
The event loop continually processes events, pulled from the system event queue, usually multiple times in a second. It watches for mouse, keyboard or touch events, invoking command callbacks and event bindings as needed. All screen updates are processed in the event loop. To change the text of a label widget, that change doesn't appear onscreen immediately. Instead, the widget notifies Tk that it needs to be redrawn. Later on, in between processing other events, Tk's event loop will ask the widget to redraw itself. All gui updates occurs in the event loop. The change appears to happen immediately because the time between changing the widget and the actual redraw in the event loop is so short. Tk uses a single-threaded, event-driven programming model. All the GUI code, the event loop, and the application run within the same thread. Because of this, any calls or computations that block event handlers are highly discouraged. To prevent blocking the event loop, event handlers must execute quickly and return control to the event loop.
If you have a long-running operation to perform or anything like network I/O that could potentially take a long time, event driven I/O programming is the way to go. We make an asynchronous I/O call, it returns immediately, before the operation is completed. The code can continue running, or in this case, return back to the event loop. Later on, when the I/O operation completes, the program is notified and can process the result of the I/O operation. We are treating I/O as another type of event, hence the term event driven i/o programming.
Putting the concepts shared together this is a sample GUI with some tkinter widgets in about 30 lines, very awesome.
There are more features of tkinter, I have included the tutorial.
Deployment
There are several third party solutions for deploying standalone python apps. The most popular and mature solution of the bunch is Pyinstaller. PyInstaller doesn’t make the process of packaging a Python app totally painless, but it goes a long way there.
PyInstaller works by reading the Python application, analyzing all of the library imports it makes, and bundling copies of the imports with the application. PyInstaller reads in the application from its entry point. For instance, if the applications entry point is main.py, you would run pyinstaller main.py to perform the analysis. PyInstaller can detect and automatically package many common Python packages, like NumPy, but you might need to provide hints in some cases.
After analyzing the code and discovering all of the libraries and modules it uses, PyInstaller then generates a “spec file.” A Python script with the extension .spec, this file includes details about how the Python app needs to be packed up. The first time you run PyInstaller on your app, PyInstaller will generate a spec file and populate it with some defaults. Don’t discard this file; it’s the key to configuring a PyInstaller deployment.
Finally, PyInstaller attempts to produce an executable from the app, bundled with all its dependencies. When it’s finished, a subfolder named dist will appear in the project directory. This dist folder contains a directory that is the bundled app — it has an .exe file to run, along with all of the libraries and other supplemental files required.
All that's needed to do to distribute the application, then, is package up this directory as a .zip file or some other bundle. The bundle will typically need to be extracted in a directory where the user has write permissions in order to run the application.
In conclusion, I believe with curiosity and persistence anyone can tinker around with python and tkinter to build a desktop application or simple fun game. Please like, subscribe, share and leave comments if you found this content insightful.