Search code examples
pythontkinternested-loopsgettersetter

Rerun part of code on function value change


I'm currently trying to create program that allows a user to view pictures of a shirt the user selects combined with different pants.

The program contains a directory filled with pictures of shirts, aswell as one of pants. The user selects a shirt from a DropDown-menu, and the program then generates, saves, and displays the all possible combinations created using the shirt. To accomplish this I'm using Tkinter & PIL (Pillow).

Here's my problem: When the user edits the DropDown-menu, and selects another shirt, I want the program to generate new images using THAT shirt, and replace the old images currently displayed.

I've read answers to similar questions, and some suggest a setter and getter, to detect and call a function when a variables' value is changed. I'm not quite sure I understand how it works, and definetly not how to implement it into my code. The function I want to call is inside a nested loop. Does that make any difference in this context?

Here is the code. The generateImage()function is a placeholder for the many rows of code that acutally generate the images.

To detect when the selected option from the DropDown-menu is changed, I use variable.trace.

shirtcolors = ["blue", "red", "green"]

def ChosenImage(*args):
        chosen_image = variable.get()
        print("value is: " + chosen_image)
        selectedimage = ""

        if chosen_image == "blue":
            selectedimage = "C:/User/Desktop/OutfitGenerator/shirts/IMG_0840.jpg"
        elif chosen_image == "red": 
            selectedimage = "C:/User/Desktop/OutfitGenerator/shirts/IMG_0850.jpg"
        elif chosen_image == "green":
            selectedimage = "C:/User/Desktop/OutfitGenerator/shirts/IMG_0860.jpg"

            return selectedimage

DropdownList = tk.OptionMenu(frame, variable, *shirtcolors)
DropdownList.pack()

variable.trace("w", callback=ChosenImage)

shirtslist = [ChosenImage()]
pantsdirectory= "C:/User/Desktop/OutfitGenerator/pants"
pantslist = [os.path.abspath(os.path.join(pantsdirecotry, h)) for h in os.listdir(pantsdirecotry)]

for i in shirtslist:
    for j in pantslist:
        def generateImage(file1, file2):

My problem is that i can't figure out how to make the program run the code below the variable.trace line again. When a callback is sent to ChosenImage I want it to also continue the rest of the code, now using the new selectedimage value. However the continuation of the code cannot happen until the callback has reached the ChosenImage function, and it has changed its value.


Solution

  • My problem is that i can't figure out how to make the program run the code below the variable.trace line again. When a callback is sent to ChosenImage I want it to also continue the rest of the code, now using the new selectedimage value.

    This is the wrong way of thinking. Your callback is the thing that should generate the combinations/images - your callback shouldn't return anything, it should only have side-effects:

    import tkinter as tk
    
    
    class Application(tk.Tk):
    
        def __init__(self, *args, **kwargs):
            tk.Tk.__init__(self, *args, **kwargs)
            self.title("Window")
            self.geometry("256x64")
            self.resizable(width=False, height=False)
    
            self.shirt_colors = ["blue", "red", "green"]
            self.pants_colors = ["brown", "black"]
    
            self.drop_down_var = tk.StringVar(self)
            self.menu = tk.OptionMenu(self, self.drop_down_var, *self.shirt_colors)
            self.menu.pack()
    
            self.drop_down_var.trace("w", callback=self.on_change)
    
        def on_change(self, *args):
            # Everytime the selection changes, generate combinations
            shirt_color = self.drop_down_var.get()
            for pant_color in self.pants_colors:
                print("{} shirt with {} pants".format(shirt_color, pant_color))
    
    
    def main():
        Application().mainloop()
        return 0
    
    
    if __name__ == "__main__":
        import sys
        sys.exit(main())