300 lines
9.2 KiB
Python
300 lines
9.2 KiB
Python
'''
|
|
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 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 = __import__(name=modname)
|
|
module = sys.modules[modname]
|
|
except ImportError:
|
|
try:
|
|
module = __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())
|