I am trying to make an app in tkinter to visualize data structures and algorithms. However, I have an issue with the way the program is updating the canvas during the visualisation process. When the visualisation process takes more than ~2 seconds, the program freezes if the user clicks on the window (not responding), and resumes when the visualisation is complete. I am sure what causes this problem, and it doesn’t arise if the user never clicks. Any ideas as to the actual problem and/or issue? The app is still in a testing phase, so it isn’t super clean, apologies for that.
"""Module for visualizing data structures and algorithms"""
import random
import tkinter as tk
import time
from tkinter import HORIZONTAL
def bubble(data, draw_data, speed):
data_length = len(data)
for i in range(data_length):
for j in range(0, data_length - i - 1):
if data[j] > data[j + 1]:
data[j], data[j + 1] = data[j + 1], data[j]
# if swapped then color becomes Green else stays Red
draw_data(data, ['Green' if x == j + 1 else 'Red' for x in range(len(data))])
time.sleep(speed)
# sorted elements generated with Green color
draw_data(data, ['Green' for _ in range(len(data))])
window = tk.Tk()
window.minsize(500, 500)
app_width, app_height = 700, 600
screen_width, screen_height = window.winfo_screenwidth(), window.winfo_screenheight()
mid_x = (screen_width - app_width) // 2
mid_y = (screen_height - app_height) // 2
window.title("StructViz")
window.iconbitmap("./assets/favicon.ico")
window.geometry(f'{app_width}x{app_height}+{mid_x}+{mid_y}')
select_alg = tk.StringVar()
data = []
def regenerate():
global data
minval = int(minEntry.get())
maxval = int(maxEntry.get())
sizeval = int(amountEntry.get())
data = []
for _ in range(sizeval):
data.append(random.randint(minval, maxval + 1))
draw_data(data, ['Red' for _ in range(len(data))])
def draw_data(data, colorlist):
structVizC.delete("all")
canvas_height = 380
canvas_width = 550
x_width = canvas_width / (len(data) + 1)
offset = 30
spacing = 10
normalized_data = [i / max(data) for i in data]
for i, height in enumerate(normalized_data):
x0 = i * x_width + offset + spacing
y0 = canvas_height - height * 340
x1 = ((i + 1) * x_width + offset)
y1 = canvas_height
structVizC.create_rectangle(x0, y0, x1, y1, fill=colorlist[i])
structVizC.create_text(x0 + 2, y0, anchor='se', text=str(data[i]))
window.update_idletasks()
def start_algorithm():
global data
bubble(data, draw_data, speedbar.get())
window.columnconfigure(0, weight=0)
window.columnconfigure(1, weight=45)
window.rowconfigure((0, 1), weight=1)
window.rowconfigure(2, weight=45)
navbarLB = tk.Listbox(window, selectmode=tk.SINGLE)
for item in ["Option 1", "Option 2", "Option 3"]:
navbarLB.insert(tk.END, item)
navbarLB.grid(row=0, column=0, rowspan=3, sticky='nsew')
userSettingsF = (tk.Frame(window, background='bisque2')
.grid(row=0, column=1, columnspan=2, rowspan=2, sticky='news', padx=7, pady=5))
# Change navbarLB.get(0) when user generates a selected option ( from func selected_item() )
AlgorithmL = tk.Label(userSettingsF, text=navbarLB.get(0), background='bisque2')
AlgorithmL.grid(row=0, column=1, sticky='nw', padx=10, pady=10)
amountEntry = tk.Scale(userSettingsF, from_=5, to=50, label='Amount', background='bisque2',
orient=HORIZONTAL, resolution=1, cursor='arrow')
amountEntry.grid(row=0, column=1, sticky='n', padx=10, pady=10)
minEntry = tk.Scale(userSettingsF, from_=0, to=10, resolution=1, background='bisque2',
orient=HORIZONTAL, label="Minimum Value")
minEntry.grid(row=1, column=1, sticky='s', padx=10, pady=10)
maxEntry = tk.Scale(userSettingsF, from_=10, to=100, resolution=1, background='bisque2',
orient=HORIZONTAL, label="Maximum Value")
maxEntry.grid(row=1, column=1, sticky='se', padx=10, pady=10)
speedbar = tk.Scale(userSettingsF, from_=0.10, to=2.0, length=100, digits=2, background='bisque2',
resolution=0.1, orient=HORIZONTAL, label="Speed")
speedbar.grid(row=0, column=1, sticky='ne', padx=10, pady=10)
tk.Button(userSettingsF, text="Start", bg="Blue", command=start_algorithm, background='bisque2').grid(
row=1, column=1, sticky='nw', padx=10, pady=10)
tk.Button(userSettingsF, text="Regenerate", bg="Red", command=regenerate, background='bisque2').grid(
row=1, column=1, sticky='sw', padx=10, pady=10)
structVizC = tk.Canvas(window, background='bisque2')
structVizC.grid(row=2, column=1, sticky='news', padx=5, pady=5)
window.mainloop()
During visualisation the app should display the process. On user click, it should do nothing. Instead, it freezes (not responding) until the process is complete.
Harbourheading is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.