mouse.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. # -*- coding: utf-8 -*-
  2. import time as _time
  3. import platform as _platform
  4. if _platform.system() == 'Windows':
  5. from. import _winmouse as _os_mouse
  6. elif _platform.system() == 'Linux':
  7. from. import _nixmouse as _os_mouse
  8. elif _platform.system() == 'Darwin':
  9. from. import _darwinmouse as _os_mouse
  10. else:
  11. raise OSError("Unsupported platform '{}'".format(_platform.system()))
  12. from ._mouse_event import ButtonEvent, MoveEvent, WheelEvent, LEFT, RIGHT, MIDDLE, X, X2, UP, DOWN, DOUBLE
  13. from ._generic import GenericListener as _GenericListener
  14. _pressed_events = set()
  15. class _MouseListener(_GenericListener):
  16. def init(self):
  17. _os_mouse.init()
  18. def pre_process_event(self, event):
  19. if isinstance(event, ButtonEvent):
  20. if event.event_type in (UP, DOUBLE):
  21. _pressed_events.discard(event.button)
  22. else:
  23. _pressed_events.add(event.button)
  24. return True
  25. def listen(self):
  26. _os_mouse.listen(self.queue)
  27. _listener = _MouseListener()
  28. def is_pressed(button=LEFT):
  29. """ Returns True if the given button is currently pressed. """
  30. _listener.start_if_necessary()
  31. return button in _pressed_events
  32. def press(button=LEFT):
  33. """ Presses the given button (but doesn't release). """
  34. _os_mouse.press(button)
  35. def release(button=LEFT):
  36. """ Releases the given button. """
  37. _os_mouse.release(button)
  38. def click(button=LEFT):
  39. """ Sends a click with the given button. """
  40. _os_mouse.press(button)
  41. _os_mouse.release(button)
  42. def double_click(button=LEFT):
  43. """ Sends a double click with the given button. """
  44. click(button)
  45. click(button)
  46. def right_click():
  47. """ Sends a right click with the given button. """
  48. click(RIGHT)
  49. def wheel(delta=1):
  50. """ Scrolls the wheel `delta` clicks. Sign indicates direction. """
  51. _os_mouse.wheel(delta)
  52. def move(x, y, absolute=True, duration=0):
  53. """
  54. Moves the mouse. If `absolute`, to position (x, y), otherwise move relative
  55. to the current position. If `duration` is non-zero, animates the movement.
  56. """
  57. x = int(x)
  58. y = int(y)
  59. # Requires an extra system call on Linux, but `move_relative` is measured
  60. # in millimiters so we would lose precision.
  61. position_x, position_y = get_position()
  62. if not absolute:
  63. x = position_x + x
  64. y = position_y + y
  65. if duration:
  66. start_x = position_x
  67. start_y = position_y
  68. dx = x - start_x
  69. dy = y - start_y
  70. if dx == 0 and dy == 0:
  71. _time.sleep(duration)
  72. else:
  73. # 120 movements per second.
  74. # Round and keep float to ensure float division in Python 2
  75. steps = max(1.0, float(int(duration * 120.0)))
  76. for i in range(int(steps)+1):
  77. move(start_x + dx*i/steps, start_y + dy*i/steps)
  78. _time.sleep(duration/steps)
  79. else:
  80. _os_mouse.move_to(x, y)
  81. def drag(start_x, start_y, end_x, end_y, absolute=True, duration=0):
  82. """
  83. Holds the left mouse button, moving from start to end position, then
  84. releases. `absolute` and `duration` are parameters regarding the mouse
  85. movement.
  86. """
  87. if is_pressed():
  88. release()
  89. move(start_x, start_y, absolute, 0)
  90. press()
  91. move(end_x, end_y, absolute, duration)
  92. release()
  93. def on_button(callback, args=(), buttons=(LEFT, MIDDLE, RIGHT, X, X2), types=(UP, DOWN, DOUBLE)):
  94. """ Invokes `callback` with `args` when the specified event happens. """
  95. if not isinstance(buttons, (tuple, list)):
  96. buttons = (buttons,)
  97. if not isinstance(types, (tuple, list)):
  98. types = (types,)
  99. def handler(event):
  100. if isinstance(event, ButtonEvent):
  101. if event.event_type in types and event.button in buttons:
  102. callback(*args)
  103. _listener.add_handler(handler)
  104. return handler
  105. def on_click(callback, args=()):
  106. """ Invokes `callback` with `args` when the left button is clicked. """
  107. return on_button(callback, args, [LEFT], [UP])
  108. def on_double_click(callback, args=()):
  109. """
  110. Invokes `callback` with `args` when the left button is double clicked.
  111. """
  112. return on_button(callback, args, [LEFT], [DOUBLE])
  113. def on_right_click(callback, args=()):
  114. """ Invokes `callback` with `args` when the right button is clicked. """
  115. return on_button(callback, args, [RIGHT], [UP])
  116. def on_middle_click(callback, args=()):
  117. """ Invokes `callback` with `args` when the middle button is clicked. """
  118. return on_button(callback, args, [MIDDLE], [UP])
  119. def wait(button=LEFT, target_types=(UP, DOWN, DOUBLE)):
  120. """
  121. Blocks program execution until the given button performs an event.
  122. """
  123. from threading import Lock
  124. lock = Lock()
  125. lock.acquire()
  126. handler = on_button(lock.release, (), [button], target_types)
  127. lock.acquire()
  128. _listener.remove_handler(handler)
  129. def get_position():
  130. """ Returns the (x, y) mouse position. """
  131. return _os_mouse.get_position()
  132. def hook(callback):
  133. """
  134. Installs a global listener on all available mouses, invoking `callback`
  135. each time it is moved, a key status changes or the wheel is spun. A mouse
  136. event is passed as argument, with type either `mouse.ButtonEvent`,
  137. `mouse.WheelEvent` or `mouse.MoveEvent`.
  138. Returns the given callback for easier development.
  139. """
  140. _listener.add_handler(callback)
  141. return callback
  142. def unhook(callback):
  143. """
  144. Removes a previously installed hook.
  145. """
  146. _listener.remove_handler(callback)
  147. def unhook_all():
  148. """
  149. Removes all hooks registered by this application. Note this may include
  150. hooks installed by high level functions, such as `record`.
  151. """
  152. del _listener.handlers[:]
  153. def record(button=RIGHT, target_types=(DOWN,)):
  154. """
  155. Records all mouse events until the user presses the given button.
  156. Then returns the list of events recorded. Pairs well with `play(events)`.
  157. Note: this is a blocking function.
  158. Note: for more details on the mouse hook and events see `hook`.
  159. """
  160. recorded = []
  161. hook(recorded.append)
  162. wait(button=button, target_types=target_types)
  163. unhook(recorded.append)
  164. return recorded
  165. def play(events, speed_factor=1.0, include_clicks=True, include_moves=True, include_wheel=True):
  166. """
  167. Plays a sequence of recorded events, maintaining the relative time
  168. intervals. If speed_factor is <= 0 then the actions are replayed as fast
  169. as the OS allows. Pairs well with `record()`.
  170. The parameters `include_*` define if events of that type should be inluded
  171. in the replay or ignored.
  172. """
  173. last_time = None
  174. for event in events:
  175. if speed_factor > 0 and last_time is not None:
  176. _time.sleep((event.time - last_time) / speed_factor)
  177. last_time = event.time
  178. if isinstance(event, ButtonEvent) and include_clicks:
  179. if event.event_type == UP:
  180. _os_mouse.release(event.button)
  181. else:
  182. _os_mouse.press(event.button)
  183. elif isinstance(event, MoveEvent) and include_moves:
  184. _os_mouse.move_to(event.x, event.y)
  185. elif isinstance(event, WheelEvent) and include_wheel:
  186. _os_mouse.wheel(event.delta)
  187. replay = play
  188. hold = press
  189. if __name__ == '__main__':
  190. print('Recording... Double click to stop and replay.')
  191. play(record())