Previous | Tutorial index | Next

Tutorial 7: Design and Implement a GUI for a Given Application – Temperature Converter

Learning Objectives

1. Introduction – From Concept to Code

Building a real GUI application is not just about knowing individual widgets—it’s about combining them to solve a specific problem. The Temperature Converter is a classic "starter" project because it involves:

This tutorial will guide you through the full lifecycle of building this application:

  1. Understand the requirements – What does the user need?
  2. Sketch the layout – Visual design on paper.
  3. Choose the widgets – Which ones fit each role?
  4. Implement step by step – Write and test incrementally.
  5. Test and refine – Fix bugs, improve user experience.
  6. Add enhancements – Make the app more useful and polished.

By the end, you will have not only a working converter but also a process you can apply to any future GUI project.

2. Requirements Analysis – What Are We Building?

2.1 User Stories

As a user, I want to:

  1. Enter a numeric temperature value.
  2. Click a button to convert it to the other unit.
  3. See the result clearly displayed.
  4. Be told if I enter something invalid (letters, symbols, empty field).

2.2 Functional Requirements

2.3 Non‑Functional Requirements

3. Sketching the Layout – Before You Code

Before writing any code, sketch the GUI on paper (or using a tool). This helps you visualise the arrangement of widgets and plan the geometry manager.

Proposed layout (grid):

+-------------------------------------------------+ | Enter Temperature: [ ] | <- Row 0 | [To Fahrenheit] [To Celsius] | <- Row 1 | +-------------------------------------------+ | <- Row 2 | | Result will appear here | | | +-------------------------------------------+ | +-------------------------------------------------+

This layout uses grid() with consistent padding for a clean look.

4. Widget Selection and Configuration

Widget Purpose Options
Label ("Enter Temperature:") Instruction text. text="Enter Temperature:"
Entry User input field. Default width (10‑15 characters).
Button (×2) Trigger conversion. text="To Fahrenheit" / "To Celsius", command callback.
Label (result) Display output or error. text="Result will appear here", relief="sunken", width=40 (to ensure consistent size), anchor="center" (optional).

Design choices:

5. Step‑by‑Step Implementation Walkthrough

Step 1: Import Tkinter

import tkinter as tk

Step 2: Create the Root Window

root = tk.Tk() root.title("Temperature Converter") root.geometry("400x150") # Optional: set initial size root.resizable(False, False) # Make it non‑resizable to keep layout simple (or leave resizable)

Step 3: Define the Conversion Functions

We write the logic before connecting it to the GUI. This keeps business logic separate from presentation.

def to_fahrenheit(): try: celsius = float(entry.get()) fahrenheit = celsius * 9/5 + 32 result_label.config(text=f"{celsius:.2f}°C = {fahrenheit:.2f}°F") except ValueError: result_label.config(text="Invalid input – please enter a number.") def to_celsius(): try: fahrenheit = float(entry.get()) celsius = (fahrenheit - 32) * 5/9 result_label.config(text=f"{fahrenheit:.2f}°F = {celsius:.2f}°C") except ValueError: result_label.config(text="Invalid input – please enter a number.")

Error handling details:

Step 4: Create and Place Widgets

We create widgets and place them using grid().

# Row 0: Label and Entry label = tk.Label(root, text="Enter Temperature:") label.grid(row=0, column=0, padx=5, pady=5) entry = tk.Entry(root, width=12) entry.grid(row=0, column=1, padx=5, pady=5) # Row 1: Two Buttons btn_f = tk.Button(root, text="To Fahrenheit", command=to_fahrenheit) btn_f.grid(row=1, column=0, padx=5, pady=5) btn_c = tk.Button(root, text="To Celsius", command=to_celsius) btn_c.grid(row=1, column=1, padx=5, pady=5) # Row 2: Result Label (spanning two columns) result_label = tk.Label(root, text="Result will appear here", relief="sunken", width=40, anchor="center") result_label.grid(row=2, column=0, columnspan=2, padx=5, pady=10)

Note: We assign the entry and result_label variables so the functions can access them.

Step 5: Start the Main Loop

root.mainloop()

6. Complete Code (with Comments)

