Pygame 教程(4):图像传输和绘制文本

article/2025/10/3 17:45:44

本章,你将学会如何传输图像和绘制文本。

导航

上一章:绘制图形
下一章:监测游戏时间

文章目录

  • 导航
  • 加载图像
  • 导出图像
  • 绘制文本
  • 实例:画板
    • 添加常量
    • 限制坐标
    • 定义属性
    • 绘制色板
    • 更改线条粗细
    • 处理鼠标事件
    • 处理键盘事件
    • 调用事件处理方法
    • 完整代码
  • 结语

加载图像

上一章学习了绘制图形,对于一些简单的应用程序来说,这或许够用,但是对于更大型的应用程序,需要用到更复杂的图形的时候,使用pygame.draw模块进行绘制就会变得烦琐,而且该模块渲染的质量较低,不符合大多数应用程序的要求。

所以,面对这种情况,通常的做法是,把复杂的图形编写在图片文件中,并使用 Pygame 加载它,最后绘制到屏幕上。

Pygame 使用 image模块进行图像传输的操作。最常用的函数就是用于加载图像文件的pygame.image.load函数。第一个参数filename可以是图像的文件名,也可以是一个 Python 类文件对象(file-like object,比如调用open函数返回的文件对象)。第二个可选参数name_hint可用于显式指定文件的类型,但在大多数情况下,Pygame 会自动识别文件类型,从数据中创建一个Surface对象。

Pygame 支持加载的图像文件类型如下(详见官方文档):

  • BMP
  • GIF (非动画,对于含动画的文件只使用第一帧画面)
  • JPEG
  • LBM(还有PBM,PGM,PPM)
  • PCX
  • PNG
  • PNM
  • SVG(有限支持,使用 Nano SVG)
  • TGA(未压缩的)
  • TIFF
  • WEBP
  • XPM

导出图像

与加载文件相反,Pygame 也支持将Surface对象导出为图像文件。使用pygame.image.save函数即可。该函数的第一个参数surface指定要导出的Surface对象。第二个参数filenameload函数相同,可以是图像的文件名,也可以是一个 Python 类文件对象。第三个可选参数name_hint也同load函数。

与加载图像文件不同的是,Pygame 中支持导出的文件类型比较少,具体如下(详见官方文档):

  • BMP
  • JPEG
  • PNG
  • TGA

image模块中其他的函数可以阅读官方文档进行学习,再次不做赘述。

绘制文本

为了与用户交互,显示文本也是一项很重要的技术。作为一个 GUI(Graphical User Interface,图形用户界面)程序,使用print输出文本是不现实的,需要将文本绘制到屏幕上。

为了绘制文本,需要加载对应的字体。Pygame 使用pygame.font模块加载并渲染字体。

pygame.font.Font类用于从给定的文件名或 Python 文件对象中加载一个字体。如果对文件名传入None将会加载 Pygame 的默认字体 freesansbold.ttf(传入字符串'freesansbold.ttf'效果相同),你可以在 Python 的安装目录中的Lib/site-packages/pygame文件夹中找到这个字体文件。参数size指定字体的高度像素值,这个值在字体创建后将无法被改变。

Font类中定义的bolditalicunderline等属性可以为字体添加更多的格式,详见官方文档。

如果始终使用文件名导入字体会比较麻烦,因此font模块中还提供了函数SysFont以从系统字体中导入文件(与Font类相反,该函数无法导入字体文件)。该函数依然返回Font对象。参数sizeFont类。可选参数bolditalic指定是否使用粗体或斜体。

Font类中还定义了一个重要的方法:render。它用于使用此字体渲染指定文本,并返回渲染的Surface对象。

render函数的完整定义是pygame.font.render(text, antialias, color, background=None) -> Surface。参数text指定渲染的文本。参数antialias指定是否使用抗锯齿技术。color指定渲染的文本颜色。可选参数background可以指定渲染文本的背景颜色,默认情况下,背景颜色是透明的。

实例:画板

依照惯例,我们将创建一个画板应用程序。

请新建一个文件,命名为drawing_board.py,并添加如下代码以创建初始窗口:

