Experiment 8: GUI and Backend Connectivity
Python Programming Lab - BTech 2nd Semester
Table of Contents
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