Langages de modélisation

Depuis la version 2.0, Gaphor supporte le concept de langages de modélisation. Cela permet de développer des langages de modélisation distincts de l’application principale de Gaphor.

Le langage principal était et reste UML. Gaphor prend désormais également en charge un sous-ensemble de SysML, RAAML et le modèle C4.

Un langage de modélisation dans Gaphor est défini par une classe implémentant la classe de base abstraite gaphor.abc.ModelingLanguage. Le langage de modélisation doit être enregistré en tant que point d’entrée gaphor.modelinglanguages.

L’interface ModelingLanguage (Langage de modélisation) est assez minimale. Elle permet à d’autres services de rechercher des éléments et des éléments de diagramme, ainsi qu’une boîte à outils et des types de diagramme. Cependant, les responsabilités d’un langage de modélisation ne s’arrêtent pas là. Certaines fonctionnalités seront mises en œuvre en enregistrant des gestionnaires pour un ensemble de fonctions génériques.

Mais n’allons pas trop vite en besogne. Quelles fonctionnalités une implémentation de langage de modélisation peut-elle offrir ?

Trois fonctionnalités sont exposées par une instance de ModelingLanguage :

  • Un modèle de données (éléments) et des éléments de diagramme

  • Types de diagrammes

  • Une définition de la boîte à outils

D’autres fonctionnalités peuvent être étendues en ajoutant des gestionnaires aux fonctions génériques correspondantes :

  • Connecteurs, permet de connecter des éléments de diagramme

  • Formater/analyser modéliser des éléments vers et à partir d’une représentation textuelle

  • Comportement de copier/coller lorsque la copie d’un élément n’est pas triviale, par exemple lorsque plusieurs éléments sont impliqués

  • Regroupement, permet d’imbriquer les éléments les uns dans les autres

  • Dropping, permet de faire glisser des éléments de l’arborescence sur un diagramme

  • Règles de nettoyage automatique pour maintenir la cohérence du modèle

Les langages de modélisation peuvent également fournir de nouveaux composants d’interface utilisateur. Ces composants ne sont pas chargés directement lors de l’importation d’un paquetage de langage de modélisation. Ils doivent être importés via le point d’entrée gaphor.modules.

Langue de modélisation

class gaphor.abc.ModelingLanguage[source]

Un fournisseur de modèle est un service spécialisé qui fournit un point d’entrée pour l’implémentation d’un modèle, comme UML, SysML ou RAAML.

abstract property diagram_types: Iterable[DiagramType]

Diagrammes types.

abstract property element_types: Iterable[ElementCreateInfo]

Itérer les types d’éléments.

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

Rechercher un élément de modèle par son nom de classe.

Un nom de domaine peut être fourni. Cela permettra au modèle d’être chargé uniquement à partir de ce langage de modélisation spécifique.

abstract property model_browser_model: type[TreeModel]

Un modèle à utiliser dans le navigateur de modèles.

abstract property name: str

Nom du langage de modélisation lisible par l’homme.

abstract property toolbox_definition: ToolboxDefinition

Structurer la boîte à outils.

Par convention, le paquet contenant le langage de modélisation doit avoir un attribut __modeling_language__ dont la valeur est identique à celle du nom du langage de modélisation dans le point d’entrée.

En guise d’illustration :

Le fichier mytool/mylang/__init__.py contient une entrée :

__modeling_language__ = "MyLang"

pyproject.toml contient un point d’entrée :

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

Connecteurs

Les connecteurs sont utilisés pour relier un élément à un autre.

Les connecteurs doivent respecter le protocole ConnectorProtocol. Normalement, vous héritez de BaseConnector.

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

Adaptateur de connexion pour les éléments du diagramme Gaphor.

L’élément ligne line se connecte avec une poignée à un élément connectable element.

Paramètres:
  • line – élément de connexion

  • element – élément connectable

Par convention, les adaptateurs sont enregistrés par (élément, ligne) – dans cet ordre.

allow(handle: Handle, port: Port) bool[source]

Détermine si les éléments peuvent être connectés.

La connexion est-elle toujours autorisée, par exemple lorsque la souris se déplace ?

Retourne True si la connexion est autorisée.

connect(handle: Handle, port: Port) bool[source]

Se connecter à un élément.

Établir une connexion entre l’élément et la ligne. Prend également en charge les déconnexions, si nécessaire (par exemple, pour les relations 1:1).

Notez qu’à ce stade, la ligne peut être connectée à un autre élément ou au même élément. La connexion au niveau du modèle reste valide.

Retourne True si une connexion est établie.

disconnect(handle: Handle) None[source]

Déconnecter les connexions au niveau du modèle.

Rupture de connexion, appelée lorsqu’on dépose une poignée sur un point où elle ne peut pas se connecter.

get_connected(handle: Handle) Presentation[Base] | None[source]

Obtenir un élément connecté à une poignée.

Formater et analyser

Les éléments de modèle peuvent être représentés sous forme textuelle. Par exemple, le navigateur de modèles utilise cette fonctionnalité. Il ne s’agit toutefois pas d’une conversion complète de l’élément de modèle.

