Experiment 8: GUI and Backend Connectivity

Python Programming Lab - BTech 2nd Semester

Introduction to GUI Programming

What is GUI Programming?

Graphical User Interface (GUI) programming allows users to interact with applications through visual elements like buttons, text fields, menus, and windows instead of command-line interfaces.

Key Concepts:

  • Event-Driven Programming: Programs respond to user actions (events) like button clicks, mouse movements
  • Widgets: GUI elements like buttons, labels, text fields
  • Layout Management: Arranging widgets in windows
  • Backend Connectivity: Connecting GUI to databases for data persistence
Why Tkinter?

Tkinter is Python's standard GUI library because:

  • Built into Python (no installation required)
  • Simple and easy to learn
  • Cross-platform compatibility
  • Good for beginners and rapid prototyping

Tkinter Basics

1. Importing Tkinter

# Method 1: Import the entire module
import tkinter as tk

# Method 2: Import specific widgets
from tkinter import Tk, Label, Button, Entry

# Method 3: Import all (not recommended for large projects)
from tkinter import *

2. Basic Window Components

# Create main window
root = tk.Tk()
root.title("My Application")
root.geometry("400x300")  # Width x Height
root.resizable(False, False)  # Fixed size

# Start the event loop
root.mainloop()

3. Common Widgets

# Label Widget - Display text
label = tk.Label(root, text="Hello World!", 
                font=("Arial", 12), 
                bg="lightblue", 
                fg="darkblue")
label.pack(pady=10)
# Button Widget - Clickable element
def button_click():
    print("Button clicked!")

button = tk.Button(root, text="Click Me", 
                  command=button_click,
                  bg="green", 
                  fg="white",
                  padx=20, pady=10)
button.pack(pady=10)
# Entry Widget - Text input field
entry = tk.Entry(root, width=30, 
                font=("Arial", 10),
                borderwidth=2)
entry.pack(pady=10)

# Get text from entry
text = entry.get()

# Set text in entry
entry.insert(0, "Default text")
# Listbox Widget - Display list of items
listbox = tk.Listbox(root, height=5, selectmode=tk.SINGLE)
items = ["Item 1", "Item 2", "Item 3"]
for item in items:
    listbox.insert(tk.END, item)
listbox.pack(pady=10)

# Get selected item
selected = listbox.get(tk.ACTIVE)

4. Layout Management

pack() - Simple Layout
# Pack widgets from top to bottom
label.pack()
button.pack()
entry.pack()

# Options: side, padx, pady, fill, expand
grid() - Table Layout
# Arrange in rows and columns
label.grid(row=0, column=0)
entry.grid(row=0, column=1)
button.grid(row=1, column=0, columnspan=2)

# Options: row, column, padx, pady, sticky
place() - Precise Position
# Position with exact coordinates
button.place(x=50, y=100, width=100, height=30)

# Options: x, y, width, height, anchor

Database Connectivity

Connecting GUI to Database

Database connectivity allows your GUI applications to store, retrieve, and manage data persistently.

1. SQLite - Built-in Database

import sqlite3

# Create connection
conn = sqlite3.connect('database.db')
cursor = conn.cursor()

# Create table
cursor.execute('''
    CREATE TABLE IF NOT EXISTS students (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        name TEXT NOT NULL,
        email TEXT UNIQUE,
        age INTEGER
    )
''')

# Insert data
cursor.execute('INSERT INTO students (name, email, age) VALUES (?, ?, ?)', 
              ('John Doe', 'john@example.com', 20))

# Commit and close
conn.commit()
conn.close()

2. MySQL - External Database

# Install: pip install mysql-connector-python
import mysql.connector

# Create connection
conn = mysql.connector.connect(
    host='localhost',
    user='username',
    password='password',
    database='school'
)

cursor = conn.cursor()

# Execute queries
cursor.execute('SELECT * FROM students')
results = cursor.fetchall()

# Close connection
conn.close()
Important Security Note

Always use parameterized queries (?) to prevent SQL injection attacks:

# GOOD - Parameterized query
cursor.execute('SELECT * FROM users WHERE username = ?', (username,))

# BAD - String formatting (vulnerable to SQL injection)
cursor.execute(f'SELECT * FROM users WHERE username = {username}')

3. Database Operations Pattern

class DatabaseManager:
    def __init__(self, db_name):
        self.conn = sqlite3.connect(db_name)
        self.cursor = self.conn.cursor()
        self.create_tables()
    
    def create_tables(self):
        # Create necessary tables
        pass
    
    def insert_data(self, table, data):
        # Insert data into table
        pass
    
    def get_data(self, table, condition=None):
        # Retrieve data from table
        pass
    
    def update_data(self, table, data, condition):
        # Update data in table
        pass
    
    def delete_data(self, table, condition):
        # Delete data from table
        pass
    
    def close(self):
        self.conn.close()

