Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat port label #134

Merged
merged 3 commits into from
Sep 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions capellambse_context_diagrams/_elkjs.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,9 @@ class ELKInputPort(BaseELKModel):
layoutOptions: LayoutOptions = pydantic.Field(default_factory=dict)
width: t.Union[int, float]
height: t.Union[int, float]
labels: cabc.MutableSequence[ELKInputLabel] = pydantic.Field(
default_factory=list
)


class ELKInputEdge(BaseELKModel):
Expand Down
25 changes: 18 additions & 7 deletions capellambse_context_diagrams/collectors/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,13 @@ def _process_exchanges(self) -> tuple[
except AttributeError:
continue

self.centerbox.ports = [
makers.make_port(uuid) for uuid in {**inc_c, **out_c}
]
for p in inc + out:
port = makers.make_port(p.uuid)
if self.diagram._display_port_labels:
port.labels = makers.make_label(p.name)
self.centerbox.ports.append(port)
self.centerbox.layoutOptions["portLabels.placement"] = "OUTSIDE"
ewuerger marked this conversation as resolved.
Show resolved Hide resolved

return (inc + out), ex_datas

def _process_ports(self, stack_heights: dict[str, float | int]) -> None:
Expand All @@ -202,20 +206,27 @@ def _process_ports(self, stack_heights: dict[str, float | int]) -> None:
makers.PORT_PADDING
+ (makers.PORT_SIZE + makers.PORT_PADDING) * len(local_ports),
)
local_port_objs = []
for j in local_ports:
port = makers.make_port(j.uuid)
if self.diagram._display_port_labels:
port.labels = makers.make_label(j.name)
local_port_objs.append(port)

if box := self.global_boxes.get(owner.uuid): # type: ignore[assignment]
if box is self.centerbox:
continue
box.ports.extend(
[makers.make_port(j.uuid) for j in local_ports]
)
box.ports.extend(local_port_objs)
box.height += height
else:
box = self._make_box(
owner,
height=height,
no_symbol=self.diagram._display_symbols_as_boxes,
)
box.ports = [makers.make_port(j.uuid) for j in local_ports]
box.ports = local_port_objs

box.layoutOptions["portLabels.placement"] = "OUTSIDE"
ewuerger marked this conversation as resolved.
Show resolved Hide resolved

