Tkinter is the most popular library for creating graphical user interfaces (GUIs) in Python, and it comes included with the standard Python installation. As a mature, stable, and widely ported tool, Tkinter is relatively easy to learn. However, it is also quite outdated, with significant drawbacks: it offers a limited set of widgets, becomes cumbersome for complex interfaces despite its simplistic learning curve, and—most critically—lacks support for mobile and web application development.

Tkinter window Tkinter window Flask GUI Flask GUI

Web Interface

When working with a traditional Tkinter desktop application, we may want to modernize it by implementing a web-based GUI. Migration can be time-consuming, but one effective approach is to treat it as an evolutionary process. This allows both the desktop and web applications to coexist temporarily, enabling developers to gradually replace Tkinter windows with web controls. This approach eliminates the need to maintain separate Tkinter and Flask versions; instead, the two GUI modules cooperate and communicate. Once the final Flask version is complete, we can remove the outdated Tkinter code.

Below is a draft implementation of this approach. Both GUIs—represented by the tk_main and flask_main functions—run in separate threads. To minimize thread overhead, we reuse the main thread for the Tkinter GUI and create a new thread for Flask (for more on Python threading, see this Real Python tutorial). The Tkinter component, defined in the TkApp class, includes a button that opens a Flask webpage in a browser. For simplicity, we pass arguments to Flask using a global variable—though this is somewhat cumbersome. After running the example, both GUIs are usable simultaneously: if the new Flask window encounters issues, the Tkinter version remains functional as a backup.

Widget Migration

Next, we’ll migrate a sample Tkinter widget: the listbox. We start with poorly structured code that mixes data computation and presentation.

This code works but violates software engineering principles. To prepare for migration—such as replacing the Tkinter interface with HTML—we must extract the data display logic. The GUI should focus solely on presenting data and avoid handling data processing.

After refactoring, we split the code into three functions: prepare_data, process_data, and show_data. These functions model a common application workflow: acquiring data, processing it, and displaying results. They exchange data using cursor or list structures. While this adds some complexity, it makes the code more maintainable and testable. We are now ready to add the Flask component, which includes the FlaskMyForm class and the flask_index function. FlaskMyForm represents an HTML form (in our simple case, it contains just one migrated widget: a SelectField). The flask_index function populates this field with data and renders it on a web page.

With this, we’ve migrated a single widget. Importantly, we didn’t need to modify the code responsible for data communication and processing. By separating GUI components from business logic, we can migrate the entire interface one component at a time.

Example Migration

The following screenshots show the GUI migration process for my hobby application. I began with a Tkinter-based project, but its limitations led me to migrate first to a web-based GUI and later to a cloud-hosted version.
Tkinter
Flask

Tkinter
Flask