Changeset 11639:ad4d94a25e19 in orange


Ignore:
Timestamp:
07/23/13 15:08:45 (9 months ago)
Author:
Ales Erjavec <ales.erjavec@…>
Branch:
default
Message:

Refactored the OWWidget management out of WidgetsScheme and SignalManager.

Location:
Orange/OrangeCanvas
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • Orange/OrangeCanvas/document/schemeedit.py

    r11616 r11639  
    12361236    def __onNodeActivate(self, item): 
    12371237        node = self.__scene.node_for_item(item) 
    1238         widget = self.scheme().widget_for_node[node] 
     1238        widget = self.scheme().widget_for_node(node) 
    12391239        widget.show() 
    12401240        widget.raise_() 
  • Orange/OrangeCanvas/scheme/signalmanager.py

    r11467 r11639  
    169169 
    170170    def on_node_removed(self, node): 
    171         # remove all pending input signals for node so we don't get stale 
    172         # references in process_node 
    173         # NOTE: This does not remove output signals this node. In particular 
    174         # the final 'None' values might be left on the queue. 
     171        # remove all pending input signals for node so we don't get 
     172        # stale references in process_node. 
     173        # NOTE: This does not remove output signals for this node. In 
     174        # particular the final 'None' will be delivered to the sink 
     175        # nodes even after the source node is no longer in the scheme. 
    175176        log.info("Node %r removed. Removing pending signals.", 
    176177                 node.title) 
  • Orange/OrangeCanvas/scheme/widgetsscheme.py

    r11623 r11639  
    2121 
    2222import sip 
    23 from PyQt4.QtGui import QShortcut, QKeySequence, QWhatsThisClickedEvent 
    24 from PyQt4.QtCore import Qt, QCoreApplication, QEvent, SIGNAL 
     23from PyQt4.QtGui import ( 
     24    QShortcut, QKeySequence, QWhatsThisClickedEvent, QWidget 
     25) 
     26 
     27from PyQt4.QtCore import Qt, QObject, QCoreApplication, QEvent, SIGNAL 
     28from PyQt4.QtCore import pyqtSignal as Signal 
    2529 
    2630from .signalmanager import SignalManager, compress_signals, can_enable_dynamic 
    2731from .scheme import Scheme, SchemeNode 
    2832from .node import UserMessage 
    29 from ..utils import name_lookup, check_arg, check_type 
     33from ..utils import name_lookup 
    3034from ..resources import icon_loader 
    31 from ..config import rc 
    3235 
    3336log = logging.getLogger(__name__) 
     
    4851        Scheme.__init__(self, parent, title, description) 
    4952 
    50         self.widgets = [] 
    51         self.widget_for_node = {} 
    52         self.node_for_widget = {} 
    5353        self.signal_manager = WidgetsSignalManager(self) 
    54         self.signal_manager.processingStarted[SchemeNode].connect( 
     54        self.widget_manager = WidgetManager(self) 
     55        self.widget_manager.set_scheme(self) 
     56 
     57    def widget_for_node(self, node): 
     58        """ 
     59        Return the OWWidget instance for a `node` 
     60        """ 
     61        return self.widget_manager.widget_for_node(node) 
     62 
     63    def node_for_widget(self, widget): 
     64        """ 
     65        Return the SchemeNode instance for the `widget`. 
     66        """ 
     67        return self.widget_manager.node_for_widget(widget) 
     68 
     69    def sync_node_properties(self): 
     70        """ 
     71        Sync the widget settings/properties with the SchemeNode.properties. 
     72        Return True if there were any changes in the properties (i.e. if the 
     73        new node.properties differ from the old value) and False otherwise. 
     74 
     75        .. note:: this should hopefully be removed in the feature, when the 
     76            widget can notify a changed setting property. 
     77 
     78        """ 
     79        changed = False 
     80        for node in self.nodes: 
     81            widget = self.widget_for_node(node) 
     82            settings = widget.getSettings(alsoContexts=False) 
     83            if settings != node.properties: 
     84                node.properties = settings 
     85                changed = True 
     86        log.debug("Scheme node properties sync (changed: %s)", changed) 
     87        return changed 
     88 
     89    def save_to(self, stream, pretty=True, pickle_fallback=False): 
     90        """ 
     91        Reimplemented from :func:`Scheme.save_to`. 
     92        """ 
     93        self.sync_node_properties() 
     94        Scheme.save_to(self, stream, pretty, pickle_fallback) 
     95 
     96 
     97class WidgetManager(QObject): 
     98    """ 
     99    OWWidget instance manager class. 
     100 
     101    This class handles the lifetime of OWWidget instances in a 
     102    :class:`WidgetsScheme`. 
     103 
     104    """ 
     105    #: A new OWWidget was created and added by the manager. 
     106    widget_for_node_added = Signal(SchemeNode, QWidget) 
     107 
     108    #: An OWWidget was removed, hidden and will be deleted when appropriate. 
     109    widget_for_node_removed = Signal(SchemeNode, QWidget) 
     110 
     111    #: Widget processing state flags: 
     112    #:   * InputUpdate - signal manager is updating/setting the 
     113    #:     widget's inputs 
     114    #:   * BlockingUpdate - widget has entered a blocking state 
     115    InputUpdate, BlockingUpdate = 1, 2 
     116 
     117    def __init__(self, parent): 
     118        QObject.__init__(self, parent) 
     119        self.__scheme = None 
     120        self.__signal_manager = None 
     121        self.__widgets = [] 
     122        self.__widget_for_node = {} 
     123        self.__node_for_widget = {} 
     124 
     125        # Widgets that were 'removed' from the scheme but were at 
     126        # the time in an input update loop and could not be deleted 
     127        # immediately 
     128        self.__delay_delete = set() 
     129 
     130        # processing state flags for all nodes (including the ones 
     131        # in __delay_delete). 
     132        self.__widget_processing_state = {} 
     133 
     134        # Tracks the widget in the update loop by the SignalManager 
     135        self.__updating_widget = None 
     136 
     137    def set_scheme(self, scheme): 
     138        """ 
     139        Set the :class:`WidgetsScheme` instance to manage. 
     140        """ 
     141        self.__scheme = scheme 
     142        self.__signal_manager = scheme.findChild(SignalManager) 
     143 
     144        self.__signal_manager.processingStarted[SchemeNode].connect( 
    55145            self.__on_processing_started 
    56146        ) 
    57         self.signal_manager.processingFinished[SchemeNode].connect( 
     147        self.__signal_manager.processingFinished[SchemeNode].connect( 
    58148            self.__on_processing_finished 
    59149        ) 
    60  
    61     def add_node(self, node): 
    62         """ 
    63         Add a `SchemeNode` instance to the scheme and create/initialize the 
    64         OWBaseWidget instance for it. 
    65  
    66         """ 
    67         check_arg(node not in self.nodes, "Node already in scheme.") 
    68         check_type(node, SchemeNode) 
    69  
    70         # Create the widget before a call to Scheme.add_node in 
    71         # case someone connected to node_added already expects 
    72         # widget_for_node, etc. to be up to date. 
     150        scheme.node_added.connect(self.add_widget_for_node) 
     151        scheme.node_removed.connect(self.remove_widget_for_node) 
     152        scheme.installEventFilter(self) 
     153 
     154    def scheme(self): 
     155        """ 
     156        Return the scheme instance on which this manager is installed. 
     157        """ 
     158        return self.__scheme 
     159 
     160    def signal_manager(self): 
     161        """ 
     162        Return the signal manager in use on the :func:`scheme`. 
     163        """ 
     164        return self.__signal_manager 
     165 
     166    def widget_for_node(self, node): 
     167        """ 
     168        Return the OWWidget instance for the scheme node. 
     169        """ 
     170        return self.__widget_for_node[node] 
     171 
     172    def node_for_widget(self, widget): 
     173        """ 
     174        Return the SchemeNode instance for the OWWidget. 
     175 
     176        Raise a KeyError if the widget does not map to a node in the scheme. 
     177        """ 
     178        return self.__node_for_widget[widget] 
     179 
     180    def add_widget_for_node(self, node): 
     181        """ 
     182        Create a new OWWidget instance for the corresponding scheme node. 
     183        """ 
    73184        widget = self.create_widget_instance(node) 
    74         Scheme.add_node(self, node) 
    75  
    76         self.widgets.append(widget) 
    77  
    78     def remove_node(self, node): 
    79         Scheme.remove_node(self, node) 
    80         widget = self.widget_for_node[node] 
    81  
    82         self.signal_manager.on_node_removed(node) 
    83  
    84         del self.widget_for_node[node] 
    85         del self.node_for_widget[widget] 
    86  
     185 
     186        self.__widgets.append(widget) 
     187        self.__widget_for_node[node] = widget 
     188        self.__node_for_widget[widget] = node 
     189 
     190        self.widget_for_node_added.emit(node, widget) 
     191 
     192    def remove_widget_for_node(self, node): 
     193        """ 
     194        Remove the OWWidget instance for node. 
     195        """ 
     196        widget = self.widget_for_node(node) 
     197 
     198        self.__widgets.remove(widget) 
     199        del self.__widget_for_node[node] 
     200        del self.__node_for_widget[widget] 
     201 
     202        self.widget_for_node_removed.emit(node, widget) 
     203 
     204        self._delete_widget(widget) 
     205 
     206    def _delete_widget(self, widget): 
     207        """ 
     208        Delete the OWBaseWidget instance. 
     209        """ 
    87210        widget.close() 
    88211 
     
    93216        # Notify the widget it will be deleted. 
    94217        widget.onDeleteWidget() 
    95         # And schedule it for deletion. 
    96         widget.deleteLater() 
    97  
    98     def add_link(self, link): 
    99         Scheme.add_link(self, link) 
    100         self.signal_manager.link_added(link) 
    101  
    102     def remove_link(self, link): 
    103         Scheme.remove_link(self, link) 
    104         self.signal_manager.link_removed(link) 
     218 
     219        if self.__widget_processing_state[widget] != 0: 
     220            # If the widget is in an update loop and/or blocking we 
     221            # delay the scheduled deletion until the widget is done. 
     222            self.__delay_delete.add(widget) 
     223        else: 
     224            widget.deleteLater() 
    105225 
    106226    def create_widget_instance(self, node): 
    107227        """ 
    108         Create a OWBaseWidget instance for the node. 
     228        Create a OWWidget instance for the node. 
    109229        """ 
    110230        desc = node.description 
     
    114234        widget = klass.__new__( 
    115235            klass, 
    116             _owInfo=rc.get("canvas.show-state-info", True), 
    117             _owWarning=rc.get("canvas.show-state-warning", True), 
    118             _owError=rc.get("canvas.show-state-error", True), 
    119             _owShowStatus=rc.get("OWWidget.show-status", True), 
    120             _useContexts=rc.get("OWWidget.use-contexts", True), 
     236            _owInfo=True, 
     237            _owWarning=True, 
     238            _owError=True, 
     239            _owShowStatus=True, 
     240            _useContexts=True, 
    121241            _category=desc.category, 
    122242            _settingsFromSchema=node.properties 
    123243        ) 
    124244 
    125         # Add the node/widget mapping s before calling __init__ 
     245        # Init the node/widget mapping and state before calling __init__ 
    126246        # Some OWWidgets might already send data in the constructor 
    127         # (should this be forbidden? Raise a warning?) 
    128         self.signal_manager.on_node_added(node) 
    129  
    130         self.widget_for_node[node] = widget 
    131         self.node_for_widget[widget] = node 
    132  
    133         widget.__init__(None, self.signal_manager) 
     247        # (should this be forbidden? Raise a warning?) triggering the signal 
     248        # manager which would request the widget => node mapping or state 
     249        self.__widget_for_node[node] = widget 
     250        self.__node_for_widget[widget] = node 
     251        self.__widget_processing_state[widget] = 0 
     252 
     253        widget.__init__(None, self.signal_manager()) 
    134254        widget.setCaption(node.title) 
    135255        widget.widgetInfo = desc 
     
    149269        self.connect(widget, 
    150270                     SIGNAL("blockingStateChanged(bool)"), 
    151                      self.signal_manager._update) 
     271                     self.__on_blocking_state_changed) 
    152272 
    153273        # Install a help shortcut on the widget 
    154274        help_shortcut = QShortcut(QKeySequence("F1"), widget) 
    155275        help_shortcut.activated.connect(self.__on_help_request) 
     276 
    156277        return widget 
    157278 
    158     def widget_settings(self): 
    159         """Return a list of dictionaries with widget settings. 
    160         """ 
    161         return [self.widget_for_node[node].getSettings(alsoContexts=False) 
    162                 for node in self.nodes] 
     279    def node_processing_state(self, node): 
     280        """ 
     281        Return the processing state flags for the node. 
     282 
     283        Same as `manager.node_processing_state(manger.widget_for_node(node))` 
     284 
     285        """ 
     286        widget = self.widget_for_node(node) 
     287        return self.__widget_processing_state[widget] 
     288 
     289    def widget_processing_state(self, widget): 
     290        """ 
     291        Return the processing state flags for the widget. 
     292 
     293        The state is an bitwise or of `InputUpdate` and `BlockingUpdate`. 
     294 
     295        """ 
     296        return self.__widget_processing_state[widget] 
     297 
     298    def eventFilter(self, receiver, event): 
     299        if receiver is self.__scheme and event.type() == QEvent.Close: 
     300            self.signal_manager().stop() 
     301 
     302            # Notify the widget instances. 
     303            for widget in self.__widget_for_node.values(): 
     304                widget.close() 
     305 
     306                if not widget._settingsFromSchema: 
     307                    # First save global settings if necessary. 
     308                    widget.saveSettings() 
     309 
     310                widget.onDeleteWidget() 
     311 
     312            event.accept() 
     313            return True 
     314 
     315        return QObject.eventFilter(self, receiver, event) 
     316 
     317    def __on_help_request(self): 
     318        """ 
     319        Help shortcut was pressed. We send a `QWhatsThisClickedEvent` to 
     320        the scheme and hope someone responds to it. 
     321 
     322        """ 
     323        # Sender is the QShortcut, and parent the OWBaseWidget 
     324        widget = self.sender().parent() 
     325        try: 
     326            node = self.node_for_widget(widget) 
     327        except KeyError: 
     328            pass 
     329        else: 
     330            url = "help://search?id={0}".format(node.description.id) 
     331            event = QWhatsThisClickedEvent(url) 
     332            QCoreApplication.sendEvent(self.scheme(), event) 
    163333 
    164334    def __on_widget_state_changed(self, message_type, message_id, 
     
    174344        """ 
    175345        widget = self.sender() 
    176         node = self.node_for_widget.get(widget) 
    177         if node is not None: 
     346        try: 
     347            node = self.node_for_widget(widget) 
     348        except KeyError: 
     349            pass 
     350        else: 
    178351            message_type = str(message_type) 
    179352            if message_type == "Info": 
     
    194367            message = UserMessage(contents, severity=level, 
    195368                                  message_id=message_type, 
    196                                   data={"contents-type": "text/html"}) 
     369                                  data={"content-type": "text/html"}) 
    197370            node.set_state_message(message) 
    198371 
    199     def sync_node_properties(self): 
    200         """Sync the widget settings/properties with the SchemeNode.properties. 
    201         Return True if there were any changes in the properties (i.e. if the 
    202         new node.properties differ from the old value) and False otherwise. 
    203  
    204         .. note:: this should hopefully be removed in the feature, when the 
    205             widget can notify a changed setting property. 
    206  
    207         """ 
    208         changed = False 
    209         for node in self.nodes: 
    210             widget = self.widget_for_node[node] 
    211             settings = widget.getSettings(alsoContexts=False) 
    212             if settings != node.properties: 
    213                 node.properties = settings 
    214                 changed = True 
    215         log.debug("Scheme node properties sync (changed: %s)", changed) 
    216         return changed 
    217  
    218     def save_to(self, stream, pretty=True, pickle_fallback=False): 
    219         self.sync_node_properties() 
    220         Scheme.save_to(self, stream, pretty, pickle_fallback) 
    221  
    222     def event(self, event): 
    223         """ 
    224         Reimplemented from `QObject.event`. 
    225  
    226         Responds to QEvent.Close event by stopping signal processing and 
    227         closing all widgets. 
    228  
    229         """ 
    230         if event.type() == QEvent.Close: 
    231             self.signal_manager.stop() 
    232  
    233             # Notify the widget instances. 
    234             for widget in self.widget_for_node.values(): 
    235                 widget.close() 
    236  
    237                 if not widget._settingsFromSchema: 
    238                     # First save global settings if necessary. 
    239                     widget.saveSettings() 
    240  
    241                 widget.onDeleteWidget() 
    242  
    243             event.accept() 
    244             return True 
     372    def __on_processing_started(self, node): 
     373        """ 
     374        Signal manager entered the input update loop for the node. 
     375        """ 
     376        widget = self.widget_for_node(node) 
     377        # Remember the widget instance. The node and the node->widget mapping 
     378        # can be removed between this and __on_processing_finished. 
     379        self.__updating_widget = widget 
     380        self.__widget_processing_state[widget] |= self.InputUpdate 
     381        self.__update_node_processing_state(node) 
     382 
     383    def __on_processing_finished(self, node): 
     384        """ 
     385        Signal manager exited the input update loop for the node. 
     386        """ 
     387        widget = self.__updating_widget 
     388        self.__widget_processing_state[widget] &= ~self.InputUpdate 
     389 
     390        if widget in self.__node_for_widget: 
     391            self.__update_node_processing_state(node) 
     392        elif widget in self.__delay_delete: 
     393            self.__try_delete(widget) 
    245394        else: 
    246             return Scheme.event(self, event) 
    247  
    248     def __on_help_request(self): 
    249         """ 
    250         Help shortcut was pressed. We send a `QWhatsThisClickedEvent` and 
    251         hope someone responds to it. 
    252  
    253         """ 
    254         # Sender is the QShortcut, and parent the OWBaseWidget 
    255         widget = self.sender().parent() 
    256         node = self.node_for_widget.get(widget) 
    257         if node: 
    258             url = "help://search?id={0}".format(node.description.id) 
    259             event = QWhatsThisClickedEvent(url) 
    260             QCoreApplication.sendEvent(self, event) 
    261  
    262     def __on_processing_started(self, node): 
    263         node.set_processing_state(1) 
    264  
    265     def __on_processing_finished(self, node): 
    266         node.set_processing_state(0) 
     395            raise ValueError("%r is not managed" % widget) 
     396 
     397        self.__updating_widget = None 
     398 
     399    def __on_blocking_state_changed(self, state): 
     400        """ 
     401        OWWidget blocking state has changed. 
     402        """ 
     403        if not state: 
     404            # schedule an update pass. 
     405            self.signal_manager()._update() 
     406 
     407        widget = self.sender() 
     408        if state: 
     409            self.__widget_processing_state[widget] |= self.BlockingUpdate 
     410        else: 
     411            self.__widget_processing_state[widget] &= ~self.BlockingUpdate 
     412 
     413        if widget in self.__node_for_widget: 
     414            node = self.node_for_widget(widget) 
     415            self.__update_node_processing_state(node) 
     416 
     417        elif widget in self.__delay_delete: 
     418            self.__try_delete(widget) 
     419 
     420    def __update_node_processing_state(self, node): 
     421        """ 
     422        Update the `node.processing_state` to reflect the widget state. 
     423        """ 
     424        state = self.node_processing_state(node) 
     425        node.set_processing_state(1 if state else 0) 
     426 
     427    def __try_delete(self, widget): 
     428        if self.__widget_processing_state[widget] == 0: 
     429            self.__delay_delete.remove(widget) 
     430            widget.deleteLater() 
     431            del self.__widget_processing_state[widget] 
    267432 
    268433 
    269434class WidgetsSignalManager(SignalManager): 
     435    """ 
     436    A signal manager for a WidgetsScheme. 
     437    """ 
    270438    def __init__(self, scheme): 
    271439        SignalManager.__init__(self, scheme) 
    272440 
    273441        scheme.installEventFilter(self) 
    274         # We keep a mapping from node->widget after the node/widget has been 
    275         # removed from the scheme until we also process all the outgoing signal 
    276         # updates. The reason is the old OWBaseWidget's MULTI channel protocol 
    277         # where the actual source widget instance is passed to the signal 
    278         # handler, and in the delayed update the mapping in `scheme()` is no 
    279         # longer available. 
    280         self._widget_backup = {} 
    281         self._widgets_to_delete = set() 
    282         self._active_node = None 
     442 
    283443        self.freezing = 0 
    284444 
    285445        self.__scheme_deleted = False 
     446 
    286447        scheme.destroyed.connect(self.__on_scheme_destroyed) 
    287  
    288     def on_node_removed(self, node): 
    289         widget = self.scheme().widget_for_node[node] 
    290  
    291         assert not self.scheme().find_links(sink_node=node), \ 
    292             "Node removed but still has input links" 
    293  
    294         signals = self.compress_signals(self.pending_input_signals(node)) 
    295         if not all(signal.value is None for signal in signals): 
    296             log.error("Non 'None' signals pending for a removed node %r", 
    297                          node.title) 
    298  
    299         SignalManager.on_node_removed(self, node) 
    300  
    301         if self.runtime_state() == SignalManager.Processing and \ 
    302                 node is self._active_node or self.is_blocking(node): 
    303             # Delay the widget delete until it finishes. 
    304             # Keep a reference to the widget and install a filter. 
    305             self._widgets_to_delete.add(widget) 
    306             widget.installEventFilter(self) 
    307  
    308         # Store the node->widget mapping for possible delayed signal id. 
    309         # It will be removed in `process_queued` when all signals 
    310         # originating from this widget are delivered. 
    311         self._widget_backup[node] = widget 
    312  
    313     def send(self, widget, channelname, value, id): 
     448        scheme.node_added.connect(self.on_node_added) 
     449        scheme.node_removed.connect(self.on_node_removed) 
     450        scheme.link_added.connect(self.link_added) 
     451        scheme.link_removed.connect(self.link_removed) 
     452 
     453    def send(self, widget, channelname, value, signal_id): 
    314454        """ 
    315455        send method compatible with OWBaseWidget. 
    316456        """ 
    317457        scheme = self.scheme() 
    318  
    319         if widget not in scheme.node_for_widget: 
    320             # The Node/Widget was already removed from the scheme 
     458        try: 
     459            node = scheme.node_for_widget(widget) 
     460        except KeyError: 
     461            # The Node/Widget was already removed from the scheme. 
     462            log.debug("Node for %r is not in the scheme.", widget) 
    321463            return 
    322  
    323         node = scheme.node_for_widget[widget] 
    324464 
    325465        try: 
     
    330470            return 
    331471 
    332         SignalManager.send(self, node, channel, value, id) 
     472        # Expand the signal_id with the unique widget id and the 
     473        # channel name. This is needed for OWBaseWidget's input 
     474        # handlers (Multiple flag). 
     475        signal_id = (widget.widgetId, channelname, signal_id) 
     476 
     477        SignalManager.send(self, node, channel, value, signal_id) 
    333478 
    334479    def is_blocking(self, node): 
    335         return self.scheme().widget_for_node[node].isBlocking() 
     480        return self.scheme().widget_manager.node_processing_state(node) != 0 
    336481 
    337482    def send_to_node(self, node, signals): 
    338483        """ 
    339         Implementation of `SignalManager.send_to_node`. Deliver data signals 
    340         to OWBaseWidget instance. 
    341  
    342         """ 
    343         if node in self.scheme().widget_for_node: 
    344             widget = self.scheme().widget_for_node[node] 
    345         else: 
    346             widget = self._widget_backup[node] 
    347  
    348         self._active_node = node 
     484        Implementation of `SignalManager.send_to_node`. 
     485 
     486        Deliver input signals to an OWBaseWidget instance. 
     487 
     488        """ 
     489        widget = self.scheme().widget_for_node(node) 
    349490        self.process_signals_for_widget(node, widget, signals) 
    350         self._active_node = None 
    351  
    352         if widget in self._widgets_to_delete: 
    353             # If this node/widget was removed during the 
    354             # 'process_signals_for_widget' 
    355             self._widgets_to_delete.remove(widget) 
    356             widget.deleteLater() 
    357491 
    358492    def compress_signals(self, signals): 
     493        """ 
     494        Reimplemented from :func:`SignalManager.compress_signals`. 
     495        """ 
    359496        return compress_signals(signals) 
    360497 
    361     def process_queued(self, max_nodes=None): 
    362         SignalManager.process_queued(self, max_nodes=max_nodes) 
    363  
    364         # Remove node->widgets backup mapping no longer needed. 
    365         nodes_removed = set(self._widget_backup.keys()) 
    366         sources_remaining = set(signal.link.source_node for 
    367                                 signal in self._input_queue) 
    368  
    369         nodes_to_remove = nodes_removed - sources_remaining 
    370         for node in nodes_to_remove: 
    371             del self._widget_backup[node] 
    372  
    373498    def process_signals_for_widget(self, node, widget, signals): 
    374499        """ 
    375         Process new signals for a OWBaseWidget. 
     500        Process new signals for the OWBaseWidget. 
    376501        """ 
    377502        # This replaces the old OWBaseWidget.processSignals method 
     
    385510            widget.processingHandler(widget, 1) 
    386511 
    387         scheme = self.scheme() 
    388512        app = QCoreApplication.instance() 
    389513 
     
    408532                args = (value,) 
    409533            else: 
    410                 source_node = link.source_node 
    411                 source_name = link.source_channel.name 
    412  
    413                 if source_node in scheme.widget_for_node: 
    414                     source_widget = scheme.widget_for_node[source_node] 
    415                 else: 
    416                     # Node is no longer in the scheme. 
    417                     source_widget = self._widget_backup[source_node] 
    418  
    419                 # The old OWBaseWidget.processSignals sends the source widget 
    420                 # instance along. 
    421                 # TODO: Does any widget actually use it, or could it be 
    422                 # removed (replaced with a unique id)? 
    423                 args = (value, (source_widget, source_name, signal.id)) 
     534                args = (value, signal.id) 
    424535 
    425536            log.debug("Process signals: calling %s.%s (from %s with id:%s)", 
     
    512623            Construct SignalLink from an SchemeLink. 
    513624            """ 
    514             w1 = scheme.widget_for_node[link.source_node] 
    515             w2 = scheme.widget_for_node[link.sink_node] 
     625            w1 = scheme.widget_for_node(link.source_node) 
     626            w2 = scheme.widget_for_node(link.sink_node) 
    516627 
    517628            # Input/OutputSignal are reused from description. Interface 
     
    598709                self.__scheme_deleted = True 
    599710                return True 
    600         elif receiver in self._widgets_to_delete and \ 
    601                 event.type() == QEvent.DeferredDelete: 
    602             if self._widget_backup.get(self._active_node, None) is receiver: 
    603                 # The widget is still being updated. We need to keep it alive, 
    604                 # it will be deleted in `send_to_node`. 
    605                 log.info("Deferring a 'DeferredDelete' until widget exits " 
    606                          "the 'process_signals_for_widget'.") 
    607                 event.setAccepted(False) 
    608                 return True 
    609711 
    610712        return SignalManager.eventFilter(self, receiver, event) 
Note: See TracChangeset for help on using the changeset viewer.