Перейти к содержанию

Клавиатура и мышь

Используйте keyboard и mouse в обычном цикле: прочитайте ввод, измените переменные, нарисуйте кадр и вызовите tick(). На первой итерации ввод нейтрален. Каждый tick() готовит один снимок для следующей итерации; чтение не расходует и не изменяет его.

Непрерывное движение

Эта полная программа перемещает круг. Ось возвращает разность «положительная клавиша нажата минус отрицательная нажата»: -1, 0 или 1. Если нажаты обе стороны или ни одна, результат равен нулю.

from drawzero import *

x = y = 500
while True:
    x += 4 * keyboard.axis('left', 'right')
    y += 4 * keyboard.axis('up', 'down')
    clear()
    filled_circle(C.red, (x, y), 20)
    tick()

Одно действие при нажатии; альтернативные клавиши и модификаторы

just_pressed истинен один кадр ввода, даже если клавишу успели отпустить в этом кадре. Удержание и автоповтор не создают новых нажатий. just_released сообщает об отпускании. Несколько частей программы могут прочитать один и тот же признак перехода.

Несколько аргументов означают хотя бы один: keyboard.pressed('left', 'a') проверяет любой из двух вариантов управления. Для сочетания используйте keyboard.pressed('ctrl') and keyboard.just_pressed('s'). Если отпустить Left, продолжая держать A, just_released('left', 'a') будет истинным.

Каждая сторона axis принимает одну клавишу или непустой плоский кортеж/список вариантов. keyboard.axis(('left', 'a'), ('right', 'd')) возвращает целое число без сглаживания, масштабирования по времени или приоритета последней клавиши. Повторы не усиливают ось.

shift, ctrl, alt, meta — виртуальные клавиши, объединяющие левую и правую стороны. cmd означает meta. Группа нажимается с первой стороной и отпускается с последней. Если левый Shift уже нажат, нажатие правого не создаёт нового нажатия группы; отпускание левого при удержании правого не отпускает группу. Если за кадр отпустить и снова нажать всю группу, истинны оба признака перехода. Доступны и отдельные названия: lshift, rshift, lctrl, rmeta и другие.

Полный пример управления также перемещает фигуру в исходную точку щелчка:

from drawzero import *

x = y = 500
color = C.red
presses = 0
while True:
    speed = 12 if keyboard.pressed('shift') else 4
    dx = keyboard.axis(('left', 'a'), ('right', 'd'))
    dy = keyboard.axis(('up', 'w'), ('down', 's'))
    x += speed * dx
    y += speed * dy
    if keyboard.just_pressed('space'):
        presses += 1
        color = C.blue if color == C.red else C.red
    for event in events():
        if event.type == E.MOUSE_DOWN and event.button == M.LEFT:
            x, y = event.pos
    clear()
    text(C.white, 'CONTROLS / STATE', (500, 70), 48)
    text(C.white, 'Arrows or WASD move; Shift speeds up', (500, 150), 32)
    text(C.white, 'Space changes color once; click places the square', (500, 205), 30)
    text(C.cyan, f'Axis: ({dx}, {dy})   Shift: {keyboard.pressed("shift")}', (500, 300), 36)
    text(C.yellow, f'Space presses: {presses}', (500, 360), 36)
    filled_rect(color, (x, y), 35, 35)
    filled_circle(C.yellow, mouse.pos, 6)
    tick(fps=60)

Удерживаются Right и Shift, ось (1, 0), одно действие по Space

Результат отрисовки примера при детерминированном воспроизведении, не проверка оборудования.

Координаты и кнопки мыши

mouse.pos — неизменяемая пара (x, y) в виртуальных координатах холста, включая дробные значения и точки за его границами. Для изменяемой точки создайте Pt(*mouse.pos). Проверяйте mouse.pressed('left'), mouse.just_pressed('right') или mouse.just_released(M.MIDDLE). Регистр названий не важен; числа и константы равнозначны: M.LEFT == 1, M.MIDDLE == 2, M.RIGHT == 3. Новый API поддерживает только эти три кнопки.

mouse.rel — сумма полученных смещений в виртуальных координатах, а не разность двух положений. Повторное чтение её не сбрасывает. Координаты преобразуются при обработке события: сохранённые события не меняются после изменения размера окна или виртуального холста. Движения до и после изменения размера могут использовать разные масштабы. При некорректном размере окна используется нейтральный масштаб.

Прокрутка и введённый текст

mouse.wheel возвращает (горизонталь, вертикаль) в единицах прокрутки, не пикселях. Положительные направления — вправо и вверх (от пользователя). Флаг обратного направления бэкенда учитывается один раз. Дробные точные значения, включая ноль, сохраняются; если старый бэкенд их не предоставляет, используются целые значения. Размер холста не влияет на прокрутку.