Task 1: Simple Tkinter Window

Objective

Create a simple Tkinter window with a title and fixed size.

Concept Explanation

  • Root Window: The main window of your application
  • Title: Text displayed in the window title bar
  • Geometry: Window dimensions (width x height)
  • Resizable: Control whether window can be resized
  • Mainloop: Starts the event loop

Complete Code

import tkinter as tk

def create_simple_window():
    # Create the main window
    root = tk.Tk()
    
    # Set window title
    root.title("My First GUI Application")
    
    # Set window size (width x height)
    root.geometry("500x400")
    
    # Make window non-resizable
    root.resizable(False, False)
    
    # Set background color
    root.configure(bg='#f0f0f0')
    
    # Add a label to show the window is working
    welcome_label = tk.Label(
        root, 
        text="Welcome to GUI Programming!",
        font=("Arial", 16, "bold"),
        bg='#f0f0f0',
        fg='#2c3e50'
    )
    welcome_label.pack(pady=50)
    
    # Add a button to close the window
    close_button = tk.Button(
        root,
        text="Close Window",
        command=root.destroy,
        bg='#e74c3c',
        fg='white',
        font=("Arial", 12),
        padx=20,
        pady=10
    )
    close_button.pack(pady=20)
    
    # Start the event loop
    root.mainloop()

# Run the application
if __name__ == "__main__":
    create_simple_window()

Key Points to Remember

  • Always call root.mainloop() at the end
  • Use geometry("widthxheight") for fixed size
  • resizable(False, False) prevents resizing
  • Widgets are added using layout managers (pack, grid, place)

Task 2: Basic Calculator

Objective

Design a GUI-based basic calculator for performing arithmetic operations.

Concept Explanation

  • Entry Widget: For displaying numbers and results
  • Button Grid: Arrange calculator buttons in grid layout
  • Event Handling: Functions to handle button clicks
  • Expression Evaluation: Using Python's eval() function

Complete Code

import tkinter as tk
from tkinter import messagebox

class Calculator:
    def __init__(self, root):
        self.root = root
        self.root.title("Basic Calculator")
        self.root.geometry("350x450")
        self.root.resizable(False, False)
        self.root.configure(bg='#2c3e50')
        
        # Variable to store current expression
        self.expression = ""
        
        # Create display
        self.create_display()
        
        # Create buttons
        self.create_buttons()
    
    def create_display(self):
        # Display frame
        display_frame = tk.Frame(self.root, bg='#2c3e50')
        display_frame.pack(pady=20)
        
        # Entry widget for display
        self.display = tk.Entry(
            display_frame,
            textvariable=tk.StringVar(),
            font=('Arial', 24),
            width=14,
            bd=10,
            insertwidth=4,
            bg='#34495e',
            fg='white',
            justify='right'
        )
        self.display.pack()
    
    def create_buttons(self):
        # Button frame
        button_frame = tk.Frame(self.root, bg='#2c3e50')
        button_frame.pack()
        
        # Button layout
        buttons = [
            '7', '8', '9', '/',
            '4', '5', '6', '*',
            '1', '2', '3', '-',
            '0', '.', '=', '+',
            'C', '⌫'
        ]
        
        # Create buttons
        row = 0
        col = 0
        for button_text in buttons:
            if button_text == '=':
                button = tk.Button(
                    button_frame,
                    text=button_text,
                    font=('Arial', 18, 'bold'),
                    width=5,
                    height=2,
                    bg='#27ae60',
                    fg='white',
                    command=self.calculate
                )
            elif button_text == 'C':
                button = tk.Button(
                    button_frame,
                    text=button_text,
                    font=('Arial', 18, 'bold'),
                    width=5,
                    height=2,
                    bg='#e74c3c',
                    fg='white',
                    command=self.clear
                )
            elif button_text == '⌫':
                button = tk.Button(
                    button_frame,
                    text=button_text,
                    font=('Arial', 18, 'bold'),
                    width=5,
                    height=2,
                    bg='#f39c12',
                    fg='white',
                    command=self.backspace
                )
            else:
                button = tk.Button(
                    button_frame,
                    text=button_text,
                    font=('Arial', 18),
                    width=5,
                    height=2,
                    bg='#34495e',
                    fg='white',
                    command=lambda text=button_text: self.append_to_expression(text)
                )
            
            button.grid(row=row, column=col, padx=2, pady=2)
            
            col += 1
            if col > 3:
                col = 0
                row += 1
    
    def append_to_expression(self, value):
        self.expression += str(value)
        self.display.delete(0, tk.END)
        self.display.insert(0, self.expression)
    
    def clear(self):
        self.expression = ""
        self.display.delete(0, tk.END)
    
    def backspace(self):
        self.expression = self.expression[:-1]
        self.display.delete(0, tk.END)
        self.display.insert(0, self.expression)
    
    def calculate(self):
        try:
            result = str(eval(self.expression))
            self.display.delete(0, tk.END)
            self.display.insert(0, result)
            self.expression = result
        except:
            messagebox.showerror("Error", "Invalid Expression")
            self.clear()

