Cours 3 Interface graphique

Quentin Lurkin

Rappel: programmation graphique

Pygame CE

Installation

&> python -m pip install pygame_ce

Premier programme graphique

import pygame
import sys

# initialisation de pygame
pygame.init()

# demande une fenêtre de 800x600 pixels
screen = pygame.display.set_mode((800, 600))

# la clock sert à limiter le nombre d'images par seconde
clock = pygame.time.Clock()

# boucle infinie
while True:
  # attends qu'il se soit passé au moins 1/60
  # de seconde depuis le dernier tick
  clock.tick(60)
  # Boucler sur tous les events
  for event in pygame.event.get():
    # Quitter si l'event est un QUIT
    if event.type == pygame.QUIT:
      sys.exit()

  # dessine un cercle rouge centré en (100, 300) de rayon 20
  pygame.draw.circle(screen, (255, 0, 0), (100, 300), 20)

  # affiche l'image qui vient d'être dessinée
  pygame.display.flip()

Avec une classe

import pygame
import sys


class App:
  def __init__(self):
    pygame.init()
    self.screen = pygame.display.set_mode((800, 600))

  def draw(self):
    pygame.draw.circle(self.screen, (255, 0, 0), (100, 300), 20)

  def run(self):
    clock = pygame.time.Clock()
    while True:
      clock.tick(60)
      for event in pygame.event.get():
        if event.type == pygame.QUIT:
          sys.exit()

      self.draw()
      pygame.display.flip()


App().run()

Animation

import pygame
import sys
from dataclasses import dataclass


@dataclass
class Ball:
  x: int
  y: int
  vx: int
  vy: int

  def move(self, dx: int, dy: int):
    self.x += dx
    self.y += dy

  def update(self):
    self.move(self.vx, self.vy)

  def draw(self, screen: pygame.Surface):
    pygame.draw.circle(screen, (255, 0, 0), (self.x, self.y), 20)


class App:
  def __init__(self):
    pygame.init()
    self.screen = pygame.display.set_mode((800, 600))
    self.ball = Ball(100, 300, 1, 0)

  def draw(self):
    self.ball.draw(self.screen)

  def update(self):
    self.ball.update()

  def run(self):
    clock = pygame.time.Clock()
    while True:
      clock.tick(60)
      for event in pygame.event.get():
        if event.type == pygame.QUIT:
          sys.exit()

      self.update()

      # dessine un rectangle noir pour effacer l'image
      pygame.draw.rect(self.screen, (0, 0, 0), pygame.Rect(0, 0, 800, 600))
      self.draw()

      pygame.display.flip()


App().run()

Événements

# Boucler sur tous les events
for event in pygame.event.get():
  # Faire quelque chose avec l'event

Clavier

Clavier

import pygame
import sys
from dataclasses import dataclass


@dataclass
class Ball:
  x: int
  y: int
  vx: int
  vy: int

  def move(self, dx: int, dy: int):
    self.x += dx
    self.y += dy

  def update(self):
    self.move(self.vx, self.vy)

  def draw(self, screen: pygame.Surface):
    pygame.draw.circle(screen, (255, 0, 0), (self.x, self.y), 20)


class App:
  def __init__(self):
    pygame.init()
    self.screen = pygame.display.set_mode((800, 600))
    self.ball = Ball(100, 300, 1, 0)

  def draw(self):
    self.ball.draw(self.screen)

  def update(self):
    self.ball.update()

  def process_events(self, event: pygame.event.Event):
    if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
      self.ball.vx = -self.ball.vx

  def run(self):
    clock = pygame.time.Clock()
    while True:
      clock.tick(60)
      for event in pygame.event.get():
        if event.type == pygame.QUIT:
          sys.exit()
        self.process_events(event)

      self.update()

      pygame.draw.rect(self.screen, (0, 0, 0), pygame.Rect(0, 0, 800, 600))
      self.draw()

      pygame.display.flip()


App().run()

Touche de modification

if event.mod & pygame.KMOD_SHIFT:
  # SHIFT était appuyé lors de cet event
if event.mod & (pygame.KMOD_SHIFT | pygame.KMOD_ALT):
  # SHIFT et ALT étaient appuyés lors de cet event

Souris

Souris

import pygame
import sys
from dataclasses import dataclass


@dataclass
class Ball:
  x: int
  y: int
  vx: int
  vy: int

  def move(self, dx: int, dy: int):
    self.x += dx
    self.y += dy

  def update(self):
    self.move(self.vx, self.vy)

  def draw(self, screen: pygame.Surface):
    pygame.draw.circle(screen, (255, 0, 0), (self.x, self.y), 20)


