首页 » 父与子的编程之旅:与小卡特一起学Python » 父与子的编程之旅:与小卡特一起学Python全文在线阅读

《父与子的编程之旅:与小卡特一起学Python》16.3 在窗口中画图

关灯直达底部

现在我们有了一个 Pygame 窗口,在我们优雅地将它关闭之前它会一直打开。代码清单 16-2 的第 3 行中的 [640, 480] 是窗口的大小,表示 640 像素宽、480 像素高。下面就在这里面画一些图形。按照代码清单 16-4 修改你的程序。

代码清单 16-3 画一个圆

什么是翻转

对于 Pygame 窗口中显示的所有内容,Pygame 中的显示对象(我们的显示对象名为 screen,这在代码清单 16-3 的第 3 行创建)都会有这些内容的两个副本。这样做的原因是,开始动画时,我们希望让动画尽可能流畅,速度尽可能快。所以不必在每次对图形做一个小小的修改时都更新显示,可以做很多修改后再“翻转”(flip)到图形的新版本。这样就会一次显示所有这些修改,而不是一个接一个地出现。这样一来,我们就不会显示出只画了一半的圆(或外星人,或者其他任何东西)。

可以把这两个副本当作一个“当前屏”和一个“下一屏”。当前屏就是我们现在看到的,下一屏是完成“翻转”之后看到的。做完“下一屏”上的所有修改后,再翻转到下一屏,就能看到这些改变。

如何建立一个圆

运行代码清单 16-3 中的程序时,应该能看到如下图这样,靠近窗口左上角有一个红色的圆。

毫不奇怪,pygame.draw.circle 函数会画一个圆。必须告诉它以下 5 件事。

  • 在哪个表面(surface)画这个圆。(在这里,要在第 3 行定义的表面上画圆,名为 screen,这就是显示表面。)

  • 用什么颜色来画。(在这里要用红色,对应的值为 [255, 0, 0]。)

  • 在什么位置画。(在这里要位于 [100, 100],这表示从左上角向下 100 像素再向右 100 像素的位置)。

  • 圆的大小。(这里是 30,这是圆的半径,也就是圆心到外围边界的距离,单位是像素。)

  • 线宽。(如果 width = 0,圆是完全填充的,这里就采用了完全填充。)

下面再来更详细地学习这 5 个方面。

术语箱

像素(pixel)这个词是“图像元素”(picture element)的简写。这表示屏幕上或图像中的一点。如果在一个图像浏览器中查看图片,充分放大(让图像非常大),就可以看到单个的像素。下面是一张照片的正常视图和放大视图,在放大视图中可以看到像素。

哇,你眼力真好!这些小线条实际上就是像素行。一般的计算机屏幕可能有 768 行像素,每行有 1024 个像素。我们就会说这个屏幕“分辨率是 1024×768”。有些屏幕的像素更多,有些可能比这要少。

Pygame 表面

在实际生活中如果我让你画一幅画,你可能会先问“我在哪儿画?”在 Pygame 中,我们要在一个表面上画图。显示表面就是我们在屏幕上看到的表面,也就是代码清单 16-3 中的 screen。不过 Pygame 程序可以有多个表面,可以把图像从一个表面复制到另一个表面。还可以对表面做一些处理,比如旋转表面或者调整它们的大小(让它们更大或更小)。

前面提到过,显示表面有两个副本。按软件术语来讲,我们说显示表面是双缓冲的(double-buffered)。正是因为这个原因,我们不会在屏幕上看到只画了一半的形状和图像。我们会在缓冲区里画圆、外星人或者任何东西,然后“翻转”显示表面,来显示已经完全绘制的图像。

Pygame 中的颜色

Pygame 中使用的颜色系统是很多计算机语言和程序中通用的系统,称为 RGB。这里的 R、G 和 B 分别代表红、绿和蓝。

你可能在自然课上已经学过,通过结合或混合光的三原色(红、绿和蓝)可以得到任何颜色。计算机上也采用了同样的做法。每个颜色(红、绿和蓝)对应一个从 0 到 255 的数。由一个包含 3 个整数的列表来给出颜色,每个数的范围在 0 到 255 之间。如果所有数都是 0,就没有任何颜色,这就是全黑,所以会得到黑色。如果三个数都是 255,会将 3 种颜色以最大亮度混合在一起,这就是白色。如果颜色是 [255, 0, 0],这就是纯红色,没有绿色和蓝色。纯绿就是 [0, 255, 0],纯蓝是 [0, 0, 255]。如果所有 3 个数都一样,比如 [150, 150,150],你会得到某种灰度。数字越小,灰度就越深,数字越大,灰度就越浅。

颜色名

Pygame 提供了一个命名颜色列表,如果你不想使用 [R, G, B] 记法,就可以使用这些命名颜色。定义好的颜色名有 600 多个。我不想在这里全部列出,不过如果你想看看到底有哪些颜色,可以在你的硬盘上搜索一个名为 colordict.py 的文件,然后在文本编辑器中打开这个文件。

如果你想使用这些颜色名,必须在程序最前面增加下面这行代码:

from pygame.color import THECOLORS  

然后,使用某个命名颜色时,可以这样做(我们的画圆例子中就是这样做的):

pygame.draw.circle(screen, THECOLORS[/"red/"],[100,100], 30, 0)  

 

到底怎么回事?

为什么是 255 呢?每个三原色(红、绿、蓝)都有 0 到 255 共 256 个不同的值。256 这个数字有什么特别的呢?为什么不是 200、300 或者 500 呢?

8 位总共正好能表示 256 个不同的值,也就是由 1 和 0 构成的 八位数的所有可能的组合。8 位也称为一个字节,字节是有自己的地址的最小内存块。计算机就是利用地址来查找某段内存的。