# Create and run the calculator
if __name__ == "__main__":
    root = tk.Tk()
    calculator = Calculator(root)
    root.mainloop()

Key Concepts Demonstrated

  • Grid Layout: Perfect for calculator button arrangement
  • Lambda Functions: Pass parameters to button commands
  • Error Handling: Try-except for invalid expressions
  • State Management: Tracking current expression

Task 3: Student Registration System

Objective

Design a GUI for student registration and store details in a database.

Concept Explanation

  • Form Validation: Ensure data integrity
  • Database Integration: SQLite for data persistence
  • CRUD Operations: Create, Read, Update, Delete
  • Data Display: Show registered students

Complete Code

import tkinter as tk
from tkinter import messagebox, ttk
import sqlite3

class StudentRegistrationSystem:
    def __init__(self, root):
        self.root = root
        self.root.title("Student Registration System")
        self.root.geometry("800x600")
        self.root.configure(bg='#ecf0f1')
        
        # Initialize database
        self.init_database()
        
        # Create GUI
        self.create_widgets()
        
        # Load students
        self.load_students()
    
    def init_database(self):
        # Create database connection
        self.conn = sqlite3.connect('students.db')
        self.cursor = self.conn.cursor()
        
        # Create table if not exists
        self.cursor.execute('''
            CREATE TABLE IF NOT EXISTS students (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                name TEXT NOT NULL,
                email TEXT UNIQUE NOT NULL,
                phone TEXT,
                course TEXT,
                age INTEGER,
                gender TEXT
            )
        ''')
        self.conn.commit()
    
    def create_widgets(self):
        # Main frame
        main_frame = tk.Frame(self.root, bg='#ecf0f1')
        main_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=20)
        
        # Left frame - Registration form
        left_frame = tk.Frame(main_frame, bg='white', relief=tk.RAISED, bd=2)
        left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(0, 10))
        
        # Right frame - Student list
        right_frame = tk.Frame(main_frame, bg='white', relief=tk.RAISED, bd=2)
        right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=(10, 0))
        
        # Registration form
        self.create_registration_form(left_frame)
        
        # Student list
        self.create_student_list(right_frame)
    
    def create_registration_form(self, parent):
        # Title
        title = tk.Label(parent, text="Student Registration", 
                        font=('Arial', 16, 'bold'), bg='white')
        title.pack(pady=20)
        
        # Form fields
        fields = [
            ("Name:", "name_entry"),
            ("Email:", "email_entry"),
            ("Phone:", "phone_entry"),
            ("Course:", "course_entry"),
            ("Age:", "age_entry"),
            ("Gender:", "gender_var")
        ]
        
        self.entries = {}
        
        for i, (label_text, entry_name) in enumerate(fields):
            # Label
            label = tk.Label(parent, text=label_text, font=('Arial', 10), bg='white')
            label.grid(row=i, column=0, padx=10, pady=5, sticky='w')
            
            if entry_name == "gender_var":
                # Gender radio buttons
                self.gender_var = tk.StringVar(value="Male")
                male_radio = tk.Radiobutton(parent, text="Male", variable=self.gender_var, 
                                         value="Male", bg='white')
                male_radio.grid(row=i, column=1, padx=10, pady=5, sticky='w')
                
                female_radio = tk.Radiobutton(parent, text="Female", variable=self.gender_var, 
                                            value="Female", bg='white')
                female_radio.grid(row=i, column=2, padx=10, pady=5, sticky='w')
            else:
                # Entry field
                entry = tk.Entry(parent, font=('Arial', 10), width=25)
                entry.grid(row=i, column=1, columnspan=2, padx=10, pady=5, sticky='w')
                self.entries[entry_name] = entry
        
        # Buttons
        button_frame = tk.Frame(parent, bg='white')
        button_frame.grid(row=len(fields), column=0, columnspan=3, pady=20)
        
        register_btn = tk.Button(button_frame, text="Register", 
                                command=self.register_student,
                                bg='#27ae60', fg='white', font=('Arial', 10, 'bold'),
                                padx=20, pady=5)
        register_btn.pack(side=tk.LEFT, padx=5)
        
        clear_btn = tk.Button(button_frame, text="Clear", 
                             command=self.clear_form,
                             bg='#e74c3c', fg='white', font=('Arial', 10, 'bold'),
                             padx=20, pady=5)
        clear_btn.pack(side=tk.LEFT, padx=5)
    
    def create_student_list(self, parent):
        # Title
        title = tk.Label(parent, text="Registered Students", 
                        font=('Arial', 16, 'bold'), bg='white')
        title.pack(pady=20)
        
        # Treeview for student list
        columns = ('ID', 'Name', 'Email', 'Phone', 'Course', 'Age', 'Gender')
        self.student_tree = ttk.Treeview(parent, columns=columns, show='headings', height=15)
        
        # Define headings
        for col in columns:
            self.student_tree.heading(col, text=col)
            self.student_tree.column(col, width=80)
        
        # Scrollbar
        scrollbar = ttk.Scrollbar(parent, orient=tk.VERTICAL, command=self.student_tree.yview)
        self.student_tree.configure(yscrollcommand=scrollbar.set)
        
        # Pack treeview and scrollbar
        self.student_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(10, 0), pady=(0, 10))
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y, padx=(0, 10), pady=(0, 10))
        
        # Delete button
        delete_btn = tk.Button(parent, text="Delete Selected", 
                              command=self.delete_student,
                              bg='#e74c3c', fg='white', font=('Arial', 10, 'bold'),
                              padx=20, pady=5)
        delete_btn.pack(pady=10)
    
    def register_student(self):
        # Get form data
        name = self.entries['name_entry'].get().strip()
        email = self.entries['email_entry'].get().strip()
        phone = self.entries['phone_entry'].get().strip()
        course = self.entries['course_entry'].get().strip()
        age = self.entries['age_entry'].get().strip()
        gender = self.gender_var.get()
        
        # Validation
        if not name or not email:
            messagebox.showerror("Error", "Name and Email are required!")
            return
        
        if age and not age.isdigit():
            messagebox.showerror("Error", "Age must be a number!")
            return
        
        # Insert into database
        try:
            self.cursor.execute('''
                INSERT INTO students (name, email, phone, course, age, gender)
                VALUES (?, ?, ?, ?, ?, ?)
            ''', (name, email, phone, course, age, gender))
            self.conn.commit()
            
            messagebox.showinfo("Success", "Student registered successfully!")
            self.clear_form()
            self.load_students()
            
        except sqlite3.IntegrityError:
            messagebox.showerror("Error", "Email already exists!")
        except Exception as e:
            messagebox.showerror("Error", f"Registration failed: {str(e)}")
    
    def load_students(self):
        # Clear existing data
        for item in self.student_tree.get_children():
            self.student_tree.delete(item)
        
        # Load from database
        self.cursor.execute('SELECT * FROM students ORDER BY id')
        students = self.cursor.fetchall()
        
        for student in students:
            self.student_tree.insert('', tk.END, values=student)
    
    def delete_student(self):
        selected = self.student_tree.selection()
        if not selected:
            messagebox.showwarning("Warning", "Please select a student to delete!")
            return
        
        if messagebox.askyesno("Confirm", "Are you sure you want to delete this student?"):
            item = self.student_tree.item(selected[0])
            student_id = item['values'][0]
            
            try:
                self.cursor.execute('DELETE FROM students WHERE id = ?', (student_id,))
                self.conn.commit()
                self.load_students()
                messagebox.showinfo("Success", "Student deleted successfully!")
            except Exception as e:
                messagebox.showerror("Error", f"Deletion failed: {str(e)}")
    
    def clear_form(self):
        for entry in self.entries.values():
            if entry:  # Skip gender_var
                entry.delete(0, tk.END)
        self.gender_var.set("Male")
    
    def __del__(self):
        # Close database connection
        if hasattr(self, 'conn'):
            self.conn.close()

