建模语言

自2.0版本起,Gaphor开始支持建模语言概念。这一特性使得建模语言的开发能够独立于Gaphor核心应用程序进行。

UML始终是Gaphor的核心建模语言。当前版本还新增支持SysML子集、RAAML及C4模型。

在Gaphor中,建模语言通过实现gaphor.abc.ModelingLanguage抽象基类的类来定义。该建模语言需注册为gaphor.modelinglanguages入口点。

ModelingLanguage接口设计极为精简,主要提供元素、工具箱及图表类型的查询功能。但建模语言的职责远不止于此,部分功能需通过向通用函数注册处理器来实现。

但让我们循序渐进地探讨:建模语言的实现具体能提供哪些功能?

ModelingLanguage实例主要提供三大功能:

  • 数据模型(元素)与图表项

  • 图表类型

  • 工具箱定义

其他功能可以通过向相应的通用功能添加处理程序(Handler)来扩展。

建模语言还可以提供新的 UI 组件。这些组件不会在导入建模语言包时直接加载,而应通过 gaphor.modules 入口点导入。

建模语言

class gaphor.abc.ModelingLanguage[源代码]

模型提供者(Model Provider)是一种特殊服务,它为UML、SysML、RAAML等模型实现提供入口点。

abstract property diagram_types: Iterable[DiagramType]

迭代图表类型。

abstract property element_types: Iterable[ElementCreateInfo]

迭代元素类型。

abstractmethod lookup_element(name: str, ns: str | None = None) type[Base] | None[源代码]

通过(类)名称查询模型元素类型。

可提供命名空间,该设置将限定模型仅能从特定建模语言加载。

abstract property model_browser_model: type[TreeModel]

用于模型浏览器的模型。

abstract property name: str

该建模语言的人类可读名称。

abstract property toolbox_definition: ToolboxDefinition

获取工具箱结构。

根据约定,包含建模语言的包应设置__modeling_language__属性,其属性值需与入口点中的建模语言名称保持一致。

示例说明:

文件mytool/mylang/__init__.py中包含以下条目:

__modeling_language__ = "MyLang"

pyproject.toml 文件中包含以下入口点配置:

[project.entry-points."gaphor.modelinglanguages"]
"MyLang" = "mytool.mylang.modelinglanguage:MyLangModelingLanguage"

连接器

连接器用于实现元素间的相互连接。

连接器应遵循ConnectorProtocol协议规范。通常建议继承BaseConnector基类实现。

class gaphor.diagram.connectors.BaseConnector(element: Presentation[Base], line: Presentation[Base])[源代码]

Gaphor 图表项连接适配器。

线段项 line 通过连接柄与可连接项 element 建立关联。

参数:
  • line -- 连接项

  • element -- 可连接项

根据规范,适配器需按(元素,线段)的顺序进行注册。

allow(handle: Handle, port: Port) bool[源代码]

判断项目是否可建立连接。

(例如在鼠标移动过程中)该连接操作是否被允许?

若允许连接,则返回True

connect(handle: Handle, port: Port) bool[源代码]

连接至元素。

建立元素与线段之间的连接关系。该操作还会根据需求(如1:1关系)处理断开连接的情况。

请注意:此时线段可能已连接至其他元素或同一元素,且模型层级的连接关系仍然存在。

若成功建立连接,则返回 True

disconnect(handle: Handle) None[源代码]

断开模型层级的连接关系。

断开连接(当连接柄被放置于不可连接位置时触发此操作)。

get_connected(handle: Handle) Presentation[Base] | None[源代码]

获取与连接柄关联的项。

格式化与解析

模型元素可格式化为简单文本表示形式(例如在模型浏览器中使用)。请注意,此非模型元素的完整序列化。

某些情况下,将文本解析回对象十分实用。该功能常用于编辑类的属性和操作时。

并非所有format()方法都需要对应的parse()函数。

gaphor.core.format.format(element: Element) str

返回模型元素的人类可读表示形式。多数情况下仅显示名称,但属性(attribute)和操作(operation)会以更详细的格式呈现:

+ attr: str
+ format(element: Element): string
gaphor.core.format.parse(element: Element, text: str) None

解析 text 并填充 element。该元素将被文本中的内容填充,这可能意味着在解析过程中会创建新的模型元素。

复制和粘贴

对于简单项(即单个图表项对应单个模型元素的情况),复制粘贴功能可开箱即用。该功能通过调用元素的load()save()方法,确保复制所有相关数据。

某些情况下,图表项需要多个模型元素协同工作。例如关联(Association)元素就需要两个关联端(association ends)才能正常运作。

