I want to do something that should be rather simple, but I'm struggling to make it work.
Basically I have 2 Tkinter windows (canvas_tk
and control_tk
).
In canvas_tk
I want to show an image and draw a circle on top of it.
In control_tk
I have an Entry to input the radius of the circle to be drawn.
In my code the critical line is at the very bottom of the code:
self.panel = tk.Label(canvas_tk, image=image)
If I make the label in the control_tk or if I dont specify the parent, it actually works fine and draws the circles in the control_tk window However I want the circles to be drawn in the canvas_tk window
self.panel = tk.Label(image=image) # Works
self.panel = tk.Label(control_tk, image=image) # Works
self.panel = tk.Label(canvas_tk, image=image) # Fails
Here's my minimal code:
import tkinter as tk
from PIL import Image, ImageTk, ImageDraw
control_tk = tk.Tk()
canvas_tk = tk.Tk()
control_tk.geometry("300x300")
canvas_tk.geometry("900x900")
class Drawing:
def __init__(self):
# Numerical entry with a variable traced with callback to "on_change" function
self.radius_var = tk.IntVar()
self.radius_var.trace_variable("w", self.on_change)
tk.Entry(control_tk, textvariable=self.radius_var).grid()
# Initialize image and panel
self.image = Image.new('RGB', (1000, 1000))
self.panel = None
# mainloop for the two tkinter windows
control_tk.mainloop()
canvas_tk.mainloop()
def on_change(self, *args):
print("Value changed") # to check that function is being called
draw = ImageDraw.Draw(self.image)
draw.ellipse((50-self.radius_var.get(), 50-self.radius_var.get(),
50+self.radius_var.get(), 50+self.radius_var.get()),
outline='blue', width=3)
image = ImageTk.PhotoImage(self.image)
if self.panel: # update the image
self.panel.configure(image=image)
self.panel.image = image
else: # if it's the first time initialize the panel
self.panel = tk.Label(canvas_tk, image=image)
self.panel.image = image
self.panel.grid(sticky="w")
Drawing()
And the traceback error basically complains about image not existing
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\lab\AppData\Local\Programs\Python\Python37\lib\tkinter\__init__.py", line 508, in get
return self._tk.getint(value)
_tkinter.TclError: expected integer but got ""
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\Users\lab\AppData\Local\Programs\Python\Python37\lib\tkinter\__init__.py", line 1705, in __call__
return self.func(*args)
File "C:/GIT/142-277-00_pyScuti2/test.py", line 29, in on_change
draw.ellipse((50-self.radius_var.get(), 50-self.radius_var.get(),
File "C:\Users\lab\AppData\Local\Programs\Python\Python37\lib\tkinter\__init__.py", line 510, in get
return int(self._tk.getdouble(value))
_tkinter.TclError: expected floating-point number but got ""
Value changed
Value changed
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\lab\AppData\Local\Programs\Python\Python37\lib\tkinter\__init__.py", line 1705, in __call__
return self.func(*args)
File "C:/GIT/142-277-00_pyScuti2/test.py", line 38, in on_change
self.panel = tk.Label(canvas_tk, image=image)
File "C:\Users\lab\AppData\Local\Programs\Python\Python37\lib\tkinter\__init__.py", line 2766, in __init__
Widget.__init__(self, master, 'label', cnf, kw)
File "C:\Users\lab\AppData\Local\Programs\Python\Python37\lib\tkinter\__init__.py", line 2299, in __init__
(widgetName, self._w) + extra + self._options(cnf))
_tkinter.TclError: image "pyimage1" doesn't exist
First of all, you should not use multiple Tk()
instances. Second, you better not using trace()
on tracking input change, use bind('<Return>', ...)
to trigger drawing after user enters value and press Enter
key. Below is a modified code based on yours:
import tkinter as tk
from PIL import Image, ImageTk, ImageDraw
control_tk = tk.Tk()
control_tk.geometry("300x300+800+600")
canvas_tk = tk.Toplevel() # use Toplevel instead of Tk
canvas_tk.geometry("900x900+10+10")
class Drawing:
def __init__(self):
# Numerical entry with a variable traced with callback to "on_change" function
self.radius_var = tk.IntVar()
entry = tk.Entry(control_tk, textvariable=self.radius_var)
entry.grid()
# binding Enter key on entry to trigger the drawing
entry.bind('<Return>', self.on_change)
# Initialize image and panel
self.image = Image.new('RGB', (1000, 1000))
self.panel = None
# mainloop for the main window
control_tk.mainloop()
def on_change(self, *args):
try:
radius = self.radius_var.get()
except:
print('Invalid number')
return
print("Value changed") # to check that function is being called
draw = ImageDraw.Draw(self.image)
draw.ellipse((50-radius, 50-radius, 50+radius, 50+radius),
outline='blue', width=3)
image = ImageTk.PhotoImage(self.image)
if self.panel:
self.panel.configure(image=image)
else:
self.panel = tk.Label(canvas_tk, image=image)
self.panel.grid(sticky='w')
self.panel.image = image
Drawing()