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

上一篇文章:https://blog.csdn.net/qq_48979387/article/details/126799308

6 文字绘制

参考资料:https://pyga.me/docs/ref/font.html

6.1 载入字体

pygame中绘制文字的第一步是载入字体。载入字体的方式通常有两种,第一种是使用指定路径的字体文件,第二种是从系统字体库中载入字体。推荐第一种方式,因为在游戏打包后,你无法确定你电脑上安装的字体文件在其他用户的电脑上也安装过。

pygame.font和pygame.freetype模块提供了文字绘制的操作。font模块功能比较简单,freetype模块是font模块功能的扩展。

Font类通过字体文件创建一个字体对象,支持包括*.ttf等一系列的TrueType字体。载入字体时,需要提供一个字体文件路径和字体大小。示例:

font = pg.font.Font("name_of_font.ttf", 12)

如果是调用系统字体文件,则使用SysFont。示例:

font = pg.font.SysFont("宋体", 12)

6.2 渲染字体

加载字体后,下一步是渲染字体。渲染字体调用字体对象的render()方法,将一段文字转换成使用该字体的pygame.Surface对象。

render(text, antialias, color, background=None) -> Surface

text是要渲染的一段文字内容,文本只能为单行,不支持使用"\n"换行符。antialias是一个布尔值,代表是否使用抗锯齿,抗锯齿的文字更加平滑,但是速度会稍慢一点点,一般选择True。color是文字的颜色。background是文字背景颜色,设为None则为透明背景。

注意:如果文字绘制的位置始终是纯色背景,那么最好是指定background背景颜色。因为使用纯色填充比使用透明填充性能会更好。

渲染后的字体可以当做表面来处理。下面的示例显示了如何绘制文字。

import pygame as pg

pg.init()

screen = pg.display.set_mode((300, 200))
font = pg.font.Font("simhei.ttf", 20) #大小为20的simhei字体文件(在同一目录下)
surf = font.render("你好!这是一段文字", True, (255, 255, 255))

while True:
    screen.fill((0, 0, 0))
    screen.blit(surf, (0, 0))
    
    for event in pg.event.get():
        if event.type == pg.QUIT:
            pg.quit()

    pg.display.flip()
注意:字体渲染不是线程安全的。这意味着你无法用多线程渲染单个字体,同一时刻只能用该字体对象渲染一段文字。如果一定要用线程,可以建立多个Font对象,但每个对象都是一样渲染结果。

6.3 字体特殊样式

Font对象提供了几个属性获取或改变字体的特殊样式,包括加粗、斜体、下划线、删除线。

属性

解释

bold

加粗

italic

斜体

underline

下划线

strikethrough

删除线

例如想要让字体加粗、斜体,可以这样:

font.bold = font.italic = True

下面的示例演示了四种效果。

import pygame as pg

pg.init()

screen = pg.display.set_mode((300, 200))

surfs = []
font = pg.font.SysFont("simhei", 20)
surfs.append(font.render("default style", True, (255, 255, 255)))

for style in ("bold", "italic", "underline", "strikethrough"):
    font = pg.font.SysFont("simhei", 20)
    setattr(font, style, True)
    surfs.append(font.render(style, True, (255, 255, 255)))

while True:
    screen.fill((0, 0, 0))

    for i, surf in enumerate(surfs):
        screen.blit(surf, (0, i * 40))
    
    for event in pg.event.get():
        if event.type == pg.QUIT:
            pg.quit()

    pg.display.flip()

6.4 font模块索引-字体操作

get_default_font() -> string

获取pygame指定的默认的字体名称,一般是freesansbold.ttf。

get_fonts() -> list of strings

获取系统上所有可用的字体名称。

match_font(name, bold=False, italic=False) -> path

返回系统上名为name的字体的完整路径。

SysFont(name, size, bold=False, italic=False) -> Font

从系统上调用字体,name为字体名,size为字体大小,bold和italic表示是否设为粗体或斜体。

Font(filename, size) -> Font

Font(pathlib.Path, size) -> Font

Font(object, size) -> Font

从文件载入字体。

Font.render(text, antialias, color, background=None) -> Surface

通过字体渲染文字,text为文字内容,antialias表示是否抗锯齿,color是文字颜色,background为背景颜色。

