Skip to content

Commit

Permalink
Merge pull request #330 from gaphor/parts-and-references
Browse files Browse the repository at this point in the history
Parts and references
  • Loading branch information
amolenaar committed May 30, 2020
2 parents b89b56b + bafd7e3 commit fab8777
Show file tree
Hide file tree
Showing 9 changed files with 289 additions and 31 deletions.
129 changes: 125 additions & 4 deletions gaphor/SysML/blocks/block.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,130 @@
from gaphor.core import gettext
from gaphor.core.modeling.properties import attribute
from gaphor.diagram.presentation import (
Classified,
ElementPresentation,
from_package_str,
)
from gaphor.diagram.shapes import (
Box,
EditableText,
FontStyle,
FontWeight,
Text,
TextAlign,
VerticalAlign,
draw_border,
draw_top_separator,
)
from gaphor.diagram.support import represents
from gaphor.SysML.sysml import Block
from gaphor.UML.classes import ClassItem
from gaphor.UML.classes.klass import (
attribute_watches,
stereotype_compartments,
stereotype_watches,
)
from gaphor.UML.modelfactory import stereotypes_str
from gaphor.UML.umlfmt import format_attribute


@represents(Block)
class BlockItem(ClassItem):
def additional_stereotypes(self):
return ["block"]
class BlockItem(ElementPresentation[Block], Classified):
def __init__(self, id=None, model=None):
super().__init__(id, model)

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.association and a.aggregation == "composite",
)
]
or []
),
*(
self.show_references
and self.subject
and [
self.block_compartment(
gettext("references"),
lambda a: a.association and 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_attribute(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,
)
52 changes: 52 additions & 0 deletions gaphor/SysML/propertypages.glade
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,58 @@ Edit, from the popup menu, will allow you to add cell
renderers and such.</property>
<property name="xalign">0</property>
</object>
<object class="GtkExpander" id="parts-and-references-editor">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="margin_top">12</property>
<property name="expanded">True</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_top">6</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkCheckButton" id="show-parts">
<property name="label" translatable="yes">Show parts</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="show-parts-changed" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="show-references">
<property name="label" translatable="yes">Show references</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="show-references-changed" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<child type="label">
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Parts and References</property>
</object>
</child>
</object>
<object class="GtkTextBuffer" id="requirement-text-buffer"/>
<object class="GtkBox" id="requirement-editor">
<property name="visible">True</property>
Expand Down
49 changes: 49 additions & 0 deletions gaphor/SysML/propertypages.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
from gaphor.core import transactional
from gaphor.diagram.propertypages import PropertyPageBase, PropertyPages
from gaphor.SysML import sysml
from gaphor.SysML.blocks.block import BlockItem
from gaphor.SysML.requirements.requirement import RequirementItem
from gaphor.UML.classes.classespropertypages import AttributesPage, OperationsPage


def new_builder(*object_ids):
Expand Down Expand Up @@ -70,3 +73,49 @@ def _on_text_changed(self, buffer):
self.subject.text = buffer.get_text(
buffer.get_start_iter(), buffer.get_end_iter(), False
)


PropertyPages.register(RequirementItem)(AttributesPage)
PropertyPages.register(RequirementItem)(OperationsPage)


@PropertyPages.register(BlockItem)
class PartsAndReferencesPage(PropertyPageBase):
"""An editor for Block items."""

order = 30

def __init__(self, item):
super().__init__()
self.item = item
self.watcher = item.subject and item.subject.watcher()

def construct(self):
if not self.item.subject:
return

builder = new_builder("parts-and-references-editor")

show_parts = builder.get_object("show-parts")
show_parts.set_active(self.item.show_parts)

show_references = builder.get_object("show-references")
show_references.set_active(self.item.show_references)

builder.connect_signals(
{
"show-parts-changed": (self._on_show_parts_change,),
"show-references-changed": (self._on_show_references_change,),
}
)
return builder.get_object("parts-and-references-editor")

@transactional
def _on_show_parts_change(self, button):
self.item.show_parts = button.get_active()
self.item.request_update()

@transactional
def _on_show_references_change(self, button):
self.item.show_references = button.get_active()
self.item.request_update()
46 changes: 33 additions & 13 deletions gaphor/SysML/requirements/requirement.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
from gaphor.diagram.presentation import from_package_str
from gaphor.core.modeling.properties import attribute
from gaphor.diagram.presentation import (
Classified,
ElementPresentation,
from_package_str,
)
from gaphor.diagram.shapes import (
Box,
EditableText,
Expand All @@ -13,34 +18,49 @@
from gaphor.diagram.support import represents
from gaphor.SysML.sysml import Requirement
from gaphor.UML.classes.klass import (
ClassItem,
attribute_watches,
attributes_compartment,
operation_watches,
operations_compartment,
stereotype_compartments,
stereotype_watches,
)
from gaphor.UML.modelfactory import stereotypes_str


@represents(Requirement)
class RequirementItem(ClassItem):
class RequirementItem(ElementPresentation[Requirement], Classified):
def __init__(self, id=None, model=None):
super().__init__(id, model)

self.show_attributes = False
self.show_operations = False
self.watch("subject[AbstractRequirement].externalId", self.update_shapes)
self.watch("subject[AbstractRequirement].text", self.update_shapes)
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)

def additional_stereotypes(self):
return ["requirement"]
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, self.additional_stereotypes()
),
text=lambda: stereotypes_str(self.subject, ["requirement"]),
style={"min-width": 0, "min-height": 0},
),
EditableText(
Expand Down Expand Up @@ -82,7 +102,7 @@ def update_shapes(self, event=None):
)

def id_and_text_compartment(self):
subject: Requirement = self.subject # type: ignore[assignment]
subject = self.subject
if subject and (subject.externalId or subject.text):
return Box(
*(
Expand Down
23 changes: 13 additions & 10 deletions gaphor/UML/classes/klass.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,21 +45,12 @@ def __init__(self, id=None, model=None):
"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[Classifier].isAbstract", self.update_shapes
)
attribute_watches(self, "Class")
operation_watches(self, "Class")
stereotype_watches(self)

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

Expand Down Expand Up @@ -139,6 +130,8 @@ def attribute_watches(presentation, cast):
f"subject[{cast}].ownedAttribute.upperValue"
).watch(
f"subject[{cast}].ownedAttribute.defaultValue"
).watch(
f"subject[{cast}].ownedAttribute.type"
).watch(
f"subject[{cast}].ownedAttribute.typeValue"
)
Expand Down Expand Up @@ -170,6 +163,16 @@ def operation_watches(presentation, cast):
)


def stereotype_watches(presentation):
presentation.watch("subject.appliedStereotype", presentation.update_shapes).watch(
"subject.appliedStereotype.classifier.name"
).watch("subject.appliedStereotype.slot", presentation.update_shapes).watch(
"subject.appliedStereotype.slot.definingFeature.name"
).watch(
"subject.appliedStereotype.slot.value", presentation.update_shapes
)


def attributes_compartment(subject):
# We need to fix the attribute value, since the for loop changes it.
def lazy_format(attribute):
Expand Down

0 comments on commit fab8777

Please sign in to comment.