Ефективна обработка на въвеждане от клавиатурата

Кой е добър начин за прилагане на работа с клавиатура? На който и да е език, където пиша интерактивна програма с клавиатура (като игра тетрис), в крайна сметка имам някакъв код, който изглежда така:

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)

(съкратено). Не ми харесва това, тъй като това трябва да мине в основния ми цикъл и се забърква с всички части на програмата. Това също така прави невъзможно да има екран за конфигуриране на потребителя, където те могат да променят кой ключ се съпоставя към кое действие. Има ли добър модел да направите това с помощта на някаква форма на обратно извикване на функции?


person Claudiu    schedule 23.11.2008    source източник
comment
Вижте също stackoverflow.com/questions/292095/   -  person S.Lott    schedule 23.11.2008


Отговори (3)


Можете да създадете речник, където клавишите са вход, а стойността е функция, която обработва натискането на клавиш:

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]()

Тогава промяната на ключовете е въпрос на модифициране на ключовете на речника.

person andrewrk    schedule 23.11.2008
comment
Не съм сигурен, но вярвам, че тези дефиниции на функции трябва да са над присвояването на разклонение, в противен случай просто ще получите речник, пълен с Nones. Добър отговор обаче, вероятно така бих го направил и аз. - person luqui; 23.11.2008
comment
Предполагам, че те все пак ще трябва да бъдат дефинирани в цикъла main() - или може би да вземат някакъв аргумент към клас GameState - person Claudiu; 24.11.2008
comment
Може би е по-добре да поддържате обратния речник: func2keys = { handle_quit: [K_q], handle_left: [K_LEFT, K_a], ...}. И генерирайте нов branch речник, ако конфигурацията е променена. branch = dict((k, f) for f, keys in func2keys.items() for k in keys) - person jfs; 30.11.2008

в допълнение към отговора на superjoe30, можете да използвате две нива на картографиране (два речника)

  • ключ => команден низ
  • команден низ => функция

Мисля, че това би улеснило разрешаването на дефинирани от потребителя съпоставяния. т.е. така че потребителите да могат да съпоставят своите ключове към "команди", а не към "името на функция"

person hasen    schedule 23.11.2008
comment
Можете също така да получите име на командата от docstring на функцията. - person Paul Fisher; 23.11.2008
comment
Можете да използвате обекти с атрибут __call__ вместо функции. - person jfs; 30.11.2008

Това, което правя в днешно време, е да имам някакъв клас/функция/нишка за събиране на входни данни, който ще проверява списък с предварително дефинирани обвързвания ключ->събитие.

Нещо като това:

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()
....

Това е основно това, което прави superjoe30, с изключение на това, че вместо директно извикване на обратни извиквания, добавям друго ниво на разделяне чрез използване на система за изпращане на събития, така че всеки код, който се интересува от натиснатите клавиши, просто слуша за това събитие.

Освен това ключовете могат лесно да бъдат обвързани с различни събития, които могат да бъдат прочетени от конфигурационен файл или нещо подобно и всеки ключ, който не е обвързан със събитие, просто се игнорира.

person Community    schedule 24.11.2008
comment
if event is not None изглежда по-добре. - person jfs; 30.11.2008