2008-11-23 6 views
9

Quelle est la meilleure façon d'implémenter la gestion du clavier? Dans toutes les langues, où j'écris un programme de clavier interactif (comme un jeu de tetris), je finis par avoir un code qui ressemble à ceci:Gestion efficace des entrées au clavier

for event in pygame.event.get(): 
    if event.type == KEYDOWN: 
     if False: pass   #make everything an elif 
     elif rotating: pass 
     elif event.key == K_q: 
     elif event.key == K_e: 
     elif event.key == K_LEFT: 
      curpiece.shift(-1, 0) 
      shadowpiece = curpiece.clone(); setupshadow(shadowpiece) 
     elif event.key == K_RIGHT: 
      curpiece.shift(1, 0) 
      shadowpiece = curpiece.clone(); setupshadow(shadowpiece) 

(raccourci). Je n'aime pas ça, car cela doit aller dans ma boucle principale, et ça dérange toutes les parties du programme. Cela rend également impossible d'avoir un écran de configuration de l'utilisateur où ils peuvent changer quelle touche correspond à quelle action. Existe-t-il un bon modèle pour cela en utilisant une forme de rappel de fonction?

+0

Voir aussi http://stackoverflow.com/questions/292095/polling-the-keyboard-in-python –

Répondre

17

Vous pouvez créer un dictionnaire dont les clés sont l'entrée et la valeur est une fonction qui gère la pression de touche:

def handle_quit(): 
    quit() 

def handle_left(): 
    curpiece.shift(-1, 0) 
    shadowpiece = curpiece.clone(); setupshadow(shadowpiece) 

def handle_right(): 
    curpiece.shift(1, 0) 
    shadowpiece = curpiece.clone(); setupshadow(shadowpiece) 

def handle_pause(): 
    if not paused: 
     paused = True 

branch = { 
    K_q: handle_quit 
    K_e: handle_pause 
    K_LEFT: handle_left 
    K_RIGHT: handle_right 
} 

for event in pygame.event.get(): 
    if event.type == KEYDOWN: 
     branch[event.key]() 

Puis, changeant les clés est une question de modifier les clés du dictionnaire.

+0

Je ne suis pas certain, mais je crois que ces fonctions defs doivent aller * au-dessus * de l'affectation de branche, sinon vous obtiendrez juste un dictionnaire plein de Nones. Bonne réponse cependant, c'est probablement comme ça que je le ferais aussi. – luqui

+0

je suppose que ceux-ci devront encore être définis dans la boucle main() - ou peut-être prendre un argument à une classe GameState – Claudiu

+0

Il pourrait être préférable de maintenir le dictionnaire inverse: 'func2keys = {handle_quit: [K_q], handle_left: [ K_LEFT, K_a], ...} '. Et générez un nouveau dictionnaire 'branch' si la configuration est modifiée. 'branch = dict ((k, f) pour f, les clés dans func2keys.items() pour k dans les clés)' – jfs

3

en plus de superjoe30's answer, vous pouvez utiliser deux niveaux de cartographie (deux dictionnaires)

  • key => string commande
  • chaîne de commande => Fonction

Je pense que ce serait il est plus facile d'autoriser des mappages définis par l'utilisateur. c'est-à-dire que les utilisateurs peuvent mapper leurs clés sur "commandes" plutôt que "le nom d'une fonction"

+0

Vous pouvez également obtenir un nom de la commande sur la docstring de la fonction. –

+0

Vous pouvez utiliser des objets avec l'attribut '__call__' au lieu de fonctions. – jfs

2

Ce que je fais de nos jours est d'avoir une sorte d'entrée rassemblant la classe/fonction/thread qui va vérifier une liste de clé prédéfinie-> les liaisons d'événements.

Quelque chose comme ceci:

class InputHandler: 
    def __init__ (self, eventDispatcher): 
     self.keys = {} 
     self.eventDispatcher = eventDispatcher 
    def add_key_binding (self, key, event): 
     self.keys.update((key, event,)) 
    def gather_input (self): 
     for event in pygame.event.get(): 
      if event.type == KEYDOWN: 
       event = self.keys.get(event.key, None) 
       if not event is None: 
        self.eventDispatcher.dispatch(event) 

.... 
inputHandler = InputHandler(EventDispatcher) 
inputHandler.add_key_binding(K_q, "quit_event") 
... 
inputHandler.gather_input() 
.... 

Il est essentiellement ce que superjoe30 fait, sauf qu'au lieu d'appeler callbacks directement, ajouter un autre niveau de séparation à l'aide d'un système de répartition événement, de sorte que tout code qui se soucie les touches étant pressées simplement écouter pour cet événement.

De plus, les clés peuvent être facilement liées à différents événements, qui peuvent être lus à partir d'un fichier de configuration ou quelque chose et toute touche qui n'est pas liée à un événement est simplement ignorée.

+0

'if event is not' est mieux. – jfs

Questions connexes