Projets : structure par projet (jeu_de_la_vie, labyrinthe, mastermind, wator)
This commit is contained in:
329
Projets/pyxel_text.py
Normal file
329
Projets/pyxel_text.py
Normal file
@@ -0,0 +1,329 @@
|
||||
"""
|
||||
Module de gestion de texte pour Pyxel
|
||||
Permet d'afficher du texte et de capturer la saisie utilisateur
|
||||
"""
|
||||
|
||||
import pyxel
|
||||
|
||||
# Mapping des touches Pyxel vers les caractères
|
||||
KEY_MAP = {
|
||||
pyxel.KEY_A: ('a', 'A'), pyxel.KEY_B: ('b', 'B'), pyxel.KEY_C: ('c', 'C'),
|
||||
pyxel.KEY_D: ('d', 'D'), pyxel.KEY_E: ('e', 'E'), pyxel.KEY_F: ('f', 'F'),
|
||||
pyxel.KEY_G: ('g', 'G'), pyxel.KEY_H: ('h', 'H'), pyxel.KEY_I: ('i', 'I'),
|
||||
pyxel.KEY_J: ('j', 'J'), pyxel.KEY_K: ('k', 'K'), pyxel.KEY_L: ('l', 'L'),
|
||||
pyxel.KEY_M: ('m', 'M'), pyxel.KEY_N: ('n', 'N'), pyxel.KEY_O: ('o', 'O'),
|
||||
pyxel.KEY_P: ('p', 'P'), pyxel.KEY_Q: ('q', 'Q'), pyxel.KEY_R: ('r', 'R'),
|
||||
pyxel.KEY_S: ('s', 'S'), pyxel.KEY_T: ('t', 'T'), pyxel.KEY_U: ('u', 'U'),
|
||||
pyxel.KEY_V: ('v', 'V'), pyxel.KEY_W: ('w', 'W'), pyxel.KEY_X: ('x', 'X'),
|
||||
pyxel.KEY_Y: ('y', 'Y'), pyxel.KEY_Z: ('z', 'Z'),
|
||||
pyxel.KEY_0: ('0', ')'), pyxel.KEY_1: ('1', '!'), pyxel.KEY_2: ('2', '@'),
|
||||
pyxel.KEY_3: ('3', '#'), pyxel.KEY_4: ('4', '$'), pyxel.KEY_5: ('5', '%'),
|
||||
pyxel.KEY_6: ('6', '^'), pyxel.KEY_7: ('7', '&'), pyxel.KEY_8: ('8', '*'),
|
||||
pyxel.KEY_9: ('9', '('),
|
||||
pyxel.KEY_SPACE: (' ', ' '),
|
||||
pyxel.KEY_MINUS: ('-', '_'),
|
||||
pyxel.KEY_PERIOD: ('.', '>'),
|
||||
pyxel.KEY_COMMA: (',', '<'),
|
||||
pyxel.KEY_SLASH: ('/', '?'),
|
||||
pyxel.KEY_SEMICOLON: (';', ':'),
|
||||
pyxel.KEY_APOSTROPHE: ("'", '"'),
|
||||
}
|
||||
|
||||
|
||||
class TextInput:
|
||||
"""
|
||||
Champ de saisie de texte pour Pyxel
|
||||
|
||||
Exemple d'utilisation:
|
||||
input_field = TextInput(x=10, y=50, max_length=20)
|
||||
|
||||
def update():
|
||||
input_field.update()
|
||||
if input_field.submitted:
|
||||
print(f"Texte saisi: {input_field.text}")
|
||||
input_field.reset()
|
||||
|
||||
def draw():
|
||||
input_field.draw()
|
||||
"""
|
||||
|
||||
def __init__(self, x=0, y=0, max_length=32, width=None, color=7, bg_color=0, cursor_color=8):
|
||||
"""
|
||||
Initialise un champ de saisie
|
||||
|
||||
Args:
|
||||
x, y: Position du champ
|
||||
max_length: Nombre maximum de caractères
|
||||
width: Largeur du champ en pixels (auto si None)
|
||||
color: Couleur du texte (0-15)
|
||||
bg_color: Couleur de fond (0-15, None pour transparent)
|
||||
cursor_color: Couleur du curseur (0-15)
|
||||
"""
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.max_length = max_length
|
||||
self.width = width if width else (max_length * 4 + 4)
|
||||
self.color = color
|
||||
self.bg_color = bg_color
|
||||
self.cursor_color = cursor_color
|
||||
|
||||
self.text = ""
|
||||
self.cursor_pos = 0
|
||||
self.active = True
|
||||
self.submitted = False
|
||||
self._cursor_visible = True
|
||||
self._cursor_timer = 0
|
||||
|
||||
def update(self):
|
||||
"""Met à jour le champ de saisie (à appeler dans update())"""
|
||||
if not self.active:
|
||||
return
|
||||
|
||||
self.submitted = False
|
||||
|
||||
# Clignotement du curseur
|
||||
self._cursor_timer += 1
|
||||
if self._cursor_timer >= 15:
|
||||
self._cursor_timer = 0
|
||||
self._cursor_visible = not self._cursor_visible
|
||||
|
||||
# Validation avec Entrée
|
||||
if pyxel.btnp(pyxel.KEY_RETURN):
|
||||
self.submitted = True
|
||||
return
|
||||
|
||||
# Suppression avec Backspace
|
||||
if pyxel.btnp(pyxel.KEY_BACKSPACE, hold=10, repeat=3):
|
||||
if self.cursor_pos > 0:
|
||||
self.text = self.text[:self.cursor_pos-1] + self.text[self.cursor_pos:]
|
||||
self.cursor_pos -= 1
|
||||
|
||||
# Suppression avec Delete
|
||||
if pyxel.btnp(pyxel.KEY_DELETE, hold=10, repeat=3):
|
||||
if self.cursor_pos < len(self.text):
|
||||
self.text = self.text[:self.cursor_pos] + self.text[self.cursor_pos+1:]
|
||||
|
||||
# Navigation avec flèches
|
||||
if pyxel.btnp(pyxel.KEY_LEFT, hold=10, repeat=3):
|
||||
self.cursor_pos = max(0, self.cursor_pos - 1)
|
||||
if pyxel.btnp(pyxel.KEY_RIGHT, hold=10, repeat=3):
|
||||
self.cursor_pos = min(len(self.text), self.cursor_pos + 1)
|
||||
if pyxel.btnp(pyxel.KEY_HOME):
|
||||
self.cursor_pos = 0
|
||||
if pyxel.btnp(pyxel.KEY_END):
|
||||
self.cursor_pos = len(self.text)
|
||||
|
||||
# Saisie de caractères
|
||||
if len(self.text) < self.max_length:
|
||||
shift = pyxel.btn(pyxel.KEY_LSHIFT) or pyxel.btn(pyxel.KEY_RSHIFT)
|
||||
for key, chars in KEY_MAP.items():
|
||||
if pyxel.btnp(key, hold=15, repeat=3):
|
||||
char = chars[1] if shift else chars[0]
|
||||
self.text = self.text[:self.cursor_pos] + char + self.text[self.cursor_pos:]
|
||||
self.cursor_pos += 1
|
||||
break
|
||||
|
||||
def draw(self):
|
||||
"""Dessine le champ de saisie (à appeler dans draw())"""
|
||||
# Fond
|
||||
if self.bg_color is not None:
|
||||
pyxel.rect(self.x, self.y, self.width, 9, self.bg_color)
|
||||
|
||||
# Bordure si actif
|
||||
if self.active:
|
||||
pyxel.rectb(self.x - 1, self.y - 1, self.width + 2, 11, self.color)
|
||||
|
||||
# Texte
|
||||
pyxel.text(self.x + 2, self.y + 1, self.text, self.color)
|
||||
|
||||
# Curseur
|
||||
if self.active and self._cursor_visible:
|
||||
cursor_x = self.x + 2 + self.cursor_pos * 4
|
||||
pyxel.rect(cursor_x, self.y + 1, 1, 7, self.cursor_color)
|
||||
|
||||
def reset(self):
|
||||
"""Réinitialise le champ"""
|
||||
self.text = ""
|
||||
self.cursor_pos = 0
|
||||
self.submitted = False
|
||||
|
||||
def set_text(self, text):
|
||||
"""Définit le texte du champ"""
|
||||
self.text = text[:self.max_length]
|
||||
self.cursor_pos = len(self.text)
|
||||
|
||||
|
||||
class TextBox:
|
||||
"""
|
||||
Zone de texte multiligne avec retour à la ligne automatique
|
||||
|
||||
Exemple d'utilisation:
|
||||
textbox = TextBox(x=10, y=10, width=100, color=7)
|
||||
textbox.set_text("Ceci est un long texte qui sera automatiquement découpé en plusieurs lignes.")
|
||||
|
||||
def draw():
|
||||
textbox.draw()
|
||||
"""
|
||||
|
||||
def __init__(self, x=0, y=0, width=100, color=7, line_spacing=8):
|
||||
"""
|
||||
Initialise une zone de texte
|
||||
|
||||
Args:
|
||||
x, y: Position de la zone
|
||||
width: Largeur en pixels
|
||||
color: Couleur du texte (0-15)
|
||||
line_spacing: Espacement entre les lignes
|
||||
"""
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.width = width
|
||||
self.color = color
|
||||
self.line_spacing = line_spacing
|
||||
self.lines = []
|
||||
|
||||
def set_text(self, text):
|
||||
"""Définit le texte avec retour à la ligne automatique"""
|
||||
self.lines = []
|
||||
chars_per_line = self.width // 4
|
||||
|
||||
paragraphs = text.split('\n')
|
||||
for paragraph in paragraphs:
|
||||
words = paragraph.split(' ')
|
||||
current_line = ""
|
||||
|
||||
for word in words:
|
||||
test_line = current_line + (" " if current_line else "") + word
|
||||
if len(test_line) <= chars_per_line:
|
||||
current_line = test_line
|
||||
else:
|
||||
if current_line:
|
||||
self.lines.append(current_line)
|
||||
# Mot trop long, on le coupe
|
||||
while len(word) > chars_per_line:
|
||||
self.lines.append(word[:chars_per_line])
|
||||
word = word[chars_per_line:]
|
||||
current_line = word
|
||||
|
||||
if current_line:
|
||||
self.lines.append(current_line)
|
||||
|
||||
def draw(self):
|
||||
"""Dessine la zone de texte"""
|
||||
for i, line in enumerate(self.lines):
|
||||
pyxel.text(self.x, self.y + i * self.line_spacing, line, self.color)
|
||||
|
||||
def get_height(self):
|
||||
"""Retourne la hauteur totale du texte"""
|
||||
return len(self.lines) * self.line_spacing
|
||||
|
||||
|
||||
class TypeWriter:
|
||||
"""
|
||||
Effet machine à écrire pour afficher du texte progressivement
|
||||
|
||||
Exemple d'utilisation:
|
||||
typewriter = TypeWriter(x=10, y=10, speed=3)
|
||||
typewriter.set_text("Bienvenue dans le jeu...")
|
||||
|
||||
def update():
|
||||
typewriter.update()
|
||||
|
||||
def draw():
|
||||
typewriter.draw()
|
||||
"""
|
||||
|
||||
def __init__(self, x=0, y=0, width=100, color=7, speed=2, line_spacing=8):
|
||||
"""
|
||||
Args:
|
||||
x, y: Position
|
||||
width: Largeur en pixels
|
||||
color: Couleur du texte
|
||||
speed: Frames entre chaque caractère (plus bas = plus rapide)
|
||||
line_spacing: Espacement entre les lignes
|
||||
"""
|
||||
self.textbox = TextBox(x, y, width, color, line_spacing)
|
||||
self.speed = speed
|
||||
self.full_text = ""
|
||||
self.displayed_chars = 0
|
||||
self.timer = 0
|
||||
self.finished = False
|
||||
|
||||
def set_text(self, text):
|
||||
"""Définit le texte à afficher progressivement"""
|
||||
self.full_text = text
|
||||
self.displayed_chars = 0
|
||||
self.timer = 0
|
||||
self.finished = False
|
||||
|
||||
def update(self):
|
||||
"""Met à jour l'animation"""
|
||||
if self.finished:
|
||||
return
|
||||
|
||||
self.timer += 1
|
||||
if self.timer >= self.speed:
|
||||
self.timer = 0
|
||||
self.displayed_chars += 1
|
||||
if self.displayed_chars >= len(self.full_text):
|
||||
self.displayed_chars = len(self.full_text)
|
||||
self.finished = True
|
||||
|
||||
self.textbox.set_text(self.full_text[:self.displayed_chars])
|
||||
|
||||
def draw(self):
|
||||
"""Dessine le texte"""
|
||||
self.textbox.draw()
|
||||
|
||||
def skip(self):
|
||||
"""Affiche tout le texte immédiatement"""
|
||||
self.displayed_chars = len(self.full_text)
|
||||
self.finished = True
|
||||
self.textbox.set_text(self.full_text)
|
||||
|
||||
def is_finished(self):
|
||||
"""Retourne True si tout le texte est affiché"""
|
||||
return self.finished
|
||||
|
||||
|
||||
# --- Exemple d'utilisation ---
|
||||
if __name__ == "__main__":
|
||||
|
||||
class Demo:
|
||||
def __init__(self):
|
||||
pyxel.init(200, 150, title="Demo TextInput")
|
||||
|
||||
self.input_field = TextInput(x=10, y=60, max_length=25, color=7)
|
||||
self.typewriter = TypeWriter(x=10, y=10, width=180, speed=2)
|
||||
self.typewriter.set_text("Bienvenue ! Entrez votre nom ci-dessous et appuyez sur Entree.")
|
||||
|
||||
self.messages = []
|
||||
|
||||
pyxel.run(self.update, self.draw)
|
||||
|
||||
def update(self):
|
||||
self.typewriter.update()
|
||||
self.input_field.update()
|
||||
|
||||
# Skip le typewriter avec espace
|
||||
if pyxel.btnp(pyxel.KEY_SPACE) and not self.input_field.active:
|
||||
self.typewriter.skip()
|
||||
|
||||
# Validation de la saisie
|
||||
if self.input_field.submitted and self.input_field.text:
|
||||
self.messages.append(f"Bonjour, {self.input_field.text} !")
|
||||
self.input_field.reset()
|
||||
|
||||
def draw(self):
|
||||
pyxel.cls(0)
|
||||
|
||||
self.typewriter.draw()
|
||||
|
||||
pyxel.text(10, 50, "Votre nom:", 6)
|
||||
self.input_field.draw()
|
||||
|
||||
# Afficher les messages
|
||||
for i, msg in enumerate(self.messages[-3:]):
|
||||
pyxel.text(10, 90 + i * 10, msg, 11)
|
||||
|
||||
Demo()
|
||||
Reference in New Issue
Block a user