Reputation: 363
Background Information
Hello. What I'm trying to do currently is create the tkinter window, run a for loop and update the label in real-time as the for loop is running through the directories (in this case, it is listing all the directories with their full paths in the C drive of my computer).
The Problem
The problem that I'm running into is that as soon as I click the start
button (which begins the for loop) the GUI completely freezes (I'm aware that this is because tkinter and loops don't play well, I just wonder if there is a solution I'm not aware of), which is counter intuitive as I'd like it to display the directory that the loop is currently iterating over within my tooltip
label.
What I've tried so far
from tkinter import Tk, Label, Frame, Button
import os
def start_command():
for root_directory, sub_directories, files in os.walk("C:\\"):
for sub_directory in sub_directories:
full_directory = os.path.join(root_directory, sub_directory)
tooltip.config(text=full_directory)
window = Tk()
tooltip = Label(window, text="Nothing Here Yet")
tooltip.pack()
start = Button(text="Start", command=start_command)
start.pack()
window.mainloop()
Tl;Dr I'm trying to run a for loop within tkinter and update the label every iteration. The problem is the GUI is freezing with the above code.
Any help would be greatly appreciated. Thank you for your time :)
Upvotes: 3
Views: 2435
Reputation: 123473
Although it's a somewhat advanced approach, you could use a combination of a coroutine, a tkinter control variable, and the universal widget after()
method to drive a loop that updates the Label
widget with minimal runtime overhead. A nice thing about control variables is that any widgets referencing one of them will automatically be updated anytime its set()
method is called from somewhere—so you don't have to do that manually.
#!/usr/bin/env python3
# https://stackoverflow.com/questions/52015836/updating-tkinter-label-based-on-loop
from tkinter import Tk, Label, Frame, Button, StringVar
import os
DELAY = 250 # Milliseconds between var updates.
def walk_dirs(start_dir):
""" Iteratively walk directory tree from start_dir. """
yield "Walking " + start_dir # Optional.
for root_directory, sub_directories, files in os.walk(start_dir):
for sub_directory in sub_directories:
full_directory = os.path.join(root_directory, sub_directory)
yield full_directory
def update_variable(var, walker):
try:
directory = next(walker)
except StopIteration:
var.set("DONE!")
return # Don't call after() so repetitive updates stop.
var.set(directory) # Setting var automatically updates Label.
tooltip.after(DELAY, update_variable, var, walker) # Rinse and repeat.
def start_command(var):
walker = walk_dirs("C:\\") # Start coroutine.
update_variable(var, walker) # Start periodic updates using it.
window = Tk()
var = StringVar()
var.set("Nothing Here Yet")
tooltip = Label(window, textvariable=var)
tooltip.pack()
start = Button(text="Start", command=lambda: start_command(var))
start.pack()
window.mainloop()
Upvotes: 0
Reputation: 1427
import tkinter as tk
import os
class MyDirectoryLabel(tk.Label):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.dirGen = self.directoryGen()
self.master = self._nametowidget(self.winfo_parent())
def directoryGen(self):
for root_directory, sub_directories, files in os.walk("C:\\"):
for sub_directory in sub_directories:
yield os.path.join(root_directory, sub_directory)
def loop(self):
try:
full_directory = next(self.dirGen)
except StopIteration as e:
self.config(text = "Finished...")
return 0
self.config(text = full_directory)
self.master.after(10, self.loop)
root = tk.Tk()
root.geometry("500x100")
tooltip = MyDirectoryLabel(root, text = "Nothing Here Yet")
start = tk.Button(root, text = "Start", command = tooltip.loop)
tooltip.pack()
start.pack()
root.mainloop()
Uses the after
method in combination with a generator
which creates the directory for the label.
Upvotes: 2