How do I use tkinter events to 'continue' or pause a different while loop?

Viewed 132

I have the tkinter GUI running on main thread and a while loop running on a second thread which updates label contents to be displayed on the GUI for a duration on n seconds. This runs fine on its own. Now I want a << and >> button on the GUI which makes the loop:

  • pause the current iteration, call the display method with the previous item, and resume the iteration when done
  • skip the current item midway and move over to the next item. But I could not find how to generate loop interrupt events. I cannot call continue or sleep depending on some variables/db entries which the buttons make (like in here Python: Tkinter pausing a while loop) because the interrupt can happen anywhere in the loop (not at a particular if/else line)

I have already referred to the Viewer program by codemy.com - https://github.com/flatplanet/Intro-To-TKinter-Youtube-Course/blob/master/viewer.py and of course google.

Here is a snippet of what I am trying to do. (https://gitlab.com/ananya26nov/PixelescoPy)

The main thread runs the GUI

guiObj = GuiWindow()
thread_list = []
thread = threading.Thread(target=worker, args=(guiObj,))
thread_list.append(thread)
thread.start()
guiObj.run_window_on_loop()
thread.join()
guiObj.quit_window()

The worker method for the thread has this:

 while len(files_not_viewed) != 0:
    chosen = random.choice(files_not_viewed)
    if is_not_viewed(chosen):
        pictureObj = Picture(chosen)
        # Display the chosen for <timer> seconds
        pictureObj.display(guiObj)
        time.sleep(timer)
        # Change the status of "chosen" to "viewed"
        mark_as_viewed(chosen)
    files_not_viewed = list(set(files_not_viewed) - set([chosen]))

The display method calls 'add_input_section' method of the following class

class GuiWindow():
def __init__(self):
    self.root = Tk()
    self.screen_width = self.root.winfo_screenwidth()
    self.screen_height = self.root.winfo_screenheight()
    self.image_label = None
    self.image = None
    self.folder_path = None
    self.timer = None

def run_window_on_loop(self):
    button_exit = Button(self.root, text="Exit Program", command=self.root.quit)
    button_exit.grid(row=1, column=1)
    self.root.mainloop()

def quit_window(self):
    self.root.quit()

def resize(self, image_path):
    my_pic = Image.open(image_path)
    pic_height = my_pic.height
    pic_width = my_pic.width
    if my_pic.height > (self.screen_height - 100):
        new_height= self.screen_height - 100
        new_width = int(new_height / pic_height * pic_width)
        pic_height = new_height
        pic_width = new_width

    if pic_width > self.screen_width - 5:
        new_width = self.screen_height - 5
        new_height = int(new_width / pic_width * pic_height)
        pic_height = new_height
        pic_width = new_width

    resized_image = my_pic.resize((pic_width, pic_height), Image.ANTIALIAS)
    return resized_image

def add_image(self, image_path):
    resized_img = self.resize(image_path)
    image_obj = ImageTk.PhotoImage(resized_img)
    image_label = Label(self.root, image=image_obj,
                        height=resized_img.height,
                        width=resized_img.width)
    self.image = image_obj # DO NOT REMOVE - Garbage collector error
    if self.image_label is not None:
        self.remove_image()
    image_label.grid(row=0, column=0, columnspan=3)
    self.image_label = image_label

def remove_image(self):
    self.image_label.grid_forget()
1 Answers

Use tk.BooleanVar() to manipulate the variables and also keep track the state when you try to pause so that you can resume when it's finished.

Related