source: orange/Orange/OrangeCanvas/scheme/widgetsscheme.py @ 11182:70ae8e72389b

Revision 11182:70ae8e72389b, 5.9 KB checked in by Ales Erjavec <ales.erjavec@…>, 17 months ago (diff)

Showing the dynamic link state in the canvas.

Line 
1import logging
2from functools import partial
3
4from PyQt4.QtCore import QTimer, SIGNAL
5
6from .. import orngSignalManager
7from .scheme import Scheme
8from .utils import name_lookup
9from ..config import rc
10from ..gui.utils import signals_disabled
11
12log = logging.getLogger(__name__)
13
14
15class WidgetsScheme(Scheme):
16    """A Scheme containing Orange Widgets managed with a SignalManager
17    instance.
18
19    """
20    def __init__(self, parent=None, title=None, description=None):
21        Scheme.__init__(self, parent, title, description)
22
23        self.widgets = []
24        self.widget_for_node = {}
25        self.signal_manager = orngSignalManager.SignalManager()
26
27        self.__loaded_from = None
28
29    def add_node(self, node):
30        widget = self.create_widget_instance(node)
31
32        # don't emit the node_added signal until the widget is successfully
33        # added to signal manager etc.
34        with signals_disabled(self):
35            Scheme.add_node(self, node)
36
37        self.widgets.append(widget)
38
39        self.widget_for_node[node] = widget
40
41        self.signal_manager.addWidget(widget)
42
43        self.node_added.emit(node)
44
45    def remove_node(self, node):
46        Scheme.remove_node(self, node)
47        widget = self.widget_for_node[node]
48        self.signal_manager.removeWidget(widget)
49        del self.widget_for_node[node]
50
51        # Save settings to user global settings.
52        if not self.__loaded_from:
53            widget.saveSettings()
54
55        # Notify the widget it will be deleted.
56        widget.onDeleteWidget()
57        # And schedule it for deletion.
58        widget.deleteLater()
59
60    def add_link(self, link):
61        Scheme.add_link(self, link)
62        source_widget = self.widget_for_node[link.source_node]
63        sink_widget = self.widget_for_node[link.sink_node]
64        source_channel = link.source_channel.name
65        sink_channel = link.sink_channel.name
66        self.signal_manager.addLink(source_widget, sink_widget, source_channel,
67                                    sink_channel, enabled=link.enabled)
68
69        link.enabled_changed.connect(
70            partial(self.signal_manager.setLinkEnabled,
71                    source_widget, sink_widget)
72        )
73
74        QTimer.singleShot(0, self.signal_manager.processNewSignals)
75
76    def remove_link(self, link):
77        Scheme.remove_link(self, link)
78
79        source_widget = self.widget_for_node[link.source_node]
80        sink_widget = self.widget_for_node[link.sink_node]
81        source_channel = link.source_channel.name
82        sink_channel = link.sink_channel.name
83
84        self.signal_manager.removeLink(source_widget, sink_widget,
85                                       source_channel, sink_channel)
86
87    def create_widget_instance(self, node):
88        desc = node.description
89        klass = name_lookup(desc.qualified_name)
90
91        log.info("Creating %r instance.", klass)
92        widget = klass.__new__(
93            klass,
94            _owInfo=rc.get("canvas.show-state-info", True),
95            _owWarning=rc.get("canvas.show-state-warning", True),
96            _owError=rc.get("canvas.show-state-error", True),
97            _owShowStatus=rc.get("OWWidget.show-status", True),
98            _useContexts=rc.get("OWWidget.use-contexts", True),
99            _category=desc.category,
100            _settingsFromSchema=node.properties
101        )
102
103        widget.__init__(None, self.signal_manager)
104        widget.setCaption(node.title)
105        widget.widgetInfo = desc
106
107        widget.setVisible(node.properties.get("visible", False))
108
109        node.title_changed.connect(widget.setCaption)
110        # Bind widgets progress/processing state back to the node's properties
111        widget.progressBarValueChanged.connect(node.set_progress)
112        widget.processingStateChanged.connect(node.set_processing_state)
113
114        # TODO: Change how the signal is emitted in signal manager (should
115        # notify the SchemeLink directly).
116        widget.connect(
117           widget,
118           SIGNAL("dynamicLinkEnabledChanged(PyQt_PyObject, bool)"),
119           self.__on_dynamic_link_enabled_changed
120        )
121
122        return widget
123
124    def __on_dynamic_link_enabled_changed(self, link, enabled):
125        rev = dict(map(reversed, self.widget_for_node.items()))
126
127        source_node = rev[link.widgetFrom]
128        sink_node = rev[link.widgetTo]
129        source_channel = source_node.output_channel(link.signalNameFrom)
130        sink_channel = sink_node.input_channel(link.signalNameTo)
131
132        links = self.find_links(source_node, source_channel,
133                                sink_node, sink_channel)
134
135        if links:
136            link = links[0]
137            link.set_dynamic_enabled(enabled)
138
139    def close_all_open_widgets(self):
140        for widget in self.widget_for_node.values():
141            widget.close()
142
143    def sync_node_properties(self):
144        """Sync the widget settings/properties with the SchemeNode.properties.
145        Return True if there were any changes in the properties (i.e. if the
146        new node.properties differ from the old value) and False otherwise.
147
148        .. note:: this should hopefully be removed in the feature, when the
149            widget can notify a changed setting property.
150
151        """
152        changed = False
153        for node in self.nodes:
154            widget = self.widget_for_node[node]
155            settings = widget.getSettings()
156            if settings != node.properties:
157                node.properties = settings
158                changed = True
159        log.debug("Scheme node properties sync (changed: %s)", changed)
160        return changed
161
162    def save_to(self, stream):
163        self.sync_node_properties()
164        Scheme.save_to(self, stream)
165
166    def load_from(self, stream):
167        """Load the scheme from xml formated stream.
168        """
169        if isinstance(stream, basestring):
170            self.__loaded_from = stream
171            stream = open(stream, "rb")
172        elif isinstance(stream, file):
173            self.__loaded_from = stream.name
174
175        Scheme.load_from(self, stream)
Note: See TracBrowser for help on using the repository browser.