import sysimport pygameclass DrawingBoard:def __init__(self):pygame.init()self.screen = pygame.display.set_mode((800, 800))pygame.display.set_caption('Drawing Board')self.screen.fill((255, 255, 255))def process_events(self):for event in pygame.event.get():if event.type == pygame.QUIT:pygame.quit()sys.exit()def run(self):while True:self.process_events()pygame.display.update()if __name__ == '__main__':app = DrawingBoard()app.run()

与上一章相似,我们把程序代码封装到了一个类中。略微不同的是,我们把处理事件循环的代码封装到了process_events方法中,因为这个实例对事件的处理比较繁重,全部堆积在run方法中并不是一个好办法。

我们在构造函数中就将屏幕填充为白色。因为画板应用程序需要实时更新,如果每一次循环都填充屏幕,那么每一次循环过后用户绘制的内容都将被覆盖。

添加常量

类的外部添加以下声明常量的代码:

import sysimport pygameCOLORS = ((0, 0, 0),(255, 0, 0),(0, 255, 0),(0, 0, 255),(255, 255, 0),(255, 0, 255),(0, 255, 255),(255, 255, 255)
)  # 色板中的所有颜色RADIUS = 15  # 色板中设置颜色的圆圈的半径
GAP = 15  # 色板中相邻的圆圈之间的空隙
CENTER_DIST = RADIUS * 2 + GAP  # 色板中相邻的圆圈的中心之间的距离
PALETTE_RECT = pygame.Rect(0, 0, 800, RADIUS * 2 + GAP * 2)  # 色板的矩形坐标
PALETTE_HEIGHT = PALETTE_RECT.height  # 色板的矩形的高度
LINE_WIDTH_MAX = 20  # 线条的最大粗度class DrawingBoard:
# ...

在编写代码时,对常量的命名可以使用全部大写加下划线的形式,以便与变量区分开。而没有把常量声明为类实例的属性是因为它们与类的关系并不是非常密切。

限制坐标

当鼠标移动到色板上时,不应该继续画在色板上。所以需要限制鼠标的坐标。在代码中添加以下两个全局函数

def clamp(value, low, high):return low if value < low else (high if value > high else value)def clamp_pos(pos):y = clamp(pos[1], PALETTE_HEIGHT, 800)return (pos[0], y)

函数clamp用于将指定的数限制在lowhigh之间。该函数使用了三目运算符,其格式为[statement_1] if [expression] else [statement_2],即当expressionTrue时,返回statement_1,反之,返回statement_2。为了方便,我们还定义了clamp_pos函数,用于将坐标的y值限制在COLORS_HEIGHT和800之间。

这两个函数也与类没有直接关系,所以声明为全局函数,而不是类方法。

定义属性

在类的构造函数中添加以下代码:

# ...
pygame.display.set_caption('Drawing Board')self.color = (0, 0, 0)  # 画笔的颜色
self.width = 1  # 线条的粗细
self.last_pos = self.pos = (0, 0)  # 鼠标上一帧的位置以及当前位置
self.circles = []  # 色板中由圆圈的中心和对应颜色的元组组合而成。
self.font = pygame.font.SysFont('Times New Roman', 20)  # 字体
self.drawing = False  # 用户是否正在绘画# ...

绘制色板

添加以下的两个类方法:

def draw_colors(self):for i in range(len(COLORS)):center = (i * CENTER_DIST + GAP + RADIUS, GAP + RADIUS)pygame.draw.circle(self.screen, COLORS[i], center, RADIUS)self.circles.append((center, COLORS[i]))pygame.draw.line(self.screen, (0, 0, 0), (0, PALETTE_HEIGHT), (800, PALETTE_HEIGHT))def draw_text(self):surf = self.font.render(f'Line width: {self.width}', True, (0, 0, 0))rect = surf.get_rect()rect.right = PALETTE_RECT.right - GAPrect.centery = PALETTE_RECT.centeryself.screen.blit(surf, rect)

draw_colors方法遍历所有的颜色,并绘制圆圈,为了使用户区分色板,我们还在下面绘制了一条线段。

