root/gaphor/tags/gaphor-0.12.5/gaphor/diagram/connector.py

Revision 2055, 18.9 kB (checked in by wrobe..@pld-linux.org, 1 year ago)

- commented out unused/dead code

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1 """
2 Connectors and connector ends from Composite Structures and Components.
3
4 Actually only Assembly connector (see Components in UML specs) is
5 implemented. This is done with AssemblyConnectorItem class.
6 AssemblyConnectorItem uses ConnectorEndItem (see ProvidedConnectorEndItem
7 and RequiredConnectorEndItem classes) instances to connect to components.
8
9 Component should provide at least one interface so ProvidedConnectorEndItem
10 can be connected to it. If there are more than one provided interfaces,
11 then user can choose appropriate one from ProvidedConnectorEndItem object
12 menu. Above also applies for required stuff.
13
14 UML Specificatiom Issues
15 ========================
16 In some areas UML specification is not clear about connector kind and
17 interfaces as connectable elements, see also
18
19     http://modeldrivenengineering.org/bin/view/Umlrtf/CurrentProposals
20
21 Connector Kind
22 ----------------------------------
23 Components chapter adds kind attribute to connector class. According to UML
24 specs it is enumeration with two possible values 'assembly' and
25 'delegation'.
26
27 This is an issue for connector between connectable elements like
28 properties, parameters or more specific ports not characterized by
29 interfaces. It is not clear what value should be assigned for connector
30 kind for connectors between such elements.
31
32
33 Interfaces as Connectable Elements
34 ----------------------------------
35 Composite Structures chapter in UML Superstructure (formal 05/07/04)
36 document does not specify interfaces as connectable elements.
37
38 But definition of assembly connector says:
39
40     An assembly connector is a connector between two components that
41     defines that one component provides the services that another component
42     requires. An assembly connector is a connector that is defined from a
43     required _interface_ or port to a provided _interface_ or port
44
45 Therefore, code of connectors is written with assumption, that interfaces
46 are connectable elements.
47
48 This is subject to change in the future when UML specification clarifies in
49 this area.
50 """
51
52 ###from gaphor import UML
53 ###from diagramline import DiagramLine, FreeLine
54 ###from elementitem import ElementItem
55 ###from component import ComponentItem
56 ###
57 ####from gaphor.diagram.interfaceicon import AssembledInterfaceIcon
58 ###from gaphor.diagram.rotatable import SimpleRotation
59 ###from gaphor.diagram import TextElement
60 ###from gaphor.misc import uniqueid
61 ###from gaphor.misc.meta import GObjectPropsMerge
62 ###from gaphor.diagram.groupable import GroupBase
63 ###
64 ###
65 ###class ConnectorEndItem(FreeLine, GroupBase):
66 ###    """
67 ###    Connector end item abstract class.
68 ###
69 ###    Connector end item without subject is called free connector end item.
70 ###
71 ###    Free connector has no id, too.
72 ###
73 ###    Connector end item has main point. Initially free connector end item is
74 ###    put in its main point. If free connector end item is moved behind the
75 ###    main point and it is not connected to any item, then it is moved to
76 ###    main point again.
77 ###
78 ###    Deriving classes should implement get_interfaces method.
79 ###
80 ###    For non-abstract implementations see ProvidedConnectorEndItem and
81 ###    RequiredConnectorEndItem classes.
82 ###    """
83 ###
84 ###    interface_actions = []
85 ###
86 ###    def __init__(self, id = None, mpt = None):
87 ###        """
88 ###        Create connector end item.
89 ###
90 ###        If main point is not specified then it is set to (0, 0).
91 ###        """
92 ###        FreeLine.__init__(self, id, mpt)
93 ###        GroupBase.__init__(self)
94 ###
95 ###        self.set_flags(diacanvas.COMPOSITE)
96 ###
97 ###        self._interface_name = TextElement('name')
98 ###        self.add(self._interface_name)
99 ###
100 ###
101 ###    def get_component(self):
102 ###        """
103 ###        Get component to which this connector end item is connected to or
104 ###        None.
105 ###        """
106 ###        if self._handle.connected_to:
107 ###            return self._handle.connected_to.subject
108 ###        return None
109 ###
110 ###
111 ###    def save(self, save_func):
112 ###        """
113 ###        Save handle position, main point and reference to connected item.
114 ###        """
115 ###        FreeLine.save(self, save_func)
116 ###        save_func('handle-position', self._handle.get_pos_w())
117 ###        save_func('main-point', self._main_point)
118 ###        save_func('connected-to', self._handle.connected_to, True)
119 ###
120 ###
121 ###    def postload(self):
122 ###        """
123 ###        Establish real connection between connector end item and connected
124 ###        item after loading diagram.
125 ###        """
126 ###        if hasattr(self, '_load_connected_to'):
127 ###            self._load_connected_to.connect_handle(self._handle)
128 ###            del self._load_connected_to
129 ###        FreeLine.postload(self)
130 ###
131 ###
132 ###    def load(self, name, value):
133 ###        """
134 ###        Load handle position, main point and reference to connected item.
135 ###
136 ###        Reference to connected item is stored in ConnectorEndItem._load_connected_to.
137 ###        Real connection between connector end item and connected item is
138 ###        established in ConnectorEndItem.postload method.
139 ###        """
140 ###        if name == 'handle-position':
141 ###            x, y = eval(value)
142 ###            self._handle.set_pos_w(x, y)
143 ###        elif name == 'main-point':
144 ###            self._main_point = eval(value)
145 ###        elif name == 'connected-to':
146 ###            self._load_connected_to = value
147 ###        else:
148 ###            FreeLine.load(self, name, value)
149 ###
150 ###
151 ###    def on_handle_motion(self, handle, x, y, event_mask):
152 ###        """
153 ###        Control handle position. If handle position is near the main point,
154 ###        then it is moved to main point.
155 ###       
156 ###        This allows user to easily move handle to main point.
157 ###        """
158 ###        if handle is self._handle:
159 ###            mpt = self._main_point
160 ###
161 ###            x, y = self.affine_point_w2i(x, y)
162 ###
163 ###            if abs(mpt[0] - x) < 10 and abs(mpt[1] - y) < 10:
164 ###                x, y = mpt
165 ###            return self.affine_point_i2w(x, y)
166 ###        else:
167 ###            return FreeLine.on_handle_motion(self, handle, x, y, event_mask)
168 ###
169 ###
170 ###    def on_update(self, affine):
171 ###        """
172 ###        Draw line between main point and current position.
173 ###        """
174 ###        def get_pos_centered(p1, p2, width, height):
175 ###            x = p1[0] > p2[0] and width + 2 or -2
176 ###            x = (p1[0] + p2[0]) / 2.0 - x
177 ###            y = p1[1] <= p2[1] and height or 0
178 ###            y = (p1[1] + p2[1]) / 2.0 - y
179 ###            return x, y
180 ###
181 ###        # move handle to main point if there is no component
182 ###        # associated with this connector end item
183 ###        if not self.is_focused() and not self.subject:
184 ###            self._handle.set_pos_i(self._main_point[0], self._main_point[1])
185 ###
186 ###        pos = self._handle.get_pos_i()
187 ###        w, h = self._interface_name.get_size()
188 ###        x, y = get_pos_centered(self._main_point, pos, w, h)
189 ###        self._interface_name.update_label(x, y)
190 ###
191 ###        FreeLine.on_update(self, affine)
192 ###        GroupBase.on_update(self, affine)
193 ###
194 ###
195 ###    def on_subject_notify(self, pspec, notifiers=()):
196 ###        """
197 ###        Generate an id when subject is set or set id to None if there is no
198 ###        subject.
199 ###        """
200 ###        FreeLine.on_subject_notify(self, pspec, notifiers)
201 ###        if self.subject and not self._id:
202 ###            self._id = uniqueid.generate_id()
203 ###
204 ###        if self.subject:
205 ###            self._interface_name.subject = self.subject
206 ###
207 ###            # kill connector end item if interface is no longer provided
208 ###            # or required by component
209 ###            component = self.get_component()
210 ###            component.connect('clientDependency', self.on_component_change)
211 ###
212 ###        else:
213 ###            self._interface_name.subject = None
214 ###            self._id = None
215 ###
216 ###        self.request_update()
217 ###
218 ###
219 ###    def allow_connect_handle(self, handle, item):
220 ###        """
221 ###        Allow to connect to components only and if they require or provide
222 ###        at least one interface.
223 ###        """
224 ###        return isinstance(item, ComponentItem) \
225 ###            and len(self.get_interfaces(item.subject)) > 0
226 ###
227 ###
228 ###    def confirm_connect_handle(self, handle):
229 ###        """
230 ###        Set first interface as subject of item.
231 ###        """
232 ###        if not self.subject:
233 ###            assert len(self.get_interfaces()) > 0
234 ###            self.set_subject(self.get_interfaces()[0])
235 ###
236 ###
237 ###    def confirm_disconnect_handle(self, handle, item):
238 ###        """
239 ###        Remove subject.
240 ###        """
241 ###        self.set_subject(None)
242 ###
243 ###
244 ###    def allow_disconnect_handle(self, handle):
245 ###        """
246 ###        It is always allowed to disconnect handle.
247 ###        """
248 ###        return True
249 ###
250 ###
251 ###    def on_component_change(self, component, data):
252 ###        """
253 ###        Kill connector end item if interface is no longer in component
254 ###        environment.
255 ###        """
256 ###        if self.subject:
257 ###            if self.subject not in self.get_interfaces():
258 ###                self.unlink()
259 ###
260 ###
261 ###
262 ###class ProvidedConnectorEndItem(ConnectorEndItem):
263 ###    """
264 ###    Connector end item which allows to connect to components, which provide
265 ###    interfaces.
266 ###    """
267 ###    def get_interfaces(self, component = None):
268 ###        """
269 ###        Get component provided interfaces.
270 ###        """
271 ###        if not component:
272 ###            component = self.get_component()
273 ###        return component.provided
274 ###
275 ###
276 ###
277 ###class RequiredConnectorEndItem(ConnectorEndItem):
278 ###    """
279 ###    Connector end item which allows to connect to components, which require
280 ###    interfaces.
281 ###    """
282 ###    def get_interfaces(self, component = None):
283 ###        """
284 ###        Get component required interfaces.
285 ###        """
286 ###        if not component:
287 ###            component = self.get_component()
288 ###        return component.required
289 ###
290 ###
291 ###
292 ###class AssemblyConnectorItem(ElementItem, SimpleRotation, GroupBase):
293 ###    """
294 ###    Assembly connector item.
295 ###
296 ###    It has exactly two free connector end items.
297 ###
298 ###    Connector end items are added and removed from assembly connector using
299 ###    canvas groupable interface.
300 ###
301 ###    Assembly connector item distinguishes between two kind of main points.
302 ###    One is for connector end items, which connect to provided interfaces/ports.
303 ###    Other is for required interfaces/ports.
304 ###    """
305 ###
306 ###    __metaclass__ = GObjectPropsMerge # merge properties from SimpleRotation
307 ###
308 ###    popup_menu = (
309 ###        'EditDelete',
310 ###        'separator',
311 ###        'Rotate',
312 ###    )
313 ###
314 ###    def __init__(self, id):
315 ###        """
316 ###        Create assembly connector item.
317 ###        """
318 ###        GroupBase.__init__(self)
319 ###        ElementItem.__init__(self, id)
320 ###        SimpleRotation.__init__(self)
321 ###
322 ###        #self._assembly = AssembledInterfaceIcon(self)
323 ###
324 ###        for h in self.handles:
325 ###            h.props.movable = False
326 ###
327 ###        self.set(width = self._assembly.width,
328 ###            height = self._assembly.height)
329 ###
330 ###
331 ###    def do_set_property(self, pspec, value):
332 ###        if pspec.name in SimpleRotation.__gproperties__:
333 ###            SimpleRotation.do_set_property(self, pspec, value)
334 ###        else:
335 ###            ElementItem.do_set_property(self, pspec, value)
336 ###
337 ###
338 ###    def do_get_property(self, pspec):
339 ###        if pspec.name in SimpleRotation.__gproperties__:
340 ###            return SimpleRotation.do_get_property(self, pspec)
341 ###        else:
342 ###            return ElementItem.do_get_property(self, pspec)
343 ###
344 ###
345 ###    def get_end_pos(self):
346 ###        return self._assembly.get_required_pos_i(), \
347 ###            self._assembly.get_provided_pos_i()
348 ###
349 ###
350 ###    def update_end_main_point(self, old_rmpt, old_pmpt):
351 ###        rmpt, pmpt = self.get_end_pos()
352 ###        for c in self.children:
353 ###            if c._main_point == old_rmpt:
354 ###                c._main_point = rmpt
355 ###            elif c._main_point == old_pmpt:
356 ###                c._main_point = pmpt
357 ###
358 ###
359 ###    def save(self, save_func):
360 ###        """
361 ###        Save non-free connector end items.
362 ###        """
363 ###        ElementItem.save(self, save_func)
364 ###        for c in self.children:
365 ###            if c.subject:
366 ###                save_func(None, c)
367 ###
368 ###
369 ###    def load(self, name, value):
370 ###        """
371 ###        Change free connector end items position after direction update.
372 ###        """
373 ###        if name == 'dir':
374 ###            old_rmpt, old_pmpt = self.get_end_pos()
375 ###            ElementItem.load(self, name, value)
376 ###            self.update_end_main_point(old_rmpt, old_pmpt)
377 ###        else:
378 ###            ElementItem.load(self, name, value)
379 ###
380 ###
381 ###
382 ###    def rotate(self, step = 1):
383 ###        """
384 ###        Rotate assembly connector icon and set appropriate main point
385 ###        of connector end items.
386 ###        """
387 ###        old_rmpt, old_pmpt = self.get_end_pos()
388 ###        SimpleRotation.rotate(self, step)
389 ###        self.update_end_main_point(old_rmpt, old_pmpt)
390 ###
391 ###        self.request_update()
392 ###
393 ###
394 ###    def create_end(self, cls, mpt):
395 ###        """
396 ###        Create new connector end item.
397 ###
398 ###        Connector end item is added using canvas groupable interface.
399 ###        """
400 ###        c = cls(mpt = mpt)
401 ###        self.add(c)
402 ###        return c
403 ###
404 ###
405 ###    def on_update(self, affine):
406 ###        """
407 ###        Update assembly connector and its connector end items.
408 ###
409 ###        Maintain also free connector end items, so there is always two of
410 ###        them, one per main point kind (provided/required).
411 ###        """
412 ###        self._assembly.update_icon()
413 ###
414 ###        rmpt = self._assembly.get_required_pos_i()
415 ###        pmpt = self._assembly.get_provided_pos_i()
416 ###
417 ###        # store free connector end items by main point
418 ###        free_ends = {
419 ###            rmpt: set(),
420 ###            pmpt: set(),
421 ###        }
422 ###
423 ###        for c in self.children:
424 ###            # store free conector end items; checking for id and subject is
425 ###            # important during diagram load
426 ###            if not c.id and not c.subject:
427 ###                free_ends[c._main_point].add(c)
428 ###
429 ###        self.update_free_ends(free_ends, ProvidedConnectorEndItem, pmpt, affine)
430 ###        self.update_free_ends(free_ends, RequiredConnectorEndItem, rmpt, affine)
431 ###
432 ###        ElementItem.on_update(self, affine)
433 ###        GroupBase.on_update(self, affine)
434 ###
435 ###
436 ###    def update_free_ends(self, free_ends, cls, mpt, affine):
437 ###        """
438 ###        There should only one free connector end item for given main point.
439 ###
440 ###        Remove these free connector end items, which are no longer
441 ###        necessary.
442 ###       
443 ###        Create free connector end item in given main point if it is
444 ###        missing.
445 ###        """
446 ###        count = len(free_ends[mpt])
447 ###        if count > 1:
448 ###            # leave only one free connector end item
449 ###           
450 ###            # first, remove all, which are not in main point
451 ###            to_be_removed = list(free_ends[mpt])
452 ###            for c in to_be_removed:
453 ###                if c._handle.get_pos_i() != c._main_point:
454 ###                    c.unlink()
455 ###                    free_ends[mpt].remove(c)
456 ###
457 ###            # now, leave only one connector end item in main point
458 ###            to_be_removed = list(free_ends[mpt])
459 ###            if len(to_be_removed) > 1:
460 ###                for c in to_be_removed[1:]:
461 ###                    c.unlink()
462 ###                    free_ends[mpt].remove(c)
463 ###
464 ###            # finally, we should have one free connector end item in given main point
465 ###            assert len(free_ends[mpt]) == 1
466 ###
467 ###        elif count == 0:
468 ###            # if there is no free connector end items, then create one
469 ###            c = self.create_end(cls, mpt)
470 ###            self.update_child(c, affine)
471 ###
472 ###
473 ###    def on_subject_notify(self, pspec, notifiers = ()):
474 ###        """
475 ###        Set appropriate value of connector kind attribute. This is
476 ###        'assembly', of course.
477 ###        """
478 ###        ElementItem.on_subject_notify(self, pspec, notifiers)
479 ###        if self.subject:
480 ###            self.subject.kind = 'assembly'
481 ###        log.debug('creating assembly connector')
482 ###
483 ###
484 ###    def on_shape_iter(self):
485 ###        """
486 ###        Return assembled interface icon as a shape.
487 ###        """
488 ###        return self._assembly.on_shape_iter()
489 ###
490 ###
491 ###
492 ###class ConnectorItem(DiagramLine):
493 ###    pass
494 ####    """
495 ####    todo: ports
496 ####    """
497 ####   
498 ####    def __init__(self, id=None):
499 ####        DiagramLine.__init__(self, id)
500 ####
501 ####
502 ####    def allow_connect_handle(self, handle, connecting_to):
503 ####        return True
504 ####        return isinstance(connecting_to.subject, UML.Interface)
505 ####
506 ####
507 ####    def confirm_connect_handle (self, handle):
508 ####        c1 = self.handles[0].connected_to
509 ####        c2 = self.handles[-1].connected_to
510 ####        if c1 and c2:
511 ####            s1 = c1.subject
512 ####            s2 = c2.subject
513 ####            print s1.end
514 ####            print s2.end
515 ####            connector = self.relationship
516 ####            if not connector:
517 ####                connector = resource(UML.ElementFactory).create(UML.Connector)
518 ####                connector.kind = 'assembly'
519 ####                create_connector_end(connector, s1)
520 ####                create_connector_end(connector, s2)
521 ####               
522 ####            self.subject = connector
523 ####
524 ####
525 ####    def confirm_disconnect_handle(self, handle, was_connected_to):
526 ####        if self.subject:
527 ####            if __debug__:
528 ####                end1, end2 = tuple(self.subject.end)
529 ####                assert len(end1.role.end) > 0
530 ####                assert len(end2.role.end) > 0
531 ####            self.set_subject(None)
532 ####            if __debug__:
533 ####                assert end1.role is None
534 ####                assert end2.role is None
535 ####
536 ####
537 ####    def is_assembly(self):
538 ####        c1 = self.handles[0].connected_to
539 ####        c2 = self.handles[-1].connected_to
540 ####        return c1 and c2 \
541 ####            and isinstance(c1.subject, UML.Interface) \
542 ####            and isinstance(c2.subject, UML.Interface)
543 ####
544 ####
545 ####    def on_update(self, affine):
546 ####        DiagramLine.on_update(self, affine)
547 ####        x1, y1 = self.handles[0].get_pos_i()
548 ####        x2, y2 = self.handles[-1].get_pos_i()
549 ####        x = (x1 + x2) / 2
550 ####        y = (y1 + y2) / 2
551 ####        if self.is_assembly():
552 ####            self._assembly.line(_required_arcs[3])
553 ####            #self._assembly.set_pos((x, y))
554 ####
555 ####
556 ####    def on_shape_iter(self):
557 ####        if self.is_assembly():
558 ####            return iter([self._assembly])
559 ####        else:
560 ####            return DiagramLine.on_shape_iter(self)
561 ###
562 ###def create_connector_end(connector, role):
563 ###    """
564 ###    Create Connector End, set role and attach created end to
565 ###    connector.
566 ###    """
567 ###    end = resource(UML.ElementFactory).create(UML.ConnectorEnd)
568 ###    end.role = role
569 ###    connector.end = end
570 ###    assert end in role.end
571 ###    return end
572 ###
573 #### vim:sw=4:et
Note: See TracBrowser for help on using the browser.