source: orange/Orange/OrangeCanvas/scheme/link.py @ 11101:e5aa7c1c5b23

Revision 11101:e5aa7c1c5b23, 4.8 KB checked in by Ales Erjavec <ales.erjavec@…>, 19 months ago (diff)

Added scheme package, containing the scheme graph workflow data structure/model.

Line 
1"""
2===========
3Scheme Link
4===========
5
6"""
7
8from PyQt4.QtCore import QObject
9from PyQt4.QtCore import pyqtSignal as Signal
10from PyQt4.QtCore import pyqtProperty as Property
11
12from .utils import name_lookup
13from .errors import IncompatibleChannelTypeError
14
15
16def compatible_channels(source_channel, sink_channel):
17    """Do the channels in link have compatible types, i.e. can they be
18    connected based on their type.
19
20    """
21    source_type = name_lookup(source_channel.type)
22    sink_type = name_lookup(sink_channel.type)
23    ret = issubclass(source_type, sink_type)
24    if source_channel.dynamic:
25        ret = ret or issubclass(sink_type, source_type)
26    return ret
27
28
29def can_connect(source_node, sink_node):
30    """Return True if any output from `source_node` can be connected to
31    any input of `sink_node`.
32
33    """
34    return bool(possible_links(source_node, sink_node))
35
36
37def possible_links(source_node, sink_node):
38    """Return a list of (OutputSignal, InputSignal) tuples, that
39    can connect the two nodes.
40
41    """
42    possible = []
43    for source in source_node.output_channels():
44        for sink in sink_node.input_channels():
45            if compatible_channels(source, sink):
46                possible.append((source, sink))
47    return possible
48
49
50class SchemeLink(QObject):
51    """A instantiation of a link between two widget nodes in the scheme.
52
53    Parameters
54    ----------
55    source_node : `SchemeNode`
56        Source node.
57    source_channel : `OutputSignal`
58        The source widget's signal.
59    sink_node : `SchemeNode`
60        The sink node.
61    sink_channel : `InputSignal`
62        The sink widget's input signal.
63    properties : `dict`
64        Additional link properties.
65
66    """
67
68    enabled_changed = Signal(bool)
69    dynamic_enabled_changed = Signal(bool)
70
71    def __init__(self, source_node, source_channel,
72                 sink_node, sink_channel, enabled=True, properties=None,
73                 parent=None):
74        QObject.__init__(self, parent)
75        self.source_node = source_node
76
77        if isinstance(source_channel, basestring):
78            source_channel = source_node.output_channel(source_channel)
79        elif source_channel not in source_node.output_channels():
80            raise ValueError("%r not in in nodes output channels." \
81                             % source_channel)
82
83        self.source_channel = source_channel
84
85        self.sink_node = sink_node
86
87        if isinstance(sink_channel, basestring):
88            sink_channel = sink_node.input_channel(sink_channel)
89        elif sink_channel not in sink_node.input_channels():
90            raise ValueError("%r not in in nodes input channels." \
91                             % source_channel)
92
93        self.sink_channel = sink_channel
94
95        if not compatible_channels(source_channel, sink_channel):
96            raise IncompatibleChannelTypeError(
97                    "Cannot connect %r to %r" \
98                    % (source_channel, sink_channel)
99                )
100
101        self.__enabled = enabled
102        self.__dynamic_enabled = False
103        self.__tool_tip = ""
104        self.properties = properties or {}
105
106    def source_type(self):
107        """Return the type of the source channel.
108        """
109        return name_lookup(self.source_channel.type)
110
111    def sink_type(self):
112        """Return the type of the sink channel.
113        """
114        return name_lookup(self.sink_channel.type)
115
116    def is_dynamic(self):
117        """Is this link dynamic.
118        """
119        return self.source_channel.dynamic and \
120            issubclass(self.sink_type(), self.source_type())
121
122    def enabled(self):
123        """Is this link enabled.
124        """
125        return self.__enabled
126
127    def set_enabled(self, enabled):
128        """Enable/disable the link.
129        """
130        if self.__enabled != enabled:
131            self.__enabled = enabled
132            self.enabled_changed.emit(enabled)
133
134    enabled = Property(bool, fget=enabled, fset=set_enabled)
135
136    def set_dynamic_enabled(self, enabled):
137        """Enable/disable the dynamic link. Has no effect if
138        the link is not dynamic.
139
140        """
141        if self.is_dynamic() and self.__dynamic_enabled != enabled:
142            self.__dynamic_enabled = enabled
143            self.dynamic_enabled_changed.emit(enabled)
144
145    def dynamic_enabled(self):
146        """Is this dynamic link and `dynamic_enabled` set to True
147        """
148        return self.is_dynamic() and self.__dynamic_enabled
149
150    dynamic_enabled = Property(bool, fget=dynamic_enabled,
151                                 fset=set_dynamic_enabled)
152
153    def set_tool_tip(self, tool_tip):
154        """Set the link tool tip.
155        """
156        if self.__tool_tip != tool_tip:
157            self.__tool_tip = tool_tip
158
159    def tool_tip(self):
160        """Return the link's tool tip
161        """
162        return self.__tool_tip
163
164    tool_tip = Property(str, fget=tool_tip,
165                          fset=set_tool_tip)
Note: See TracBrowser for help on using the repository browser.