#------------------------------------------------------------------------------
# Copyright (c) 2013, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
#------------------------------------------------------------------------------
from atom.api import ChangeType, Event, Typed, Str, observe as default_observe
from atom.datastructures.api import sortedmap
from .declarative_meta import DeclarativeMeta
from .expression_engine import ExpressionEngine
from .object import Object, flag_generator, flag_property
#: Declarative observe ignores create events
OBSERVE_CHANGE_TYPES = ChangeType.ANY & ~ChangeType.CREATE
def observe(*names, change_types=OBSERVE_CHANGE_TYPES):
""" An observe decorator which ignores the created event by default.
Parameters
----------
*names
The str names of the attributes to observe on the object.
These must be of the form 'foo' or 'foo.bar'.
change_types
The flag specifying the type of changes to observe.
"""
return default_observe(*names, change_types=change_types)
[docs]
def d_(member, readable=True, writable=True, final=True):
""" Mark an Atom member as bindable from Enaml syntax.
Parameters
----------
member : Member
The atom member to mark as bindable from Enaml syntax.
readable : bool, optional
Whether the member is readable from Enaml syntax. The member
must be readable to use the '>>', ':=', and '::' operators.
The default is True.
writable : bool, optional
Whether the member is writable from Enaml syntax. The member
must be writable to use the '=', '<<', and ':=' operators.
The default is True.
final : bool, optional
Whether or not the member can be redefined from Enaml syntax
using the 'attr' keyword. The default is True and indicates
that the member cannot be overridden.
"""
metadata = member.metadata
if metadata is None:
metadata = member.metadata = {}
metadata['d_member'] = True
metadata['d_readable'] = readable
metadata['d_writable'] = writable
metadata['d_final'] = final
return member
def d_func(func):
""" Mark a method as overridable from Enaml syntax.
Parameters
----------
func : FunctionType
The function to tag as declarative.
Returns
-------
result : func
The original function tagged with the compiler metadata.
"""
func._d_func = True
return func
#: The flag indicating that the Declarative object has been initialized.
INITIALIZED_FLAG = next(flag_generator)
[docs]
class Declarative(Object, metaclass=DeclarativeMeta):
""" The most base class of the Enaml declarative objects.
This class provides the core functionality required of declarative
Enaml types. It can be used directly in a declarative Enaml object
tree to store and react to state changes. It has no concept of a
visual representation; that functionality is added by subclasses.
"""
#: Export the 'name' attribute as a declarative member.
name = d_(Str())
#: An event fired when an object is initialized. It is triggered
#: once during the object lifetime, at the end of the initialize
#: method.
initialized = d_(Event(), writable=False)
#: A property which gets and sets the initialized flag. This should
#: not be manipulated directly by user code.
is_initialized = flag_property(INITIALIZED_FLAG)
#: Storage space for the declarative runtime. This value should not
#: be manipulated by user code.
_d_storage = Typed(sortedmap, ())
#: Storage space for the declarative engine. This value should not
#: be manipulated by user code.
_d_engine = Typed(ExpressionEngine)
[docs]
def initialize(self):
""" Initialize this object all of its children recursively.
This is called to give the objects in the tree the opportunity
to initialize additional state which depends upon the object
tree being fully built. It is the responsibility of external
code to call this method at the appropriate time. This will
emit the `initialized` signal after all of the children have
been initialized.
"""
# Iterate over a copy since the children add and remove
# other children during initialization.
for child in self.children[:]:
if isinstance(child, Declarative):
child.initialize()
self.is_initialized = True
self.initialized()
[docs]
def destroy(self):
""" An overridden destructor method for declarative cleanup.
"""
self.is_initialized = False
del self._d_storage
del self._d_engine
super(Declarative, self).destroy()
[docs]
def child_added(self, child):
""" An overridden child added event handler.
This handler will automatically initialize a declarative child
if this object itself has already been initialized.
"""
super(Declarative, self).child_added(child)
if isinstance(child, Declarative):
if self.is_initialized and not child.is_initialized:
child.initialize()