import tkinter as tk # --- Conversion functions --- def to_fahrenheit(): """Convert Celsius (from entry) to Fahrenheit and display.""" try: celsius = float(entry.get()) fahrenheit = celsius * 9/5 + 32 result_label.config(text=f"{celsius:.2f}°C = {fahrenheit:.2f}°F") except ValueError: result_label.config(text="Invalid input – please enter a number.") def to_celsius(): """Convert Fahrenheit (from entry) to Celsius and display.""" try: fahrenheit = float(entry.get()) celsius = (fahrenheit - 32) * 5/9 result_label.config(text=f"{fahrenheit:.2f}°F = {celsius:.2f}°C") except ValueError: result_label.config(text="Invalid input – please enter a number.") # --- Create the main window --- root = tk.Tk() root.title("Temperature Converter") root.geometry("400x150") root.resizable(False, False) # --- Create widgets --- # Row 0 label = tk.Label(root, text="Enter Temperature:") label.grid(row=0, column=0, padx=5, pady=5) entry = tk.Entry(root, width=12) entry.grid(row=0, column=1, padx=5, pady=5) # Row 1 btn_f = tk.Button(root, text="To Fahrenheit", command=to_fahrenheit) btn_f.grid(row=1, column=0, padx=5, pady=5) btn_c = tk.Button(root, text="To Celsius", command=to_celsius) btn_c.grid(row=1, column=1, padx=5, pady=5) # Row 2 result_label = tk.Label(root, text="Result will appear here", relief="sunken", width=40, anchor="center") result_label.grid(row=2, column=0, columnspan=2, padx=5, pady=10) # --- Start the event loop --- root.mainloop()

7. Enhancing the Application – Going Beyond the Basics

The basic converter works, but we can add several features to make it more robust and user‑friendly.

7.1 Keyboard Shortcut: Press Enter to Convert

We can bind the <Return> key to automatically perform the conversion based on which button is focused or based on a default direction. For simplicity, we can bind to both buttons' commands? Actually we can set focus to the entry and bind to the root.

def on_enter(event): # By default, convert to Fahrenheit? Or let the user decide via a variable. to_fahrenheit() # Or to_celsius – we can add a radio button for direction. root.bind('<Return>', on_enter)

Better: We can set the default conversion direction via a Radiobutton (see extension).

7.2 Add a "Clear" Button

A button that clears the entry and resets the result label.

def clear_all(): entry.delete(0, tk.END) result_label.config(text="Result will appear here") btn_clear = tk.Button(root, text="Clear", command=clear_all) btn_clear.grid(row=1, column=2, padx=5, pady=5) # Need to adjust columns (maybe add extra column)

We would need to adjust the grid: move the two buttons to columns 0 and 1, and clear button in column 2 with columnspan adjustments.

7.3 Conversion Direction Selector (Radio Buttons)

Allow the user to choose which conversion they want, then a single "Convert" button.

direction_var = tk.StringVar(value="C_to_F") tk.Radiobutton(root, text="C → F", variable=direction_var, value="C_to_F").grid(row=1, column=0) tk.Radiobutton(root, text="F → C", variable=direction_var, value="F_to_C").grid(row=1, column=1) def convert(): if direction_var.get() == "C_to_F": to_fahrenheit() else: to_celsius() btn_convert = tk.Button(root, text="Convert", command=convert) btn_convert.grid(row=2, column=0, columnspan=2)

7.4 Input Validation in Real Time (Using validate and validatecommand)

We can prevent non‑numeric input from being typed at all (except for minus sign and decimal point). This is more advanced and can be done with the validate option.

def validate_input(char, current_text): # Allow empty, digits, a single minus at start, and a single dot. if char == "": return True # Only allow digits, '-', '.' if char in "0123456789.-": # Ensure only one '-' and only at start if char == "-" and (current_text != "" or "-" in current_text): return False # Ensure only one '.' if char == "." and current_text.count(".") >= 1: return False return True return False vcmd = (root.register(validate_input), "%S", "%P") entry.config(validate="key", validatecommand=vcmd)

This makes the entry reject invalid characters immediately.

7.5 Add a History of Conversions

Store the last few conversions in a Listbox or Text widget.

history_list = tk.Listbox(root, height=4) history_list.grid(row=3, column=0, columnspan=2, padx=5, pady=5) def log_conversion(original, converted, direction): history_list.insert(tk.END, f"{original}{converted} ({direction})") if history_list.size() > 10: # limit history history_list.delete(0)

Call log_conversion inside to_fahrenheit and to_celsius.

7.6 Copy Result to Clipboard

Add a button that copies the result to the system clipboard.

def copy_result(): root.clipboard_clear() root.clipboard_append(result_label.cget("text"))

8. Testing Your Application – What to Check

9. Check Your Understanding (Quizzes)

Quiz 1: Multiple Choice

  1. What happens if the user clicks a conversion button when the entry is empty?
AnswerB – The `float()` conversion raises `ValueError` and the `except` block displays the error message.
  1. Why do we use float(entry.get()) inside a try block?
AnswerB – To handle non‑numeric input gracefully.
  1. What is the purpose of the columnspan=2 option in the result label's grid() call?