Font.size(text) -> (width, height)

返回渲染一段文字所需的表面大小。

Font.set_underline(bool) -> None

Font.get_underline() -> bool

设置和获取文本的下划线状态。

Font.set_strikethrough(bool) -> None

Font.get_strikethrough() -> bool

设置和获取文本的删除线状态。

Font.set_bold(bool) -> None

Font.get_bold() -> bool

设置和获取文本的粗体状态。

Font.set_italic(bool) -> None

Font.get_italic() -> bool

设置和获取文本的斜体状态。

Font.metrics(text) -> list

返回一个列表,包含给定文本内容中每个字符的信息。列表包含多个元组,每个元组中是每个字符的最小x偏移量、最大x偏移量、最小y偏移量、最大y偏移量、提前偏移量。无法识别的字符则为None。

Font.get_height() -> int

返回字体的平均高度。

Font.get_ascent() -> int

返回字体上升的高度,即从基线到字符顶端的高度。

Font.get_descent() -> int

返回字体下降的高度,即从基线到字符底部的高度。

6.5 freetype模块索引-字体操作扩展

参考资料:https://pyga.me/docs/ref/freetype.html

freetype模块是font模块的扩展,一般用font模块就足够了。

注意:freetype模块没有事先在pygame中导入,所以使用时不能直接调用pg.freetype,而要在开头进行导入:
from pygame import freetype

get_default_font() -> string

获取pygame指定的默认的字体名称。

SysFont(name, size, bold=False, italic=False) -> Font

从系统上调用字体,name为字体名,size为字体大小,bold和italic表示是否设为粗体或斜体。

Font(file, size=0, font_index=0, resolution=0, ucs4=False) -> Font

Font(pathlib.Path) -> Font

从文件载入字体。size是字体的大小,设为0则使用默认大小。font_index是字体位于某个字体文件中的索引。resolution是像素大小,用于缩放字形。

Font.name -> string

字体名称。

Font.path -> string

字体文件路径。

Font.size -> float

Font.size -> (float, float)

字体大小。

Font.ascender -> int

默认大小状态下字体上升高度。

Font.descender -> int

默认大小状态下字体下降高度。

Font.style -> int

字体默认样式。可以通过freetype模块中定义的几个常量(不是locals中的常量)来设定,多个常量之间用按位或"|"操作符连接起来。

Font.underline -> bool

字体默认是否添加下划线。

Font.strong -> bool

字体默认是否加粗

Font.oblique -> bool

字体默认是否倾斜。

Font.wide -> bool

字体默认是否增宽(不支持旋转后的字体)。

Font.strength -> float

字体strong或wide样式中,字形被放大的量。默认值约等于1/36。

Font.underline_adjustment -> float

字体下划线的位置偏移,值的范围是-2.0到2.0之间。默认是1.0。设为0时下划线位于文本基线处,设为负数为上划线,设为正数为下划线。

Font.fixed_sizes -> int

只读属性。字体是否固定宽度。

Font.fixed_sizes -> int

只读属性。字体包含位图字符图像的点大小的数量。

Font.scalable -> bool

只读属性。字体是否可伸缩。

Font.antialiased -> bool

字体是否抗锯齿,默认为True

Font.kerning -> bool

字体是否可调整字距,默认为False。

Font.vertical -> bool

字体是否以垂直方向显示,默认为False。

Font.rotation -> int

字体基线默认的逆时针旋转角度。

Font.fgcolor -> Color

字体前景色,默认为黑色。

Font.bgcolor -> Color

字体背景色,默认为透明色。

Font.get_rect(text, style=STYLE_DEFAULT, rotation=0, size=0) -> rect

返回渲染text文字表面后的矩形对象。矩形对象的大小是表面的大小,位置(x, y)是文本原点的位置(也就是(0, 字体上升高度))。指定rotation将渲染的文字逆时针旋转,size改变字体的大小。

Font.get_metrics(text, size=0) -> [(...), ...]

返回一个列表,包含给定文本内容中每个字符的信息。

Font.get_sized_ascender(<size>=0) -> int

根据字体大小获取字体上升的高度。

Font.get_sized_descender(<size>=0) -> int

根据字体大小获取字体下降的高度。

