Пример #1
0
class ArtifactItem(ElementPresentation, Classified):
    def __init__(self, id=None, model=None):
        super().__init__(id, model)

        self.watch("show_stereotypes", self.update_shapes)
        self.watch("subject[NamedElement].name")
        self.watch("subject.appliedStereotype", self.update_shapes)
        self.watch("subject.appliedStereotype.classifier.name")
        self.watch("subject.appliedStereotype.slot", self.update_shapes)
        self.watch("subject.appliedStereotype.slot.definingFeature.name")
        self.watch("subject.appliedStereotype.slot.value", self.update_shapes)

    show_stereotypes: attribute[int] = attribute("show_stereotypes", int)

    def update_shapes(self, event=None):
        self.shape = Box(
            Box(
                Text(text=lambda: UML.model.stereotypes_str(self.subject),),
                EditableText(
                    text=lambda: self.subject.name or "",
                    style={"font-weight": FontWeight.BOLD},
                ),
                style={"padding": (4, 34, 4, 4), "min-height": 44},
                draw=draw_artifact_icon,
            ),
            *(self.show_stereotypes and stereotype_compartments(self.subject) or []),
            style={
                "min-width": 100,
                "min-height": 50,
                "vertical-align": VerticalAlign.TOP,
            },
            draw=draw_border
        )
Пример #2
0
class ComponentItem(ElementPresentation, Classified):
    def __init__(self, diagram, id=None):
        super().__init__(diagram, id)

        self.watch("show_stereotypes", self.update_shapes)
        self.watch("subject[NamedElement].name")
        self.watch("subject.appliedStereotype", self.update_shapes)
        self.watch("subject.appliedStereotype.classifier.name")
        self.watch("subject.appliedStereotype.slot", self.update_shapes)
        self.watch("subject.appliedStereotype.slot.definingFeature.name")
        self.watch("subject.appliedStereotype.slot.value", self.update_shapes)
        self.watch("subject[Classifier].useCase", self.update_shapes)

    show_stereotypes: attribute[int] = attribute("show_stereotypes", int)

    def update_shapes(self, event=None):
        self.shape = Box(
            Box(
                Text(text=lambda: UML.model.stereotypes_str(self.subject), ),
                EditableText(
                    text=lambda: self.subject.name or "",
                    style={"font-weight": FontWeight.BOLD},
                ),
                style={"padding": (4, 32, 4, 4)},
                draw=draw_component_icon,
            ),
            *(self.show_stereotypes and stereotype_compartments(self.subject)
              or []),
            style={
                "vertical-align":
                VerticalAlign.TOP
                if self.diagram and self.children else VerticalAlign.MIDDLE,
            },
            draw=draw_border)
Пример #3
0
class StyleSheet(Element):
    _compiled_style_sheet: CompiledStyleSheet

    def __init__(self, id=None, model=None):
        super().__init__(id, model)

        self.compile_style_sheet()

    styleSheet: attribute[str] = attribute("styleSheet", str, "")

    def compile_style_sheet(self) -> None:
        self._compiled_style_sheet = CompiledStyleSheet(self.styleSheet)

    def match(self, node: StyleNode) -> Style:
        return self._compiled_style_sheet.match(node)

    def postload(self):
        super().postload()
        self.compile_style_sheet()

    def handle(self, event):
        # Ensure compiled style sheet is always up to date:
        if (isinstance(event, AttributeUpdated)
                and event.property is StyleSheet.styleSheet):
            self.compile_style_sheet()

        super().handle(event)
Пример #4
0
class StyleSheet(Element):
    def __init__(self, id=None, model=None):
        super().__init__(id, model)
        self._style = read_style_py()

    styleSheet: attribute[str] = attribute("styleSheet", str)

    def item_style(self, item):
        return self._style
Пример #5
0
class NodeItem(ElementPresentation, Classified):
    """
    Representation of node or device from UML Deployment package.
    """
    def __init__(self, id=None, model=None):
        super().__init__(id, model)

        self.watch("show_stereotypes", self.update_shapes)
        self.watch("subject[NamedElement].name")
        self.watch("subject.appliedStereotype", self.update_shapes)
        self.watch("subject.appliedStereotype.classifier.name")
        self.watch("subject.appliedStereotype.slot", self.update_shapes)
        self.watch("subject.appliedStereotype.slot.definingFeature.name")
        self.watch("subject.appliedStereotype.slot.value", self.update_shapes)
        self.watch("subject[Node].ownedConnector", self.update_shapes)
        self.watch("subject[Node].deployment", self.update_shapes)

    show_stereotypes: attribute[int] = attribute("show_stereotypes", int)

    def update_shapes(self, event=None):
        self.shape = Box(
            Box(
                Text(
                    text=lambda: UML.model.stereotypes_str(
                        self.subject,
                        isinstance(self.subject, UML.Device) and
                        ("device", ) or (),
                    ),
                    style={
                        "min-width": 0,
                        "min-height": 0
                    },
                ),
                EditableText(
                    text=lambda: self.subject.name or "",
                    style={"font-weight": FontWeight.BOLD},
                ),
                style={"padding": (4, 4, 4, 4)},
            ),
            *(self.show_stereotypes and stereotype_compartments(self.subject)
              or []),
            style={
                "min-width":
                100,
                "min-height":
                50,
                "vertical-align":
                VerticalAlign.TOP if self.canvas
                and self.canvas.get_children(self) else VerticalAlign.MIDDLE,
            },
            draw=draw_node)

    def postload(self):
        self.update_shapes()
        super().postload()