AnswerB – It occupies both columns.
  1. Which of the following would be a good enhancement to add keyboard shortcut support?
AnswerA – Binding to the root window's `` event.
  1. When adding input validation with validatecommand, which option must be set on the Entry?
AnswerC – Both options are required.

Quiz 2: True or False

  1. True / False: The tryexcept block is unnecessary because users always enter valid numbers.
AnswerFalse – Users can enter anything; error handling is essential.
  1. True / False: The result label should be placed after the entry creation so the conversion functions can reference it.
AnswerTrue – The functions reference the `entry` and `result_label` variables; they must be defined before the functions are called (though they can be defined before).
  1. True / False: grid() cannot be mixed with other geometry managers in the same container.
AnswerTrue – That is the golden rule of geometry managers.
  1. True / False: To copy text to the clipboard, we use root.clipboard_append() followed by root.clipboard_clear().
AnswerFalse – The order should be: clear first, then append.
  1. True / False: The relief="sunken" option makes the label look like a display screen.
AnswerTrue – Sunken relief gives a recessed appearance.

Quiz 3: Fill in the Blanks

  1. The conversion from Celsius to Fahrenheit uses the formula: F = C * ____ + 32.
Answer`9/5` (or 1.8)
  1. The method to retrieve text from an Entry widget is .____().
Answer`get`
  1. To make a widget span two columns in grid(), we use the option ________=2.
Answer`columnspan`
  1. The special variable tk.END is often used with entry.delete(0, tk.____) to clear all text.
Answer`END`
  1. To automatically convert when the user presses Enter, we bind to the event <________>.
Answer`Return`

10. Hands‑On Lab (In‑Class Exercises)

Exercise 1: Add a "Clear" Button

Modify the basic temperature converter to include a "Clear" button. It should:

Sample Solution Add a new button with a callback: ```python def clear_all(): entry.delete(0, tk.END) result_label.config(text="Result will appear here") btn_clear = tk.Button(root, text="Clear", command=clear_all) btn_clear.grid(row=1, column=2, padx=5, pady=5) ``` Adjust grid columns accordingly (make buttons in columns 0,1,2 or use columnspan).

Exercise 2: Add Keyboard Shortcuts

Sample Solution ```python root.bind('', lambda e: to_fahrenheit()) root.bind('', lambda e: clear_all()) ```

Exercise 3: Add a Conversion Direction Selector

Replace the two buttons with:

Sample Solution ```python direction_var = tk.StringVar(value="C_to_F") tk.Radiobutton(root, text="C→F", variable=direction_var, value="C_to_F").grid(row=1, column=0) tk.Radiobutton(root, text="F→C", variable=direction_var, value="F_to_C").grid(row=1, column=1) def convert(): if direction_var.get() == "C_to_F": to_fahrenheit() else: to_celsius() btn_convert = tk.Button(root, text="Convert", command=convert) btn_convert.grid(row=2, column=0, columnspan=2) ```

Exercise 4: Input Validation with validatecommand

Implement real‑time input validation so that the entry only accepts numbers, a single minus sign (only at the start), and a single decimal point. Use the validate="key" and validatecommand approach.

Sample Solution See Section 7.4 for code.

Exercise 5: History Log

Add a Listbox below the result that shows the last 5 conversions. Each entry should show the original and converted values (e.g., "25°C = 77°F"). Automatically add to the list after each conversion.

Sample Solution Create a listbox and a logging function, and call it from `to_fahrenheit` and `to_celsius`.

11. Homework Assignment

Objective

Demonstrate your ability to enhance a given GUI application by adding new features, improving usability, and handling edge cases. You will also reflect on the design process.

Part A: Extended Temperature Converter (25 points)

Build upon the basic temperature converter to create a fully featured application with the following specifications:

Core Requirements:

Additional Features:

  1. Clear Button – Clears the entry and resets the result label.
  2. Keyboard Shortcuts:
  3. History Panel – A Listbox that stores the last 10 conversions (oldest at top, newest at bottom). Each history item should be like "25.0°C → 77.0°F".
  4. Copy to Clipboard – A "Copy Result" button that copies the current result text to the clipboard.
  5. Precision Option – A Spinbox (or Scale) that lets the user choose the number of decimal places (0 to 4). The result should respect this setting.
  6. Real‑time Input Validation – Use validatecommand to allow only digits, one minus (at start), and one decimal point.

Layout Requirements:

