Changeset 11465:5c3834e1d2ea in orange


Ignore:
Timestamp:
04/22/13 16:07:34 (12 months ago)
Author:
Ales Erjavec <ales.erjavec@…>
Branch:
default
Message:

Delay the deletion of the Scheme and/or OWBaseWidget until SignalManager finishes the current update.

Location:
Orange/OrangeCanvas/scheme
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • Orange/OrangeCanvas/scheme/signalmanager.py

    r11269 r11465  
    171171        # NOTE: This does not remove output signals this node. In particular 
    172172        # the final 'None' values might be left on the queue. 
    173         log.info("Node %r removed. Removing pending signals.") 
     173        log.info("Node %r removed. Removing pending signals.", 
     174                 node.title) 
    174175        self.remove_pending_signals(node) 
    175176 
  • Orange/OrangeCanvas/scheme/widgetsscheme.py

    r11411 r11465  
    215215        SignalManager.__init__(self, scheme) 
    216216 
     217        scheme.installEventFilter(self) 
    217218        # We keep a mapping from node->widget after the node/widget has been 
    218219        # removed from the scheme until we also process all the outgoing signal 
    219220        # updates. The reason is the old OWBaseWidget's MULTI channel protocol 
    220221        # where the actual source widget instance is passed to the signal 
    221         # handler, and in the delaeyd update the mapping in `scheme()` is no 
     222        # handler, and in the delayed update the mapping in `scheme()` is no 
    222223        # longer available. 
    223224        self._widget_backup = {} 
    224  
     225        self._widgets_to_delete = set() 
     226        self._active_node = None 
    225227        self.freezing = 0 
     228 
     229        self.__scheme_deleted = False 
     230        scheme.destroyed.connect(self.__on_scheme_destroyed) 
    226231 
    227232    def on_node_removed(self, node): 
    228233        widget = self.scheme().widget_for_node[node] 
     234 
     235        assert not self.scheme().find_links(sink_node=node), \ 
     236            "Node removed but still has input links" 
     237 
     238        signals = self.compress_signals(self.pending_input_signals(node)) 
     239        if not all(signal.value is None for signal in signals): 
     240            log.error("Non 'None' signals pending for a removed node %r", 
     241                         node.title) 
     242 
    229243        SignalManager.on_node_removed(self, node) 
    230244 
     245        if self.runtime_state() == SignalManager.Processing and \ 
     246                node is self._active_node or self.is_blocking(node): 
     247            # Delay the widget delete until it finishes. 
     248            # Keep a reference to the widget and install a filter. 
     249            self._widgets_to_delete.add(widget) 
     250            widget.installEventFilter(self) 
     251 
    231252        # Store the node->widget mapping for possible delayed signal id. 
    232         # It will be removes in `process_queued` when all signals 
     253        # It will be removed in `process_queued` when all signals 
    233254        # originating from this widget are delivered. 
    234255        self._widget_backup[node] = widget 
     
    259280 
    260281        """ 
    261         widget = self.scheme().widget_for_node[node] 
     282        if node in self.scheme().widget_for_node: 
     283            widget = self.scheme().widget_for_node[node] 
     284        else: 
     285            widget = self._widget_backup[node] 
     286 
     287        self._active_node = node 
    262288        self.process_signals_for_widget(node, widget, signals) 
     289        self._active_node = None 
     290 
     291        if widget in self._widgets_to_delete: 
     292            # If this node/widget was removed during the 
     293            # 'process_signals_for_widget' 
     294            self._widgets_to_delete.remove(widget) 
     295            widget.deleteLater() 
    263296 
    264297    def compress_signals(self, signals): 
     
    354387 
    355388        if widget.processingHandler: 
    356             widget.processingHandler(self, 0) 
     389            widget.processingHandler(widget, 0) 
    357390 
    358391    def scheduleSignalProcessing(self, widget=None): 
     
    480513                return False 
    481514 
     515            if self.__scheme_deleted: 
     516                log.debug("Scheme has been/is being deleted. No more " 
     517                          "signals will be delivered to any nodes.") 
     518                event.setAccepted(True) 
     519                return True 
     520        # Retain a reference to the scheme until the 'process_queued' finishes 
     521        # in SignalManager.event. 
     522        scheme = self.scheme() 
    482523        return SignalManager.event(self, event) 
     524 
     525    def eventFilter(self, receiver, event): 
     526        if receiver is self.scheme() and event.type() == QEvent.DeferredDelete: 
     527            if self.runtime_state() == SignalManager.Processing: 
     528                log.debug("Deferring a 'DeferredDelete' event for the Scheme " 
     529                          "instance until SignalManager exits the current " 
     530                          "update loop.") 
     531                event.setAccepted(False) 
     532                self.processingFinished.connect(self.scheme().deleteLater) 
     533                self.__scheme_deleted = True 
     534                return True 
     535        elif receiver in self._widgets_to_delete and \ 
     536                event.type() == QEvent.DeferredDelete: 
     537            if self._widget_backup.get(self._active_node, None) is receiver: 
     538                # The widget is still being updated. We need to keep it alive, 
     539                # it will be deleted in `send_to_node`. 
     540                log.debug("Deferring a DeferredDelete until widget exits " 
     541                          "the 'process_signals_for_widget'.") 
     542                event.setAccepted(False) 
     543                return True 
     544 
     545        return SignalManager.eventFilter(self, receiver, event) 
     546 
     547    def __on_scheme_destroyed(self, obj): 
     548        self.__scheme_deleted = True 
    483549 
    484550 
    485551class SignalLink(object): 
    486552    """ 
    487     Back compatiblity with old orngSignalManager, do not use. 
     553    Back compatibility with old orngSignalManager, do not use. 
    488554    """ 
    489555    def __init__(self, widgetFrom, outputSignal, widgetTo, inputSignal, 
Note: See TracChangeset for help on using the changeset viewer.