Version

Quick search

Table Of Contents

Source code for kivy.factory

'''
Factory object
==============

The factory can be used to automatically register any class or module
and instantiate classes from it anywhere in your project. It is an
implementation of the
`Factory Pattern <http://en.wikipedia.org/wiki/Factory_pattern>`_.

The class list and available modules are automatically generated by setup.py.

Example for registering a class/module::

    >>> from kivy.factory import Factory
    >>> Factory.register('Widget', module='kivy.uix.widget')
    >>> Factory.register('Vector', module='kivy.vector')

Example of using the Factory::

    >>> from kivy.factory import Factory
    >>> widget = Factory.Widget(pos=(456,456))
    >>> vector = Factory.Vector(9, 2)

Example using a class name::

    >>> from kivy.factory import Factory
    >>> Factory.register('MyWidget', cls=MyWidget)

By default, the first classname you register via the factory is permanent.
If you wish to change the registered class, you need to unregister the
classname before you re-assign it::

    >>> from kivy.factory import Factory
    >>> Factory.register('MyWidget', cls=MyWidget)
    >>> widget = Factory.MyWidget()
    >>> Factory.unregister('MyWidget')
    >>> Factory.register('MyWidget', cls=CustomWidget)
    >>> customWidget = Factory.MyWidget()
'''

__all__ = ('Factory', 'FactoryBase', 'FactoryException')

import copy
import importlib
from kivy.logger import Logger
from kivy.context import register_context


[docs]class FactoryException(Exception): pass
class FactoryBase(object): def __init__(self): super(FactoryBase, self).__init__() self.classes = {} @classmethod def create_from(cls, factory): """Creates a instance of the class, and initializes to the state of ``factory``. :param factory: The factory to initialize from. :return: A new instance of this class. """ obj = cls() obj.classes = copy.copy(factory.classes) return obj def is_template(self, classname): '''Return True if the classname is a template from the :class:`~kivy.lang.Builder`. .. versionadded:: 1.0.5 ''' if classname in self.classes: return self.classes[classname]['is_template'] else: return False def register(self, classname, cls=None, module=None, is_template=False, baseclasses=None, filename=None, warn=False): '''Register a new classname referring to a real class or class definition in a module. Warn, if True will emit a warning message when a class is re-declared. .. versionchanged:: 1.9.0 `warn` was added. .. versionchanged:: 1.7.0 :attr:`baseclasses` and :attr:`filename` added .. versionchanged:: 1.0.5 :attr:`is_template` has been added in 1.0.5. ''' if cls is None and module is None and baseclasses is None: raise ValueError( 'You must specify either cls= or module= or baseclasses =') if classname in self.classes: if warn: info = self.classes[classname] Logger.warning('Factory: Ignored class "{}" re-declaration. ' 'Current - module: {}, cls: {}, baseclass: {}, filename: {}. ' 'Ignored - module: {}, cls: {}, baseclass: {}, filename: {}.'. format(classname, info['module'], info['cls'], info['baseclasses'], info['filename'], module, cls, baseclasses, filename)) return self.classes[classname] = { 'module': module, 'cls': cls, 'is_template': is_template, 'baseclasses': baseclasses, 'filename': filename} def unregister(self, *classnames): '''Unregisters the classnames previously registered via the register method. This allows the same classnames to be re-used in different contexts. .. versionadded:: 1.7.1 ''' for classname in classnames: if classname in self.classes: self.classes.pop(classname) def unregister_from_filename(self, filename): '''Unregister all the factory objects related to the filename passed in the parameter. .. versionadded:: 1.7.0 ''' to_remove = [x for x in self.classes if self.classes[x]['filename'] == filename] for name in to_remove: del self.classes[name] def __getattr__(self, name): classes = self.classes if name not in classes: if name[0] == name[0].lower(): # if trying to access attributes like checking for `bind` # then raise AttributeError raise AttributeError( 'First letter of class name <%s> is in lowercase' % name) raise FactoryException('Unknown class <%s>' % name) item = classes[name] cls = item['cls'] # No class to return, import the module if cls is None: if item['module']: module = importlib.__import__( name=item['module'], fromlist='*', level=0 # force absolute ) if not hasattr(module, name): raise FactoryException( 'No class named <%s> in module <%s>' % ( name, item['module'])) cls = item['cls'] = getattr(module, name) elif item['baseclasses']: rootwidgets = [] for basecls in item['baseclasses'].split('+'): rootwidgets.append(Factory.get(basecls)) cls = item['cls'] = type(str(name), tuple(rootwidgets), {}) else: raise FactoryException('No information to create the class') return cls get = __getattr__ #: Factory instance to use for getting new classes Factory: FactoryBase = register_context('Factory', FactoryBase) # Now import the file with all registers # automatically generated by build_factory import kivy.factory_registers # NOQA Logger.info('Factory: %d symbols loaded' % len(Factory.classes)) if __name__ == '__main__': Factory.register('Vector', module='kivy.vector') Factory.register('Widget', module='kivy.uix.widget')