当前位置:   article > 正文

Python turtle 实现绘制图片、图片旋转动画效果详解_python turtle image

python turtle image

众所周知, turtle模块由于使用简单便捷, 是Python初学者较常用的模块之一。本文介绍如何使用PIL库在用turtle编写的程序中实现绘制图片,以及图片旋转的动画效果。
(Python初学者如果看不懂文章,可直接将文中的代码复制粘贴到你的代码中使用。)

使用PIL库

在Python中, 实现图片的旋转需要使用PIL库, 该库可通过pip安装。
使用PIL的Image.open函数可以实现加载图片; 使用ImageTk.PhotoImage可将PIL.Image对象转换为tkinter使用的类型。
由于 turtle模块与tkinter紧密结合,turtle的绘图依赖于tkinter实现,这里就需要用到tkinter。
PIL与tkinter实现的显示图片的程序如下:

from tkinter import *
from PIL import Image,ImageTk
root=Tk()
cv=Canvas(root,bg='black')
cv.pack(side=TOP,expand=True,fill=BOTH)
image=Image.open("bh.png","r")
image=image.resize((100,100))
imtk=ImageTk.PhotoImage(image)
id=cv.create_image(100,100,image=imtk)
cv.after(20,animate)

root.mainloop()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

效果图:
效果图
利用PIL.Image对象的rotate方法可实现图片的旋转。
有动画实现的代码:

from tkinter import *
from PIL import Image,ImageTk

angle = 0
def animate():
    global angle,id
    angle += 10
    image=old.rotate(angle);imtk=ImageTk.PhotoImage(image)
    cv.delete(id)
    id=cv.create_image(100,100,image=imtk)
    cv.after(20,animate)

root=Tk()
cv=Canvas(root,bg='black')
cv.pack(side=TOP,expand=True,fill=BOTH)
old=Image.open("blackhole.jpg","r").resize((100,100))
imtk=ImageTk.PhotoImage(old)
id=cv.create_image(100,100,image=imtk)
cv.after(20,animate)

root.mainloop()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 问: 为什么这里不加global imtk就不能产生旋转的效果?
    在tkinter中, ImageTk.PhotoImage()对象必须被创建为一个引用, 否则对象的内存空间将会被回收, 导致图像无法正常显示。因此, 应将ImageTk.PhotoImage()对象保存到一个列表或字典中, 或声明为全局变量(比如在这里)。
from tkinter import *
from PIL import Image,ImageTk

angle = 0
def animate():
    global imtk,angle,id # 注意这行增加的imtk
    angle += 10
    image=old.rotate(angle)
    imtk=ImageTk.PhotoImage(image)
    cv.delete(id)
    id=cv.create_image(100,100,image=imtk)
    cv.after(20,animate)

root=Tk()
cv=Canvas(root,bg='black')
cv.pack(side=TOP,expand=True,fill=BOTH)
old=Image.open("blackhole.jpg","r").resize((100,100))
imtk=ImageTk.PhotoImage(old)
id=cv.create_image(100,100,image=imtk)
cv.after(20,animate)

root.mainloop()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

运行效果图:
效果图

实现图片旋转

作者打开冗长的turtle模块的源代码, (读了半天)找到了TurtleScreenBase类, 用于底层的绘制图形等操作。 程序的关键是用自定义的函数, 替代turtle中原有的函数

完整代码如下(如果看不懂,可以直接复制粘贴到你的代码中使用):

from turtle import *
from turtle import TurtleScreenBase
try:
    from PIL import Image,ImageTk
except ImportError:
    Image=None

# 重写turtle模块中的函数,在turtle模块源码的基础上加以修改
images={} # 用于创建图像的引用
def _image(self,filename):
    img=Image.open(filename)
    im = ImageTk.PhotoImage(img)
    im.raw = img
     # 图像的缩放缓存,两项分别是放大倍数和PhotoImage对象,避免重复缩放图片,提高性能
    im.zoomcache = [None,None]
    return im

def _createimage(self, image):
    """Create and return image item on canvas.
    """

    id = self.cv.create_image(0, 0, image=image)
    return id

