def __init__(self, diagram, id=None): super().__init__(diagram, id) for h in self.handles(): h.movable = False self.shape = IconBox( Box(style={ "min-width": 30, "min-height": 30 }, 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) self.shape_tail = Box( Text(text=lambda: stereotypes_str(self.subject), ), EditableText(text=lambda: self.subject.name or ""), ) self.watch("subject[NamedElement].name") self.watch("subject.appliedStereotype.classifier.name") self.shape_middle = EditableText( text=lambda: self.subject and self.subject.guard or "") self.watch("subject[ControlFlow].guard") self.watch("subject[ObjectFlow].guard") self.draw_tail = draw_arrow_tail
def __init__(self, connections, id=None, model=None): super().__init__(connections, id, model) self.shape_tail = Box( Text(text=lambda: stereotypes_str(self.subject), ), EditableText(text=lambda: self.subject.name or ""), ) self.watch("subject[NamedElement].name") self.watch("subject.appliedStereotype.classifier.name") self.shape_middle = EditableText( text=lambda: self.subject and self.subject.guard and self.subject. guard.specification or "") self.watch("subject[Transition].guard[Constraint].specification") self.draw_tail = draw_arrow_tail
def __init__(self, id=None, model=None): super().__init__(id, model) no_movable_handles(self) self.shape = IconBox( Box(style={ "min-width": 30, "min-height": 30 }, 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, 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 __init__(self, id=None, model=None): super().__init__(id, model, style={"dash-style": (7.0, 5.0)}) self.shape_middle = Box( Text( text=lambda: stereotypes_str(self.subject, (self.relation_type, )), style={ "min-width": 0, "min-height": 0 }, ), EditableText(text=lambda: self.subject.name or ""), ) self.draw_head = draw_arrow_head self.watch("subject[NamedElement].name").watch( "subject.appliedStereotype.classifier.name")
def __init__(self, id=None, model=None): super().__init__(id, model) # AssociationEnds are really inseperable 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._show_direction = False self._dir_angle = 0 self._dir_pos = 0, 0 self.shape_middle = Box( Text( text=lambda: stereotypes_str(self.subject), style={ "min-width": 0, "min-height": 0 }, ), 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}.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].ownedEnd").watch( "subject[Association].navigableOwnedEnd")
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, connections, id=None, model=None): super().__init__(connections, id, model) self.shape = 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), "border-radius": 15, }, draw=draw_border, ) self.watch("subject[NamedElement].name") self.watch("subject.appliedStereotype.classifier.name")
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") self.constraint(vertical=(h1.pos, h2.pos)) self.constraint(above=(h1.pos, h2.pos), delta=30)
def attributes_compartment(subject): # We need to fix the attribute value, since the for loop changes it. def lazy_format(attribute): return lambda: format(attribute) return Box( *(Text( text=lazy_format(attribute), style={ "text-align": TextAlign.LEFT, "text-decoration": TextDecoration.UNDERLINE if attribute.isStatic else TextDecoration.NONE, }, ) for attribute in subject.ownedAttribute if not attribute.association), style={"padding": (4, 4, 4, 4)}, draw=draw_top_separator, )
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), ), EditableText( text=lambda: self.subject.name or "", style={"font-weight": FontWeight.BOLD}, ), ) self.watch("subject[NamedElement].name") self.watch("subject.appliedStereotype.classifier.name")
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 test_icon_box_child_placement_left_middle(context): style = { "text-align": TextAlign.LEFT, "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 == -w assert y == (bounding_box.height - h) / 2
def ball_and_socket_shape(self, connectors=None): if connectors is None: # distinguish between None and [] connected_items = [ c.item for c in self.diagram.connections.get_connections( connected=self) ] connectors = any( map(lambda i: isinstance(i.subject, UML.Connector), connected_items)) return IconBox( Box(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 _create_stereotype_compartment(appliedStereotype): def lazy_format(slot): return lambda: format(slot) slots = [slot for slot in appliedStereotype.slot if slot.value] if slots: return Box( Text( text=lazy_format(appliedStereotype.classifier[0]), style={"padding": (0, 0, 4, 0)}, ), *( Text(text=lazy_format(slot), style={"text-align": TextAlign.LEFT}) for slot in slots ), style={"padding": (4, 4, 4, 4), "vertical-align": VerticalAlign.TOP}, draw=draw_top_separator, ) else: return None
def __init__(self, diagram, id=None): super().__init__(diagram, id, style={"dash-style": (7.0, 5.0)}) self._dependency_type = UML.Dependency # auto_dependency is used by connection logic, not in this class itself self.auto_dependency = True additional_stereotype = { UML.Usage: ("use", ), UML.Realization: ("realize", ), UML.InterfaceRealization: ("implements", ), } self.shape_middle = Box( Text(text=lambda: stereotypes_str( self.subject, additional_stereotype.get(self._dependency_type, ())), ), EditableText(text=lambda: self.subject.name or ""), ) self.watch("subject[NamedElement].name") self.watch("subject.appliedStereotype.classifier.name")
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": "x-small", "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)}, draw=draw_top_separator, )
def __init__(self, id=None, model=None): super().__init__( id, model, shape_middle=Box( Text( text=lambda: stereotypes_str(self.subject), style={ "min-width": 0, "min-height": 0 }, ), EditableText(text=lambda: self.subject.name or ""), ), ) self._is_communication = False self._arrow_pos = 0, 0 self._arrow_angle = 0 self.watch("subject[NamedElement].name") self.watch("subject.appliedStereotype.classifier.name")
def __init__(self, id=None, model=None): super().__init__(id, model) self.shape = Box( Text( text=lambda: stereotypes_str( self.subject, isinstance(self.subject, UML.Profile) and ("profile", ) or (), ), style={ "min-width": 0, "min-height": 0 }, ), EditableText( text=lambda: self.subject and 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={ "min-width": 50, "min-height": 70, "padding": (25, 10, 5, 10) }, draw=draw_package, ) self.watch("subject[NamedElement].name") self.watch("subject[NamedElement].namespace") self.watch("subject.appliedStereotype.classifier.name")
def __init__(self, id=None, model=None): super().__init__(id, model) OFFSET = 5 ear = self.EAR self.min_width = ear + 2 * OFFSET self.height = 50 self.width = 100 self.body = Text( text=lambda: self.subject.body or "", width=lambda: self.width - ear - 2 * OFFSET, style={ "text-align": TextAlign.LEFT, "vertical-align": VerticalAlign.TOP }, ) self.shape = Box( self.body, style={"padding": (OFFSET, ear + OFFSET, OFFSET, OFFSET)}, draw=self.draw_border, ) self.watch("subject[Comment].body")
def attributes_compartment(subject): # We need to fix the attribute value, since the for loop changes it. def lazy_format(attribute): # str(), so we never ever get an error on a property part of an association return lambda: (UML.format(attribute)) return Box( *( Text( text=lazy_format(attribute), style={ "text-align": TextAlign.LEFT, "text-decoration": TextDecoration.UNDERLINE if attribute.isStatic else TextDecoration.NONE, }, ) for attribute in subject.ownedAttribute if not attribute.association ), style={"padding": (4, 4, 4, 4), "min-height": 8}, draw=draw_top_separator, )
def __init__(self, id=None, model=None): super().__init__(id, model) self.shape = Box( 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}, ), style={ "min-width": 50, "min-height": 30 }, draw=draw_usecase, ) self.watch("subject[NamedElement].name") self.watch("subject.appliedStereotype.classifier.name")
def __init__(self, id=None, model=None): super().__init__(id, model) self.shape_tail = Box( Text( text=lambda: stereotypes_str(self.subject), style={ "min-width": 0, "min-height": 0 }, ), EditableText(text=lambda: self.subject.name or ""), ) self.watch("subject<NamedElement>.name") self.watch("subject.appliedStereotype.classifier.name") self.shape_middle = EditableText( text=lambda: self.subject and self.subject.guard or "") self.watch("subject<ControlFlow>.guard") self.watch("subject<ObjectFlow>.guard") self.draw_tail = draw_arrow_tail
def update_shapes(self, event=None): text_align = (TextAlign.LEFT if self.diagram and self.children else TextAlign.CENTER) self.shape = Box( EditableText( text=lambda: self.subject.name or "", style={ "font-weight": FontWeight.BOLD, "text-align": text_align }, ), Text( text=lambda: self.subject.technology and f"[{gettext(self.subject.type)}: {self.subject.technology}]" or f"[{gettext(self.subject.type)}]", style={ "font-size": "x-small", "text-align": text_align }, ), *(() if self.children else (Text( text=lambda: self.subject.description or "", width=lambda: self.width - 8, style={ "padding": (4, 0, 0, 0), "text-align": text_align }, ), )), style={ "padding": (4, 4, 4, 4), "vertical-align": VerticalAlign.BOTTOM if self.diagram and self.children else VerticalAlign.MIDDLE, }, draw=draw_border, )
def operations_compartment(subject): def lazy_format(operation): return lambda: format(operation, visibility=True, type=True, multiplicity=True, default=True) return Box( *(Text( text=lazy_format(operation), style={ "text-align": TextAlign.LEFT, "font-style": FontStyle.ITALIC if operation.isAbstract else FontStyle.NORMAL, "text-decoration": TextDecoration.UNDERLINE if operation.isStatic else TextDecoration.NONE, }, ) for operation in subject.ownedOperation), style={"padding": (4, 4, 4, 4)}, draw=draw_top_separator, )
def test_draw_empty_box(): box = Box(draw=None) box.draw(context=None, bounding_box=Rectangle())
def test_box_size(): box = Box() assert box.size(cr=None) == (0, 0)
class PartitionItem(ElementPresentation, Named): DELTA = 30 def __init__(self, id=None, model=None): super().__init__(id, model) self._toplevel = False self._bottom = False self._subpart = False self._hdmax = 0 # maximum subpartition header height self.shape = Box( Text( text=lambda: stereotypes_str( self.subject, self.subject and self.subject.isExternal and ("external",) or (), ), ), Text(text=lambda: self.subject.name or ""), style={ "min-width": 0, "min-height": 0, "line-width": 2.4, "vertical-align": VerticalAlign.TOP, "padding": (2, 2, 2, 2), }, draw=self.draw_partition, ) self.min_width = 100 self.min_height = 300 @property def toplevel(self): return self._toplevel def pre_update(self, context): assert self.canvas self._header_size = self.shape.size( SizeContext.from_context(context, self.style) ) # get subpartitions children: List[PartitionItem] = list( k for k in self.canvas.get_children(self) if isinstance(k, PartitionItem) ) self._toplevel = self.canvas.get_parent(self) is None self._subpart = len(children) > 0 self._bottom = not self._toplevel and not self._subpart if self._toplevel: self._header_size = self._header_size[0], self.DELTA handles = self.handles() # toplevel partition controls the height # partitions at the very bottom control the width # middle partitions control nothing for h in handles: h.movable = False h.visible = False if self._bottom: h = handles[1] h.visible = h.movable = True if self._toplevel: h1, h2 = handles[2:4] h1.visible = h1.movable = True h2.visible = h2.movable = True if self._subpart: wsum: int = sum(sl.width for sl in children) self._hdmax = max(sl._header_size[1] for sl in children) # extend width of swimline due the children but keep the height # untouched self.width = wsum dp = 0 for sl in self.canvas.get_children(self): x, y = sl.matrix[4], sl.matrix[5] x = dp - x y = -y + self._header_size[1] + self._hdmax - sl._header_size[1] sl.matrix.translate(x, y) sl.height = sl.min_height = max(0, self.height - self._header_size[1]) dp += sl.width def draw_partition(self, box, context, bounding_box): """ By default vertical partition is drawn. It is open on the bottom. """ assert self.canvas cr = context.cairo cr.set_line_width(context.style["line-width"]) if self.subject and not self.subject.isDimension and self._toplevel: cr.move_to(0, 0) cr.line_to(bounding_box.width, 0) h = self._header_size[1] # draw outside lines if this item is toplevel partition if self._toplevel: cr.move_to(0, bounding_box.height) cr.line_to(0, h) cr.line_to(bounding_box.width, h) cr.line_to(bounding_box.width, bounding_box.height) if self._subpart: # header line for all subparitions hd = h + self._hdmax cr.move_to(0, hd) cr.line_to(bounding_box.width, hd) if self._subpart: # draw inside lines for all children but last one dp = 0 for sl in self.canvas.get_children(self)[:-1]: dp += sl.width cr.move_to(dp, h) cr.line_to(dp, bounding_box.height) stroke(context) if context.hovered or context.dropzone: 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) draw_highlight(context) cr.stroke()
class ExecutionSpecificationItem(Presentation[UML.ExecutionSpecification], Item): """ Representation of interaction execution specification. """ def __init__(self, id=None, model=None): super().__init__(id, model) self.bar_width = 12 ht, hb = Handle(), Handle() ht.connectable = True # TODO: need better interface for this! self._handles.append(ht) self._handles.append(hb) self.constraint(vertical=(ht.pos, hb.pos)) r = self.bar_width / 2 nw = Position((-r, 0), strength=WEAK) ne = Position((r, 0), strength=WEAK) se = Position((r, 0), strength=WEAK) sw = Position((-r, 0), strength=WEAK) self.constraint(horizontal=(sw, hb.pos)) self.constraint(horizontal=(se, hb.pos)) self._ports.append(LinePort(nw, sw)) self._ports.append(LinePort(ne, se)) self.shape = Box(style={"fill": "white"}, draw=draw_border) @property def top(self): return self._handles[0] @property def bottom(self): return self._handles[1] def dimensions(self): d = self.bar_width pt, pb = (h.pos for h in self._handles) return Rectangle(pt.x - d / 2, pt.y, d, y1=pb.y) def draw(self, context): self.shape.draw(context, self.dimensions()) def point(self, pos): return distance_rectangle_point(self.dimensions(), pos) def save(self, save_func): def save_connection(name, handle): assert self.canvas c = self.canvas.get_connection(handle) if c: save_func(name, c.connected, reference=True) points = [tuple(map(float, h.pos)) for h in self.handles()] save_func("matrix", tuple(self.matrix)) save_func("points", points) save_connection("head-connection", self.handles()[0]) super().save(save_func) def load(self, name, value): if name == "matrix": self.matrix = ast.literal_eval(value) elif name == "points": points = ast.literal_eval(value) for h, p in zip(self.handles(), points): h.pos = p elif name == "head-connection": self._load_head_connection = value else: super().load(name, value) def postload(self): if hasattr(self, "_load_head_connection"): postload_connect(self, self.handles()[0], self._load_head_connection) del self._load_head_connection super().postload()