Ajout du GUI
This commit is contained in:
299
kivy/modules/joycursor.py
Normal file
299
kivy/modules/joycursor.py
Normal file
@@ -0,0 +1,299 @@
|
||||
'''
|
||||
JoyCursor
|
||||
=========
|
||||
|
||||
.. versionadded:: 1.10.0
|
||||
|
||||
The JoyCursor is a tool for navigating with a joystick as if using a mouse
|
||||
or touch. Most of the actions that are possible for a mouse user are available
|
||||
in this module.
|
||||
|
||||
For example:
|
||||
|
||||
* left click
|
||||
* right click
|
||||
* double click (two clicks)
|
||||
* moving the cursor
|
||||
* holding the button (+ moving at the same time)
|
||||
* selecting
|
||||
* scrolling
|
||||
|
||||
There are some properties that can be edited live, such as intensity of the
|
||||
JoyCursor movement and toggling mouse button holding.
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
For normal module usage, please see the :mod:`~kivy.modules` documentation
|
||||
and these bindings:
|
||||
|
||||
+------------------+--------------------+
|
||||
| Event | Joystick |
|
||||
+==================+====================+
|
||||
| cursor move | Axis 3, Axis 4 |
|
||||
+------------------+--------------------+
|
||||
| cursor intensity | Button 0, Button 1 |
|
||||
+------------------+--------------------+
|
||||
| left click | Button 2 |
|
||||
+------------------+--------------------+
|
||||
| right click | Button 3 |
|
||||
+------------------+--------------------+
|
||||
| scroll up | Button 4 |
|
||||
+------------------+--------------------+
|
||||
| scroll down | Button 5 |
|
||||
+------------------+--------------------+
|
||||
| hold button | Button 6 |
|
||||
+------------------+--------------------+
|
||||
| joycursor on/off | Button 7 |
|
||||
+------------------+--------------------+
|
||||
|
||||
The JoyCursor, like Inspector, can also be imported and used as a normal
|
||||
python module. This has the added advantage of being able to activate and
|
||||
deactivate the module programmatically::
|
||||
|
||||
from kivy.lang import Builder
|
||||
from kivy.base import runTouchApp
|
||||
runTouchApp(Builder.load_string("""
|
||||
#:import jc kivy.modules.joycursor
|
||||
BoxLayout:
|
||||
Button:
|
||||
text: 'Press & activate with Ctrl+E or Button 7'
|
||||
on_release: jc.create_joycursor(root.parent, root)
|
||||
Button:
|
||||
text: 'Disable'
|
||||
on_release: jc.stop(root.parent, root)
|
||||
"""))
|
||||
'''
|
||||
|
||||
__all__ = ('start', 'stop', 'create_joycursor')
|
||||
|
||||
from kivy.clock import Clock
|
||||
from kivy.logger import Logger
|
||||
from kivy.uix.widget import Widget
|
||||
from kivy.graphics import Color, Line
|
||||
from kivy.properties import (
|
||||
ObjectProperty,
|
||||
NumericProperty,
|
||||
BooleanProperty
|
||||
)
|
||||
|
||||
|
||||
class JoyCursor(Widget):
|
||||
win = ObjectProperty()
|
||||
activated = BooleanProperty(False)
|
||||
cursor_width = NumericProperty(1.1)
|
||||
cursor_hold = BooleanProperty(False)
|
||||
intensity = NumericProperty(4)
|
||||
dead_zone = NumericProperty(10000)
|
||||
offset_x = NumericProperty(0)
|
||||
offset_y = NumericProperty(0)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(JoyCursor, self).__init__(**kwargs)
|
||||
self.avoid_bring_to_top = False
|
||||
self.size_hint = (None, None)
|
||||
self.size = (21, 21)
|
||||
self.set_cursor()
|
||||
|
||||
# draw cursor
|
||||
with self.canvas:
|
||||
Color(rgba=(0.19, 0.64, 0.81, 0.5))
|
||||
self.cursor_ox = Line(
|
||||
points=self.cursor_pts[:4],
|
||||
width=self.cursor_width + 0.1
|
||||
)
|
||||
self.cursor_oy = Line(
|
||||
points=self.cursor_pts[4:],
|
||||
width=self.cursor_width + 0.1
|
||||
)
|
||||
Color(rgba=(1, 1, 1, 0.5))
|
||||
self.cursor_x = Line(
|
||||
points=self.cursor_pts[:4],
|
||||
width=self.cursor_width
|
||||
)
|
||||
self.cursor_y = Line(
|
||||
points=self.cursor_pts[4:],
|
||||
width=self.cursor_width
|
||||
)
|
||||
self.pos = [-i for i in self.size]
|
||||
|
||||
def on_window_children(self, win, *args):
|
||||
# pull JoyCursor to the front when added
|
||||
# as a child directly to the window.
|
||||
if self.avoid_bring_to_top or not self.activated:
|
||||
return
|
||||
self.avoid_bring_to_top = True
|
||||
win.remove_widget(self)
|
||||
win.add_widget(self)
|
||||
self.avoid_bring_to_top = False
|
||||
|
||||
def on_activated(self, instance, activated):
|
||||
# bind/unbind when JoyCursor's state is changed
|
||||
if activated:
|
||||
self.win.add_widget(self)
|
||||
self.move = Clock.schedule_interval(self.move_cursor, 0)
|
||||
self.win.fbind('on_joy_axis', self.check_cursor)
|
||||
self.win.fbind('on_joy_button_down', self.set_intensity)
|
||||
self.win.fbind('on_joy_button_down', self.check_dispatch)
|
||||
self.win.fbind('mouse_pos', self.stop_cursor)
|
||||
mouse_pos = self.win.mouse_pos
|
||||
self.pos = (
|
||||
mouse_pos[0] - self.size[0] / 2.0,
|
||||
mouse_pos[1] - self.size[1] / 2.0
|
||||
)
|
||||
Logger.info('JoyCursor: joycursor activated')
|
||||
else:
|
||||
self.pos = [-i for i in self.size]
|
||||
Clock.unschedule(self.move)
|
||||
self.win.funbind('on_joy_axis', self.check_cursor)
|
||||
self.win.funbind('on_joy_button_down', self.set_intensity)
|
||||
self.win.funbind('on_joy_button_down', self.check_dispatch)
|
||||
self.win.funbind('mouse_pos', self.stop_cursor)
|
||||
self.win.remove_widget(self)
|
||||
Logger.info('JoyCursor: joycursor deactivated')
|
||||
|
||||
def set_cursor(self, *args):
|
||||
# create cursor points
|
||||
px, py = self.pos
|
||||
sx, sy = self.size
|
||||
self.cursor_pts = [
|
||||
px, py + round(sy / 2.0), px + sx, py + round(sy / 2.0),
|
||||
px + round(sx / 2.0), py, px + round(sx / 2.0), py + sy
|
||||
]
|
||||
|
||||
def check_cursor(self, win, stickid, axisid, value):
|
||||
# check axes and set offset if a movement is registered
|
||||
intensity = self.intensity
|
||||
dead = self.dead_zone
|
||||
|
||||
if axisid == 3:
|
||||
if value < -dead:
|
||||
self.offset_x = -intensity
|
||||
elif value > dead:
|
||||
self.offset_x = intensity
|
||||
else:
|
||||
self.offset_x = 0
|
||||
elif axisid == 4:
|
||||
# invert Y axis to behave like mouse
|
||||
if value < -dead:
|
||||
self.offset_y = intensity
|
||||
elif value > dead:
|
||||
self.offset_y = -intensity
|
||||
else:
|
||||
self.offset_y = 0
|
||||
else:
|
||||
self.offset_x = 0
|
||||
self.offset_y = 0
|
||||
|
||||
def set_intensity(self, win, stickid, buttonid):
|
||||
# set intensity of joycursor with joystick buttons
|
||||
intensity = self.intensity
|
||||
if buttonid == 0 and intensity > 2:
|
||||
intensity -= 1
|
||||
elif buttonid == 1:
|
||||
intensity += 1
|
||||
self.intensity = intensity
|
||||
|
||||
def check_dispatch(self, win, stickid, buttonid):
|
||||
if buttonid == 6:
|
||||
self.cursor_hold = not self.cursor_hold
|
||||
if buttonid not in (2, 3, 4, 5, 6):
|
||||
return
|
||||
|
||||
x, y = self.center
|
||||
# window event, correction necessary
|
||||
y = self.win.system_size[1] - y
|
||||
modifiers = []
|
||||
actions = {
|
||||
2: 'left',
|
||||
3: 'right',
|
||||
4: 'scrollup',
|
||||
5: 'scrolldown',
|
||||
6: 'left'
|
||||
}
|
||||
button = actions[buttonid]
|
||||
|
||||
self.win.dispatch('on_mouse_down', x, y, button, modifiers)
|
||||
if not self.cursor_hold:
|
||||
self.win.dispatch('on_mouse_up', x, y, button, modifiers)
|
||||
|
||||
def move_cursor(self, *args):
|
||||
# move joycursor as a mouse
|
||||
self.pos[0] += self.offset_x
|
||||
self.pos[1] += self.offset_y
|
||||
modifiers = []
|
||||
if self.cursor_hold:
|
||||
self.win.dispatch(
|
||||
'on_mouse_move',
|
||||
self.center[0],
|
||||
self.win.system_size[1] - self.center[1],
|
||||
modifiers
|
||||
)
|
||||
|
||||
def stop_cursor(self, instance, mouse_pos):
|
||||
# pin the cursor to the mouse pos
|
||||
self.offset_x = 0
|
||||
self.offset_y = 0
|
||||
self.pos = (
|
||||
mouse_pos[0] - self.size[0] / 2.0,
|
||||
mouse_pos[1] - self.size[1] / 2.0
|
||||
)
|
||||
|
||||
def on_pos(self, instance, new_pos):
|
||||
self.set_cursor()
|
||||
self.cursor_x.points = self.cursor_pts[:4]
|
||||
self.cursor_y.points = self.cursor_pts[4:]
|
||||
self.cursor_ox.points = self.cursor_pts[:4]
|
||||
self.cursor_oy.points = self.cursor_pts[4:]
|
||||
|
||||
def keyboard_shortcuts(self, win, scancode, *args):
|
||||
modifiers = args[-1]
|
||||
if scancode == 101 and modifiers == ['ctrl']:
|
||||
self.activated = not self.activated
|
||||
return True
|
||||
elif scancode == 27:
|
||||
if self.activated:
|
||||
self.activated = False
|
||||
return True
|
||||
|
||||
def joystick_shortcuts(self, win, stickid, buttonid):
|
||||
if buttonid == 7:
|
||||
self.activated = not self.activated
|
||||
if self.activated:
|
||||
self.pos = [round(i / 2.0) for i in win.size]
|
||||
|
||||
|
||||
def create_joycursor(win, ctx, *args):
|
||||
'''Create a JoyCursor instance attached to the *ctx* and bound to the
|
||||
Window's :meth:`~kivy.core.window.WindowBase.on_keyboard` event for
|
||||
capturing the keyboard shortcuts.
|
||||
|
||||
:Parameters:
|
||||
`win`: A :class:`Window <kivy.core.window.WindowBase>`
|
||||
The application Window to bind to.
|
||||
`ctx`: A :class:`~kivy.uix.widget.Widget` or subclass
|
||||
The Widget for JoyCursor to attach to.
|
||||
|
||||
'''
|
||||
ctx.joycursor = JoyCursor(win=win)
|
||||
win.bind(children=ctx.joycursor.on_window_children,
|
||||
on_keyboard=ctx.joycursor.keyboard_shortcuts)
|
||||
# always listen for joystick input to open the module
|
||||
# (like a keyboard listener)
|
||||
win.fbind('on_joy_button_down', ctx.joycursor.joystick_shortcuts)
|
||||
|
||||
|
||||
def start(win, ctx):
|
||||
Clock.schedule_once(lambda *t: create_joycursor(win, ctx))
|
||||
|
||||
|
||||
def stop(win, ctx):
|
||||
'''Stop and unload any active JoyCursors for the given *ctx*.
|
||||
'''
|
||||
if hasattr(ctx, 'joycursor'):
|
||||
ctx.joycursor.activated = False
|
||||
win.unbind(children=ctx.joycursor.on_window_children,
|
||||
on_keyboard=ctx.joycursor.keyboard_shortcuts)
|
||||
win.funbind('on_joy_button_down', ctx.joycursor.joystick_shortcuts)
|
||||
win.remove_widget(ctx.joycursor)
|
||||
del ctx.joycursor
|
||||
Reference in New Issue
Block a user