def _drawimage(self, item, pos, image, angle=None,zoom=None):
    """Configure image item as to draw image object
    at position (x,y) on canvas)
    """
    w=self.window_width();h=self.window_height()
    if not (-h//2 < pos[1] < h//2\
        and -w//2 <= -pos[0] < w//2): # 图像不在屏幕内
        self.cv.itemconfig(item, image=self._blankimage()) # 不绘制
        return
    prev=image
    if zoom:
        if zoom == image.zoomcache[0]:
            image=image.zoomcache[1] # 使用该图像的缩放缓存
        else:
            raw=image.raw
            size=(int(raw.size[0] * zoom), int(raw.size[1] * zoom))
            raw = raw.resize(size,resample=Image.Resampling.BILINEAR)
            image=ImageTk.PhotoImage(raw)
            image.raw=raw # 更新PhotoImage的raw属性
            prev.zoomcache=[zoom,image] # 更新缩放缓存

    if angle is not None:
        
        raw=image.raw
        image=ImageTk.PhotoImage(raw.rotate(angle)) # 旋转一定角度
        image.raw=raw
        
    images[item]=image # 创建image的引用, 防止image被Python垃圾回收而图像无法绘制

    x, y = pos
    self.cv.coords(item, (x * self.xscale, -y * self.yscale))
    self.cv.itemconfig(item, image=image)

def register_shape(self, name, shape=None):
    if shape is None:
        # 形状名称中需要加入"."才能用于图片
        if "." in name:
            shape = Shape("image", self._image(name))
        else:
            raise TurtleGraphicsError("Bad arguments for register_shape.\n"
                                      + "Use  help(register_shape)" )
    elif isinstance(shape, tuple):
        shape = Shape("polygon", shape)
    ## else shape assumed to be Shape-instance
    self._shapes[name] = shape

def _drawturtle(self):
        """Manages the correct rendering of the turtle with respect to
        its shape, resizemode, stretch and tilt etc."""
        screen = self.screen
        shape = screen._shapes[self.turtle.shapeIndex]
        ttype = shape._type
        titem = self.turtle._item
        if self._shown and screen._updatecounter == 0 and screen._tracing > 0:
            self._hidden_from_screen = False
            tshape = shape._data
            if ttype == "polygon":
                if self._resizemode == "noresize": w = 1
                elif self._resizemode == "auto": w = self._pensize
                else: w =self._outlinewidth
                shape = self._polytrafo(self._getshapepoly(tshape))
                fc, oc = self._fillcolor, self._pencolor
                screen._drawpoly(titem, shape, fill=fc, outline=oc,
                                                      width=w, top=True)
            elif ttype == "image":
                screen._drawimage(titem, self._position, tshape,
                                  self.heading(),self._stretchfactor[0])
            elif ttype == "compound":
                for item, (poly, fc, oc) in zip(titem, tshape):
                    poly = self._polytrafo(self._getshapepoly(poly, True))
                    screen._drawpoly(item, poly, fill=self._cc(fc),
                                     outline=self._cc(oc), width=self._outlinewidth, top=True)
        else:
            if self._hidden_from_screen:
                return
            if ttype == "polygon":
                screen._drawpoly(titem, ((0, 0), (0, 0), (0, 0)), "", "")
            elif ttype == "image":
                screen._drawimage(titem, self._position,
                                          screen._shapes["blank"]._data)
                if titem in images:del images[titem] # 如果已隐藏,则释放图像引用
            elif ttype == "compound":
                for item in titem:
                    screen._drawpoly(item, ((0, 0), (0, 0), (0, 0)), "", "")
            self._hidden_from_screen = True

if Image: # 若导入PIL模块成功
    # 修改turtle模块,用重写的函数替换turtle模块中原来的函数
    turtle.TurtleScreenBase._image=_image
    turtle.TurtleScreenBase._createimage=_createimage
    turtle.TurtleScreenBase._drawimage=_drawimage
    turtle.TurtleScreen.register_shape=register_shape
    turtle.RawTurtle._drawturtle=_drawturtle

# 下面是你自己的程序代码
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119

下面是用turtle模块编写的真正的程序,测试图像旋转功能:

scr=getscreen()
scr.register_shape('blackhole.jpg')
shape('blackhole.jpg')
while True:
    forward(60)
    left(72)
done()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

运行效果:
运行效果

总结

Python自带的turtle模块的确存在一些缺陷。如不支持jpgpng等常见图像格式,不能实现图像旋转等。
这里用了自定义的函数,替换了turtle库中自带的函数,弥补了turtle模块的缺陷。读者可以将上面的代码直接嵌入自己的代码中使用。
当然,或许有其他的解决方案,比如直接在Python的安装目录中。修改turtle模块的源代码等。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/874191
推荐阅读
相关标签
  

闽ICP备14008679号