# Create and run the application
if __name__ == "__main__":
    root = tk.Tk()
    app = StudentRegistrationSystem(root)
    root.mainloop()

Key Features

  • Form Validation: Checks for required fields and data types
  • Database Operations: Insert and delete student records
  • Data Display: Treeview widget for tabular data
  • Error Handling: Database constraints and user input errors

Task 4: Task Manager

Objective

Create a GUI-based task manager where users can add, edit, and remove tasks.

Concept Explanation

  • Listbox Widget: Display list of tasks
  • Task Management: Add, edit, delete operations
  • State Tracking: Task completion status
  • Data Persistence: SQLite database storage

Complete Code

import tkinter as tk
from tkinter import messagebox, simpledialog
import sqlite3
from datetime import datetime

class TaskManager:
    def __init__(self, root):
        self.root = root
        self.root.title("Task Manager")
        self.root.geometry("600x500")
        self.root.configure(bg='#34495e')
        
        # Initialize database
        self.init_database()
        
        # Create GUI
        self.create_widgets()
        
        # Load tasks
        self.load_tasks()
    
    def init_database(self):
        # Create database connection
        self.conn = sqlite3.connect('tasks.db')
        self.cursor = self.conn.cursor()
        
        # Create table if not exists
        self.cursor.execute('''
            CREATE TABLE IF NOT EXISTS tasks (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                task TEXT NOT NULL,
                status TEXT DEFAULT 'pending',
                created_date TEXT,
                completed_date TEXT
            )
        ''')
        self.conn.commit()
    
    def create_widgets(self):
        # Title
        title = tk.Label(self.root, text="Task Manager", 
                        font=('Arial', 20, 'bold'), 
                        bg='#34495e', fg='white')
        title.pack(pady=20)
        
        # Input frame
        input_frame = tk.Frame(self.root, bg='#34495e')
        input_frame.pack(pady=10)
        
        # Task entry
        self.task_entry = tk.Entry(input_frame, font=('Arial', 12), width=40)
        self.task_entry.pack(side=tk.LEFT, padx=5)
        
        # Add button
        add_btn = tk.Button(input_frame, text="Add Task", 
                           command=self.add_task,
                           bg='#27ae60', fg='white', 
                           font=('Arial', 10, 'bold'),
                           padx=15, pady=5)
        add_btn.pack(side=tk.LEFT, padx=5)
        
        # Task list frame
        list_frame = tk.Frame(self.root, bg='white', relief=tk.RAISED, bd=2)
        list_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=10)
        
        # Listbox with scrollbar
        scrollbar = tk.Scrollbar(list_frame)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        
        self.task_listbox = tk.Listbox(list_frame, 
                                       font=('Arial', 11),
                                       selectmode=tk.SINGLE,
                                       yscrollcommand=scrollbar.set,
                                       height=15)
        self.task_listbox.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        scrollbar.config(command=self.task_listbox.yview)
        
        # Bind double-click for editing
        self.task_listbox.bind('', self.edit_task)
        
        # Button frame
        button_frame = tk.Frame(self.root, bg='#34495e')
        button_frame.pack(pady=10)
        
        # Action buttons
        complete_btn = tk.Button(button_frame, text="Mark Complete", 
                                command=self.complete_task,
                                bg='#3498db', fg='white',
                                font=('Arial', 10, 'bold'),
                                padx=15, pady=5)
        complete_btn.pack(side=tk.LEFT, padx=5)
        
        edit_btn = tk.Button(button_frame, text="Edit Task", 
                            command=self.edit_task_button,
                            bg='#f39c12', fg='white',
                            font=('Arial', 10, 'bold'),
                            padx=15, pady=5)
        edit_btn.pack(side=tk.LEFT, padx=5)
        
        delete_btn = tk.Button(button_frame, text="Delete Task", 
                              command=self.delete_task,
                              bg='#e74c3c', fg='white',
                              font=('Arial', 10, 'bold'),
                              padx=15, pady=5)
        delete_btn.pack(side=tk.LEFT, padx=5)
        
        # Status bar
        self.status_bar = tk.Label(self.root, text="Ready", 
                                  font=('Arial', 9), 
                                  bg='#2c3e50', fg='white',
                                  anchor=tk.W)
        self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)
    
    def add_task(self):
        task = self.task_entry.get().strip()
        if not task:
            messagebox.showwarning("Warning", "Please enter a task!")
            return
        
        try:
            created_date = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            self.cursor.execute('''
                INSERT INTO tasks (task, status, created_date)
                VALUES (?, ?, ?)
            ''', (task, 'pending', created_date))
            self.conn.commit()
            
            self.task_entry.delete(0, tk.END)
            self.load_tasks()
            self.update_status(f"Task '{task}' added successfully!")
            
        except Exception as e:
            messagebox.showerror("Error", f"Failed to add task: {str(e)}")
    
    def load_tasks(self):
        # Clear existing tasks
        self.task_listbox.delete(0, tk.END)
        
        # Load from database
        self.cursor.execute('SELECT * FROM tasks ORDER BY created_date DESC')
        tasks = self.cursor.fetchall()
        
        for task in tasks:
            task_id, task_text, status, created_date, completed_date = task
            display_text = f"[{status.upper()}] {task_text}"
            if status == 'completed':
                display_text = "✓ " + display_text
            self.task_listbox.insert(tk.END, display_text)
    
    def get_selected_task_id(self):
        selected = self.task_listbox.curselection()
        if not selected:
            return None
        
        # Get the actual task from database
        self.cursor.execute('SELECT * FROM tasks ORDER BY created_date DESC')
        tasks = self.cursor.fetchall()
        
        if selected[0] < len(tasks):
            return tasks[selected[0]][0]  # Return task ID
        return None
    
    def complete_task(self):
        task_id = self.get_selected_task_id()
        if not task_id:
            messagebox.showwarning("Warning", "Please select a task!")
            return
        
        try:
            completed_date = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            self.cursor.execute('''
                UPDATE tasks SET status = 'completed', completed_date = ?
                WHERE id = ?
            ''', (completed_date, task_id))
            self.conn.commit()
            
            self.load_tasks()
            self.update_status("Task marked as complete!")
            
        except Exception as e:
            messagebox.showerror("Error", f"Failed to complete task: {str(e)}")
    
    def edit_task_button(self):
        task_id = self.get_selected_task_id()
        if not task_id:
            messagebox.showwarning("Warning", "Please select a task!")
            return
        
        self.edit_task(None)
    
    def edit_task(self, event):
        task_id = self.get_selected_task_id()
        if not task_id:
            return
        
        # Get current task text
        self.cursor.execute('SELECT task FROM tasks WHERE id = ?', (task_id,))
        current_task = self.cursor.fetchone()[0]
        
        # Ask for new task text
        new_task = simpledialog.askstring("Edit Task", 
                                         "Enter new task text:",
                                         initialvalue=current_task)
        
        if new_task and new_task.strip():
            try:
                self.cursor.execute('''
                    UPDATE tasks SET task = ? WHERE id = ?
                ''', (new_task.strip(), task_id))
                self.conn.commit()
                
                self.load_tasks()
                self.update_status("Task updated successfully!")
                
            except Exception as e:
                messagebox.showerror("Error", f"Failed to update task: {str(e)}")
    
    def delete_task(self):
        task_id = self.get_selected_task_id()
        if not task_id:
            messagebox.showwarning("Warning", "Please select a task!")
            return
        
        if messagebox.askyesno("Confirm", "Are you sure you want to delete this task?"):
            try:
                self.cursor.execute('DELETE FROM tasks WHERE id = ?', (task_id,))
                self.conn.commit()
                
                self.load_tasks()
                self.update_status("Task deleted successfully!")
                
            except Exception as e:
                messagebox.showerror("Error", f"Failed to delete task: {str(e)}")
    
    def update_status(self, message):
        self.status_bar.config(text=message)
        self.root.after(3000, lambda: self.status_bar.config(text="Ready"))
    
    def __del__(self):
        # Close database connection
        if hasattr(self, 'conn'):
            self.conn.close()