class App:
  def __init__(self):
    pygame.init()
    self.screen = pygame.display.set_mode((800, 600))
    self.ball = Ball(100, 300, 1, 0)

  def draw(self):
    self.ball.draw(self.screen)

  def update(self):
    self.ball.update()

  def process_events(self, event: pygame.event.Event):
    if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
        self.ball.vx = -self.ball.vx
    if event.type == pygame.MOUSEBUTTONDOWN:
        self.ball.x = event.pos[0]
        self.ball.y = event.pos[1]

  def run(self):
    clock = pygame.time.Clock()
    while True:
      clock.tick(60)
      for event in pygame.event.get():
        if event.type == pygame.QUIT:
          sys.exit()
        self.process_events(event)

      self.update()

      pygame.draw.rect(self.screen, (0, 0, 0), pygame.Rect(0, 0, 800, 600))
      self.draw()

      pygame.display.flip()


App().run()

Widget

Installation

&> python -m pip install pygame_gui

Pygame_gui

import pygame
import pygame_gui
import sys
from dataclasses import dataclass


@dataclass
class Ball:
  x: int
  y: int
  vx: int
  vy: int

  def move(self, dx: int, dy: int):
    self.x += dx
    self.y += dy

  def update(self):
    self.move(self.vx, self.vy)

  def draw(self, screen: pygame.Surface):
    pygame.draw.circle(screen, (255, 0, 0), (self.x, self.y), 20)


class App:
  def __init__(self):
    pygame.init()
    self.size = (800, 600)
    self.screen = pygame.display.set_mode(self.size)
    self.manager = pygame_gui.UIManager(self.size)
    self.ball = Ball(100, 300, 1, 0)

  def draw(self):
    self.ball.draw(self.screen)

  def update(self):
    self.ball.update()

  def process_events(self, event: pygame.event.Event):
    if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
      self.ball.vx = -self.ball.vx
    if event.type == pygame.MOUSEBUTTONDOWN:
      self.ball.x = event.pos[0]
      self.ball.y = event.pos[1]

  def run(self):
    clock = pygame.time.Clock()
    while True:
      # tick renvoie le temps écoulé depuis le dernier tick (en ms)
      time_delta = clock.tick(60)
      for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()

        # Manager.process_event() permet à pygame_gui de recevoir les
        # events. Cette méthode renvoi True si elle a utilisé l'événement
        if not self.manager.process_events(event):
          # Si le manager n'a pas utiliser l'événement, alors on le traite
          # nous même
          self.process_events(event)

      self.update()

      # Permet à pygame_gui de mettre à jour les animations (en s)
      self.manager.update(time_delta/1000)

      pygame.draw.rect(self.screen, (0, 0, 0), pygame.Rect((0, 0), self.size))
      self.draw()

      # Affiche les widgets
      self.manager.draw_ui(self.screen)

      pygame.display.flip()


App().run()

Ajout d'un widget

import pygame
import pygame_gui
from pygame_gui.elements import UIButton
import sys
from dataclasses import dataclass


@dataclass
class Ball:
  x: int
  y: int
  vx: int
  vy: int

  def move(self, dx: int, dy: int):
    self.x += dx
    self.y += dy

  def update(self):
    self.move(self.vx, self.vy)

  def draw(self, screen: pygame.Surface):
    pygame.draw.circle(screen, (255, 0, 0), (self.x, self.y), 20)


class App:
  def __init__(self):
    pygame.init()
    self.size = (800, 600)
    self.screen = pygame.display.set_mode(self.size)
    self.manager = pygame_gui.UIManager(self.size)
    self.ball = Ball(100, 300, 1, 0)

    self.hello_button = UIButton(
      relative_rect=pygame.Rect(350, 275, 150, 50),
      text='Hello World',
      manager=self.manager
    )

  def draw(self):
    self.ball.draw(self.screen)

  def update(self):
    self.ball.update()

  def process_events(self, event: pygame.event.Event):
    if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
      self.ball.vx = -self.ball.vx
    if event.type == pygame.MOUSEBUTTONDOWN:
      self.ball.x = event.pos[0]
      self.ball.y = event.pos[1]

  def run(self):
    clock = pygame.time.Clock()
    while True:
      time_delta = clock.tick(60)
      for event in pygame.event.get():
        if event.type == pygame.QUIT:
          sys.exit()
        if not self.manager.process_events(event):
          self.process_events(event)

      self.update()
      self.manager.update(time_delta/1000)

      pygame.draw.rect(self.screen, (0, 0, 0), pygame.Rect((0, 0), self.size))
      self.draw()
      self.manager.draw_ui(self.screen)

      pygame.display.flip()