keyboard.typed объединяет подтверждённый текст в порядке поступления. Поддерживается Unicode, включая кириллицу и греческие буквы. Незавершённая композиция доступна через E.TEXT_EDITING и не попадает в typed. Поле unicode нажатия клавиши не добавляется повторно: двойной вставки нет.

Упорядоченные события

Когда важен порядок, используйте events(). В течение кадра возвращается один и тот же неизменяемый кортеж. keyboard.events содержит клавиши и текст, mouse.events — кнопки, движение и прокрутку. Кортежи кешируются и ссылаются на те же объекты событий, что и общий поток. Фокус и размер окна входят только в общий поток. Сохранённые кортежи остаются действительными после следующих кадров. Поддерживаются перечисленные ниже семейства, а не все события SDL. Закрытие окна, как обычно, завершает программу.

typed — удобная сводка, а не текстовый редактор. Последовательность текст → Backspace → текст нужно обрабатывать по порядку. Эта полная программа также показывает прокрутку по двум осям:

from drawzero import *

value = ''
scroll_x = scroll_y = 0
composition = ''
while True:
    for event in events():
        if event.type == E.TEXT_INPUT:
            value += event.text
            composition = ''
        elif event.type == E.TEXT_EDITING:
            composition = event.text
        elif event.type == E.KEY_DOWN and event.key == K.BACKSPACE:
            value = value[:-1]
        elif event.type == E.FOCUS_LOST:
            composition = ''
    dx, dy = mouse.wheel
    scroll_x += dx
    scroll_y += dy
    clear()
    text(C.white, 'TEXT / TWO-DIMENSIONAL SCROLL', (500, 70), 40)
    text(C.white, 'Type text; Backspace deletes; scroll moves the marker', (500, 150), 28)
    text(C.cyan, value, (80, 300), 48, '<.')
    text(C.gray, composition, (80, 370), 36, '<.')
    text(C.yellow, f'Scroll: ({scroll_x:.1f}, {scroll_y:.1f})', (500, 480), 36)
    line(C.gray, (200, 720), (800, 720))
    line(C.gray, (500, 570), (500, 870))
    filled_circle(C.orange, (500 + 30 * scroll_x, 720 - 30 * scroll_y), 22)
    tick(fps=60)

Упорядоченное редактирование даёт DrawZero: ready, прокрутка (1.5, 2.5)

Отрисованный результат воспроизведения событий текста и прокрутки, не проверка аппаратного ввода или IME. Пример удаляет один символ Python, а не целый графемный кластер Unicode.

Точное рисование и перетаскивание

event.pos, event.rel, event.buttons описывают момент события. mouse.pos и mouse.pressed() — конечное состояние кадра. Кнопки проверяют через M.LEFT in event.buttons. Умножать итоговое состояние кнопки на mouse.rel всего кадра недостаточно для точного перетаскивания: часть движений могла произойти до нажатия или после отпускания. Обрабатывайте события по порядку и отменяйте действие при потере фокуса:

from drawzero import *

segments = []
previous = None
while True:
    for event in events():
        if event.type == E.FOCUS_LOST:
            previous = None
        elif event.type == E.MOUSE_DOWN and event.button == M.LEFT:
            previous = event.pos
        elif event.type == E.MOUSE_MOVE:
            if previous is not None and M.LEFT in event.buttons:
                segments.append((previous, event.pos))
                previous = event.pos
        elif event.type == E.MOUSE_UP and event.button == M.LEFT:
            if previous is not None and not event.source.cancelled:
                segments.append((previous, event.pos))
            previous = None
    if keyboard.just_pressed('space'):
        segments.clear()
    clear()
    text(C.white, 'PRECISE DRAWING', (500, 70), 48)
    text(C.white, 'Hold left mouse button to draw; Space clears', (500, 150), 32)
    for start, end in segments:
        line(C.cyan, start, end, line_width=6)
    filled_circle(C.yellow, mouse.pos, 8)
    text(C.gray, f'Path segments: {len(segments)}', (500, 900), 32)
    tick(fps=60)

Голубая линия проходит через полученные положения указателя

Отрисовка фиксированной последовательности движений. Сохраняется каждое полученное движение; режима объединения движений нет.

Справочник API

