Building a Random Student Selector with Python tkinter
Data Preparation
A classroom roster typically contains student IDs and names stored in an Excel file. This project uses pandas to read Excel data.
First, install the required dependenceis:
pip install pandas openpyxl
Consider an Excel file roster.xlsx with columns including ID and Name. The following steps demonstrate how to process this data:
import pandas as pd
df = pd.read_excel("roster.xlsx")
print(df.head())
Convert the DataFrame to a simple list format suitable for the selector:
for idx, row in df.iterrows():
print(f"{row['ID']} - {row['Name']}")
Validation ensures the Excel file contains required columns. Using assertions prevents the program from proceeding with invalid data:
columns = df.columns.values.tolist()
assert "ID" in columns, "Excel must contain an 'ID' column"
assert "Name" in columns, "Excel must contain a 'Name' column"
A complete data processing function:
def load_roster(filepath):
df = pd.read_excel(filepath)
columns = df.columns.values.tolist()
assert "ID" in columns, "Excel must contain an 'ID' column"
assert "Name" in columns, "Excel must contain a 'Name' column"
return [f"{row['ID']} {row['Name']}" for idx, row in df.iterrows()]
GUI Implementation
The tkinter library provides the foundation for building the graphical interface.
import tkinter as tk
import random
Basic Window Setup
root = tk.Tk()
root.mainloop()
Display Label
A StringVar holds the currently displayed name, which updates rapidly during the selection animation:
display_var = tk.StringVar(value="READY")
name_label = tk.Label(root, textvariable=display_var, font=("Arial", 24))
name_label.pack(pady=50)
Control Buttons
start_btn = tk.Button(root, text='Start', font=("Arial", 14))
start_btn.pack(side="left", padx=20)
stop_btn = tk.Button(root, text='Stop', font=("Arial", 14))
stop_btn.pack(side="right", padx=20)
Enimation Logic
The rolling effect uses after() to repeatedly update the display at short intervals:
is_running = False
def start_rolling(var, data_list):
global is_running
if is_running:
return
is_running = True
roll_animation(var, data_list)
def roll_animation(var, data_list):
var.set(random.choice(data_list))
if is_running:
root.after(50, roll_animation, var, data_list)
def stop_rolling():
global is_running
is_running = False
Connect these functions to the buttons:
start_btn.config(command=lambda: start_rolling(display_var, student_data))
stop_btn.config(command=stop_rolling)
Window Centering
Position the window in the screen center for better user expeirence:
def center_window(win, width, height):
screen_w = win.winfo_screenwidth()
screen_h = win.winfo_screenheight()
x = (screen_w - width) // 2
y = (screen_h - height) // 2
win.geometry(f"{width}x{height}+{x}+{y}")
Complete Selector Class
Encapsulating the functionality in a class provides clean organization:
class RandomSelector(tk.Tk):
def __init__(self, data_list):
super().__init__()
self.student_data = data_list
self.is_running = False
self.title("Random Selector")
center_window(self, 400, 300)
self.display_var = tk.StringVar(value="READY")
self.name_label = tk.Label(
self,
textvariable=self.display_var,
font=("Arial", 28, "bold")
)
self.name_label.pack(expand=True)
btn_frame = tk.Frame(self)
btn_frame.pack(pady=30)
self.start_btn = tk.Button(
btn_frame,
text='Start',
width=10,
command=lambda: self.begin_rolling(self.display_var)
)
self.start_btn.grid(row=0, column=0, padx=10)
self.stop_btn = tk.Button(
btn_frame,
text='Stop',
width=10,
command=self.halt_rolling
)
self.stop_btn.grid(row=0, column=1, padx=10)
def roll_animation(self, var):
var.set(random.choice(self.student_data))
if self.is_running:
self.after(50, self.roll_animation, var)
def begin_rolling(self, var):
if self.is_running:
return
self.is_running = True
self.roll_animation(var)
def halt_rolling(self):
self.is_running = False
File Upload Interface
Adding a file selection dialog improves usability by allowing users to browse for Excel files instead of hardcoding paths.
File Browser Dialog
from tkinter import filedialog
def browse_file(entry_widget):
filename = filedialog.askopenfilename(
title="Select Roster File",
filetypes=[("Excel Files", "*.xls *.xlsx")]
)
entry_widget.delete(0, tk.END)
entry_widget.insert(0, filename)
Upload Window Layout
class FileUploader(tk.Tk):
def __init__(self):
super().__init__()
self.title("Load Roster")
center_window(self, 400, 120)
path_label = tk.Label(self, text="File Path:")
path_label.grid(row=0, column=0, padx=10, pady=15, sticky="e")
self.path_entry = tk.Entry(self, width=35)
self.path_entry.grid(row=0, column=1, columnspan=2, padx=(0, 10))
browse_btn = tk.Button(
self,
text="Browse",
command=self.select_file
)
browse_btn.grid(row=1, column=1, pady=10, ipadx=15)
load_btn = tk.Button(
self,
text="Load Data",
command=self.load_roster_data
)
load_btn.grid(row=1, column=2, ipadx=15)
def select_file(self):
filename = filedialog.askopenfilename(
title="Select Roster File",
filetypes=[("Excel Files", "*.xls *.xlsx")]
)
self.path_entry.delete(0, tk.END)
self.path_entry.insert(0, filename)
def load_roster_data(self):
try:
roster = load_roster(self.path_entry.get())
self.destroy()
RandomSelector(roster)
except AssertionError as e:
from tkinter.messagebox import showerror
showerror("Error", str(e))
except Exception as e:
from tkinter.messagebox import showerror
showerror("Error", f"Failed to load data\n{e}")
Application Entry Point
if __name__ == "__main__":
FileUploader().mainloop()
Summary
This application consists of two main windows: a file upload interface for loading Excel rosters, and a random selector interface for choosing students. The RandomSelector class manages the rolling animation using tkinter's after() method, while the FileUploader class handles file selection and data validation. Both windows use the center_window() function to appear centered on screen.