draw_text方法显示当前的线条粗细。它使用render方法渲染文本,再绘制到屏幕上。为了调整文本的位置,我们做了一些工作:先把矩形的右侧移动到距离色板的右侧一个GAP的空隙,再使矩形的y中心对齐到色板矩形的y中心。这也是pygame.Rect的强大之处,多个属性可以使位置的处理更加灵活。

在构造函数的最后,调用这两个方法:

# ...
self.drawing = False  # 用户是否正在绘画self.draw_colors()
self.draw_text()# ...

更改线条粗细

本实例的设计为,当用户按下上箭头或下箭头时,更改线条的粗细。这里需要一些处理。因为如果直接地填充屏幕,用户之前所画的图形将消失,虽然可以通过截取子图像(使用pygame.Surface.subsurface方法,详见拓展)保留用户所画的图形的副本,但是会过于麻烦。本实例选择修改draw_text方法,在绘制文本之前填充一个白色的矩形覆盖原有文本,修改后的代码如下:

def draw_text(self):surf = self.font.render(f'Line width: {self.width}', True, (0, 0, 0))rect = surf.get_rect()rect.right = PALETTE_RECT.right - GAPrect.centery = PALETTE_RECT.centeryfill_rect = pygame.Rect(rect.left - 5, rect.top - 5, rect.width + 10, rect.height + 10)pygame.draw.rect(self.screen, (255, 255, 255), fill_rect)self.screen.blit(surf, rect)

fill_rect是由rect向外延伸5个像素而成的,因为不同的文本渲染的宽度不同,多延伸5个像素可以确保完全地覆盖在原来的文本上。

处理鼠标事件

首先,添加以下全局函数到代码中:

def dist_between_points(point1, point2):return ((point1[0] - point2[0]) ** 2 + (point1[1] - point2[1]) ** 2) ** 0.5

该函数用于计算两点之间的距离,公式为 d = ( x 1 − x 2 ) 2 + ( y 1 − y 2 ) 2 d=\sqrt{(x_1-x_2)^2+(y_1-y_2)^2} d=(x1x2)2+(y1y2)2

然后,添加以下方法用于处理MOUSEBUTTONDOWN事件:

def on_mouse_down(self, pos):if PALETTE_RECT.collidepoint(pos):for center, color in self.circles:if dist_between_points(pos, center) < RADIUS:self.color = colorelse:self.last_pos = self.pos = posself.drawing = True

pygame.Rect.collidepoint方法用于检测指定的点是否在此矩形对象内。在本实例中,如果该方法返回True,说明用户点击了色板,于是遍历所有的圆圈,判断鼠标是否点击到了圆圈,如果是,则改变画笔的颜色。为了检测一个点是否在圆内,我们判断鼠标与圆心的距离是否小于半径,是,则说明鼠标在圆圈内。如果collidepoint方法返回了False,说明用户在画画的区域按下了鼠标,所以将last_pospos属性都设置为当前鼠标的位置,并设置标志drawingTrue

再添加以下方法用于处理MOUSEMOTION事件:

def on_mouse_move(self, pos):if self.drawing:self.pos = clamp_pos(pos)pygame.draw.line(self.screen, self.color, self.last_pos, self.pos, self.width)self.last_pos = clamp_pos(pos)

该方法先将pos更新为当前鼠标的坐标,在poslast_pos之间画一条线段,再把last_pos更新为当前鼠标的坐标。

处理键盘事件

添加以下方法用于处理KEYDOWN事件:

def on_key_down(self, key):if self.drawing:returnif key == pygame.K_UP:self.width += 1self.width = clamp(self.width, 1, LINE_WIDTH_MAX)self.draw_text()elif key == pygame.K_DOWN:self.width -= 1self.width = clamp(self.width, 1, LINE_WIDTH_MAX)self.draw_text()

return使用户只能在没有进行绘画时更改线条粗细。这里再次使用了clamp函数限制线条粗细。

调用事件处理方法

更改process_events方法,以调用事件处理的方法,代码如下:

