Эффективная обработка ввода с клавиатуры

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

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)

(сокращенно). Мне это не нравится, так как это должно происходить в моем основном цикле, и это мешает всем частям программы. Это также делает невозможным наличие экрана конфигурации пользователя, на котором он может изменять, какие клавиши соответствуют какому действию. Есть ли хороший шаблон для этого с использованием некоторой формы обратных вызовов функций?

Также см. stackoverflow.com/questions/292095/…

S.Lott 23.11.2008 15:40
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
9
1
3 449
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 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]()

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

Я не уверен, но я считаю, что эти функции defs должны идти над при назначении ветки, иначе вы просто получите словарь, полный Nones. Хороший ответ, наверное, я бы тоже так поступил.

luqui 23.11.2008 11:04

я предполагаю, что их все равно нужно будет определить внутри цикла main () - или, возможно, принять какой-то аргумент в класс GameState

Claudiu 24.11.2008 04:59

Возможно, было бы лучше сохранить обратный словарь: 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)

jfs 30.11.2008 05:25

помимо ответ superjoe30 можно использовать два уровня отображения (два словаря)

  • ключ => командная строка
  • командная строка => функция

Я думаю, это упростило бы создание пользовательских сопоставлений. т.е. чтобы пользователи могли сопоставить свои ключи "командам", а не "имени функции".

Вы также можете получить имя команды из строки документации функции.

Paul Fisher 23.11.2008 23:36

Вместо функций можно использовать объекты с атрибутом __call__.

jfs 30.11.2008 05:32

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

Что-то вроде этого:

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, за исключением того, что вместо прямого вызова обратных вызовов я добавляю еще один уровень разделения с помощью системы диспетчеризации событий, так что любой код, который заботится о нажатых клавишах, просто прослушивает это событие.

Кроме того, ключи можно легко привязать к различным событиям, которые можно прочитать из файла конфигурации или чего-то еще, и любой ключ, который не привязан к событию, просто игнорируется.

if event is not None выглядит лучше.
jfs 30.11.2008 05:33

Другие вопросы по теме