App().run()

Pygame_gui events

Pygame_gui events

import pygame
import pygame_gui
from pygame_gui.elements import UIButton
import sys
from dataclasses import dataclass


@dataclass
class Ball:
  x: int
  y: int
  vx: int
  vy: int

  def move(self, dx: int, dy: int):
    self.x += dx
    self.y += dy

  def update(self):
    self.move(self.vx, self.vy)

  def draw(self, screen: pygame.Surface):
    pygame.draw.circle(screen, (255, 0, 0), (self.x, self.y), 20)


class App:
  def __init__(self):
    pygame.init()
    self.size = (800, 600)
    self.screen = pygame.display.set_mode(self.size)
    self.manager = pygame_gui.UIManager(self.size)
    self.ball = Ball(100, 300, 1, 0)

    self.hello_button = UIButton(
      relative_rect=pygame.Rect(350, 275, 150, 50),
      text='Hello World',
      manager=self.manager
    )

  def draw(self):
    self.ball.draw(self.screen)

  def update(self):
    self.ball.update()

  def process_events(self, event: pygame.event.Event):
    if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
      self.ball.vx = -self.ball.vx
    if event.type == pygame.MOUSEBUTTONDOWN:
      self.ball.x = event.pos[0]
      self.ball.y = event.pos[1]
    if event.type == pygame_gui.UI_BUTTON_PRESSED:
      if event.ui_element is self.hello_button:
        # affiche 'Hello World!' dans le terminal
        print('Hello World!')

  def run(self):
      clock = pygame.time.Clock()
      while True:
        time_delta = clock.tick(60)
        for event in pygame.event.get():
          if event.type == pygame.QUIT:
            sys.exit()
          if not self.manager.process_events(event):
            self.process_events(event)

        self.update()
        self.manager.update(time_delta/1000)

        pygame.draw.rect(self.screen, (0, 0, 0), pygame.Rect((0, 0), self.size))
        self.draw()
        self.manager.draw_ui(self.screen)

        pygame.display.flip()


App().run()

Interaction entre Widget

import pygame
import pygame_gui
from pygame_gui.elements import UIButton, UITextEntryLine, UILabel
import sys
from dataclasses import dataclass


@dataclass
class Ball:
  x: int
  y: int
  vx: int
  vy: int

  def move(self, dx: int, dy: int):
    self.x += dx
    self.y += dy

  def update(self):
    self.move(self.vx, self.vy)

  def draw(self, screen: pygame.Surface):
    pygame.draw.circle(screen, (255, 0, 0), (self.x, self.y), 20)


class App:
  def __init__(self):
    pygame.init()
    self.size = (800, 600)
    self.screen = pygame.display.set_mode(self.size)
    self.manager = pygame_gui.UIManager(self.size)
    self.ball = Ball(100, 300, 1, 0)

    self.hello_button = UIButton(
      relative_rect=pygame.Rect(350, 275, 150, 50),
      text='Hello',
      manager=self.manager
    )

    self.input = UITextEntryLine(
      relative_rect=pygame.Rect(350, 220, 150, 50),
      manager=self.manager
    )

    self.display = UILabel(
      relative_rect=pygame.Rect(350, 330, 150, 50),
      text='',
      manager=self.manager
    )

  def draw(self):
    self.ball.draw(self.screen)

  def update(self):
    self.ball.update()

  def process_events(self, event: pygame.event.Event):
    if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
      self.ball.vx = -self.ball.vx
    if event.type == pygame.MOUSEBUTTONDOWN:
      self.ball.x = event.pos[0]
      self.ball.y = event.pos[1]
    if event.type == pygame_gui.UI_BUTTON_PRESSED:
      if event.ui_element is self.hello_button:
        name = self.input.text
        self.display.set_text(f'Hello {name}')

  def run(self):
    clock = pygame.time.Clock()
    while True:
      time_delta = clock.tick(60)
      for event in pygame.event.get():
        if event.type == pygame.QUIT:
          sys.exit()
        if not self.manager.process_events(event):
          self.process_events(event)

      self.update()
      self.manager.update(time_delta/1000)

      pygame.draw.rect(self.screen, (0, 0, 0), pygame.Rect((0, 0), self.size))
      self.draw()
      self.manager.draw_ui(self.screen)

      pygame.display.flip()


App().run()

Documentation

Pygame CE

https://pyga.me/docs/

Pygame_gui

pygame-gui.readthedocs.io/en/latest/