def process_events(self):for event in pygame.event.get():if event.type == pygame.QUIT:pygame.quit()sys.exit()elif event.type == pygame.MOUSEBUTTONDOWN:self.on_mouse_down(event.pos)elif event.type == pygame.MOUSEMOTION:self.on_mouse_move(event.pos)elif event.type == pygame.MOUSEBUTTONUP:self.drawing = Falseelif event.type == pygame.KEYDOWN:self.on_key_down(event.key)

注意,这里还添加了处理MOUSEBUTTONUP事件的代码。

完整代码

import sysimport pygameCOLORS = ((0, 0, 0),(255, 0, 0),(0, 255, 0),(0, 0, 255),(255, 255, 0),(255, 0, 255),(0, 255, 255),(255, 255, 255)
)  # 色板中的所有颜色RADIUS = 15  # 色板中设置颜色的圆圈的半径
GAP = 15  # 色板中相邻的圆圈之间的空隙
CENTER_DIST = RADIUS * 2 + GAP  # 色板中相邻的圆圈的中心之间的距离
PALETTE_RECT = pygame.Rect(0, 0, 800, RADIUS * 2 + GAP * 2)  # 色板的矩形坐标
PALETTE_HEIGHT = PALETTE_RECT.height  # 色板的矩形的高度
LINE_WIDTH_MAX = 20  # 线条的最大粗度def clamp(value, low, high):return low if value < low else (high if value > high else value)def clamp_pos(pos):y = clamp(pos[1], PALETTE_HEIGHT, 800)return (pos[0], y)def dist_between_points(point1, point2):return ((point1[0] - point2[0]) ** 2 + (point1[1] - point2[1]) ** 2) ** 0.5class DrawingBoard:def __init__(self):pygame.init()self.screen = pygame.display.set_mode((800, 800))self.screen.fill((255, 255, 255))pygame.display.set_caption('Drawing Board')self.color = (0, 0, 0)  # 画笔的颜色self.width = 1  # 线条的粗细self.last_pos = self.pos = (0, 0)  # 鼠标上一帧的位置以及当前位置self.circles = []  # 色板中由圆圈的中心和对应颜色的元组组合而成。self.font = pygame.font.SysFont('Times New Roman', 20)  # 字体self.drawing = False  # 用户是否正在绘画self.draw_colors()self.draw_text()def draw_colors(self):for i in range(len(COLORS)):center = (i * CENTER_DIST + GAP + RADIUS, GAP + RADIUS)pygame.draw.circle(self.screen, COLORS[i], center, RADIUS)self.circles.append((center, COLORS[i]))pygame.draw.line(self.screen, (0, 0, 0), (0, PALETTE_HEIGHT), (800, PALETTE_HEIGHT))def draw_text(self):surf = self.font.render(f'Line width: {self.width}', True, (0, 0, 0))rect = surf.get_rect()rect.right = PALETTE_RECT.right - GAPrect.centery = PALETTE_RECT.centeryfill_rect = pygame.Rect(rect.left - 5, rect.top - 5, rect.width + 10, rect.height + 10)pygame.draw.rect(self.screen, (255, 255, 255), fill_rect)self.screen.blit(surf, rect)def on_mouse_down(self, pos):if PALETTE_RECT.collidepoint(pos):for center, color in self.circles:if dist_between_points(pos, center) < RADIUS:self.color = colorelse:self.last_pos = self.pos = posself.drawing = Truedef on_mouse_move(self, pos):if self.drawing:self.pos = clamp_pos(pos)pygame.draw.line(self.screen, self.color, self.last_pos, self.pos, self.width)self.last_pos = clamp_pos(pos)def on_key_down(self, key):if self.drawing:returnif key == pygame.K_UP:self.width += 1self.width = clamp(self.width, 1, LINE_WIDTH_MAX)self.draw_text()elif key == pygame.K_DOWN:self.width -= 1self.width = clamp(self.width, 1, LINE_WIDTH_MAX)self.draw_text()def process_events(self):for event in pygame.event.get():if event.type == pygame.QUIT:pygame.quit()sys.exit()elif event.type == pygame.MOUSEBUTTONDOWN:self.on_mouse_down(event.pos)elif event.type == pygame.MOUSEMOTION:self.on_mouse_move(event.pos)elif event.type == pygame.MOUSEBUTTONUP:self.drawing = Falseelif event.type == pygame.KEYDOWN:self.on_key_down(event.key)def run(self):while True:self.process_events()pygame.display.update()if __name__ == '__main__':app = DrawingBoard()app.run()

