如何动态地将 Pillow 图像导入 Tkinter

How to get Pillow images into Tkinter dynamically

根据我的阅读,ImageTk 应该将 Pillow 图像转换为 Tkinter 可以显示的格式。 (事实上​​ ,当我不这样做时,Tkinter 会失败并出现错误。)但是,当我在我的 Windows 计算机上 运行 这个程序时,Tkinter 框说它没有响应并且不显示图像。

from PIL import Image, ImageDraw

class Block:
    def __init__(self, x=30, y=10, speed=None, size=2.5):
        self.x = x
        self.y = y
        self.size = size # radius

        if speed is None:
            speed = self.size * 2

        self.speed = speed

    def draw(self, image):
        width, height = image.size
        drawer = ImageDraw.Draw(image)
        drawer.rectangle(((self.x - self.size, height - (self.y - self.size)), (self.x + self.size, height - (self.y + self.size))), fill="white")


class BallDodge():
    def __init__(self):
        self.width = 400
        self.height = 300
        self.main_player = Block(self.width // 2)
        self.balls = []
        self.frame = 0
        self.frames_between_balls = 20
        self.frames_since_last_ball = self.frames_between_balls
        self.input_function = self.get_human_input
        self.states = []


    @property
    def state(self):
        ans = Image.new('RGBA', (self.width, self.height), "black")
        self.main_player.draw(ans)

        return ans

    def get_human_input(self, *args, **kwargs):
        try:
            ans = int(input("1, 2, 3, or 4:"))
            if ans > 4:
                raise ValueError("Can't be greater than 4")
            elif ans < 1:
                raise ValueError("Can't be less than 1")
            if ans == 1:
                return [1, 0]
            elif ans == 2:
                return [0, 0]
            elif ans == 3:
                return [0, 1]
            elif ans == 4:
                return [1, 1]
            else:
                raise ValueError("Somehow it's not one of the specified numbers. You are magical.")
        except Exception as e:
            print("Invalid input")
            print(e)
            return self.get_human_input()

    def step(self):
        inpt = self.input_function(self.state)
        directions = [-1, 1]
        for i in range(2):
            self.main_player.x += directions[i] * inpt[i]

        self.states.append(self.state)

if __name__ == "__main__":
    game = BallDodge()
    import tkinter as tk
    from PIL import ImageTk, Image
    root = tk.Tk()
    my_image = ImageTk.PhotoImage(game.state)
    game.state.show()
    while True:
        panel = tk.Label(image = ImageTk.PhotoImage(game.state), master = root)
        panel.pack()
        # panel = Label(root, image = ImageTk.PhotoImage(game.state))
        # panel.pack(side = "bottom", fill = "both", expand = "yes")
        root.update_idletasks()
        root.update()
        game.step()

我注意到的事情: 如果我这样做 game.state.show() 它会很好地打开图像,所以那里肯定有图像。 如果我按下其中一个数字键前进到下一帧,Tkinter window 会变成原来的两倍高,但仍然不显示任何内容。

如何获得一个循环,以便我可以在 Tkinter 中显示 Pillow 图像 window,获取用户输入,然后显示新图像?

使用以下循环对我有用。 (它很乱,可以改进,但它有效。)

if __name__ == "__main__":
    import tkinter as tk
    from PIL import ImageTk, Image

    game = BallDodge()
    root = tk.Tk()
    pilImage = game.state
    tkimage = ImageTk.PhotoImage(pilImage)
    width, height = pilImage.size
    root.geometry('{}x{}'.format(width+1, height+1))
    canvas = tk.Canvas(root, width=width, height=height, bg="blue")
    canvas.pack()
    imagesprite = canvas.create_image(0, 0, anchor=tk.NW, image=tkimage)
    while True:
        tkimage =  ImageTk.PhotoImage(game.state)
        imagesprite = canvas.create_image(0, 0, anchor=tk.NW, image=tkimage)
        root.update_idletasks()
        root.update()
        game.step()