Reputation: 19627
Based on this answer, I wanted to add a second widget below the main image.
The image is intended to fit the window when this one is resized by the user.
See the example from @Marcin:
The issue is that if I add any widget to the window, something weird is happening (here with a Text):
The image is growing progressively until it fill the entire window, and the second widget disappears.
Here is my code:
from tkinter import *
from PIL import Image, ImageTk
from io import BytesIO
import base64
root = Tk()
root.title("Title")
root.geometry("600x600")
class ImageFrame(Frame):
def __init__(self, master, *pargs):
Frame.__init__(self, master, *pargs)
self.black_pixel = BytesIO(base64.b64decode("R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs="))
self.image = Image.open(self.black_pixel)
self.img_copy= self.image.copy()
self.background_image = ImageTk.PhotoImage(self.image)
self.background = Label(self, image=self.background_image)
self.background.pack(fill=BOTH, expand=YES)
self.background.bind('<Configure>', self._resize_image)
self.width = 1
self.height = 1
def _resize_image(self,event):
new_width = event.width
new_height = event.height
if new_width != self.width or new_height != self.height:
self.width = new_width
self.height = new_height
self.image = self.img_copy.resize((new_width, new_height))
self.background_image = ImageTk.PhotoImage(self.image)
self.background.configure(image = self.background_image)
img = ImageFrame(root)
txt = Text(root)
img.pack(fill=BOTH, expand=YES)
txt.pack()
root.mainloop()
I tried by packing img
and txt
with different options but it chnanged nothing.
Does anyone have an idea of what I am doing wrong, please?
Upvotes: 2
Views: 1671
Reputation: 21453
Calling the .configure
on a widget from within the <Configure>
callback on same widget is a lot like recursion, there is always a risk of never ending.
When <Configure>
triggers the event.width
and event.height
include the automatic padding of 2 pixels, so setting the image to that size increases the size of the Label
by 4 pixels firing the <Configure>
event again.
This doesn't happen in the example by @markus because once the Label is the size of a window with forced geometry it won't reconfigure again, this is also what happens in your program once the image has finished consuming all the space on the window.
The really quick fix is to change:
new_width = event.width
new_height = event.height
To:
new_width = event.width - 4
new_height = event.height - 4
To compensate for the spacing but I give absolutely no guarantee that it will work consistently.
Here is a different implementation that uses a Canvas
instead of a Frame
and Label
:
class ImageFrame(Canvas):
def __init__(self, master, *pargs ,**kw):
Canvas.__init__(self, master, *pargs,**kw)
self.black_pixel = BytesIO(base64.b64decode("R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs="))
self.img_copy = Image.open(self.black_pixel)
self.image = None #this is overriden every time the image is redrawn so there is no need to make it yet
self.bind("<Configure>",self._resize_image)
def _resize_image(self,event):
origin = (0,0)
size = (event.width, event.height)
if self.bbox("bg") != origin + size:
self.delete("bg")
self.image = self.img_copy.resize(size)
self.background_image = ImageTk.PhotoImage(self.image)
self.create_image(*origin,anchor="nw",image=self.background_image,tags="bg")
self.tag_lower("bg","all")
This way instead of reconfiguring the widget it just redraws the image on the canvas.
Upvotes: 1