代码运行截图:

代码运行截图

结语

以上,就是本章的所有内容。在下一章,你将学习如何监测游戏时间。


http://chatgpt.dhexx.cn/article/85iej8cl.shtml

相关文章

Pygame基础教程(一)

写在前面的话&#xff1a; 本系列教程仅有一些在本机调试通过的代码&#xff08;如代码中发现bug&#xff0c;恳请包涵&#xff09;。除代码中出现的一些主要注释外&#xff0c;不会出现太多其他文字解释&#xff0c;但是&#xff0c;文章中会给出主要模块的官方文档地址。再次…

Pygame 教程(5):监测游戏时间

本章&#xff0c;你将学习如何监测游戏时间。 导航 上一章&#xff1a;图像传输和绘制文本 下一章&#xff1a;努力更新中…… 文章目录 导航监测时间游戏帧速率实例&#xff1a;绘图性能对比结语 监测时间 在游戏程序中&#xff0c;时常需要随着时间的流逝而做出不同的动作…

Pygame教程系列四:播放音频篇

【前言】 pygame播放音频文件这部分相对来说比较简单&#xff0c;主要是用到pygame.mixer模块&#xff0c;不过也有一些地方需要注意的&#xff0c;咱们直接先看看案例 1、案例效果图 2、案例代码 import pygame from mutagen.mp3 import MP3 # 标识是否退出循环 exitFlag Fa…

python3.8安装pygame_Python3.8安装Pygame教程

注&#xff1a;因为最近想用一下Python做一些简单小游戏的开发作为项目练手之用&#xff0c;而Pygame模块里面提供了大量的有用的方法和属性。今天我们就在之前安装过PyCharm的基础上&#xff0c;安装Pygame&#xff0c;下面是安装的步骤&#xff0c;希望能够帮到大家。 第一步…

Pygame 教程(3):绘制图形

本章&#xff0c;你将学习如何在 Pygame 中绘制图形。 导航 上一章&#xff1a;重要的概念及对象 下一章&#xff1a;图像传输和绘制文本 文章目录 导航抗锯齿draw 模块实例&#xff1a;跟随鼠标的图形创建初始窗口添加变量捕捉鼠标事件绘制图形完整代码 结语 抗锯齿 抗锯齿…

Mac Pycharm导入Pygame教程(超细)

首先先新建一个想要使用Pygame的项目 进入项目后&#xff0c;点击文件&#xff08;File&#xff09;——新项目设置&#xff08;settings&#xff09; 点击新项目的偏好设置&#xff08;Preferences for new project &#xff09; 随后可以看到 点击Python 编译器&#xff0…

mac python3.8上怎么安装pygame 第三方库_Python3.8安装Pygame教程步骤详解

注:因为最近想用一下python做一些简单小游戏的开发作为项目练手之用,而Pygame模块里面提供了大量的有用的方法和属性。今天我们就在之前安装过PyCharm的基础上,安装Pygame,下面是安装的步骤,希望能够帮到大家。 第一步 安装Python和pip 如果已安装,使用python --version …

Pygame教程系列一:快速入门篇

【简介】 Pygame 是python用来开发视频游戏的游戏引擎&#xff0c;底层主要是SDL库实现&#xff0c;算是目前利用python开发小游戏的一个性能比较高的一个游戏框架 一、安装pygame 使用pip下载安装 pip install pygame二、入门案例详析 1、示例效果 2、示例代码 import os …

pygame教程3

目录 做个小游戏精灵类精灵类介绍使用精灵类 做个小游戏 这个小游戏使用了pygame教程2的知识。 #↓初始化 import pygame,sys from pygame.locals import * pygame.init()#初始化pygame。 screen pygame.display.set_mode((800,600)) pygame.display.set_caption("Hell…

pygame教程2