Пример #6
0
class PropertyItem(ElementPresentation[UML.Property], Named):
    def __init__(self, diagram, id=None):
        super().__init__(diagram, id)

        self.watch("show_stereotypes", self.update_shapes)
        self.watch("subject[Property].name")
        self.watch("subject[Property].type.name")
        self.watch("subject[Property].lowerValue")
        self.watch("subject[Property].upperValue")
        self.watch("subject.appliedStereotype", self.update_shapes)
        self.watch("subject.appliedStereotype.classifier.name")
        self.watch("subject.appliedStereotype.slot", self.update_shapes)
        self.watch("subject.appliedStereotype.slot.definingFeature.name")
        self.watch("subject.appliedStereotype.slot.value", self.update_shapes)
        self.watch("subject[Property].aggregation", self.update_shapes)

    show_stereotypes: attribute[int] = attribute("show_stereotypes", int)

    def alignment(self) -> VerticalAlign:
        if self.diagram and self.children:
            return VerticalAlign.TOP
        else:
            return VerticalAlign.MIDDLE

    def dash(self) -> Sequence[Union[int, float]]:
        if self.subject and self.subject.aggregation != "composite":
            return (7.0, 5.0)
        else:
            return ()

    def update_shapes(self, event=None):
        self.shape = Box(
            Box(
                Text(
                    text=lambda: UML.model.stereotypes_str(self.subject),
                ),
                EditableText(
                    text=lambda: format_property(
                        self.subject, type=True, multiplicity=True
                    )
                    or "",
                    style={"font-weight": FontWeight.BOLD},
                ),
                style={"padding": (12, 4, 12, 4), "min-height": 44},
            ),
            *(self.show_stereotypes and stereotype_compartments(self.subject) or []),
            style={
                "min-width": 100,
                "min-height": 50,
                "vertical-align": self.alignment(),
                "dash-style": self.dash(),
            },
            draw=draw_border
        )
Пример #7
0
def test_attributes():
    class A(Element):
        a: attribute[str]

    A.a = attribute("a", str, "default")

    a = A()
    assert a.a == "default", a.a
    a.a = "bar"
    assert a.a == "bar", a.a
    del a.a
    assert a.a == "default"
    try:
        a.a = 1
    except AttributeError:
        pass  # ok
    else:
        assert 0, "should not set integer"
Пример #8
0
class ObjectNodeItem(ElementPresentation, Named):
    """Representation of object node. Object node is ordered and has upper
    bound specification.

    Ordering information can be hidden by user.
    """
    def __init__(self, diagram, id=None):
        super().__init__(
            diagram,
            id,
            shape=IconBox(
                Box(
                    Text(text=lambda: stereotypes_str(self.subject), ),
                    EditableText(text=lambda: self.subject.name or ""),
                    style={
                        "min-width": 50,
                        "min-height": 30,
                        "padding": (5, 10, 5, 10),
                    },
                    draw=draw_border,
                ),
                Text(text=lambda: self.subject.upperBound not in
                     (None, "", DEFAULT_UPPER_BOUND) and
                     f"{{ upperBound = {self.subject.upperBound} }}" or "", ),
                Text(text=lambda: self.show_ordering and self.subject.ordering
                     and f"{{ ordering = {self.subject.ordering} }}" or "", ),
            ),
        )

        self.watch("subject[NamedElement].name")
        self.watch("subject.appliedStereotype.classifier.name")
        self.watch("subject[ObjectNode].upperBound")
        self.watch("subject[ObjectNode].ordering")
        self.watch("show_ordering")

    show_ordering: attribute[int] = attribute("show_ordering",
                                              int,
                                              default=False)

    def load(self, name, value):
        if name == "show-ordering":
            name = "show_ordering"
        super().load(name, value)
Пример #9
0
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"]
Пример #10
0
    pass


class View(Class):
    pass


class Viewpoint(Class):
    concernList: relation_many[Comment]
    language: attribute[str]
    presentation: attribute[str]
    purpose: attribute[str]
    stakeholder: relation_many[Stakeholder]


Heritage.isSubstitutable = attribute("isSubstitutable", int)
ObjectNode.ordering = enumeration("ordering",
                                  ("unordered", "ordered", "LIFO", "FIFO"),
                                  "FIFO")
Heritage.general = association("general", Classifier, lower=1, upper=1)
Classifier.heritage = association("heritage",
                                  Heritage,
                                  composite=True,
                                  opposite="specific")
Heritage.specific = association("specific",
                                Classifier,
                                lower=1,
                                upper=1,
                                opposite="heritage")
