Quick search

Table Of Contents

Source code for kivy.input.providers.mouse

Mouse provider implementation

On linux systems, the mouse provider can be annoying when used with another
multitouch provider (hidinput or mtdev). The Mouse can conflict with them: a
single touch can generate one event from the mouse provider and another
from the multitouch provider.

To avoid this behavior, you can activate the "disable_on_activity" token in
the mouse configuration. Then, if any touches are created by another
provider, the mouse event will be discarded. Add this to your configuration::

    mouse = mouse,disable_on_activity

Using multitouch interaction with the mouse

.. versionadded:: 1.3.0

By default, the middle and right mouse buttons, as well as a combination of
ctrl + left mouse button are used for multitouch emulation.
If you want to use them for other purposes, you can disable this behavior by
activating the "disable_multitouch" token::

   mouse = mouse,disable_multitouch

.. versionchanged:: 1.9.0

You can now selectively control whether a click initiated as described above
will emulate multi-touch. If the touch has been initiated in the above manner
(e.g. right mouse button), a `multitouch_sim` value will be added to the
touch's profile, and a `multitouch_sim` property will be added to the touch.
By default, `multitouch_sim` is True and multitouch will be emulated for that
touch. If, however, `multitouch_on_demand` is added to the config::

   mouse = mouse,multitouch_on_demand

then `multitouch_sim` defaults to `False`. In that case, if `multitouch_sim`
is set to True before the mouse is released (e.g. in on_touch_down/move), the
touch will simulate a multi-touch event. For example::

    if 'multitouch_sim' in touch.profile:
        touch.multitouch_sim = True

Following is a list of the supported values for the
:attr:`~kivy.input.motionevent.MotionEvent.profile` property list.

================ ==========================================================
Profile value    Description
---------------- ----------------------------------------------------------
button           Mouse button (one of `left`, `right`, `middle`, `scrollup`
                 or `scrolldown`). Accessed via the 'button' property.
pos              2D position. Also reflected in the
                 and :attr:`~kivy.input.motionevent.MotionEvent.pos`
multitouch_sim   Specifies whether multitouch is simulated or not. Accessed
                 via the 'multitouch_sim' property.
================ ==========================================================


__all__ = ('MouseMotionEventProvider', )

from kivy.base import EventLoop
from collections import deque
from kivy.logger import Logger
from kivy.input.provider import MotionEventProvider
from kivy.input.factory import MotionEventFactory
from kivy.input.motionevent import MotionEvent

# late binding
Color = Ellipse = None

class MouseMotionEvent(MotionEvent):

    def depack(self, args):
        profile = self.profile
        # don't overwrite previous profile
        if not profile:
            profile.extend(('pos', 'button'))
        self.is_touch = True, = args[:2]
        if len(args) >= 3:
            self.button = args[2]
        if len(args) == 4:
            self.multitouch_sim = args[3]
        super(MouseMotionEvent, self).depack(args)

    # Create automatically touch on the surface.
    def update_graphics(self, win, create=False):
        global Color, Ellipse
        de = self.ud.get('_drawelement', None)
        if de is None and create:
            if Color is None:
                from import Color, Ellipse
            with win.canvas.after:
                de = (
                    Color(.8, .2, .2, .7),
                    Ellipse(size=(20, 20), segments=15))
            self.ud._drawelement = de
        if de is not None:

            # use same logic as WindowBase.on_motion() so we get correct
            # coordinates when _density != 1
            w, h = win._get_effective_size()

            self.scale_for_screen(w, h, rotation=win.rotation)

            de[1].pos = self.x - 10, self.y - 10

    def clear_graphics(self, win):
        de = self.ud.pop('_drawelement', None)
        if de is not None:

[docs]class MouseMotionEventProvider(MotionEventProvider): __handlers__ = {} def __init__(self, device, args): super(MouseMotionEventProvider, self).__init__(device, args) self.waiting_event = deque() self.touches = {} self.counter = 0 self.current_drag = None self.alt_touch = None self.disable_on_activity = False self.disable_multitouch = False self.multitouch_on_demand = False # split arguments args = args.split(',') for arg in args: arg = arg.strip() if arg == '': continue elif arg == 'disable_on_activity': self.disable_on_activity = True elif arg == 'disable_multitouch': self.disable_multitouch = True elif arg == 'multitouch_on_demand': self.multitouch_on_demand = True else: Logger.error('Mouse: unknown parameter <%s>' % arg)
[docs] def start(self): '''Start the mouse provider''' if not EventLoop.window: return EventLoop.window.bind( on_mouse_move=self.on_mouse_motion, on_mouse_down=self.on_mouse_press, on_mouse_up=self.on_mouse_release)
[docs] def stop(self): '''Stop the mouse provider''' if not EventLoop.window: return EventLoop.window.unbind( on_mouse_move=self.on_mouse_motion, on_mouse_down=self.on_mouse_press, on_mouse_up=self.on_mouse_release)
def test_activity(self): if not self.disable_on_activity: return False # trying to get if we currently have other touch than us # discard touches generated from kinetic touches = EventLoop.touches for touch in touches: # discard all kinetic touch if touch.__class__.__name__ == 'KineticMotionEvent': continue # not our instance, stop mouse if touch.__class__ != MouseMotionEvent: return True return False def find_touch(self, x, y): factor = 10. / EventLoop.window.system_size[0] for t in self.touches.values(): if abs(x - < factor and abs(y - < factor: return t return False def create_touch(self, rx, ry, is_double_tap, do_graphics, button): self.counter += 1 id = 'mouse' + str(self.counter) args = [rx, ry, button] if do_graphics: args += [not self.multitouch_on_demand] self.current_drag = cur = MouseMotionEvent(self.device, id=id, args=args) cur.is_double_tap = is_double_tap self.touches[id] = cur if do_graphics: # only draw red circle if multitouch is not disabled, and # if the multitouch_on_demand feature is not enable # (because in that case, we wait to see if multitouch_sim # is True or not before doing the multitouch) create_flag = ( (not self.disable_multitouch) and (not self.multitouch_on_demand) ) cur.update_graphics(EventLoop.window, create_flag) self.waiting_event.append(('begin', cur)) return cur def remove_touch(self, cur): if not in self.touches: return del self.touches[] cur.update_time_end() self.waiting_event.append(('end', cur)) cur.clear_graphics(EventLoop.window) def on_mouse_motion(self, win, x, y, modifiers): width, height = EventLoop.window.system_size rx = x / float(width) ry = 1. - y / float(height) if self.current_drag: cur = self.current_drag cur.move([rx, ry]) cur.update_graphics(win) self.waiting_event.append(('update', cur)) elif self.alt_touch is not None and 'alt' not in modifiers: # alt just released ? is_double_tap = 'shift' in modifiers cur = self.create_touch(rx, ry, is_double_tap, True, []) return True def on_mouse_press(self, win, x, y, button, modifiers): if self.test_activity(): return width, height = EventLoop.window.system_size rx = x / float(width) ry = 1. - y / float(height) new_me = self.find_touch(rx, ry) if new_me: self.current_drag = new_me else: is_double_tap = 'shift' in modifiers do_graphics = (not self.disable_multitouch) and ( button != 'left' or 'ctrl' in modifiers) cur = self.create_touch(rx, ry, is_double_tap, do_graphics, button) if 'alt' in modifiers: self.alt_touch = cur self.current_drag = None return True def on_mouse_release(self, win, x, y, button, modifiers): # special case, if button is all, then remove all the current mouses. if button == 'all': for cur in list(self.touches.values())[:]: self.remove_touch(cur) self.current_drag = None cur = self.current_drag if cur: not_right = button in ( 'left', 'scrollup', 'scrolldown', 'scrollleft', 'scrollright' ) not_ctrl = not ('ctrl' in modifiers) not_multi = ( self.disable_multitouch or 'multitouch_sim' not in cur.profile or not cur.multitouch_sim ) if (not_right and not_ctrl or not_multi): self.remove_touch(cur) self.current_drag = None else: cur.update_graphics(EventLoop.window, True) if self.alt_touch: self.remove_touch(self.alt_touch) self.alt_touch = None return True
[docs] def update(self, dispatch_fn): '''Update the mouse provider (pop event from the queue)''' try: while True: event = self.waiting_event.popleft() dispatch_fn(*event) except IndexError: pass
# registers MotionEventFactory.register('mouse', MouseMotionEventProvider)