API Результат
keyboard.pressed(key, *other_keys) Хотя бы одна клавиша удерживается: bool
keyboard.just_pressed(key, *other_keys) Хотя бы одно настоящее нажатие: bool
keyboard.just_released(key, *other_keys) Хотя бы одно отпускание: bool
keyboard.axis(negative, positive) Целое -1, 0 или 1
keyboard.typed Подтверждённый текст или ''
keyboard.events Кортеж событий клавиш и текста
mouse.pressed(button, *other_buttons) Хотя бы одна кнопка удерживается: bool
mouse.just_pressed(button, *other_buttons) Хотя бы одно нажатие: bool
mouse.just_released(button, *other_buttons) Хотя бы одно отпускание: bool
mouse.pos, mouse.rel Неизменяемые пары виртуальных координат
mouse.wheel Неизменяемая пара горизонтальной/вертикальной прокрутки
mouse.events Кортеж событий мыши
events() Кортеж всех поддерживаемых событий в порядке получения
K, KEY Один объект с кодами клавиш: K.LEFT, K.a, K.A, K.SPACE
M LEFT, MIDDLE, RIGHT
MOD Типизированные флаги: SHIFT, CTRL, ALT, META/GUI, стороны L/R, NUM, CAPS, MODE, NONE
E Константы типов событий из таблицы ниже
tick(r=1, *, fps=30) Показывает рисунок, ждёт/опрашивает r раз, публикует один снимок; возвращает None

Клавиши задают числовым кодом или названием из K без учёта регистра: left, a, space и т. п. enter/return и esc/escape — явные синонимы. A и a обозначают одну логическую клавишу и не подразумевают Shift. Это логические клавиши, а не физические сканкоды или произвольный текст Unicode. Раскладка меняет соответствие физическим клавишам; сканкоды сохранены для просмотра в событиях, но API физических привязок нет.

Запрос состояния требует хотя бы один аргумент и проверяет все аргументы, даже после совпадения. Неизвестные имена/коды, логические значения, вложенные и пустые группы оси, неподдерживаемые объекты вызывают ошибку. MOD — маски, не клавиши; используйте event.mod & MOD.CTRL. Язык ошибок задаётся через set_lang('en') или set_lang('ru'); для похожих названий предлагается исправление.

Поля событий

Это неизменяемые кортежи со слотами, именованными типизированными полями и читаемым представлением. Классы доступны в drawzero.utils.input. У всех есть type и source.

event.type Класс и поля
E.KEY_DOWN, E.KEY_UP KeyEvent: key, mod, scancode, unicode, repeat (false для отпускания)
E.TEXT_INPUT TextEvent: text
E.TEXT_EDITING EditingEvent: text, start, length
E.MOUSE_DOWN, E.MOUSE_UP ButtonEvent: button, pos, mod
E.MOUSE_MOVE MotionEvent: pos, rel, buttons (frozenset идентификаторов), mod
E.MOUSE_WHEEL WheelEvent: дробные x, y, историческое pos или None
E.FOCUS_LOST, E.FOCUS_GAINED FocusEvent: без дополнительных полей
E.WINDOW_RESIZED ResizeEvent: size — (ширина, высота) в нативных координатах окна

Неизвестные поля клавиш: key=0, mod=MOD.NONE, scancode=None, unicode=''. Автоповтор определяется флагом бэкенда или уже удерживаемой физической/логической клавишей. Маски берутся из события или истории модификаторов, не из конечного состояния. source — неизменяемый Provenance(window, which, touch, cancelled): неизвестные идентификаторы и происхождение от касания равны None, cancelled по умолчанию false. Явная отмена очищает соответствующее состояние без обычного признака отпускания. Pygame не всегда помечает синтетические отпускания; DrawZero не пытается их угадать. Аппаратные метки времени и точность сверх порядка полученной очереди не обещаются.

Кадры, фокус и время

Нажатие и отпускание за один кадр дают held=false, just-pressed=true, just-released=true. Несколько нажатий сохраняются в событиях, но признаки переходов остаются логическими. Пустой кадр очищает события, переходы, текст, движение и прокрутку, сохраняя удержание. Чтение не обращается к ОС, не опрашивает очередь, не ждёт и не создаёт окно.

tick(fps=60) и tick(fps=120) меняют ограничение частоты; tick(fps=0) отключает ожидание. Отрицательные и неконечные значения запрещены. tick(60) по-прежнему означает 60 опросов. sleep(t) ждёт/опрашивает int(t * 30 + 0.5) раз при 30 FPS и публикует накопленную историю один раз. tick(0) и sleep(0) показывают рисунок и очищают временные данные без опроса. Долгий sleep хранит O(число полученных событий) истории. Очередь SDL конечна: если надолго блокировать программу, ввод может потеряться до получения DrawZero.

Потеря фокуса очищает удерживаемые клавиши/кнопки и накопленное движение без вымышленных отпусканий. Настоящие более ранние переходы сохраняются; отменяйте перетаскивание и композицию при E.FOCUS_LOST. Выход мыши из окна сам по себе не означает потерю фокуса. После пакета с возвращением фокуса DrawZero сверяет конечное удержание с состоянием pygame без новых нажатий и без удаления настоящей истории переходов. Снимок содержит только логические клавиши; физические соответствия восстанавливаются при следующих нажатиях.

В EJUDGE ввод нейтрален и сам по себе не импортирует графический бэкенд. quit() окончательно закрывает окно в этом процессе; последующее рисование не создаёт его заново. Статический рисунок, как и раньше, остаётся открытым до закрытия пользователем.