Font.get_sized_height(<size>=0) -> int

根据字体大小获取字体的平均高度。

Font.get_sized_glyph_height(<size>=0) -> int

根据字体大小获取字体包围框的平均高度。(字体尺寸较小时和get_sized_height返回的结果近似)

Font.get_sizes() -> [(int, int, int, float, float), ...]

Font.get_sizes() -> []

返回嵌入式位图的可用大小。

Font.render(text, fgcolor=None, bgcolor=None, style=STYLE_DEFAULT, rotation=0, size=0) -> (Surface, Rect)

渲染text文本。fgcolor, bgcolor分别表示前景和背景色,style是文本样式,rotation是逆时针旋转角度,size是文本的字体大小。Surface是渲染的文本表面,Rect是文本的大小,位于文字的原点。

text可以设为None,表示重新渲染上一次在get_rect(), render(), render_to(), render_raw(), or render_raw_to()这几个方法调用过的文本。

Font.render_to(surf, dest, text, fgcolor=None, bgcolor=None, style=STYLE_DEFAULT, rotation=0, size=0) -> Rect

将render的运行结果绘制到surf上,位于dest处。

Font.render_raw(text, style=STYLE_DEFAULT, rotation=0, size=0, invert=False) -> (bytes, (int, int))

将渲染的文字以8位灰度值形式返回,前景色为255,背景色为0。

Font.render_raw_to(array, text, dest=None, style=STYLE_DEFAULT, rotation=0, size=0, invert=False) -> Rect

将render_raw的运行结果呈现到一个数组上(二维),位于dest处。

7 按键处理

参考资料:https://pyga.me/docs/ref/key.html

pygame.key模块提供了一些处理按键的操作。和键盘相关的event相比,key模块支持了更多的功能,有时比事件更好用。

7.1 获取持续按下的按键

pg.key.get_pressed方法返回一个字典,其中包含每个按键的按下情况。字典的键是按键的标识符常量,是一个整数;字典的值是一个布尔值,表示是否按下了这个按键。只有当按键持续按下,并且没有松开时才会设为True。

上一章的末尾“行走的人”示例中,关于玩家移动是使用event来做的。如果用event,就需要同时处理KEYDOWN和KEYUP事件,判断按键按下且没有松开。但如果使用pg.key.get_pressed,就可以减少麻烦,如下示例:

import pygame as pg

pg.init()

screen = pg.display.set_mode((300, 200))
clock = pg.time.Clock()
image = pg.image.load("logo.png")
image_rect = image.get_rect()
speed = 2

while True:
    screen.fill((0, 0, 0))
    screen.blit(image, image_rect)

    keys = pg.key.get_pressed()
    if keys[pg.K_UP]: #如果按下上方向键
        image_rect.y -= speed
    elif keys[pg.K_DOWN]:
        image_rect.y += speed
    if keys[pg.K_LEFT]:
        image_rect.x -= speed
    elif keys[pg.K_RIGHT]:
        image_rect.x += speed
    
    for event in pg.event.get():
        if event.type == pg.QUIT:
            pg.quit()

    clock.tick(60)
    pg.display.flip()

这样可以很简洁地实现持续移动。

7.2 获取组合键

组合键是指按下多个按键。如果要对按下的多个按键进行处理,可以用pg.key.get_mods()方法。

import pygame as pg

pg.init()
screen = pg.display.set_mode((300, 200))

while True:
    mods = pg.key.get_mods()
    if mods == pg.KMOD_NONE:
        print("无组合键按下")
    elif mods & pg.KMOD_CTRL:
        print("按下了Ctrl和标识符为", event.key, "的按键")
        
    for event in pg.event.get():
        if event.type == pg.QUIT:
            pg.quit()

get_mods方法获得的结果类似于event.mods,但是KEYDOWN或KEYUP只会在刚按下和刚松开按键时触发,而get_mods方法获得的是持续按下不松开的按键内容。没有组合键按下时返回pg.KMOD_NONE相同的标识符。

7.3 控制重复触发KEYDOWN事件

pg.key.set_repeat方法可以控制KEYDOWN事件的频率。比如持续按下某个按键时,想要让KEYDOWN事件每过一段时间就触发一次,就可以用set_repeat方法。

