1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
|
#------------------------------------------------------------------------------
# Copyright (c) 2013-2025, 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 Atom, Event, Typed, ForwardTyped
from enaml.application import Application
from enaml.core.declarative import Declarative, d_
from enaml.core.object import flag_generator, flag_property
class ProxyToolkitObject(Atom):
""" The base class of all proxy toolkit objects.
A ProxyToolkitObject is responsible for the communication between
the Declarative declaration of the object and then implementation
object which actually performs the behavior.
Initialization of proxy is backend dependent behavior. Most uses
will want to initialize the entire proxy tree using traversals
which are appropriate for their use case. The top level Window
widget provides a entry point method into the proxy tree for this
to occur.
"""
#: A reference to the ToolkitObject declaration.
declaration = ForwardTyped(lambda: ToolkitObject)
@property
def is_active(self):
""" Test whether or not the proxy is fully activated.
"""
return self.declaration.proxy_is_active
def activate_top_down(self):
""" A method called by the declaration to activate the proxy.
This method is called in top-down order, during the descent
phase of the declaration's proxy activation pass.
"""
pass
def activate_bottom_up(self):
""" A method called by the declaration to activate the proxy.
This method is called in bottom-up order, during the ascent
phase of the declaration's proxy activation pass.
"""
pass
def destroy(self):
""" Destroy the proxy and any of its resources.
This method is called by the declaration when it is destroyed.
It should be reimplemented by subclasses when more control
is required.
"""
del self.declaration
def parent(self):
""" Get the parent proxy object for this object.
Returns
-------
result : ProxyToolkitObject or None
The parent toolkit object of this object, or None if no
such parent exists.
"""
d = self.declaration.parent
if isinstance(d, ToolkitObject):
return d.proxy
def children(self):
""" Get the child objects for this object.
Returns
-------
result : generator
A generator which yields the child proxy objects.
"""
for d in self.declaration.children:
if isinstance(d, ToolkitObject):
yield d.proxy
def child_added(self, child):
""" Handle a child being added to the object.
This method will only be called after the proxy tree is active
and the UI is running. The default implementation is a no-op.
Parameters
----------
child : ProxyToolkitObject
The toolkit proxy child added to the object.
"""
pass
def child_removed(self, child):
""" Handle a child being removed from the object.
This method will only be called after the proxy tree is active
and the UI is running. Notably, it will not be called when the
child is removed because it was destroyed. To handle that case,
reimplement the 'destroy' method in a subclass. The default
implementation is a no-op.
Parameters
----------
child : ProxyToolkitObject
The toolkit proxy removed the object.
"""
pass
#: A flag indicating that the object's proxy is ready for use.
ACTIVE_PROXY_FLAG = next(flag_generator)
class ToolkitObject(Declarative):
""" The base class of all toolkit objects in Enaml.
"""
#: An event fired when an object's proxy is activated. It is
#: triggered once during the object lifetime, at the end of the
#: activate_proxy method.
activated = d_(Event(), writable=False)
#: A reference to the ProxyToolkitObject
proxy = Typed(ProxyToolkitObject)
#: A property which gets and sets the active proxy flag. This should
#: not be manipulated directly by user code. This flag will be set to
#: True by external code after the proxy widget hierarchy is setup.
proxy_is_active = flag_property(ACTIVE_PROXY_FLAG)
def initialize(self):
""" A reimplemented initializer.
This initializer will invoke the application to create the
proxy if one has not already been provided.
"""
if not self.proxy:
app = Application.instance()
if app is None:
msg = 'cannot create a proxy without an active Application'
raise RuntimeError(msg)
self.proxy = app.create_proxy(self)
super(ToolkitObject, self).initialize()
def destroy(self):
""" A reimplemented destructor.
This destructor invokes the 'destroy' method on the proxy
toolkit object.
"""
super(ToolkitObject, self).destroy()
self.proxy_is_active = False
if self.proxy:
self.proxy.destroy()
del self.proxy
def child_added(self, child):
""" A reimplemented child added event handler.
This handler will invoke the superclass handler and then invoke
the 'child_added()' method on an active proxy.
"""
super(ToolkitObject, self).child_added(child)
if isinstance(child, ToolkitObject) and self.proxy_is_active:
if not child.proxy_is_active:
child.activate_proxy()
self.proxy.child_added(child.proxy)
def child_removed(self, child):
""" A reimplemented child removed event handler.
This handler will invoke the superclass handler and then invoke
the 'child_removed()' method on an active proxy. The method on
the active proxy will be called even if the child proxy has been
destroyed.
"""
super(ToolkitObject, self).child_removed(child)
if isinstance(child, ToolkitObject) and self.proxy_is_active:
self.proxy.child_removed(child.proxy)
def activate_proxy(self):
""" Activate the proxy object tree.
This method should be called by a node to activate the proxy
tree by making two initialization passes over the tree, from
this node downward. This method is automatically at the proper
times and should not normally need to be invoked by user code.
"""
self.activate_top_down()
for child in self.children:
if isinstance(child, ToolkitObject):
child.activate_proxy()
self.activate_bottom_up()
self.proxy_is_active = True
self.activated()
def activate_top_down(self):
""" Initialize the proxy on the top-down activation pass.
By default, this method calls the 'activate_top_down' method on the
proxy object. It may be reimplemented by subclasses which wish
to perform toolkit-specific initialization.
"""
self.proxy.activate_top_down()
def activate_bottom_up(self):
""" Initialize the proxy on the bottom-up activation pass.
By default, this method calls the 'activate_bottom_up' method on the
proxy object. It may be reimplemented by subclasses which wish
to perform toolkit-specific initialization.
"""
self.proxy.activate_bottom_up()
#--------------------------------------------------------------------------
# Private API
#--------------------------------------------------------------------------
def _update_proxy(self, change):
""" Update the proxy widget when the Widget data changes.
This method only updates the proxy when an attribute is updated;
not when it is created or deleted. It is useful for subclasses
as a base observer handler
"""
if change['type'] == 'update' and self.proxy_is_active:
handler = getattr(self.proxy, 'set_' + change['name'], None)
if handler is not None:
handler(change['value'])
|