def __init__(self, connections, id=None, model=None): super().__init__(id=id, model=model) self._matrix = Matrix() self._matrix_i2c = Matrix() self._connections = connections h1, h2 = Handle(), Handle() self._handles = [h1, h2] self._ports = [LinePort(h1.pos, h2.pos)] self._combined = None self.shape = IconBox( Box(style={"min-width": 0, "min-height": 45}, draw=self.draw_fork_node), Text( text=lambda: stereotypes_str(self.subject), ), EditableText(text=lambda: self.subject and self.subject.name or ""), Text( text=lambda: isinstance(self.subject, UML.JoinNode) and self.subject.joinSpec not in (None, DEFAULT_JOIN_SPEC) and f"{{ joinSpec = {self.subject.joinSpec} }}" or "", ), ) self.watch("subject[NamedElement].name") self.watch("subject.appliedStereotype.classifier.name") self.watch("subject[JoinNode].joinSpec") connections.add_constraint(self, constraint(vertical=(h1.pos, h2.pos))) connections.add_constraint(self, constraint(above=(h1.pos, h2.pos), delta=30))
def __init__(self, diagram, id=None): super().__init__(diagram, id=id) h1, h2 = Handle(), Handle() self._handles = [h1, h2] self._ports = [LinePort(h1.pos, h2.pos)] self.watch_handle(h1) self.watch_handle(h2) self.shape = IconBox( Box(draw=self.draw_fork_node), Text( text=lambda: stereotypes_str(self.subject), ), EditableText(text=lambda: self.subject and self.subject.name or ""), Text( text=lambda: isinstance(self.subject, UML.JoinNode) and self.subject.joinSpec not in (None, DEFAULT_JOIN_SPEC) and f"{{ joinSpec = {self.subject.joinSpec} }}" or "", ), ) self.watch("subject[NamedElement].name") self.watch("subject.appliedStereotype.classifier.name") self.watch("subject[JoinNode].joinSpec") diagram.connections.add_constraint(self, constraint(vertical=(h1.pos, h2.pos))) diagram.connections.add_constraint( self, constraint(above=(h1.pos, h2.pos), delta=30) )
def __init__(self, id=None, model=None): super().__init__(id, model) h1, h2 = Handle(), Handle() self._handles.append(h1) self._handles.append(h2) self._ports.append(LinePort(h1.pos, h2.pos)) self._combined = None self.shape = IconBox( Box(style={"min-width": 0, "min-height": 45}, draw=self.draw_fork_node), Text( text=lambda: stereotypes_str(self.subject), style={"min-width": 0, "min-height": 0}, ), EditableText(text=lambda: self.subject and self.subject.name or ""), Text( text=lambda: isinstance(self.subject, UML.JoinNode) and self.subject.joinSpec not in (None, DEFAULT_JOIN_SPEC) and f"{{ joinSpec = {self.subject.joinSpec} }}" or "", style={"min-width": 0, "min-height": 0}, ), ) self.watch("subject[NamedElement].name") self.watch("subject.appliedStereotype.classifier.name") self.watch("subject[JoinNode].joinSpec")
def update_shapes(self): self.shape = IconBox( Box(style={"background-color": (1, 1, 1, 1)}, draw=draw_border), Text(text=lambda: stereotypes_str(self.subject, ("proxy", ))), EditableText( text=lambda: self.subject and self.subject.name or ""), style=text_position(self.connected_side()), ) self.request_update()
def test_draw_icon_box(cr): box_drawn = None def box_draw(box, context, bounding_box): nonlocal box_drawn box_drawn = bounding_box shape = IconBox(Box(draw=box_draw), Text(text="some text")) bounding_box = Rectangle(11, 12, 13, 14) shape.draw(cr, bounding_box) assert box_drawn == bounding_box
def test_icon_box_child_placement_right_middle(context): style = {"text-align": TextAlign.RIGHT, "vertical-align": VerticalAlign.MIDDLE} text = Text(text="some text") shape = IconBox(Box(), text,) shape.size(context) w, h = shape.sizes[0] bounding_box = Rectangle(0, 0, 10, 20) x, y, _, _ = shape.child_pos(style, bounding_box) assert x == bounding_box.width assert y == (bounding_box.height - h) / 2
def test_icon_box_child_placement_center_bottom(context): style = {"text-align": TextAlign.CENTER, "vertical-align": VerticalAlign.BOTTOM} text = Text(text="some text") shape = IconBox(Box(), text,) shape.size(context) w, h = shape.sizes[0] bounding_box = Rectangle(0, 0, 10, 20) x, y, _, _ = shape.child_pos(style, bounding_box) assert x == (bounding_box.width - w) / 2 assert y == bounding_box.height
def __init__(self, id=None, model=None): super().__init__(id, model) self.shape = IconBox( Box( style={ "min-width": ARM * 2, "min-height": HEAD + NECK + BODY + ARM }, draw=draw_actor, ), Text( text=lambda: stereotypes_str(self.subject), style={ "min-width": 0, "min-height": 0 }, ), EditableText( text=lambda: self.subject.name or "", style={"font-weight": FontWeight.BOLD}, ), ) self.watch("subject<NamedElement>.name") self.watch("subject.appliedStereotype.classifier.name")
def update_shapes(self, event=None): if self._folded == Folded.NONE: self.shape = Box( Box( Text( text=lambda: UML.model.stereotypes_str( self.subject, ("interface", )), style={ "min-width": 0, "min-height": 0 }, ), EditableText( text=lambda: self.subject.name or "", style={"font-weight": FontWeight.BOLD}, ), Text( text=lambda: from_package_str(self), style={ "font": "sans 8", "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_attrs and stereotype_compartments(self.subject) or []), style={ "min-width": 100, "min-height": 50, "vertical-align": VerticalAlign.TOP, }, draw=draw_border, ) else: self.shape = 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), style={ "min-width": 0, "min-height": 0 }, ), EditableText( text=lambda: self.subject.name or "", style={"font-weight": FontWeight.BOLD}, ), )
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")
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 update_shapes(self): self.shape = IconBox( Box(draw=draw_history_pseudostate if self.subject and self.subject. kind == "shallowHistory" else draw_initial_pseudostate), Text(text=lambda: stereotypes_str(self.subject), ), EditableText(text=lambda: self.subject.name or ""), )
def __init__(self, id=None, model=None): super().__init__(id, model) no_movable_handles(self) self._combined = None self.shape = IconBox( Box(style={ "min-width": 20, "min-height": 30 }, draw=draw_decision_node), # Text should be left-top Text( text=lambda: stereotypes_str(self.subject), style={ "min-width": 0, "min-height": 0 }, ), EditableText( text=lambda: self.subject and self.subject.name or ""), ) self.watch("subject[NamedElement].name") self.watch("subject.appliedStereotype.classifier.name")
def update_shapes(self, event=None): if self.subject and self.subject.kind == "shallowHistory": box = Box(draw=draw_history_pseudostate) self.handles()[SE].pos = (30, 30) else: box = Box(draw=draw_initial_pseudostate) self.handles()[SE].pos = (20, 20) self.shape = IconBox( box, Text(text=lambda: stereotypes_str(self.subject), ), EditableText(text=lambda: self.subject.name or ""), )
def __init__(self, id=None, model=None): super().__init__(id, model) no_movable_handles(self) self.shape = IconBox( Box(style={"min-width": 20, "min-height": 20}, draw=draw_flow_final_node), # Text should be right-bottom 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")
def __init__(self, diagram, id=None): super().__init__(diagram, id, width=30, height=30) for h in self.handles(): h.movable = False self.shape = IconBox( Box(draw=draw_final_state), 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")
def __init__(self, id=None, model=None): super().__init__(id, model) for h in self.handles(): h.movable = False self.shape = IconBox( Box(draw=draw_history_pseudostate), Text( text=lambda: stereotypes_str(self.subject), style={"min-width": 0, "min-height": 0}, ), EditableText(text=lambda: self.subject and self.subject.name or ""), ) self.watch("subject[NamedElement].name") self.watch("subject.appliedStereotype.classifier.name")
def __init__(self, diagram, id=None): super().__init__(diagram, id, width=30, height=30) no_movable_handles(self) self.shape = IconBox( Box(draw=draw_activity_final_node), # Text should be right-bottom 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")
def __init__(self, id=None, model=None): super().__init__(id, model) self._show_ordering = False self.shape = IconBox( Box( Text( text=lambda: stereotypes_str(self.subject), style={ "min-width": 0, "min-height": 0 }, ), 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} }}", style={ "min-width": 0, "min-height": 0 }, ), Text( text=lambda: self._show_ordering and self.subject.ordering and f"{{ ordering = {self.subject.ordering} }}", style={ "min-width": 0, "min-height": 0 }, ), ) self.watch("subject<NamedElement>.name") self.watch("subject.appliedStereotype.classifier.name") self.watch("subject<ObjectNode>.upperBound") self.watch("subject<ObjectNode>.ordering")
def __init__(self, diagram, id=None): super().__init__(diagram, id, width=ARM * 2, height=HEAD + NECK + BODY + ARM) self.shape = IconBox( Box( draw=draw_actor, ), Text( text=lambda: stereotypes_str(self.subject), ), EditableText( text=lambda: self.subject.name or "", style={"font-weight": FontWeight.BOLD}, ), ) self.watch("subject[NamedElement].name") self.watch("subject.appliedStereotype.classifier.name")
def update_shapes(self, event=None): if self.subject and self.subject.kind == "shallowHistory": box = Box(style={ "min-height": 30, "min-width": 30 }, draw=draw_history_pseudostate) else: box = Box(style={ "min-height": 20, "min-width": 20 }, draw=draw_initial_pseudostate) self.shape = IconBox( box, Text(text=lambda: stereotypes_str(self.subject), ), EditableText(text=lambda: self.subject.name or ""), )
class ForkNodeItem(Presentation[UML.ForkNode], HandlePositionUpdate, Named): """Representation of fork and join node.""" def __init__(self, diagram, id=None): super().__init__(diagram, id=id) h1, h2 = Handle(), Handle() self._handles = [h1, h2] self._ports = [LinePort(h1.pos, h2.pos)] self.watch_handle(h1) self.watch_handle(h2) self.shape = IconBox( Box(draw=self.draw_fork_node), Text( text=lambda: stereotypes_str(self.subject), ), EditableText(text=lambda: self.subject and self.subject.name or ""), Text( text=lambda: isinstance(self.subject, UML.JoinNode) and self.subject.joinSpec not in (None, DEFAULT_JOIN_SPEC) and f"{{ joinSpec = {self.subject.joinSpec} }}" or "", ), ) self.watch("subject[NamedElement].name") self.watch("subject.appliedStereotype.classifier.name") self.watch("subject[JoinNode].joinSpec") diagram.connections.add_constraint(self, constraint(vertical=(h1.pos, h2.pos))) diagram.connections.add_constraint( self, constraint(above=(h1.pos, h2.pos), delta=30) ) combined: relation_one[UML.ControlNode] = association( "combined", UML.ControlNode, upper=1 ) def handles(self): return self._handles def ports(self): return self._ports def pre_update(self, context): pass def post_update(self, context): pass def save(self, save_func): save_func("matrix", tuple(self.matrix)) save_func("height", float(self._handles[1].pos.y)) super().save(save_func) def load(self, name, value): if name == "matrix": self.matrix.set(*ast.literal_eval(value)) elif name == "height": self._handles[1].pos.y = ast.literal_eval(value) else: super().load(name, value) def draw(self, context): h1, h2 = self.handles() height = h2.pos.y - h1.pos.y self.shape.draw(context, Rectangle(0, 0, 1, height)) def draw_fork_node(self, _box, context, _bounding_box): """ Draw vertical line - symbol of fork and join nodes. Join specification is also drawn above the item. """ cr = context.cairo cr.set_line_width(6) stroke = context.style.get("color") if stroke: cr.set_source_rgba(*stroke) h1, h2 = self._handles cr.move_to(h1.pos.x, h1.pos.y) cr.line_to(h2.pos.x, h2.pos.y) cr.stroke() def point(self, x, y): h1, h2 = self._handles d, p = distance_line_point(h1.pos, h2.pos, (x, y)) # Subtract line_width / 2 return d - 3
class ForkNodeItem(Presentation[UML.ForkNode], Item, Named): """ Representation of fork and join node. """ def __init__(self, id=None, model=None): super().__init__(id, model) h1, h2 = Handle(), Handle() self._handles.append(h1) self._handles.append(h2) self._ports.append(LinePort(h1.pos, h2.pos)) self._combined = None self.shape = IconBox( Box(style={ "min-width": 0, "min-height": 45 }, draw=self.draw_fork_node), Text(text=lambda: stereotypes_str(self.subject), ), EditableText( text=lambda: self.subject and self.subject.name or ""), Text(text=lambda: isinstance(self.subject, UML.JoinNode) and self. subject.joinSpec not in (None, DEFAULT_JOIN_SPEC) and f"{{ joinSpec = {self.subject.joinSpec} }}" or "", ), ) self.watch("subject[NamedElement].name") self.watch("subject.appliedStereotype.classifier.name") self.watch("subject[JoinNode].joinSpec") self.constraint(vertical=(h1.pos, h2.pos)) self.constraint(above=(h1.pos, h2.pos), delta=30) def save(self, save_func): save_func("matrix", tuple(self.matrix)) save_func("height", float(self._handles[1].pos.y)) if self._combined: save_func("combined", self._combined) super().save(save_func) def load(self, name, value): if name == "matrix": self.matrix = ast.literal_eval(value) elif name == "height": self._handles[1].pos.y = ast.literal_eval(value) elif name == "combined": self._combined = value else: # DiagramItem.load(self, name, value) super().load(name, value) @observed def _set_combined(self, value): # self.preserve_property('combined') self._combined = value combined = reversible_property(lambda s: s._combined, _set_combined) def draw(self, context): h1, h2 = self.handles() height = h2.pos.y - h1.pos.y self.shape.draw(context, Rectangle(0, 0, 1, height)) def draw_fork_node(self, _box, context, _bounding_box): """ Draw vertical line - symbol of fork and join nodes. Join specification is also drawn above the item. """ cr = context.cairo cr.set_line_width(6) h1, h2 = self._handles cr.move_to(h1.pos.x, h1.pos.y) cr.line_to(h2.pos.x, h2.pos.y) stroke(context) def point(self, pos): h1, h2 = self._handles d, p = distance_line_point(h1.pos, h2.pos, pos) # Subtract line_width / 2 return d - 3
class ForkNodeItem(UML.Presentation, Item): """ Representation of fork and join node. """ def __init__(self, id=None, model=None): super().__init__(id, model) h1, h2 = Handle(), Handle() self._handles.append(h1) self._handles.append(h2) self._ports.append(LinePort(h1.pos, h2.pos)) self._combined = None self.shape = IconBox( Box(style={"min-width": 0, "min-height": 45}, draw=self.draw_fork_node), Text( text=lambda: stereotypes_str(self.subject), style={"min-width": 0, "min-height": 0}, ), EditableText(text=lambda: self.subject and self.subject.name or ""), Text( text=lambda: isinstance(self.subject, UML.JoinNode) and self.subject.joinSpec not in (None, DEFAULT_JOIN_SPEC) and f"{{ joinSpec = {self.subject.joinSpec} }}" or "", style={"min-width": 0, "min-height": 0}, ), ) self.watch("subject[NamedElement].name") self.watch("subject.appliedStereotype.classifier.name") self.watch("subject[JoinNode].joinSpec") def save(self, save_func): save_func("matrix", tuple(self.matrix)) save_func("height", float(self._handles[1].pos.y)) if self._combined: save_func("combined", self._combined, reference=True) super().save(save_func) def load(self, name, value): if name == "matrix": self.matrix = ast.literal_eval(value) elif name == "height": self._handles[1].pos.y = ast.literal_eval(value) elif name == "combined": self._combined = value else: # DiagramItem.load(self, name, value) super().load(name, value) @observed def _set_combined(self, value): # self.preserve_property('combined') self._combined = value combined = reversible_property(lambda s: s._combined, _set_combined) def setup_canvas(self): assert self.canvas super().setup_canvas() h1, h2 = self._handles cadd = self.canvas.solver.add_constraint c1 = EqualsConstraint(a=h1.pos.x, b=h2.pos.x) c2 = LessThanConstraint(smaller=h1.pos.y, bigger=h2.pos.y, delta=30) self.__constraints = (cadd(c1), cadd(c2)) list(map(self.canvas.solver.add_constraint, self.__constraints)) def teardown_canvas(self): assert self.canvas super().teardown_canvas() list(map(self.canvas.solver.remove_constraint, self.__constraints)) def pre_update(self, context): cr = context.cairo _, h2 = self.handles() _, height = self.shape.size(cr) h2.pos.y = max(h2.pos.y, height) def draw(self, context): h1, h2 = self.handles() height = h2.pos.y - h1.pos.y self.shape.draw(context, Rectangle(0, 0, 1, height)) def draw_fork_node(self, _box, context, _bounding_box): """ Draw vertical line - symbol of fork and join nodes. Join specification is also drawn above the item. """ cr = context.cairo cr.set_line_width(6) h1, h2 = self._handles cr.move_to(h1.pos.x, h1.pos.y) cr.line_to(h2.pos.x, h2.pos.y) cr.stroke() def point(self, pos): h1, h2 = self._handles d, p = distance_line_point(h1.pos, h2.pos, pos) # Substract line_width / 2 return d - 3
class ProxyPortItem(Presentation[sysml.ProxyPort], Named): def __init__(self, connections, id=None, model=None): super().__init__(id=id, model=model) self._matrix = Matrix() self._matrix_i2c = Matrix() self._connections = connections h1 = Handle(connectable=True) self._handles = [h1] d = self.dimensions() top_left = Position(d.x, d.y) top_right = Position(d.x1, d.y) bottom_right = Position(d.x1, d.y1) bottom_left = Position(d.x, d.y1) self._ports = [ LinePort(top_left, top_right), LinePort(top_right, bottom_right), LinePort(bottom_right, bottom_left), LinePort(bottom_left, top_left), ] self._last_connected_side = None self.watch("subject[NamedElement].name") self.update_shapes() @property def matrix(self) -> Matrix: return self._matrix @property def matrix_i2c(self) -> Matrix: return self._matrix_i2c def handles(self): return self._handles def ports(self): return self._ports def update_shapes(self): self.shape = IconBox( Box(style={"background-color": (1, 1, 1, 1)}, draw=draw_border), Text(text=lambda: stereotypes_str(self.subject, ("proxy", ))), EditableText( text=lambda: self.subject and self.subject.name or ""), style=text_position(self.connected_side()), ) self.request_update() def connected_side(self) -> Optional[str]: cinfo = self._connections.get_connection(self._handles[0]) return cinfo.connected.port_side(cinfo.port) if cinfo else None def dimensions(self): return Rectangle(-8, -8, 16, 16) def point(self, x, y): return distance_rectangle_point(self.dimensions(), (x, y)) def save(self, save_func): save_func("matrix", tuple(self.matrix)) c = self._connections.get_connection(self.handles()[0]) if c: save_func("connection", c.connected) super().save(save_func) def load(self, name, value): if name == "matrix": self.matrix.set(*ast.literal_eval(value)) elif name == "connection": self._load_connection = value else: super().load(name, value) def postload(self): super().postload() if hasattr(self, "_load_connection"): postload_connect(self, self.handles()[0], self._load_connection) del self._load_connection self.update_shapes() def pre_update(self, context): side = self.connected_side() if self._last_connected_side != side: self._last_connected_side = side self.update_shapes() self.shape.size(context) def post_update(self, context): pass def draw(self, context): self.shape.draw(context, self.dimensions())
class ProxyPortItem(Presentation[sysml.ProxyPort], Item, Named): def __init__(self, id=None, model=None): super().__init__(id, model) h1 = Handle(connectable=True) self._handles.append(h1) d = self.dimensions() top_left = Position((d.x, d.y)) top_right = Position((d.x1, d.y)) bottom_right = Position((d.x1, d.y1)) bottom_left = Position((d.x, d.y1)) self._ports.append(LinePort(top_left, top_right)) self._ports.append(LinePort(top_right, bottom_right)) self._ports.append(LinePort(bottom_right, bottom_left)) self._ports.append(LinePort(bottom_left, top_left)) self._last_connected_side = None self.watch("subject[NamedElement].name") def update_shapes(self): self.shape = IconBox( Box(style={"background-color": (1, 1, 1, 1)}, draw=draw_border), Text(text=lambda: stereotypes_str(self.subject, ("proxy", ))), EditableText( text=lambda: self.subject and self.subject.name or ""), style=text_position(self.connected_side()), ) self.request_update() def connected_side(self) -> Optional[str]: if not self.canvas: return None cinfo = self.canvas.get_connection(self._handles[0]) return cinfo.connected.port_side(cinfo.port) if cinfo else None def dimensions(self): return Rectangle(-8, -8, 16, 16) def point(self, pos): return distance_rectangle_point(self.dimensions(), pos) def setup_canvas(self): super().setup_canvas() self.subscribe_all() # Invoke here, since we do not receive events, unless we're attached to a canvas self.update_shapes() def teardown_canvas(self): self.unsubscribe_all() super().teardown_canvas() def save(self, save_func): save_func("matrix", tuple(self.matrix)) assert self.canvas c = self.canvas.get_connection(self.handles()[0]) if c: save_func("connection", c.connected) super().save(save_func) def load(self, name, value): if name == "matrix": self.matrix = ast.literal_eval(value) elif name == "connection": self._load_connection = value else: super().load(name, value) def postload(self): super().postload() if hasattr(self, "_load_connection"): postload_connect(self, self.handles()[0], self._load_connection) del self._load_connection self.update_shapes() def pre_update(self, context): side = self.connected_side() if self._last_connected_side != side: self._last_connected_side = side self.update_shapes() self.shape.size(context) def draw(self, context): self.shape.draw(context, self.dimensions())