0
# Generates a frame that also contains all the necessary widgets for all mounts in a list
# Takes the parent window the frame will be part of (TK()),
# the row (int) and column (int) the new frame will be located on the parent window,
# the name of the frame we're creating (string),
# and a list of all the mounts to go into the frame (list of string),
# the complement_checkbox (tkinter.CheckButton())
# Returns (tkinter.LabelFrame)
def generate_widgets(master, frame_name, mount_list, complement_checkbox):
    new_frame = LabelFrame(master, text = frame_name, font=("Arial Narrow", 18), padx = 5, pady = 5)
    new_frame.grid(row = 0, column = 0, padx = 10, pady = 10, sticky = N + S + E + W)

    # We have row weight be equal to the number of mounts per frame
    master.rowconfigure(0, weight = len(mount_list))
    master.columnconfigure(0, weight = 1)

    label_widgets = {}
    image = {}
    icon_label = {}
    attempts_string = {}
    spin_widgets = {}
    button_widgets = {}
    for i in range(len(mount_list)):

        full_name = mount_list[i]
        mount_name = full_name.split(' - ')[1]

        label_widgets[i] = Label(new_frame, text = mount_list[i], font = ("Arial Narrow", 12))
        label_widgets[i].grid(row = i, column = 0, sticky = W)

        image[i] = PhotoImage(file = "Assets\\" + mount_name + ".png")
        icon_label[i] = Label(new_frame, image = image[i])
        icon_label[i].grid(row = i, column = 1, sticky = E)

        attempts_string[i] = StringVar()
        attempts_string[i].set(load_attempts(mount_name))

        spin_widgets[i] = Spinbox(new_frame, from_ = 0, to = 1024, width = 5, textvariable = attempts_string[i])
        spin_widgets[i].grid(row = i, column = 2, sticky = E)

        button_widgets[i] = Button(new_frame,
                                   text = "Calculate",
                                   # j = i saves the current value of i into j when lambda is defined,
                                   # if we don't have this line, the command will always use the value of i when the
                                   # command is called, which will be the last row (i-th)
                                   command = lambda j = i: open_and_save(mount_list[j],
                                                                         mount_list[j].split(' - ')[1],
                                                                         spin_widgets[j].get(),
                                                                         complement_checkbox.get()))
        button_widgets[i].grid(row = i, column = 3, sticky = E)

        new_frame.rowconfigure(i, weight = 1)

    # Column 0 is label, column 1 is spinbox, column 2 is button
    new_frame.columnconfigure(0, weight = 1)
    new_frame.columnconfigure(1, weight = 1)
    new_frame.columnconfigure(2, weight = 1)
    new_frame.columnconfigure(3, weight = 1)

    return new_frame

I'm using the above code to create a new frame and fill it iteratively with widgets. So the problem I'm having right now is that the images I'm trying to display with icon_label[i] isn't working, and is instead just a blank space. I'm quite sure why this is happening, as when I try adding an image to the frame after the function has already run, the image display. I also noticed that if I make images{} and icon_label{} global, some of the images display, but not all (some frames have none, some only have the last few, or only the last). So I'm just wondering what is going on?

Verzus
  • 1
  • 1
    Does this answer your question? [Why does Tkinter image not show up if created in a function?](https://stackoverflow.com/questions/16424091/why-does-tkinter-image-not-show-up-if-created-in-a-function) – acw1668 Feb 05 '21 at 01:46
  • How would I integrate the fix into my function? Adding the global tag to the dictionaries doesn't seem to quite work – Verzus Feb 05 '21 at 02:00
  • Adding `global image` inside `generate_widgets()` should work. – acw1668 Feb 05 '21 at 02:25
  • This doesn't entirely work, with the images only showing up in the last frame to be generated. This is probably because each time the function is called to generate function, the global image is probably being overwritten, I would probably need to make a different global variable each time its called? – Verzus Feb 05 '21 at 02:33
  • Then change `image = {}` to `new_frame.image = {}` (and all other `image` to `new_frame.image`), then no need to declare `image` as global. – acw1668 Feb 05 '21 at 02:42
  • Thank you! That seemed to work. Though could I ask how exactly does new_frame.image work? Image isn't a member of the new_frame's class, and I don't think I've seen something like this in Java or C – Verzus Feb 05 '21 at 08:24
  • You can create new attribute in a Python object. So `new_frame.image = {}` will create a new attribute `image` in `new_frame` object and assign it an empty dictionary. – acw1668 Feb 05 '21 at 09:01

0 Answers0