I'm new to programming in general and currently programming a little bit in python. I'm trying to read a lot about programming, but unfortunately i'm still having trouble grasping the concepts of classes and threading. So i would be quite happy about some help. I'm trying to write a program where a main gui is created and calls a function in another module. That function is creating a gui with progress bar and executing a long background task.
First i did this using after(), but the gui window wasn't responsive and didn't work anymore after minimizing. So i read in several sources that for something like this threading should be used. As i was calling two separate functions, that both needed access to the same gui elements (one for creating the window, the other for updating the progress bar and adding the finish-button afterward), i also found that i should use a class for this. I tried to help myself with this tutorial: intro psthon threading.
Doing it like this worked:
import tkinter as tk
from tkinter import ttk
import threading
import time
class ProgressManager:
def __init__(self):
self.progressbar = None
self.complete_button = None
def programming_xyz_pair(self):
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(5)
self.progressbar.update_idletasks()
self.complete_button.pack()
def progress_bar(self):
progress_gui = tk.Toplevel()
progress_gui.title("xyz")
window_width = 800
window_height = 200
screen_width = progress_gui.winfo_screenwidth()
screen_height = progress_gui.winfo_screenheight()
x_position = (screen_width - window_width) // 2
y_position = (screen_height - window_height) // 4
progress_gui.geometry(f"{window_width}x{window_height}+{x_position}+{y_position}")
# text
label_font = ('Calibri', 13)
label = tk.Label(progress_gui,
text="xyz-Pärchen wird konfiguriert und gepaart, bitte warten...",
font=label_font)
label.pack(padx=30, pady=30)
frame = tk.Frame(progress_gui)
frame.pack(padx=50, pady=30)
# Create and place the progress bar inside the Frame
self.progressbar = ttk.Progressbar(frame, length=450)
self.progressbar.pack(expand=True, fill=tk.X)
def continue_button_click():
progress_gui.quit()
self.complete_button = tk.Button(progress_gui, text="Continue", command=continue_button_click)
self.complete_button.pack_forget()
# progress_gui.after(100, self.programming_ZA1739_B_pair)
progress_gui.mainloop()
progress_gui.destroy()
progress_manager = ProgressManager()
# Create a thread for programming_ZA1739_B_pair
programming_thread = threading.Thread(target=progress_manager.programming_xyz_pair)
# Create a thread for progress_bar
progress_bar_thread = threading.Thread(target=progress_manager.progress_bar)
# Start both threads
programming_thread.start()
progress_bar_thread.start()
# Wait for both threads to finish
programming_thread.join()
progress_bar_thread.join()
print('finished')
But as i need to call this part from a different modul, i tried to implement it like this:
main:
import further_threading_test
import tkinter as tk
from tkinter import ttk
def on_start_button_click():
root.withdraw()
further_threading_test.progress_bar(root)
print('Startfenster wird wieder aufgerufen')
root.deiconify()
root = tk.Tk()
def create_main_window():
root.title("xyz Programmier-Tool")
root.configure(background='#E5E5E5')
start_button = ttk.Button(root, text="\nStart\n", command=on_start_button_click)
start_button.pack()
root.mainloop()
create_main_window()
further_threading_test
import tkinter as tk
from tkinter import ttk
import threading
import time
class ProgressManager:
def __init__(self, root):
self.root = root
self.progressbar = None
self.complete_button = None
self.progress_ready_event = threading.Event()
def programming_xyz_pair(self):
self.progress_ready_event.wait()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(3.8)
self.progressbar.update_idletasks()
time.sleep(1)
self.progressbar.step(5)
self.progressbar.update_idletasks()
self.complete_button.pack()
def progress_bar_gui(self):
progress_gui = tk.Toplevel(self.root)
progress_gui.title("ZA1739-B")
window_width = 800
window_height = 200
screen_width = progress_gui.winfo_screenwidth()
screen_height = progress_gui.winfo_screenheight()
x_position = (screen_width - window_width) // 2
y_position = (screen_height - window_height) // 4
progress_gui.geometry(f"{window_width}x{window_height}+{x_position}+{y_position}")
# text
label_font = ('Calibri', 13)
label = tk.Label(progress_gui,
text="ZA1739B-Pärchen wird konfiguriert und gepaart, bitte warten...",
font=label_font)
label.pack(padx=30, pady=30)
frame = tk.Frame(progress_gui)
frame.pack(padx=50, pady=30)
# Create and place the progress bar inside the Frame
self.progressbar = ttk.Progressbar(frame, length=450)
self.progressbar.pack(expand=True, fill=tk.X)
def continue_button_click():
progress_gui.quit()
self.complete_button = tk.Button(progress_gui, text="Continue", command=continue_button_click)
self.complete_button.pack_forget()
self.progress_ready_event.set()
# progress_gui.after(100, self.programming_ZA1739_B_pair)
progress_gui.mainloop()
progress_gui.destroy()
def progress_bar(root):
progress_manager = ProgressManager(root)
programming_thread = threading.Thread(target=progress_manager.programming_xyz_pair)
progress_bar_thread = threading.Thread(target=progress_manager.progress_bar_gui)
programming_thread.start()
progress_bar_thread.start()
programming_thread.join()
progress_bar_thread.join()
return
I found out in the meantime, that i shouldn't be opening new windows and using frames instead, but i still don't understand why the first one is working and the second not. Bevore i added self.progress_ready_event = threading.Event() i had the problem that step wasn't known as attribute for progressbar, so i added this to make sure that progressbar is finished bevore i update it. But now after pressing start nothing happens. I think i don't understand a overlaying concept here. Any help or explanation what exactly i'm doing wrong here or misunderstanding would be greatly appreciated. Thanks in advance!
Edit: I now also found this answer from @TheLizzard where it says: # Don't use a tk.Toplevel here. Can this have to do something with this? If yes, why?