Dans certains cas, il peut être utile d’analyser le texte afin de le réintégrer dans un objet. C’est notamment le cas lorsque l’on modifie les attributs et les opérations d’une classe.

Chaque format() n’a pas besoin d’avoir une fonction parse() équivalente.

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

Il renvoie une représentation lisible de l’élément de modèle. Dans la plupart des cas, il s’agit simplement du nom, mais les propriétés (attributs) et les opérations sont présentées de manière plus détaillée :

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

Analyse text et remplit element. L’élément est alors rempli avec les éléments du texte. Cela peut signifier que de nouveaux éléments du modèle sont créés dans le cadre du processus d’analyse.

Copier et coller

Le copier-coller fonctionne immédiatement pour des éléments simples, comme un élément de diagramme avec un élément de modèle (le subject). Il s’appuie sur les méthodes load() et save() des éléments pour s’assurer que toutes les données pertinentes sont copiées.

Parfois, certains éléments ont besoin de plus d’un élément du modèle pour fonctionner. Une association, par exemple, a deux extrémités.

Dans ces cas spécifiques, vous devez implémenter vos propres fonctions de copier-coller. Pour ce faire, vous devez créer deux fonctions : l’une pour copier et l’autre pour coller.

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

Créer une copie d’un élément (ou d’une liste d’éléments). Le type de retour doit être distinct afin que la fonction paste()puisse être correctement distribuée. Une fonction de copie ne copie normalement que l’élément et les éléments connexes obligatoires. Par exemple, une association a besoin de deux extrémités.

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

Collez les données précédemment recopiées. Dupliquez les éléments copiés en fonction du type de données créé dans la fonction copy(). Renvoie l’élément nouvellement créé.

Gaphor fournit quelques fonctions de confort :

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

Copier items. La fonction lookup est utilisée pour rechercher les éléments possédés (affichés comme des noeuds enfants dans le navigateur de modèle).

Collez une copie de l’élément Présentation dans le diagramme, mais essayez de lier l’élément de modèle sous-jacent. Une copie succinte.

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

Coller une copie de la présentation et de l’élément de modèle. Une copie approfondie.

Regroupement

Le regroupement s’effectue en faisant glisser un élément sur un autre, dans un diagramme ou dans l’arborescence.

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

Retourne le propriétaire de l’élément. Il peut s’agir de Root, indiquant que l’élément doit être placé à la racine de la hiérarchie de propriété. Si None est retourné, il n’y a pas de propriétaire.

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

Retourne tous les éléments appartenant à `element

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

Un élément peut être regroupé dans un élément parent. Ce regroupement peut être basé sur la propriété, mais d’autres types de regroupement sont également possibles.

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

Supprime le regroupement d’un élément. La fonction doit vérifier si le nœud parent fourni est le bon.

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

Cette fonction tente de déterminer si le regroupement est possible sans pour autant effectuer l’opération en question. Elle n’est pas précise à 100 %.

Dépose

Le dépôt s’effectue en faisant glisser un élément de l’arborescence et en le déposant sur un diagramme. Il s’agit d’un moyen facile d’ajouter des éléments de modèle déjà existants à un diagramme.

Il est également possible de déposer un élément de présentation au-dessus d’un autre élément.

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

La fonction « drop » crée une nouvelle présentation pour un élément du diagramme qui n’en est pas encore une. Pour les relations, un dépôt ne fonctionne que si les deux éléments connectés sont présents dans le même diagramme.

Alors que le regroupement permet d’articuler les éléments du modèle, le dépôt permet de créer et de placer les éléments de présentation au bon endroit dans un diagramme.

Nettoyage automatisé des modèles

Gaphor souhaite maintenir le modèle synchronisé avec les diagrammes.

Une petite fonction d’envoi permet de déterminer si un élément du modèle peut être retiré.

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

Détermine si un élément du modèle peut être retiré en toute sécurité.

Pages de l’éditeur de propriétés

La page de l’éditeur est composée d’extraits. Par exemple, presque chaque élément a un nom, et un élément d’interface utilisateur vous permet donc d’éditer un nom.

Chaque page de propriété (snippet) doit hériter de PropertyPageBase.

class gaphor.diagram.propertypages.PropertyPageBase[source]

Une page de propriété qui peut s’afficher dans un bloc-notes.

close() None[source]

Appelée lorsqu’une page de propriétés est supprimée.

Cela nous permet de faire le ménage.

abstractmethod construct() Gtk.Widget | None[source]

Créer la page (Gtk.Widget) qui appartient à la page de propriété.

Renvoie le widget de premier niveau de la page (Gtk.Widget).

Fenêtres contextuelles instantanées de l’éditeur (de diagrammes)

Lorsque vous double-cliquez sur un élément d’un diagramme, une fenêtre contextuelle peut s’afficher, ce qui vous permet de modifier facilement le nom.

Par défaut, cela fonctionne pour tout élément nommé. Vous pouvez enregistrer votre propre fonction d’édition en ligne si nécessaire.

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

Affiche une petite fenêtre d’édition dans le diagramme. Permet de faciliter l’édition sans avoir recours à l’éditeur d’éléments.

Dans le cas d’un clic de la souris, la position de la souris (par rapport à l’élément) est également fournie.