# Create and run the application
if __name__ == "__main__":
    root = tk.Tk()
    app = TaskManager(root)
    root.mainloop()

Advanced Features

  • Double-click Editing: Double-click task to edit
  • Status Tracking: Pending vs completed tasks
  • Timestamps: Track creation and completion times
  • Status Bar: User feedback for actions

Task 5: Login and Signup System

Objective

Design a login and signup authentication system with GUI.

Concept Explanation

  • Authentication: Verify user credentials
  • Password Hashing: Secure password storage
  • Session Management: Track logged-in users
  • Window Switching: Navigate between login/signup/main windows

Complete Code

import tkinter as tk
from tkinter import messagebox
import sqlite3
import hashlib
import random
import string

class AuthenticationSystem:
    def __init__(self):
        # Initialize database
        self.init_database()
        
        # Create main window
        self.root = tk.Tk()
        self.root.title("Authentication System")
        self.root.geometry("400x300")
        self.root.resizable(False, False)
        
        # Current user (for session management)
        self.current_user = None
        
        # Show login screen
        self.show_login_screen()
        
        # Start the application
        self.root.mainloop()
    
    def init_database(self):
        # Create database connection
        self.conn = sqlite3.connect('auth_system.db')
        self.cursor = self.conn.cursor()
        
        # Create users table if not exists
        self.cursor.execute('''
            CREATE TABLE IF NOT EXISTS users (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                username TEXT UNIQUE NOT NULL,
                email TEXT UNIQUE NOT NULL,
                password_hash TEXT NOT NULL,
                created_date TEXT,
                last_login TEXT
            )
        ''')
        self.conn.commit()
    
    def clear_window(self):
        # Clear all widgets from window
        for widget in self.root.winfo_children():
            widget.destroy()
    
    def show_login_screen(self):
        self.clear_window()
        self.root.geometry("400x300")
        
        # Title
        title = tk.Label(self.root, text="Login", 
                        font=('Arial', 20, 'bold'),
                        bg='#3498db', fg='white')
        title.pack(fill=tk.X, pady=(0, 30))
        
        # Login frame
        login_frame = tk.Frame(self.root, bg='white', relief=tk.RAISED, bd=2)
        login_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=20)
        
        # Username
        tk.Label(login_frame, text="Username:", font=('Arial', 10), bg='white').grid(row=0, column=0, padx=10, pady=10, sticky='w')
        self.login_username = tk.Entry(login_frame, font=('Arial', 10), width=25)
        self.login_username.grid(row=0, column=1, padx=10, pady=10)
        
        # Password
        tk.Label(login_frame, text="Password:", font=('Arial', 10), bg='white').grid(row=1, column=0, padx=10, pady=10, sticky='w')
        self.login_password = tk.Entry(login_frame, font=('Arial', 10), width=25, show='*')
        self.login_password.grid(row=1, column=1, padx=10, pady=10)
        
        # Login button
        login_btn = tk.Button(login_frame, text="Login", 
                            command=self.login,
                            bg='#27ae60', fg='white',
                            font=('Arial', 10, 'bold'),
                            padx=20, pady=5)
        login_btn.grid(row=2, column=0, columnspan=2, pady=20)
        
        # Signup link
        signup_link = tk.Button(login_frame, text="Don't have an account? Sign up",
                              command=self.show_signup_screen,
                              bg='#3498db', fg='white',
                              font=('Arial', 9),
                              relief=tk.FLAT, cursor='hand2')
        signup_link.grid(row=3, column=0, columnspan=2)
        
        # Bind Enter key to login
        self.root.bind('', lambda event: self.login())
        
        # Focus on username field
        self.login_username.focus()
    
    def show_signup_screen(self):
        self.clear_window()
        self.root.geometry("450x400")
        
        # Title
        title = tk.Label(self.root, text="Sign Up", 
                        font=('Arial', 20, 'bold'),
                        bg='#e74c3c', fg='white')
        title.pack(fill=tk.X, pady=(0, 30))
        
        # Signup frame
        signup_frame = tk.Frame(self.root, bg='white', relief=tk.RAISED, bd=2)
        signup_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=20)
        
        # Username
        tk.Label(signup_frame, text="Username:", font=('Arial', 10), bg='white').grid(row=0, column=0, padx=10, pady=10, sticky='w')
        self.signup_username = tk.Entry(signup_frame, font=('Arial', 10), width=25)
        self.signup_username.grid(row=0, column=1, padx=10, pady=10)
        
        # Email
        tk.Label(signup_frame, text="Email:", font=('Arial', 10), bg='white').grid(row=1, column=0, padx=10, pady=10, sticky='w')
        self.signup_email = tk.Entry(signup_frame, font=('Arial', 10), width=25)
        self.signup_email.grid(row=1, column=1, padx=10, pady=10)
        
        # Password
        tk.Label(signup_frame, text="Password:", font=('Arial', 10), bg='white').grid(row=2, column=0, padx=10, pady=10, sticky='w')
        self.signup_password = tk.Entry(signup_frame, font=('Arial', 10), width=25, show='*')
        self.signup_password.grid(row=2, column=1, padx=10, pady=10)
        
        # Confirm Password
        tk.Label(signup_frame, text="Confirm Password:", font=('Arial', 10), bg='white').grid(row=3, column=0, padx=10, pady=10, sticky='w')
        self.signup_confirm_password = tk.Entry(signup_frame, font=('Arial', 10), width=25, show='*')
        self.signup_confirm_password.grid(row=3, column=1, padx=10, pady=10)
        
        # Signup button
        signup_btn = tk.Button(signup_frame, text="Sign Up", 
                             command=self.signup,
                             bg='#e74c3c', fg='white',
                             font=('Arial', 10, 'bold'),
                             padx=20, pady=5)
        signup_btn.grid(row=4, column=0, columnspan=2, pady=20)
        
        # Login link
        login_link = tk.Button(signup_frame, text="Already have an account? Login",
                             command=self.show_login_screen,
                             bg='#3498db', fg='white',
                             font=('Arial', 9),
                             relief=tk.FLAT, cursor='hand2')
        login_link.grid(row=5, column=0, columnspan=2)
        
        # Focus on username field
        self.signup_username.focus()
    
    def hash_password(self, password):
        # Hash password using SHA-256
        return hashlib.sha256(password.encode()).hexdigest()
    
    def validate_email(self, email):
        # Simple email validation
        return '@' in email and '.' in email
    
    def login(self):
        username = self.login_username.get().strip()
        password = self.login_password.get().strip()
        
        if not username or not password:
            messagebox.showerror("Error", "Please enter both username and password!")
            return
        
        try:
            password_hash = self.hash_password(password)
            self.cursor.execute('''
                SELECT * FROM users WHERE username = ? AND password_hash = ?
            ''', (username, password_hash))
            
            user = self.cursor.fetchone()
            
            if user:
                # Update last login
                from datetime import datetime
                last_login = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                self.cursor.execute('''
                    UPDATE users SET last_login = ? WHERE id = ?
                ''', (last_login, user[0]))
                self.conn.commit()
                
                self.current_user = {'id': user[0], 'username': user[1], 'email': user[2]}
                messagebox.showinfo("Success", f"Welcome back, {username}!")
                self.show_dashboard()
            else:
                messagebox.showerror("Error", "Invalid username or password!")
                
        except Exception as e:
            messagebox.showerror("Error", f"Login failed: {str(e)}")
    
    def signup(self):
        username = self.signup_username.get().strip()
        email = self.signup_email.get().strip()
        password = self.signup_password.get().strip()
        confirm_password = self.signup_confirm_password.get().strip()
        
        # Validation
        if not username or not email or not password or not confirm_password:
            messagebox.showerror("Error", "Please fill all fields!")
            return
        
        if len(username) < 3:
            messagebox.showerror("Error", "Username must be at least 3 characters!")
            return
        
        if not self.validate_email(email):
            messagebox.showerror("Error", "Please enter a valid email address!")
            return
        
        if len(password) < 6:
            messagebox.showerror("Error", "Password must be at least 6 characters!")
            return
        
        if password != confirm_password:
            messagebox.showerror("Error", "Passwords do not match!")
            return
        
        try:
            # Check if username or email already exists
            self.cursor.execute('''
                SELECT username, email FROM users WHERE username = ? OR email = ?
            ''', (username, email))
            
            existing_user = self.cursor.fetchone()
            
            if existing_user:
                if existing_user[0] == username:
                    messagebox.showerror("Error", "Username already exists!")
                else:
                    messagebox.showerror("Error", "Email already registered!")
                return
            
            # Create new user
            password_hash = self.hash_password(password)
            from datetime import datetime
            created_date = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            
            self.cursor.execute('''
                INSERT INTO users (username, email, password_hash, created_date)
                VALUES (?, ?, ?, ?)
            ''', (username, email, password_hash, created_date))
            
            self.conn.commit()
            
            messagebox.showinfo("Success", "Account created successfully! Please login.")
            self.show_login_screen()
            
        except Exception as e:
            messagebox.showerror("Error", f"Signup failed: {str(e)}")
    
    def show_dashboard(self):
        self.clear_window()
        self.root.geometry("500x400")
        
        # Title
        title = tk.Label(self.root, text=f"Welcome, {self.current_user['username']}!", 
                        font=('Arial', 18, 'bold'),
                        bg='#27ae60', fg='white')
        title.pack(fill=tk.X, pady=(0, 30))
        
        # Dashboard frame
        dashboard_frame = tk.Frame(self.root, bg='white', relief=tk.RAISED, bd=2)
        dashboard_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=20)
        
        # User info
        info_frame = tk.Frame(dashboard_frame, bg='white')
        info_frame.pack(pady=20)
        
        tk.Label(info_frame, text=f"Username: {self.current_user['username']}", 
                font=('Arial', 12), bg='white').pack(anchor='w', pady=5)
        tk.Label(info_frame, text=f"Email: {self.current_user['email']}", 
                font=('Arial', 12), bg='white').pack(anchor='w', pady=5)
        
        # Get last login
        self.cursor.execute('SELECT last_login FROM users WHERE id = ?', (self.current_user['id'],))
        last_login = self.cursor.fetchone()[0]
        if last_login:
            tk.Label(info_frame, text=f"Last Login: {last_login}", 
                    font=('Arial', 12), bg='white').pack(anchor='w', pady=5)
        
        # Welcome message
        welcome_label = tk.Label(dashboard_frame, 
                               text="You have successfully logged in!\nThis is your personal dashboard.",
                               font=('Arial', 11), bg='white', justify='center')
        welcome_label.pack(pady=30)
        
        # Logout button
        logout_btn = tk.Button(dashboard_frame, text="Logout", 
                             command=self.logout,
                             bg='#e74c3c', fg='white',
                             font=('Arial', 10, 'bold'),
                             padx=20, pady=5)
        logout_btn.pack(pady=20)
    
    def logout(self):
        self.current_user = None
        messagebox.showinfo("Success", "You have been logged out successfully!")
        self.show_login_screen()
    
    def __del__(self):
        # Close database connection
        if hasattr(self, 'conn'):
            self.conn.close()

# Run the authentication system
if __name__ == "__main__":
    app = AuthenticationSystem()

Security Features

  • Password Hashing: SHA-256 for secure password storage
  • Input Validation: Check for required fields and format
  • Session Management: Track logged-in user state
  • Error Handling: Prevent information disclosure

Summary and Best Practices

GUI Best Practices

  • Use consistent color schemes and fonts
  • Provide clear user feedback (messages, status bars)
  • Implement proper error handling
  • Make interfaces intuitive and responsive
  • Use appropriate layout managers

Database Best Practices

  • Always use parameterized queries
  • Implement proper error handling
  • Close database connections properly
  • Use transactions for data integrity
  • Validate user input before database operations

Key Takeaways

After completing this experiment, students should be able to:

  • Create professional GUI applications using Tkinter
  • Integrate GUI applications with databases
  • Implement CRUD operations with user interfaces
  • Design authentication systems with secure password handling
  • Apply proper software engineering practices