set_repeat() -> None
set_repeat(delay) -> None
set_repeat(delay, interval) -> None

如果set_repeat不传递任何参数,那么就是默认的模式,只在按下时触发一次KEYDOWN事件。如果指定了delay参数,那么在按下按键后每经过delay毫秒就重复触发一次KEYDOWN事件。如果同时指定delay和interval参数,那么就表示触发一次KEYDOWN事件,等待delay毫秒后,再以interval毫秒的间隔重复触发KEYDOWN事件。

举例说明:

import pygame as pg

pg.init()
screen = pg.display.set_mode((300, 200))

pg.key.set_repeat(1000, 200)

while True:
    for event in pg.event.get():
        if event.type == pg.QUIT:
            pg.quit()
        elif event.type == pg.KEYDOWN:
            print("KEYDOWN")

当用户按下某个按键时,会先打印一次"KEYDOWN",然后等待1000ms,再以200ms的时间间隔触发事件,打印多次"KEYDOWN",如果不能理解可以运行代码试一下。

这样的停顿主要应用于更改数值的计数器,当点击增加时数值+1,持续按下时数值增加的速度变快。

7.4 更改文本输入候选框位置

前面介绍TEXTINPUT和TEXTEDITING的时候,已经介绍过在pygame窗口上控制文本输入的方法。现在需要做的是改变文本输入候选框的位置,而不是让其固定在一处。

首先需要了解控制文本输入的两个函数:

pg.key.start_text_input() -> None
pg.key.stop_text_input() -> None

这两个函数分别表示开始文本输入和停止文本输入。

在默认情况下是允许文本输入的,所以一般不需要调用start_text_input。在调用stop_text_input函数后,TEXTINPUT和TEXTEDITING无法被接收到,文本候选框将会持续被隐藏。重启文本输入功能可以再次调用start_text_input函数。

默认情况下,文本候选框是无论输入状态如何都不被显示的。需要显示文本候选框,可以调用下面的代码。

import os
os.environ["SDL_IME_SHOW_UI"] = "1" #显示输入候选框UI

这样的话,当处于允许输入状态下,输入法的文本候选框会在输入时显示。

注意:文本候选框是指类似于下图的这样一个窗口,即IME,不同输入法不一样。

set_text_input_rect()方法控制了输入框的位置,它接受一个Rect对象表示文本候选框的位置(相对于pygame屏幕)。

pg.key.set_text_input_rect(Rect) -> None

示例如下:

import pygame as pg
import os

os.environ["SDL_IME_SHOW_UI"] = "1" #显示输入候选框UI
pg.init()

screen = pg.display.set_mode((300, 200))
font = pg.font.SysFont("simhei", 20)

text = ""
pg.key.set_text_input_rect((0, 0, 0, 0))

while True:
    screen.fill((0, 0, 0))
    screen.blit(font.render(text, True, (255, 255, 255)), (0, 0)) #绘制文字
    
    for event in pg.event.get():
        if event.type == pg.QUIT:
            pg.quit()
        elif event.type == pg.TEXTINPUT:
            text += event.text
            pg.key.set_text_input_rect((font.size(text)[0], 0, 0, 0))
        elif event.type == pg.KEYDOWN:
            if event.key == pg.K_BACKSPACE: #退格键
                text = text[:-1]

    pg.display.flip()

运行效果:

7.5 key模块索引-按键操作

get_focused() -> bool

判断窗口是否获取输入焦点。

get_pressed() -> bools

以字典形式返回按下且没有松开的按键。

get_mods() -> int

返回当前按下且没有松开的组合键。

set_repeat() -> None

set_repeat(delay) -> None

set_repeat(delay, interval) -> None

控制重复触发KEYDOWN事件

get_repeat() -> (delay, interval)

获取set_repeat设置的值。

name(key, use_compat=True) -> str

通过按键常量(键码)返回键名,未找到则返回空字符串。

key_code(name=string) -> int

通过按键名返回按键常量,示例:

>>> pygame.key.key_code("return") == pygame.K_RETURN
True
>>> pygame.key.key_code("0") == pygame.K_0
True
>>> pygame.key.key_code("space") == pygame.K_SPACE
True

start_text_input() -> None

开始键盘输入。

stop_text_input() -> None

结束键盘输入。

