项目场景:

turtle库画图的保存

项目场景:我们用turtle画的图,有时想保存成png或者jpg格式,该怎么处理呢?


问题描述

使用postscript函数和PIL库所遇到的问题

在网上很容易搜索到,turtle库中有postscript函数,可以将画图内容保存成eps文件,然后使用PIL中的Image.open()的方法,就可以打开该文件,并且save成想要的格式。但在使用过程中,我遇到了如下问题:

import turtle
from PIL import Image
Image.MAX_IMAGE_PIXELS = None
# 设置参数

turtle.screensize(3000, 3000)
turtle.color("red")
turtle.pensize(1)
turtle.speed(0)
turtle.ht()

# 画正方形,中心为原点

square = 1000
turtle.penup()
turtle.goto(square, square)
turtle.pendown()
turtle.rt(90)
for i in range(4):
    turtle.forward(square*2)
    turtle.rt(90)

# 保存图片,并修改格式

turtle.getscreen().getcanvas().postscript(file="1.eps")
im = Image.open("1.eps")
im.save("1.jpg")

square参数是正方形边长的一半,当我把square写为100,会得到以下图片:

python turtle怎么保存 turtle画图如何保存_python

分辨率704x591,图形看起来正常的,但是设置的画布长宽均为3000,背景不符合要求。

接下来把正方形半边长调整为1000,会得到如下图像:


得到了一张704x593的白图,我的猜想是postscript()函数默认保存的是一定大小的图片,我们需要设置相应的参数来使得画布上的内容全部保存。打开postscript()函数的介绍:

def postscript(self, cnf={}, **kw):
        """Print the contents of the canvas to a postscript
        file. Valid options: colormap, colormode, file, fontmap,
        height, pageanchor, pageheight, pagewidth, pagex, pagey,
        rotate, width, x, y."""
        return self.tk.call((self._w, 'postscript') +
                    self._options(cnf, kw))

我们可以看到有width和height的参数,我修改代码后运行。

turtle.getscreen().getcanvas().postscript(file="1.eps", width=1000, height=1000)

得到的还是一张白图,分辨率为751x752,可知width和height的设置并没有起到想要的结果。

为了解决这个问题,接下来就开始走各种各样的弯路,turtle的setup函数,以及打开eps文件看到的0.7504这串神秘数字,具体过程就不再一一赘述,也是这个过程发现网上这个方面的资料几乎没有,才想着写下这篇博客。


原因分析:

最终还是转到了postscript()这个函数本身:

turtle.getscreen().getcanvas(),这个函数返回的是canvas对象,属于tk库中的内容,找到了tk中canvas.postscrip()的相关文档(我只列了和本文相关的)。

height

Sets the height of the area to print. The default height is the Canvas height.

width

Sets the width of the Canvas area to be printed. Defaults to the width of the Canvas

pageheight

Sets the height of the printed page. The Canvas image will be scaled to fit. height is any valid screen distance.

pagewidth

Sets the width of the printed page. The Canvas image will be scaled to fit.

pagex

Sets the coordinate for the x positioning point. Can be any valid screen distance.

pagey

Sets the coordinate for the y positioning point. Can be any valid screen distance.

经过百度查询后得知postscript是页面描述语言,用于打印机打印文件,我们可以将postscript()函数理解为将画布上的内容打印到一张纸上,想要将内容完整打印,就要设置好画布的大小,即width和height,还要设置好这张纸的大小,即pagewidth和pageheight,turtle画图默认从中心开始画图,打印机默认从左上角开始打印,我们就需要调整坐标系,即调整x和y。

这是了解postscript函数机理后重新写的代码。

import turtle
from PIL import Image
Image.MAX_IMAGE_PIXELS = None


canvas_width = 1000
canvas_height = 1000

turtle.screensize(canvas_width, canvas_height)
turtle.color("red")
turtle.pensize(0)
turtle.speed(0)
turtle.ht()

# 例1***************************************************************************************
# 画正方形


square = 500
turtle.penup()
turtle.goto(square, square)
turtle.pendown()
turtle.rt(90)
for i in range(4):
    turtle.forward(square*2+1)
    turtle.rt(90)

turtle.getscreen().getcanvas().postscript(colormode="color", file="1.eps", pagewidth=canvas_width, pageheight=canvas_height, width=canvas_width, height=canvas_height, x=-canvas_width/2, y=-(canvas_height/2))
turtle.clear()
im = Image.open("1.eps")
print(im.size)
print(im.getpixel((canvas_width/2-square, canvas_height/2-square+1)))
for i in range(square*2):
    im.putpixel((canvas_width//2-square+i+1, canvas_height//2-square+i+1+1), (255, 0, 0))
# for i in range(1001):
#     if(im.getpixel((i, 500))!=(255,255,255)):
#         print(i)
#         break
# print(im.getpixel((200, 501)))
im.save('1.jpg')

#  例2  画圆********************************************************************
turtle.clearscreen()
turtle.color("red")
turtle.pensize(0)
turtle.speed(0)
turtle.ht()

center_x = 390
center_y = 0
circle = 100

turtle.penup()
turtle.goto(center_x, center_y-circle)
turtle.pendown()
turtle.seth(0)
turtle.circle(circle)
turtle.getscreen().getcanvas().postscript(colormode="color", file="2.eps", pagewidth=canvas_width, pageheight=canvas_height, width=canvas_width, height=canvas_height, x=-canvas_width/2, y=-canvas_height/2)

im = Image.open("2.eps")

for i in range(circle*2):
    im.putpixel((center_x - circle + i + 1+canvas_width//2, center_y+canvas_height//2), (255, 0, 0))
    im.putpixel((center_x+canvas_width//2, center_y - circle + i + 1+canvas_height//2), (255, 0, 0))
im.save('2.jpg')

python turtle怎么保存 turtle画图如何保存_python turtle怎么保存_02


python turtle怎么保存 turtle画图如何保存_开发语言_03


得到的图片分辨率1001x1001,图形大小和位置均基本符合要求,但整张图长宽多了一个像素。


总结:

关于为什么会多一个像素:

我的猜想是turtle库的画图语法本身是一种比较艺术的语言,例如原点是(0,0),向右移动100个像素turtle.forward(100),然后海龟的位置就变成了(3,0),这样来看海龟本身是不占像素的,但是我们储存位图的时候是按照矩阵的形式储存的,每一个点都是矩阵中的一个元素,这两种描述有冲突的地方,所以turtle库没有提供直接把画布内容转换成位图的函数,而是提供了一种转换成矢量图的方法postscript,我们最后再把矢量图转换成位图,经过了两次转换,就可能在某个环节就会出现问题。
因为我的目的只是能够画出完整且不严重失真的图片,所以就没有细究太多。(细究也没弄懂放弃了)