针对这些特定情况,您需要自行实现复制粘贴功能。具体需要创建两个函数:一个处理复制操作,另一个处理粘贴操作。

gaphor.diagram.copypaste.copy(obj: Base | Iterable) Iterator[tuple[Id, Opaque]]

创建元素(或元素列表)的副本。返回类型应具有独特性,以便paste()函数能正确分发。复制函数通常仅复制元素及其必需关联元素,例如关联(Association)必须包含两个关联端。

gaphor.diagram.copypaste.paste(copy_data: Opaque, diagram: Diagram, lookup: Callable[[str], Base | None]) Iterator[Base]

粘贴先前复制的数据。根据copy()函数中创建的数据类型,尝试复制被拷贝的元素。返回新创建的项或元素。

Gaphor 提供以下便捷功能:

gaphor.diagram.copypaste.copy_full(items: Collection[Base], lookup: Callable[[Id], Base | None] | None = None) CopyData:

复制 items。该操作使用 lookup 函数查询所属元素(这些元素在模型浏览器中显示为子节点)。

将呈现元素(Presentation element)的副本粘贴到图表中,同时尝试关联底层模型元素。(该操作为浅拷贝)。

gaphor.diagram.copypaste.paste_full(copy_data: CopyData, diagram: Diagram) set[~gaphor.core.modeling.Presentation]:

粘贴呈现元素(Presentation element)及模型元素(model element)的副本。(该操作为深拷贝)。

分组

分组操作通过将一个元素拖拽到另一个元素上来实现,可在图表或树状视图中进行。

gaphor.diagram.group.owner(element: Base) Base | RootType | None

返回 element 的所有者。所有者可能为 Root(表示该元素应置于所属关系层级的根节点)。若返回 None,则表示该元素没有所有者。

gaphor.diagram.group.owns(element: Base) list[Base]

返回 element 所拥有的所有元素

gaphor.diagram.group.group(parent: Base, element: Base) bool

将元素归组到父元素中。分组可基于所属关系(Ownership)实现,但也支持其他分组类型。

gaphor.diagram.group.ungroup(parent: Base, element: Base) bool

移除元素的分组关系。该功能需要检查所指定的 parent 节点是否正确。

gaphor.diagram.group.can_group(parent_type: type[Base], element_or_type: type[Base] | Base) bool

该功能会尝试判断是否可以进行分组操作(但不会实际执行分组),其判断结果并非100%准确。

拖放

拖放操作通过将元素从树状视图拖拽至图表中完成。这是使用现有模型元素快速扩展图表的便捷方式。

此外,也可将展示项(Presentation item)拖放至其他元素上方。

gaphor.diagram.drop.drop(element: Base | Presentation, diagram: Diagram | Presentation, x: float, y: float) Presentation | None

拖放功能会为元素在图表中创建新的展示项(若该元素尚未被展示)。对于关联关系,只有当被连接的两个元素都存在于同一图表中时,拖放操作才会生效。

分组(Grouping) 用于关联模型元素,而 拖放(Dropping) 则用于在图表中的目标项上创建和定位展示元素。

自动模型清理

Gaphor 致力于保持模型与图表之间的同步。

通过一个小型调度函数来判断模型元素是否可被移除。

gaphor.diagram.deletable.deletable(element: Base) bool

判断模型元素是否可安全移除。

属性编辑器页面

编辑器页面由多个代码片段构建而成。例如:几乎每个元素都包含名称属性,因此会有一个专门的UI片段供用户编辑名称。

每个属性页面(代码片段)都应继承自 PropertyPageBase 基类。

class gaphor.diagram.propertypages.PropertyPageBase[源代码]

一个可在记事本中显示自身的属性页面。

close() None[源代码]

Called when a property page is removed.

This allows us to clean up.

abstractmethod construct() Gtk.Widget | None[源代码]

创建属于属性页面的页面部件(Gtk.Widget)。

返回该页面的顶级部件(Gtk.Widget)。

即时(图表)编辑器弹窗

在图表中双击某一元素时,会弹出编辑窗口,此时可快速修改名称。

默认情况下,此功能适用于所有带名称的元素。如需扩展功能,可注册自定义的行内编辑器函数。

gaphor.diagram.instanteditors.instant_editor(item: Item, view, event_manager: EventManager, pos: tuple[int, int] | None = None) bool

在图表中显示小型编辑弹窗,无需借助元素编辑器即可轻松修改。

当触发鼠标按下事件时,系统还会提供鼠标位置(相对于元素的坐标)。