if self.diagram._display_parent_relation:
current = owner
Expand Down
3 changes: 3 additions & 0 deletions capellambse_context_diagrams/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,12 +221,14 @@ class ContextDiagram(m.AbstractDiagram):
* slim_center_box — Minimal width for the center box, containing
just the icon and the label. This is False if hierarchy was
identified.
* display_port_labels — Display port labels on the diagram.
"""

_display_symbols_as_boxes: bool
_display_parent_relation: bool
_display_derived_interfaces: bool
_slim_center_box: bool
_display_port_labels: bool

def __init__(
self,
Expand All @@ -248,6 +250,7 @@ def __init__(
"display_parent_relation": False,
"display_derived_interfaces": False,
"slim_center_box": True,
"display_port_labels": False,
} | default_render_parameters

if standard_filter := STANDARD_FILTERS.get(class_):
Expand Down
55 changes: 27 additions & 28 deletions capellambse_context_diagrams/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,35 +217,34 @@ class type that stores all previously named classes.
self._cache[uuid] = element
elif child.type == "label":
assert parent is not None
if not parent.port:
if parent.JSON_TYPE != "symbol":
parent.styleoverrides.update(styleoverrides)

if isinstance(parent, cdiagram.Box):
attr_name = "floating_labels"
else:
attr_name = "labels"

if labels := getattr(parent, attr_name):
label_box = labels[-1]
label_box.label += " " + child.text
label_box.size = cdiagram.Vector2D(
max(label_box.size.x, child.size.width),
label_box.size.y + child.size.height,
)
label_box.pos = cdiagram.Vector2D(
min(label_box.pos.x, ref.x + child.position.x),
label_box.pos.y,
)
else:
labels.append(
cdiagram.Box(
ref + (child.position.x, child.position.y),
(child.size.width, child.size.height),
label=child.text,
styleoverrides=styleoverrides,
)
if parent.JSON_TYPE != "symbol":
parent.styleoverrides.update(styleoverrides)

if isinstance(parent, cdiagram.Box):
attr_name = "floating_labels"
else:
attr_name = "labels"

if labels := getattr(parent, attr_name):
label_box = labels[-1]
label_box.label += " " + child.text
label_box.size = cdiagram.Vector2D(
max(label_box.size.x, child.size.width),
label_box.size.y + child.size.height,
)
label_box.pos = cdiagram.Vector2D(
min(label_box.pos.x, ref.x + child.position.x),
label_box.pos.y,
)
else:
labels.append(
cdiagram.Box(
ref + (child.position.x, child.position.y),
(child.size.width, child.size.height),
label=child.text,
styleoverrides=styleoverrides,
)
)

element = parent
elif child.type == "junction":
Expand Down
19 changes: 19 additions & 0 deletions docs/extras/styling.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,22 @@ Style your diagram elements ([ElkChildType][capellambse_context_diagrams.seriali
<img src="../../assets/images/Context of Lost red junction.svg" width="1000000">
<figcaption>Context diagram of Lost SystemFunction with junction point styling</figcaption>
</figure>

# Display Port Labels

The `display_port_labels` render parameter allows you to display the port labels and `port_label_position` allows you to set the position of the port labels.

??? example display port labels for "Hierarchical diagram"

``` py
import capellambse

model = capellambse.MelodyModel("tests/data/ContextDiagram.aird")
obj = model.by_uuid("16b4fcc5-548d-4721-b62a-d3d5b1c1d2eb")
diagram = obj.context_diagram.render("svgdiagram", display_port_labels=True)
diagram.save(pretty=True)
```
<figure markdown>
<img src="../../assets/images/Context of Hierarchy display_port_labels.svg" width="1000000">
<figcaption>Context diagram of Hierarchy LogicalComponenet with type [LAB] display_port_labels</figcaption>
</figure>
14 changes: 14 additions & 0 deletions docs/gen_images.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,19 @@ def generate_no_edgelabel_image(uuid: str) -> None:
)


def generate_display_port_labels_image(uuid: str) -> None:
cdiagram: context.ContextDiagram = model.by_uuid(uuid).context_diagram
cdiagram.invalidate_cache()
filename = " ".join((str(dest / cdiagram.name), "display_port_labels"))
with mkdocs_gen_files.open(f"{filename}.svg", "w") as fd:
print(
cdiagram.render(
"svg", display_port_labels=True, transparent_background=False
),
file=fd,
)


def generate_filter_image(
uuid: str, filter_name: str, suffix: str = ""
) -> None:
Expand Down Expand Up @@ -198,6 +211,7 @@ def generate_interface_with_hide_interface_image():

wizard_uuid = general_context_diagram_uuids["educate Wizards"]
generate_no_edgelabel_image(wizard_uuid)
generate_display_port_labels_image(hierarchy_context)

lost_uuid = general_context_diagram_uuids["Lost"]
generate_filter_image(lost_uuid, filters.EX_ITEMS, "ex")
Expand Down
14 changes: 14 additions & 0 deletions tests/test_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
CAP_EXPLOIT = "4513c8cd-b94b-4bde-bd00-4c18aaf600ff"
FNC_UUID = "a5642060-c9cc-4d49-af09-defaa3024bae"
INTERF_UUID = "9cbdd233-aff5-47dd-9bef-9be1277c77c3"
HIERARCHY_UUID = "16b4fcc5-548d-4721-b62a-d3d5b1c1d2eb"

Types = list[t.Union[mm.fa.FunctionalExchange, mm.fa.ComponentExchange]]

Expand Down Expand Up @@ -196,3 +197,16 @@ def test_context_diagrams_no_edgelabels_render_param_is_applied(
for aedge in adiag:
if isinstance(aedge, diagram.Edge):
assert not aedge.labels


def test_context_diagrams_display_port_labels_render_param_is_applied(
model: MelodyModel,
) -> None:
obj = model.by_uuid(HIERARCHY_UUID)
diag: context.ContextDiagram = obj.context_diagram

adiag = diag.render(None, display_port_labels=True)

for aport in adiag:
if isinstance(aport, diagram.Box) and aport.port:
assert aport.floating_labels
Loading