Object Oriented Python - En.pt
Object Oriented Python - En.pt
Object Oriented Python - En.pt
com
PARTE II
USUÁRIO GRÁFICO
INTER FAC ESWITHP YG AME
Instalando o Pygame
Pygame é um pacote gratuito para download. Usaremos o gerenciador de pacotes
pip(abreviatura depip instala pacotes) para instalar pacotes Python. Conforme
mencionado na introdução, estou assumindo que você instalou a versão oficial do
Python depython.org. O programa pip está incluído como parte desse download,
então você já deve tê-lo instalado.
Ao contrário de um aplicativo padrão, você deve executar o pip na linha de comando.
Em um Mac, inicie o aplicativo Terminal (localizado naServiços de utilidade pública
subpasta dentro doFormuláriospasta). Em um sistema Windows, clique no ícone do
Windows, digitecmde pressione ENTER.
NOTA Este livro não foi testado com sistemas Linux. No entanto, a maioria, se não todo, o conteúdo deve
funcionar com ajustes mínimos. Para instalar o pygame em uma distribuição Linux, abra um
terminal da maneira que você estiver acostumado.
importar pygame
Se você vir uma mensagem dizendo algo como “Olá da comunidade pygame” ou se
você não receber nenhuma mensagem, então o pygame foi instalado corretamente. A
falta de uma mensagem de erro indica que o Python conseguiu encontrar e carregar o
pacote pygame e está pronto para uso. Se você gostaria de ver um jogo de exemplo
usando pygame, digite o seguinte comando (que inicia uma versão doInvasores do
espaço):
python3 -m pygame.examples.aliens
90 capítulo 5
Antes de começarmos a usar o pygame, preciso explicar dois conceitos importantes. Primeiro,
explicarei como os pixels individuais são endereçados em programas que usam uma GUI. Em
seguida, discutirei os programas orientados a eventos e como eles diferem dos programas típicos
baseados em texto. Depois disso, codificaremos alguns programas que demonstram os principais
recursos do pygame.
Detalhes da janela
Uma tela de computador é composta por um grande número de linhas e colunas de
pequenos pontos chamadospíxeis(das palavraselemento de imagem). Um usuário
interage com um programa GUI por meio de uma ou mais janelas; cada janela é uma
parte retangular da tela. Os programas podem controlar a cor de qualquer pixel
individual em sua(s) janela(s). Se você estiver executando vários programas GUI, cada
programa normalmente é exibido em sua própria janela. Nesta seção, discutirei como
você aborda e altera pixels individuais em uma janela. Esses conceitos são
independentes do Python; eles são comuns a todos os computadores e são usados em
todas as linguagens de programação.
Você provavelmente está familiarizado com as coordenadas cartesianas em uma grade como a Figura 5-1.
eixo y
1
eixo x
-6 -5 -4 -3 -2 -1 1 2 3 4 5 6
-1
-2
-3
-4
-5
-6
Introdução ao Pygame 91
Qualquer ponto em uma grade cartesiana pode ser localizado especificando suas
coordenadas x e y (nessa ordem). A origem é o ponto especificado como (0, 0) e se
encontra no centro da grade.
As coordenadas da janela do computador funcionam de maneira semelhante (Figura 5-2).
0 Máx. x
Max y
3. Os valores xey são sempre inteiros. Cada par (x, y) especifica um único pixel
na janela. Esses valores são sempre especificados em relação ao canto
superior esquerdo da janela, não à tela. Dessa forma, o usuário pode
mover a janela para qualquer lugar da tela sem afetar as coordenadas
dos elementos do programa exibidos na janela.
A tela completa do computador tem seu próprio conjunto de coordenadas (x, y) para
cada pixel e usa o mesmo tipo de sistema de coordenadas, mas os programas raramente
precisam lidar com as coordenadas da tela.
Quando escrevemos um aplicativo pygame, precisamos especificar a largura e a
altura da janela que queremos criar. Dentro da janela, podemos endereçar qualquer
pixel usando suas coordenadas x e y, como mostrado na Figura 5-3.
A Figura 5-3 mostra um pixel preto na posição (3, 5). Esse é um valor x de 3
(observe que esta é na verdade a quarta coluna, já que as coordenadas começam em
0) e um valor y de 5 (na verdade, a sexta linha). Cada pixel em uma janela é
comumente referido como umponto. Para referenciar um ponto em uma janela, você
normalmente usaria uma tupla Python. Por exemplo, você pode ter uma instrução de
atribuição como esta, com o valor x primeiro:
pixelLocalização = (3, 5)
92 capítulo 5
0 1 2 3 4 5 6 7 8 9 10 11 12 13…
10
11
12
13
…
Figura 5-3: Um único ponto (um único pixel) em uma janela de computador
Introdução ao Pygame 93
0 1 2 3 4 5 6 7 8 9 10 11 12 13…
10
11
12
13
…
Cores de pixel
Vamos explorar como as cores são representadas na tela do computador. Se você tem
experiência com um programa gráfico como o Photoshop, provavelmente já sabe como
isso funciona, mas pode querer uma rápida atualização de qualquer maneira.
Cada pixel na tela é composto por uma combinação de três cores: vermelho, verde
e azul, muitas vezes referidas comoRGB. A cor exibida em qualquer pixel é composta
por alguma quantidade de vermelho, verde e azul, onde a quantidade de cada um é
especificada como um valor de 0, significando nenhum, a 255, significando intensidade
total. Portanto, existem 256 × 256 × 256 combinações possíveis, ou 16.777.216 (muitas
vezes referidas como apenas “16 milhões”) cores possíveis, para cada pixel.
As cores no pygame são fornecidas como valores RGB e as escrevemos como tuplas
Python de três números. Aqui está como criamos constantes para as cores principais:
94 capítulo 5
Aqui estão as definições de mais algumas cores. Você pode criar uma cor
usando qualquer combinação de três números entre 0 e 255:
TEAL = (0, 128, 128)# sem vermelho, verde com metade da força, azul com metade da força
AMARELO = (255, 255, 0) ROXO = (128, 0, 128)
No pygame, você precisará especificar cores quando quiser preencher o plano de fundo de uma janela,
desenhar uma forma em uma cor, desenhar um texto em uma cor e assim por diante. Definir cores
antecipadamente como constantes de tupla as torna muito fáceis de identificar posteriormente no código.
NOTA Chamadas paraimprimir()ainda pode ser muito útil para depuração, quando usado para escrever resultados
intermediários.
evento Algo que acontece enquanto seu programa está sendo executado e que seu programa deseja ou
precisa responder. A maioria dos eventos são gerados por ações do usuário
Introdução ao Pygame 95
Por exemplo, digamos que temos um programa GUI simples que exibe dois
botões: Bark e Meow. Quando clicado, o botão Bark reproduz o som de um
cachorro latindo e o botão Meow reproduz o som de um gato miando (Figura 5-5).
Usando Pygame
A princípio, o pygame pode parecer um pacote extremamente grande com muitas chamadas
diferentes disponíveis. Embora seja grande, na verdade não há muito que você precise
entender para colocar um pequeno programa em funcionamento. Para apresentar o pygame,
primeiro fornecerei um modelo que você pode usar para todos os programas pygame que
você criar. Então eu vou construir sobre esse modelo, adicionando peças-chave de
funcionalidade pouco a pouco.
Nas seções a seguir, mostrarei como:
96 capítulo 5
Abrindo uma janela em branco
Como eu disse anteriormente, os programas pygame são executados constantemente em
um loop, verificando eventos. Pode ajudar pensar em seu programa como uma animação,
onde cada passagem pelo loop principal é um quadro. O usuário pode clicar em algo durante
qualquer quadro, e seu programa deve não apenas responder a essa entrada, mas também
acompanhar tudo o que precisa desenhar na janela. Por exemplo, em um programa de
exemplo mais adiante neste capítulo, moveremos uma bola pela janela para que em cada
quadro a bola seja desenhada em uma posição ligeiramente diferente.
A Listagem 5-1 é um modelo genérico que você pode usar como ponto de partida
para todos os seus programas pygame. Este programa abre uma janela e pinta todo o
conteúdo de preto. A única coisa que o usuário pode fazer é clicar no botão fechar para
sair do programa.
Arquivo: PygameDemo0_WindowOnly/PygameWindowOnly.py
#1 - Importar pacotes
importar pygame
de pygame.locals import *
import sys
# 2 - Definir constantes
PRETO = (0, 0, 0)
WINDOW_WIDTH = 640
WINDOW_HEIGHT = 480
FRAMES_PER_SECOND = 30
#3 - Inicialize o mundo
pygame.init()
janela = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
clock = pygame.time.Clock()
#5 - Inicialize as variáveis
# 9 - Limpe a janela
Introdução ao Pygame 97
window.fill(PRETO)
1. Importe pacotes.
O modelo começa com oimportardeclarações. Primeiro importamos o próprio pacote
pygame, depois algumas constantes definidas dentro do pygame que usaremos mais
tarde. A última importação é asistemapacote, que usaremos para sair do nosso programa.
2. Defina constantes.
Em seguida, definimos quaisquer constantes para nosso programa. Primeiro definimos o
valor RGB paraPRETO,que usaremos para pintar o fundo da nossa janela. Em seguida,
definimos constantes para a largura e altura da nossa janela em pixels e uma constante
para a taxa de atualização do nosso programa. Este número define o número máximo de
vezes que o programa fará um loop (e, portanto, redesenhará a janela) por segundo.
Nosso valor de 30 é bastante típico. Se a quantidade de trabalho feito em nosso loop
principal for excessiva, o programa pode ser executado mais lentamente que esse valor,
mas nunca será executado mais rápido. Uma taxa de atualização muito alta pode fazer
com que o programa seja executado muito rápido. Em nosso exemplo de bola, isso
significa que a bola pode quicar na janela mais rápido do que o pretendido.
Esta é uma seção de espaço reservado, na qual eventualmente adicionaremos código para
carregar imagens externas, sons e assim por diante do disco para uso em nosso programa.
Neste programa básico não estamos usando nenhum ativo externo, então esta seção está
vazia por enquanto.
5. Inicialize as variáveis.
Aqui, eventualmente, inicializaremos quaisquer variáveis que nosso programa
usará. Atualmente não temos nenhum, então não temos código aqui.
98 capítulo 5
6. Faça um loop para sempre.
Neste programa mínimo, onde a única ação que um usuário pode realizar é
fechar a janela, o único tipo de evento que verificamos é a constante
pygame.QUIT,gerado pelo pygame quando o usuário clica no botão fechar. Se encontrarmos
esse evento, dizemos ao pygame para sair, o que libera todos os recursos que ele estava
usando. Então desistimos do nosso programa.
9. Limpe a janela.
Em cada iteração através do loop principal, nosso programa deve redesenhar tudo na
janela, o que significa que precisamos limpá-lo primeiro. A abordagem mais simples é
apenas preencher a janela com uma cor, o que fazemos aqui com uma chamada para
janela.preencher(),especificando um fundo preto. Também poderíamos desenhar uma
imagem de fundo, mas vamos adiar isso por enquanto.
Aqui colocaremos o código para desenhar tudo o que queremos mostrar em nossa
janela. Neste programa de exemplo não há nada para desenhar.
Introdução ao Pygame 99
12. Desacelere um pouco as coisas.
Quando você executa este programa, o programa apenas exibe uma janela em branco
preenchida com preto. Para encerrar o programa, clique no botão Fechar na barra de título.
Vamos desenhar algo na janela. Há duas partes para mostrar uma imagem gráfica:
primeiro carregamos a imagem na memória do computador, depois exibimos a
imagem na janela do aplicativo.
Com o pygame, todas as imagens (e sons) precisam ser mantidas em arquivos externos
ao seu código. O Pygame suporta muitos formatos de arquivos gráficos padrão, incluindo.png,
.jpg, e.gif. Neste programa vamos carregar uma imagem de uma bola do arquivobola.png.
Como lembrete, o código e os recursos associados a todas as principais listagens deste livro
estão disponíveis para download emhttps://www.nostarch.com/objectorientedpython/ehttps://
github.com/IrvKalb/Object-Oriented-Python-Code/.
Embora só precisemos de um arquivo gráfico neste programa, é uma boa ideia usar uma
abordagem consistente para lidar com arquivos gráficos e de som, então vou apresentar um
para você aqui. Primeiro, crie uma pasta de projeto. Coloque seu programa principal nessa
pasta, junto com quaisquer arquivos relacionados que contenham classes e funções do
Python. Em seguida, dentro da pasta do projeto, crie umimagenspasta na qual você colocará
os arquivos de imagem que deseja usar em seu programa. Crie também umsonspasta e
coloque os arquivos de som que deseja usar lá. A Figura 5-6 mostra a estrutura sugerida.
Todos os programas de exemplo neste livro usarão este layout de pasta de projeto.
100 capítulo 5
PyCharm, ele define a pasta atual para aquela que contém seu programa Python principal para que
você possa usar caminhos relativos com facilidade. Neste livro, assumirei que você está usando um
IDE e representará todos os caminhos como caminhos relativos.
O caminho relativo para um arquivo gráfico (por exemplo,bola.png) na mesma pasta
que seu arquivo principal do Python seria apenas o nome do arquivo como uma string (por
exemplo, 'bola.png').Usando a estrutura de projeto sugerida, o relativo
caminho seria 'imagens/bola.png'.
Isso diz que dentro da pasta do projeto haverá outra pasta chamada imagens, e dentro
dessa pasta há um arquivo chamadobola.png. Em strings de caminho, os nomes das pastas
são separados pelo caractere barra.
No entanto, se você espera executar seu programa a partir da linha de comando,
precisará construir caminhos absolutos para todos os arquivos. Umcaminho absolutoé aquele
que começa na raiz do sistema de arquivos e inclui toda a hierarquia de pastas do seu arquivo.
Para construir um caminho absoluto para qualquer arquivo, você pode usar um código como
este, que cria uma string de caminho absoluto para obola.pngarquivo noimagenspasta dentro
da pasta do projeto:
Agora vamos criar o código do programa ball, começando com o modelo anterior de 12
etapas e adicionando apenas duas novas linhas de código, conforme mostrado na Listagem 5-2.
Arquivo: PygameDemo1_OneImage/PygameOneImage.py
- - - recorte ---
#3 - Inicialize o mundo
pygame.init()
janela = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
clock = pygame.time.Clock()
#5 - Inicialize as variáveis
- - - recorte ---
NOTA Na documentação oficial do pygame, cada imagem, incluindo a janela do aplicativo, é conhecida
comosuperfície. Usarei termos mais específicos: vou me referir à janela do aplicativo simplesmente
como umjanelae para qualquer imagem carregada de um arquivo externo como imagem. reservo o
prazosuperfíciepara qualquer imagem desenhada em tempo real.
Nós então dizemos ao programa para desenhar a bola2toda vez que passamos pelo loop
principal. Especificamos a localização que representa a posição para colocar o canto superior
esquerdo do retângulo delimitador da imagem, normalmente como uma tupla de
coordenadas x e y.
O nome da funçãoblit()é uma referência muito antiga às palavrastransferência de
bloco de bits, mas neste contexto significa apenas “desenhar”. Como o programa
carregou a imagem da bola anteriormente, o pygame sabe o tamanho da imagem,
então só precisamos dizer onde desenhar a bola. Na Listagem 5-2, damos um valor x
de 100 e um valor y de 200.
Quando você executa o programa, em cada iteração através do loop (30
vezes por segundo) cada pixel na janela é definido como preto, então a bola é
desenhada sobre o fundo. Do ponto de vista do usuário, parece que nada está
acontecendo - a bola apenas fica em um ponto com o canto superior esquerdo
de seu retângulo delimitador no local (100, 200).
Arquivo: PygameDemo2_ImageClickAndMove/PygameImageClickAndMove.py
#1 - Importar pacotes
importar pygame
de pygame.locals import *
import sys
102 capítulo 5
1importar aleatório
# 2 - Definir constantes
PRETO = (0, 0, 0)
WINDOW_WIDTH = 640
WINDOW_HEIGHT = 480
FRAMES_PER_SECOND = 30
2BALL_WIDTH_HEIGHT = 100
MAX_WIDTH = WINDOW_WIDTH - BALL_WIDTH_HEIGHT
MAX_HEIGHT = WINDOW_HEIGHT - BALL_WIDTH_HEIGHT
#3 - Inicialize o mundo
pygame.init()
janela = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
clock = pygame.time.Clock()
#5 - Inicialize as variáveis
3bolaX = random.randrange(MAX_WIDTH)
bolaY = random.randrange(MAX_HEIGHT)
4ballRect = pygame.Rect(ballX, ballY, BALL_WIDTH_HEIGHT, BALL_WIDTH_HEIGHT)
# 9 - Limpe a janela
window.fill(PRETO)
<rectObject>=pygame.Rect(<x>,<e>,<largura>,<altura>)
<booleanVariable>=<someRectangle>.collidepoint(<algumXYLocalização>)
104 capítulo 5
O método retorna um booleanoVerdadeirose o ponto dado está dentro do
retângulo. Se o usuário clicou na bola, selecionamos aleatoriamente novos valores para
bolaXebolaY.Usamos esses valores para criar um novo retângulo para a bola no
novo local aleatório.
A única mudança aqui é que sempre desenhamos a bola no local dado
pela tupla (bolaX, bolaY)7.O efeito é que sempre que o usuário clica dentro
do retângulo da bola, a bola parece se mover para algum novo local
aleatório na janela.
Manipulando o teclado
O próximo passo é permitir que o usuário controle algum aspecto do programa através do teclado.
Existem duas maneiras diferentes de lidar com as interações do teclado do usuário: quando uma
tecla individual é pressionada e quando um usuário mantém pressionada uma tecla para indicar que
uma ação deve ocorrer enquanto essa tecla estiver pressionada (conhecida comomodo contínuo).
Assim como os cliques do mouse, cada pressionamento de tecla gera dois eventos: tecla para baixo e tecla
para cima. Os dois eventos têm diferentes tipos de eventos:pygame.KEYDOWNepygame.KEYUP.
A Listagem 5-4 mostra um pequeno programa de exemplo que permite ao
usuário mover a imagem da bola na janela usando o teclado. O programa também
mostra um retângulo de destino na janela. O objetivo do usuário é mover a imagem da
bola para que ela se sobreponha à imagem alvo.
Arquivo: PygameDemo3_MoveByKeyboard/PygameMoveByKeyboardOncePerKey.py
#1 - Importar pacotes
importar pygame
de pygame.locals import *
import sys
importar aleatório
# 2 - Definir constantes
PRETO = (0, 0, 0)
WINDOW_WIDTH = 640
WINDOW_HEIGHT = 480
FRAMES_PER_SECOND = 30
BALL_WIDTH_HEIGHT = 100
MAX_WIDTH = WINDOW_WIDTH - BALL_WIDTH_HEIGHT
MAX_HEIGHT = WINDOW_HEIGHT - BALL_WIDTH_HEIGHT 1
META_X = 400
TARGET_Y = 320
TARGET_WIDTH_HEIGHT = 120
N_PIXELS_TO_MOVE = 3
#3 - Inicialize o mundo
pygame.init()
# 9 - Limpe a janela
window.fill(PRETO)
106 capítulo 5
Primeiro adicionamos algumas novas constantes1para definir as coordenadas xey
do canto superior esquerdo do retângulo de destino e a largura e altura do destino. Em
seguida, carregamos a imagem do retângulo alvo2.
No loop em que procuramos por eventos, adicionamos um teste para um pressionamento de
tecla verificando um evento do tipopygame.KEYDOWN3.Se um evento de tecla pressionada for
detectado, analisamos o evento para descobrir qual tecla foi pressionada. Cada tecla tem uma
constante associada em pygame, então aqui verificamos se o usuário pressionou a seta para a
esquerda, para cima, para baixo ou para a direita. Para cada uma dessas chaves, modificamos o
valor da coordenada x ou y da bola apropriadamente por um pequeno número de pixels.
<booleanVariable>=<rect1>.colliderect(<rect2>)
<aTuplo>=pygame.key.get_pressed()
keyPressedTuple = pygame.key.get_pressed()
# Agora use uma constante para obter o elemento apropriado da tupla
aIsDown = keyPressedTuple[pygame.K_a]
Arquivo: PygameDemo3_MoveByKeyboard/PygameMoveByKeyboardContinuous.py
# pygame demo 3(b) - uma imagem, modo contínuo, mova enquanto uma tecla estiver pressionada
- - - recorte ---
# 7 - Verifique e manipule eventos
para evento em pygame.event.get():
# Clicou no botão fechar? Saia do pygame e termine o programa if
event.type == pygame.QUIT:
pygame.quit()
sys.exit()
1keyPressedTuple = pygame.key.get_pressed()
if keyPressedTuple[pygame.K_UP]:#subindo
bolaY = bolaY - N_PIXELS_TO_MOVE
108 capítulo 5
A outra mudança é que essa abordagem permite verificar se várias chaves estão inativas
ao mesmo tempo. Por exemplo, se o usuário pressionar e segurar as teclas de seta para a
esquerda e para baixo, a bola se moverá diagonalmente para baixo e para a esquerda. Você
pode verificar quantas teclas estão sendo pressionadas conforme desejar. No entanto, o
número desimultâneopressionamentos de teclas que podem ser detectados são limitados
pelo sistema operacional, o hardware do teclado e muitos outros fatores. O limite típico é de
cerca de quatro chaves, mas sua milhagem pode variar.
Arquivo: PygameDemo4_OneBallBounce/PygameOneBallBounceXY.py
# pygame demo 4(a) - uma imagem, salte pela janela usando (x, y) coordenadas
#1 - Importar pacotes
importar pygame
de pygame.locals import *
import sys
importar aleatório
# 2 - Definir constantes
PRETO = (0, 0, 0)
WINDOW_WIDTH = 640
WINDOW_HEIGHT = 480
FRAMES_PER_SECOND = 30
BALL_WIDTH_HEIGHT = 100
N_PIXELS_PER_FRAME = 3
#3 - Inicialize o mundo
pygame.init()
janela = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
clock = pygame.time.Clock()
#5 - Inicialize as variáveis
Listagem 5-6: Uma animação baseada em localização, quicando uma bola pela janela
110 capítulo 5
e saísse da borda direita, mudaríamos o valor dexVelocidadea partir de3para
- 3fazer com que a bola comece a se mover para a esquerda e vice-versa. Em seguida,
fazemos uma verificação semelhante para a coordenada y para fazer a bola quicar na
borda superior ou inferior, conforme necessário.
Finalmente, atualizamos a posição da bola adicionando oxVelocidadepara o
bolaXcoordenar e adicionar oyVelocidadepara obolacoordenada3.Isso posiciona a
bola em um novo local em ambos os eixos.
Na parte inferior do loop principal, desenhamos a bola. Como estamos
atualizando os valores debolaXebolaem cada quadro, a bola parece animar
suavemente. Experimente. Sempre que a bola atinge qualquer borda, ela parece
ricochetear.
Atributo Descrição
<correto>.x A coordenada x da borda esquerda doreto
<correto>.y A coordenada y da borda superior doreto
<correto>.deixei A coordenada x da borda esquerda doreto (igual a
<correto>.x)
<correto>.topo A coordenada y da borda superior doreto (igual a
<correto>.y)
<correto>.certo A coordenada x da borda direita doreto
<correto>.fundo A coordenada y da borda inferior doreto
<correto>.topleft Uma tupla de dois inteiros: as coordenadas do canto superior esquerdo da
reto
<correto>.canto superior direito Uma tupla de dois inteiros: as coordenadas do canto superior direito da
reto
<correto>.canto inferior direito Uma tupla de dois inteiros: as coordenadas do canto inferior direito da
reto
(contínuo)
Atributo Descrição
<correto>.midtop Uma tupla de dois inteiros: as coordenadas do ponto médio da borda
superior doreto
Um pygameretotambém pode ser pensado e acessado como uma lista de quatro elementos.
Especificamente, você pode usar um índice para obter ou definir qualquer parte individual de um
reto.Por exemplo, usando obolaReto,os elementos individuais podem ser acessados como:
Arquivo: PygameDemo4_OneBallBounce/PygameOneBallBounceRects.py
# pygame demo 4(b) - uma imagem, salte pela janela usando rects
#1 - Importar pacotes
importar pygame
de pygame.locals import *
import sys
importar aleatório
# 2 - Definir constantes
PRETO = (0, 0, 0)
WINDOW_WIDTH = 640
WINDOW_HEIGHT = 480
112 capítulo 5
FRAMES_PER_SECOND = 30
N_PIXELS_PER_FRAME = 3
#3 - Inicialize o mundo
pygame.init()
janela = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
clock = pygame.time.Clock()
Listagem 5-7: Uma animação baseada em localização, quicando uma bola pela janela, usandoretos
Tocando sons
Existem dois tipos de sons que você pode querer tocar em seus
programas: efeitos sonoros curtos e música de fundo.
Todos os efeitos sonoros devem estar em arquivos externos e devem estar em.ondaou
. oggformato. Tocar um efeito sonoro relativamente curto consiste em duas etapas: carregar o
som de um arquivo de som externo uma vez; então, no(s) momento(s) apropriado(s), toque
seu som.
Para carregar um efeito sonoro na memória, você usa uma linha como esta:
<soundVariable>.Toque()
Modificaremos a Listagem 5-7 para adicionar um efeito sonoro “boing” sempre que
a bola quicar em um lado da janela. Existe umsonspasta na pasta do projeto no mesmo
nível do programa principal. Logo após carregar a imagem da bola, carregamos o
arquivo de som adicionando este código:
114 capítulo 5
Para reproduzir o efeito sonoro “boing” sempre que alteramos a direção
horizontal ou vertical da bola, modificamos a seção #8 para ficar assim:
Quando você encontra uma condição que deve reproduzir um efeito sonoro, você
adiciona uma chamada aoToque()método do som. Existem muitas outras opções para controlar
os efeitos sonoros; você pode encontrar detalhes na documentação oficial emhttps://
www.pygame.org/docs/ref/mixer.html.
memória:
o<caminho para o arquivo de som>é uma string de caminho onde o arquivo de som pode ser
encontrado. Você pode usar.mp3arquivos, que parecem funcionar melhor, bem como.ondaou.ogg
arquivos. Quando você deseja iniciar a reprodução da música, você precisa fazer esta chamada:
Para reproduzir uma música de fundo repetidamente, você pode passar um -1por
<número de voltas>para executar a música para sempre. o<posição inicial>é normalmente
definido para0para indicar que você deseja reproduzir o som desde o início.
Existe uma versão modificada para download do programa quicando bola que
carrega corretamente o efeito sonoro e os arquivos de música de fundo e inicia a
reprodução do som de fundo. As únicas mudanças estão na seção #4, conforme
mostrado aqui.
Arquivo: PygameDemo4_OneBallBounce/PyGameOneBallBounceWithSound.py
Formas de desenho
O Pygame oferece várias funções integradas que permitem desenhar certas formas
conhecidas comoprimitivos, que incluem linhas, círculos, elipses, arcos, polígonos e
retângulos. A Tabela 5-2 fornece uma lista dessas funções. Observe que existem duas
chamadas que atraemanti-aliaslinhas. Estas são linhas que incluem cores misturadas
nas bordas para fazer com que as linhas pareçam suaves e menos irregulares. Existem
duas vantagens principais em usar essas funções de desenho: elas são executadas com
extrema rapidez e permitem que você desenhe formas simples sem precisar criar ou
carregar imagens de arquivos externos.
Função Descrição
pygame.draw.aaline() Desenha uma linha anti-alias
Arquivo: PygameDemo5_DrawingShapes.py
- - - recorte ---
enquanto Verdadeiro:
116 capítulo 5
# 8 - Faça qualquer ação "por frame"
# 9 - Limpe a janela
window.fill(CINZA)
# Desenha um arco
pygame.draw.arc(janela, AZUL, (20, 400, 100, 100), 0, 2, 5)
# Desenhe linhas com suavização de serrilhado: uma única linha, depois uma lista
de pontos pygame.draw.aaline(window, RED, (500, 400), (540, 470), 1)
pygame.draw.aalines(window, BLUE, True,
((580, 400), (587, 450), (595,
460), (600, 444)), 1)
Listagem 5-8: Um programa para demonstrar chamadas para funções primitivas de desenho em pygame
Linha anti-alias
Linhas anti-alias
118 capítulo 5
Arco
Círculo
Elipse
Linha
Linhas
Polígono
Resumo
Neste capítulo, apresentei o básico do pygame. Você instalou o pygame em seu
computador, então aprendeu sobre o modelo de programação orientada a eventos e o
uso de eventos, que é muito diferente da codificação de programas baseados em texto.
Expliquei o sistema de coordenadas de pixels em uma janela e a forma como as cores
são representadas no código.
Para começar logo no início com o pygame, apresentei um modelo de 12 seções
que não faz nada além de abrir uma janela e pode ser usado para construir qualquer
programa baseado em pygame. Usando essa estrutura, construímos programas de
amostra que mostravam como desenhar uma imagem na janela (usandoblit()), como
detectar eventos do mouse e como lidar com a entrada do teclado. A próxima
demonstração explicou como construir uma animação baseada em localização.
Retângulos são muito importantes no pygame, então eu abordei como os atributos de
umretoobjeto pode ser usado. Também forneci alguns códigos de exemplo para mostrar como
reproduzir efeitos sonoros e música de fundo para aumentar o prazer do usuário com seus
programas. Por fim, apresentei como usar os métodos pygame para desenhar formas
primitivas em uma janela.
Embora eu tenha introduzido muitos conceitos dentro do pygame,
quase tudo que mostrei neste capítulo foi essencialmente procedural. oreto
object é um exemplo de código orientado a objetos construído diretamente no pygame. No
próximo capítulo, mostrarei como usar OOP no código para usar o pygame de forma mais
eficaz.
120 capítulo 5
OBJEC T- ORIENTED YG AME
6
Neste capítulo, demonstrarei como você pode usar
técnicas de POO efetivamente dentro do framework
pygame. Começaremos com um exemplo de código
procedural, depois dividiremos esse código em uma única
classe e algum código principal que chama os métodos dessa
classe. Depois disso, vamos construir duas classes,Botão Simplese
Texto Simples,que implementam widgets básicos da interface do
O primeiro passo é criar uma pasta de projeto, na qual você precisa de umBall.py para o
novoBolaclass, o arquivo de código principalMain_BallBounce.py, e umimagens pasta
contendo obola.pngarquivo de imagem.
A Listagem 6-1 mostra o código do novoBolaclasse.
Arquivo: PygameDemo6_BallBounceObjectOriented/Ball.py
importar pygame
de pygame.locals import *
import random
# aula de bola
class Bola():
122 Capítulo 6
2self.image = pygame.image.load('images/ball.png')
# Um rect é composto por [x, y, largura, altura]
ballRect = self.image.get_rect() self.width =
ballRect.width self.height = ballRect.height
self.maxWidth = windowWidth - self.width
self.maxHeight = windowHeight - self.height
5atualização def(self):
# Verifique se está batendo em uma parede. Se sim, mude essa
direção. if (self.x < 0) ou (self.x >= self.maxWidth):
self.xSpeed = -self.xSpeed
6def draw(self):
self.window.blit(self.image, (self.x, self.y))
Arquivo: PygameDemo6_BallBounceObjectOriented/Main_BallBounce.py
#1 - Importar pacotes
importar pygame
de pygame.locals import *
import sys
importar aleatório
1da importação de bola *# traz o código da classe Ball
# 2 - Definir constantes
PRETO = (0, 0, 0)
WINDOW_WIDTH = 640
WINDOW_HEIGHT = 480
FRAMES_PER_SECOND = 30
#3 - Inicialize o mundo
pygame.init()
janela = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
clock = pygame.time.Clock()
#5 - Inicialize as variáveis
2oBola = Bola(janela, WINDOW_WIDTH, WINDOW_HEIGHT)
124 Capítulo 6
# 9 - Limpe a janela antes de desenhá-la novamente
window.fill(PRETO)
Listagem 6-2: O novo programa principal que instancia umBolae faz chamadas para seus métodos
Arquivo: PygameDemo6_BallBounceObjectOriented/Main_BallBounceManyBalls.py
- - - recorte ---
N_BOLAS = 3
- - - recorte ---
#5 - Inicialize as variáveis 1
bolaLista = []
para oBall no intervalo (0, N_BALLS):
# Cada vez que passar pelo loop, crie um objeto Ball oBola =
Bola(janela, WINDOW_WIDTH, WINDOW_HEIGHT)
- - - recorte ---
126 Capítulo 6
Figura 6-1: Criando, atualizando e desenhando 300Bolaobjetos
O fato de podermos instanciar qualquer número de objetos de um único script será vital
não apenas na definição de objetos do jogo como naves espaciais, zumbis, balas, tesouros e
assim por diante, mas também na construção de controles GUI, como botões, caixas de
seleção, entrada de texto campos e saídas de texto.
O comportamento do botão deve ser comum e consistente para todos os botões usados em uma
GUI, portanto, construiremos uma classe que cuide dos detalhes do comportamento. Uma vez que
criamos uma classe de botão simples, podemos instanciar qualquer número de botões e todos eles
funcionarão exatamente da mesma maneira.
Vamos considerar quais comportamentos nossa classe de botão deve suportar. Vamos precisar de
métodos para:
Arquivo: PygameDemo7_SimpleButton/SimpleButton.py
# classe SimpleButton
#
# Usa uma abordagem de "máquina de estado"
#
importar pygame
da importação de pygame.locals *
class SimpleButton():
# Usado para rastrear o estado do botão
STATE_IDLE = 'ocioso'# botão está para cima, o mouse não está sobre o botão
STATE_ARMED = 'armado'# botão está pressionado, passe o mouse sobre o botão
STATE_DISARMED = 'desarmado'# clicou no botão, saiu
128 Capítulo 6
self.surfaceDown = pygame.image.load(down)
# Pega o retículo do botão (usado para ver se o mouse está sobre o botão) self.rect
= self.surfaceUp.get_rect() self.rect[0] = loc[0]
self.rect[1] = loc[1]
self.state = SimpleButton.STATE_IDLE
eventPointInButtonRect = self.rect.collidepoint(eventObj.pos)
if self.state == SimpleButton.STATE_IDLE:
if (eventObj.type == MOUSEBUTTONDOWN) e eventPointInButtonRect:
self.state = SimpleButton.STATE_ARMED
retorna falso
def draw(self):4
# Desenha a aparência atual do botão para a janela if
self.state == SimpleButton.STATE_ARMED:
self.window.blit(self.surfaceDown, self.loc)
Esta linha cria umBotão Simplesobjeto, especificando um local para desenhá-lo (como
sempre, as coordenadas são para o canto superior esquerdo do retângulo delimitador) e
fornecendo os caminhos para as imagens para cima e para baixo do botão. No loop
principal, sempre que qualquer evento acontecer, precisamos chamar o
handleEvent()para
ver se o usuário clicou no botão. Se o usuário clicar no
botão, o programa deverá realizar alguma ação. Também no loop principal,
precisamos chamar oempate()método para fazer o botão aparecer na janela.
Figura 6-2: A interface do usuário de um programa com uma única instância de umBotão Simples
Arquivo: PygameDemo7_SimpleButton/Main_SimpleButton.py
- - - recorte ---
#5 - Inicialize as variáveis
# Cria uma instância de um SimpleButton
130 Capítulo 6
1oButton = SimpleButton(janela, (150, 30),
'imagens/buttonUp.png',
'imagens/buttonDown.png')
# 9 - Limpe a janela
window.fill(CINZA)
Não precisamos fazer nenhuma alteração noBotão Simplesarquivo de classe para fazer isso.
Simplesmente modificamos nosso código principal para instanciar trêsBotão Simples
objetos em vez de um.
Arquivo: PygameDemo7_SimpleButton/Main_SimpleButton3Buttons.py
oButtonA.draw()
oButtonB.draw()
oButtonC.draw()
Ao executar o programa, você verá uma janela com três botões. Clicar em
qualquer um dos botões imprime uma mensagem mostrando o nome do botão
que foi clicado.
A ideia chave aqui é que, como estamos usando três instâncias do mesmo
Botão Simplesclasse, o comportamento de cada botão será idêntico. Um benefício
importante dessa abordagem é que qualquer alteração no código no
Botão Simplesclasse afetará todos os botões instanciados da classe. O programa
principal não precisa se preocupar com nenhum detalhe do funcionamento
interno do código do botão, precisando apenas chamar ohandleEvent()método de
cada botão no loop principal. Cada botão retornaráVerdadeiroouFalsopara dizer
que foi ou não clicado.
132 Capítulo 6
Construindo uma exibição de texto reutilizável orientada a objetos
Existem dois tipos diferentes de texto em um programa pygame: texto de exibição e texto de
entrada. O texto de exibição é gerado pelo seu programa, equivalente a uma chamada para o
imprimir()função, exceto que é exibido em uma janela pygame. O texto de entrada é uma string
de entrada do usuário, equivalente a uma chamada paraentrada().Nesta seção, discutirei o texto
de exibição. Veremos como lidar com o texto de entrada no próximo capítulo.
pygame.font.init()
Arquivo: PygameDemo8_SimpleTextDisplay/SimpleText.py
# classe SimpleText
importar pygame
da importação de pygame.locals *
5def draw(self):
self.window.blit(self.textSurface, self.loc)
Você pode pensar em umTexto Simplesobjeto como um campo na janela onde você
deseja que o texto seja exibido. Você pode usar um para exibir texto de etiqueta imutável ou
para exibir texto que muda ao longo de um programa.
oTexto Simplesclasse tem apenas três métodos. O __iniciar__()método1 espera que
a janela seja desenhada, o local no qual desenhar o texto na janela, qualquer texto
inicial que você deseja ver exibido no campo e uma cor de texto. Ligando
pygame.font.init()2inicia o sistema de fontes do pygame. A chamada na primeira
instanciadaTexto Simplesobjeto realmente faz a inicialização; qualquer adicionalTexto
Simplesobjetos também farão esta chamada, mas como as fontes já foram
inicializadas, a chamada retorna imediatamente. Criamos um novoFonte
objeto compygame.font.SysFont()3.Em vez de fornecer um nome de fonte específico,
Nenhumindica que usaremos qualquer que seja a fonte padrão do sistema.
osetValue()O método renderiza uma imagem do texto a ser exibido e salva essa
imagem noself.textSurfacevariável de instância4.À medida que o programa é
executado, sempre que você quiser alterar o texto exibido, você chama osetValue()
método, passando o novo texto a ser exibido. osetValue()O método também tem
uma otimização: ele lembra o último texto que renderizou e, antes de fazer
qualquer outra coisa, verifica se o novo texto é igual ao texto anterior. Se o texto
não mudou, não há nada a fazer e o método apenas retorna. Se houver novo texto,
ele renderiza o novo texto em uma superfície a ser desenhada.
oempate()método5desenha a imagem contida noself.textSurface
variável de instância na janela no local fornecido. Este método deve ser
chamado em cada frame.
Existem várias vantagens nessa abordagem:
134 Capítulo 6
• CadaTexto SimplesO objeto lembra a janela na qual ele desenha, o local
onde o texto deve ser colocado e a cor do texto. Portanto, você só
precisa especificar esses valores uma vez, ao instanciar um
Texto Simplesobjeto, normalmente antes do início do loop principal.
• CadaTexto SimplesO objeto também é otimizado para lembrar tanto o texto que foi
solicitado a desenhar pela última vez quanto a imagem (self.textSurface)que fez a
partir do texto atual. Ele só precisa renderizar uma nova superfície quando o texto
muda.
• Para mostrar vários pedaços de texto em uma janela, você só precisa instanciar
váriosTexto Simplesobjetos. Este é um conceito chave da programação orientada a
objetos.
Arquivo: PygameDemo8_SimpleTextDisplay/Main_BallTextAndButton.py
#1 - Importar pacotes
importar pygame
de pygame.locals import *
import sys
importar aleatório
1da importação de bola *# traz o código da classe Ball
da importação SimpleText * da
importação SimpleButton *
# 2 - Definir constantes
PRETO = (0, 0, 0) BRANCO =
(255, 255, 255)
WINDOW_WIDTH = 640
WINDOW_HEIGHT = 480
FRAMES_PER_SECOND = 30
#3 - Inicialize o mundo
pygame.init()
janela = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
clock = pygame.time.Clock()
#5 - Inicialize as variáveis
2oBola = Bola(janela, WINDOW_WIDTH, WINDOW_HEIGHT)
oFrameCountLabel = SimpleText(janela, (60, 20),
'O programa passou por tantos loops: ', BRANCO)
3if oRestartButton.handleEvent(evento):
contador de quadros = 0# botão clicado, reset do contador
Listagem 6-7: Um exemplo de programa principal para mostrarBola,Texto Simples, eBotão Simples
136 Capítulo 6
Na instanciação doTexto Simplesobjetos, o último argumento é uma cor de texto
e especificamos que os objetos devem ser renderizados emBRANCOpara que
possam ser vistos contra umPRETOfundo. No próximo capítulo, mostrarei como
expandir oTexto Simplesclass para incorporar mais atributos, sem complicar a
interface da classe. Construiremos um objeto de texto mais completo que tenha
valores padrão razoáveis para cada um desses atributos, mas que permita
substituir esses padrões.
Retornos de chamada
if oButton.handleEvent(evento):
print('O botão foi clicado')
ligue de volta Uma função ou método de um objeto que é chamado quando uma determinada ação, evento ou
condição acontece
Uma maneira fácil de entender isso é pensar no filme de sucesso de 1984 Caça-
fantasmas. O slogan do filme é “Quem você vai chamar?” No filme, os Caça-Fantasmas
veicularam um anúncio na TV que dizia às pessoas que, se vissem um fantasma (esse é o
evento a ser procurado), deveriam ligar para os Caça-Fantasmas (o retorno de chamada) para
se livrar dele. Ao receber a chamada, os Caça-Fantasmas tomam as medidas apropriadas
para eliminar o fantasma.
Como exemplo, considere um objeto de botão que é inicializado para ter um retorno
de chamada. Quando o usuário clicar no botão, o botão chamará a função ou método de
retorno de chamada. Essa função ou método executa qualquer código necessário para
reagir ao clique do botão.
importar tkinter
def minhaFunção():
print('myCallBackFunction foi chamado')
138 Capítulo 6
fora disso. Isso geralmente envolve vários encadeamentos Python e está além do escopo
deste livro, mas a técnica de usar um retorno de chamada é a maneira geral como isso é
feito.
opcional adicional, o chamador pode fornecer uma função ou método de um objeto a ser chamado
de volta quando um clique em umBotão Simplesobjeto acontece. Cada instância de
Botão Simpleslembra o retorno de chamada em uma variável de instância. Quando o usuário
completa um clique, a instância deBotão Simpleschama o retorno de chamada.
O programa principal na Listagem 6-8 cria três instâncias do
Botão Simplesclasse, cada uma das quais lida com o clique do botão de uma maneira diferente. O
Arquivo: PygameDemo9_SimpleButtonWithCallback/Main_SimpleButtonCallback.py
#1 - Importar pacotes
importar pygame
from pygame.locals import *
from SimpleButton import *
import sys
# # 2 - Definir constantes
CINZA = (200, 200, 200)
WINDOW_WIDTH = 400
WINDOW_HEIGHT = 100
FRAMES_PER_SECOND = 30
def meuMétodo(self):
print('O usuário pressionou ButtonC, chamado myMethod do objeto CallBackTest')
#3 - Inicialize o mundo
pygame.init()
janela = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
clock = pygame.time.Clock()
#5 - Inicialize as variáveis
oButtonC.handleEvent(evento)7
# 9 - Limpe a janela
window.fill(CINZA)
Listagem 6-8: Uma versão do programa principal que lida com cliques de botão de três maneiras diferentes
140 Capítulo 6
Começamos com uma função simples,myCallBackFunction()1,que apenas imprime uma
mensagem para anunciar que foi chamado. A seguir, temos umTeste de retorno de chamada
classe que contém o métodomeuMétodo()2,que imprime sua própria mensagem para anunciar que
foi chamado. Nós criamos umoCallBackTestobjeto doTeste de retorno de chamadaclasse3.Precisamos
deste objeto para que possamos configurar um retorno de chamada para
oCallBack.myMethod().
Então criamos trêsBotão Simplesobjetos, cada um usando uma abordagem diferente4.O
primeiro,oBotãoA,não tem retorno de chamada. O segundo,oBotãoB,define seu retorno de
chamada para a funçãomyCallBackFunction().O terceiro,oBotãoC,define seu
retorno de chamada paraoCallBack.myMethod().
Resumo
Neste capítulo, mostrei como você pode começar com um programa procedural e extrair
o código relacionado para construir uma classe. Nós criamos umBolaclass para
demonstrar isso, então modifiquei o código principal do nosso programa de
demonstração do capítulo anterior para chamar métodos da classe para informar oBola
objetoo quefazer, sem se preocuparComo asatinge o resultado. Com todo o código
relacionado em uma classe separada, é fácil criar uma lista de objetos e instanciar e
gerenciar quantos objetos quisermos.
Construímos então umBotão Simplesclasse e umTexto Simplesclasse que escondem a
complexidade dentro de sua implementação e criam código altamente reutilizável. No
próximo capítulo, desenvolverei essas classes para desenvolver classes de exibição de texto e
botões de “força profissional”.
Finalmente, apresentei o conceito de callback, onde você passa uma função ou método em uma
chamada para um objeto. O retorno de chamada é posteriormente chamado de volta quando um
evento acontece ou uma ação é concluída.
Figura 7-1: Como os argumentos passados para um método correspondem aos seus parâmetros
No entanto, o Python (e algumas outras linguagens) permite que você torne alguns dos
argumentos opcionais. Se um argumento opcional não for fornecido em uma chamada,
podemos fornecer um valor padrão para usar na função ou método. Vou explicar por meio de
uma analogia do mundo real.
Se você pedir um hambúrguer em um restaurante Burger King, seu
hambúrguer virá com ketchup, mostarda e picles. Mas o Burger King é
famoso por dizer: “Você pode fazer do seu jeito”. Caso queira alguma outra
combinação de condimentos, deve dizer o que quer (ou não quer) ao fazer
seu pedido.
Começaremos escrevendo umpedirBurgers()função que simula fazer um
pedido de hambúrguer da maneira normal que definimos funções, sem
implementar valores padrão:
Vamos ver como o Python nos permite configurar parâmetros opcionais que podem
receber valores padrão se nada for especificado.
144 Capítulo 7
Parâmetros posicionais e de palavra-chave
pedirHambúrgueres(2)
Nesta chamada, estamos novamente especificando dois hambúrgueres com ketchup, sem
mostarda e sem picles.
orderBurgers(2, mostarda=Falso)
ou:
O chamador deve especificar um sabor, mas por padrão receberá uma colher em um
cone sem granulado. O chamador pode substituir esses padrões com valores de palavra-
chave diferentes.
146 Capítulo 7
o chamador pode opcionalmente passar em uma única cobertura desejada. Se o chamador
quiser uma cobertura, devemos cobrar extra.
Na Listagem 7-1, usaremos um parâmetro posicional para oTamanhoe
parâmetros de palavras-chave para oestiloecobertura.O padrão paraestiloé a corda
'regular'.Como a escolha de cobertura é opcional, usaremos o valor especial do
Python deNenhumcomo o padrão, mas o chamador pode passar a cobertura de sua
escolha.
Arquivo: OrderPizzaWithNone.py
se tamanho == 'pequeno':
preço = 10,00
elif tamanho == 'médio':
preço = 14,00
senão:# ampla
preço = 18,00
if estilo == 'deepdish':
preço = preço + 2,00# cobra extra pelo deepdish
line = 'Você pediu uma ' + tamanho + ' ' + estilo + ' pizza com ' 1se a
cobertura for Nenhum:# verifica se nenhuma cobertura foi passada
print(linha + 'sem cobertura')
else:
print(linha + cobertura)
preço = preço + PRICE_OF_TOPPING
O uso de valores padrão torna a chamada de funções e métodos mais simples, mas há uma
desvantagem. Sua escolha de cada palavra-chave para parâmetros de palavras-chave é muito
importante. Depois que os programadores começam a fazer chamadas que substituem os
valores padrão, é muito difícil alterar o nome de um parâmetro de palavra-chave porque esse
nome deve ser alterado emtudochamadas para a função ou método em lockstep. Caso
contrário, o código que estava funcionando irá quebrar. Para código mais amplamente
distribuído, isso pode causar muita dor aos programadores que usam seu código. Resumindo,
não altere o nome de um parâmetro de palavra-chave a menos que seja absolutamente
necessário. Então, escolha sabiamente!
Também é muito importante usar valores padrão que devem atender a maior variedade
possível de usuários. (Em uma nota pessoal, euodiarmostarda! Sempre que vou ao Burger
King, tenho que me lembrar de não especificar mostarda ou compro o que considero um
hambúrguer intragável. Eu acho que eles fizeram uma má escolha padrão.)
Na próxima seção, apresentarei uma coleção de classes que você pode usar para criar
facilmente elementos GUI, como botões e campos de texto dentro do pygame. Essas classes
serão inicializadas usando alguns parâmetros posicionais, mas também terão vários
parâmetros de palavras-chave opcionais, todos com padrões razoáveis para permitir que os
programadores criem widgets GUI especificando apenas alguns argumentos posicionais. Um
controle mais preciso pode ser obtido especificando valores para substituir os valores padrão
dos parâmetros de palavras-chave.
Para um exemplo aprofundado, veremos um widget para exibir texto na janela do
aplicativo. O texto pode ser mostrado em uma variedade de fontes, tamanhos de fonte,
cores, cores de fundo e assim por diante. Nós vamos construir umTexto de exibiçãoclasse que
terá valores padrão para todos esses atributos, mas dará ao código do cliente a opção de
especificar valores diferentes.
O pacote pygwidgets
O restante deste capítulo se concentrará napygwidgets (pronunciado “pig
wijits”), que foi escrito com dois objetivos em mente:
Botão personalizado
148 Capítulo 7
TextCheckBox
Caixa de seleção com arte padrão, construída a partir de uma string de texto
TextRadioButton
Botões de rádio com arte padrão, construídos a partir de uma string de texto
Texto de exibição
Entrada de texto
Arrasta
Imagem
Coleção de imagens
Animação
SpriteSheetAnimation
Configurando
importar widgets
Conforme mostrado no Capítulo 5, uma das primeiras coisas que você faz em todo
programa pygame é definir a janela do aplicativo. A linha a seguir cria uma janela
do aplicativo e salva uma referência a ela em uma variável chamadajanela:
O passo 1 para usar qualquer widget é instanciar um com uma linha como esta:
enquanto Verdadeiro:
if oWidget.handleEvent(evento):
# O usuário fez algo no oWidget que devemos responder
# Adicione o código aqui
O passo 3 é adicionar uma linha perto da parte inferior doenquantoloop para chamar o
empate()método do widget, para fazê-lo aparecer na janela:
oWidget.draw()
150 Capítulo 7
Como especificamos a janela para desenhar, o local e todos os detalhes
que afetam a aparência do widget na etapa 1, não passamos nada na
chamada paraempate().
Nosso primeiro exemplo será o widget mais simples: usaremos oImagemclasse para
exibir uma imagem em uma janela. Ao instanciar umImagemobjeto, os únicos
argumentos necessários são a janela, o local na janela para desenhar a imagem e o
caminho para o arquivo de imagem. Crie oImagemobject antes do loop principal
começar, assim:
O caminho usado aqui assume que a pasta do projeto que contém o programa
principal também contém uma pasta chamadaimagens, dentro do qual está o
SomeImage.pngArquivo. Então, no loop principal você só precisa chamar o objeto
empate()método:
oImage.draw()
oImage.setLoc((novoX, novoY))
Na próxima vez que a imagem for desenhada, ela aparecerá nas novas coordenadas. A
documentação lista muitos métodos adicionais que você pode chamar para inverter, girar,
dimensionar, obter a localização e o retângulo da imagem e assim por diante.
O MÓDULO SPRITE
O Pygame possui um módulo embutido para mostrar imagens em uma janela, chamado de
módulo sprite . Tais imagens são chamadasduendes . O módulo sprite fornece umSprite
classe para lidar com sprites individuais e umGrupoclasse para lidar com vários
Spriteobjetos. Juntas, essas classes fornecem excelente funcionalidade e, se você
pretende fazer programação pygame pesada, provavelmente vale a pena dar uma
olhada nelas. No entanto, para explicar os conceitos de POO subjacentes, optei por não
usar essas classes. Em vez disso, continuarei com os elementos gerais da GUI para que
possam ser usados em qualquer ambiente e linguagem Se você quiser aprender mais
sobre o módulo sprite, veja o tutorial emhttps://www.pygame.org/docs/tut/
SpriteIntro.html
Quando você instancia um botão, caixa de seleção ou widget de botão de opção empygwidgets, você
tem duas opções: instanciar uma versão de texto que desenha sua própria arte e adiciona um rótulo
de texto com base em uma string que você passa ou instanciar uma versão personalizada onde você
fornece a arte. A Tabela 7-1 mostra as diferentes classes de botões disponíveis.
Botões de texto
152 Capítulo 7
Quando você cria uma instância de umTextButton,você só precisa passar na
janela, o local na janela e o texto a ser mostrado no botão. Se você especificar
apenas esses parâmetros posicionais, seu botão usará padrões razoáveis para
largura e altura, as cores de fundo para os quatro estados do botão (diferentes
tons de cinza), a fonte e o tamanho da fonte. Por padrão, nenhum efeito sonoro
será reproduzido quando o usuário clicar no botão.
O código para criar umTextButtonusando todos os padrões fica assim:
Você pode substituir qualquer um ou todos os parâmetros padrão com valores de palavra-
chave como:
Botões personalizados
oBotão personalizadoclass permite que você use sua própria arte para um botão. Para
instanciar umbotão personalizado,você só precisa passar uma janela, um local e um
caminho para a imagem do estado ativo do botão. Aqui está um exemplo:
Aqui também especificamos um efeito sonoro que deve ser reproduzido quando o
usuário clica no botão e fornecemos um apelido interno que podemos usar mais tarde.
Usando botões
Após a instanciação, aqui está um código típico para usar um objeto de botão,oBotão,
independente de ser umTextButtonou umBotão personalizado:
enquanto Verdadeiro:
if oButton.handleEvent(evento):
# O usuário clicou neste botão
<Qualquer código que você queira executar aqui quando o botão for clicado>
- - - recorte ---
oButton.draw()# na parte inferior do loop while, diga para desenhar
Saída de texto
154 Capítulo 7
instanciar umTexto de exibiçãocampo, os únicos argumentos necessários são a janela e o local.
O primeiro parâmetro de palavra-chave évalor,que pode ser especificado com uma string
como texto inicial a ser mostrado no campo. Isso normalmente é usado para um valor de
usuário final padrão ou para texto que nunca muda, como um rótulo ou instruções. Desde
valoré o primeiro parâmetro de palavra-chave, ele pode ser fornecido como um argumento
posicional ou de palavra-chave. Por exemplo, isso:
oTextField.draw()
E, claro, você pode criar quantosTexto de exibiçãoobjetos como desejar, cada um exibindo
um texto diferente e cada um com sua própria fonte, tamanho, cor e assim por diante.
Entrada de texto
enquanto Verdadeiro:
if oInputField.handleEvent(evento):
# O usuário pressionou Enter ou Return
userText = oInputField.getValue() # pega o texto que o usuário digitou
<Qualquer código que você deseja executar usando a entrada do usuário>
- - - recorte ---
oInputField.draw()# na parte inferior do loop while principal
156 Capítulo 7
NOTA No momento da redação, oEntrada de textoclass não lida com o realce de vários caracteres arrastando o
mouse. Se essa funcionalidade for adicionada em uma versão posterior, nenhuma alteração será
necessária nos programas que usamEntrada de textoporque o código estará inteiramente dentro dessa
classe. Qualquer novo comportamento será suportado automaticamente em todos
Entrada de textoobjetos.
Você pode então criar umColeção de imagensobjeto, especificando este dicionário e a chave
da imagem com a qual você deseja começar. Para mudar para uma imagem diferente, você
chama osubstituir()método e passar uma chave diferente. Chamando o
empate()O método na parte inferior do loop sempre mostra a imagem atual.
oArrastaclass exibe uma única imagem, mas permite que o usuário arraste a
imagem para qualquer lugar na janela. Você deve chamar seuhandleEvent()método no
loop de eventos. Quando o usuário terminar de arrastar,handleEvent()retornaVerdadeiro,
e você pode ligar para oArrastado objetogetMouseUpLoc()método para obter o local onde
o usuário soltou o botão do mouse.
oAnimaçãoeSpriteSheetAnimationclasses lidam com a construção e exibição de
uma animação. Ambos requerem um conjunto de imagens para iterar. oAnimação
classe obtém as imagens de arquivos individuais, enquanto a classe
SpriteSheetAnimationclasse requer uma única imagem com imagens internas uniformemente
espaçadas. Exploraremos essas classes mais detalhadamente no Capítulo 14.
A Figura 7-4 mostra uma captura de tela de um programa de exemplo que demonstra
objetos instanciados de muitas das classes empygwidgets,IncluindoImagem,
DisplayText, InputText, TextButton, CustomButton, TextRadioButton, CustomRadioButton,
TextCheckBox, CustomCheckBox, ImageCollection,eArrastador.
A fonte deste programa de exemplo pode ser encontrada nopygwidgets_test pasta
no meu repositório GitHub,https://github.com/IrvKalb/pygwidgets/.
Resumo
Este capítulo forneceu uma introdução à orientação a objetospygwidgets
pacote de widgets de interface gráfica do usuário. Começamos discutindo valores padrão para
parâmetros em métodos e expliquei que um parâmetro de palavra-chave permite que um
valor padrão seja usado se nenhum valor de argumento correspondente for especificado em
uma chamada.
158 Capítulo 7
Em seguida, apresentei-lhe opygwidgetsmódulo, que contém várias classes
de widgets GUI pré-construídas e mostrou como usar várias delas. Por fim,
mostrei um programa de amostra que fornece exemplos da maioria desses
widgets.
Há duas vantagens principais em escrever aulas como as depygwidgets. Primeiro, as
classes podem ocultar a complexidade dos métodos. Depois de ter sua aula funcionando
corretamente, você nunca mais precisará se preocupar com os detalhes internos. Segundo,
você pode reutilizar o código criando quantas instâncias de uma classe forem necessárias.
Suas classes podem fornecer funcionalidade básica incluindo parâmetros de palavras-chave
com valores padrão bem escolhidos. No entanto, os valores padrão podem ser facilmente
substituídos para permitir a personalização.
Você pode publicar as interfaces de suas classes para que outros
programadores (e você mesmo) aproveitem em diferentes projetos. Uma boa
documentação e consistência ajudam bastante a tornar esses tipos de classes
altamente utilizáveis.