[Rod Stephens Books]
Index Books Python Examples About Rod Contact
[Mastodon] [Bluesky] [Facebook]
[Build Your Own Python Action Arcade!]

[Build Your Own Ray Tracer With Python]

[Beginning Database Design Solutions, Second Edition]

[Beginning Software Engineering, Second Edition]

[Essential Algorithms, Second Edition]

[The Modern C# Challenge]

[WPF 3d, Three-Dimensional Graphics with WPF and C#]

[The C# Helper Top 100]

[Interview Puzzles Dissected]

Title: Make an interactive unit converter with tkinter in Python

[Converting between units like furlongs to feet in Python]

My previous post, Make a unit converter in Python, showed how you can build a unit converter to convert values between units like feet to inches or seconds to weeks. This post uses tkinter to build an interactive user interface for the converter. The tkinter details made it a bit harder than I expected.

The basic idea is for the user to enter an amount in the entry widget on the left and select the units for the conversion.

When you select a new "from" unit from the left combo box, the program updates the right combo box to hold the available choices. For example, if you select ft from the left combo box, the right combo box should only allow distance units like in, m, and furlong.

The next section shows how the program builds its widgets. The sections after that show helper methods, the code that updates the right combo box's choices, and the code that performs the actual conversions.

Building Widgets

Here's the code that the program creates the example's widgets.

def __init__(self): self.window = tk.Tk() self.window.title('unit_converter_tk') self.window.protocol('WM_DELETE_WINDOW', self.kill_callback) self.window.geometry('400x100') # Make a UnitConverter. self.converter = UnitConverter() # Make widgets. self.from_amount_var = tk.StringVar(value='1') from_entry = tk.Entry(self.window, width=10, textvariable=self.from_amount_var) from_entry.grid(row=0, column=0, padx=10, pady=10) self.from_amount_var.trace('w', self.convert) combo_width = max(len(unit) for unit in self.converter.units) self.from_unit_combo = ttk.Combobox(self.window, width=combo_width, values=self.converter.units, state='readonly') self.from_unit_combo.grid(row=0, column=1) self.from_unit_combo.current(0) self.from_unit_combo.bind('<>', self.from_unit_changed) label = tk.Label(self.window, text=' equals') label.grid(row=0, column=2, padx=5) self.to_amount_var = tk.StringVar(value='') to_entry = tk.Entry(self.window, width=10, state='readonly', textvariable=self.to_amount_var) to_entry.grid(row=0, column=3, padx=5, pady=5) self.to_unit_var = tk.StringVar() self.to_unit_combo = ttk.Combobox(self.window, width=combo_width, state='readonly') self.to_unit_combo.grid(row=0, column=4) self.to_unit_combo.bind('<>', self.convert) # Set the initial from unit choices. self.from_unit_changed(None) from_entry.focus_force() self.window.mainloop()

After the usual setup tasks, the code creates a UnitConverter and then starts creating widgets.

It makes the left entry widget and adds a trace to its text variable so the program executes self.convert when the user types there.

Next, the code creates the left combo box. It sets that widget's choices to the result of the converter's units list, which is described shortly. It sets the combo box's state to readonly so the user cannot type in it and can only pick items from the list of choices. The code binds the combo box to the self.from_unit_changed method so it executes when the user picks a new choice.

The program then creates the read-only result entry widget.

It uses code similar to the steps it used to make the "from" combo box to create the "to" combo box, although this time it doesn't set the box's choices. It binds the "to" combo box to the self.convert method.

After it creates the widgets, the code calls self.from_unit_changed(None) to fire the from_unit_changed method as if the user had selected a choice from the "from" combo box.

The code finishes by setting focus to the first entry widget and entering the tkinter main loop.

The following sections fill in some additional details.

Converter Units

The widget-creation code sets the "from" combo box's choices to the converter's units list. When the unit converter is created, its constructor calls the following method to create that list and the quantities dictionary.

def make_dictionaries(self): '''Make some lookup dictionaries.''' # Invert the conversions so, for example, we can see # that mm is a Distance and hour is a Time. # Using loops. # self.quantities = {} # for quantity in self.conversions: # for unit in self.conversions[quantity]: # # E.g. quantities['mm'].append('Distance') # self.quantities[unit] = quantity # Or using a dictionary comprehension. self.quantities = { unit: quantity for quantity in self.conversions for unit in self.conversions[quantity] } # Make a list of the units. self.units = sorted(self.quantities.keys())

The code first makes a quantities dictionary that lets you look up each unit's quantity type. For example, quantities['mm'] returns Distance because millimeters is a unit of distance. The code includes a commented out version that uses loops and a dictionary comprehension so you can pick the one you prefer.

After it creates quantities, the code sets self.units to the sorted keys in that dictionary. The result is a sorted list of all of the units that the converter knows.

The program's widget-creation code makes the "from" combo box use those units for its list of choices.

Selecting "From" Unit

When you select a new unit from the "from" combo box, the following code fills the "to" combo box with appropriate units.

def from_unit_changed(self, event): '''The to unit changed. Update the from unit combo choices.''' unit = self.from_unit_combo.get() values = self.converter.get_related_units(unit) self.to_unit_combo.configure(values=values) self.to_unit_combo.current(0) # Perform the conversion if possible. self.convert()

This code first gets the new "from" unit selection. It calls the converter's get_related_units method to get the units that match. For example, if you select pint, get_related_units returns a list of volume units.

The code then selects the first "to" unit and calls self.convert to perform the conversion if possible.

The following code show the converter's get_related_units method.

The code uses self.conversions[quantity] to get the conversion entries that belong to the returned quantity. (E.g. all of the entries that are volumes.) The result is a dictionary holding the appropriate units and their linear equation coefficients for converting into the base unit. (See my earlier post if you don't remember how that works.)

The code gets the keys from that dictionary (e.g. the units that are volumes), sorts them, and returns the final result. The calling code sets the "to" combo box's choices to that sorted list of related units.

Performing Conversions

When you change the entered value or the units, the following code converts the amount you entered between the selected units.

def convert(self, *_): '''Perform the conversion if possible.''' # Get the values. try: from_amount = float(self.from_amount_var.get()) from_unit = self.from_unit_combo.get() to_unit = self.to_unit_combo.get() to_quantity = self.converter.convert(from_amount, from_unit, to_unit) self.to_amount_var.set(to_quantity) except Exception as ex: return

The code gets the amount entered and the "from" and "to" units. It calls the converter's convert method to perform the conversion and saves the result in the variable bound to the right entry widget so it appears on the window. If anything goes wrong, it simply returns. That happens if you delete the amount or type something that isn't a number like apple or ten.

Conclusion

This was a bit more work than I had anticipated. The three trickiest parts are the converter's quantities dictionary that maps from units (hour) to quantities (Time), its units list holds all of the units, and its get_related_units method that digs through the conversion data to find the units related to particular unit.

My next post will build a unit converter that uses a different user interface that may be slightly easier to use.

Download the example to experiment with it and to see additional details.

© 2025 Rocky Mountain Computer Consulting, Inc. All rights reserved.