first commit

This commit is contained in:
Yura 2024-09-15 15:12:16 +03:00
commit 417e54da96
5696 changed files with 900003 additions and 0 deletions

View file

@ -0,0 +1,300 @@
'''
Modules
=======
Modules are classes that can be loaded when a Kivy application is starting. The
loading of modules is managed by the config file. Currently, we include:
* :class:`~kivy.modules.touchring`: Draw a circle around each touch.
* :class:`~kivy.modules.monitor`: Add a red topbar that indicates the FPS
and a small graph indicating input activity.
* :class:`~kivy.modules.keybinding`: Bind some keys to actions, such as a
screenshot.
* :class:`~kivy.modules.recorder`: Record and playback a sequence of
events.
* :class:`~kivy.modules.screen`: Emulate the characteristics (dpi/density/
resolution) of different screens.
* :class:`~kivy.modules.inspector`: Examines your widget hierarchy and
widget properties.
* :class:`~kivy.modules.webdebugger`: Realtime examination of your app
internals via a web browser.
* :class:`~kivy.modules.joycursor`: Navigate in your app with a joystick.
* :class:`~kivy.modules.showborder`: Show widget's border.
Modules are automatically loaded from the Kivy path and User path:
* `PATH_TO_KIVY/kivy/modules`
* `HOME/.kivy/mods`
Activating a module
-------------------
There are various ways in which you can activate a kivy module.
Activate a module in the config
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
To activate a module this way, you can edit your configuration file (in your
`HOME/.kivy/config.ini`)::
[modules]
# uncomment to activate
touchring =
# monitor =
# keybinding =
Only the name of the module followed by "=" is sufficient to activate the
module.
Activate a module in Python
^^^^^^^^^^^^^^^^^^^^^^^^^^^
Before starting your application, preferably at the start of your import, you
can do something like this::
import kivy
kivy.require('1.0.8')
# Activate the touchring module
from kivy.config import Config
Config.set('modules', 'touchring', '')
Activate a module via the commandline
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
When starting your application from the commandline, you can add a
*-m <modulename>* to the arguments. For example::
python main.py -m webdebugger
.. note::
Some modules, such as the screen, may require additional parameters. They
will, however, print these parameters to the console when launched without
them.
Create your own module
----------------------
Create a file in your `HOME/.kivy/mods`, and create 2 functions::
def start(win, ctx):
pass
def stop(win, ctx):
pass
Start/stop are functions that will be called for every window opened in
Kivy. When you are starting a module, you can use these to store and
manage the module state. Use the `ctx` variable as a dictionary. This
context is unique for each instance/start() call of the module, and will
be passed to stop() too.
'''
__all__ = ('Modules', )
from kivy.config import Config
from kivy.logger import Logger
import kivy
import importlib
import os
import sys
class ModuleContext:
'''Context of a module
You can access to the config with self.config.
'''
def __init__(self):
self.config = {}
def __repr__(self):
return repr(self.config)
class ModuleBase:
'''Handle Kivy modules. It will automatically load and instantiate the
module for the general window.'''
def __init__(self, **kwargs):
self.mods = {}
self.wins = []
def add_path(self, path):
'''Add a path to search for modules in'''
if not os.path.exists(path):
return
if path not in sys.path:
sys.path.append(path)
dirs = os.listdir(path)
for module in dirs:
name, ext = os.path.splitext(module)
# accept only python extensions
if ext not in ('.py', '.pyo', '.pyc') or name == '__init__':
continue
self.mods[name] = {
'name': name,
'activated': False,
'context': ModuleContext()}
def list(self):
'''Return the list of available modules'''
return self.mods
def import_module(self, name):
try:
modname = 'kivy.modules.{0}'.format(name)
module = importlib.__import__(name=modname)
module = sys.modules[modname]
except ImportError:
try:
module = importlib.__import__(name=name)
module = sys.modules[name]
except ImportError:
Logger.exception('Modules: unable to import <%s>' % name)
# protect against missing module dependency crash
self.mods[name]['module'] = None
return
# basic check on module
if not hasattr(module, 'start'):
Logger.warning('Modules: Module <%s> missing start() function' %
name)
return
if not hasattr(module, 'stop'):
err = 'Modules: Module <%s> missing stop() function' % name
Logger.warning(err)
return
self.mods[name]['module'] = module
def activate_module(self, name, win):
'''Activate a module on a window'''
if name not in self.mods:
Logger.warning('Modules: Module <%s> not found' % name)
return
mod = self.mods[name]
# ensure the module has been configured
if 'module' not in mod:
self._configure_module(name)
pymod = mod['module']
if not mod['activated']:
context = mod['context']
msg = 'Modules: Start <{0}> with config {1}'.format(
name, context)
Logger.debug(msg)
pymod.start(win, context)
mod['activated'] = True
def deactivate_module(self, name, win):
'''Deactivate a module from a window'''
if name not in self.mods:
Logger.warning('Modules: Module <%s> not found' % name)
return
if 'module' not in self.mods[name]:
return
module = self.mods[name]['module']
if self.mods[name]['activated']:
module.stop(win, self.mods[name]['context'])
self.mods[name]['activated'] = False
def register_window(self, win):
'''Add the window to the window list'''
if win not in self.wins:
self.wins.append(win)
self.update()
def unregister_window(self, win):
'''Remove the window from the window list'''
if win in self.wins:
self.wins.remove(win)
self.update()
def update(self):
'''Update the status of the module for each window'''
modules_to_activate = [x[0] for x in Config.items('modules')]
for win in self.wins:
for name in self.mods:
if name not in modules_to_activate:
self.deactivate_module(name, win)
for name in modules_to_activate:
try:
self.activate_module(name, win)
except:
import traceback
traceback.print_exc()
raise
def configure(self):
'''(internal) Configure all the modules before using them.
'''
modules_to_configure = [x[0] for x in Config.items('modules')]
for name in modules_to_configure:
if name not in self.mods:
Logger.warning('Modules: Module <%s> not found' % name)
continue
self._configure_module(name)
def _configure_module(self, name):
if 'module' not in self.mods[name]:
try:
self.import_module(name)
except ImportError:
return
# convert configuration like:
# -m mjpegserver:port=8080,fps=8
# and pass it in context.config token
config = dict()
args = Config.get('modules', name)
if args != '':
values = Config.get('modules', name).split(',')
for value in values:
x = value.split('=', 1)
if len(x) == 1:
config[x[0]] = True
else:
config[x[0]] = x[1]
self.mods[name]['context'].config = config
# call configure if module have one
if hasattr(self.mods[name]['module'], 'configure'):
self.mods[name]['module'].configure(config)
def usage_list(self):
print('Available modules')
print('=================')
for module in sorted(self.list()):
if 'module' not in self.mods[module]:
self.import_module(module)
# ignore modules without docstring
if not self.mods[module]['module'].__doc__:
continue
text = self.mods[module]['module'].__doc__.strip("\n ")
text = text.split('\n')
# make sure we don't get IndexError along the way
# then pretty format the header
if len(text) > 2:
if text[1].startswith('='):
# '\n%-12s: %s' -> 12 spaces + ": "
text[1] = '=' * (14 + len(text[1]))
text = '\n'.join(text)
print('\n%-12s: %s' % (module, text))
Modules = ModuleBase()
Modules.add_path(kivy.kivy_modules_dir)
if 'KIVY_DOC' not in os.environ:
Modules.add_path(kivy.kivy_usermodules_dir)
if __name__ == '__main__':
print(Modules.list())

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,75 @@
'''
Cursor
======
Shows a cursor following mouse motion events, useful on systems with no
visible native mouse cursor.
Configuration
-------------
:Parameters:
`texture`: str, defaults to
'data/images/cursor.png' Image used to represent the cursor if
displayed
`size`: tuple, defaults to (40, 40)
Apparent size of the mouse cursor, if displayed, (None,None) value
will keep its real size.
`offset`: tuple, defaults to (None, None)
Offset of the texture image. The default value will align the
top-left corner of the image to the mouse pos.
Example
-------
In your configuration (`~/.kivy/config.ini`), you can add something like
this::
[modules]
cursor = texture=mypointer.png,size=20x20,offset=20x20
.. versionadded:: 1.10.0
'''
__all__ = ('start', 'stop')
from kivy.core.image import Image
from kivy.graphics import Color, Rectangle
from kivy import kivy_data_dir
from kivy.compat import string_types
from os.path import join
from functools import partial
def _mouse_move(texture, size, offset, win, pos, *args):
if hasattr(win, '_cursor'):
c = win._cursor
else:
with win.canvas.after:
Color(1, 1, 1, 1, mode='rgba')
win._cursor = c = Rectangle(texture=texture, size=size)
c.pos = pos[0] + offset[0], pos[1] - size[1] + offset[1]
def start(win, ctx):
cursor_texture = Image(
ctx.config.get('texture', join(kivy_data_dir, 'images', 'cursor.png'))
).texture
cursor_size = ctx.config.get('size')
if isinstance(cursor_size, string_types):
cursor_size = [int(x) for x in cursor_size.split('x')]
elif not cursor_size:
cursor_size = cursor_texture.size
cursor_offset = ctx.config.get('offset', (0, 0))
if isinstance(cursor_offset, string_types):
cursor_offset = [int(x) for x in cursor_offset.split('x')]
win.bind(
mouse_pos=partial(
_mouse_move, cursor_texture, cursor_size, cursor_offset))
def stop(win, ctx):
win.unbind(mouse_pos=_mouse_move)

View file

@ -0,0 +1,755 @@
'''
Inspector
=========
.. versionadded:: 1.0.9
.. warning::
This module is highly experimental, use it with care.
The Inspector is a tool for finding a widget in the widget tree by clicking or
tapping on it.
Some keyboard shortcuts are activated:
* "Ctrl + e": activate / deactivate the inspector view
* "Escape": cancel widget lookup first, then hide the inspector view
Available inspector interactions:
* tap once on a widget to select it without leaving inspect mode
* double tap on a widget to select and leave inspect mode (then you can
manipulate the widget again)
Some properties can be edited live. However, due to the delayed usage of
some properties, it might crash if you don't handle all the cases.
Usage
-----
For normal module usage, please see the :mod:`~kivy.modules` documentation.
The Inspector, however, can also be imported and used just like a normal
python module. This has the added advantage of being able to activate and
deactivate the module programmatically::
from kivy.core.window import Window
from kivy.app import App
from kivy.uix.button import Button
from kivy.modules import inspector
class Demo(App):
def build(self):
button = Button(text="Test")
inspector.create_inspector(Window, button)
return button
Demo().run()
To remove the Inspector, you can do the following::
inspector.stop(Window, button)
'''
__all__ = ('start', 'stop', 'create_inspector')
import weakref
from functools import partial
from itertools import chain
from kivy.animation import Animation
from kivy.logger import Logger
from kivy.graphics.transformation import Matrix
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.factory import Factory
from kivy.weakproxy import WeakProxy
from kivy.properties import (
ObjectProperty, BooleanProperty, ListProperty,
NumericProperty, StringProperty, OptionProperty,
ReferenceListProperty, AliasProperty, VariableListProperty
)
Builder.load_string('''
<Inspector>:
layout: layout
widgettree: widgettree
treeview: treeview
content: content
BoxLayout:
orientation: 'vertical'
id: layout
size_hint_y: None
height: 250
padding: 5
spacing: 5
top: 0
canvas:
Color:
rgb: .4, .4, .4
Rectangle:
pos: self.x, self.top
size: self.width, 1
Color:
rgba: .185, .18, .18, .95
Rectangle:
pos: self.pos
size: self.size
# Top Bar
BoxLayout:
size_hint_y: None
height: 50
spacing: 5
Button:
text: 'Move to Top'
on_release: root.toggle_position(args[0])
size_hint_x: None
width: 120
ToggleButton:
text: 'Inspect'
on_state: root.inspect_enabled = args[1] == 'down'
size_hint_x: None
state: 'down' if root.inspect_enabled else 'normal'
width: 80
Button:
text: 'Parent'
on_release:
root.highlight_widget(root.widget.parent) if root.widget \
else None
size_hint_x: None
width: 80
Button:
text: '%r' % root.widget
on_release: root.show_widget_info()
Button:
text: 'X'
size_hint_x: None
width: 50
on_release: root.activated = False
# Bottom Bar
BoxLayout:
ScrollView:
scroll_type: ['bars', 'content']
bar_width: 10
size_hint_x: 0.0001
WidgetTree:
id: widgettree
hide_root: True
size_hint: None, None
height: self.minimum_height
width: max(self.parent.width, self.minimum_width)
selected_widget: root.widget
on_select_widget: root.highlight_widget(args[1])
Splitter:
sizeable_from: 'left'
min_size: self.parent.width / 2
max_size: self.parent.width
BoxLayout:
ScrollView:
scroll_type: ['bars', 'content']
bar_width: 10
TreeView:
id: treeview
size_hint_y: None
hide_root: True
height: self.minimum_height
Splitter:
sizeable_from: 'left'
keep_within_parent: True
rescale_with_parent: True
max_size: self.parent.width / 2
min_size: 0
ScrollView:
id: content
<TreeViewProperty>:
height: max(lkey.texture_size[1], ltext.texture_size[1])
Label:
id: lkey
text: root.key
text_size: (self.width, None)
width: 150
size_hint_x: None
Label:
id: ltext
text: [repr(getattr(root.widget, root.key, '')), root.refresh][0]\
if root.widget else ''
text_size: (self.width, None)
<-TreeViewWidget>:
height: self.texture_size[1] + sp(4)
size_hint_x: None
width: self.texture_size[0] + sp(4)
canvas.before:
Color:
rgba: self.color_selected if self.is_selected else (0, 0, 0, 0)
Rectangle:
pos: self.pos
size: self.size
Color:
rgba: 1, 1, 1, int(not self.is_leaf)
Rectangle:
source:
('atlas://data/images/defaulttheme/tree_%s' %
('opened' if self.is_open else 'closed'))
size: 16, 16
pos: self.x - 20, self.center_y - 8
canvas:
Color:
rgba:
(self.disabled_color if self.disabled else
(self.color if not self.markup else (1, 1, 1, 1)))
Rectangle:
texture: self.texture
size: self.texture_size
pos:
(int(self.center_x - self.texture_size[0] / 2.),
int(self.center_y - self.texture_size[1] / 2.))
''')
class TreeViewProperty(Factory.BoxLayout, Factory.TreeViewNode):
widget_ref = ObjectProperty(None, allownone=True)
def _get_widget(self):
wr = self.widget_ref
if wr is None:
return
wr = wr()
if wr is None:
self.widget_ref = None
return
return wr
widget = AliasProperty(_get_widget, None, bind=('widget_ref', ))
key = ObjectProperty(None, allownone=True)
inspector = ObjectProperty(None)
refresh = BooleanProperty(False)
class TreeViewWidget(Factory.Label, Factory.TreeViewNode):
widget = ObjectProperty(None)
class WidgetTree(Factory.TreeView):
selected_widget = ObjectProperty(None, allownone=True)
__events__ = ('on_select_widget',)
def __init__(self, **kwargs):
super(WidgetTree, self).__init__(**kwargs)
self.update_scroll = Clock.create_trigger(self._update_scroll)
def find_node_by_widget(self, widget):
for node in self.iterate_all_nodes():
if not node.parent_node:
continue
try:
if node.widget == widget:
return node
except ReferenceError:
pass
return
def update_selected_widget(self, widget):
if widget:
node = self.find_node_by_widget(widget)
if node:
self.select_node(node, False)
while node and isinstance(node, TreeViewWidget):
if not node.is_open:
self.toggle_node(node)
node = node.parent_node
def on_selected_widget(self, inst, widget):
if widget:
self.update_selected_widget(widget)
self.update_scroll()
def select_node(self, node, select_widget=True):
super(WidgetTree, self).select_node(node)
if select_widget:
try:
self.dispatch('on_select_widget', node.widget.__self__)
except ReferenceError:
pass
def on_select_widget(self, widget):
pass
def _update_scroll(self, *args):
node = self._selected_node
if not node:
return
self.parent.scroll_to(node)
class Inspector(Factory.FloatLayout):
widget = ObjectProperty(None, allownone=True)
layout = ObjectProperty(None)
widgettree = ObjectProperty(None)
treeview = ObjectProperty(None)
inspect_enabled = BooleanProperty(False)
activated = BooleanProperty(False)
widget_info = BooleanProperty(False)
content = ObjectProperty(None)
at_bottom = BooleanProperty(True)
_update_widget_tree_ev = None
def __init__(self, **kwargs):
self.win = kwargs.pop('win', None)
super(Inspector, self).__init__(**kwargs)
self.avoid_bring_to_top = False
with self.canvas.before:
self.gcolor = Factory.Color(1, 0, 0, .25)
Factory.PushMatrix()
self.gtransform = Factory.Transform(Matrix())
self.grect = Factory.Rectangle(size=(0, 0))
Factory.PopMatrix()
Clock.schedule_interval(self.update_widget_graphics, 0)
def on_touch_down(self, touch):
ret = super(Inspector, self).on_touch_down(touch)
if (('button' not in touch.profile or touch.button == 'left') and
not ret and self.inspect_enabled):
self.highlight_at(*touch.pos)
if touch.is_double_tap:
self.inspect_enabled = False
self.show_widget_info()
ret = True
return ret
def on_touch_move(self, touch):
ret = super(Inspector, self).on_touch_move(touch)
if not ret and self.inspect_enabled:
self.highlight_at(*touch.pos)
ret = True
return ret
def on_touch_up(self, touch):
ret = super(Inspector, self).on_touch_up(touch)
if not ret and self.inspect_enabled:
ret = True
return ret
def on_window_children(self, win, children):
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 highlight_at(self, x, y):
widget = None
# reverse the loop - look at children on top first and
# modalviews before others
win_children = self.win.children
children = chain(
(c for c in win_children if isinstance(c, Factory.ModalView)),
(
c for c in reversed(win_children)
if not isinstance(c, Factory.ModalView)
)
)
for child in children:
if child is self:
continue
widget = self.pick(child, x, y)
if widget:
break
self.highlight_widget(widget)
def highlight_widget(self, widget, info=True, *largs):
# no widget to highlight, reduce rectangle to 0, 0
self.widget = widget
if not widget:
self.grect.size = 0, 0
if self.widget_info and info:
self.show_widget_info()
def update_widget_graphics(self, *largs):
if not self.activated:
return
if self.widget is None:
self.grect.size = 0, 0
return
self.grect.size = self.widget.size
matrix = self.widget.get_window_matrix()
if self.gtransform.matrix.get() != matrix.get():
self.gtransform.matrix = matrix
def toggle_position(self, button):
to_bottom = button.text == 'Move to Bottom'
if to_bottom:
button.text = 'Move to Top'
if self.widget_info:
Animation(top=250, t='out_quad', d=.3).start(self.layout)
else:
Animation(top=60, t='out_quad', d=.3).start(self.layout)
bottom_bar = self.layout.children[1]
self.layout.remove_widget(bottom_bar)
self.layout.add_widget(bottom_bar)
else:
button.text = 'Move to Bottom'
if self.widget_info:
Animation(top=self.height, t='out_quad', d=.3).start(
self.layout)
else:
Animation(y=self.height - 60, t='out_quad', d=.3).start(
self.layout)
bottom_bar = self.layout.children[1]
self.layout.remove_widget(bottom_bar)
self.layout.add_widget(bottom_bar)
self.at_bottom = to_bottom
def pick(self, widget, x, y):
ret = None
# try to filter widgets that are not visible (invalid inspect target)
if (hasattr(widget, 'visible') and not widget.visible):
return ret
if widget.collide_point(x, y):
ret = widget
x2, y2 = widget.to_local(x, y)
# reverse the loop - look at children on top first
for child in reversed(widget.children):
ret = self.pick(child, x2, y2) or ret
return ret
def on_activated(self, instance, activated):
if not activated:
self.grect.size = 0, 0
if self.at_bottom:
anim = Animation(top=0, t='out_quad', d=.3)
else:
anim = Animation(y=self.height, t='out_quad', d=.3)
anim.bind(on_complete=self.animation_close)
anim.start(self.layout)
self.widget = None
self.widget_info = False
else:
self.win.add_widget(self)
Logger.info('Inspector: inspector activated')
if self.at_bottom:
Animation(top=60, t='out_quad', d=.3).start(self.layout)
else:
Animation(y=self.height - 60, t='out_quad', d=.3).start(
self.layout)
ev = self._update_widget_tree_ev
if ev is None:
ev = self._update_widget_tree_ev = Clock.schedule_interval(
self.update_widget_tree, 1)
else:
ev()
self.update_widget_tree()
def animation_close(self, instance, value):
if not self.activated:
self.inspect_enabled = False
self.win.remove_widget(self)
self.content.clear_widgets()
treeview = self.treeview
for node in list(treeview.iterate_all_nodes()):
node.widget_ref = None
treeview.remove_node(node)
self._window_node = None
if self._update_widget_tree_ev is not None:
self._update_widget_tree_ev.cancel()
widgettree = self.widgettree
for node in list(widgettree.iterate_all_nodes()):
widgettree.remove_node(node)
Logger.info('Inspector: inspector deactivated')
def show_widget_info(self):
self.content.clear_widgets()
widget = self.widget
treeview = self.treeview
for node in list(treeview.iterate_all_nodes())[:]:
node.widget_ref = None
treeview.remove_node(node)
if not widget:
if self.at_bottom:
Animation(top=60, t='out_quad', d=.3).start(self.layout)
else:
Animation(y=self.height - 60, t='out_quad', d=.3).start(
self.layout)
self.widget_info = False
return
self.widget_info = True
if self.at_bottom:
Animation(top=250, t='out_quad', d=.3).start(self.layout)
else:
Animation(top=self.height, t='out_quad', d=.3).start(self.layout)
for node in list(treeview.iterate_all_nodes())[:]:
treeview.remove_node(node)
keys = list(widget.properties().keys())
keys.sort()
node = None
if type(widget) is WeakProxy:
wk_widget = widget.__ref__
else:
wk_widget = weakref.ref(widget)
for key in keys:
node = TreeViewProperty(key=key, widget_ref=wk_widget)
node.bind(is_selected=self.show_property)
try:
widget.bind(**{key: partial(
self.update_node_content, weakref.ref(node))})
except:
pass
treeview.add_node(node)
def update_node_content(self, node, *largs):
node = node()
if node is None:
return
node.refresh = True
node.refresh = False
def keyboard_shortcut(self, win, scancode, *largs):
modifiers = largs[-1]
if scancode == 101 and set(modifiers) & {'ctrl'} and not set(
modifiers) & {'shift', 'alt', 'meta'}:
self.activated = not self.activated
if self.activated:
self.inspect_enabled = True
return True
elif scancode == 27:
if self.inspect_enabled:
self.inspect_enabled = False
return True
if self.activated:
self.activated = False
return True
def show_property(self, instance, value, key=None, index=-1, *largs):
# normal call: (tree node, focus, )
# nested call: (widget, prop value, prop key, index in dict/list)
if value is False:
return
content = None
if key is None:
# normal call
nested = False
widget = instance.widget
key = instance.key
prop = widget.property(key)
value = getattr(widget, key)
else:
# nested call, we might edit subvalue
nested = True
widget = instance
prop = None
dtype = None
if isinstance(prop, AliasProperty) or nested:
# trying to resolve type dynamically
if type(value) in (str, str):
dtype = 'string'
elif type(value) in (int, float):
dtype = 'numeric'
elif type(value) in (tuple, list):
dtype = 'list'
if isinstance(prop, NumericProperty) or dtype == 'numeric':
content = Factory.TextInput(text=str(value) or '', multiline=False)
content.bind(text=partial(
self.save_property_numeric, widget, key, index))
elif isinstance(prop, StringProperty) or dtype == 'string':
content = Factory.TextInput(text=value or '', multiline=True)
content.bind(text=partial(
self.save_property_text, widget, key, index))
elif (isinstance(prop, ListProperty) or
isinstance(prop, ReferenceListProperty) or
isinstance(prop, VariableListProperty) or
dtype == 'list'):
content = Factory.GridLayout(cols=1, size_hint_y=None)
content.bind(minimum_height=content.setter('height'))
for i, item in enumerate(value):
button = Factory.Button(
text=repr(item),
size_hint_y=None,
height=44
)
if isinstance(item, Factory.Widget):
button.bind(on_release=partial(self.highlight_widget, item,
False))
else:
button.bind(on_release=partial(self.show_property, widget,
item, key, i))
content.add_widget(button)
elif isinstance(prop, OptionProperty):
content = Factory.GridLayout(cols=1, size_hint_y=None)
content.bind(minimum_height=content.setter('height'))
for option in prop.options:
button = Factory.ToggleButton(
text=option,
state='down' if option == value else 'normal',
group=repr(content.uid), size_hint_y=None,
height=44)
button.bind(on_press=partial(
self.save_property_option, widget, key))
content.add_widget(button)
elif isinstance(prop, ObjectProperty):
if isinstance(value, Factory.Widget):
content = Factory.Button(text=repr(value))
content.bind(on_release=partial(self.highlight_widget, value))
elif isinstance(value, Factory.Texture):
content = Factory.Image(texture=value)
else:
content = Factory.Label(text=repr(value))
elif isinstance(prop, BooleanProperty):
state = 'down' if value else 'normal'
content = Factory.ToggleButton(text=key, state=state)
content.bind(on_release=partial(self.save_property_boolean, widget,
key, index))
self.content.clear_widgets()
if content:
self.content.add_widget(content)
def save_property_numeric(self, widget, key, index, instance, value):
try:
if index >= 0:
getattr(widget, key)[index] = float(instance.text)
else:
setattr(widget, key, float(instance.text))
except:
pass
def save_property_text(self, widget, key, index, instance, value):
try:
if index >= 0:
getattr(widget, key)[index] = instance.text
else:
setattr(widget, key, instance.text)
except:
pass
def save_property_boolean(self, widget, key, index, instance, ):
try:
value = instance.state == 'down'
if index >= 0:
getattr(widget, key)[index] = value
else:
setattr(widget, key, value)
except:
pass
def save_property_option(self, widget, key, instance, *largs):
try:
setattr(widget, key, instance.text)
except:
pass
def _update_widget_tree_node(self, node, widget, is_open=False):
tree = self.widgettree
update_nodes = []
nodes = {}
for cnode in node.nodes[:]:
try:
nodes[cnode.widget] = cnode
except ReferenceError:
# widget no longer exists, just remove it
pass
tree.remove_node(cnode)
for child in widget.children:
if child is self:
continue
if child in nodes:
cnode = tree.add_node(nodes[child], node)
else:
cnode = tree.add_node(TreeViewWidget(
text=child.__class__.__name__, widget=child.proxy_ref,
is_open=is_open), node)
update_nodes.append((cnode, child))
return update_nodes
def update_widget_tree(self, *args):
if not hasattr(self, '_window_node') or not self._window_node:
self._window_node = self.widgettree.add_node(
TreeViewWidget(text='Window', widget=self.win, is_open=True))
nodes = self._update_widget_tree_node(self._window_node, self.win,
is_open=True)
while nodes:
ntmp = nodes[:]
nodes = []
for node in ntmp:
nodes += self._update_widget_tree_node(*node)
self.widgettree.update_selected_widget(self.widget)
def create_inspector(win, ctx, *largs):
'''Create an Inspector instance attached to the *ctx* and bound to the
Window's :meth:`~kivy.core.window.WindowBase.on_keyboard` event for
capturing the keyboard shortcut.
: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 to be inspected.
'''
# Dunno why, but if we are creating inspector within the start(), no lang
# rules are applied.
ctx.inspector = Inspector(win=win)
win.bind(children=ctx.inspector.on_window_children,
on_keyboard=ctx.inspector.keyboard_shortcut)
def start(win, ctx):
ctx.ev_late_create = Clock.schedule_once(
partial(create_inspector, win, ctx))
def stop(win, ctx):
'''Stop and unload any active Inspectors for the given *ctx*.'''
if hasattr(ctx, 'ev_late_create'):
ctx.ev_late_create.cancel()
del ctx.ev_late_create
if hasattr(ctx, 'inspector'):
win.unbind(children=ctx.inspector.on_window_children,
on_keyboard=ctx.inspector.keyboard_shortcut)
win.remove_widget(ctx.inspector)
del ctx.inspector

View 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

View file

@ -0,0 +1,64 @@
'''Keybinding
==========
This module forces the mapping of some keys to functions:
* F11: Rotate the Window through 0, 90, 180 and 270 degrees
* Shift + F11: Switches between portrait and landscape on desktops
* F12: Take a screenshot
Note: this doesn't work if the application requests the keyboard beforehand.
Usage
-----
For normal module usage, please see the :mod:`~kivy.modules` documentation.
The Keybinding module, however, can also be imported and used just
like a normal python module. This has the added advantage of being
able to activate and deactivate the module programmatically::
from kivy.app import App
from kivy.uix.button import Button
from kivy.modules import keybinding
from kivy.core.window import Window
class Demo(App):
def build(self):
button = Button(text="Hello")
keybinding.start(Window, button)
return button
Demo().run()
To remove the Keybinding, you can do the following::
Keybinding.stop(Window, button)
'''
from kivy.utils import platform
__all__ = ('start', 'stop')
def _on_keyboard_handler(instance, key, scancode, codepoint, modifiers):
if key == 293 and modifiers == []: # F12
instance.screenshot()
elif key == 292 and modifiers == []: # F11
instance.rotation += 90
elif key == 292 and modifiers == ['shift']: # Shift + F11
if platform in ('win', 'linux', 'macosx'):
instance.rotation = 0
w, h = instance.size
w, h = h, w
instance.size = (w, h)
def start(win, ctx):
win.bind(on_keyboard=_on_keyboard_handler)
def stop(win, ctx):
win.unbind(on_keyboard=_on_keyboard_handler)

View file

@ -0,0 +1,88 @@
'''
Monitor module
==============
The Monitor module is a toolbar that shows the activity of your current
application :
* FPS
* Graph of input events
Usage
-----
For normal module usage, please see the :mod:`~kivy.modules` documentation.
'''
__all__ = ('start', 'stop')
from kivy.uix.label import Label
from kivy.graphics import Rectangle, Color
from kivy.clock import Clock
from functools import partial
_statsinput = 0
_maxinput = -1
def update_fps(ctx, *largs):
ctx.label.text = 'FPS: %f' % Clock.get_fps()
ctx.rectangle.texture = ctx.label.texture
ctx.rectangle.size = ctx.label.texture_size
def update_stats(win, ctx, *largs):
global _statsinput
ctx.stats = ctx.stats[1:] + [_statsinput]
_statsinput = 0
m = max(1., _maxinput)
for i, x in enumerate(ctx.stats):
ctx.statsr[i].size = (4, ctx.stats[i] / m * 20)
ctx.statsr[i].pos = (win.width - 64 * 4 + i * 4, win.height - 25)
def _update_monitor_canvas(win, ctx, *largs):
with win.canvas.after:
ctx.overlay.pos = (0, win.height - 25)
ctx.overlay.size = (win.width, 25)
ctx.rectangle.pos = (5, win.height - 20)
class StatsInput(object):
def process(self, events):
global _statsinput, _maxinput
_statsinput += len(events)
if _statsinput > _maxinput:
_maxinput = float(_statsinput)
return events
def start(win, ctx):
# late import to avoid breaking module loading
from kivy.input.postproc import kivy_postproc_modules
kivy_postproc_modules['fps'] = StatsInput()
global _ctx
ctx.label = Label(text='FPS: 0.0')
ctx.inputstats = 0
ctx.stats = []
ctx.statsr = []
with win.canvas.after:
ctx.color = Color(1, 0, 0, .5)
ctx.overlay = Rectangle(pos=(0, win.height - 25),
size=(win.width, 25))
ctx.color = Color(1, 1, 1)
ctx.rectangle = Rectangle(pos=(5, win.height - 20))
ctx.color = Color(1, 1, 1, .5)
for i in range(64):
ctx.stats.append(0)
ctx.statsr.append(
Rectangle(pos=(win.width - 64 * 4 + i * 4, win.height - 25),
size=(4, 0)))
win.bind(size=partial(_update_monitor_canvas, win, ctx))
Clock.schedule_interval(partial(update_fps, ctx), .5)
Clock.schedule_interval(partial(update_stats, win, ctx), 1 / 60.)
def stop(win, ctx):
win.canvas.remove(ctx.label)

View file

@ -0,0 +1,94 @@
'''
Recorder module
===============
.. versionadded:: 1.1.0
Create an instance of :class:`~kivy.input.recorder.Recorder`, attach to the
class, and bind some keys to record / play sequences:
- F6: play the last record in a loop
- F7: read the latest recording
- F8: record input events
Configuration
-------------
.. |attrs| replace:: :attr:`~kivy.input.recorder.Recorder.record_attrs`
.. |profile_mask| replace::
:attr:`~kivy.input.recorder.Recorder.record_profile_mask`
:Parameters:
`attrs`: str, defaults to |attrs| value.
Attributes to record from the motion event
`profile_mask`: str, defaults to |profile_mask| value.
Mask for motion event profile. Used to filter which profile will appear
in the fake motion event when replayed.
`filename`: str, defaults to 'recorder.kvi'
Name of the file to record / play with
Usage
-----
For normal module usage, please see the :mod:`~kivy.modules` documentation.
'''
__all__ = ('start', 'stop')
from kivy.logger import Logger
from functools import partial
def replay(recorder, *args):
if recorder.play:
return
else:
recorder.play = True
def on_recorder_key(recorder, window, key, *largs):
if key == 289: # F8
if recorder.play:
Logger.error('Recorder: Cannot start recording while playing.')
return
recorder.record = not recorder.record
elif key == 288: # F7
if recorder.record:
Logger.error('Recorder: Cannot start playing while recording.')
return
recorder.play = not recorder.play
elif key == 287: # F6
if recorder.play:
recorder.unbind(play=replay)
else:
recorder.bind(play=replay)
recorder.play = True
def start(win, ctx):
keys = {}
# attributes
value = ctx.config.get('attrs', None)
if value is not None:
keys['record_attrs'] = value.split(':')
# profile mask
value = ctx.config.get('profile_mask', None)
if value is not None:
keys['record_profile_mask'] = value.split(':')
# filename
value = ctx.config.get('filename', None)
if value is not None:
keys['filename'] = value
from kivy.input.recorder import Recorder
ctx.recorder = Recorder(window=win, **keys)
win.bind(on_key_down=partial(on_recorder_key, ctx.recorder))
def stop(win, ctx):
if hasattr(ctx, 'recorder'):
ctx.recorder.release()

View file

@ -0,0 +1,183 @@
'''Screen
======
This module changes some environment and configuration variables
to match the density / dpi / screensize of a specific device.
To see a list of the available screenid's, just run::
python main.py -m screen
To simulate a medium-density screen such as the Motorola Droid 2::
python main.py -m screen:droid2
To simulate a high-density screen such as HTC One X, in portrait::
python main.py -m screen:onex,portrait
To simulate the iPad 2 screen::
python main.py -m screen:ipad
If the generated window is too large, you can specify a scale::
python main.py -m screen:note2,portrait,scale=.75
Note that to display your contents correctly on a scaled window you
must consistently use units 'dp' and 'sp' throughout your app. See
:mod:`~kiv.metrics` for more details.
'''
import sys
from os import environ
from kivy.config import Config
from kivy.logger import Logger
# taken from http://en.wikipedia.org/wiki/List_of_displays_by_pixel_density
devices = {
# device: (name, width, height, dpi, density)
'onex': ('HTC One X', 1280, 720, 312, 2),
'one': ('HTC One', 1920, 1080, 468, 3),
'onesv': ('HTC One SV', 800, 480, 216, 1.5),
's3': ('Galaxy SIII', 1280, 720, 306, 2),
'note2': ('Galaxy Note II', 1280, 720, 267, 2),
'droid2': ('Motorola Droid 2', 854, 480, 240, 1.5),
'xoom': ('Motorola Xoom', 1280, 800, 149, 1),
'ipad': ('iPad (1 and 2)', 1024, 768, 132, 1),
'ipad3': ('iPad 3', 2048, 1536, 264, 2),
'iphone4': ('iPhone 4', 960, 640, 326, 2),
'iphone5': ('iPhone 5', 1136, 640, 326, 2),
'xperiae': ('Xperia E', 480, 320, 166, 1),
'nexus4': ('Nexus 4', 1280, 768, 320, 2),
'nexus7': ('Nexus 7 (2012 version)', 1280, 800, 216, 1.325),
'nexus7.2': ('Nexus 7 (2013 version)', 1920, 1200, 323, 2),
# taken from design.google.com/devices
# please consider using another data instead of
# a dict for autocompletion to work
# these are all in landscape
'phone_android_one': ('Android One', 854, 480, 218, 1.5),
'phone_htc_one_m8': ('HTC One M8', 1920, 1080, 432, 3.0),
'phone_htc_one_m9': ('HTC One M9', 1920, 1080, 432, 3.0),
'phone_iphone': ('iPhone', 480, 320, 168, 1.0),
'phone_iphone_4': ('iPhone 4', 960, 640, 320, 2.0),
'phone_iphone_5': ('iPhone 5', 1136, 640, 320, 2.0),
'phone_iphone_6': ('iPhone 6', 1334, 750, 326, 2.0),
'phone_iphone_6_plus': ('iPhone 6 Plus', 1920, 1080, 400, 3.0),
'phone_lg_g2': ('LG G2', 1920, 1080, 432, 3.0),
'phone_lg_g3': ('LG G3', 2560, 1440, 533, 3.0),
'phone_moto_g': ('Moto G', 1280, 720, 327, 2.0),
'phone_moto_x': ('Moto X', 1280, 720, 313, 2.0),
'phone_moto_x_2nd_gen': ('Moto X 2nd Gen', 1920, 1080, 432, 3.0),
'phone_nexus_4': ('Nexus 4', 1280, 768, 240, 2.0),
'phone_nexus_5': ('Nexus 5', 1920, 1080, 450, 3.0),
'phone_nexus_5x': ('Nexus 5X', 1920, 1080, 432, 2.6),
'phone_nexus_6': ('Nexus 6', 2560, 1440, 496, 3.5),
'phone_nexus_6p': ('Nexus 6P', 2560, 1440, 514, 3.5),
'phone_oneplus_3t': ('OnePlus 3t', 1863, 1080, 380, 2.375),
'phone_oneplus_6t': ('OnePlus 6t', 2340, 1080, 420, 2.625),
'phone_samsung_galaxy_note_4': ('Samsung Galaxy Note 4',
2560, 1440, 514, 3.0),
'phone_samsung_galaxy_s5': ('Samsung Galaxy S5', 1920, 1080, 372, 3.0),
'phone_samsung_galaxy_s6': ('Samsung Galaxy S6', 2560, 1440, 576, 4.0),
'phone_sony_xperia_c4': ('Sony Xperia C4', 1920, 1080, 400, 2.0),
'phone_sony_xperia_z_ultra': ('Sony Xperia Z Ultra', 1920, 1080, 348, 2.0),
'phone_sony_xperia_z1_compact': ('Sony Xperia Z1 Compact',
1280, 720, 342, 2.0),
'phone_sony_xperia_z2z3': ('Sony Xperia Z2/Z3', 1920, 1080, 432, 3.0),
'phone_sony_xperia_z3_compact': ('Sony Xperia Z3 Compact',
1280, 720, 313, 2.0),
'tablet_dell_venue_8': ('Dell Venue 8', 2560, 1600, 355, 2.0),
'tablet_ipad': ('iPad', 1024, 768, 132, 1.0),
'tablet_ipad_mini': ('iPad Mini', 1024, 768, 163, 1.0),
'tablet_ipad_mini_retina': ('iPad Mini Retina', 2048, 1536, 326, 2.0),
'tablet_ipad_pro': ('iPad Pro', 2732, 2048, 265, 2.0),
'tablet_ipad_retina': ('iPad Retina', 2048, 1536, 264, 2.0),
'tablet_nexus_10': ('Nexus 10', 2560, 1600, 297, 2.0),
'tablet_nexus_7_12': ('Nexus 7 12', 1280, 800, 216, 1.3),
'tablet_nexus_7_13': ('Nexus 7 13', 1920, 1200, 324, 2.0),
'tablet_nexus_9': ('Nexus 9', 2048, 1536, 288, 2.0),
'tablet_samsung_galaxy_tab_10': ('Samsung Galaxy Tab 10',
1280, 800, 148, 1.0),
'tablet_sony_xperia_z3_tablet': ('Sony Xperia Z3 Tablet',
1920, 1200, 282, 2.0),
'tablet_sony_xperia_z4_tablet': ('Sony Xperia Z4 Tablet',
2560, 1600, 297, 2.0),
'tablet_huawei_mediapad_m3_lite_10': ('HUAWEI MediaPad M3 Lite 10',
1920, 1200, 320, 2.25)
}
def start(win, ctx):
pass
def stop(win, ctx):
pass
def apply_device(device, scale, orientation):
name, width, height, dpi, density = devices[device]
if orientation == 'portrait':
width, height = height, width
Logger.info('Screen: Apply screen settings for {0}'.format(name))
Logger.info('Screen: size={0}x{1} dpi={2} density={3} '
'orientation={4}'.format(width, height, dpi, density,
orientation))
try:
scale = float(scale)
except:
scale = 1
environ['KIVY_METRICS_DENSITY'] = str(density * scale)
environ['KIVY_DPI'] = str(dpi * scale)
Config.set('graphics', 'width', str(int(width * scale)))
# simulate with the android bar
# FIXME should be configurable
Config.set('graphics', 'height', str(int(height * scale - 25 * density)))
Config.set('graphics', 'fullscreen', '0')
Config.set('graphics', 'show_mousecursor', '1')
def usage(device=None):
if device:
Logger.error('Screen: The specified device ({0}) is unknown.',
device)
print('\nModule usage: python main.py -m screen:deviceid[,orientation]\n')
print('Available devices:\n')
print('{0:12} {1:<22} {2:<8} {3:<8} {4:<5} {5:<8}'.format(
'Device ID', 'Name', 'Width', 'Height', 'DPI', 'Density'))
for device, info in devices.items():
print('{0:12} {1:<22} {2:<8} {3:<8} {4:<5} {5:<8}'.format(
device, *info))
print('\n')
print('Simulate a medium-density screen such as Motorola Droid 2:\n')
print(' python main.py -m screen:droid2\n')
print('Simulate a high-density screen such as HTC One X, in portrait:\n')
print(' python main.py -m screen:onex,portrait\n')
print('Simulate the iPad 2 screen\n')
print(' python main.py -m screen:ipad\n')
print('If the generated window is too large, you can specify a scale:\n')
print(' python main.py -m screen:note2,portrait,scale=.75\n')
sys.exit(1)
def configure(ctx):
scale = ctx.pop('scale', None)
orientation = 'landscape'
ctx.pop('landscape', None)
if ctx.pop('portrait', None):
orientation = 'portrait'
if not ctx:
return usage(None)
device = list(ctx.keys())[0]
if device not in devices:
return usage('')
apply_device(device, scale, orientation)
if __name__ == "__main__":
for n in devices.values():
assert n[1] > n[2]

View file

@ -0,0 +1,33 @@
'''
Show border
===========
Shows widget's border.
The idea was taken from
http://robertour.com/2013/10/02/easy-way-debugging-kivy-interfaces/
'''
__all__ = ('start', 'stop')
from kivy.lang import Builder
KV_CODE = '''
<Widget>:
canvas.after:
Color:
rgba: 1, 1, 1, 1
Line:
rectangle: self.x + 1, self.y + 1, self.width - 1, self.height - 1
dash_offset: 5
dash_length: 3
'''
def start(win, ctx):
Builder.load_string(KV_CODE, filename=__file__)
def stop(win, ctx):
Builder.unload_file(__file__)

View file

@ -0,0 +1,97 @@
'''
Touchring
=========
Shows rings around every touch on the surface / screen. You can use this module
to check that you don't have any calibration issues with touches.
Configuration
-------------
:Parameters:
`image`: str, defaults to '<kivy>/data/images/ring.png'
Filename of the image to use.
`scale`: float, defaults to 1.
Scale of the image.
`alpha`: float, defaults to 1.
Opacity of the image.
Example
-------
In your configuration (`~/.kivy/config.ini`), you can add something like
this::
[modules]
touchring = image=mypointer.png,scale=.3,alpha=.7
'''
__all__ = ('start', 'stop')
from kivy.core.image import Image
from kivy.graphics import Color, Rectangle
from kivy import kivy_data_dir
from os.path import join
pointer_image = None
pointer_scale = 1.0
pointer_alpha = 0.7
def _touch_down(win, touch):
ud = touch.ud
with win.canvas.after:
ud['tr.color'] = Color(1, 1, 1, pointer_alpha)
iw, ih = pointer_image.size
ud['tr.rect'] = Rectangle(
pos=(
touch.x - (pointer_image.width / 2. * pointer_scale),
touch.y - (pointer_image.height / 2. * pointer_scale)),
size=(iw * pointer_scale, ih * pointer_scale),
texture=pointer_image.texture)
if not ud.get('tr.grab', False):
ud['tr.grab'] = True
touch.grab(win)
def _touch_move(win, touch):
ud = touch.ud
if not ud.get('tr.rect', False):
_touch_down(win, touch)
ud['tr.rect'].pos = (
touch.x - (pointer_image.width / 2. * pointer_scale),
touch.y - (pointer_image.height / 2. * pointer_scale))
def _touch_up(win, touch):
if touch.grab_current is win:
ud = touch.ud
win.canvas.after.remove(ud['tr.color'])
win.canvas.after.remove(ud['tr.rect'])
if ud.get('tr.grab') is True:
touch.ungrab(win)
ud['tr.grab'] = False
def start(win, ctx):
# XXX use ctx !
global pointer_image, pointer_scale, pointer_alpha
pointer_fn = ctx.config.get('image',
'atlas://data/images/defaulttheme/ring')
pointer_scale = float(ctx.config.get('scale', 1.0))
pointer_alpha = float(ctx.config.get('alpha', 1.0))
pointer_image = Image(pointer_fn)
win.bind(on_touch_down=_touch_down,
on_touch_move=_touch_move,
on_touch_up=_touch_up)
def stop(win, ctx):
win.unbind(on_touch_down=_touch_down,
on_touch_move=_touch_move,
on_touch_up=_touch_up)

View file

@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
'''
Web Debugger
============
.. versionadded:: 1.2.0
.. warning::
This module is highly experimental, use it with care.
This module will start a webserver and run in the background. You can
see how your application evolves during runtime, examine the internal
cache etc.
Run with::
python main.py -m webdebugger
Then open your webbrowser on http://localhost:5000/
'''
__all__ = ('start', 'stop')
import os
if 'KIVY_DOC' not in os.environ:
from kivy.modules._webdebugger import start, stop
else:
start = stop = lambda *x: True