Как динамически получать изображения подушек в Tkinter

1

Из того, что я прочитал, 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 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 будет в два раза выше, но все равно ничего не отображает.

Как я могу получить такой цикл, чтобы я мог отображать изображение Pillow в окне Tkinter, получать вход пользователя, а затем отображать новое изображение?

Теги:
loops
image
tkinter
pillow

1 ответ

0
Лучший ответ

Для меня работал следующий цикл. (Это беспорядочно и может быть улучшено, но оно работает.)

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()
  • 1
    Я предлагаю вам прочитать весь связанный ответ. Где-то в одном из комментариев @Bryan Oakley, эксперта tkinter, говорится: «Нет причины вызывать одновременно update_idletasks и update ». Другое дело, что в дальнейшем речь идет об использовании after() как я и предлагал, для чего не нужно помещать «всю мою логику в функцию tkinter», потому что функция обратного вызова after() сама может делать все, что захочет, включая выполнение вызовов всех функций, не связанных с tkinter, которые ему необходимы.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню