first commit
This commit is contained in:
commit
417e54da96
5696 changed files with 900003 additions and 0 deletions
300
kivy_venv/lib/python3.11/site-packages/kivy/modules/__init__.py
Normal file
300
kivy_venv/lib/python3.11/site-packages/kivy/modules/__init__.py
Normal 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())
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
1059
kivy_venv/lib/python3.11/site-packages/kivy/modules/console.py
Normal file
1059
kivy_venv/lib/python3.11/site-packages/kivy/modules/console.py
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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)
|
755
kivy_venv/lib/python3.11/site-packages/kivy/modules/inspector.py
Normal file
755
kivy_venv/lib/python3.11/site-packages/kivy/modules/inspector.py
Normal 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
|
299
kivy_venv/lib/python3.11/site-packages/kivy/modules/joycursor.py
Normal file
299
kivy_venv/lib/python3.11/site-packages/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
|
|
@ -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)
|
|
@ -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)
|
|
@ -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()
|
183
kivy_venv/lib/python3.11/site-packages/kivy/modules/screen.py
Normal file
183
kivy_venv/lib/python3.11/site-packages/kivy/modules/screen.py
Normal 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]
|
|
@ -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__)
|
|
@ -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)
|
|
@ -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
|
Loading…
Add table
Add a link
Reference in a new issue