Из того, что я прочитал, 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, получать вход пользователя, а затем отображать новое изображение?
Для меня работал следующий цикл. (Это беспорядочно и может быть улучшено, но оно работает.)
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()
update_idletasks
иupdate
». Другое дело, что в дальнейшем речь идет об использованииafter()
как я и предлагал, для чего не нужно помещать «всю мою логику в функцию tkinter», потому что функция обратного вызоваafter()
сама может делать все, что захочет, включая выполнение вызовов всех функций, не связанных с tkinter, которые ему необходимы.