pythontkintertkinter-entrytkinter-buttontkinter-layout

The .place() function in tkinter uses different left window anchor for text and entry and buttons?


I place my text, entry and buttons in the same way based on the image width as seen in the code. When I now make the window bigger than the image width, then the buttons start moving like they use the left side of the window as a reference point while the text and entry still use the image corner as the reference point. What is the reason for this behavior and how to prevent it?

enter image description here When increasing the window width, only the buttons use the new window size as new reference. The text and entry have the image corner as a reference. enter image description here

edit: Minimum code example of the behavior

import tkinter as tk
from PIL import Image, ImageTk

# random pil image
image = Image.new('RGB', (800, 600), color = 'red')
img_shape = image.size

root = tk.Tk()
canvas = tk.Canvas(root, width=img_shape[0], height=img_shape[1] + 50)

tk_image = ImageTk.PhotoImage(image=image)
canvas.create_image(0, 0, anchor='nw', image=tk_image)
canvas.pack()

depth_min_entry = tk.Label(canvas, text="right")
depth_min_entry.place(x=img_shape[0] - 65, y=img_shape[1] + 5, height=20, width=50, anchor='ne')
depth_min_entry = tk.Entry(canvas, width=6)
depth_min_entry.place(x=img_shape[0] - 10, y=img_shape[1] + 5, height=20, width=50, anchor='ne')
depth_min_entry.insert(0, 5)

camera_info = tk.Label(canvas, text=f"left")
camera_info.place(x=10, y=img_shape[1] + 5, height=20, width=50, anchor='nw')

update_button = tk.Button(root, text="b right")
update_button.place(x=img_shape[0] - 10, y=img_shape[1] + 25, height=20, width=100, anchor='ne')

prev_button = tk.Button(root, text="b left")
prev_button.place(x=10, y=img_shape[1] + 25, height=20, width=100, anchor='nw')

root.mainloop()

Solution

  • place uses coordinates relative to the parent of the widget. Since your widgets have different parents, they could have different origins when using place.

    Since the label and entry is in the canvas, the coordinates will be relative to the canvas. You use pack to put the canvas in the root window and by default pack centers widgets along a side (in this case the top). So, when you make the window bigger the canvas moves inward and the widgets inside of the canvas also move. However, widgets outside of the canvas don't move since they are relative to the root window.

    You can prevent this in many different ways. One simple solution is to use the in_ parameter so that the widgets in the root are placed relative to the canvas.

    update_button.place(..., in_=canvas)
    prev_button.place(..., in_=canvas)
    

    Another solution is to make the two buttons children of the canvas so that everything is relative to the canvas.

    Another third solution is to create a frame for the buttons, and use appropriate pack options for it as well. That would give both the canvas and the frame for the buttons.

    I would argue that the best solution is to not use place at all. Instead, consider using grid in this particular case since you are in effect laying things out in a grid.