set_text_input_rect(Rect) -> None

设置IME的位置。

8 鼠标处理

参考资料:

https://pyga.me/docs/ref/mouse.html

https://pyga.me/docs/ref/cursors.html

8.1 获取鼠标位置

关于鼠标操作,包括位置、鼠标按键等一系列操作都位于pygame.mouse模块。其中最常用的方法是pg.mouse.get_pos(),用于返回鼠标相对于pygame屏幕的位置。鼠标位于屏幕外时,无法准确获取鼠标位置。

注意:通过MOUSEMOTION事件也可以获取鼠标位置,但只有在鼠标移动的时候才会触发这个事件。
import pygame as pg

pg.init()

screen = pg.display.set_mode((300, 200))
font = pg.font.SysFont("simhei", 20)

text = ""

while True:
    text = "鼠标位置:" + str(pg.mouse.get_pos())
    
    screen.fill((0, 0, 0))
    screen.blit(font.render(text, True, (255, 255, 255)), (0, 0)) #绘制文字
    
    for event in pg.event.get():
        if event.type == pg.QUIT:
            pg.quit()

    pg.display.flip()

运行效果:

8.2 隐藏和显示光标

set_visible方法设置鼠标的可见性。当设为False时,光标在屏幕内时会隐藏不可见。

pg.mouse.set_visible(bool) -> bool

8.3 光标样式

光标可以更改样式,首先需要创建一个pg.cursor.Cursor光标对象。

创建光标对象的方式主要有这几种:通过Surface对象、通过xbm文件、通过光标字符串、通过光标常量载入系统光标。然后通过pg.mouse.set_cursor方法设置样式。

先介绍载入系统光标的方式。只需要将光标样式常量传递给set_cursor方法。pygame模块中有以下用于光标样式的常量。

常量

样式描述

SYSTEM_CURSOR_ARROW

箭头

SYSTEM_CURSOR_IBEAM

提示光标输入的工形标

SYSTEM_CURSOR_WAIT

等待

SYSTEM_CURSOR_CROSSHAIR

十字形

SYSTEM_CURSOR_WAITARROW

较小的等待箭头

SYSTEM_CURSOR_SIZENWSE

左上至右下的双向箭头

SYSTEM_CURSOR_SIZENESW

左下至右上的双向箭头

SYSTEM_CURSOR_SIZEWE

左右方向的双向箭头

SYSTEM_CURSOR_SIZENS

上下方向的双向箭头

SYSTEM_CURSOR_SIZEALL

四个方向的箭头

SYSTEM_CURSOR_NO

禁止的符号

SYSTEM_CURSOR_HAND

手的形状,提示点击

例如:

pg.mouse.set_cursor(pg.SYSTEM_CURSOR_HAND)

还可以通过Surface对象设置光标样式,这样就可以把某一张图片设为光标。所需的参数是光标热点的位置和表面对象。例如:

pg.mouse.set_cursor((0, 0), pg.image.load("cursor.png"))

8.4 mouse模块索引-鼠标操作

get_pressed(num_buttons=3) -> (button1, button2, button3)

get_pressed(num_buttons=5) -> (button1, button2, button3, button4, button5)

获取各个鼠标按键的按下状态。默认情况下只支持左键、中键、右键,也可以支持滚轮向上和向下。

get_pos() -> (x, y)

获取鼠标位置,相对于pygame屏幕。

get_rel() -> (x, y)

获取鼠标位置,相对于于上一次鼠标位置。

set_pos([x, y]) -> None

设置鼠标位置。

set_visible(bool) -> bool

设置鼠标可见性。

get_visible() -> bool

获取鼠标可见性。

get_focused() -> bool

判断窗口是否在获取鼠标输入。

set_cursor(pygame.cursors.Cursor) -> None

set_cursor(size, hotspot, xormasks, andmasks) -> None

set_cursor(hotspot, surface) -> None

set_cursor(constant) -> None

设置鼠标样式。

get_cursor() -> pygame.cursors.Cursor

获取鼠标样式。

8.5 cursor模块索引-光标样式

compile(strings, black='X', white='.', xor='o') -> data, mask

编译光标字符串。下面是一个光标字符串的示例(箭头光标)。