目录 响应键盘上的事件让画面动起来了解x轴和y轴画圆形圆形动起来 响应键盘上的事件 import pygame#导入pygame。 import sys#导入sys from pygame.locals import *#导入pygame所有的常量&#xff0c;方便以后使用。 pygame.init()#初始化pygame。 screen pygame.display.set…

pygame教程笔记

pygame教程 安装pygameGame Development 1-1: Getting Started with PygameGame Development 1-2: Working with SpritesGame Development 1-3: More About SpritesPygame Shmup Part 1: Player Sprite and ControlsPygame Shmup Part 2: Enemy SpritesPygame Shmup Part 3: Co…

pygame基础教程

pygame简介 pygame可以实现python游戏的一个基础包。 pygame实现窗口 初始化pygame&#xff0c;init()类似于java类的初始化方法&#xff0c;用于pygame初始化。 pygame.init() 设置屏幕&#xff0c;(500,400)设置屏幕初始大小为500 * 400的大小&#xff0c; 0和32 是比较高…

Python pygame(GUI编程)模块最完整教程(1)

提示&#xff1a;下滑文章左侧可以查看目录&#xff01;本教程分为多篇&#xff0c;总目录如下。 总目录&#xff1a; README.md Python-ZZY/Python-Pygame最完整教程 - Gitee.com 1 初识pygame 1.1 简介 pygame是python中一个流行的GUI编程模块&#xff0c;是专门为了开发游…

Pygame教程(非常详细)

文章目录 教程特点阅读条件 Pygame是什么扩展知识 Pygame下载和安装1) pip包管理器安装2) 二进制安装包安装 第一个Pygame程序初始化程序创建Surface对象事件监听游戏循环 Pygame Display显示模块详解Pygame Surface创建图像Pygame Transform图像变形Pygame Time时间控制详解1)…

GeoWave0.9.8开发人员指南

GeoWave0.9.8开发人员指南 官方英文地址&#xff1a;http://s3.amazonaws.com/geowave/0.9.8/docs/devguide.html 介绍 什么是GeoWave GeoWave是一个开源库&#xff0c;用于在排序的键值数据存储和流行的大数据框架之上存储&#xff0c;索引和搜索多维数据。GeoWave包含特定的…

行人检测之DPM

基于可识别训练的部件模型的目标检测 Object Detection with Discriminatively Trained Part Based Models 摘要 基于多尺度可变形的部件模型我们来描述一个目标检测系统。我们的系统能够表示高度可变的对象类并在PASCAL目标检测挑战达到最先进的结果。虽然可变形部件模型变…

Xuxiang Qi--Deep Learning based Semantic Labelling of 3D Point Cloud in Visual SLAM

元学习论文总结||小样本学习论文总结 2017-2019年计算机视觉顶会文章收录 AAAI2017-2019 CVPR2017-2019 ECCV2018 ICCV2017-2019 ICLR2017-2019 NIPS2017-2019 提出了一种建立三维密集语义地图的方法&#xff0c;采用GPU&#xff0c;ROS消息通信无法支持。 摘要 三维(3D)点云…

古老的DPM

基于可识别训练的部件模型的目标检测 Object Detection with Discriminatively Trained Part Based Models 摘要 基于多尺度可变形的部件模型我们来描述一个目标检测系统。我们的系统能够表示高度可变的对象类并在PASCAL目标检测挑战达到最先进的结果。虽然可变形部件模型变得…

OpenCV-Python (官方)中文教程(部分一)

官网链接(英文版):https://docs.opencv.org/4.1.1/d6/d00/tutorial_py_root.html 第一章. OpenCV简介 了解如何在计算机上设置OpenCV-Python&#xff01; 1.OpenCV-Python教程简介 1.1 OpenCV OpenCV是由Gary Bradsky于1999年在Intel公司创建的&#xff0c;第一次发布是在…

【视觉SLAM十四讲】视觉里程计—特征点法

本文为视觉 SLAM 学习笔记&#xff0c;讲解视觉里程计中的特征点法。 本讲内容概要 图像特征的意义&#xff0c;在单幅及多幅图像中提取特征点。对极几何的原理&#xff0c;利用对极几何的约束恢复图像间相机的三维运动PnP 问题&#xff0c;利用已知三维结构与图像的对应关系…