版本要求:pygame.2.0.0以上
今天在用PyQt5创建文本框时,突发奇想,能不能用pygame实现文本框?pygame什么都行,就文本框不行,经过一番搜寻后,我在pygame安装目录里找到了一个textinput.py文件,内容如下:(不同版本的pygame文件不一样)
#!/usr/bin/env python
""" pg.examples.textinput
A little "console" where you can write in text.
Shows how to use the TEXTEDITING and TEXTINPUT events.
"""
import sys
import os
from typing import List
import pygame
import pygame as pg
import pygame.freetype as freetype
# This environment variable is important
# If not added the candidate list will not show
os.environ["SDL_IME_SHOW_UI"] = "1"
class TextInput:
"""
A simple TextInput class that allows you to receive inputs in pygame.
"""
# Add font name for each language,
# otherwise some text can't be correctly displayed.
FONT_NAMES = ",".join(
str(x)
for x in [
"notosanscjktcregular",
"notosansmonocjktcregular",
"notosansregular,",
"microsoftjhengheimicrosoftjhengheiuilight",
"microsoftyaheimicrosoftyaheiuilight",
"msgothicmsuigothicmspgothic",
"msmincho",
"Arial",
]
)
def __init__(
self, prompt: str, pos, screen_dimensions, print_event: bool, text_color="white"
) -> None:
self.prompt = prompt
self.print_event = print_event
# position of chatlist and chatbox
self.CHAT_LIST_POS = pg.Rect((pos[0], pos[1] + 50), (screen_dimensions[0], 400))
self.CHAT_BOX_POS = pg.Rect(pos, (screen_dimensions[1], 40))
self.CHAT_LIST_MAXSIZE = 20
self._ime_editing = False
self._ime_text = ""
self._ime_text_pos = 0
self._ime_editing_text = ""
self._ime_editing_pos = 0
self.chat_list: List[str] = []
# Freetype
# The font name can be a comma separated list
# of font names to search for.
self.font = freetype.SysFont(self.FONT_NAMES, 24)
self.font_small = freetype.SysFont(self.FONT_NAMES, 16)
self.text_color = text_color
print("Using font: " + self.font.name)
def update(self, events) -> None:
"""
Updates the text input widget
"""
for event in events:
if event.type == pg.KEYDOWN:
if self.print_event:
print(event)
if self._ime_editing:
if len(self._ime_editing_text) == 0:
self._ime_editing = False
continue
if event.key == pg.K_BACKSPACE:
if len(self._ime_text) > 0 and self._ime_text_pos > 0:
self._ime_text = (
self._ime_text[0 : self._ime_text_pos - 1]
+ self._ime_text[self._ime_text_pos :]
)
self._ime_text_pos = max(0, self._ime_text_pos - 1)
elif event.key == pg.K_DELETE:
self._ime_text = (
self._ime_text[0 : self._ime_text_pos]
+ self._ime_text[self._ime_text_pos + 1 :]
)
elif event.key == pg.K_LEFT:
self._ime_text_pos = max(0, self._ime_text_pos - 1)
elif event.key == pg.K_RIGHT:
self._ime_text_pos = min(
len(self._ime_text), self._ime_text_pos + 1
)
# Handle ENTER key
elif event.key in [pg.K_RETURN, pg.K_KP_ENTER]:
# Block if we have no text to append
if len(self._ime_text) == 0:
continue
# Append chat list
self.chat_list.append(self._ime_text)
if len(self.chat_list) > self.CHAT_LIST_MAXSIZE:
self.chat_list.pop(0)
self._ime_text = ""
self._ime_text_pos = 0
elif event.type == pg.TEXTEDITING:
if self.print_event:
print(event)
self._ime_editing = True
self._ime_editing_text = event.text
self._ime_editing_pos = event.start
elif event.type == pg.TEXTINPUT:
if self.print_event:
print(event)
self._ime_editing = False
self._ime_editing_text = ""
self._ime_text = (
self._ime_text[0 : self._ime_text_pos]
+ event.text
+ self._ime_text[self._ime_text_pos :]
)
self._ime_text_pos += len(event.text)
def draw(self, screen: pygame.Surface) -> None:
"""
Draws the text input widget onto the provided surface
"""
# Chat List updates
chat_height = self.CHAT_LIST_POS.height / self.CHAT_LIST_MAXSIZE
for i, chat in enumerate(self.chat_list):
self.font_small.render_to(
screen,
(self.CHAT_LIST_POS.x, self.CHAT_LIST_POS.y + i * chat_height),
chat,
self.text_color,
)
# Chat box updates
start_pos = self.CHAT_BOX_POS.copy()
ime_text_l = self.prompt + self._ime_text[0 : self._ime_text_pos]
ime_text_m = (
self._ime_editing_text[0 : self._ime_editing_pos]
+ "|"
+ self._ime_editing_text[self._ime_editing_pos :]
)
ime_text_r = self._ime_text[self._ime_text_pos :]
rect_text_l = self.font.render_to(
screen, start_pos, ime_text_l, self.text_color
)
start_pos.x += rect_text_l.width
# Editing texts should be underlined
rect_text_m = self.font.render_to(
screen,
start_pos,
ime_text_m,
self.text_color,
None,
freetype.STYLE_UNDERLINE,
)
start_pos.x += rect_text_m.width
self.font.render_to(screen, start_pos, ime_text_r, self.text_color)
class Game:
"""
A class that handles the game's events, mainloop etc.
"""
# CONSTANTS
# Frames per second, the general speed of the program
FPS = 50
# Size of window
SCREEN_WIDTH, SCREEN_HEIGHT = 640, 480
BG_COLOR = "black"
def __init__(self, caption: str) -> None:
# Initialize
pg.init()
self.screen = pg.display.set_mode((self.SCREEN_WIDTH, self.SCREEN_HEIGHT))
pg.display.set_caption(caption)
self.clock = pg.time.Clock()
# Text input
# Set to true or add 'showevent' in argv to see IME and KEYDOWN events
self.print_event = "showevent" in sys.argv
self.text_input = TextInput(
prompt="> ",
pos=(0, 20),
screen_dimensions=(self.SCREEN_WIDTH, self.SCREEN_HEIGHT),
print_event=self.print_event,
text_color="green",
)
def main_loop(self) -> None:
pg.key.start_text_input()
input_rect = pg.Rect(80, 80, 320, 40)
pg.key.set_text_input_rect(input_rect)
while True:
events = pg.event.get()
for event in events:
if event.type == pg.QUIT:
pg.quit()
return
self.text_input.update(events)
# Screen updates
self.screen.fill(self.BG_COLOR)
self.text_input.draw(self.screen)
pg.display.update()
self.clock.tick(self.FPS)
# Main loop process
def main():
game = Game("Text Input Example")
game.main_loop()
if __name__ == "__main__":
main()
(只要把FONT_NAMES的定义改成:
FONT_NAMES = ",".join(
str(x)
for x in [
"宋体,SimSun",
"notosanscjktcregular",
"notosansmonocjktcregular",
"notosansregular,",
"microsoftjhengheimicrosoftjhengheiuilight",
"microsoftyaheimicrosoftyaheiuilight",
"msgothicmsuigothicmspgothic",
"msmincho",
"Arial",
]
)
就可以显示中文了。
)
在这里,我们只用关注os.environ["SDL_IME_SHOW_UI"] = "1",pg.key.start_text_input(),pg.TEXTINPUT事件和pg.key.set_text_input_rect(input_rect)就可以了。
os.environ["SDL_IME_SHOW_UI"] = "1"
这条代码是让SDL显示输入法,SDL默认不显示输入法。需要注意的是,此代码应当在pygame.init()之后,在pygame.display.set_mode之前。
pg.TEXTINPUT
这个事件是用输入法输入文字时才触发的,这个事件有text属性,用来接收输入法输入的文字。
pg.key.stop_text_input()
这条代码可以让输入法暂时隐藏,不过此代码应当在pygame.display.set_mode之后。
pg.key.start_text_input()
这条代码可以让暂时隐藏的输入法显示,不过此代码也应当在pygame.display.set_mode之后。
pygame.key.set_text_input_rect(input_rect)
这条代码可以让输入法候选框移动位置,参数是input_rect,input_rect的类型是pygame.Rect。
这是一个示例:
import pygame
import os
pygame.init()
os.environ['SDL_IME_SHOW_UI'] = "1"
screen = pygame.display.set_mode((640, 640))
r = True
while r:
for event in pygame.event.get():
if event.type == pygame.QUIT:
r = False
elif event.type == pygame.TEXTINPUT:
print(event.text)
pygame.quit()