Как обрабатывать события от мыши

На этом занятии мы с вами познакомимся с обработкой событий от мыши. Всего в PyGame существует четыре их типа (значение атрибута type объекта Event):

  • pygame.MOUSEBUTTONDOWN – нажатие кнопки мыши;
  • pygame.MOUSEBUTTONUP – отпускание кнопки мыши;
  • pygame.MOUSEMOTION – перемещение курсора мыши;
  • pygame.MOUSEWHEEL – кручение колесика мыши.

Давайте для начала рассмотрим пример обработки события pygame.MOUSEBUTTONDOWN:

import pygame
 
pygame.init()
 
W = 600
H = 400
 
sc = pygame.display.set_mode((W, H))
pygame.display.set_caption("События от мыши")
pygame.display.set_icon(pygame.image.load("app.bmp"))
 
WHITE = (255, 255, 255)
BLUE = (0, 0, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
 
FPS = 60        # число кадров в секунду
clock = pygame.time.Clock()
 
while 1:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            exit()
        elif event.type == pygame.MOUSEBUTTONDOWN:
            print("Нажата кнопка: ", event.button)
 
    clock.tick(FPS)

Здесь при возникновении события pygame.MOUSEBUTTONDOWN в консоль выводится информация о номере нажатой кнопки, используя свойство button объекта event. Причем, это свойство существует только в событиях от мыши. Поэтому, мы сначала должны проверить, что объект Event действительно соответствует одному из событий мыши и только потом обращаться к свойству button. Иначе, возникнет исключение, что такого атрибута не существует.

После запуска этой программы мы видим, что кнопкам мыши присвоены следующие значения:

  • 1 – левая кнопка;
  • 2 – центральная;
  • 3 – правая кнопка;
  • 4 – для колесика.

Если в этой программе добавить следующие две строчки:

       elif event.type == pygame.MOUSEMOTION:
            print("Позиция мыши: ", event.pos)

То в консоли мы увидим кортеж из координат положения мыши относительно клиентской области окна. То есть, свойство pos хранит координаты мыши и мы ими всегда можем воспользоваться.

Если вместо атрибута pos записать атрибут rel:

print("Смещение мыши: ", event.rel)

то увидим относительные смещения курсора мыши (относительно положения в предыдущем событии MOUSEMOTION). Причем, свойство rel существует только для события pygame.MOUSEMOTION.

Давайте теперь рассмотрим программу, которая позволяет рисовать в окне прямоугольник с помощью мыши. Она будет следующей:

flStartDraw = False
sp = ep = None
 
sc.fill(WHITE)
pygame.display.update()
 
while 1:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            exit()
        elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
            flStartDraw = True
            sp = event.pos
        elif event.type == pygame.MOUSEMOTION:
            if flStartDraw:
                pos = event.pos
 
                width = pos[0] - sp[0]
                height = pos[1] - sp[1]
 
                sc.fill(WHITE)
                pygame.draw.rect(sc, RED, pygame.Rect(sp[0], sp[1], width, height))
                pygame.display.update()
        elif event.type == pygame.MOUSEBUTTONUP and event.button == 1:
            flStartDraw = False
 
    clock.tick(FPS)

Ее работа вполне очевидна. При нажатии на левую кнопку мыши мы устанавливаем флаг flStartDraw в значение True, т.е. включаем режим рисования при перемещении мыши. Соответственно, при событии MOUSEMOTION срабатывает условие и рисуется прямоугольник от начальной координаты sp до текущей позиции pos. При отпускании левой кнопки мыши, переменная flStartDraw становится равной False.

Однако, обратите внимание, события MOUSEBUTTONDOWN и MOUSEBUTTONUP срабатывают только один раз. То есть, если все время держать нажатой кнопку мыши, то произойдет только одно событие MOUSEBUTTONDOWN. А вот событие MOUSEMOTION происходит постоянно при каждом перемещении курсора мыши в клиентской области окна.

Если на каждой итерации главного цикла нужно определять: нажата ли какая-либо кнопка мыши, то следует использовать модуль

pygame.mouse

в котором, в частности, имеется функция:

pygame.mouse.get_pressed()

Она работает аналогично функции pygame.key.get_pressed() – определения нажатия клавиш, о которой мы говорили на прошлом занятии. Здесь все то же самое, только с кнопками мыши. На выходе pygame.mouse.get_pressed() выдает кортеж с тремя значениями:

Единица соответствует нажатой кнопке в соответствии с ее индексом:

  • 0 – левая кнопка;
  • 1 – центральная;
  • 2 – правая кнопка.

Перепишем предыдущую программу с использованием этой функции:

sp = None
 
sc.fill(WHITE)
pygame.display.update()
 
while 1:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            exit()
 
    pressed = pygame.mouse.get_pressed()
    if pressed[0]:
        pos = pygame.mouse.get_pos()
 
        if sp is None:
            sp = pos
 
        width = pos[0] - sp[0]
        height = pos[1] - sp[1]
 
        sc.fill(WHITE)
        pygame.draw.rect(sc, RED, pygame.Rect(sp[0], sp[1], width, height))
        pygame.display.update()
    else:
        sp = None
 
    clock.tick(FPS)

Смотрите, мы здесь вначале проверяем нажатие левой кнопки мыши (индекс 0) и если величина sp равна None, то начальной позиции для рисования прямоугольника еще нет и ее нужно приравнять текущей позиции курсора. Текущая позиция определяется с помощью функции get_pos(). Затем, удерживая нажатой левую кнопку и перемещая мышь, мы будем получать другие значения pos, но начальная позиция sp будет неизменной. В результате выполняется рисование прямоугольника. При отпускании левой кнопки, значение sp вновь становится равным None.

Наконец, можно скрыть курсор мыши с помощью функции:

pygame.mouse.set_visible(False)

И отобразить свой собственный, например, так:

pygame.mouse.set_visible(False)
while 1:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            exit()
 
    sc.fill(WHITE)
 
    pos = pygame.mouse.get_pos()
    if pygame.mouse.get_focused():
        pygame.draw.circle(sc, BLUE, pos, 7)
 
    pressed = pygame.mouse.get_pressed()
    if pressed[0]:
        if sp is None:
            sp = pos
 
        width = pos[0] - sp[0]
        height = pos[1] - sp[1]
        pygame.draw.rect(sc, RED, pygame.Rect(sp[0], sp[1], width, height))
    else:
        sp = None
 
    pygame.display.update()
 
    clock.tick(FPS)

Мы здесь в каждой позиции скрытого курсора рисуем синий кружок радиусом 7. При этом проверяем: находится ли курсор в области окна (иначе, его рисовать нет смысла).

Конечно, это не все функции модуля pygame.mouse, подробнее можно почитать на странице официальной документации:

https://www.pygame.org/docs/ref/mouse.html