Classifier.general = derived("general", Classifier, 0, "*",
                             lambda self: [g.general for g in self.heritage])
Пример #11
0
 class A(Element):
     attr = attribute("attr", bytes, default="default")
Пример #12
0
    ownerContainer: relation_one[C4Container]
    owningContainer: relation_many[C4Container]
    technology: attribute[str]
    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]
Пример #13
0
    pass


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(
Пример #14
0
# 55: override Diagram
# defined in gaphor.core.modeling.diagram

# 46: override Presentation
# defined in gaphor.core.modeling.presentation


class Comment(Element):
    body: attribute[str]
    annotatedElement: relation_many[Element]


# 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,
Пример #15
0
class LinePresentation(gaphas.Line, HandlePositionUpdate, Presentation[S]):
    def __init__(
        self,
        diagram: Diagram,
        id=None,
        style: Style = {},
        shape_head=None,
        shape_middle=None,
        shape_tail=None,
    ):
        super().__init__(connections=diagram.connections, diagram=diagram, id=id)  # type: ignore[call-arg]

        self.style = style
        self.shape_head = shape_head
        self.shape_middle = shape_middle
        self.shape_tail = shape_tail

        self.fuzziness = 2
        self._shape_head_rect = None
        self._shape_middle_rect = None
        self._shape_tail_rect = None
        self.watch("orthogonal", self._on_orthogonal).watch(
            "horizontal", self._on_horizontal
        )
        self.watch_handle(self.head)
        self.watch_handle(self.tail)

    head = property(lambda self: self._handles[0])
    tail = property(lambda self: self._handles[-1])

    orthogonal: attribute[int] = attribute("orthogonal", int, 0)
    horizontal: attribute[int] = attribute("horizontal", int, 0)

    def insert_handle(self, index: int, handle: Handle) -> None:
        super().insert_handle(index, handle)
        self.watch_handle(handle)

    def remove_handle(self, handle: Handle) -> None:
        self.remove_watch_handle(handle)
        super().remove_handle(handle)

    def pre_update(self, context):
        pass

    def post_update(self, context):
        def shape_bounds(shape, align):
            if shape:
                size = shape.size(context)
                x, y = text_point_at_line(points, size, align)
                return Rectangle(x, y, *size)

        points = [h.pos for h in self.handles()]
        self._shape_head_rect = shape_bounds(self.shape_head, TextAlign.LEFT)
        self._shape_middle_rect = shape_bounds(self.shape_middle, TextAlign.CENTER)
        self._shape_tail_rect = shape_bounds(self.shape_tail, TextAlign.RIGHT)

    def point(self, x, y):
        """Given a point (x, y) return the distance to the diagram item."""
        d0 = super().point(x, y)
        ds = [
            distance_rectangle_point(shape, (x, y))
            for shape in (
                self._shape_head_rect,
                self._shape_middle_rect,
                self._shape_tail_rect,
            )
            if shape
        ]
        return min(d0, *ds) if ds else d0

    def draw(self, context):
        def draw_line_end(end_handle, second_handle, draw):
            pos, p1 = end_handle.pos, second_handle.pos
            angle = atan2(p1.y - pos.y, p1.x - pos.x)
            cr = context.cairo
            cr.save()
            try:
                cr.translate(*pos)
                cr.rotate(angle)
                draw(context)
            finally:
                cr.restore()

        style = combined_style(context.style, self.style)
        context = replace(context, style=style)

        cr = context.cairo
        cr.set_line_width(self.line_width)
        cr.set_dash(style.get("dash-style", ()), 0)
        stroke = style["color"]
        if stroke:
            cr.set_source_rgba(*stroke)

        handles = self._handles
        draw_line_end(handles[0], handles[1], self.draw_head)

        for h in self._handles[1:-1]:
            cr.line_to(*h.pos)

        draw_line_end(handles[-1], handles[-2], self.draw_tail)

        draw_highlight(context)
        cr.stroke()

        for shape, rect in (
            (self.shape_head, self._shape_head_rect),
            (self.shape_middle, self._shape_middle_rect),
            (self.shape_tail, self._shape_tail_rect),
        ):
            if shape:
                shape.draw(context, rect)

    def save(self, save_func):
        def save_connection(name, handle):
            c = self._connections.get_connection(handle)
            if c:
                save_func(name, c.connected)

        super().save(save_func)
        save_func("matrix", tuple(self.matrix))
        points = [tuple(map(float, h.pos)) for h in self.handles()]
        save_func("points", points)

        save_connection("head-connection", self.head)
        save_connection("tail-connection", self.tail)

    def load(self, name, value):
        if name == "matrix":
            self.matrix.set(*ast.literal_eval(value))
        elif name == "points":
            points = ast.literal_eval(value)
            for _ in range(len(points) - 2):
                h = Handle((0, 0))
                self._handles.insert(1, h)
                self.watch_handle(h)
            for i, p in enumerate(points):
                self.handles()[i].pos = p
            self._update_ports()
        elif name in ("head_connection", "head-connection"):
            self._load_head_connection = value
        elif name in ("tail_connection", "tail-connection"):
            self._load_tail_connection = value
        else:
            super().load(name, value)

    def postload(self):
        if self.orthogonal:
            self._set_orthogonal(self.orthogonal)

        if hasattr(self, "_load_head_connection"):
            postload_connect(self, self.head, self._load_head_connection)
            assert self._connections.get_connection(self.head)
            del self._load_head_connection

        if hasattr(self, "_load_tail_connection"):
            postload_connect(self, self.tail, self._load_tail_connection)
            assert self._connections.get_connection(self.tail)
            del self._load_tail_connection

        super().postload()

    def _on_orthogonal(self, event):
        self._set_orthogonal(event.new_value)

    def _on_horizontal(self, event):
        self._set_horizontal(event.new_value)
Пример #16
0
class ClassItem(ElementPresentation[UML.Class], Classified):
    """This item visualizes a Class instance.

    A ClassItem contains two compartments: one for attributes and one for
    operations.
    """
    def __init__(self, id=None, model=None):
        super().__init__(id, model)

        self.watch("show_stereotypes", self.update_shapes).watch(
            "show_attributes", self.update_shapes).watch(
                "show_operations",
                self.update_shapes).watch("subject[NamedElement].name").watch(
                    "subject[NamedElement].namespace.name").watch(
                        "subject[Classifier].isAbstract", self.update_shapes)
        attribute_watches(self, "Class")
        operation_watches(self, "Class")
        stereotype_watches(self)

    show_stereotypes: attribute[int] = attribute("show_stereotypes", int)

    show_attributes: attribute[int] = attribute("show_attributes",
                                                int,
                                                default=True)

    show_operations: attribute[int] = attribute("show_operations",
                                                int,
                                                default=True)

    def additional_stereotypes(self):
        if isinstance(self.subject, UML.Stereotype):
            return ["stereotype"]
        elif UML.model.is_metaclass(self.subject):
            return ["metaclass"]
        else:
            return ()

    def update_shapes(self, event=None):
        self.shape = Box(
            Box(
                Text(text=lambda: UML.model.stereotypes_str(
                    self.subject, self.additional_stereotypes()), ),
                EditableText(
                    text=lambda: self.subject.name or "",
                    style={
                        "font-weight":
                        FontWeight.BOLD,
                        "font-style":
                        FontStyle.ITALIC if self.subject
                        and self.subject.isAbstract else FontStyle.NORMAL,
                    },
                ),
                Text(
                    text=lambda: from_package_str(self),
                    style={
                        "font-size": 10,
                        "min-width": 0,
                        "min-height": 0
                    },
                ),
                style={"padding": (12, 4, 12, 4)},
            ),
            *(self.show_attributes and self.subject
              and [attributes_compartment(self.subject)] or []),
            *(self.show_operations and self.subject
              and [operations_compartment(self.subject)] or []),
            *(self.show_stereotypes and stereotype_compartments(self.subject)
              or []),
            style={
                "min-width": 100,
                "min-height": 50,
                "vertical-align": VerticalAlign.TOP,
            },
            draw=draw_border,
        )
Пример #17
0
class InterfaceItem(ElementPresentation, Classified):
    """Interface item supporting class box, folded notations and assembly
    connector icon mode.

    When in folded mode, provided (ball) notation is used by default.
    """

    RADIUS_PROVIDED = 10
    RADIUS_REQUIRED = 14

    def __init__(self, id=None, model=None):
        super().__init__(id, model)
        self._folded = Folded.NONE
        self.side = Side.N

        handles = self.handles()
        h_nw = handles[NW]
        h_ne = handles[NE]
        h_sw = handles[SW]
        h_se = handles[SE]

        def is_folded():
            return self._folded != Folded.NONE

        # edge of element define default element ports
        self._ports = [
            InterfacePort(h_nw.pos, h_ne.pos, is_folded, Side.N),
            InterfacePort(h_ne.pos, h_se.pos, is_folded, Side.E),
            InterfacePort(h_se.pos, h_sw.pos, is_folded, Side.S),
            InterfacePort(h_sw.pos, h_nw.pos, is_folded, Side.W),
        ]

        self.watch("show_stereotypes", self.update_shapes).watch(
            "show_attributes", self.update_shapes
        ).watch("show_operations", self.update_shapes).watch(
            "subject[NamedElement].name"
        ).watch("subject[NamedElement].namespace.name").watch(
            "subject.appliedStereotype", self.update_shapes
        ).watch("subject.appliedStereotype.classifier.name").watch(
            "subject.appliedStereotype.slot", self.update_shapes).watch(
                "subject.appliedStereotype.slot.definingFeature.name").watch(
                    "subject.appliedStereotype.slot.value",
                    self.update_shapes).watch(
                        "subject[Interface].supplierDependency",
                        self.update_shapes)
        attribute_watches(self, "Interface")
        operation_watches(self, "Interface")

    show_stereotypes: attribute[int] = attribute("show_stereotypes", int)

    show_attributes: attribute[int] = attribute("show_attributes",
                                                int,
                                                default=True)

    show_operations: attribute[int] = attribute("show_operations",
                                                int,
                                                default=True)

    def load(self, name, value):
        if name == "folded":
            self._folded = Folded(ast.literal_eval(value))
        else:
            super().load(name, value)

    def save(self, save_func):
        super().save(save_func)
        save_func("folded", self._folded.value)

    def _set_folded(self, folded):
        """Set folded notation.

        :param folded: Folded state, see Folded.* enum.
        """
        if self._folded == folded:
            return

        self._folded = folded

        if folded == Folded.NONE:
            movable = True
        else:
            if self._folded == Folded.PROVIDED:
                icon_size = self.RADIUS_PROVIDED * 2
            else:  # required interface or assembly icon mode
                icon_size = self.RADIUS_REQUIRED * 2

            self.min_width, self.min_height = icon_size, icon_size
            self.width, self.height = icon_size, icon_size

            # update only h_se handle - rest of handles should be updated by
            # constraints
            h_nw = self._handles[NW]
            h_se = self._handles[SE]
            h_se.pos.x = h_nw.pos.x + self.min_width
            h_se.pos.y = h_nw.pos.y + self.min_height

            movable = False

        for h in self._handles:
            h.movable = movable

        self.update_shapes()

    folded = property(
        lambda s: s._folded,
        _set_folded,
        doc="Check or set folded notation, see Folded.* enum.",
    )

    def pre_update(self, context):
        assert isinstance(self.canvas, Canvas)
        connected_items = [
            c.item for c in self.canvas.get_connections(connected=self)
        ]
        connectors = any(
            map(lambda i: isinstance(i.subject, UML.Connector),
                connected_items))
        if connectors or self._folded != Folded.NONE:
            provided = connectors or any(
                map(lambda i: isinstance(i.subject, UML.Implementation),
                    connected_items))
            required = any(
                map(lambda i: isinstance(i.subject, UML.Usage),
                    connected_items))
            if required and provided:
                self.folded = Folded.ASSEMBLY
            elif required:
                self.folded = Folded.REQUIRED
            else:
                self.folded = Folded.PROVIDED
            self.update_shapes(connectors=connectors)
        super().pre_update(context)

    def update_shapes(self, event=None, connectors=None):
        if self._folded == Folded.NONE:
            self.shape = self.class_shape()
        else:
            self.shape = self.ball_and_socket_shape(connectors)

    def class_shape(self):
        return Box(
            Box(
                Text(text=lambda: UML.model.stereotypes_str(
                    self.subject, ("interface", )), ),
                EditableText(
                    text=lambda: self.subject.name or "",
                    style={"font-weight": FontWeight.BOLD},
                ),
                Text(
                    text=lambda: from_package_str(self),
                    style={
                        "font-size": 10,
                        "min-width": 0,
                        "min-height": 0
                    },
                ),
                style={"padding": (12, 4, 12, 4)},
            ),
            *(self.show_attributes and self.subject
              and [attributes_compartment(self.subject)] or []),
            *(self.show_operations and self.subject
              and [operations_compartment(self.subject)] or []),
            *(self.show_stereotypes and stereotype_compartments(self.subject)
              or []),
            style={
                "min-width": 100,
                "min-height": 50,
                "vertical-align": VerticalAlign.TOP,
            },
            draw=draw_border,
        )

    def ball_and_socket_shape(self, connectors=None):
        assert self.canvas
        if connectors is None:
            # distinguish between None and []
            connected_items = [
                c.item for c in self.canvas.get_connections(connected=self)
            ]
            connectors = any(
                map(lambda i: isinstance(i.subject, UML.Connector),
                    connected_items))
        return IconBox(
            Box(
                style={
                    "min-width": self.min_width,
                    "min-height": self.min_height
                },
                draw=self.draw_interface_ball_and_socket,
            ),
            Text(text=lambda: UML.model.stereotypes_str(self.subject), ),
            EditableText(
                text=lambda: self.subject.name or "",
                style={
                    "font-weight":
                    FontWeight.NORMAL if connectors else FontWeight.BOLD
                },
            ),
        )

    def draw_interface_ball_and_socket(self, _box, context, _bounding_box):
        cr = context.cairo

        h_nw = self._handles[NW]
        cx, cy = (h_nw.pos.x + self.width / 2, h_nw.pos.y + self.height / 2)

        if self._folded in (Folded.REQUIRED, Folded.ASSEMBLY):
            r = self.RADIUS_REQUIRED
            if self.side == Side.N:
                x, y = r * 2, r
            elif self.side == Side.E:
                x, y = r, r * 2
            elif self.side == Side.S:
                x, y = 0, r
            elif self.side == Side.W:
                x, y = r, 0

            cr.move_to(x, y)
            cr.arc_negative(cx, cy, self.RADIUS_REQUIRED, self.side.value,
                            pi + self.side.value)

        if self._folded in (Folded.PROVIDED, Folded.ASSEMBLY):
            cr.move_to(cx + self.RADIUS_PROVIDED, cy)
            cr.arc(cx, cy, self.RADIUS_PROVIDED, 0, pi * 2)

        stroke(context)
Пример #18
0
class ClassItem(ElementPresentation[UML3.Class], Classified):
    """This item visualizes a Class instance.

    A ClassItem contains two compartments: one for attributes and one for
    operations.
    """

    def __init__(self, id=None, model=None):
        super().__init__(id, model)

        self.watch("show_stereotypes", self.update_shapes).watch(
            "show_attributes", self.update_shapes
        ).watch("show_operations", self.update_shapes).watch(
            "subject[NamedElement].name"
        ).watch(
            "subject[NamedElement].namespace.name"
        ).watch(
            "subject[Classifier].isAbstract", self.update_shapes
        )
        attribute_watches(self, "Class")
        operation_watches(self, "Class")
        stereotype_watches(self)

    show_stereotypes: attribute[int] = attribute("show_stereotypes", int)

    show_attributes: attribute[int] = attribute("show_attributes", int, default=True)

    show_operations: attribute[int] = attribute("show_operations", int, default=True)

    def additional_stereotypes(self):
        #aqui foram adicionados estereótipos da ontouml
        if isinstance(self.subject, UML3.StereotypeKind):
            return ["kind"]
        elif isinstance(self.subject, UML3.StereotypeSubkind):
            return ["subkind"]
        elif isinstance(self.subject, UML3.StereotypePhase):
            return ["phase"]
        elif isinstance(self.subject, UML3.StereotypeRole):
            return ["role"]
        elif isinstance(self.subject, UML3.StereotypeCollective):
            return ["collective"]
        elif isinstance(self.subject, UML3.StereotypeQuantity):
            return ["quantity"]
        elif isinstance(self.subject, UML3.StereotypeRelator):
            return ["relator"]
        elif isinstance(self.subject, UML3.StereotypeCategory):
            return ["category"]
        elif isinstance(self.subject, UML3.StereotypeRolemixin):
            return ["rolemixin"]
        elif isinstance(self.subject, UML3.StereotypeMixin):
            return ["mixin"]
        elif isinstance(self.subject, UML3.StereotypeMode):
            return ["mode"]
        elif isinstance(self.subject, UML3.StereotypePhasemixin):
            return ["phasemixin"]
        elif isinstance(self.subject, UML3.StereotypeQuality):
            return ["quality"]
        elif isinstance(self.subject, UML3.Material):
            return ["material"]
        else:
            return ()

    def update_shapes(self, event=None):
        self.shape = Box(
            Box(
                Text(
                    text=lambda: UML3.model.stereotypes_str(
                        self.subject, self.additional_stereotypes()
                    ),
                ),
                EditableText(
                    text=lambda: self.subject.name or "",
                    style={
                        "font-weight": FontWeight.BOLD,
                        "font-style": FontStyle.ITALIC
                        if self.subject and self.subject.isAbstract
                        else FontStyle.NORMAL,
                    },
                ),
                Text(
                    text=lambda: from_package_str(self),
                    style={"font-size": 10, "min-width": 0, "min-height": 0},
                ),
                style={"padding": (12, 4, 12, 4)},
            ),
            *(
                self.show_attributes
                and self.subject
                and [attributes_compartment(self.subject)]
                or []
            ),
            *(
                self.show_operations
                and self.subject
                and [operations_compartment(self.subject)]
                or []
            ),
            *(self.show_stereotypes and stereotyperelator_compartments(self.subject) or []),
            style={
                "min-width": 100,
                "min-height": 50,
                "vertical-align": VerticalAlign.TOP,
            },
            draw=draw_border,
        )
Пример #19
0
class BlockItem(ElementPresentation[Block], Classified):
    def __init__(self, diagram, id=None):
        super().__init__(diagram, id)

        self.watch("show_stereotypes", self.update_shapes).watch(
            "show_parts", self.update_shapes).watch(
                "show_references",
                self.update_shapes).watch("subject[NamedElement].name").watch(
                    "subject[NamedElement].namespace.name").watch(
                        "subject[Classifier].isAbstract",
                        self.update_shapes).watch(
                            "subject[Class].ownedAttribute.aggregation",
                            self.update_shapes)
        attribute_watches(self, "Block")
        stereotype_watches(self)

    show_stereotypes: attribute[int] = attribute("show_stereotypes", int)

    show_parts: attribute[int] = attribute("show_parts", int, default=False)

    show_references: attribute[int] = attribute("show_references",
                                                int,
                                                default=False)

    def update_shapes(self, event=None):
        self.shape = Box(
            Box(
                Text(
                    text=lambda: stereotypes_str(self.subject, ["block"]),
                    style={
                        "min-width": 0,
                        "min-height": 0
                    },
                ),
                EditableText(
                    text=lambda: self.subject.name or "",
                    width=lambda: self.width - 4,
                    style={
                        "font-weight":
                        FontWeight.BOLD,
                        "font-style":
                        FontStyle.ITALIC if self.subject
                        and self.subject.isAbstract else FontStyle.NORMAL,
                    },
                ),
                Text(
                    text=lambda: from_package_str(self),
                    style={
                        "font-size": 10,
                        "min-width": 0,
                        "min-height": 0
                    },
                ),
                style={"padding": (12, 4, 12, 4)},
            ),
            *(self.show_parts and self.subject and [
                self.block_compartment(
                    gettext("parts"),
                    lambda a: a.aggregation == "composite",
                )
            ] or []),
            *(self.show_references and self.subject and [
                self.block_compartment(
                    gettext("references"),
                    lambda a: a.aggregation != "composite",
                )
            ] or []),
            *(self.show_stereotypes and stereotype_compartments(self.subject)
              or []),
            style={
                "min-width": 100,
                "min-height": 50,
                "vertical-align": VerticalAlign.TOP,
            },
            draw=draw_border,
        )

    def block_compartment(self, name, predicate):
        # We need to fix the attribute value, since the for loop changes it.
        def lazy_format(attribute):
            return lambda: format_property(attribute) or gettext("unnamed")

        return Box(
            Text(
                text=name,
                style={
                    "padding": (0, 0, 4, 0),
                    "font-size": 10,
                    "font-style": FontStyle.ITALIC,
                },
            ),
            *(Text(text=lazy_format(attribute),
                   style={"text-align": TextAlign.LEFT})
              for attribute in self.subject.ownedAttribute
              if predicate(attribute)),
            style={
                "padding": (4, 4, 4, 4),
                "min-height": 8
            },
            draw=draw_top_separator,
        )
Пример #20
0
class AssociationItem(LinePresentation[UML.Association], Named):
    """AssociationItem represents associations.

    An AssociationItem has two AssociationEnd items. Each AssociationEnd
    item represents a Property (with Property.association == my
    association).
    """

    def __init__(self, diagram, id=None):
        super().__init__(diagram, id)

        # AssociationEnds are really inseparable from the AssociationItem.
        # We give them the same id as the association item.
        self._head_end = AssociationEnd(owner=self, end="head")
        self._tail_end = AssociationEnd(owner=self, end="tail")

        # Direction depends on the ends that hold the ownedEnd attributes.
        self._dir_angle = 0
        self._dir_pos = 0, 0

        self.shape_middle = Box(
            Text(
                text=lambda: stereotypes_str(self.subject),
            ),
            EditableText(text=lambda: self.subject.name or ""),
        )

        # For the association ends:
        base = "subject[Association].memberEnd[Property]"
        self.watch("subject[NamedElement].name").watch(
            "subject.appliedStereotype.classifier.name"
        ).watch(f"{base}.name", self.on_association_end_value).watch(
            f"{base}.aggregation", self.on_association_end_value
        ).watch(
            f"{base}.appliedStereotype.slot.definingFeature.name",
            self.on_association_end_value,
        ).watch(
            f"{base}.appliedStereotype.slot.value", self.on_association_end_value
        ).watch(
            f"{base}.classifier", self.on_association_end_value
        ).watch(
            f"{base}.visibility", self.on_association_end_value
        ).watch(
            f"{base}.lowerValue", self.on_association_end_value
        ).watch(
            f"{base}.upperValue", self.on_association_end_value
        ).watch(
            f"{base}.owningAssociation", self.on_association_end_value
        ).watch(
            f"{base}.type[Class].ownedAttribute", self.on_association_end_value
        ).watch(
            f"{base}.type[Interface].ownedAttribute", self.on_association_end_value
        ).watch(
            f"{base}.appliedStereotype.classifier", self.on_association_end_value
        ).watch(
            "subject[Association].memberEnd"
        ).watch(
            "subject[Association].ownedEnd"
        ).watch(
            "subject[Association].navigableOwnedEnd"
        ).watch(
            "show_direction"
        )

    show_direction: attribute[int] = attribute("show_direction", int, default=False)

    def load(self, name, value):
        # end_head and end_tail were used in an older Gaphor version
        if name in ("head_end", "head-subject"):
            name = "head_subject"
        elif name in ("tail_end", "tail-subject"):
            name = "tail_subject"
        elif name == "show-direction":
            name = "show_direction"

        super().load(name, value)

    def postload(self):
        super().postload()
        self._head_end.set_text()
        self._tail_end.set_text()

    head_end = property(lambda self: self._head_end)
    tail_end = property(lambda self: self._tail_end)
    head_subject = association("head_subject", UML.Property, upper=1)
    tail_subject = association("tail_subject", UML.Property, upper=1)

    def invert_direction(self):
        """Invert the direction of the association, this is done by swapping
        the head and tail-ends subjects."""
        if not self.subject:
            return

        self.subject.memberEnd.swap(
            self.subject.memberEnd[0], self.subject.memberEnd[1]
        )
        self.request_update()

    def on_association_end_value(self, event):
        """Handle events and update text on association end."""
        for end in (self._head_end, self._tail_end):
            end.set_text()
        self.request_update()

    def post_update(self, context):
        """Update the shapes and sub-items of the association."""

        handles = self.handles()

        # Update line endings:
        head_subject = self.head_subject
        tail_subject = self.tail_subject

        # Update line ends using the aggregation and isNavigable values:
        if head_subject and tail_subject:
            if tail_subject.aggregation == "composite":
                self.draw_head = draw_head_composite
            elif tail_subject.aggregation == "shared":
                self.draw_head = draw_head_shared
            elif head_subject.navigability is True:
                self.draw_head = draw_head_navigable
            elif head_subject.navigability is False:
                self.draw_head = draw_head_none
            else:
                self.draw_head = draw_default_head
            if head_subject.aggregation == "composite":
                self.draw_tail = draw_tail_composite
            elif head_subject.aggregation == "shared":
                self.draw_tail = draw_tail_shared
            elif tail_subject.navigability is True:
                self.draw_tail = draw_tail_navigable
            elif tail_subject.navigability is False:
                self.draw_tail = draw_tail_none
            else:
                self.draw_tail = draw_default_tail
            if self.show_direction:
                inverted = self.tail_subject is self.subject.memberEnd[0]
                pos, angle = get_center_pos(self.handles(), inverted)
                self._dir_pos = pos
                self._dir_angle = angle
        else:
            self.draw_head = draw_default_head
            self.draw_tail = draw_default_tail

        # update relationship after self.set calls to avoid circural updates
        super().post_update(context)

        # Calculate alignment of the head name and multiplicity
        self._head_end.post_update(context, handles[0].pos, handles[1].pos)

        # Calculate alignment of the tail name and multiplicity
        self._tail_end.post_update(context, handles[-1].pos, handles[-2].pos)

    def point(self, x, y):
        """Returns the distance from the Association to the (mouse) cursor."""
        return min(
            super().point(x, y), self._head_end.point(x, y), self._tail_end.point(x, y)
        )

    def draw(self, context):
        super().draw(context)
        self._head_end.draw(context)
        self._tail_end.draw(context)
        if self.show_direction:
            with cairo_state(context.cairo) as cr:
                cr.translate(*self._dir_pos)
                cr.rotate(self._dir_angle)
                cr.move_to(0, 0)
                cr.line_to(6, 5)
                cr.line_to(0, 10)
                cr.fill()
Пример #21
0
class RequirementItem(ElementPresentation[Requirement], Classified):
    def __init__(self, connections, id=None, model=None):
        super().__init__(connections, id, model)

        self.watch("show_stereotypes", self.update_shapes).watch(
            "show_attributes", self.update_shapes).watch(
                "show_operations",
                self.update_shapes).watch("subject[NamedElement].name").watch(
                    "subject[NamedElement].namespace.name").watch(
                        "subject[Classifier].isAbstract",
                        self.update_shapes).watch(
                            "subject[AbstractRequirement].externalId",
                            self.update_shapes).watch(
                                "subject[AbstractRequirement].text",
                                self.update_shapes)
        attribute_watches(self, "Requirement")
        operation_watches(self, "Requirement")
        stereotype_watches(self)

    show_stereotypes: attribute[int] = attribute("show_stereotypes", int)

    show_attributes: attribute[int] = attribute("show_attributes",
                                                int,
                                                default=False)

    show_operations: attribute[int] = attribute("show_operations",
                                                int,
                                                default=False)

    def update_shapes(self, event=None):
        self.shape = Box(
            Box(
                Text(
                    text=lambda: stereotypes_str(self.subject, ["requirement"]
                                                 ),
                    style={
                        "min-width": 0,
                        "min-height": 0
                    },
                ),
                EditableText(
                    text=lambda: self.subject.name or "",
                    width=lambda: self.width - 4,
                    style={
                        "font-weight":
                        FontWeight.BOLD,
                        "font-style":
                        FontStyle.ITALIC if self.subject
                        and self.subject.isAbstract else FontStyle.NORMAL,
                    },
                ),
                Text(
                    text=lambda: from_package_str(self),
                    style={
                        "font-size": 10,
                        "min-width": 0,
                        "min-height": 0
                    },
                ),
                style={"padding": (12, 4, 12, 4)},
            ),
            *(self.show_attributes and self.subject
              and [attributes_compartment(self.subject)] or []),
            *(self.show_operations and self.subject
              and [operations_compartment(self.subject)] or []),
            *(self.show_stereotypes and stereotype_compartments(self.subject)
              or []),
            self.id_and_text_compartment(),
            style={
                "min-width": 100,
                "min-height": 50,
                "vertical-align": VerticalAlign.TOP,
            },
            draw=draw_border,
        )

    def id_and_text_compartment(self):
        subject = self.subject
        if subject and (subject.externalId or subject.text):
            return Box(
                *([
                    Text(
                        text=lambda: f"Id: {subject.externalId}",
                        style={"text-align": TextAlign.LEFT},
                    )
                ] if subject and subject.externalId else []),
                *([
                    Text(
                        text=lambda: f"Text: {subject.text}",
                        width=lambda: self.width - 8,
                        style={"text-align": TextAlign.LEFT},
                    )
                ] if subject and subject.text else []),
                style={
                    "padding": (4, 4, 4, 4),
                    "min-height": 8
                },
                draw=draw_top_separator,
            )
        else:
            return Box()