Sample Solution Outline - Create root, set title, geometry, resizable. - Use frames: `control_frame` for input, radiobuttons, convert, clear, precision; `history_frame` for listbox and scrollbar. - Use `grid()` inside frames. - Define conversion functions that use `precision_var` for decimal formatting. - Bind `` and ``. - Implement validation with `validatecommand`. - Implement `copy_result` using clipboard. - Add logging to history listbox with length limit.

Part B: Code Analysis and Error Explanation (10 points)

The following code is a partial implementation of a converter that tries to use place() but has layout and logical issues. Identify four distinct problems (not syntax errors, but design/logic flaws), explain each, and suggest a fix.

import tkinter as tk root = tk.Tk() root.title("Bad Converter") entry = tk.Entry(root, width=10) entry.place(x=50, y=20) def convert(): try: c = float(entry.get()) f = c * 9/5 + 32 label.config(text=f) # Problem: No unit indication except: label.config(text="Error") label = tk.Label(root, text="Result") label.place(x=50, y=70) btn = tk.Button(root, text="Convert", command=convert) btn.place(x=50, y=120) root.mainloop()

Your answer should list each problem and propose a concrete correction.

Answers 1. Using `place()` makes the layout non‑resizable and difficult to maintain; use `grid()` or `pack()`. 2. The conversion only goes one way (C→F); need both directions or a selector. 3. The result label does not show the original value or the unit; should display `f"{c}°C = {f:.2f}°F"`. 4. The `except` block catches all exceptions and shows a generic "Error" message; should catch specific `ValueError` and display a user‑friendly message. 5. The window is not given a size; could use `geometry()`.

Part C: Testing and Edge Cases (5 points)

Provide a test plan for the temperature converter you built in Part A. Write a table with:

Include at least 5 test cases covering normal, edge (e.g., -40, 1000), and invalid inputs (empty, letters, multiple decimals).

Sample Test Plan | Test Case | Expected Result | | :--- | :--- | | Input 0, C→F | "0.00°C = 32.00°F" | | Input 100, C→F | "100.00°C = 212.00°F" | | Input -40, C→F | "-40.00°C = -40.00°F" | | Input "abc", any | Error message in label | | Input "", any | Error message | | Input 32, F→C | "32.00°F = 0.00°C" | | Input 1000, C→F | "1000.00°C = 1832.00°F" | | Input "25.5", C→F | "25.50°C = 77.90°F" |

Part D: Reflection – The Design Process (5 points)

Write a short essay (5‑7 sentences) answering: "Why is it beneficial to sketch the layout on paper before writing any code for a GUI application? How did the grid layout help in this specific project?" Relate it to the temperature converter development.

Sample Answer Sketching the layout on paper before coding helps to visualise the widget arrangement, identify potential alignment issues, and decide on the appropriate geometry manager. In the temperature converter, sketching showed that `grid` was ideal because we had a neat row/column structure: labels and entries in one row, buttons in another, and a result label spanning both columns. This upfront planning saved time and prevented multiple rewrites of the placement code, ensuring a clean and consistent UI from the start.

Part E: Extension – Additional Conversion Types (Bonus: 5 extra points)

Add support for Kelvin conversion. Add a new radiobutton option for "Celsius ↔ Kelvin" (or a separate set). Modify the conversion functions to handle the new unit. Ensure all existing features (history, precision, etc.) work for the new conversion.

Sample Solution Outline Add a third radiobutton value (e.g., "C_to_K" and "K_to_C" or use a combined set). Implement conversion functions `to_kelvin()` and `to_celsius_from_kelvin()`. Update the convert logic to branch on the direction var. Ensure precision and history work similarly.
  • Part B (listed problems and fixes)
  • Part C (test plan table)
  • Part D (essay)
  • Ensure your code is well‑structured (use functions, avoid global variables where possible, and include docstrings for major functions).
  • 12. Summary of Key Terms (Glossary)

    Term Definition
    GUI Application A program with a graphical user interface that responds to user events.
    Requirements Analysis The process of gathering and defining what the application should do.
    Layout Sketch A rough drawing of where widgets will be placed before coding.
    Grid Geometry Manager A layout system that arranges widgets in a table of rows and columns.
    Callback A function attached to an event (like a button click) that executes when the event occurs.
    Error Handling Using try/except to catch and manage runtime errors (e.g., invalid input).
    Validation Checking user input for correctness (e.g., numeric only).
    History A log of previous actions or results, often displayed in a Listbox.
    Clipboard A system‑wide storage for copied text; accessible via clipboard_append() and clipboard_clear().

    13. Further Resources for Self‑Study

    This tutorial is designed to take approximately 3 hours of study, lab work, and homework. The temperature converter, though simple, is a perfect sandbox to practice all the core Tkinter concepts you have learned so far.

    Previous | Tutorial index | Next