233 lines
7.5 KiB
Python
233 lines
7.5 KiB
Python
|
# -*- coding: utf-8 -*-
|
||
|
import warnings
|
||
|
warnings.simplefilter('always', DeprecationWarning)
|
||
|
warnings.warn('The mouse sub-library is deprecated and will be removed in future versions. Please use the standalone package `mouse`.', DeprecationWarning, stacklevel=2)
|
||
|
|
||
|
import time as _time
|
||
|
|
||
|
import platform as _platform
|
||
|
if _platform.system() == 'Windows':
|
||
|
from. import _winmouse as _os_mouse
|
||
|
elif _platform.system() == 'Linux':
|
||
|
from. import _nixmouse as _os_mouse
|
||
|
elif _platform.system() == 'Darwin':
|
||
|
from. import _darwinmouse as _os_mouse
|
||
|
else:
|
||
|
raise OSError("Unsupported platform '{}'".format(_platform.system()))
|
||
|
|
||
|
from ._mouse_event import ButtonEvent, MoveEvent, WheelEvent, LEFT, RIGHT, MIDDLE, X, X2, UP, DOWN, DOUBLE
|
||
|
from ._generic import GenericListener as _GenericListener
|
||
|
|
||
|
_pressed_events = set()
|
||
|
class _MouseListener(_GenericListener):
|
||
|
def init(self):
|
||
|
_os_mouse.init()
|
||
|
def pre_process_event(self, event):
|
||
|
if isinstance(event, ButtonEvent):
|
||
|
if event.event_type in (UP, DOUBLE):
|
||
|
_pressed_events.discard(event.button)
|
||
|
else:
|
||
|
_pressed_events.add(event.button)
|
||
|
return True
|
||
|
|
||
|
def listen(self):
|
||
|
_os_mouse.listen(self.queue)
|
||
|
|
||
|
_listener = _MouseListener()
|
||
|
|
||
|
def is_pressed(button=LEFT):
|
||
|
""" Returns True if the given button is currently pressed. """
|
||
|
_listener.start_if_necessary()
|
||
|
return button in _pressed_events
|
||
|
|
||
|
def press(button=LEFT):
|
||
|
""" Presses the given button (but doesn't release). """
|
||
|
_os_mouse.press(button)
|
||
|
|
||
|
def release(button=LEFT):
|
||
|
""" Releases the given button. """
|
||
|
_os_mouse.release(button)
|
||
|
|
||
|
def click(button=LEFT):
|
||
|
""" Sends a click with the given button. """
|
||
|
_os_mouse.press(button)
|
||
|
_os_mouse.release(button)
|
||
|
|
||
|
def double_click(button=LEFT):
|
||
|
""" Sends a double click with the given button. """
|
||
|
click(button)
|
||
|
click(button)
|
||
|
|
||
|
def right_click():
|
||
|
""" Sends a right click with the given button. """
|
||
|
click(RIGHT)
|
||
|
|
||
|
def wheel(delta=1):
|
||
|
""" Scrolls the wheel `delta` clicks. Sign indicates direction. """
|
||
|
_os_mouse.wheel(delta)
|
||
|
|
||
|
def move(x, y, absolute=True, duration=0):
|
||
|
"""
|
||
|
Moves the mouse. If `absolute`, to position (x, y), otherwise move relative
|
||
|
to the current position. If `duration` is non-zero, animates the movement.
|
||
|
"""
|
||
|
x = int(x)
|
||
|
y = int(y)
|
||
|
|
||
|
# Requires an extra system call on Linux, but `move_relative` is measured
|
||
|
# in millimiters so we would lose precision.
|
||
|
position_x, position_y = get_position()
|
||
|
|
||
|
if not absolute:
|
||
|
x = position_x + x
|
||
|
y = position_y + y
|
||
|
|
||
|
if duration:
|
||
|
start_x = position_x
|
||
|
start_y = position_y
|
||
|
dx = x - start_x
|
||
|
dy = y - start_y
|
||
|
|
||
|
if dx == 0 and dy == 0:
|
||
|
_time.sleep(duration)
|
||
|
else:
|
||
|
# 120 movements per second.
|
||
|
# Round and keep float to ensure float division in Python 2
|
||
|
steps = max(1.0, float(int(duration * 120.0)))
|
||
|
for i in range(int(steps)+1):
|
||
|
move(start_x + dx*i/steps, start_y + dy*i/steps)
|
||
|
_time.sleep(duration/steps)
|
||
|
else:
|
||
|
_os_mouse.move_to(x, y)
|
||
|
|
||
|
def drag(start_x, start_y, end_x, end_y, absolute=True, duration=0):
|
||
|
"""
|
||
|
Holds the left mouse button, moving from start to end position, then
|
||
|
releases. `absolute` and `duration` are parameters regarding the mouse
|
||
|
movement.
|
||
|
"""
|
||
|
if is_pressed():
|
||
|
release()
|
||
|
move(start_x, start_y, absolute, 0)
|
||
|
press()
|
||
|
move(end_x, end_y, absolute, duration)
|
||
|
release()
|
||
|
|
||
|
def on_button(callback, args=(), buttons=(LEFT, MIDDLE, RIGHT, X, X2), types=(UP, DOWN, DOUBLE)):
|
||
|
""" Invokes `callback` with `args` when the specified event happens. """
|
||
|
if not isinstance(buttons, (tuple, list)):
|
||
|
buttons = (buttons,)
|
||
|
if not isinstance(types, (tuple, list)):
|
||
|
types = (types,)
|
||
|
|
||
|
def handler(event):
|
||
|
if isinstance(event, ButtonEvent):
|
||
|
if event.event_type in types and event.button in buttons:
|
||
|
callback(*args)
|
||
|
_listener.add_handler(handler)
|
||
|
return handler
|
||
|
|
||
|
def on_click(callback, args=()):
|
||
|
""" Invokes `callback` with `args` when the left button is clicked. """
|
||
|
return on_button(callback, args, [LEFT], [UP])
|
||
|
|
||
|
def on_double_click(callback, args=()):
|
||
|
"""
|
||
|
Invokes `callback` with `args` when the left button is double clicked.
|
||
|
"""
|
||
|
return on_button(callback, args, [LEFT], [DOUBLE])
|
||
|
|
||
|
def on_right_click(callback, args=()):
|
||
|
""" Invokes `callback` with `args` when the right button is clicked. """
|
||
|
return on_button(callback, args, [RIGHT], [UP])
|
||
|
|
||
|
def on_middle_click(callback, args=()):
|
||
|
""" Invokes `callback` with `args` when the middle button is clicked. """
|
||
|
return on_button(callback, args, [MIDDLE], [UP])
|
||
|
|
||
|
def wait(button=LEFT, target_types=(UP, DOWN, DOUBLE)):
|
||
|
"""
|
||
|
Blocks program execution until the given button performs an event.
|
||
|
"""
|
||
|
from threading import Lock
|
||
|
lock = Lock()
|
||
|
lock.acquire()
|
||
|
handler = on_button(lock.release, (), [button], target_types)
|
||
|
lock.acquire()
|
||
|
_listener.remove_handler(handler)
|
||
|
|
||
|
def get_position():
|
||
|
""" Returns the (x, y) mouse position. """
|
||
|
return _os_mouse.get_position()
|
||
|
|
||
|
def hook(callback):
|
||
|
"""
|
||
|
Installs a global listener on all available mouses, invoking `callback`
|
||
|
each time it is moved, a key status changes or the wheel is spun. A mouse
|
||
|
event is passed as argument, with type either `mouse.ButtonEvent`,
|
||
|
`mouse.WheelEvent` or `mouse.MoveEvent`.
|
||
|
|
||
|
Returns the given callback for easier development.
|
||
|
"""
|
||
|
_listener.add_handler(callback)
|
||
|
return callback
|
||
|
|
||
|
def unhook(callback):
|
||
|
"""
|
||
|
Removes a previously installed hook.
|
||
|
"""
|
||
|
_listener.remove_handler(callback)
|
||
|
|
||
|
def unhook_all():
|
||
|
"""
|
||
|
Removes all hooks registered by this application. Note this may include
|
||
|
hooks installed by high level functions, such as `record`.
|
||
|
"""
|
||
|
del _listener.handlers[:]
|
||
|
|
||
|
def record(button=RIGHT, target_types=(DOWN,)):
|
||
|
"""
|
||
|
Records all mouse events until the user presses the given button.
|
||
|
Then returns the list of events recorded. Pairs well with `play(events)`.
|
||
|
|
||
|
Note: this is a blocking function.
|
||
|
Note: for more details on the mouse hook and events see `hook`.
|
||
|
"""
|
||
|
recorded = []
|
||
|
hook(recorded.append)
|
||
|
wait(button=button, target_types=target_types)
|
||
|
unhook(recorded.append)
|
||
|
return recorded
|
||
|
|
||
|
def play(events, speed_factor=1.0, include_clicks=True, include_moves=True, include_wheel=True):
|
||
|
"""
|
||
|
Plays a sequence of recorded events, maintaining the relative time
|
||
|
intervals. If speed_factor is <= 0 then the actions are replayed as fast
|
||
|
as the OS allows. Pairs well with `record()`.
|
||
|
|
||
|
The parameters `include_*` define if events of that type should be inluded
|
||
|
in the replay or ignored.
|
||
|
"""
|
||
|
last_time = None
|
||
|
for event in events:
|
||
|
if speed_factor > 0 and last_time is not None:
|
||
|
_time.sleep((event.time - last_time) / speed_factor)
|
||
|
last_time = event.time
|
||
|
|
||
|
if isinstance(event, ButtonEvent) and include_clicks:
|
||
|
if event.event_type == UP:
|
||
|
_os_mouse.release(event.button)
|
||
|
else:
|
||
|
_os_mouse.press(event.button)
|
||
|
elif isinstance(event, MoveEvent) and include_moves:
|
||
|
_os_mouse.move_to(event.x, event.y)
|
||
|
elif isinstance(event, WheelEvent) and include_wheel:
|
||
|
_os_mouse.wheel(event.delta)
|
||
|
|
||
|
replay = play
|
||
|
hold = press
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
print('Recording... Double click to stop and replay.')
|
||
|
play(record())
|