0

I'm trying to place a set of images in a horizontal row in Tkinter. I do this by looping over a list, and loading the corresponding images from the working directory. I place the images according to the index multiplied with a certain amount of spacing. However, when i actually place the images, they all get placed on top of each other in the last spot, instead of spaced out. The count value works fine, and when i print(spacing*(count+1)) it outputs the correct values but when placing they all get bunched up in the last place.

Does this have something to do with the Label() class?

for count, mood in enumerate(mood_options):
    mood_img = Image.open(f"img/{mood}.png")
    mood_img_copy = mood_img.resize(img_size, Image.ANTIALIAS)
    mood_img_resized = ImageTk.PhotoImage(mood_img_copy)
    mood_img_label = Label(root, image=mood_img_resized).place(relx=spacing*(count+1), rely=0.35)
    print(spacing * (count + 1))

EDIT: I have used this exact method for placing buttons, see below:

for count, mood in enumerate(mood_options):
    mood_btn = Button(root, text=mood.capitalize(), command=lambda mood=mood: register_mood(mood), width=7) \
        .place(relx=(count + 1) * spacing, rely=0.5)

This works flawlessly, which makes me wonder why it wouldn't work with images, instead of buttons.

Barco
  • 79
  • 6
  • I would suggest that you use either `.pack` or `.grid` to make the placement easier since you wouldn't need to calculate anything and `Label` is a class not a function – Matiiss Oct 22 '21 at 12:17
  • Throughout the whole project i'm using `.place`, and combining that with either `.pack` or `.grid` would not work. – Barco Oct 22 '21 at 12:22
  • first of why are you using `.place`? second, pack and either one of the other layout managers can be used simultaneously in the same container – Matiiss Oct 22 '21 at 12:33
  • I thought i would have more control over the exact placement using `.place`. With `.pack`, you only have control in left, bottom, etc. as far as i know. And using `.pack` when placing iteratively seems a bit strange when comparing it to `.place` as the exact position can be chosen. – Barco Oct 22 '21 at 12:39
  • You can also use `.grid` which has more control than pack but iteratively it certainly is easier to use `.pack` because it will automatically stack the things wherever needed (I mean yes left right top bottom but usually that is enough), otherwise you can use `.grid` which allows to configure row and column, also usually you don't need an exact pixel perfect placement in a GUI, it would be hard to adjust it if the window is resized and other stuff, for example here you use the `x` coordinates, in `.grid` you could just use `column=count` and that's it, if using pack then specify `side='right'` – Matiiss Oct 22 '21 at 12:43
  • But i'm using the `relx` parameter in `.place` for consistent positions when the window is resized, which places the image as a 'fraction' of the window size. – Barco Oct 22 '21 at 12:51
  • 2
    It's NOT anything to do with the images being "bunched up", it's simply due to the fact that all of your images other than the last one were garbage-collected and ceased to exist. You have to hold a reference to a `PhotoImage` object for its entire desired lifetime - but you are overwriting `mood_img_resized` on each iteration of your loop. – jasonharper Oct 22 '21 at 13:15

2 Answers2

2

If the same logic works for buttons, then the problem might be the image is garbage collected, in this case you cannot store the image as an attribute of some class to hold reference, because over each iteration each image will be overwritten, holding refence to the last image only. Same happens with global as well. So the way to go here would be appending to a list. Try something like:

lst = []
for count, mood in enumerate(mood_options):
    mood_img = Image.open(f"img/{mood}.png")
    mood_img_copy = mood_img.resize(img_size, Image.ANTIALIAS)
    mood_img_resized = ImageTk.PhotoImage(mood_img_copy)
    lst.append(mood_img_resized)
    Label(root, image=mood_img_resized).place(relx=spacing*(count+1), rely=0.35)
    print(spacing * (count + 1))

Though it would be much better to place stuff using grid to place it in a grid like manner. If your worried about responsiveness of the window then look at weight:

from tkinter import *

root = Tk()

mood_options = ['a', 'b', 'c']
for count, mood in enumerate(mood_options):
    Label(root, text=mood).grid(row=0,column=count,padx=50)
    root.grid_columnconfigure(count,weight=1)

root.grid_rowconfigure(0,weight=1)

root.mainloop()
Delrius Euphoria
  • 14,910
  • 3
  • 15
  • 46
0

Is your spacing the actual amount of space in pixels? If so, leave away the relx and only use x. relx has to be a float between 0.0 and 1.0.

meifster
  • 28
  • 3
  • I'm using `relx` to preserve relative position when the window is rescaled. The thing is that i have used the exact same method for placing buttons (with relx, spacing, count etc.) which works, but somehow for images, or labels doesn't work. – Barco Oct 22 '21 at 12:44
  • Well I just checked, and your method also works for Labels (with text only, no images): `from tkinter import * root = Tk() root.geometry('500x500') mood_options = ['a', 'b', 'c'] spacing = 0.25 for count, mood in enumerate(mood_options): mood_img_label = Label(root, text=mood).place(relx=spacing*(count+1), rely=0.35) print(spacing * (count + 1)) root.mainloop()` – meifster Oct 22 '21 at 12:49
  • Yes, that's why i find it confusing. I works with buttons (as i have done before) and labels, but not with a label with an image in it. It is just the case that it is not possible like this or am i just doing it wrong? – Barco Oct 22 '21 at 12:57