Ignore:
File:
1 edited

Legend:

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

    r11623 r11641  
    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    #:   * ProcessingUpdate - widget has entered processing state 
     116    InputUpdate, BlockingUpdate, ProcessingUpdate = 1, 2, 4 
     117 
     118    def __init__(self, parent): 
     119        QObject.__init__(self, parent) 
     120        self.__scheme = None 
     121        self.__signal_manager = None 
     122        self.__widgets = [] 
     123        self.__widget_for_node = {} 
     124        self.__node_for_widget = {} 
     125 
     126        # Widgets that were 'removed' from the scheme but were at 
     127        # the time in an input update loop and could not be deleted 
     128        # immediately 
     129        self.__delay_delete = set() 
     130 
     131        # processing state flags for all nodes (including the ones 
     132        # in __delay_delete). 
     133        self.__widget_processing_state = {} 
     134 
     135        # Tracks the widget in the update loop by the SignalManager 
     136        self.__updating_widget = None 
     137 
     138    def set_scheme(self, scheme): 
     139        """ 
     140        Set the :class:`WidgetsScheme` instance to manage. 
     141        """ 
     142        self.__scheme = scheme 
     143        self.__signal_manager = scheme.findChild(SignalManager) 
     144 
     145        self.__signal_manager.processingStarted[SchemeNode].connect( 
    55146            self.__on_processing_started 
    56147        ) 
    57         self.signal_manager.processingFinished[SchemeNode].connect( 
     148        self.__signal_manager.processingFinished[SchemeNode].connect( 
    58149            self.__on_processing_finished 
    59150        ) 
    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. 
     151        scheme.node_added.connect(self.add_widget_for_node) 
     152        scheme.node_removed.connect(self.remove_widget_for_node) 
     153        scheme.installEventFilter(self) 
     154 
     155    def scheme(self): 
     156        """ 
     157        Return the scheme instance on which this manager is installed. 
     158        """ 
     159        return self.__scheme 
     160 
     161    def signal_manager(self): 
     162        """ 
     163        Return the signal manager in use on the :func:`scheme`. 
     164        """ 
     165        return self.__signal_manager 
     166 
     167    def widget_for_node(self, node): 
     168        """ 
     169        Return the OWWidget instance for the scheme node. 
     170        """ 
     171        return self.__widget_for_node[node] 
     172 
     173    def node_for_widget(self, widget): 
     174        """ 
     175        Return the SchemeNode instance for the OWWidget. 
     176 
     177        Raise a KeyError if the widget does not map to a node in the scheme. 
     178        """ 
     179        return self.__node_for_widget[widget] 
     180 
     181    def add_widget_for_node(self, node): 
     182        """ 
     183        Create a new OWWidget instance for the corresponding scheme node. 
     184        """ 
    73185        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  
     186 
     187        self.__widgets.append(widget) 
     188        self.__widget_for_node[node] = widget 
     189        self.__node_for_widget[widget] = node 
     190 
     191        self.widget_for_node_added.emit(node, widget) 
     192 
     193    def remove_widget_for_node(self, node): 
     194        """ 
     195        Remove the OWWidget instance for node. 
     196        """ 
     197        widget = self.widget_for_node(node) 
     198 
     199        self.__widgets.remove(widget) 
     200        del self.__widget_for_node[node] 
     201        del self.__node_for_widget[widget] 
     202 
     203        self.widget_for_node_removed.emit(node, widget) 
     204 
     205        self._delete_widget(widget) 
     206 
     207    def _delete_widget(self, widget): 
     208        """ 
     209        Delete the OWBaseWidget instance. 
     210        """ 
    87211        widget.close() 
    88212 
     
    93217        # Notify the widget it will be deleted. 
    94218        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) 
     219 
     220        if self.__widget_processing_state[widget] != 0: 
     221            # If the widget is in an update loop and/or blocking we 
     222            # delay the scheduled deletion until the widget is done. 
     223            self.__delay_delete.add(widget) 
     224        else: 
     225            widget.deleteLater() 
    105226 
    106227    def create_widget_instance(self, node): 
    107228        """ 
    108         Create a OWBaseWidget instance for the node. 
     229        Create a OWWidget instance for the node. 
    109230        """ 
    110231        desc = node.description 
     
    114235        widget = klass.__new__( 
    115236            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), 
     237            _owInfo=True, 
     238            _owWarning=True, 
     239            _owError=True, 
     240            _owShowStatus=True, 
     241            _useContexts=True, 
    121242            _category=desc.category, 
    122243            _settingsFromSchema=node.properties 
    123244        ) 
    124245 
    125         # Add the node/widget mapping s before calling __init__ 
     246        # Init the node/widget mapping and state before calling __init__ 
    126247        # 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) 
     248        # (should this be forbidden? Raise a warning?) triggering the signal 
     249        # manager which would request the widget => node mapping or state 
     250        self.__widget_for_node[node] = widget 
     251        self.__node_for_widget[widget] = node 
     252        self.__widget_processing_state[widget] = 0 
     253 
     254        widget.__init__(None, self.signal_manager()) 
    134255        widget.setCaption(node.title) 
    135256        widget.widgetInfo = desc 
     
    143264        node.title_changed.connect(widget.setCaption) 
    144265 
    145         # Bind widgets progress/processing state back to the node's properties 
     266        # Widget's info/warning/error messages. 
     267        widget.widgetStateChanged.connect(self.__on_widget_state_changed) 
     268 
     269        # Widget's progress bar value state. 
    146270        widget.progressBarValueChanged.connect(node.set_progress) 
    147         widget.processingStateChanged.connect(node.set_processing_state) 
    148         widget.widgetStateChanged.connect(self.__on_widget_state_changed) 
     271 
     272        # Widget processing state (progressBarInit/Finished) 
     273        # and the blocking state. 
     274        widget.processingStateChanged.connect( 
     275            self.__on_processing_state_changed 
     276        ) 
    149277        self.connect(widget, 
    150278                     SIGNAL("blockingStateChanged(bool)"), 
    151                      self.signal_manager._update) 
     279                     self.__on_blocking_state_changed) 
    152280 
    153281        # Install a help shortcut on the widget 
    154282        help_shortcut = QShortcut(QKeySequence("F1"), widget) 
    155283        help_shortcut.activated.connect(self.__on_help_request) 
     284 
    156285        return widget 
    157286 
    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] 
     287    def node_processing_state(self, node): 
     288        """ 
     289        Return the processing state flags for the node. 
     290 
     291        Same as `manager.node_processing_state(manger.widget_for_node(node))` 
     292 
     293        """ 
     294        widget = self.widget_for_node(node) 
     295        return self.__widget_processing_state[widget] 
     296 
     297    def widget_processing_state(self, widget): 
     298        """ 
     299        Return the processing state flags for the widget. 
     300 
     301        The state is an bitwise or of `InputUpdate` and `BlockingUpdate`. 
     302 
     303        """ 
     304        return self.__widget_processing_state[widget] 
     305 
     306    def eventFilter(self, receiver, event): 
     307        if receiver is self.__scheme and event.type() == QEvent.Close: 
     308            self.signal_manager().stop() 
     309 
     310            # Notify the widget instances. 
     311            for widget in self.__widget_for_node.values(): 
     312                widget.close() 
     313 
     314                if not widget._settingsFromSchema: 
     315                    # First save global settings if necessary. 
     316                    widget.saveSettings() 
     317 
     318                widget.onDeleteWidget() 
     319 
     320            event.accept() 
     321            return True 
     322 
     323        return QObject.eventFilter(self, receiver, event) 
     324 
     325    def __on_help_request(self): 
     326        """ 
     327        Help shortcut was pressed. We send a `QWhatsThisClickedEvent` to 
     328        the scheme and hope someone responds to it. 
     329 
     330        """ 
     331        # Sender is the QShortcut, and parent the OWBaseWidget 
     332        widget = self.sender().parent() 
     333        try: 
     334            node = self.node_for_widget(widget) 
     335        except KeyError: 
     336            pass 
     337        else: 
     338            url = "help://search?id={0}".format(node.description.id) 
     339            event = QWhatsThisClickedEvent(url) 
     340            QCoreApplication.sendEvent(self.scheme(), event) 
    163341 
    164342    def __on_widget_state_changed(self, message_type, message_id, 
     
    174352        """ 
    175353        widget = self.sender() 
    176         node = self.node_for_widget.get(widget) 
    177         if node is not None: 
     354        try: 
     355            node = self.node_for_widget(widget) 
     356        except KeyError: 
     357            pass 
     358        else: 
    178359            message_type = str(message_type) 
    179360            if message_type == "Info": 
     
    194375            message = UserMessage(contents, severity=level, 
    195376                                  message_id=message_type, 
    196                                   data={"contents-type": "text/html"}) 
     377                                  data={"content-type": "text/html"}) 
    197378            node.set_state_message(message) 
    198379 
    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 
    245         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) 
     380    def __on_processing_state_changed(self, state): 
     381        """ 
     382        A widget processing state has changed (progressBarInit/Finished) 
     383        """ 
     384        widget = self.sender() 
     385        try: 
     386            node = self.node_for_widget(widget) 
     387        except KeyError: 
     388            return 
     389 
     390        if state: 
     391            self.__widget_processing_state[widget] |= self.ProcessingUpdate 
     392        else: 
     393            self.__widget_processing_state[widget] &= ~self.ProcessingUpdate 
     394        self.__update_node_processing_state(node) 
    261395 
    262396    def __on_processing_started(self, node): 
    263         node.set_processing_state(1) 
     397        """ 
     398        Signal manager entered the input update loop for the node. 
     399        """ 
     400        widget = self.widget_for_node(node) 
     401        # Remember the widget instance. The node and the node->widget mapping 
     402        # can be removed between this and __on_processing_finished. 
     403        self.__updating_widget = widget 
     404        self.__widget_processing_state[widget] |= self.InputUpdate 
     405        self.__update_node_processing_state(node) 
    264406 
    265407    def __on_processing_finished(self, node): 
    266         node.set_processing_state(0) 
     408        """ 
     409        Signal manager exited the input update loop for the node. 
     410        """ 
     411        widget = self.__updating_widget 
     412        self.__widget_processing_state[widget] &= ~self.InputUpdate 
     413 
     414        if widget in self.__node_for_widget: 
     415            self.__update_node_processing_state(node) 
     416        elif widget in self.__delay_delete: 
     417            self.__try_delete(widget) 
     418        else: 
     419            raise ValueError("%r is not managed" % widget) 
     420 
     421        self.__updating_widget = None 
     422 
     423    def __on_blocking_state_changed(self, state): 
     424        """ 
     425        OWWidget blocking state has changed. 
     426        """ 
     427        if not state: 
     428            # schedule an update pass. 
     429            self.signal_manager()._update() 
     430 
     431        widget = self.sender() 
     432        if state: 
     433            self.__widget_processing_state[widget] |= self.BlockingUpdate 
     434        else: 
     435            self.__widget_processing_state[widget] &= ~self.BlockingUpdate 
     436 
     437        if widget in self.__node_for_widget: 
     438            node = self.node_for_widget(widget) 
     439            self.__update_node_processing_state(node) 
     440 
     441        elif widget in self.__delay_delete: 
     442            self.__try_delete(widget) 
     443 
     444    def __update_node_processing_state(self, node): 
     445        """ 
     446        Update the `node.processing_state` to reflect the widget state. 
     447        """ 
     448        state = self.node_processing_state(node) 
     449        node.set_processing_state(1 if state else 0) 
     450 
     451    def __try_delete(self, widget): 
     452        if self.__widget_processing_state[widget] == 0: 
     453            self.__delay_delete.remove(widget) 
     454            widget.deleteLater() 
     455            del self.__widget_processing_state[widget] 
    267456 
    268457 
    269458class WidgetsSignalManager(SignalManager): 
     459    """ 
     460    A signal manager for a WidgetsScheme. 
     461    """ 
    270462    def __init__(self, scheme): 
    271463        SignalManager.__init__(self, scheme) 
    272464 
    273465        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 
     466 
    283467        self.freezing = 0 
    284468 
    285469        self.__scheme_deleted = False 
     470 
    286471        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): 
     472        scheme.node_added.connect(self.on_node_added) 
     473        scheme.node_removed.connect(self.on_node_removed) 
     474        scheme.link_added.connect(self.link_added) 
     475        scheme.link_removed.connect(self.link_removed) 
     476 
     477    def send(self, widget, channelname, value, signal_id): 
    314478        """ 
    315479        send method compatible with OWBaseWidget. 
    316480        """ 
    317481        scheme = self.scheme() 
    318  
    319         if widget not in scheme.node_for_widget: 
    320             # The Node/Widget was already removed from the scheme 
     482        try: 
     483            node = scheme.node_for_widget(widget) 
     484        except KeyError: 
     485            # The Node/Widget was already removed from the scheme. 
     486            log.debug("Node for %r is not in the scheme.", widget) 
    321487            return 
    322  
    323         node = scheme.node_for_widget[widget] 
    324488 
    325489        try: 
     
    330494            return 
    331495 
    332         SignalManager.send(self, node, channel, value, id) 
     496        # Expand the signal_id with the unique widget id and the 
     497        # channel name. This is needed for OWBaseWidget's input 
     498        # handlers (Multiple flag). 
     499        signal_id = (widget.widgetId, channelname, signal_id) 
     500 
     501        SignalManager.send(self, node, channel, value, signal_id) 
    333502 
    334503    def is_blocking(self, node): 
    335         return self.scheme().widget_for_node[node].isBlocking() 
     504        return self.scheme().widget_manager.node_processing_state(node) != 0 
    336505 
    337506    def send_to_node(self, node, signals): 
    338507        """ 
    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 
     508        Implementation of `SignalManager.send_to_node`. 
     509 
     510        Deliver input signals to an OWBaseWidget instance. 
     511 
     512        """ 
     513        widget = self.scheme().widget_for_node(node) 
    349514        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() 
    357515 
    358516    def compress_signals(self, signals): 
     517        """ 
     518        Reimplemented from :func:`SignalManager.compress_signals`. 
     519        """ 
    359520        return compress_signals(signals) 
    360521 
    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  
    373522    def process_signals_for_widget(self, node, widget, signals): 
    374523        """ 
    375         Process new signals for a OWBaseWidget. 
     524        Process new signals for the OWBaseWidget. 
    376525        """ 
    377526        # This replaces the old OWBaseWidget.processSignals method 
     
    385534            widget.processingHandler(widget, 1) 
    386535 
    387         scheme = self.scheme() 
    388536        app = QCoreApplication.instance() 
    389537 
     
    408556                args = (value,) 
    409557            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)) 
     558                args = (value, signal.id) 
    424559 
    425560            log.debug("Process signals: calling %s.%s (from %s with id:%s)", 
     
    446581            app.restoreOverrideCursor() 
    447582 
    448         # TODO: Test if async processing works, then remove this 
    449         while widget.isBlocking(): 
    450             self.thread().msleep(50) 
    451             app.processEvents() 
    452  
    453583        if widget.processingHandler: 
    454584            widget.processingHandler(widget, 0) 
     
    512642            Construct SignalLink from an SchemeLink. 
    513643            """ 
    514             w1 = scheme.widget_for_node[link.source_node] 
    515             w2 = scheme.widget_for_node[link.sink_node] 
     644            w1 = scheme.widget_for_node(link.source_node) 
     645            w2 = scheme.widget_for_node(link.sink_node) 
    516646 
    517647            # Input/OutputSignal are reused from description. Interface 
     
    598728                self.__scheme_deleted = True 
    599729                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 
    609730 
    610731        return SignalManager.eventFilter(self, receiver, event) 
Note: See TracChangeset for help on using the changeset viewer.