Python 使用PIL和Tkinter裁剪和显示图像
在永远使用VB6后,我开始学习Python。为了帮助自己学习Tkinter的gui,我正在制作一个简单的老虎机。要设置轮子的动画,我有一个~300x2000的图像,其中所有我打算使用的图片都显示为条带。现在大约有6张照片。我只是一次显示一部分图像(轮子),使其旋转。不管怎样,我在代码的最后一部分遇到了问题,我需要从图像的结尾转换回开始(轮子向下旋转,所以开始是底部,结束是顶部) 由于某些原因,我无法正确地裁剪和显示图像,即使我认为我的脚本在逻辑上有意义。我的代码贴在下面。您可以制作一个分辨率为300x2000左右的图像,用于查看我遇到的问题。我的代码开始在我希望它显示的区域之外打印(在showsize变量之外),我不知道为什么 对此问题的任何帮助都将不胜感激。看起来这些作物并没有把图像剪得足够短,但是我找到的所有关于它的信息让我觉得我的脚本应该可以正常工作。我试着注释我的代码来解释发生了什么Python 使用PIL和Tkinter裁剪和显示图像,python,user-interface,tkinter,python-imaging-library,crop,Python,User Interface,Tkinter,Python Imaging Library,Crop,在永远使用VB6后,我开始学习Python。为了帮助自己学习Tkinter的gui,我正在制作一个简单的老虎机。要设置轮子的动画,我有一个~300x2000的图像,其中所有我打算使用的图片都显示为条带。现在大约有6张照片。我只是一次显示一部分图像(轮子),使其旋转。不管怎样,我在代码的最后一部分遇到了问题,我需要从图像的结尾转换回开始(轮子向下旋转,所以开始是底部,结束是顶部) 由于某些原因,我无法正确地裁剪和显示图像,即使我认为我的脚本在逻辑上有意义。我的代码贴在下面。您可以制作一个分辨率为3
from Tkinter import *
from PIL import Image, ImageTk
def main():
root = Tk()
root.title = ("Slot Machine")
canvas = Canvas(root, width=1500, height=800)
canvas.pack()
im = Image.open("colors.png")
wheelw = im.size[0] #width of source image
wheelh = im.size[1] #height of source image
showsize = 400 #amount of source image to show at a time - part of 'wheel' you can see
speed = 3 #spin speed of wheel
bx1 = 250 #Box 1 x - where the box will appear on the canvas
by = 250 #box 1 y
numberofspins = 100 #spin a few times through before stopping
cycle_period = 0 #amount of pause between each frame
for spintimes in range(1,numberofspins):
for y in range(wheelh,showsize,-speed): #spin to end of image, from bottom to top
cropped = im.crop((0, y-showsize, wheelw, y)) #crop which part of wheel is seen
tk_im = ImageTk.PhotoImage(cropped)
canvas.create_image(bx1, by, image=tk_im) #display image
canvas.update() # This refreshes the drawing on the canvas.
canvas.after(cycle_period) # This makes execution pause
for y in range (speed,showsize,speed): #add 2nd image to make spin loop
cropped1 = im.crop((0, 0, wheelw, showsize-y)) #img crop 1
cropped2 = im.crop((0, wheelh - y, wheelw, wheelh)) #img crop 2
tk_im1 = tk_im2 = None
tk_im1 = ImageTk.PhotoImage(cropped1)
tk_im2 = ImageTk.PhotoImage(cropped2)
#canvas.delete(ALL)
canvas.create_image(bx1, by, image=tk_im2) ###THIS IS WHERE THE PROBLEM IS..
canvas.create_image(bx1, by + y, image=tk_im1) ###PROBLEM
#For some reason these 2 lines are overdrawing where they should be. as y increases, the cropped img size should decrease, but doesn't
canvas.update() # This refreshes the drawing on the canvas
canvas.after(cycle_period) # This makes execution pause
root.mainloop()
if __name__ == '__main__':
main()
如果不在每个循环中重新创建图像,它会更简单、更快、更平滑。下面是我使用
canvas.move()
的解决方案。注意,我将canvas.create_image
调用移到for循环之外。我还将代码放在一个类中,并添加了一个“旋转”按钮,并添加了一些东西,使其在没有错误的情况下退出
from Tkinter import *
from PIL import Image, ImageTk
import sys
class SlotMachine():
def __init__(self):
root = Tk()
root.title = ("Slot Machine")
self.canvas = Canvas(root, width=1200, height=800)
self.canvas.grid(column=0,row=0)
button = Button(root, text="Spin!", width=20, command = self.spin)
button.grid(column=0,row=1)
self.alive = True
root.protocol("WM_DELETE_WINDOW", self.die)
root.mainloop()
def spin(self):
im = Image.open("colors.png")
wheelw = im.size[0] #width of source image
wheelh = im.size[1] #height of source image
showsize = 400 # amount of source image to show at a time -
# part of 'wheel' you can see
speed = 3 #spin speed of wheel
bx1 = 250 #Box 1 x - where the box will appear on the canvas
by = 250 #box 1 y
numberofspins = 100 #spin a few times through before stopping
cycle_period = 3 #amount of pause between each frame
tk_im1 = ImageTk.PhotoImage(im)
tk_im2 = ImageTk.PhotoImage(im)
im1id = self.canvas.create_image(bx1, by + showsize, image=tk_im1)
im2id = self.canvas.create_image(bx1, by + showsize + wheelh,
image=tk_im2)
for spintimes in range(1,numberofspins):
for y in range(wheelh,0,-speed):
if self.alive:
self.canvas.move(im1id, 0, -speed)
self.canvas.move(im2id, 0, -speed)
self.canvas.update()
self.canvas.after(cycle_period)
else:
sys.exit()
self.canvas.move(im1id, 0, wheelh)
self.canvas.move(im2id, 0, wheelh)
def die(self):
self.alive = False
if __name__ == '__main__':
mySlotMachine = SlotMachine()
这将把轮子的一部分放在盒子外面,但是你可以把你的老虎机纹理放在上面。我喜欢你使用
move
的方式,但这不是一个很好的解决方案。一个很好的经验法则是永远不要将sleep语句放在GUI中,这样做是通过在之后调用。这将导致GUI完全冻结,这不是一个好的用户体验。更好的解决方案是在制作动画之前启动事件循环,然后在
之后使用定期调用函数来更新显示。@BryanOakley:我明白你的意思,这不理想,我会改变它。尽管如此,这一部分并不是真正的问题所在。这确实解决了错误绘制图像的主要问题。@Junxx:除非你看到了我没有看到的东西,否则不会更好。您仍然使用单个参数在
之后调用,这意味着GUI将定期休眠。您还调用了update
,这可能会产生不良副作用。除非你教初学者调用update
的缺点,否则首先教他们使用它不是一个好主意。