def test_derivedunion(): class A(Element): a: relation_many[A] b: relation_one[A] u: relation_many[A] A.a = association("a", A) A.b = association("b", A, 0, 1) A.u = derivedunion("u", object, 0, "*", A.a, A.b) a = A() assert len(a.a) == 0, f"a.a = {a.a}" assert len(a.u) == 0, f"a.u = {a.u}" a.a = b = A() a.a = c = A() assert len(a.a) == 2, f"a.a = {a.a}" assert b in a.a assert c in a.a assert len(a.u) == 2, f"a.u = {a.u}" assert b in a.u assert c in a.u a.b = d = A() assert len(a.a) == 2, f"a.a = {a.a}" assert b in a.a assert c in a.a assert d == a.b assert len(a.u) == 3, f"a.u = {a.u}" assert b in a.u assert c in a.u assert d in a.u
def test_association_1_x(): # # 1:- # class A(Element): one: relation_one[Optional[B]] class B(Element): two: relation_one[A] A.one = association("one", B, 0, 1, opposite="two") B.two = association("two", A, 0, 1) a = A() b = B() a.one = b assert a.one is b assert b.two is a a.one = None assert a.one is None assert b.two is None a.one = b assert a.one is b assert b.two is a del a.one assert a.one is None assert b.two is None
def test_composite(): class A(Element): is_unlinked = False name: str comp: relation_many[A] other: relation_many[A] def unlink(self): self.is_unlinked = True Element.unlink(self) A.comp = association("comp", A, composite=True, opposite="other") A.other = association("other", A, composite=False, opposite="comp") a = A() a.name = "a" b = A() b.name = "b" a.comp = b assert b in a.comp assert a in b.other a.unlink() assert a.is_unlinked assert b.is_unlinked
def test_association_n_n(): # # n:n # class A(Element): one: relation_many[B] class B(Element): two: relation_many[A] class C(Element): pass A.one = association("one", B, 0, "*", opposite="two") B.two = association("two", A, 0, "*", opposite="one") a1 = A() a2 = A() b1 = B() b2 = B() a1.one = b1 assert b1 in a1.one assert a1 in b1.two assert not a2.one assert not b2.two a1.one = b2 assert b1 in a1.one assert b2 in a1.one assert a1 in b1.two assert a1 in b2.two assert not a2.one a2.one = b1 assert len(a1.one) == 2 assert len(a2.one) == 1 assert len(b1.two) == 2 assert len(b2.two) == 1 assert b1 in a1.one assert b2 in a1.one assert a1 in b1.two assert a1 in b2.two assert b1 in a2.one assert a2 in b1.two del a1.one[b1] assert len(a1.one) == 1 assert len(a2.one) == 1 assert len(b1.two) == 1 assert len(b2.two) == 1 assert b1 not in a1.one assert b2 in a1.one assert a1 not in b1.two assert a1 in b2.two assert b1 in a2.one assert a2 in b1.two
def test_undo_association_1_n(self): from gaphor.core.modeling import Element from gaphor.core.modeling.properties import association event_manager = EventManager() undo_manager = UndoManager(event_manager) element_factory = ElementFactory(event_manager) class A(Element): pass class B(Element): pass A.one = association("one", B, lower=0, upper=1, opposite="two") B.two = association("two", A, lower=0, upper="*", opposite="one") a1 = element_factory.create(A) a2 = element_factory.create(A) b1 = element_factory.create(B) element_factory.create(B) undo_manager.begin_transaction() b1.two = a1 undo_manager.commit_transaction() assert a1 in b1.two assert b1 is a1.one assert len(undo_manager._undo_stack) == 1 assert len(undo_manager._undo_stack[0]._actions) == 2, undo_manager._undo_stack[ 0 ]._actions undo_manager.undo_transaction() assert len(b1.two) == 0 assert a1.one is None assert undo_manager.can_redo() assert len(undo_manager._redo_stack) == 1 assert len(undo_manager._redo_stack[0]._actions) == 2, undo_manager._redo_stack[ 0 ]._actions undo_manager.redo_transaction() assert a1 in b1.two assert b1 is a1.one undo_manager.begin_transaction() b1.two = a2 undo_manager.commit_transaction() assert a1 in b1.two assert a2 in b1.two assert b1 is a1.one assert b1 is a2.one undo_manager.shutdown()
def test_uml_associations(self): from gaphor.core.modeling.event import AssociationUpdated from gaphor.core.modeling.properties import association, derivedunion from gaphor.UML import Element event_manager = EventManager() undo_manager = UndoManager(event_manager) element_factory = ElementFactory(event_manager) class A(Element): is_unlinked = False def unlink(self): self.is_unlinked = True Element.unlink(self) A.a1 = association("a1", A, upper=1) A.a2 = association("a2", A, upper=1) A.b1 = association("b1", A, upper="*") A.b2 = association("b2", A, upper="*") A.b3 = association("b3", A, upper=1) A.derived_a = derivedunion("derived_a", A, 0, 1, A.a1, A.a2) A.derived_b = derivedunion("derived_b", A, 0, "*", A.b1, A.b2, A.b3) events = [] @event_handler(AssociationUpdated) def handler(event, events=events): events.append(event) event_manager.subscribe(handler) try: a = element_factory.create(A) undo_manager.begin_transaction() a.a1 = element_factory.create(A) undo_manager.commit_transaction() assert ( len(events) == 2 ), events # both AssociationSet and DerivedSet events assert events[0].property is A.a1 assert undo_manager.can_undo() undo_manager.undo_transaction() assert not undo_manager.can_undo() assert undo_manager.can_redo() assert len(events) == 4, events assert events[2].property is A.a1 finally: event_manager.unsubscribe(handler) undo_manager.shutdown()
def test_undo_association_1_x(self): from gaphor.core.modeling import Element from gaphor.core.modeling.properties import association event_manager = EventManager() undo_manager = UndoManager(event_manager) element_factory = ElementFactory(event_manager) class A(Element): pass class B(Element): pass A.one = association("one", B, 0, 1, opposite="two") B.two = association("two", A, 0, 1) a = element_factory.create(A) b = element_factory.create(B) assert a.one is None assert b.two is None undo_manager.begin_transaction() a.one = b undo_manager.commit_transaction() assert a.one is b assert b.two is a assert len(undo_manager._undo_stack) == 1 assert len(undo_manager._undo_stack[0]._actions) == 2, undo_manager._undo_stack[ 0 ]._actions undo_manager.undo_transaction() assert a.one is None assert b.two is None assert undo_manager.can_redo() assert len(undo_manager._redo_stack) == 1 assert len(undo_manager._redo_stack[0]._actions) == 2, undo_manager._redo_stack[ 0 ]._actions undo_manager.redo_transaction() assert len(undo_manager._undo_stack) == 1 assert len(undo_manager._undo_stack[0]._actions) == 2, undo_manager._undo_stack[ 0 ]._actions assert b.two is a assert a.one is b undo_manager.shutdown()
def test_undo_association_1_n(event_manager, element_factory, undo_manager): class A(Element): pass class B(Element): pass A.one = association("one", B, lower=0, upper=1, opposite="two") B.two = association("two", A, lower=0, upper="*", opposite="one") with Transaction(event_manager): a1 = element_factory.create(A) a2 = element_factory.create(A) b1 = element_factory.create(B) element_factory.create(B) undo_manager.clear_undo_stack() undo_manager.begin_transaction() b1.two = a1 undo_manager.commit_transaction() assert a1 in b1.two assert b1 is a1.one assert len(undo_manager._undo_stack) == 1 assert len(undo_manager._undo_stack[0]._actions ) == 2, undo_manager._undo_stack[0]._actions undo_manager.undo_transaction() assert len(b1.two) == 0 assert a1.one is None assert undo_manager.can_redo() assert len(undo_manager._redo_stack) == 1 assert len(undo_manager._redo_stack[0]._actions ) == 2, undo_manager._redo_stack[0]._actions undo_manager.redo_transaction() assert a1 in b1.two assert b1 is a1.one undo_manager.begin_transaction() b1.two = a2 undo_manager.commit_transaction() assert a1 in b1.two assert a2 in b1.two assert b1 is a1.one assert b1 is a2.one
def test_set_association_outside_transaction(undo_manager, element_factory, event_manager): class A(Element): pass A.a = association("a", A, upper=1, opposite="b") A.b = association("b", A, opposite="a") with Transaction(event_manager): a = element_factory.create(A) with pytest.raises(NotInTransactionException): a.a = a assert not a.b assert a.a is None
def test_association_swap(): class A(Element): one: relation_many[B] class B(Element): pass class C(Element): pass A.one = association("one", B, 0, "*") a = A() b1 = B() b2 = B() a.one = b1 a.one = b2 assert a.one.size() == 2 assert a.one[0] is b1 assert a.one[1] is b2 events: List[object] = [] @event_handler(AssociationUpdated) def handler(event, events=events): events.append(event) a.one.swap(b1, b2) assert a.one.size() == 2 assert a.one[0] is b2 assert a.one[1] is b1
def test_association_unlink_1(): class A(Element): one: relation_many[B] class B(Element): pass class C(Element): pass A.one = association("one", B, 0, "*") a1 = A() a2 = A() b1 = B() b2 = B() a1.one = b1 a1.one = b2 assert b1 in a1.one assert b2 in a1.one a2.one = b1 # remove b1 from all elements connected to b1 # also the signal should be removed b1.unlink() assert b1 not in a1.one assert b2 in a1.one
class Presentation(Element, Generic[S]): """ This presentation is used to link the behaviors of `gaphor.core.modeling` and `gaphas.Item`. """ def __init__(self, id=None, model=None): super().__init__(id, model) def update(event): self.request_update() self._watcher = self.watcher(default_handler=update) self.watch("subject") subject: relation_one[S] = association("subject", Element, upper=1, opposite="presentation") handles: Callable[[Presentation], List[Handle]] request_update: Callable[[Presentation], None] canvas: Optional[Canvas] matrix: Matrix def watch(self, path, handler=None): """ Watch a certain path of elements starting with the DiagramItem. The handler is optional and will default to a simple self.request_update(). Watches should be set in the constructor, so they can be registered and unregistered in one shot. This interface is fluent(returns self). """ self._watcher.watch(path, handler) return self def subscribe_all(self): """ Subscribe all watched paths, as defined through `watch()`. """ self._watcher.subscribe_all() def unsubscribe_all(self): """ Subscribe all watched paths, as defined through `watch()`. """ self._watcher.unsubscribe_all() def unlink(self): """ Remove the item from the canvas and set subject to None. """ if self.canvas: self.canvas.remove(self) super().unlink()
def test_association_n_x(): # # n:- # class A(Element): one: relation_many[B] class B(Element): two: relation_one[A] A.one = association("one", B, 0, "*", opposite="two") B.two = association("two", A, 0, 1) a = A() b = B() a.one = b assert b in a.one assert b.two is a
def test_undo_association_1_x(event_manager, element_factory, undo_manager): class A(Element): pass class B(Element): pass A.one = association("one", B, 0, 1, opposite="two") B.two = association("two", A, 0, 1) with Transaction(event_manager): a = element_factory.create(A) b = element_factory.create(B) undo_manager.clear_undo_stack() assert a.one is None assert b.two is None undo_manager.begin_transaction() a.one = b undo_manager.commit_transaction() assert a.one is b assert b.two is a assert len(undo_manager._undo_stack) == 1 assert len(undo_manager._undo_stack[0]._actions ) == 2, undo_manager._undo_stack[0]._actions undo_manager.undo_transaction() assert a.one is None assert b.two is None assert undo_manager.can_redo() assert len(undo_manager._redo_stack) == 1 assert len(undo_manager._redo_stack[0]._actions ) == 2, undo_manager._redo_stack[0]._actions undo_manager.redo_transaction() assert len(undo_manager._undo_stack) == 1 assert len(undo_manager._undo_stack[0]._actions ) == 2, undo_manager._undo_stack[0]._actions assert b.two is a assert a.one is b
def test_uml_associations(event_manager, element_factory, undo_manager): class A(Element): is_unlinked = False def unlink(self): self.is_unlinked = True Element.unlink(self) A.a1 = association("a1", A, upper=1) A.a2 = association("a2", A, upper=1) A.b1 = association("b1", A, upper="*") A.b2 = association("b2", A, upper="*") A.b3 = association("b3", A, upper=1) A.derived_a = derivedunion("derived_a", A, 0, 1, A.a1, A.a2) A.derived_b = derivedunion("derived_b", A, 0, "*", A.b1, A.b2, A.b3) events = [] @event_handler(AssociationUpdated) def handler(event, events=events): events.append(event) event_manager.subscribe(handler) undo_manager.begin_transaction() a = element_factory.create(A) a.a1 = element_factory.create(A) undo_manager.commit_transaction() assert len( events) == 2, events # both AssociationSet and DerivedSet events assert events[0].property is A.a1 assert undo_manager.can_undo() undo_manager.undo_transaction() assert not undo_manager.can_undo() assert undo_manager.can_redo() assert len(events) == 4, events assert events[2].property is A.a1
def test_association_1_1(): # # 1:1 # class A(Element): one: relation_one[B] class B(Element): two: relation_one[A] class C(Element): pass A.one = association("one", B, 0, 1, opposite="two") B.two = association("two", A, 0, 1, opposite="one") a = A() b = B() a.one = b a.one = b assert a.one is b assert b.two is a a.one = B() assert a.one is not b assert b.two is None c = C() try: a.one = c except Exception: pass else: assert a.one is not c del a.one assert a.one is None assert b.two is None
def test_derivedunion_listmixins(): class A(Element): a: relation_many[A] b: relation_many[A] u: relation_many[A] name: attribute[str] A.a = association("a", A) A.b = association("b", A) A.u = derivedunion("u", A, 0, "*", A.a, A.b) A.name = attribute("name", str, "default") a = A() a.a = A() a.a = A() a.b = A() a.a[0].name = "foo" a.a[1].name = "bar" a.b[0].name = "baz" assert list(a.a[:].name) == ["foo", "bar"] assert sorted(list(a.u[:].name)) == ["bar", "baz", "foo"]
def test_association_unlink_2(): # # unlink # class A(Element): one: relation_many[B] class B(Element): two: relation_many[A] class C(Element): pass A.one = association("one", B, 0, "*", opposite="two") B.two = association("two", A, 0, "*") a1 = A() a2 = A() b1 = B() b2 = B() a1.one = b1 a1.one = b2 assert b1 in a1.one assert b2 in a1.one assert a1 in b1.two assert a1 in b2.two a2.one = b1 # remove b1 from all elements connected to b1 # also the signal should be removed b1.unlink() assert b1 not in a1.one assert b2 in a1.one assert a1 not in b1.two assert a1 in b2.two
def test_derivedunion_notify_for_multiple_derived_properties(): class A(Element): pass class E(Element): notified = False a: relation_many[A] aa: relation_many[A] u: relation_many[A] def handle(self, event): if event.property is E.u: self.notified = True E.a = association("a", A) E.aa = association("aa", A) E.u = derivedunion("u", A, 0, "*", E.a, E.aa) e = E() e.a = A() assert e.notified is True
def test_set_derived_union_outside_transaction(undo_manager, element_factory, event_manager): class A(Element): pass A.a = association("a", A, upper=1, opposite="b") A.b = association("b", A, opposite="a") A.derived_a = derivedunion("derived_a", A, 0, 1, A.a) A.derived_b = derivedunion("derived_b", A, 0, "*", A.b) with Transaction(event_manager): a = element_factory.create(A) with pytest.raises(NotInTransactionException): a.a = a with pytest.raises(NotInTransactionException): a.b = a assert not a.b assert not a.a assert not a.derived_a assert not a.derived_b
def test_association_swap(): class A(Element): one: relation_many[B] class B(Element): pass class C(Element): pass A.one = association("one", B, 0, "*") a = A() b1 = B() b2 = B() a.one = b1 a.one = b2 assert a.one.size() == 2 assert a.one[0] is b1 assert a.one[1] is b2 events: List[object] = [] @event_handler(AssociationUpdated) def handler(event, events=events): events.append(event) # Application.register_handler(handler) # try: a.one.swap(b1, b2) # assert len(events) == 1 # assert events[0].property is A.one # assert events[0].element is a # finally: # Application.unregister_handler(handler) assert a.one.size() == 2 assert a.one[0] is b2 assert a.one[1] is b1
class DecisionNodeItem(ElementPresentation, ActivityNodeItem): """Representation of decision or merge node.""" def __init__(self, diagram, id=None): super().__init__(diagram, id, width=20, height=30) no_movable_handles(self) self.shape = IconBox( Box(draw=draw_decision_node), # Text should be left-top Text( text=lambda: stereotypes_str(self.subject), ), EditableText(text=lambda: self.subject and self.subject.name or ""), ) self.watch("subject[NamedElement].name") self.watch("subject.appliedStereotype.classifier.name") combined: relation_one[UML.ControlNode] = association( "combined", UML.ControlNode, upper=1 )
class Diagram(PackageableElement): """Diagrams may contain model elements and can be owned by a Package.""" package: relation_one[Package] def __init__( self, id: Optional[Id] = None, model: Optional[RepositoryProtocol] = None ): """Initialize the diagram with an optional id and element model. The diagram also has a canvas. """ super().__init__(id, model) self._connections = gaphas.connections.Connections() self._connections.add_handler(self._on_constraint_solved) self._registered_views: Set[gaphas.view.model.View] = set() self._watcher = self.watcher() self._watcher.watch("ownedPresentation", self._presentation_removed) # Record all items changed during constraint solving, # so their `post_update()` method can be called. self._resolved_items: Set[gaphas.item.Item] = set() ownedPresentation: relation_many[Presentation] = association( "ownedPresentation", Presentation, composite=True, opposite="diagram" ) def _presentation_removed(self, event): if isinstance(event, AssociationDeleted) and event.old_value: self._update_views(removed_items=(event.old_value,)) @property def styleSheet(self) -> Optional[StyleSheet]: return next(self.model.select(StyleSheet), None) def style(self, node: StyleNode) -> Style: style_sheet = self.styleSheet return { **FALLBACK_STYLE, # type: ignore[misc] **(style_sheet.match(node) if style_sheet else {}), } def save(self, save_func): """Apply the supplied save function to this diagram and the canvas.""" super().save(save_func) save_func("canvas", PseudoCanvas(self)) def postload(self): """Handle post-load functionality for the diagram.""" super().postload() def create(self, type, parent=None, subject=None): """Create a new diagram item on the diagram. It is created with a unique ID and it is attached to the diagram's root item. The type parameter is the element class to create. The new element also has an optional parent and subject. """ return self.create_as(type, str(uuid.uuid1()), parent, subject) def create_as(self, type, id, parent=None, subject=None): if not (type and issubclass(type, Presentation)): raise TypeError( f"Type {type} can not be added to a diagram as it is not a diagram item" ) # Avoid events that reference this element before its created-event is emitted. with self.model.block_events(): item = type(diagram=self, id=id) assert isinstance( item, gaphas.Item ), f"Type {type} does not comply with Item protocol" self.model.handle(DiagramItemCreated(self, item)) if subject: item.subject = subject if parent: item.parent = parent self.request_update(item) return item def lookup(self, id): for item in self.get_all_items(): if item.id == id: return item def unlink(self): """Unlink all canvas items then unlink this diagram.""" for item in self.ownedPresentation: self.connections.remove_connections_to_item(item) self._watcher.unsubscribe_all() super().unlink() def select(self, expression=None) -> Iterator[Presentation]: """Return a iterator of all canvas items that match expression.""" if expression is None: yield from self.get_all_items() elif isinstance(expression, type): yield from (e for e in self.get_all_items() if isinstance(e, expression)) else: yield from (e for e in self.get_all_items() if expression(e)) @property def connections(self) -> gaphas.connections.Connections: return self._connections def get_all_items(self) -> Iterable[Presentation]: """Get all items owned by this diagram, ordered depth-first.""" def iter_children(item): for child in item.children: yield child yield from iter_children(child) for root in self.ownedPresentation: if not root.parent: yield root yield from iter_children(root) def get_parent(self, item: Presentation) -> Optional[Presentation]: return item.parent def get_children(self, item: Presentation) -> Iterable[Presentation]: return iter(item.children) def sort(self, items: Sequence[Presentation]) -> Iterable[Presentation]: items_set = set(items) return (n for n in self.get_all_items() if n in items_set) def request_update( self, item: gaphas.item.Item, update: bool = True, matrix: bool = True ) -> None: if update and matrix: self._update_views(dirty_items=(item,), dirty_matrix_items=(item,)) elif update: self._update_views(dirty_items=(item,)) elif matrix: self._update_views(dirty_matrix_items=(item,)) def _update_views(self, dirty_items=(), dirty_matrix_items=(), removed_items=()): """Send an update notification to all registered views.""" for v in self._registered_views: v.request_update(dirty_items, dirty_matrix_items, removed_items) @gaphas.decorators.nonrecursive def update_now( self, dirty_items: Sequence[Presentation], dirty_matrix_items: Sequence[Presentation] = (), ) -> None: """Update the diagram canvas.""" sort = self.sort def dirty_items_with_ancestors(): for item in set(dirty_items): yield item yield from gaphas.canvas.ancestors(self, item) all_dirty_items = list(reversed(list(sort(dirty_items_with_ancestors())))) contexts = self._pre_update_items(all_dirty_items) self._resolved_items.clear() self._connections.solve() all_dirty_items.extend(self._resolved_items) self._post_update_items(reversed(list(sort(all_dirty_items))), contexts) def _pre_update_items(self, items): contexts = {} for item in items: context = UpdateContext(style=self.style(StyledItem(item))) item.pre_update(context) contexts[item] = context return contexts def _post_update_items(self, items, contexts): for item in items: context = contexts.get(item) if not context: context = UpdateContext(style=self.style(StyledItem(item))) item.post_update(context) def _on_constraint_solved(self, cinfo: gaphas.connections.Connection) -> None: dirty_items = set() if cinfo.item: dirty_items.add(cinfo.item) self._resolved_items.add(cinfo.item) if cinfo.connected: dirty_items.add(cinfo.connected) self._resolved_items.add(cinfo.connected) if dirty_items: self._update_views(dirty_items) def register_view(self, view: gaphas.view.model.View[Presentation]) -> None: self._registered_views.add(view) def unregister_view(self, view: gaphas.view.model.View[Presentation]) -> None: self._registered_views.discard(view)
return contexts def _post_update_items(self, items, contexts): for item in items: context = contexts.get(item) if not context: context = UpdateContext(style=self.style(StyledItem(item))) item.post_update(context) def _on_constraint_solved(self, cinfo: gaphas.connections.Connection) -> None: dirty_items = set() if cinfo.item: dirty_items.add(cinfo.item) self._resolved_items.add(cinfo.item) if cinfo.connected: dirty_items.add(cinfo.connected) self._resolved_items.add(cinfo.connected) if dirty_items: self._update_views(dirty_items) def register_view(self, view: gaphas.view.model.View[Presentation]) -> None: self._registered_views.add(view) def unregister_view(self, view: gaphas.view.model.View[Presentation]) -> None: self._registered_views.discard(view) Presentation.diagram = association( "diagram", Diagram, upper=1, opposite="ownedPresentation" )
type: attribute[str] class C4Database(C4Container): pass class C4Person(Actor): description: attribute[str] location: attribute[str] C4Container.description = attribute("description", str) C4Container.location = attribute("location", str) C4Container.ownerContainer = association("ownerContainer", C4Container, upper=1, opposite="owningContainer") C4Container.owningContainer = association("owningContainer", C4Container, composite=True, opposite="ownerContainer") C4Container.technology = attribute("technology", str) C4Container.type = attribute("type", str) C4Person.description = attribute("description", str) C4Person.location = attribute("location", str) C4Container.namespace.subsets.add( C4Container.ownerContainer) # type: ignore[attr-defined] C4Container.ownedMember.subsets.add( C4Container.owningContainer) # type: ignore[attr-defined]
class PartitionItem(ElementPresentation): def __init__(self, diagram, id=None): super().__init__(diagram, id) self.min_height = 300 self.shape = Box( style={ "line-width": 2.4, "padding": (2, 2, 2, 2), "vertical-align": VerticalAlign.TOP, }, draw=self.draw_swimlanes, ) self.watch("subject[NamedElement].name") self.watch("subject.appliedStereotype.classifier.name") self.watch("partition.name") self.watch("partition") partition = association("partition", UML.ActivityPartition, composite=True) def postload(self): super().postload() if self.subject and self.subject not in self.partition: self.partition = self.subject def pre_update(self, context: DrawContext) -> None: """Set the min width of all the swimlanes.""" self.min_width = 150 * len(self.partition) def draw_swimlanes(self, box: Box, context: DrawContext, bounding_box: Rectangle) -> None: """Draw the vertical partitions as connected swimlanes. The partitions are open on the bottom. We divide the total size by the total number of partitions and space them evenly. """ cr = context.cairo cr.set_line_width(context.style["line-width"]) self.draw_outline(bounding_box, context) self.draw_partitions(bounding_box, context) stroke(context) self.draw_hover(bounding_box, context) def draw_hover(self, bounding_box: Rectangle, context: DrawContext) -> None: """Add dashed line on bottom of swimlanes when hovered.""" if context.hovered or context.dropzone: cr = context.cairo with cairo_state(cr): cr.set_dash((1.0, 5.0), 0) cr.set_line_width(1.0) cr.rectangle(0, 0, bounding_box.width, bounding_box.height) cr.stroke() def draw_partitions(self, bounding_box: Rectangle, context: DrawContext) -> None: """Draw partition separators and add the name.""" cr = context.cairo if self.partition: partition_width = bounding_box.width / len(self.partition) else: partition_width = bounding_box.width / 2 layout = Layout() style = context.style padding_top = context.style["padding"][0] for num, partition in enumerate(self.partition): cr.move_to(partition_width * num, 0) cr.line_to(partition_width * num, bounding_box.height) layout.set(text=partition.name, font=style) cr.move_to(partition_width * num, padding_top * 3) layout.show_layout( cr, partition_width, default_size=(partition_width, HEADER_HEIGHT), ) def draw_outline(self, bounding_box: Rectangle, context: DrawContext) -> None: """Draw the outline and header of the swimlanes.""" cr = context.cairo cr.move_to(0, bounding_box.height) cr.line_to(0, 0) cr.line_to(bounding_box.width, 0) cr.line_to(bounding_box.width, bounding_box.height) cr.move_to(0, bounding_box.height) cr.line_to(0, HEADER_HEIGHT) cr.line_to(0 + bounding_box.width, HEADER_HEIGHT) cr.line_to(0 + bounding_box.width, bounding_box.height)
class Viewpoint(Class): concernList: relation_many[Comment] language: attribute[str] presentation: attribute[str] purpose: attribute[str] stakeholder: relation_many[Stakeholder] class _Refine: pass AbstractRequirement.externalId = attribute("externalId", str) AbstractRequirement.text = attribute("text", str) AdjuntProperty.principal = association("principal", Element, upper=1) Block.isEncapsulated = attribute("isEncapsulated", int) BoundReference.boundend = association("boundend", ConnectorEnd, upper=1) ChangeSructuralFeatureEvent.structuralFeature = association( "structuralFeature", StructuralFeature, upper=1 ) ConnectorProperty.connector = association("connector", Connector, upper=1) DirectedFeature.featureDirection = enumeration( "kind", ("required", "providedRequired", "provided"), "required" ) DirectedRelationshipPropertyPath.sourceContext = association( "sourceContext", Classifier, upper=1 ) DirectedRelationshipPropertyPath.sourcePropertyPath = association( "sourcePropertyPath", Property )
# 40: override StyleSheet # defined in gaphor.core.modeling.presentation NamedElement.name = attribute("name", str) Comment.body = attribute("body", str) # 43: override StyleSheet.styleSheet # defined in gaphor.core.modeling.presentation # 52: override Presentation.subject # defined in gaphor.core.modeling.presentation # 49: override Element.presentation # defined in gaphor.core.modeling.presentation Comment.annotatedElement = association("annotatedElement", Element, opposite="ownedComment") Element.ownedComment = association("ownedComment", Comment, opposite="annotatedElement") # 20: override NamedElement.qualifiedName(NamedElement.namespace): derived[List[str]] def _namedelement_qualifiedname(self) -> List[str]: """ Returns the qualified name of the element as a tuple """ if self.namespace: return _namedelement_qualifiedname(self.namespace) + [self.name] else: return [self.name]
# 43: override StyleSheet # defined in gaphor.core.modeling.presentation NamedElement.name = attribute("name", str) Comment.body = attribute("body", str) # 46: override StyleSheet.styleSheet # defined in gaphor.core.modeling.presentation # 58: override Presentation.subject # defined in gaphor.core.modeling.presentation # 52: override Element.presentation # defined in gaphor.core.modeling.presentation Comment.annotatedElement = association("annotatedElement", Element, opposite="comment") Element.comment = association("comment", Comment, opposite="annotatedElement") # 70: override Diagram.ownedPresentation # defined in gaphor.core.modeling.presentation # 55: override Presentation.diagram # defined in gaphor.core.modeling.presentation # 61: override Presentation.parent # defined in gaphor.core.modeling.presentation # 64: override Presentation.children # defined in gaphor.core.modeling.presentation # 22: override NamedElement.qualifiedName(NamedElement.namespace): derived[List[str]]
def test_association_1_n(): # # 1:n # class A(Element): one: relation_one[B] class B(Element): two: relation_many[A] class C(Element): pass A.one = association("one", B, lower=0, upper=1, opposite="two") B.two = association("two", A, lower=0, upper="*", opposite="one") a1 = A() a2 = A() b1 = B() b2 = B() b1.two = a1 assert len(b1.two) == 1, "len(b1.two) == %d" % len(b1.two) assert a1 in b1.two assert a1.one is b1, f"{a1.one}/{b1}" b1.two = a1 b1.two = a1 assert len(b1.two) == 1, "len(b1.two) == %d" % len(b1.two) assert a1 in b1.two assert a1.one is b1, f"{a1.one}/{b1}" b1.two = a2 assert a1 in b1.two assert a2 in b1.two assert a1.one is b1 assert a2.one is b1 try: del b1.two except Exception: pass # ok else: assert b1.two != [] assert a1 in b1.two assert a2 in b1.two assert a1.one is b1 assert a2.one is b1 b1.two.remove(a1) assert len(b1.two) == 1 assert a1 not in b1.two assert a2 in b1.two assert a1.one is None assert a2.one is b1 a2.one = b2 assert len(b1.two) == 0 assert len(b2.two) == 1 assert a2 in b2.two assert a1.one is None assert a2.one is b2 del b1.two[a1]