1# ----------------------------------------------------------------------------- 2# 3# Copyright (c) 2006, Enthought, Inc. 4# All rights reserved. 5# 6# This software is provided without warranty under the terms of the BSD 7# license included in LICENSE.txt and may be redistributed only 8# under the conditions described in the aforementioned license. The license 9# is also available online at http://www.enthought.com/licenses/BSD.txt 10# 11# Thanks for using Enthought open source! 12# 13# Author: Dave Peterson <dpeterson@enthought.com> 14# 15# ----------------------------------------------------------------------------- 16 17""" 18A handler that delegates the handling of events to a set of sub-handlers. 19 20This is typically used as the handler for dynamic views. See the 21**traits.has_dynamic_view** module. 22""" 23 24# Enthought library imports 25from traits.api import HasTraits, List 26from .ui import Dispatcher 27 28# Local imports. 29from .handler import Handler 30 31# Set up a logger: 32import logging 33 34logger = logging.getLogger(__name__) 35 36 37class DelegatingHandler(Handler): 38 """ A handler that delegates the handling of events to a set of 39 sub-handlers. 40 """ 41 42 # -- Public 'DelegatingHandler' Interface --------------------------------- 43 44 #: The list of sub-handlers this object delegates to: 45 sub_handlers = List(HasTraits) 46 47 # -- Protected 'DelegatingHandler' Interface ------------------------------ 48 49 #: A list of dispatchable handler methods: 50 _dispatchers = List() 51 52 # ------------------------------------------------------------------------- 53 # 'Handler' interface: 54 # ------------------------------------------------------------------------- 55 56 # -- Public Methods ------------------------------------------------------- 57 58 def closed(self, info, is_ok): 59 """ Handles the user interface being closed by the user. 60 61 This method is overridden here to unregister any dispatchers that 62 were set up in the *init()* method. 63 """ 64 for d in self._dispatchers: 65 d.remove() 66 67 def init(self, info): 68 """ Initializes the controls of a user interface. 69 70 This method is called after all user interface elements have been 71 created, but before the user interface is displayed. Use this method to 72 further customize the user interface before it is displayed. 73 74 This method is overridden here to delegate to sub-handlers. 75 76 Parameters 77 ---------- 78 info : *UIInfo* object 79 The UIInfo object associated with the view 80 81 Returns 82 ------- 83 initialized : bool 84 A boolean, indicating whether the user interface was successfully 85 initialized. A True value indicates that the UI can be displayed; 86 a False value indicates that the display operation should be 87 cancelled. 88 """ 89 90 # Iterate through our sub-handlers, and for each method whose name is 91 # of the form 'object_name_changed', where 'object' is the name of an 92 # object in the UI's context, create a trait notification handler that 93 # will call the method whenever object's 'name' trait changes. 94 logger.debug("Initializing delegation in DelegatingHandler [%s]", self) 95 context = info.ui.context 96 for h in self.sub_handlers: 97 # fixme: I don't know why this wasn't here before... I'm not 98 # sure this is right! 99 h.init(info) 100 101 for name in self._each_trait_method(h): 102 if name[-8:] == "_changed": 103 prefix = name[:-8] 104 col = prefix.find("_", 1) 105 if col >= 0: 106 object = context.get(prefix[:col]) 107 if object is not None: 108 logger.debug( 109 "\tto method [%s] on handler[%s]", name, h 110 ) 111 method = getattr(h, name) 112 trait_name = prefix[col + 1:] 113 self._dispatchers.append( 114 Dispatcher(method, info, object, trait_name) 115 ) 116 117 # Also invoke the method immediately so initial 118 # user interface state can be correctly set. 119 if object.base_trait(trait_name).type != "event": 120 method(info) 121 122 # fixme: These are explicit workarounds for problems with:- 123 # 124 # 'GeometryHierarchyViewHandler' 125 # 126 # which is used in the :- 127 # 128 # 'GeometryHierarchyTreeEditor' 129 # 130 # which are in the 'encode.cad.ui.geometry' package. 131 # 132 # The tree editor has dynamic views, and hence the handler gets 133 # wrapped by a 'DelegatingHandler'. Unfortunately the handler 134 # has a couple of methods that aren't picked up by the usual 135 # wrapping strategy:- 136 # 137 # 1) 'tree_item_selected' 138 # 139 # - which is obviously called when a tree item is selected. 140 # 141 # 2) 'inspect_object' 142 # 143 # - which is called directly as as action from the context menu 144 # defined in the tree editor. 145 # 146 elif name in ["tree_item_selected", "inspect_object"]: 147 self.__dict__[name] = self._create_delegate(h, name) 148 149 return True 150 151 def _create_delegate(self, h, name): 152 """ Quick fix for handler methods that are currently left out! 153 """ 154 155 def delegate(*args, **kw): 156 method = getattr(h, name) 157 return method(*args, **kw) 158 159 return delegate 160