Tkinter 无法绘制到另一个 window

Tkinter cannot draw to another window

我想做一些应该相当简单的事情,但我正在努力让它发挥作用。

基本上我有 2 个 Tkinter windows(canvas_tkcontrol_tk)。

canvas_tk 中,我想显示一张图片并在其上画一个圆圈。

control_tk中,我有一个输入要绘制的圆的半径的条目。

在我的代码中,关键行位于代码的最底部:

self.panel = tk.Label(canvas_tk, image=image)

如果我在 control_tk 中制作标签或者如果我不指定父级,它实际上可以正常工作并在 control_tk window 中绘制圆圈 但是我希望在 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

这是我的最小代码:

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()

回溯错误基本上是抱怨图片不存在

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

首先,你不应该使用多个Tk()实例。其次,你最好不要在跟踪输入变化时使用 trace(),在用户输入值并按下 Enter 键后使用 bind('<Return>', ...) 触发绘图。以下是基于您的修改后的代码:

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()