这就像在街道上一样。你的房子或公寓有一个地址,不过你的房间没有地址。房子就是街道上最小的“可寻址单位”。字节则是计算机内存中最小的“可寻址单位”。

也可以用 8 位以上来表示每种颜色,不过,8 位之后可用的位数可能就到 16 位(2 个字节)了,因为不完整的字节使用起来会不太方便。事实证明,根据人眼识别颜色的方式,用 8 位来表示实际可见的颜色完全足够了。

由于有 3 个值(红、绿、蓝),每个值有 8 位,总共就是 24 位,所以这种表示颜色的方法也称为“24 位颜色表示法”。对每个像素使用 24 位,每个三原色分别使用 8 位。

如果你想试验一下,看看红色、绿色和蓝色如何结合来生成不同的颜色,可以试试 colormixer.py 程序,运行本书的安装程序时会把这个程序放在 examples 文件夹下。利用这个程序,你可以尝试红、绿、蓝的任意组合,看看能得到什么颜色。

位置——屏幕坐标

如果想在屏幕上画个东西或者放上一个东西,需要指定这个东西应当放在屏幕上的哪个位置。这里有两个数:一个对应 x 轴(水平方向),还有一个对应 y 轴(垂直方向)。在 Pygame 中,这两个数从窗口左上角的 [0, 0] 坐标开始。

看到类似 [320, 240] 的一对数时,要知道第一个数表示水平方向,也就是相对于左边界的距离。第二个数表示垂直方向,也就是相对于顶边的距离。在数学和编程中,字母 x 通常用来表示水平距离,y 常用来表示垂直距离。

我们建立了一个 640 像素宽、480 像素高的窗口。如果希望在窗口正中间画圆,需要在 [320, 240] 上绘制。这个位置离左边界 320 像素,离上边界 240 像素。

下面尝试在窗口中间画圆。运行代码清单 16-4 中的程序。

代码清单 16-4 把圆放在窗口中间

这里使用坐标 [320, 240] 作为圆心。把运行代码清单 16-3 的结果与运行代码清单 16-4 时看到的结果做个比较,看看有什么差别。

形状大小

使用 Pygame 的 draw 函数画形状时,必须指定形状的尺寸。对于圆来说,只有一个尺寸,也就是半径。而像矩形之类的形状,则必须指定长和宽。

Pygame 有一种特殊的对象,名为 rect(这是“rectangle”(矩形)的简写),用来定义矩形区域。rect 要使用左上角坐标、宽和高来定义:

Rect(left, top, width, height)  

这里同时定义了位置和大小。下面是一个例子:

my_rect = Rect(250, 150, 300, 200)  

这会创建一个矩形,它的左上角距离窗口左边界 250 像素,距离窗口上边界 150 像素,宽为 300 像素,高为 200 像素。下面来试试看。

用下面这行代码替换代码清单 16-4 中的第 5 行,看看结果是什么:

矩形的位置和大小可以是一个简单的数字列表(或元组),也可以是一个 Pygame 的 Rect 对象。所以还可以把前面一行替换为下面这两行代码:

my_list = [250, 150, 300, 200]pygame.draw.rect(screen, [255,0,0], my_list, 0)  

或者

my_rect = pygame.Rect(250, 150, 300, 200)pygame.draw.rect(screen, [255,0,0], my_rect, 0) 

这就是最后得到的矩形。我增加了一些尺寸标注来说明每个数字分别表示什么含义。

注意这里只向 pygame.draw.rect 传递了 4 个参数,因为 rect 用一个参数就表示了位置和大小。在 pygame.draw.circle 中,位置和大小分别由两个不同的参数表示,所以需要传递 5 个参数。

像(Pygame)程序员一样思考

如果用 Rect(left, top, width, height) 创建一个矩形,还可以使用其他一些属性来移动和对齐这个 Rect:

  • 4 条边:topleftbottomright

  • 4 个角 : topleftbottomlefttoprightbottomright

  • 每条边的中点:midtopmidleftmidbottommidright

  • 中心:centercenterxcentery

  • 尺寸:sizewidthheight

这些属性只是为了提供方便。所以,如果你想移动一个矩形,让它的中心位于某个点,不必得出上坐标和左坐标分别是什么;可以直接访问中心位置。

线宽

画形状时最后需要指定的一点是线的粗细。在之前的例子中,我们使用的线宽都是 0,这会填充整个形状。如果使用不同的线宽,会看到形状的轮廓。

试着把线宽改为 2:

试试看有什么结果。再试试其他线宽。

现代艺术

想不想让计算机生成某种现代艺术?玩玩呗,试试代码清单 16-5。也可以在代码清单 16-4 的基础上做些修改,或者干脆从头开始键入。

代码清单 16-5 使用 draw.rect 实现艺术创作

import pygame, sys, randompygame.initscreen = pygame.display.set_mode([640,480])screen.fill([255, 255, 255])for i in range (100):    width = random.randint(0, 250)    height = random.randint(0, 100)    top = random.randint(0, 400)    left = random.randint(0, 500)    pygame.draw.rect(screen, [0,0,0], [left, top, width, height], 1)pygame.display.fliprunning = Truewhile running:    for event in pygame.event.get:if event.type == pygame.QUIT:    running = Falsepygame.quit  

运行这个程序,看看会得到什么。应该能得到如下图所示的结果:

你明白这个程序是怎么工作的吗?它会随机画 100 个大小不等、位置不同的矩形。为了让它更有“艺术性”,再增加一些颜色,另外将线宽也设为随机,如代码清单 16-7 所示。

代码清单 16-6 带颜色的现代艺术

运行这个程序时,每次你都会看到不同的东西。如果有看着不错的,可以给它起个富有想象力的名字,比如“机器之声”,看看能不能把它卖到你们当地的美术馆 !