thickarrow_strings = (               #sized 24x24
  "XX                      ",
  "XXX                     ",
  "XXXX                    ",
  "XX.XX                   ",
  "XX..XX                  ",
  "XX...XX                 ",
  "XX....XX                ",
  "XX.....XX               ",
  "XX......XX              ",
  "XX.......XX             ",
  "XX........XX            ",
  "XX........XXX           ",
  "XX......XXXXX           ",
  "XX.XXX..XX              ",
  "XXXX XX..XX             ",
  "XX   XX..XX             ",
  "     XX..XX             ",
  "      XX..XX            ",
  "      XX..XX            ",
  "       XXXX             ",
  "       XX               ",
  "                        ",
  "                        ",
  "                        ")

load_xbm(cursorfile) -> cursor_args

load_xbm(cursorfile, maskfile) -> cursor_args

加载xbm格式的位图文件作为光标。返回的cursor_args可以直接解包传递给set_cursor方法。

Cursor(size, hotspot, xormasks, andmasks) -> Cursor

Cursor(hotspot, surface) -> Cursor

Cursor(constant) -> Cursor

Cursor(Cursor) -> Cursor

Cursor() -> Cursor

光标对象。

Cursor.type -> string

光标类型,可能是"system", "bitmap", "color"

Cursor.data -> tuple

光标数据

Cursor.copy() -> Cursor

复制光标对象

实战:键盘输入程序

本章是实战练习环节,将实现以下效果。

完整代码

import pygame as pg
from pygame.locals import * #导入所有常量
import os

os.environ["SDL_IME_SHOW_UI"] = "1" #显示输入候选框UI
pg.init()

screen = pg.display.set_mode((300, 300), RESIZABLE) #窗口可调整大小
font = pg.font.SysFont("simhei", 20)

text = ""
index = 0
index_tip = ""
show_index = True

pg.key.set_repeat(750, 25) #持续触发退格键
pg.key.set_text_input_rect((0, 0, 0, 0))
pg.mouse.set_cursor(SYSTEM_CURSOR_IBEAM)
pg.time.set_timer(USEREVENT, 500) #重复生成事件,光标闪烁

def split_text(text): #将字符分行
    res = [""]
    for char in text:
        if font.size(res[-1]+char)[0] > screen.get_width(): #get_width返回表面宽度
            res.append("")
        else:
            res[-1] += char

    return res
            
while True:
    screen.fill((0, 0, 0))

    s_list = split_text(text)
    length = 0
    for i, s in enumerate(s_list):
        if length != -1:
            length += len(s)
        if length >= index - i:
            index_line = i
            idx = index - sum([len(s) for s in s_list[:i]])
            s = s[:idx] + index_tip + s[idx:]
            length = -1

            pg.key.set_text_input_rect((font.size(s[:idx] + "|")[0], i *24, 0, 0))
            
        screen.blit(font.render(s, True, (255, 255, 255)), (0, i * 24))
    
    for event in pg.event.get():
        if event.type == QUIT:
            pg.quit()
            
        elif event.type == WINDOWFOCUSLOST: #窗口失去焦点,隐藏输入光标
            show_index = False      
        elif event.type == WINDOWFOCUSGAINED: #窗口获取焦点,显示输入光标
            show_index = True
            
        elif event.type == TEXTINPUT: #文本输入
            text = text[:index] + event.text + text[index:]
            index += len(event.text)
            
        elif event.type == KEYDOWN:
            if event.key == K_BACKSPACE: #退格键
                index -= 1
                if index < 0:
                    index = 0
                else:
                    text = text[:index] + text[index+1:]
            elif event.key == K_DELETE: #向右删除键
                text = text[:index] + text[index+1:]

            elif event.key == K_LEFT: #光标向左
                index -= 1
                if index < 0:
                    index = 0
            elif event.key == K_RIGHT: #光标向右
                index += 1
                if index > len(s_list[index_line]):
                    index -= 1

        elif event.type == USEREVENT: #更新光标
            if show_index:
                index_tip = " " if index_tip != " " else "|"
            else:
                index_tip = "|"

    pg.display.flip()

下一篇文章

https://blog.csdn.net/qq_48979387/article/details/128865416

猜你喜欢

转载自blog.csdn.net/qq_48979387/article/details/128784116