O programu Gaphor

Napomena

Dokumentacija je aktualna za Gaphor 2.25.1

Gaphor is a UML and SysML modeling application written in Python. It is designed to be easy to use, while still being powerful. Gaphor implements a fully-compliant UML 2 data model, so it is much more than a picture drawing tool.

You can use Gaphor to quickly visualize different aspects of a system as well as create complete, highly complex models.

_images/gaphor-core.png

Gaphor is 100% Open source, available under a friendly Apache 2 license. The code and issue tracker can be found on GitHub.

What are you waiting for? Let’s get started!

For download instructions, and the blog, please visit the Gaphor Website.

Did you know Gaphor has excellent integration with Sphinx and Jupyter notebooks?

Počni raditi s Gaphorom

Gaphor is more than a diagram editor: it’s a modeling environment. Where simple diagram editors such as Microsoft Visio and draw.io allow you to create pictures, Gaphor actually keeps track of the elements you add to the model. In Gaphor you can create diagrams to track and visualize different aspects of the system you’re developing.

Enough talk, let’s get started.

You can find installers for Gaphor on the Gaphor Website. Gaphor can be installed on Linux (Flatpak), Windows, and macOS.

Nakon pokretanja Gaphora, prikazuje se uvodni ekran. Prikazuje prethodno otvorene modele i predloške modela.

uvodni ekran

Počni izborom predloška.

  • Generički: prazni model

  • UML: Predložak za Unified Modeling Language za modeliranje softverskog sustava

  • SysML: Predložak za Systems Modeling Language za modeliranje raznih sustavskih modela i sustava-od-sustava

  • RAAML: Predložak za Risk Analysis and Assessment Modeling language za sigurnost i analizu pouzdanosti

  • C4 Model: A template for Context, Containers, Components, and Code which is for lean modeling of software architecture

Nakon što se sučelje modela učita, vidjet ćeš sučelje za modeliranje.

novi model

Raspored sučelja Gaphora je podijeljen u četiri odjeljka, naime:

  1. Preglednik modela

  2. Kutija alata za elemente dijagrama

  3. Dijagrami

  4. Uređivač svojstava

Svaki odjeljak ima vlastitu specifičnu funkciju.

Preglednik modela

The Model Browser section of the interface displays a hierarchical view of your model. Every model element you create will be inserted into the Model Browser. This view acts as a tree where you can expand and collapse different elements of your model. This provides an easy way to view the elements of your model from an elided perspective. That is, you can collapse those model elements that are irrelevant to the task at hand.

In the figure above, you will see that there are two elements in the Model Browser. The root element, New Model is a package. Notice the small arrow beside New Model that is pointing downward. This indicates that the element is expanded. You will also notice the two sub-elements are slightly indented in relation to New Model. The main element is a diagram.

In the Model Browser view, you can also right-click the model elements to get a context menu. This context menu allows you to find out in which diagram model elements are shown, add new diagrams and packages, and delete an element.

Double-clicking on a diagram element will show it in the Diagram section. Elements such as classes and packages can be dragged from the tree view on the diagrams.

Kutija alata

The toolbox is used to add new items to a diagram. Select the element you want to add by clicking on it. When you click on the diagram, the selected element is created. The arrow is selected again, so the element can be manipulated.

Tools can be selected by simply left-clicking on them. By default, the pointer tool is selected after every item placement. This can be changed by disabling the „Reset tool” option in the Preferences window. Tools can also be selected by keyboard shortcuts. The keyboard shortcut can be displayed as a tooltip by hovering over the tool button in the toolbox. Finally, it is also possible to drag elements on the Diagram from the toolbox.

Dijagrami

The diagram section contains diagrams of the model and takes up the most space in the UI because it is where most of the modeling is done. Diagrams consist of items placed on the diagram. There are two main types of items:

  1. Elementi

  2. Odnosi

Moguće je otvoriti više dijagrama odjednom: oni se prikazuju u karticama. Kartice se mogu zatvoriti pritiskom Ctrl+w ili pritiskom lijevog gumba miša na oznaku „x” u kartici dijagrama.

Elementi

Elements are the shapes that you add to a diagram, and together with Relations, allow you to build up a model.

To resize an element on the diagram, left-click on the element to select it and then drag the resize handles that appear at each corner.

To move an element on the diagram, drag the element where you want to place it by pressing and holding the left mouse button, and moving the mouse before releasing the button.

Veze

Relations are line-like items that form relationships between elements in the diagram. Each end of a relation is in one of two states:

  1. Spajanje s elementom – crvena ručka

  2. Odspajanje od elementa – zelena ručka

Ako su oba kraja veze nepovezana, veza se može pomaknuti pritiskom lijeve tipke miša i povlačenjem.

A new segment in a relation can be added by left-clicking on the relation to select it and then by hovering your mouse over it. A green handle will appear in the middle of the line segments that exist. Drag the handle to add another segment. For example, when you first create a new relation, it will have only one segment. If you drag the segment handle, then it will now have two segments with the knee of the two segments where the handle was.

Copy and Paste

As stated before, Gaphor is a modeling environment. This means that every item in a diagram is backed by a model element found in the model browser. This means that you can show the same model element in different diagrams.

  • Ctrl+v is used to paste only the presentation element.

  • Ctrl+Shift+v is used to paste a new presentation with a new model element.

Važno

Ctrl+v does a „shallow” paste. Ctrl+Shift+v does a „deep” paste.

Poništi i ponovi

Undo a change press Ctrl+z or left-click on the back arrow at the top of the Property Editor. To re-do a change, hit Ctrl+Shift+z or press the forward arrow at the top of the Property Editor.

Uređivač svojstava

The Property Editor is present on the right side of the diagrams. When no item is selected in the diagram, it shows you some tips and tricks. When an item is selected on the diagram, it contains the item details like name, attributes and stereotypes. It can be opened with F9 and the sidebar-show-right-symbolic icon in the header bar.

The properties that are shown depend on the item that is selected.

Postavke modela

The Property Editor also contains model preferences: Click the document-properties-symbolic button.

Reset Tool Automatically

By default, the pointer tool is selected after an element is placed from the toolbox. If this option is turned off, the same type of element will be placed by clicking in the diagram until another element is selected in the toolbox.

Remove Unused Elements

By default, elements that are not part of any diagram in the model will be removed. If this option is turned off, elements remain in the model and may be found in the model browser.

Diagram Language

The diagram language modifier is only applicable to the loaded model and how it is shown in the diagram. The diagram language setting is saved as part of the model and defaults to English.

The UI language of Gaphor is controlled by the operating system.

Napomena

Gaphor considers the LANG environment variable on Linux, Windows and macOS.

On Windows and macOS it can be set independently of the operating system’s language settings to a different language.

Style Sheet

The style sheet allows to change the visual appearance of diagrams and model elements.

Tvoj prvi model

Napomena

In this tutorial we refer to the different parts of the gaphor interface: Model Browser, Toolbox, Property Editor.

Although the names should speak for themselves, you can check out the Getting Started page for more information.

Once Gaphor is started, and you can start a new model with the Generic template. The initial diagram is already open in the Diagram section.

Select an element you want to place, in this case a Class (newclass) by clicking on the icon in the Toolbox and click on the diagram. This will place a new Class item instance on the diagram and add a new Class to the model – it shows up in the Model Browser. The selected tool will reset itself to the Pointer tool after the element is placed on the diagram.

The Property Editor on the right side will show you details about the newly added class, such as its name (New Class), attributes and operations (methods). The Note field can contain any text you wish to associate with the element, (this will not show on a diagram).

slika

Elementi se mogu jednostavno dodati dijagramu.

Gaphor ne pretpostavlja koji se elementi trebaju postaviti na dijagram. Dijagram je dijagram. UML definira sve različite vrste dijagrama, kao što su dijagrami klasa, dijagrami komponenti, dijagrami radnji, dijagrami sekvenci. Ali Gaphor ne postavlja nikakva ograničenja.

Dodavanje veza

Add another Class. Change the names to Shape and Circle. Let’s define that Circle is a sub-type of Shape. You can do this by selecting one and changing the name in the Property Editor, or by double-clicking the element.

Odberi generaliziranje (new generalization).

Move the mouse cursor over Shape. Click, hold and drag the line end over Circle. Release the mouse button, and you should have your relationship between Shape and Circle. You can see both ends of the relation are red, indicating they are connected to their class.

slika

Optionally you can run the auto-layout (openmenu → Tools → Auto Layout) to align the elements on the diagram.

Stvaranje novih dijagrama

Za izradu novog dijagrama koristi „Preglednik modela”. Odaberi element koji bi trebao sadržavati novi dijagram. Za sada, odaberi Novi model. Pritisni izbornik „Novi dijagram” (novi dijagram) u traci zaglavlja.

slika

Select New Generic Diagram and a new diagram is created.

Now drag the elements from the Model Browser onto the new diagram. First the classes Shape and Circle. Add the generalization last. Drop it somewhere between the two classes. The relation will be created to the diagram.

Now change the name of class Circle to Ellipse. Check the other diagram. The name has been changed there as well.

Važno

Elements in a diagram are only a representation of the elements in the underlying model. The model is what you see in the Model Browser.

Elements in the model are automatically removed when there are no more representations in any of the diagrams.

Tutorial: Coffee Machine

Napomena

In this tutorial we refer to the different parts of the gaphor interface: Model Browser, Toolbox, Property Editor.

Although the names should speak for themselves, you can check out the Getting Started page for more information about those sections.

Introduction

In the bustling town of Antville, a colony of ants had formed a Systems Engineering consulting company called AntSource. They value collaboration, transparency, and community-driven engineering, and seeks to empower their employees and customers through open communication and participation in the systems engineering process.

The engineers at AntSource all have nicknames that reflect the key principles and concepts of their craft: Qual-ant, Reli-ant, Safe-ant, Usa-ant, and Sust-ant. They were experts in designing and optimizing complex systems, and they took pride in their work.

One day, a new client approached AntSource with an unusual request. Cappuccino, a cat who owned a popular coffee shop called Milk & Whiskers Café, needed a custom espresso machine designed specifically for felines. Cats just love their coffee strong, with a creamy and smooth body and topped with the perfect foamy layer of crema. The ants were intrigued by the challenge and immediately set to work.

Qual-ant was responsible for ensuring that the machine met all quality standards and specifications, while Reli-ant was tasked with making sure that the machine was dependable and would work correctly every time it was used. Safe-ant designed the machine with safety in mind, ensuring that it wouldn’t cause harm to anyone who used it. Usa-ant designed the machine to be easy and intuitive to use, while Sust-ant ensured that the machine was environmentally friendly and sustainable. In this tutorial we follow the adventures of AntSource to create the perfect kittie espresso machine.

Two shots of espresso being pulled from an espressomachine

The first thing the ants did was to open Gaphor to the Greeter window and start a new model with the SysML template. You can now decide to either:

  • recreate their work as part of this tutorial. For this, open the SysML Example model shown at the bottom of the Greeter window

  • inspect the result of their work by opening the coffee-machine model located in the examples folder.

Abstraction Levels

Abstraction is a way of simplifying complex systems by focusing on only the most important details, while ignoring the rest. It’s a process of reducing complexity by removing unnecessary details and highlighting the key aspects of a system in order to focus on the problem to be solved. It is the key to rigorous analysis of a system.

To understand abstraction, think about a painting. When you look at a painting, you see a representation of something - perhaps a person, a landscape, or an object. The artist has simplified the real world into a set of lines, shapes, and colors that represent the most important details of the subject. In the same way, systems engineers, like our friends the ants, use abstraction to represent complex systems by breaking them down into their essential components and highlighting the most important aspects.

Abstraction levels refer to the different levels of detail at which a system can be represented. These levels are used to break down complex systems into smaller, more manageable parts that can be analyzed and optimized. Said another way, abstraction levels group portions of a design where similar types of questions are answered.

There are typically three levels of abstraction in systems engineering and these are the three levels used in the SysML template:

  • Concept Level: Sometimes also called Conceptual Level. Defines the problem being solved. This is the highest level of abstraction, where the system is described in terms of its overall purpose, goals, and functions. At this level, the focus is on understanding the system’s requirements and how it will interact with other systems.

  • Logical Level: Defines a technology-agnostic solution. This is the middle level of abstraction, where the system is described in terms of its structure and behavior. At this level, the focus is on how the system components are organized and how they interact with each other.

  • Technology Level: Sometimes also called Physical level. Defines the detailed technical solution. This is the lowest level of abstraction, where the system is described in terms of its components and their properties. At this level, the focus is on the details of the system’s implementation.

Each level of abstraction provides a different perspective on the system, and each level is important for different aspects of system design and analysis. For example, the conceptual level is important for understanding the overall goals and requirements of the system, while the physical level is important for understanding how the system will be built and how it will interact with the environment.

There is a fourth abstraction level called the Implementation Level that isn’t modeled, which is the concrete built system.

In the upper left hand corner of Gaphor, the Model Browser shows the three top level packages, dividing up the model in to these three abstraction levels.

Top level packages of the SysMLtemplate

Pillars

There are four pillars of SysML which help classify the types of diagrams based on what they represent:

  • Behavior: The functionality of a system

  • Structure: How a system is formed using parts and connections

  • Requirements: Written statements that constrain the system

  • Parametric: Enforces mathematical rules across values in the system

If you want to learn more about these four pillars, there is a 30-minute video by Rick Steiner called The Four Pillars of SysML.

Since Parametric Diagrams are one of the least used diagram types in SysML, we are going to only focus on the first three. The power of SysML comes in being able to make relationships between these three pillars. For example, by allocating behavior like an activity to an element of the structure like a block.

If you expand the top-level Abstraction Level packages in the Model Browser, each one contains three more packages, one for each pillar. It is in these packages that we will start to build up the design for the espresso machine.

Three pillar packages nested under each abstraction levelpackage

Table of Contents

Coffee Machine: Concept Level

Introduction

The concept level defines the problem we are trying to solve. For the espresso machine, we are going to use diagrams at this abstraction level to answer questions like:

  • Who will use the machine and what are their goals while using it?

  • What sequence of events will a person take while operating the machine?

  • What are the key features and capabilities required for the machine to perform its intended function?

  • What are the design constraints and requirements that must be considered when designing the machine?

  • What are the key performance metrics that the machine must meet in order to be considered successful?

  • How will the machine fit into the larger context of the café, and how will it interact with other systems and components within the café?

  • What are the needs of others like those marketing, selling, manufacturing, or buying the machine?

At this level, the focus is on understanding the big picture of the espresso machine and its role within the café system. The answers to these questions will help guide the design and development of the machine at the logical and technology levels of abstraction.

Use Case Diagram

First the ants work on the behavior of the system. Expand the Behavior package in the Model Browser and double-click on the diagram named Use Cases.

A use case diagram is a type of visual representation used in systems engineering to describe the functional requirements of a system, such as an espresso machine. In the context of the espresso machine, a use case diagram would be used to identify and define the different ways in which the machine will be used by its users, such as the café staff and customers.

The diagram would typically include different actors or users, such as the barista, the customer, and possibly a manager or maintenance technician. It would also include different „use cases” or scenarios, which describe the different actions that the users can take with the machine, such as placing an order, making an espresso, or cleaning the machine.

The use case diagram helps to ensure that all the necessary functional requirements of the espresso machine are identified and accounted for, and that the system is designed to meet the needs of its users. It can also be used as a communication tool between the different stakeholders involved in the development of the machine, such as the ants and Cappuccino the cat.

The ants need your help updating the diagrams, so let’s get started:

  1. Double-click on the actor to pop up the rename dialog, and replace User with Barista.

  2. Update the name of the oval Use Case from Use Case #1 to Brew espresso.

  3. Update the name of the rectangle Block from Feature to Espresso Machine

A barista interacts with the espresso machine. The barista is provided a simple interface with a few push buttons.

In this particular use case diagram, we have one actor named Barista and one use case called Brew espresso, which is allocated to a block called Espresso Machine. The actor, in this case, is a cat barista who interacts with the system (an espresso machine) to accomplish a particular task, which is brewing espresso.

Use case diagram showing an actor named Barista and a use case called Brew espresso

The use case Brew espresso represents a specific functionality or action that the system (the Espresso Machine block) can perform. It describes the steps or interactions necessary to complete the task of brewing espresso, such as selecting the appropriate settings, starting the brewing process, and stopping the process once it’s complete.

The use case diagram shows the relationship between the actor and the use case. It is represented by an oval shape with the use case name inside and an association with the actor. The association represents the interaction from the actor to the use case.

Domain Diagram

A domain diagram is a graphical representation of the concepts, terms, and relationships within a specific domain. In the case of a coffee shop, a domain diagram could represent the key elements and relationships within the coffee shop domain.

The following is a domain diagram that builds upon the context diagram with additional blocks:

  • Barista

  • Coffee Machine

  • Roasted Coffee

  • Coffee Grinder

  • Water Supply

  • Customer

Each block in the Block Definition Diagram (bdd) represents a key concept within the coffee shop domain, and the containment relationship is used between the domain and the blocks to show that they are part of the domain.

Block Definition Diagram showing hierarchy of blocks in the Coffee Shop domain

The Barista block is responsible for preparing and serving the coffee to the customers. The Roasted Coffee block contains the types of coffee available for the barista to use. The Coffee Grinder block grinds the roasted coffee beans to the desired consistency before brewing. The Water Supply block contains the water source for the coffee machine, and finally the Customer block represents the person who orders and receives the coffee.

The ants need more of your help to rename the Feature Domain diagram and update it so that it matches the one above. Make sure that „Profile: SysML” is selected in the top-left corner of the Gaphor user interface. The names of the blocks can be changed directly in the diagram, but the name of the bdd can only be changed in the Model Browser. In the Structure package, right-click on the Blocks with the B symbol and rename them from the context menu. Also remember that you can use auto-layout to align and distribute all elements.

The domain diagram provides a high-level view of the coffee shop domain and the key concepts and relationships involved in it. It can be a useful tool for understanding the relationships between different elements of the domain and for communicating these relationships to others.

Context Diagram

The context diagram is a high-level view of the system, and it shows its interaction with external entities. In the case of a coffee machine, a context diagram provides a clear and concise representation of the system and its interactions with the external environment.

The context diagram for a coffee machine shows the coffee machine as the system at the center, with all its external entities surrounding it. The external entities include the barista, the power source, the coffee grinder, and the water source.

Block definition diagram showing context of the coffee shop with external entities

The ants need more of your help to rename the Feature Context diagram and update it so that it matches the one above. To create the specific arrows shown, use an Association entity, then toggle Enable Item Flow to on for that association and fill in the Item Property field.

Overall, the context diagram for a coffee machine provides a high-level view of the system and its interactions with external entities. It is a useful tool for understanding the system and its role in the broader environment.

Concept Requirements

Concept requirements are typically collected by analyzing the needs of the stakeholders involved in the development of the coffee machine. This involves identifying and gathering input from various stakeholders, such as the barista, the other engineers working on the product, manufacturing, and service.

To collect concept requirements, stakeholders may be asked questions about what they want the coffee machine to do, what features it should have, and what problems it should solve. They may also be asked to provide feedback on existing coffee machines to identify areas where improvements could be made.

Once the needs of the stakeholders have been gathered, they can be analyzed to identify common themes and requirements. This information can then be used to develop the concept requirements for the coffee machine, which serve as a starting point for the design process.

The following are some concept requirements for a coffee machine that addresses a water tank, heat-up time, and HMI button:

  • Water Tank: The coffee machine shall have a water tank of sufficient size to make multiple cups of coffee before needing a refill. The water tank should be easy to access and fill.

  • Heat-up Time: The coffee machine shall have a heat-up time of no more than 10 minutes from the time the user turns on the machine until it’s ready to brew coffee.

  • 1 Cup Button: The coffee machine shall have an HMI with a 1 cup brew button to make it easy for the user to select the amount of coffee they want to brew.

Concept requirements for water tank, heat-up time, and HMI button

Help the ants update the Concept Requirements diagram with these requirements.

Throughout the design process, the concept requirements will be refined and expanded upon as more information becomes available and the needs of the stakeholders become clearer. This iterative process ensures that the final design of the coffee machine meets the needs of all stakeholders and delivers a high-quality product.

Coffee Machine: Logical Level

Introduction

At the logical Level, we’ll define a technology-agnostic solution. This is the middle level of abstraction, where the system is described in terms of its structure and behavior. At this level, the focus is on how the system components are organized and how they interact with each other.

Functional Boundary Behavior

A Functional Boundary Behavior diagram is a type of SysML Activity diagram used to show the interactions between different logical blocks. The swim lanes divide the diagram into different areas, each representing a different functional block or component.

In this case, the diagram includes swimlanes for the HMI, Controller, Water Pump, Water Heater, Group Head, and Portafilter. The HMI receives the button press from the barista and then sends a command to the Controller. The Controller then commands the Water Heater to start, and once the water has reached the correct temperature, the Controller commands the Pump to start. The water would then be pumped through the Group Head and into the Portafilter, brewing the coffee. The diagram shows the flow of information and actions between the different logical blocks, and help to ensure that the behavior that each block provides is properly connected and integrated into the system.

_images/9fdf0b3c-7170-11ec-999a-f47b099bf663.svg

From the Logical package, expand the Behavior package in the Model Browser and double-click on the diagram named Functional Boundary Behavior. Additional swimlanes can be added by clicking on the swimlanes and add additional partitions in the Property Editor. The name of the partition before the colon can also be changed in the Property Editor. The names of the Blocks can be changed in the Structure package, as was explained in the Domain Diagram section.

Additional Object Flows, pins (pay attention to inputs vs outputs), and actions can be created using the Toolbox. The Parameter Nodes which are attached to the Activity on the very left and right of the diagram are created and renamed created by clicking on the Activity and modifying them in the Property Editor.

Logical State Machine

The logical state machine for the coffee machine is a diagram that shows the different states and transitions that the machine goes through to make coffee. In this case, there are two main states: On and Off.

When the coffee machine is turned on, it enters the On state. Inside the On state, there are some substates, starting with the heat water state. The machine will transition from the heat water state to the ready state when the temperature reaches the set point.

Once the machine is in the ready state, the user can select one or two cup mode. Depending on the mode selected, the machine will transition to either the one cup mode or two cup mode.

State machine diagram showing logical states including on and off

Open the Logical States diagram and add a region to the On state via the Property Editor. Next use the Toolbox to add the additional substates and transition. Guards for the transitions, shown surrounded by brackets, are added by selecting the transition and adding the guard in the Property Editor.

The logical state machine diagram for the coffee machine shows these states, and the different conditions that trigger the transitions. This helps the ants designing the machine to understand how the coffee machine works and ensure that it functions properly.

Logical Structure

The logical structure shows which logical blocks the espresso machine is made up of. Since we are at the logical level, these blocks should be agnostic to technical choices.

The following logical blocks are part of the espresso machine:

  • Water tank

  • Water pump

  • Water heater

  • Portafilter

  • Controller

  • Group head

  • HMI

Each block represents a key portion of the espresso machine, and the containment relationship is used between the espresso machine and its logical parts.

Block definition diagram showing the coffee machine and its logical parts
  • Water tank: The water tank is a container that stores the water used in the espresso machine. It typically has a specific capacity and is designed for easy filling and cleaning. The water tank supplies water to the water pump when needed.

  • Water pump: The water pump is responsible for drawing water from the water tank and creating the necessary pressure to force the water through the coffee grounds in the portafilter. It plays a crucial role in the espresso extraction process by ensuring a consistent flow of water.

  • Water heater: The water heater, also known as the boiler or heating element, is responsible for heating the water to the optimal temperature for brewing espresso. It maintains the water at the desired temperature to ensure proper extraction and flavor.

  • Portafilter: The portafilter is a detachable handle-like device that holds the coffee grounds. It is attached to the espresso machine and acts as a filter holder. The water from the pump is forced through the coffee grounds in the portafilter to extract the flavors and create the espresso.

  • Controller: The controller, often a microcontroller or a dedicated circuit board, is the brain of the espresso machine. It manages and coordinates the operation of various components, such as the water pump, water heater, and HMI, to ensure the correct brewing process. It monitors and controls temperature, pressure, and other parameters to maintain consistency and deliver the desired results.

  • Group head: The group head is a part of the espresso machine where the portafilter attaches. It provides a secure connection between the portafilter and the machine, allowing the brewed espresso to flow out of the portafilter and into the cup. The group head also helps to maintain proper temperature and pressure during the brewing process.

  • HMI (Human-Machine Interface): The HMI is the user interface of the espresso machine. It provides a means for the user to interact with the machine, usually through buttons, switches, or a touchscreen. The HMI allows the user to select different brewing options, adjust settings, and monitor the status of the machine. It provides feedback and displays information related to the brewing process, such as brewing time, temperature, and cup size selection.

We didn’t make any technical choices at this time, for example we didn’t specify which type of controller, the pump capacity, or the model of the group head. These details will be defined once we get to the Technology level.

The ants need more of your help to update the Logical Structure diagram so that it matches the one above.

Logical Boundary

The Logical Boundary is a type of Internal Block Diagram that represents the internal structure of a system, illustrating the relationships between its internal components or blocks. It helps to visualize how these blocks interact and exchange information within the system. The term boundary used here means a clear box view inside the espresso machine at the logical boundary. It uses part properties of the blocks that were in the Logical Structure diagram above.

Internal block diagram showing the clear box view of the espresso machine

The interactions between the part properties inside the espresso machine are shown as ItemFlows on the Connectors.

  • Water: Represents the flow of water from the water tank to the water pump.

  • On/Off: Represents the command or signal to turn the espresso machine on or off.

  • Volume Adjustment: Represents the user-selected volume adjustment for the coffee output.

  • Pressurized Water: Represents the water flow under pressure for extracting coffee.

  • Heat Command: Represents the command or signal to activate the water heater and initiate the heating process.

  • Temperature: Represents the feedback signal indicating the current temperature of the water.

  • Hot Pressurized Water: Represents the pressurized hot water for brewing coffee.

  • Coffee Water Mixture: Represents the mixture of hot water and coffee grounds during the brewing process.

Pozor

Notice that we aren’t actually showing anything entering or leaving the boundary of the espresso machine, like the input from the barista or the resulting coffee. Gaphor doesn’t current support adding ports to the boundary of an internal block diagram, but hopefully we’ll be able to add support soon!

These item flows capture the essential interactions and exchanges within the espresso machine. They represent the flow of water, control signals, temperature feedback, and the resulting coffee water mixture. The item flows illustrate the sequence and connections between the various components, allowing for a better understanding of how the machine functions as a whole.

Once again, help the ants by updating the Logical Boundary diagram so that it matches the one above.

Logical Requirements

Logical requirements refer to the high-level specifications and functionalities that describe what a system or product should accomplish without specifying how it will be implemented. These requirements focus on the desired outcomes and behavior of the system rather than the specific technical details.

We have also already defined the behavior and the structure of the espresso machine at the logical level, so the main task now is to translate that information in to words as requirement statements.

Savjet

If you need help writing good requirements, the INCOSE Guide to Needs and Requirements and the Easy Approach to Requirements Syntax are recommended resources.

We use the Derive Requirement relation from the Logical Requirement to the Concept Requirements that we previously created. The direction of this relationship is in the derived from direction, which might be opposite to what you are used to where the higher level requirement points to the lower level requirement.

Here we derive two requirements:

  • Controller commands heat up

  • 900kPa of water pressure

Logical requirements for the controller command and the water pressure derived from concept requirements

Update the Logical Requirements diagram with these requirements. If you want, you can also develop additional requirements for all the logical behavior and structure that we specified in the other diagrams.

Coffee Machine: Summary

The Technology Level design uses a very similar approach as the Logical Level. Work on the behavior, structure, and then the requirements. At this level, you will now specify all the design details for how this specific espresso machine will work. We’ll leave this exercise up to you to do, and we would be glad to have contributions of this design back in to this tutorial if you are interested in getting involved in Gaphor.

As they worked, the ants encountered numerous challenges. They had to ensure that the machine was safe, efficient, and easy to use, all while meeting the unique needs of their feline client. But with their deep understanding of systems engineering and their commitment to key principles and concepts, they were able to overcome these challenges and design an exceptional espresso machine.

In the end, Cappuccino was thrilled with the machine, which worked flawlessly and was a big hit with his customers. He was so impressed with the ants’ work that he offered them a long-term contract to design all of his café’s systems. The ants were proud of their success, knowing that it was all thanks to their expertise and deep understanding of systems engineering principles. They had proven that, with the right tools and approach, anything is possible.

Change Log

Our change log is with the project’s source code on GitHub.

Napomena

The latest version may not have been released yet.

Style Sheets

Since Gaphor 2.0, diagrams can have a different look by means of style sheets. Style sheets use the Cascading Style Sheets (CSS) syntax. CSS is used to describe the presentation of a document written in a markup language, and is most commonly used with HTML for web pages.

On the W3C CSS home page, CSS is described as:

Cascading Style Sheets (CSS) is a simple mechanism for adding style (e.g., fonts, colors, spacing) to Web documents.

Its application goes well beyond web documents, though. Gaphor uses CSS to provide style elements to items in diagrams. CSS allows us, users of Gaphor, to change the visual appearance of our diagrams. Color and line styles can be changed to make it easier to read the diagrams.

Since we’re dealing with a diagram, and not a HTML document, some CSS features have been left out.

The style is part of the model, so everyone working on a model will have the same style. To edit the style press the tools page button at the top right corner in gaphor:

Button to access style code

Here is a simple example of how to change the background color of a class:

class {
  background-color: beige;
}
background beige

Or change the color of a component, only when it’s nested in a node:

node component {
  background-color: skyblue;
}
nested component

The diagram itself is also expressed as a CSS node. It’s pretty easy to define a „dark” style:

diagram {
  background-color: #343131;
}

* {
  color: white;
  text-color: white;
}
dark-style

Here you already see the first custom attribute: text-color. This property allows you to control the color of the text drawn in an item. color is used for the lines (strokes) that make the layout of a diagram item.

Supported selectors

Since we are dealing with diagrams and models, we do not need all the features of CSS. Below you’ll find a summary of all CSS features supported by Gaphor.

*

All items on the diagram, including the diagram itself.

node component

Any component item which is a descendant of a node.

node > component

A component item which is a child of a node.

generalization[subject]

A generalization item with a subject present.

class[name=Foo]

A class with name „Foo”.

diagram[name^=draft]

A diagram with a name starting with „draft”.

diagram[name$=draft]

A diagram with a name ends with „draft”.

diagram[name*=draft]

A diagram with a name containing the text „draft”.

diagram[name~=draft item]

A diagram with a name of „draft” or „item”.

diagram[name|=draft]

A diagram with a name is „draft” or starts with „draft-„.

:focus

The focused item. Other pseudo classes are:

  • :active selected items

  • :hover for the item under the mouse

  • :drop if an item is dragged and can be dropped on this item

  • :disabled if an element is grayed out during handle movement

:empty

A node containing no child nodes in the diagram.

:root

Refers to the diagram itself.

This is only applicable for the diagram

:first-child

A node is the first element among a group of sibling.

:has()

The item contains any of the provided selectors.

E.g. node:has(component): a node containing a component item.

:is()

Match any of the provided selectors.

E.g. :is(node, subsystem) > component: a node or subsystem.

:not()

Negate the selector.

E.g. :not([subject]): Any item that has no „subject”.

::after

Provide extra content after a text. Only the content property is supported.

  • Službena specifikacija za CSS3 selektore atributa.

  • Gaphor omogućuje selektor atributa |= radi cjelovitosti. U ovom kontekstu vjerojatno ipak nije od velike koristi.

  • Gaphor CSS ne podržava ID-ove za elemente dijagrama, stoga se CSS sintaksa za ID-ove (#some-id) ne koristi. Sintaksa za klase (.some-class) se trenutačno također ne podržava.

Svojstva stilova

Gaphor podržava podskup CSS svojstava i neka Gaphor specifična svojstva. Tumač stilske tablice je relativno jednostavan. Sve širine, visine i veličine mjere se u pikselima. Ne možeš koristiti složene deklaracije stilova poput HTML/CSS svojstva font, koje može sadržavati obitelj fontova, veličinu, debljinu.

Some properties are inherited from the parent style. The parent often is a diagram. When you set a color`` or a font-familyondiagram`, it will propagate down to the items contained in the diagram.

Boje

background-color

Primjeri:

background-color: azure;

background-color: rgb(255, 255, 255);

background-color: hsl(130, 95%, 10%);

color

Color used for lines. (inherited)

text-color

Color for text. (inherited)

Zastarijelo od verzije 2.23.0: Use color if possible.

opacity

Faktor neprozirnosti boje (0.0 - 1.0). Primijenjuje se na sve boje.

  • Boja može biti bilo koji CSS3 kod boje, kao što je opisano u CSS dokumentaciji. Gaphor podržava sve oznake boja: rgb(), rgba(), hsl(), hsla(), heksadecimalni kod (#ffffff) i imena boja.

Tekst i fontovi

font-family

A single font name (e.g. sans, serif, courier). (inherited)

font-size

An absolute size (e.g. 14) or a size value (e.g. small). (inherited)

font-style

Either normal or italic. (inherited)

font-weight

Either normal or bold. (inherited)

text-align

Either left, center, right. (inherited)

text-decoration

none ili underline.

vertical-align

Okomiti položaj teksta.

top, middle or bottom.

vertical-spacing

Postavi okomiti razmak za ikonske elemente (aktori, početno stanje).

Primjer: vertical-spacing: 4.

white-space

Change the line wrapping behavior for text. (inherited)

  • font-family mora biti samo jedno ime fonta. Navođenje popisa fontova kao u HTML-u nije moguće.

  • font-size može biti broj CSS absolute-size values. Podržane su samo vrijednosti x-small, small, medium, large i x-large.

Crtanje i razmaci

border-radius

Radijus pravokutnika: border-radius: 4.

dash-style

Stil crtica: dash-style: 7 5.

justify-content

Poravnanje sadržaja za okvire.

start, end, center ili stretch.

line-style

normal ili sloppy [factor].

line-width

Set the width for lines: line-width: 2. (inherited)

min-height

Postavi minimalnu visinu elementa: min-height: 50.

min-width

Postavi minimalnu širinu elementa: min-width: 100.

padding

CSS odmak (gore, desno, dolje, lijevo).

Primjer: padding: 3 4.

  • padding (odmak) se određuje cijelim brojevima od 1 do 4. Jedinice (px, pt, em) se ne moraju koristiti. Za sve vrijednosti se koriste pikseli.

  • dash-style je popis brojeva (crtica, razmak, crtica, razmak, …)

  • line-style djeluje samo kada je definiran u dijagramu. Iskrivljenost linija se može odrediti faktorom u rasponu od -2 do 2.

Pseudo elements

Currently, only the ::after pseudo element is supported.

content

Extra content to be shown after a text.

Variables

Since Gaphor 2.16.0 you can use CSS variables in your style sheets.

This allows you to define often used values in a more generic way. Think of things like line dash style and colors.

The var() function has some limitations:

  • Values can’t have a default value.

  • Variables can’t have a variable as their value.

Example:

diagram {
  --bg-color: whitesmoke;
  background-color: var(--bg-color);
}

diagram[diagramType=sd] {
  --bg-color: rgb(200, 200, 255);
}

All diagrams have a white background. Sequence diagrams get a blue-ish background.

Media queries

Gaphor supports dark and light mode since 2.16.0. Dark and light color schemes are exclusively used for on-screen editing. When exporting images, only the default color scheme is applied. Color schemes can be defined with @media queries. The official prefers-color-scheme = dark query is supported, as well as a more convenient dark-mode.

/* The background you see in exported diagrams: */
diagram {
  background-color: transparent;
}

/* Use a slightly grey background in the editor: */
@media light-mode {
  diagram {
    background-color: #e1e1e1;
  }
}

/* And anthracite a slightly grey background in the editor: */
@media dark-mode {
  diagram {
    background-color: #393D47;
  }
}

Diagram styles

Only a few properties can be defined on a diagram, namely background-color and line-style. You define the diagram style separately from the diagram item styles. That way it’s possible to set the background color for diagrams specifically. The line style can be the normal straight lines, or a more playful „sloppy” style. For the sloppy style an optional wobliness factor can be provided to set the level of line wobbliness. 0.5 is default, 0.0 is a straight line. The value should be between -2.0 and 2.0. Values between 0.0 and 0.5 make for a subtle effect.

CSS model elements

Gaphor has many model elements. How can you find out which item should be styled?

Gaphor only styles the elements that are in the model, so you should be explicit on their names. For example: Component inherits from Class in the UML model, but changing a color for Class does not change it for Component.

If you hover over a button the toolbox (bottom-left section), a popup will appear with the item’s name and a shortcut. As a general rule, you can use the component name, glued together as the name in the stylesheet. A Component can be addressed as component, Use Case as usecase. The name matching is case insensitive. CSS names are written in lower case by default.

However, since the CSS element names are derived from names used within Gaphor, there are a few exceptions.

Profil

Grupa

Element

CSS element

*

*

ime elementa

ime elementa bez znakova razmaka

Npr. class, usecase.

UML

Klase

sve veze

association

UML

Komponente

Uređaj/čvor

node

UML

Radnje

Čvor odluke/sjedinjavanja

decisionnode

UML

Radnje

Čvor grananja/spajanja

forknode

UML

Radnje

Traka

partition

UML

Interakcije

Refleksivna poruka

message

UML

Stanja

Početno pseudostanje

pseudostate

UML

Stanja

Povijest pseudostanja

pseudostate

UML

Profili

Metaklasa

class

C4 model

C4 model

Osoba

c4person

C4 model

C4 model

Softverski sustav

c4container[type="Software System"]

C4 model

C4 model

Komponenta

c4container[type="Component"]

C4 model

C4 model

Kontejner

c4container[type="Container"]

C4 model

C4 model

Kontejner: Baza podataka

c4database

SysML

Blokovi

Vrsta vrijednosti

datatype

SysML

Blokovi

Osnovni element

datatype

SysML

Uvjeti

Izvedeni uvjet

derivedreq

RAAML

FTA (analiza kvarova)

jedan od I/ILI/… sklopova

and, or, itd.

Ideje

Here are some ideas that go just beyond changing a color or a font. With the following examples we dig in to Gaphor’s model structure to reveal more information to the users.

To create your own expression you may want to use the Console (open menu → Tools → Console). Drop us a line on Gitter and we would be happy to help you.

The drafts package

All diagrams in the package „Drafts” should be drawn using sloppy lines:

diagram[owner.name=drafts] {
  line-style: sloppy 0.3;
}

diagram[owner.name=drafts] * {
  font-family: Purisa; /* Or use some other font that's installed on your system */
}
draft style

Nepovezani odnosi

All items on a diagram that are not backed by a model element, should be drawn in a dark red color. This can be used to spot not-so-well connected relationships, such as Generalization, Implementation, and Dependency. These items will only be backed by a model element once you connect both line ends. This rule will exclude simple elements, like lines and boxes, which will never have a backing model element.

:not(:is(:root, line, box, ellipse, commentline))[subject=""] {
  color: firebrick;
}
nepovezani odnos

Solid Control Flow lines

In Gaphor, Control Flow lines follow the SysML styling: dashed. If you want, or need to strictly follow the official UML specifications, you can simply make those solid lines.

controlflow {
  dash-style: 0;
}
control flow

Todo note highlight

All comments beginning with the phrase „todo” can be highlighted in a different user-specific colour. This can be used to make yourself aware that you have to do some additional work to finalize the diagram.

comment[body^="TODO"] {
  background-color: skyblue;
}
highlighted todo note

Emphesize abstract classes and operations

It may be that the italic font used is not distinguishable enough to differentiate between concrete and abstract classes or operations. To make this work we check if the isAbstract attribute is set on the element:

:is(name, operation)[isabstract]::after {
  content: " {abstract}"
}
emphasize abstract elements

System Style Sheet

/* Gaphor diagram style sheet */

* {
  --opaque-background-color: white;
  background-color: transparent;
}

:drop {
  color: #1a5fb4;
  line-width: 3;
}

:disabled {
  opacity: 0.5;
}

@media light-mode {
  * {
    --opaque-background-color: #fafafa;
  }
}

@media dark-mode {
  * {
    --opaque-background-color: #242424;
    color: white;
  }

  :drop {
    color: #62a0ea;
  }
}

:root {
  color: black;
  font-family: sans;
  font-size: 14 ;
  line-width: 2;
  padding: 0;
}

:root > pentagon {
  line-width: 1;
  background-color: var(--opaque-background-color);
}

:root > pentagon > diagramtype {
  font-weight: bold;
  padding: 4 0 4 4;
}

:root > pentagon > name {
  padding: 4;
}

/* Relationships */

commentline,
dependency,
interfacerealization,
include,
extend,
packageimport,
lifetime {
  dash-style: 7 5;
}

dependency[on_folded_interface = true],
interfacerealization[on_folded_interface = true] {
  dash-style: 0;
}

/* General */

comment {
  text-align: left;
  vertical-align: top;
  padding: 4 16 4 4;
}

comment body {
  padding: 0;
}

diagram > icon {
  padding: 4;
  border-radius: 4;
}

diagram > type {
  font-weight: bold;
}

metadata {
  justify-content: stretch;
  text-align: left;
}

metadata cell {
  padding: 4;
}

metadata heading {
  font-weight: bold;
  font-style: normal;
  font-size: small;
}

pentagon {
  padding: 4;
  justify-content: start;
}

/* UML */

controlflow {
  dash-style: 9 3;
}

objectnode > icon {
  padding: 4 12;
}

decisionnode > type {
  font-size: small;
}

proxyport > icon,
activityparameternode,
executionspecification {
  background-color: var(--opaque-background-color);
}

partition {
  padding: 4 12 4 12;
  justify-content: stretch;
}

package {
  padding: 24 12 4 12;
}

interaction {
  justify-content: start;
}

activity {
  padding: 4 12;
  border-radius: 20;
  justify-content: start;
}

activityparameternode {
  padding: 4 12;
  min-width: 120;
  text-align: center;
}

action,
valuespecificationaction {
  padding: 4 12;
  border-radius: 15;
}

callbehavioraction {
  padding: 4 24 4 12;
  border-radius: 15;
}

sendsignalaction {
  padding: 4 24 4 12;
}

accepteventaction {
  padding: 4 12 4 24;
}

usecase {
  padding: 4;
}

swimlane {
  min-width: 150;
  padding: 4 12 4 12;
  justify-content: start;
  white-space: normal;
}

association > end {
  font-size: x-small;
  padding: 4;
}

/* SysML */

requirement {
  justify-content: start;
}

requirement text {
  white-space: normal;
}

directedrelationshippropertypath {
  dash-style: 7 5;
}

/* Classifiers */

compartment:first-child {
  padding: 12 4;
}

compartment + compartment {
  padding: 4;
  min-height: 8;
  text-align: left;
  justify-content: start;
  white-space: nowrap;
}

artifact compartment:first-child,
component compartment:first-child {
  padding: 12 24 12 4;
}

state compartment:first-child {
  padding: 4;
}

:has(compartment + compartment),
:has(regions),
:not([children=""]):has(compartment),
:not([children=""]) > compartment {
  justify-content: start;
}

regions {
  justify-content: stretch;
}

region {
  padding: 4;
  min-height: 100;
  justify-content: start;
  text-align: left;
}

region + region {
  dash-style: 7 3;
}

and name,
xor name,
intermediateevent name,
dormantevent name,
basicevent name,
houseevent name,
topevent name,
inhibit name,
conditionalevent name,
zeroevent name,
or name,
not name,
transferin name,
transferout name,
undevelopedevent name,
seq name,
majorityvote name,
unsafecontrolaction name,
operationalsituation name,
controlaction name,
interfaceblock name,
block name,
property name,
requirement name,
c4person name,
c4database name,
c4container name,
package name,
enumeration name,
interface name,
class name,
datatype name,
component name,
statemachine name,
usecase name,
actor name,
artifact name,
node name {
  font-weight: bold;
}

name[isabstract] {
  font-style: italic;
}

from {
  font-size: x-small;
}

interaction > pentagon,
activity > :is(name, stereotypes) {
  text-align: left;
}

compartment heading {
  padding: 0 0 4 0;
  font-size: x-small;
  font-style: italic;
  text-align: center;
}

operation[isabstract] {
  font-style: italic;
}

attribute[isstatic],
operation[isstatic] {
  text-decoration: underline;
}

property:not([aggregation="composite"]) {
  dash-style: 7 5;
}

/* Attached */

:has(icon)[connected_side] {
  text-align: right;
  vertical-align: top;
}

:has(icon)[connected_side="left"] {
  text-align: left;
}

:has(icon)[connected_side="bottom"] {
  vertical-align: bottom;
}

/* C4 model */

c4container, c4person {
  padding: 4 4 4 4;
}

c4database {
  padding: 20 4 4 4;
}

:is(c4container, c4database, c4person):not([children=""]) {
  justify-content: end;
}

:is(c4container, c4database, c4person):not([children=""]) > :is(name, technology) {
  text-align: left;
}

:is(c4container, c4database, c4person) technology {
  font-size: x-small;
}

:is(c4container, c4database, c4person) description {
  padding: 4 4 0 4;
}

Sphinx Extension

What’s more awesome than to use Gaphor diagrams directly in your Sphinx documentation. Whether you write your docs in reStructured Text or Markdown, we’ve got you covered.

Savjet

Here we cover the reStructured Text syntax. If you prefer markdown, we suggest you have a look at the MyST-parser, as it supports Sphinx directives.

It requires minimal effort to set up. Adding a diagram is as simple as:

.. diagram:: main
_images/88ff97d7-5c0c-11ea-8042-9771210c7122.svg

In case you use multiple Gaphor source files, you need to define a :model: attribute and add the model names to the Sphinx configuration file (conf.py).

.. diagram:: main
   :model: example

Diagrams can be referenced by their name, or by their fully qualified name.

.. diagram:: New model.main

Image properties can also be applied:

.. diagram:: main
   :width: 50%
   :align: right
   :alt: A description suitable for an example
A description suitable for an example

Konfiguracija

To add Gaphor diagram support to Sphinx, make sure Gaphor is listed as a dependency.

Važno

Gaphor requires at least Python 3.9.

Secondly, add the following to your conf.py file:

Step 1: Add gaphor as extension.

extensions = [
    "gaphor.extensions.sphinx",
]

Step 2: Add references to models

# A single model
gaphor_models = "../examples/sequence-diagram.gaphor"

# Or multiple models
gaphor_models = {
    "connect": "connect.gaphor",
    "example": "../examples/sequence-diagram.gaphor"
}

Now include diagram directives in your documents.

Read the Docs

The diagram directive plays nice with Read the docs. To make diagrams render, it’s best to use a .readthedocs.yaml file in your project. Make sure to include the extra apt_packages as shown below.

This is the .readthedocs.yaml file we use for Gaphor:

version: 2
formats: all
build:
  os: ubuntu-22.04
  tools:
    python: "3.11"
  apt_packages:
  - libgirepository1.0-dev
  - gir1.2-pango-1.0
  - graphviz
  jobs:
    pre_install:
    - pip install --constraint=.github/constraints.txt poetry
    - poetry config virtualenvs.create false
    post_install:
    - VIRTUAL_ENV=$READTHEDOCS_VIRTUALENV_PATH poetry install --with docs
sphinx:
  configuration: docs/conf.py
  fail_on_warning: true
  • libgirepository1.0-dev is required to build PyGObject.

  • gir1.2-pango-1.0 is required for text rendering.

Napomena

For Gaphor 2.7.0, gir1.2-gtk-3.0 and gir1.2-gtksource-4 are required apt_packages, although we do not use the GUI. From Gaphor 2.7.1 onwards all you need is GI-repository and Pango.

Greške

Errors are shown on the console when the documentation is built and in the document.

An error will appear in the documentation. Something like this:

Greška

No diagram ‘Wrong name’ in model ‘example’ (../examples/sequence-diagram.gaphor).

Jupyter and Scripting

One way to work with models is through the GUI. However, you may also be interested in getting more out of your models by interacting with them through Python scripts and Jupyter notebooks.

You can use scripts to:

  • Explore the model, check for (in)valid conditions.

  • Generate code, as is done for Gaphor’s data model.

  • Update a model from another source, like a CSV file.

Since Gaphor is written in Python, it also functions as a library.

Prvi koraci

To get started, you’ll need a Python console. You can use the interactive console in Gaphor, use a Jupyter notebook, although that will require setting up a Python development environment.

Query a model

The first step is to load a model. For this you’ll need an ElementFactory. The ElementFactory is responsible to creating and maintaining the model. It acts as a repository for the model while you’re working on it.

from gaphor.core.modeling import ElementFactory

element_factory = ElementFactory()
Settings schema not found and settings won’t be saved. Run `gaphor install-schemas`.

The module gaphor.storage contains everything to load and save models. Gaphor supports multiple modeling languages. The ModelingLanguageService consolidates those languages and makes it easy for the loader logic to find the appropriate classes.

Napomena

In versions before 2.13, an EventManager is required. In later versions, the ModelingLanguageService can be initialized without event manager.

from gaphor.core.eventmanager import EventManager
from gaphor.services.modelinglanguage import ModelingLanguageService
from gaphor.storage import storage

event_manager = EventManager()

modeling_language = ModelingLanguageService(event_manager=event_manager)

with open("../models/Core.gaphor", encoding="utf-8") as file_obj:
    storage.load(
        file_obj,
        element_factory,
        modeling_language,
    )

At this point the model is loaded in the element_factory and can be queried.

Napomena

A modeling language consists of the model elements, and diagram items. Graphical components are loaded separately. For the most basic manupilations, GTK (the GUI toolkit we use) is not required, but you may run into situations where Gaphor tries to load the GTK library.

One trick to avoid this (when generating Sphinx docs at least) is to use autodoc’s mock function to mock out the GTK and GDK libraries. However, Pango needs to be installed for text rendering.

A simple query only tells you what elements are in the model. The method ElementFactory.select() returns an iterator. Sometimes it’s easier to obtain a list directly. For those cases you can use ElementFatory.lselect(). Here we select the last five elements:

for element in element_factory.lselect()[:5]:
    print(element)
<gaphor.UML.uml.Package element 3867dda4-7a95-11ea-a112-7f953848cf85>
<gaphor.core.modeling.diagram.Diagram element 3867dda5-7a95-11ea-a112-7f953848cf85>
<gaphor.UML.classes.klass.ClassItem element 4cda498f-7a95-11ea-a112-7f953848cf85>
<gaphor.UML.classes.klass.ClassItem element 5cdae47f-7a95-11ea-a112-7f953848cf85>
<gaphor.UML.classes.klass.ClassItem element 639b48d1-7a95-11ea-a112-7f953848cf85>

Elements can also be queried by type and with a predicate function:

from gaphor import UML
for element in element_factory.select(UML.Class):
    print(element.name)
Element
Diagram
Presentation
Comment
StyleSheet
Property
Tagged
ElementChange
ValueChange
RefChange
PendingChange
ChangeKind
Picture
for diagram in element_factory.select(
    lambda e: isinstance(e, UML.Class) and e.name == "Diagram"
):
    print(diagram)
<gaphor.UML.uml.Class element 5cdae47e-7a95-11ea-a112-7f953848cf85>

Now, let’s say we want to do some simple (pseudo-)code generation. We can iterate class attributes and write some output.

diagram: UML.Class

def qname(element):
    return ".".join(element.qualifiedName)

diagram = next(element_factory.select(lambda e: isinstance(e, UML.Class) and e.name == "Diagram"))

print(f"class {diagram.name}({', '.join(qname(g) for g in diagram.general)}):")
for attribute in diagram.attribute:
    if attribute.typeValue:
        # Simple attribute
        print(f"    {attribute.name}: {attribute.typeValue}")
    elif attribute.type:
        # Association
        print(f"    {attribute.name}: {qname(attribute.type)}")
class Diagram(Core.Element):
    diagramType: String
    qualifiedName: String
    name: String
    ownedPresentation: Core.Presentation
    element: Core.Element

To find out which relations can be queried, have a look at the modeling language documentation. Gaphor’s data models have been built using the UML language.

You can find out more about a model property by printing it.

print(UML.Class.ownedAttribute)
<association ownedAttribute: Property[0..*] <>-> class_>

In this case it tells us that the type of UML.Class.ownedAttribute is UML.Property. UML.Property.class_ is set to the owner class when ownedAttribute is set. It is a bidirectional relation.

Draw a diagram

Another nice feature is drawing the diagrams. At this moment this requires a function. This behavior is similar to the diagram directive.

from gaphor.core.modeling import Diagram
from gaphor.extensions.ipython import draw

d = next(element_factory.select(Diagram))
draw(d, format="svg")
_images/c8d118162847ccc074a7373aecafd551991cc540caae9716fb069eedb9ae7c06.svg

Create a diagram

(Requires Gaphor 2.13)

Now let’s make something a little more fancy. We still have the core model loaded in the element factory. From this model we can create a custom diagram. With a little help of the auto-layout service, we can make it a readable diagram.

To create the diagram, we drop elements on the diagram. Items on a diagram represent an element in the model. We’ll also drop all associations on the model. Only if both ends can connect, the association will be added.

from gaphor.diagram.drop import drop
from gaphor.extensions.ipython import auto_layout

temp_diagram = element_factory.create(Diagram)

for name in ["Presentation", "Diagram", "Element"]:
    element = next(element_factory.select(
        lambda e: isinstance(e, UML.Class) and e.name == name
    ))
    drop(element, temp_diagram, x=0, y=0)

# Drop all assocations, see what sticks
for association in element_factory.lselect(UML.Association):
    drop(association, temp_diagram, x=0, y=0)

auto_layout(temp_diagram)

draw(temp_diagram, format="svg")
_images/69b3aabc155707588316369e0104e5b0fcf9ccbb48aea2ccfca58df8b214b4ef.svg

The diagram is not perfect, but you get the picture.

Update a model

Updating a model always starts with the element factory: that’s where elements are created.

To create a UML Class instance, you can:

my_class = element_factory.create(UML.Class)
my_class.name = "MyClass"

To give it an attribute, create an attribute type (UML.Property) and then assign the attribute values.

my_attr = element_factory.create(UML.Property)
my_attr.name = "my_attr"
my_attr.typeValue = "string"
my_class.ownedAttribute = my_attr

Adding it to the diagram looks like this:

my_diagram = element_factory.create(Diagram)
drop(my_class, my_diagram, x=0, y=0)
draw(my_diagram, format="svg")
_images/fec592a94e63a2fe2765cac5c4550d9c863dfd3ee71e1dbffa0d60195765bf9a.svg

If you save the model, your changes are persisted:

with open("../my-model.gaphor", "w") as out:
    storage.save(out, element_factory)

Updating elements

If you need to update existing elements, this can be done by keeping track of the element ID. Each element in the model has a unique internal id. Once again we need some imports from Gaphor:

from pathlib import Path

from gaphor import UML
from gaphor.application import Session  # needed to run services
from gaphor.transaction import Transaction  # needed to make changes
from gaphor.storage import storage  # needed to save to file

Then start up the services we will use:

# Create the Gaphor application object.
session = Session()
# Get services we need.
element_factory = session.get_service("element_factory")
file_manager = session.get_service("file_manager")
event_manager = session.get_service("event_manager")

and load in the model to the session

# The model file to load.
model_filename = "../my-model.gaphor"

# Load model from file.
file_manager.load(Path(model_filename))
# Now we query the model to get the element we want to change:
the_class = element_factory.select(
    lambda e: isinstance(e, UML.Class) and e.name == "My Class"
)
uid = the_class._id
print(f"Original element: {the_class.name} -- {the_class.my_attr}")

Importantly, the changes are made as part of a Transaction. Here we find the element with the same id, and then update the content. We then save the altered model to a file.

# change the name and write back into the model
with Transaction(event_manager) as ctx:

    cls = next(
        element_factory.select(
            lambda e: isinstance(e, UML.Class) and e._id == uid
        )
    )
    cls.name = "Not My Class Anymore"
    cls.attr = "updated string"

# Write changes to file here
with Transaction(event_manager) as ctx:
    with open(model_filename, "w") as out:
        storage.save(out, element_factory)

print(f"Updated element: {cls.name} -- {cls.my_attr}")

What else

What else is there to know…

  • Gaphor supports derived associations. For example, element.owner points to the owner element. For an attribute that would be its containing class.

  • All data models are described in the Modeling Languages section of the docs.

  • If you use Gaphor’s Console, you’ll need to apply all changes in a transaction, or they will result in an error.

  • If you want a comprehensive example of a code generator, have a look at Gaphor’s coder module. This module is used to generate the code for the data models used by Gaphor.

  • This page is rendered with MyST-NB. It’s actually a Jupyter Notebook!

Primjeri

Expanding on the information above the following snippetts show how to create requirements and interfaces.

Requirements from text fields

txts = ['req1', 'req2', 'bob the cat']
my_diagram = element_factory.create(Diagram)
my_diagram.name=' my diagram'
reqPackage = element_factory.create(UML.Package)
reqPackage.name = "Requirements"
drop(reqPackage, my_diagram, x=0, y=0)

for req_id,txt in enumerate(txts):
    my_class = element_factory.create(SysML.sysml.Requirement)
    my_class.name = f"{req_id}-{txt[:3]}"
    my_class.text = f"{txt}"
    my_class.externalId = f"{req_id}"

    drop(my_class, my_diagram, x=0, y=0)

with open(outfile, "w") as out:
    storage.save(out, element_factory)

Interfaces from dictionaries


# get interface definitions from file into this dictionary format
interfaces = {'Interface1': ['signal1:type1', 'signal2:type1', 'signal3:type1'],
              'Interface2': ['signal4:type2', 'signal5:type2', 'signal6:type2']}


my_diagram = element_factory.create(Diagram)
my_diagram.name=' my diagram'
intPackage = element_factory.create(UML.Package)
intPackage.name = "Interfaces"
drop(intPackage, my_diagram, x=0, y=0)

for interface,signals in interfaces.items():
    my_class = element_factory.create(UML.uml.Interface)
    my_class.name = f"{interface}"
    for s in signals:
        my_attr = element_factory.create(UML.Property)
        name,vtype = s.split(':')
        my_attr.name = name
        my_attr.typeValue = vtype
        my_class.ownedAttribute = my_attr

    drop(my_class, my_diagram, x=0, y=0)


with open(outfile, "w") as out:
    storage.save(out, element_factory)

Here is another example:

Example: Gaphor services

In this example we’re doing something a little less trivial. In Gaphor, services are defined as entry points. Each service is a class, and takes parameters with names that match other services. This allows services to depend on other services.

It looks something like this:

# entry point name: my_service
class MyService:
    ...

# entry point name: my_other_service
class MyOtherService:
    def __init__(self, my_service):
        ...

Let’s first load the entry points.

from gaphor.entrypoint import load_entry_points

entry_points = load_entry_points("gaphor.services")

entry_points
Settings schema not found and settings won’t be saved. Run `gaphor install-schemas`.
{'align': gaphor.plugins.align.align.AlignService,
 'auto_layout': gaphor.plugins.autolayout.pydot.AutoLayoutService,
 'component_registry': gaphor.services.componentregistry.ComponentRegistry,
 'console_window': gaphor.plugins.console.consolewindow.ConsoleWindow,
 'copy': gaphor.ui.copyservice.CopyService,
 'diagram_export': gaphor.plugins.diagramexport.DiagramExport,
 'diagrams': gaphor.ui.diagrams.Diagrams,
 'element_dispatcher': gaphor.core.modeling.elementdispatcher.ElementDispatcher,
 'element_editor': gaphor.ui.elementeditor.ElementEditor,
 'element_factory': gaphor.core.modeling.elementfactory.ElementFactory,
 'event_manager': gaphor.core.eventmanager.EventManager,
 'export_menu': gaphor.ui.menufragment.MenuFragment,
 'file_manager': gaphor.ui.filemanager.FileManager,
 'main_window': gaphor.ui.mainwindow.MainWindow,
 'model_browser': gaphor.ui.modelbrowser.ModelBrowser,
 'model_changed': gaphor.ui.modelchanged.ModelChanged,
 'modeling_language': gaphor.services.modelinglanguage.ModelingLanguageService,
 'properties': gaphor.services.properties.Properties,
 'recent_files': gaphor.ui.recentfiles.RecentFiles,
 'sanitizer': gaphor.UML.sanitizerservice.SanitizerService,
 'toolbox': gaphor.ui.toolbox.Toolbox,
 'tools_menu': gaphor.ui.menufragment.MenuFragment,
 'undo_manager': gaphor.services.undomanager.UndoManager,
 'xmi_export': gaphor.plugins.xmiexport.XMIExport}

Now let’s create a component in our model for every service.

from gaphor import UML
from gaphor.core.modeling import ElementFactory

element_factory = ElementFactory()

def create_component(name):
    c = element_factory.create(UML.Component)
    c.name = name
    return c

components = {name: create_component(name) for name in entry_points}
components
{'align': <gaphor.UML.uml.Component element d8ba7c0a-0c0c-11ef-aad4-0242ac110002>,
 'auto_layout': <gaphor.UML.uml.Component element d8ba813c-0c0c-11ef-aad4-0242ac110002>,
 'component_registry': <gaphor.UML.uml.Component element d8ba83ee-0c0c-11ef-aad4-0242ac110002>,
 'console_window': <gaphor.UML.uml.Component element d8ba8678-0c0c-11ef-aad4-0242ac110002>,
 'copy': <gaphor.UML.uml.Component element d8ba88bc-0c0c-11ef-aad4-0242ac110002>,
 'diagram_export': <gaphor.UML.uml.Component element d8ba8ac4-0c0c-11ef-aad4-0242ac110002>,
 'diagrams': <gaphor.UML.uml.Component element d8ba8cd6-0c0c-11ef-aad4-0242ac110002>,
 'element_dispatcher': <gaphor.UML.uml.Component element d8ba8f10-0c0c-11ef-aad4-0242ac110002>,
 'element_editor': <gaphor.UML.uml.Component element d8ba9168-0c0c-11ef-aad4-0242ac110002>,
 'element_factory': <gaphor.UML.uml.Component element d8ba933e-0c0c-11ef-aad4-0242ac110002>,
 'event_manager': <gaphor.UML.uml.Component element d8ba9500-0c0c-11ef-aad4-0242ac110002>,
 'export_menu': <gaphor.UML.uml.Component element d8ba96d6-0c0c-11ef-aad4-0242ac110002>,
 'file_manager': <gaphor.UML.uml.Component element d8ba9898-0c0c-11ef-aad4-0242ac110002>,
 'main_window': <gaphor.UML.uml.Component element d8ba9a5a-0c0c-11ef-aad4-0242ac110002>,
 'model_browser': <gaphor.UML.uml.Component element d8ba9c12-0c0c-11ef-aad4-0242ac110002>,
 'model_changed': <gaphor.UML.uml.Component element d8ba9dca-0c0c-11ef-aad4-0242ac110002>,
 'modeling_language': <gaphor.UML.uml.Component element d8ba9f78-0c0c-11ef-aad4-0242ac110002>,
 'properties': <gaphor.UML.uml.Component element d8baa126-0c0c-11ef-aad4-0242ac110002>,
 'recent_files': <gaphor.UML.uml.Component element d8baa2d4-0c0c-11ef-aad4-0242ac110002>,
 'sanitizer': <gaphor.UML.uml.Component element d8baa496-0c0c-11ef-aad4-0242ac110002>,
 'toolbox': <gaphor.UML.uml.Component element d8baa644-0c0c-11ef-aad4-0242ac110002>,
 'tools_menu': <gaphor.UML.uml.Component element d8baa7f2-0c0c-11ef-aad4-0242ac110002>,
 'undo_manager': <gaphor.UML.uml.Component element d8baa9c8-0c0c-11ef-aad4-0242ac110002>,
 'xmi_export': <gaphor.UML.uml.Component element d8baab76-0c0c-11ef-aad4-0242ac110002>}

With all components mapped, we can create dependencies between those components, based on the constructor parameter names.

import inspect

for name, cls in entry_points.items():
    for param_name in inspect.signature(cls).parameters:
        if param_name not in components:
            continue

        dep = element_factory.create(UML.Usage)
        dep.client = components[name]
        dep.supplier = components[param_name]

With all elements in the model, we can create a diagram. Let’s drop the components and dependencies on the diagram and let auto-layout do its magic.

To make the dependency look good, we have to add a style sheet. If you create a new diagram via the GUI, this element is automatically added.

from gaphor.core.modeling import Diagram, StyleSheet
from gaphor.diagram.drop import drop

element_factory.create(StyleSheet)
diagram = element_factory.create(Diagram)

for element in element_factory.lselect():
    drop(element, diagram, x=0, y=0)

Last step is to layout and draw the diagram.

from gaphor.extensions.ipython import auto_layout, draw

auto_layout(diagram)

draw(diagram, format="svg")
_images/a2c01332aee4fab3a852b1e62250504253bc55b1f71d5e71b7d53dbdd99ff06e.svg

That’s all. As you can see from the diagram, a lot of services rely on EventManager.

Stereotipi

In UML, stereotypes are way to extend the application of the UML language to new domains. For example: SysML started as a profile for UML.

Gaphor supports stereotypes too. They’re the way for you to adapt your models to your specific needs.

The UML, SysML, RAAML and other models used in Gaphor – the code is generated from Gaphor model files – make use of stereotypes to provide specific information used when generating the data model code.

To create a stereotype, ensure the UML Profile is active and open the Profile section of the toolbox. First add a Metaclass to your diagram. Next add a Stereotype, and connect both with a Extension. The «metaclass» stereotype will only show once the Extension is connected both class and stereotype.

Napomena

The class names in the metaclass should be a class name from the UML model, such as Class, Interface, Property, Association. Or even Element if you want to use the stereotype on all elements.

Your stereotype declaration may look something like this:

_images/79803af8-4c9d-11ec-9b16-0456e5e540ed.svg

The Asynchronous stereotype has a property priority. This property can be proved a value once the stereotype is applied to a Property, such as an association end.

When a stereotype can be applied to a model element, a Stereotype section will appear in the editor.

Stereotype usage example

Creating a profile

In SysML extending the profile using stereotypes is often required to tailor the model to your needs. For example, creating Customer vs System requirements.

To add a profile to your model:

  • Create a package called profile this can be done by right clicking in the left hand column.

  • Switch modelling language to the UML profile (top of left hand menu drop down)

  • Within the package create a profile diagram (prf)

  • Add a profile element to the diagram

  • Add a meta-class element to the diagram, within the profile.

  • Add a stereotype element to the diagram, within the profile.

  • Connect meta-class and stereotype with an Extension relation. The head should be attached to the class. As soon as the Extension is connected, the class will get a stereotype metaclass assigned.

With the meta-class and stereotype placed on the diagram, either:

  • Double-click the meta-class and name it after the base element you want to create your stereotype from.

  • Select the base element from the drop down menu in the Property Editor on the right hand side. In this case only UML elements can be used as base elements.

Styling Stereotypes

You can apply styling to stereotypes. For example here the base element requirement has a stereotype system requirement

/*Add style to Requirement element*/
requirement{
    background-color: #C5E7E7;
    text-color: #2A2A2A;
}
/*Update Requirement styling for the System stereotype*/
requirement[appliedStereotype.classifier.name=system]{
    background-color: #D5F7E7;
    text-color: #2A2A2A;
}

Style Sheets has more detail on how CSS works in Gaphor

Resolve Merge Conflicts

Suppose you’re working on a model. If you create a change, while someone else has also made changes, there’s a fair chance you’ll end up with a merge conflict.

Gaphor tries to make the changes to a model as small as possible: all elements are stored in the same order. However, since a Gaphor model is a persisted graph of objects, merging changes is not as simple as opening a text editor.

From Gaphor 2.18 onwards Gaphor is also capable of merging models. Once a merge conflict has been detected (i.e., when the model file contains git conflict-resolution markers <<<<<<<, =======, and >>>>>>>), Gaphor will offer the option to open the current model, the incoming model or merge changes manually via the Merge Editor.

merge dialog

If you choose Open Merge Editor, both models will be loaded. The current model remains as is. In addition, the changes made to the incoming model are calculated. Those changes are stored as pending change objects in the model.

Savjet

Pending changes are part of the model, you can save the model with changes and resolve those at a later point.

The Merge Editor is shown on the right side, replacing the (normal) Property Editor.

merge conflict window

Merge actions are grouped by diagram, where possible. When you apply a change, all changes listed as children are also applied. Once changes are applied, they can only be reverted by undoing the change (hit Undo).

Napomena

The Merge Editor replaces the Property Editor, as long as there are pending changes in the model.

It is concidered good practice to resolve the merge conflict before you continue modeling.

When all conflicts have been resolved, press Resolve to finish merge conflict resolution.

Plugins

Važno

Plugins is an experimental feature! The API may change.

We welcome you to try and provide your feedback.

Plugins allow you to extend the functionality of Gaphor beyond the features provided in the standard distributions. In particular, plugins can be helpful if you install the binary distributions available on the download page.

Gaphor can be extended via entry points in several ways:

  1. Application (global) services (gaphor.appservices)

  2. Session specific services (gaphor.services)

  3. Modeling languages (gaphor.modelinglanguages)

  4. (Sub)command line parsers (gaphor.argparsers)

  5. Indirectly loaded modules (gaphor.modules), mainly for UI components

The default location for plugins is $HOME/.local/gaphor/plugins-2 ($USER/.local/gaphor/plugins-2 on Windows). This location can be changed by setting the environment variable GAPHOR_PLUGIN_PATH and point to a directory.

Install a plugin

At this moment Gaphor does not have functionality bundled to install and maintain plugins. To install a plugin, use pip from a Python installation on your computer. On macOS and Linux, that should be easy, on Windows you may need to install Python separately from python.org or the Windows Store.

Važno

  1. Since plugins are installed with your system Python version, it’s important that plugins are pure Python and do not contain compiled C code.

  2. If you use Gaphor installed as Flatpak, you need to grant Gaphor access to user files (filesystem=home), so Gaphor can find files in your .local folder. You can use FlatSeal to change permissions of Flatpaks.

For example: to install the Hello World plugin on Linux and macOS, enter:

pip install --target $HOME/.local/gaphor/plugins-2 git+https://github.com/gaphor/gaphor_plugin_helloworld.git

Then start Gaphor as you normally would. A new Hello World entry has been added to the tools menu (open menu → Tools → Hello World).

Create your own plugin

If you want to write a plugin yourself, you can familiarize yourself with Gaphor’s design principles, service oriented architecture (includes a plugin example), and event driven framework.

Example plugin

You can have a look at the Hello World plugin available on GitHub for an example of how to create your own plugin.

The pyproject.toml file contains a plugin:

[tool.poetry.plugins."gaphor.services"]
"helloworld" = "gaphor_helloworld_plugin:HelloWorldPlugin"

This refers to the class HelloWorldPlugin in package/module gaphor_plugins_helloworld.

Here is a stripped version of the hello world plugin:

from gaphor.abc import Service, ActionProvider
from gaphor.core import _, action

class HelloWorldPlugin(Service, ActionProvider):     # 1.

    def __init__(self, tools_menu):                  # 2.
        self.tools_menu = tools_menu
        tools_menu.add_actions(self)                 # 3.

    def shutdown(self):                              # 4.
        self.tools_menu.remove_actions(self)

    @action(                                         # 5.
        name="helloworld",
        label=_("Hello world"),
        tooltip=_("Every application…"),
    )
    def helloworld_action(self):
        main_window = self.main_window
        ...  # gtk code left out
  1. As stated before, a plugin should implement the Service interface. It also implements ActionProvider, saying it has some actions to be performed by the user.

  2. The menu entry will be part of the „Tools” extension menu. This extension point is created as a service. Other services can also be passed as dependencies. Services can get references to other services by defining them as arguments of the constructor.

  3. All action defined in this service are registered.

  4. Each service has a shutdown() method. This allows the service to perform some cleanup when it’s shut down.

  5. The action that can be invoked. The action is defined and will be picked up by add_actions() method (see 3.)

Gaphor on Linux

Gaphor can be installed as Flatpak on Linux, some distributions provide packages. Check out the Gaphor download page for details.

Older releases are available from GitHub.

CI builds are also available.

Development Environment

There are two ways to set up a development environment:

  1. GNOME Builder, ideal for “drive by” contributions.

  2. A local environment.

GNOME Builder

Open GNOME Builder 43 or newer, clone the repository. Check if the Build Profile is set to org.gaphor.Gaphor.json. If so, hit the Run button to start the application.

A Local Environment

To set up a development environment with Linux, you first need a fairly new Linux distribution version. For example, the latest Ubuntu LTS or newer, Arch, Debian Testing, SUSE Tumbleweed, or similar. Gaphor depends on newer versions of GTK, and we don’t test for backwards compatibility. You will also need the latest stable version of Python. In order to get the latest stable version without interfering with your system-wide Python version, we recommend that you install pyenv.

Install the pyenv prerequisites first, and then install pyenv:

curl https://pyenv.run | bash

Make sure you follow the instruction at the end of the installation script to install the commands in your shell’s rc file. Next install the latest version of Python by executing:

pyenv install 3.x.x

Where 3.x.x is replaced by the latest stable version of Python (pyenv should let you tab-complete available versions).

Next install the Gaphor prerequisites by installing the gobject introspection and cairo build dependencies, for example, in Ubuntu execute:

sudo apt-get install -y python3-dev python3-gi python3-gi-cairo
gir1.2-gtk-4.0 libgirepository1.0-dev libcairo2-dev libgtksourceview-5-dev

Install Poetry using pipx:

pipx install poetry

Next, clone the repository, after which you need to execute the following consecutive commands:

cd gaphor
# activate latest python for this project
pyenv local 3.x.x # 3.x.x is the version you installed earlier
poetry env use 3.x # ensures poetry /consistently/ uses latest major release
poetry install
poetry run pre-commit install

Now, you can run gaphor as

poetry run gaphor

NOTE: Gaphor requires GTK 4. It works best with GTK >=4.8 and libadwaita >=1.2.

Debugging using Visual Studio Code

Before you start debugging you’ll need to open Gaphor in vscode (the folder containing pyproject.toml). You’ll need to have the Python extension installed.

Create a file .vscode/launch.json with the following content:

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Python: Gaphor UI",
            "type": "python",
            "request": "launch",
            "module": "gaphor",
            "justMyCode": false,
            "env": {
                "GDK_BACKEND": "wayland"
            }
        }
    ]
}

GDK_BACKEND is added since VSCode by default uses XWayland (the X11 emulator).

Create a Flatpak Package

The main method that Gaphor is packaged for Linux is with a Flatpak package. Flatpak is a software utility for software deployment and package management for Linux. It offers a sandbox environment in which users can run application software in isolation from the rest of the system.

We distribute the official Flatpak using Flathub, and building of the image is done at the Gaphor Flathub repository.

  1. Instaliraj Flatpak

  2. Instaliraj flatpak-builder

    sudo apt-get install flatpak-builder
    
  3. Instaliraj GNOME SDK

    flatpak install flathub org.gnome.Sdk 43
    
  4. Kopiraj Flathub repozitorij i instaliraj potrebni SDK:

    git clone https://github.com/flathub/org.gaphor.Gaphor.git
    cd org.gaphor.Gaphor
    make setup
    
  5. Izgradi Gaphor Flatpak

    make
    
  6. Instaliraj Flatpak

    make install
    

Paketi za Linux distribuciju

Examples of Gaphor and Gaphas RPM spec files can be found in PLD Linux repository:

  • https://github.com/pld-linux/python-gaphas

  • https://github.com/pld-linux/gaphor

There is also an Arch User Repository (AUR) for Gaphor available for Arch users.

Please, do not hesitate to contact us if you need help to create a Linux package for Gaphor or Gaphas.

Gaphor na macOS-u

The latest release of Gaphor can be downloaded from the Gaphor download page. Gaphor can also be installed as a Homebrew cask.

Older releases are available from GitHub.

CI builds are also available.

Development Environment

To set up a development environment with macOS:

  1. Instaliraj Homebrew

  2. Otvori terminal i izvrši:

brew install python3 gobject-introspection gtk4 gtksourceview5 libadwaita adwaita-icon-theme graphviz

Install Poetry using pipx:

pipx install poetry

Next, clone the repository, after which you can execute the following consecutive commands to install the poetry environment:

cd gaphor
poetry install
poetry run pre-commit install

Now, you can run gaphor as

poetry run gaphor

If PyGObject does not compile and complains about a missing ffi.h file, set the following environment variable and run poetry install again:

export PKG_CONFIG_PATH=/opt/homebrew/opt/libffi/lib/pkgconfig  # use /usr/local/ for older Homebrew installs
poetry install

Debugging using Visual Studio Code

Before you start debugging you’ll need to open Gaphor in VSCode (the folder containing pyproject.toml). You’ll need to have the Python extension installed.

Create a file .vscode/launch.json with the following content:

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Python: Gaphor UI",
            "type": "python",
            "request": "launch",
            "module": "gaphor",
            "justMyCode": false
        }
    ]
}

Paketiranje za macOS

In order to create an exe installation package for macOS, we utilize PyInstaller which analyzes Gaphor to find all the dependencies and bundle them in to a single folder.

  1. Follow the instructions for settings up a development environment above

  2. Open a terminal and execute the following from the repository directory:

poetry run python po/build-babel.py
poetry install --with packaging
poetry run poe package

Gaphor na Windowsu

Gaphor can be installed as with our installer. Check out the Gaphor download page for details.

Older releases are available from GitHub.

CI builds are also available.

Development Environment

Choco

Preporučujemo korištenje Chocolately kao upravlajača paketima u Windowsu.

Za instaliranje otvori PowerShell kao administrator, a zatim izvrši:

Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))

Za pokretanje lokalnih skripta u sljedećim koracima, također izvrši

Set-ExecutionPolicy RemoteSigned

To omogućuje pokretanje lokalnih PowerShell skripta bez potpisivanja, ali još uvijek zahtijeva potpisivanje za udaljena skripta.

Git

To set up a development environment in Windows first install Git by executing as an administrator:

choco install git

MSYS2

Razvojno okruženje u sljedećem koraku mora imati instaliran MSYS2 za pružanje nekih Linuxovih alata naredbenog retka u sustavu Windows.

Ostavi PowerShell otvorenim kao administrator i instaliraj MSYS2:

choco install msys2

GTK i Python s gvsbuild

gvsbuild provides a Python script helps you build the GTK library stack for Windows using Visual Studio. By compiling GTK with Visual Studio, we can then use a standard Python development environment in Windows.

First we will install the gvsbuild dependencies:

  1. Visual C++ build tools workload for Visual Studio 2022 Build Tools

  2. Python

Instaliraj Visual Studio 2022

S tojim administratorskim PowerShell terminalom:

choco install visualstudio2022-workload-vctools
Instaliraj najnoviju Python verziju

In Windows, The full installer contains all the Python components and is the best option for developers using Python for any kind of project.

For more information on how to use the official installer, please see the full installer instructions. The default installation options should be fine for use with Gaphor.

  1. Install the latest Python version using the official installer.

  2. Open a PowerShell terminal as a normal user and check the python version:

    py -3.11 --version
    
Instaliraj Graphviz

Graphviz is used by Gaphor for automatic diagram formatting.

  1. Install from Chocolately with administrator PowerShell:

    choco install graphviz
    
  2. Restart your PowerShell terminal as a normal user and check that the dot command is available:

    dot -?
    
Instaliraj pipx

Iz običnog korisničkog PowerShell terminala izvrši:

py -3.11 -m pip install --user pipx
py -3.11 -m pipx ensurepath
Instaliraj gvsbuild

Iz običnog korisničkog PowerShell terminala izvrši:

pipx install gvsbuild
Izgradi GTK

U istom PowerShell terminalu izvrši:

gvsbuild build --enable-gi --py-wheel gobject-introspection gtk4 libadwaita gtksourceview5 pygobject pycairo adwaita-icon-theme hicolor-icon-theme

Popij kavu, izgradnja će trajati nekoliko minuta.

Postavi Gaphor

U istom PowerShell terminalu kopiraj repozitorij:

cd (to the location you want to put Gaphor)
git clone https://github.com/gaphor/gaphor.git
cd gaphor

Instaliraj Poetry

pipx install poetry

Dodaj GTK svojim varijablama okruženja:

$env:Path = $env:Path + ";C:\gtk-build\gtk\x64\release\bin"
$env:LIB = "C:\gtk-build\gtk\x64\release\lib"
$env:INCLUDE = "C:\gtk-build\gtk\x64\release\include;C:\gtk-build\gtk\x64\release\include\cairo;C:\gtk-build\gtk\x64\release\include\glib-2.0;C:\gtk-build\gtk\x64\release\include\gobject-introspection-1.0;C:\gtk-build\gtk\x64\release\lib\glib-2.0\include;"
$env:XDG_DATA_HOME = "$HOME\.local\share"

Također možeš urediti varijable okruženja svog računa kako bi postojale tijekom PowerShell sesija.

Instaliraj Gaphor ovisnosti

poetry install

Install the git hook scripts

poetry run pre-commit install

Ponovo instaliraj PyGObject i pycairo pomoću gvsbuild kotačića

poetry run pip install --force-reinstall (Resolve-Path C:\gtk-build\build\x64\release\pygobject\dist\PyGObject*.whl)
poetry run pip install --force-reinstall (Resolve-Path C:\gtk-build\build\x64\release\pycairo\dist\pycairo*.whl)

Pokreni Gaphor!

poetry run gaphor

Debugging using Visual Studio Code

Start a new PowerShell terminal, and set current directory to the project folder:

cd (to the location you put gaphor)

Ensure that path environment variable is set:

$env:Path = "C:\gtk-build\gtk\x64\release\bin;" + $env:Path

Start Visual Studio Code:

code .

To start the debugger, execute the following steps:

  1. Open __main__.py file from gaphor folder

  2. Add a breakpoint on line main(sys.argv)

  3. In the menu, select Run → Start debugging

  4. Choose Select module from the list

  5. Enter gaphor as module name

Visual Studio Code will start the application in debug mode, and will stop at main.

Paketiranje za Windows

In order to create an exe installation package for Windows, we utilize PyInstaller which analyzes Gaphor to find all the dependencies and bundle them in to a single folder. We then use a custom bash script that creates a Windows installer using NSIS and a portable installer using 7-Zip. To install them, open PowerShell as an administrator, then execute:

choco install nsis 7zip

Then build your installer using:

poetry install --only main,packaging,automation
poetry build
poetry run poe package
poetry run poe win-installer

Gaphor u kontejneru

Umjesto postavljanja lokalnog razvojnog okruženja, najlakši način doprinositi projektu je putem GitHub Codespaces.

GitHub Codespaces

Za otvaranje Gaphora u Codespace, slijedi ove korake:

  1. Idi na https://github.com/gaphor/gaphor

  2. Pritisni padajući izbornik za kod i odaberi opciju Otvori pomoću Codespaces.

  3. Odaberi + Novi codespace na dnu ploče.

Za daljnje informacije pogledaj GitHub documentation.

Remote access to Gaphor graphic window with Codespaces

When using Codespaces, chances are that you also want to interact with the graphical window of Gaphor.

This is facilitated in Gaphor by use of container feature called desktop-lite. This feature is activated by default in the Gaphor’s devcontainer.json file.

Notice the webPort/vncPort and password values. These are used in subsequent steps.

    		"desktop-lite": {
			"password": "vscode",
			"webPort": "6080",
			"vncPort": "5901"
		},

There are two options:

Using a local VNC viewer

  1. Download and install VNC viewer of your choice (e.g. realvnc)

  2. Specify remote hostname as localhost and port as 5901 and connect VNC. The port number should be same as specified in attribute vncPort

  3. Upon debugging/running Gaphor the familiar Graphic window should be displayed in VNC view

Using noVNC viewer on the Browser

  1. This is based on noVNC application

  2. Open the browser on your local machine and give address as http://127.0.0.1:6080/. The port number should be same as specified in attribute webPort

  3. A noVNC window will open, click on Connect and provide password as vscode. The password should be same as specified in attribute password

  4. Upon debugging/running Gaphor the familiar Graphic window should be displayed in noVNC view on Browser

Contribute to Gaphor

Would you like to contribute to the development of Gaphor? If you think this is only something for experienced developers, please reconsider, there are plenty of ways to contribute to Gaphor, no matter your level of experience or skill set.

You can help to support Gaphor in your language. You can do that on Weblate, directly from your browser. This service will periodically create Pull Requests for Gaphor with updated translations.

It is also very nice if you create your own content around Gaphor. Think about blog posts, videos, and conference talks. When you created something, please drop us a line, so we can add it to the Tutorials page on the website.

If you want to contribute code, a good starting point is this site. It contains a lot of information on how Gaphor is build, especially in the Concepts section.

Some issues are labeled with first-timers-only. Those issues are a good starting point to make yourself familiar with the code and code style.

It’s always a good idea to create an issue or start a discussion if you want to build something for which there is no issue yet. That way you can assure that your idea is implemented in a way consistent with the application, and it increases the chance your work will be accepted.

You’ll need to fork Gaphor and set up a development environment (macOS, Windows).

Napomena

In our pipeline we do a couple of quality checks. We strongly recommend that you install pre-commit and its git hook scripts, so your PR does build on our build environment.

Modeling Language Core

The Core modeling language is the the basis for any other language.

The Element class acts as the root for all gaphor domain classes. Diagram and Presentation form the basis for everything you see in a diagram.

All data models in Gaphor are generated from actual Gaphor model files. This allows us to provide you nice diagrams of Gaphor’s internal model.

_images/3867dda5-7a95-11ea-a112-7f953848cf85.svg

The Element base class provides event notification and integrates with the model repository (internally known as ElementFactory). Bi-directional relationships are also possible, as well as derived relations.

The RepositoryProtocol, and EventWatcherProtocol protocols are important to connect the model to the repository and event handling mechanisms.

The Element Class

The class Element is the core of Gaphor’s data model.

class gaphor.core.modeling.Element(id: str | None = None, model: RepositoryProtocol | None = None)[source]

Base class for all model data classes.

property id: str

An id (read-only), unique within the model.

property model: RepositoryProtocol

The owning model, raises TypeError when model is not set.

Unlink the element.

All the elements references are destroyed. For composite associations, the associated elements are also unlinked.

The unlink lock is acquired while unlinking this element’s properties to avoid recursion problems.

Event handling

handle(event) None[source]

Propagate incoming events.

This only works if the element has been created by an ElementFactory

watcher(default_handler: Callable[[ElementUpdated], None] | None = None) EventWatcherProtocol[source]

Create a new watcher for this element.

Watchers provide a convenient way to get signalled when a property relative to self has been changed.

To use a watcher, the element should be created by a properly wired up ElementFactory`.

This example is purely illustrative:

>>> element = Element()
>>> watcher = element.watcher(default_handler=print)
>>> watcher.watch("note")  # Watch for changed on element.note

Loading and saving

load(name, value) None[source]

Loads value in name.

Make sure that after all elements are loaded, postload() should be called.

postload() None[source]

Fix up the odds and ends.

This is run after all elements are loaded.

save(save_func) None[source]

Save the state by calling save_func(name, value).

OCL-style methods

isKindOf(class_: type[Element]) bool[source]

Returns True if the object is an instance of class_.

isTypeOf(other: Element) bool[source]

Returns True if the object is of the same type as the other.

The Presentation class

class gaphor.core.modeling.Presentation(diagram: Diagram, id: Id | None = None)[source]

A special type of Element that can be displayed on a Diagram.

Subtypes of Presentation should implement the gaphas.item.Item protocol.

request_update() None[source]

Mark this presentation object for update.

Updates are orchestrated by diagrams.

watch(path: str, handler: Callable[[ElementUpdated], None] | None = None) Self[source]

Watch a certain path of elements starting with self.

The handler is optional and will default to a simple request_update.

Watches should be set in the constructor, so they can be registered and unregistered in one shot.

self.watch("subject[NamedElement].name")

This interface is fluent: returns self.

change_parent(new_parent: Presentation | None) None[source]

Change the parent and update the item’s matrix so the item visually remains in the same place.

The Diagram class

class gaphor.core.modeling.Diagram(id: str | None = None, model: RepositoryProtocol | None = None)[source]

Diagrams may contain Presentation elements and can be owned by any element.

create(type_: type[P], parent: Presentation | None = None, subject: Element | None = None) P[source]

Create a new diagram item on the diagram.

It is created with a unique ID, and it is attached to the diagram’s root item. The type parameter is the element class to create. The new element also has an optional parent and subject.

lookup(id: str) Presentation | None[source]

Find a presentation item by id.

Returns a presentation in this diagram or return None.

select(expression: Callable[[Presentation], bool]) Iterator[Presentation][source]
select(expression: type[P]) Iterator[P]
select(expression: None) Iterator[Presentation]

Return an iterator of all canvas items that match expression.

request_update(item: Item) None[source]

Schedule an item for updating.

No update is done at this point, it’s only added to the set of to-be updated items.

This method is part of the gaphas.model.Model protocol.

update(dirty_items: Collection[Presentation] = ()) None[source]

Update the diagram.

All items that requested an update via request_update() are now updates. If an item has an update(context: UpdateContext) method, it’s invoked. Constraints are solved.

Protocols

class gaphor.core.modeling.element.RepositoryProtocol(*args, **kwargs)[source]
create(type: type[T]) T[source]

Create a new element in the repository.

select(self, expression: Callable[[Element], bool]) Iterator[Element]

Select elements from the repository that fulfill expression.

select(self, type_: type[T]) Iterator[T]

Select all elements from the repository of type type_.

select(self, expression: None) Iterator[Element]

Select all elements from the repository.

lookup(id: str) Element | None[source]

Get an element by id from the repository.

Returns None if no such element exists.

class gaphor.core.modeling.element.EventWatcherProtocol(*args, **kwargs)[source]
watch(path: str, handler: Callable[[ElementUpdated], None] | None = None) EventWatcherProtocol[source]

Add a watch for a specific path. The path is relative to the element that created the watcher object.

Returns self, so watch operations can be chained.

unsubscribe_all() None[source]

Should be called before the watcher is disposed, to release all watched paths.

Change Sets

The core model has support for change sets, which are sets of pending changes. Normally you end up with a change set when you resolve a merge conflict in your model.

_images/6f72b252-5ee4-11ed-9155-0456e5e540ed.svg

Unified Modeling Language

UML model je najopsežniji model u Gaphoru. Koristi se kao osnovni jezik za SysML, RAAML i C4.

Gaphor slijedi službeni model podataka UML 2.5.1. Tamo gdje su napravljene promjene, modelu je dodan komentar. Osobito gdje m:n odnosi čine podskup 1:n odnosa.

_images/DCE%3ACBCB3DDA-379D-11D9-9FA4-00C00C03A405.svg

01. Uobičajena struktura

1. Osnova

_images/DCE%3AE71AAA3E-45CE-11D7-B5CA-613352B2821F.svg

2. Predlošci

Nije implementirano.

3. Imenski prostori

_images/DCE%3AD2653A36-464C-11D7-AA08-1B85D5275D8A.svg

4. Vrste i mnogostrukost

_images/DCE%3ADD80D3EC-4A86-11D7-B086-133D836EF880.svg

5. Ograničenja

_images/DCE%3A34764E0C-4A89-11D7-B08E-133D836EF880.svg

6. Zavisnosti

_images/DCE%3A4E445586-50A4-11D7-807A-302CB4EF44FD.svg

02. Vrijednosti

1. Literals

_images/3bf6ebec-e20d-11ea-b7ab-f5b4c130f24e.svg

2. Izrazi

_images/DCE%3A33061A3A-4A88-11D7-B08B-133D836EF880.svg

03. Klasifikacija

1. Klasifikatori

_images/DCE%3A90F60210-4B33-11D7-B391-02BBFE4396CE.svg

3. Značajke

_images/DCE%3A539997D4-4B37-11D7-B391-02BBFE4396CE.svg

4. Svojstva

_images/DCE%3A5198F474-4B3D-11D7-B391-02BBFE4396CE.svg

5. Operacije

_images/DCE%3A94ED9700-4B3A-11D7-B391-02BBFE4396CE.svg

7. Instance

_images/DCE%3A039A7C82-4B32-11D7-B391-02BBFE4396CE.svg

04. Jednostavni klasifikatori

1. Vrste podataka

_images/DCE%3A9661A9EE-509B-11D7-811E-AF5893A6470D.svg

3. Sučelja

_images/DCE%3A2D47DA48-50A5-11D7-807A-302CB4EF44FD.svg

05. Strukturirani klasifikatori

1. Strukturirani klasifikatori

_images/DCE%3A3B936BE0-5FD2-11D9-89DE-0050040A5923.svg

2. Ugrađeni klasifikatori

_images/DCE%3A647E10B2-8352-11DD-8EDA-000D936B094A.svg

3. Klase

_images/b136e974-b123-11de-abbf-000d93868322.svg

4. Veze

_images/70258172-e964-11ea-96dc-bf74f1f80424.svg

5. Komponente

_images/DCE%3A4A9E3AF4-A419-11D8-B4C8-00061BC22919.svg

6. Suradnje

_images/e48f0fae-4065-11e0-b382-0019d2b28af0.svg

06. Paketiranje

1. Paketi

_images/DCE%3A2817DA5A-50A1-11D7-9F0D-F80F4B06507D.svg

2. Profili

_images/DCE%3AF4CAA7BA-97B9-11D8-9E36-00C00C03A405.svg

07. Opća ponašanja

1. Ponašanja

_images/DCE%3A30B2FAF8-6679-11D7-A84E-6C8643AD0CA4.svg

2. Događaji

_images/d14d351a-40f1-11e0-9acb-0019d2b28af0.svg

08. State Machines

1. Behavior State Machines

_images/DCE%3AAEC6FCA4-4586-11DA-A04F-00123FE76EBE.svg

09. Aktivnosti

1. Aktivnosti

_images/DCE%3A3A957AF4-8325-11D8-8D1E-00C00C03A405.svg

2. Control Nodes

_images/DCE%3A54022A8C-8325-11D8-8D1E-00C00C03A405.svg

3. Object Nodes

_images/DCE%3A052880F2-8323-11D8-8D1E-00C00C03A405.svg

4. Executable Nodes

_images/c6e0fa86-ea00-11ea-96dc-bf74f1f80424.svg

5. Activity Groups

_images/DCE%3ADA3B1A4E-8405-11D8-82A2-7B88E55A3BEC.svg

10. Radnje

1. Radnje

_images/DCE%3A47C3C202-8325-11D8-8D1E-00C00C03A405.svg

2. Invocation Actions

_images/5c9a2260-38fd-11e0-98cd-0021e9e5a3cd.svg

7. Structural Feature Actions

_images/eca56402-3801-11e0-87bf-0021e9e5a3cd.svg

9. Accept Event Actions

_images/bb8daa5a-3801-11e0-87bf-0021e9e5a3cd.svg

11. Interakcije

1. Interakcije

_images/DCE%3A8E6B58CC-3B13-11D9-83CE-A0BAF22E8A12.svg

2. Lifelines

_images/DCE%3AEA8890EA-3B14-11D9-83CE-A0BAF22E8A12.svg

3. Poruke

_images/DCE%3A14C6D1BC-3B17-11D9-83CE-A0BAF22E8A12.svg

4. Pojavljivanja

_images/6cd26680-55b6-11ea-8c7d-fd01dce16f0a.svg

12. Use Cases

UseCases

_images/DCE%3A71EC6E5E-6690-11D7-A84E-6C8643AD0CA4.svg

13. Deployments

1. Deployments

_images/f4ad7aaa-b13d-11de-9fae-000d93868322.svg

2. Artifacts

_images/DCE%3A1FD159C8-A41B-11D8-BCB9-00061BC22919.svg

3. Čvorovi

_images/DCE%3A954E534C-A41C-11D8-BCB9-00061BC22919.svg

14. Information Flows

_images/5485a3f0-ebea-11eb-8d68-2d4b211d5079.svg

A. Gaphor Specific Constructs

1. Aplikacije stereotipa

_images/DCE%3A8B3C1FAA-B7DE-11D8-8AD1-9AC43285B64C.svg

B. Gaphor profil

In order to provide extra information to the diagram elements (mainly association ends), the Gaphor model has been extended with stereotypes.

_images/9ab5c2dc-3fe1-11de-8375-00224128e79d.svg

Jezici modeliranja sustava

Gaphor implementira dio SysML 1.6 specifikacije.

_images/1bad943e-20e9-11ea-9209-e76c3117c943.svg

Aktivnosti

_images/ac4b90fc-2140-11ea-ab0b-c72c0738acd2.svg

Dodjeljivanja

AllocatedActivityPartition

_images/fb031740-2142-11ea-ab0b-c72c0738acd2.svg

Allocation

_images/662db256-2142-11ea-ab0b-c72c0738acd2.svg

Blokovi

_images/47ca5c16-20e7-11ea-9209-e76c3117c943.svg

Adjunt and Classifier Behavior Properties

_images/bfa2ba84-210b-11ea-ab0b-c72c0738acd2.svg

Bound References

_images/e1591070-210a-11ea-ab0b-c72c0738acd2.svg

Krajevi spojnica

_images/3747bba4-210a-11ea-ab0b-c72c0738acd2.svg

Svojstva

_images/541dcb16-2104-11ea-ab0b-c72c0738acd2.svg

Staze svojstava

_images/4a1ef44a-2105-11ea-ab0b-c72c0738acd2.svg

Vrste svojstava

_images/ac3ce272-210a-11ea-ab0b-c72c0738acd2.svg

Izrazi svojstava

_images/f63737e4-2356-11ea-ab0b-c72c0738acd2.svg

Vrste vrijednosti

_images/6d89dc70-2104-11ea-ab0b-c72c0738acd2.svg

ConstraintBlocks

_images/2b70bf3e-2140-11ea-ab0b-c72c0738acd2.svg

Biblioteke

_images/6ede6852-2145-11ea-ab0b-c72c0738acd2.svg

ModelElements

_images/447ed65c-213e-11ea-ab0b-c72c0738acd2.svg

PortsAndFlows

Actions on Nested Ports

_images/dd8165c6-2112-11ea-ab0b-c72c0738acd2.svg

Port Stereotypes

_images/d86faf8c-210f-11ea-ab0b-c72c0738acd2.svg

Property Value Change Events

_images/aef5d558-2116-11ea-ab0b-c72c0738acd2.svg

Postojeće i potrebne značajke

_images/84aaf232-2117-11ea-ab0b-c72c0738acd2.svg

Tok elementa

_images/7a0704c6-5921-11ec-befa-0456e5e540ed.svg

Uvjeti

_images/b354e9ca-20e1-11ea-9209-e76c3117c943.svg

Risk Analysis and Assessment Modeling Language

Gaphor implements parts of the RAAML 1.0 specification.

_images/5921d168-235f-11ea-ab0b-c72c0738acd2.svg

Core

Core Library/Any Situation

_images/6111d8be-2364-11ea-ab0b-c72c0738acd2.svg

Core Profile/Controlling Measure

_images/1636e506-5f7b-11eb-bf7c-d51178c71081.svg

Core Profile/Relevant To

_images/8ee428ca-5f7a-11eb-bf7c-d51178c71081.svg

Core Profile/Situation

_images/1f4246c8-5f7a-11eb-bf7c-d51178c71081.svg

Core Profile/Violates

_images/de98b04c-5f7b-11eb-bf7c-d51178c71081.svg_images/76c26950-2b50-11ea-99a7-7d79dbbd423e.svg

Opće

Osnovni događaj

_images/2ca042cc-2a7c-11ea-99a7-7d79dbbd423e.svg

General Concepts Library/Abstract Cause

_images/9e1d24fa-2370-11ea-99a7-7d79dbbd423e.svg

General Concepts Library/Abstract Effect

_images/2b69d716-2396-11ea-99a7-7d79dbbd423e.svg

General Concepts Library/Abstract Event

_images/4e3fb614-2366-11ea-ab0b-c72c0738acd2.svg

General Concepts Library/Abstract Failure Mode

_images/43a4a34e-2394-11ea-99a7-7d79dbbd423e.svg

General Concepts Library/Abstract Risk

_images/62b87052-242b-11ea-99a7-7d79dbbd423e.svg

General Concepts Library/Activation

_images/3789cb08-2398-11ea-99a7-7d79dbbd423e.svg

General Concepts Library/Cause

_images/7a90a4f8-2370-11ea-99a7-7d79dbbd423e.svg

General Concepts Library/Dysfunctional Event

_images/c2532de2-2370-11ea-99a7-7d79dbbd423e.svg

General Concepts Library/Effect

_images/2fec63e8-2397-11ea-99a7-7d79dbbd423e.svg

General Concepts Library/Error Propagation

_images/bcfa898e-2399-11ea-99a7-7d79dbbd423e.svg

General Concepts Library/Error Realization

_images/4ca3e422-239a-11ea-99a7-7d79dbbd423e.svg

General Concepts Library/Harm Potential

_images/b662935e-239a-11ea-99a7-7d79dbbd423e.svg

General Concepts Library/Hazard

_images/ff0937c0-239a-11ea-99a7-7d79dbbd423e.svg

General Concepts Library/Scenario

_images/9b1b2828-242a-11ea-99a7-7d79dbbd423e.svg

General Concepts Library/Undesired State

_images/8b368f84-5614-11eb-a85e-18cf5efc6bb9.svg

General Concepts Profile/Detection

_images/028a5afe-242d-11ea-99a7-7d79dbbd423e.svg

General Concepts Profile/Failure State

_images/4b357e5e-242e-11ea-99a7-7d79dbbd423e.svg

General Concepts Profile/Mitigation

_images/e885a252-242d-11ea-99a7-7d79dbbd423e.svg

General Concepts Profile/Prevention

_images/74d658ba-242d-11ea-99a7-7d79dbbd423e.svg

General Concepts Profile/Recommendation

_images/1d77da84-242e-11ea-99a7-7d79dbbd423e.svg

General Concepts Profile/Undeveloped

_images/c92a9cd0-5480-11eb-a85e-18cf5efc6bb9.svg_images/d173abb6-2b50-11ea-99a7-7d79dbbd423e.svg

Metode

FTA/FTA biblioteka/Događaji/Osnovni događaj

_images/2ca042cc-2a7c-11ea-99a7-7d79dbbd423e.svg

FTA/FTA biblioteka/Događaji/Uvjetni događaj

_images/a2210f70-2a7e-11ea-99a7-7d79dbbd423e.svg

FTA/FTA biblioteka/Događaji/Neaktivni događaj

_images/fa2fc8c4-2a7d-11ea-99a7-7d79dbbd423e.svg

FTA/FTA biblioteka/Događaji/Događaji

_images/6c969142-69a5-11eb-9cb6-b14bb5bac2d1.svg

FTA/FTA biblioteka/Događaji/Događaj

_images/c5551376-2a72-11ea-99a7-7d79dbbd423e.svg

FTA/FTA biblioteka/Događaji/Kućni događaj

_images/24547c48-2a7f-11ea-99a7-7d79dbbd423e.svg

FTA/FTA biblioteka/Događaji/Međudogađaj

_images/6d7b2540-2a82-11ea-99a7-7d79dbbd423e.svg

FTA/FTA biblioteka/Događaji/Glavni događaj

_images/a0b44e0a-2a82-11ea-99a7-7d79dbbd423e.svg

FTA/FTA biblioteka/Događaji/Nerazvijeni događaj

_images/4a9d0538-2b0a-11ea-99a7-7d79dbbd423e.svg

FTA/FTA biblioteka/Događaji/Nula događaja

_images/e09392d2-2a7e-11ea-99a7-7d79dbbd423e.svg

FTA/FTA biblioteka/FTA element

_images/bcb9dfd8-2a6f-11ea-99a7-7d79dbbd423e.svg

FTA/FTA biblioteka/FTA biblioteka

_images/b4220604-2a72-11ea-99a7-7d79dbbd423e.svg

FTA/FTA biblioteka/FTA stablo

_images/380fea2e-2a70-11ea-99a7-7d79dbbd423e.svg

FTA/FTA biblioteka/Sklopke/I

_images/83c3bd96-2a80-11ea-99a7-7d79dbbd423e.svg

FTA/FTA biblioteka/Sklopke/Sklopka

_images/f6a2fc9a-2a7c-11ea-99a7-7d79dbbd423e.svg

FTA/FTA biblioteka/Sklopke/S preduvjetom

_images/cccd82be-2a81-11ea-99a7-7d79dbbd423e.svg

FTA/FTA biblioteka/Sklopke/VEĆINA_GLASOVA

_images/07f18afc-2a82-11ea-99a7-7d79dbbd423e.svg

FTA/FTA biblioteka/Sklopke/NE

_images/3b0f9366-2a82-11ea-99a7-7d79dbbd423e.svg

FTA/FTA biblioteka/Sklopke/ILI

_images/0df0c900-2a81-11ea-99a7-7d79dbbd423e.svg

FTA/FTA biblioteka/Sklopke/sprovođenje slijeda

_images/3b992fbe-2a81-11ea-99a7-7d79dbbd423e.svg

FTA/FTA biblioteka/Sklopke/XILI

_images/8dde5e98-2a81-11ea-99a7-7d79dbbd423e.svg

FTA/FTA profil/I

_images/83c3bd96-2a80-11ea-99a7-7d79dbbd423e.svg

FTA/FTA profil/Uvjetni događaj

_images/a2210f70-2a7e-11ea-99a7-7d79dbbd423e.svg

FTA/FTA profil/Neaktivni događaj

_images/fa2fc8c4-2a7d-11ea-99a7-7d79dbbd423e.svg

FTA/FTA profil/Događaj

_images/c5551376-2a72-11ea-99a7-7d79dbbd423e.svg

FTA/FTA profil/Sklopka

_images/f6a2fc9a-2a7c-11ea-99a7-7d79dbbd423e.svg

FTA/FTA profil/Domaći događaj

_images/24547c48-2a7f-11ea-99a7-7d79dbbd423e.svg

FTA/FTA profil/UVJET

_images/cccd82be-2a81-11ea-99a7-7d79dbbd423e.svg

FTA/FTA profil/Međudogađaj

_images/6d7b2540-2a82-11ea-99a7-7d79dbbd423e.svg

FTA/FTA profil/VEĆINA_GLASOVA

_images/07f18afc-2a82-11ea-99a7-7d79dbbd423e.svg

FTA/FTA profil/NE

_images/3b0f9366-2a82-11ea-99a7-7d79dbbd423e.svg

FTA/FTA profil/ILI

_images/0df0c900-2a81-11ea-99a7-7d79dbbd423e.svg

FTA/FTA profil/SPROVOĐENJE_SLIJDA

_images/3b992fbe-2a81-11ea-99a7-7d79dbbd423e.svg

FTA/FTA profil/Glavni događaj

_images/a0b44e0a-2a82-11ea-99a7-7d79dbbd423e.svg

FTA/FTA profil/Prijenos u

_images/e1154abc-2a82-11ea-99a7-7d79dbbd423e.svg

FTA/FTA profil/Prijenos iz

_images/0eacc392-2a83-11ea-99a7-7d79dbbd423e.svg

FTA/FTA profil/Stablo

_images/96ed3524-2a70-11ea-99a7-7d79dbbd423e.svg

FTA/FTA profil/XILI

_images/8dde5e98-2a81-11ea-99a7-7d79dbbd423e.svg

FTA/FTA profil/Nula događaj

_images/e09392d2-2a7e-11ea-99a7-7d79dbbd423e.svg

FTA/FTA

_images/94a4c198-2b50-11ea-99a7-7d79dbbd423e.svg_images/b515fb22-2b50-11ea-99a7-7d79dbbd423e.svg

The C4 Model

The C4 model is a simple visual language to describe the static structure of a software system.

It’s based on the UML language.

_images/2e656718-6cae-11eb-aee1-750f3fed8beb.svg

Načela konstruiranja

Gaphor postoji već nekoliko godina. Tijekom tih godina su programeri Gaphora naučili neke stvari o tome kako ga izgraditi. Gaphor je namijenjen za početnike i za iskusnije korisnike.

Gaphor nije običan uređivač. Gaphor je okruženje za modeliranje. To znači da postoji jezik na kojem se modeli temelje. Jezici se pridržavaju pravila i Gaphor se pokušava pridržavati tih pravila.

Upotrebljivost je vrlo važna. Kao početnik ćeš se lako snaći. Osnovno poznavanje UML-a je dovoljno barem za stvaranje dijagrama klasa.

_images/bf6c3c3b-81b2-11ee-ad01-a85e451ea141.svg

Upute

Kako bi pomogao korisnicima, Gaphor bi trebao pružiti upute gdje god može.

Pomoć s odnosima

Dijagram ima značajku da zasivi sve elemente s kojima se odnos ne može povezati. To pomaže pri odlučivanju gdje se odnos može povezati. Različiti elementi se mogu i nadalje miješati, ali mi se trudimo to pojednostavniti kako bi se izradili konzistentni modeli.

Održavanje sinkronizacije modela

Važan dio modeliranja je stvaranje sustava abstraktno i biti u stanju objasniti to drugima. Kako sustavi postaju kompliciraniji, važno je imati dizajn (model) koji se prikazuje u dijagramima.

Gaphor održava sinkronizaciju modela s dijagramima. Pritom se nekorišteni elementi mogu automatski ukloniti iz modela ako se više ne prikazuju u nijednom dijagramu.

Bez smetnji

Tijekom modeliranja koncentriraj se na problem ili rješenje, a ne na alat. Gaphor će se uplesti što manje moguće. Neće te gnjaviti porukama o greškama zbog modela koji nije „ispravan”.

Izbjegavanje dijaloga

Čineći ispravnu stvar i ne zaokupljati korisnika, Gaphor izbjegava korištenje dijaloga koliko god je to moguće.

Gaphor bi trebao omogućiti da učiniš razumnu stvar (vidi gore) i da te ne smeta raznim pitanjima tijekom tvog rada.

Obavijesti o promjenama

Kada Gaphor radi nešto što nije izravno vidljivo, vidjet ćeš obavijest. Na primjer, element koji je neizravno uklonjen iz modela. Neće te prekidati dijalozima, već će samo pružiti malu obavijest unutar aplikacije. Ako ne želiš prihvatiti promjenu, pritisni poništi.

Uravnoteženo

Iako Gaphor implementira podosta UML 2 modela, on nije potpun. Pokušavamo uspostaviti ravnotežu značajki kako bi odgovarale stručnjacima i početnicima modeliranja.

Kontinuitet

Model koji se izradi bi se trebao moći koristiti u budućnosti. Gaphor to podupire. Brinemo se o kompatibilnosti.

Kompatibilnost sa starijim verzijama

Gaphor može učitavati modele izrađene s Gaphor verzijom 1.0. Važno je da alat uvijek dopušta učitavanje starijih modela.

Višeplatformski

Uložili smo puno truda kako bi Gaphor radio na svim glavnim platformama: Windows, macOS i Linux. Imati Gaphor dostupan na svim platformama bitno je ako model treba dijeliti. Bilo bi užasno ako bi se morao pokrenuti jedan određeni operacijski sustav za otvaranje modela.

Za sada ne podržavamo četvrtu glavnu platformu (web). Izvorne aplikacije pružaju bolje korisničko iskustvo (nakon instaliranja). To će se može promijeniti.

Korisnička interakcija

Gaphor is originally written on Linux. It uses GTK as it’s user interface toolkit. This sort of implies that Gaphor follows the GNOME Human Interface Guidelines (HIG). Gaphor is also a multi-platform application. We try to stay close to the GNOME HIG, but try not to introduce concepts that are not available on Windows and macOS.

User interface components are not generated. We found that UI generation (like many enterprise modeling tools do) provides an awful user experience. We want users to use Gaphor on a regular basis, so we aim for it to be a tool that’s pleasant to look at and easy to work with.

What else?

  • Idempotency Allow the same operation to be applied multiple times. This should not affect the result.

  • Event Driven Gaphor is a user application. It acts to user events. The application uses an internal event dispatches (event bus) to distribute events to interested parties. Everyone should be able to listen to events.

Okruženje

Pregled

Gaphor je izgrađen za jednostavan, uslužno orijentirani način. Aplikacija je podijeljena na niz usluga, kao što su upravljači datoteka, događaja i poništavanja. Te se usluge učitavaju na temelju ulaznih točaka definiranih u datoteci pyproject.toml. Za više informacija o arhitekturi, pogledaj opis za Service Oriented Architecture.

Event driven

Parts of Gaphor communicate with each other through events. Whenever something important happens, for example, an attribute of a model element changes, an event is sent. When other parts of the application are interested in a change, they register an event handler for that event type. Events are emitted though a central broker so you do not have to register on every individual element that can send an event they are interested in. For example, a diagram item could register an event rule and then check if the element that sent the event is actually the event the item is representing. For more information see the full description of the event system.

Transakcijski

Gaphor is transactional, which means it keeps track of the functions it performs as a series of transactions. The transactions work by sending an event when a transaction starts and sending another when a transaction ends. This allows, for example, the undo manager to keep a running log of the previous transactions so that a transaction can be reversed if the undo button is pressed.

Glavne komponente

Glavni dio Gaphora koji se najprije izvršava zove se Application. Gaphor može imati više otvorenih modela u bilo kojem trenutku. Svaki model se čuva u Sesiji. Aktivna je samo jedna instanca aplikacije. Svaka će sesija učitati vlastite usluge definirane kao gaphor.services.

Najistaknutije usluge su:

event_manager

Ovo je središnja komponenta koja se koristi za slanje događaja. Svaka usluga koja radi nešto s događajima (slanje i primanje) ovisi o ovoj komponenti.

file_manager

Učitavanje i spremanje modela vrši se putem ove usluge.

element_factory

The data model itself is maintained in the element factory (gaphor.core.modeling.elementfactory). This service is used to create model elements, as well as to lookup elements or query for a set of elements.

undo_manager

Jedna od najcjenjenijih usluga. Omogućuje korisnicima da tu i tamo pogriješe!

Upravljač poništavanja je transakcijski. Korisničke radnje se spremaju samo ako je transakcija aktivna. Ako je transakcija dovršena (poslana), sprema se nova radnja poništavanja. Transakcije se također mogu vratiti. U tom slučaju se sve promjene reproduciraju izravno. Za više informacija pogledaj opis undo manager.

Service Oriented Architecture

Gaphor has a service oriented architecture. What does this mean? Well, Gaphor is built as a set of small islands (services). Each island provides a specific piece of functionality. For example, we use separate services to load/save models, provide the menu structure, and to handle the undo system.

We define services as entry points in the pyproject.toml. With entry points, applications can register functionality for specific purposes. We also group entry points in to entry point groups. For example, we use the console_scripts entry point group to start an application from the command line.

Usluge

Gaphor is modeled around the concept of services. Each service can be registered with the application and then it can be used by other services or other objects living within the application.

Each service should implement the Service interface. This interface defines one method:

shutdown(self)

Which is called when a service needs to be cleaned up.

We allow each service to define its own methods, as long as the service is implemented too.

Services should be defined as entry points in the pyproject.toml file.

Typically, a service does some work in the background. Services can also expose actions that can be invoked by users. For example, the Ctrl-z key combo (undo) is implemented by the UndoManager service.

A service can also depend on another services. Service initialization resolves these dependencies. To define a service dependency, just add it to the constructor by its name defined in the entry point:

class MyService(Service):

    def __init__(self, event_manager, element_factory):
        self.event_manager = event_manager
        self.element_factory = element_factory
        event_manager.subscribe(self._element_changed)

    def shutdown(self):
        self.event_manager.unsubscribe(self._element_changed)

    @event_handler(ElementChanged)
    def _element_changed(self, event):

Services that expose actions should also inherit from the ActionProvider interface. This interface does not require any additional methods to be implemented. Action methods should be annotated with an @action annotation.

Example: ElementFactory

A nice example of a service in use is the ElementFactory. It is one of the core services.

The UndoManager depends on the events emitted by the ElementFactory. When an important events occurs, like an element is created or destroyed, that event is emitted. We then use an event handler for ElementFactory that stores the add/remove signals in the undo system. Another example of events that are emitted are with UML.Elements. Those classes, or more specifically, the properties, send notifications every time their state changes.

Entry Points

Gaphor uses a main entry point group called gaphor.services.

Services are used to perform the core functionality of the application while breaking the functions in to individual components. For example, the element factory and undo manager are both services.

Plugins can also be created to extend Gaphor beyond the core functionality as an add-on. For example, a plugin could be created to connect model data to other applications. Plugins are also defined as services. For example a new XMI export plugin would be defined as follows in the pyproject.toml:

[tool.poetry.plugins."gaphor.services"]
"xmi_export" = "gaphor.plugins.xmiexport:XMIExport"

Sučelja

Each service (and plugin) should implement the gaphor.abc.Service interface:

class gaphor.abc.Service[source]

Base interface for all services in Gaphor.

abstract shutdown() None[source]

Shutdown the services, free resources.

Another more specialized service that also inherits from gaphor.abc.Service, is the UI Component service. Services that use this interface are used to define windows and user interface functionality. A UI component should implement the gaphor.ui.abc.UIComponent interface:

class gaphor.ui.abc.UIComponent[source]

A user interface component.

abstract close()[source]

Close the UI component.

The component can decide to hide or destroy the UI components.

abstract open()[source]

Create and display the UI components (windows).

shutdown()[source]

Shut down this component.

It’s not supposed to be opened again.

Typically, a service and UI component would like to present some actions to the user, by means of menu entries. Every service and UI component can advertise actions by implementing the gaphor.abc.ActionProvider interface:

class gaphor.abc.ActionProvider[source]

An action provider is a special service that provides actions via @action decorators on its methods (see gaphor/action.py).

Sustav događaja

Sustav događaja u Gaphoru pruža API za rukovanje događajima i za pretplatu na događaje.

U Gaphoru upravljamo pretplatama za rukovanje događajima putem usluge „Upravljač događaja”. Gaphor je izrazito vođen događajima:

  • Promjene u učitanom modelu emitiraju se kao događaji

  • Promjene u dijagramima emitiraju se kao događaji

  • Promjene u korisničkom sučelju emitiraju se kao događaji

Although Gaphor depends heavily on GTK for its user interface, Gaphor is using its own event dispatcher. Events can be structured in hierarchies. For example, an AttributeUpdated event is a subtype of ElementUpdated. If we are interested in all changes to elements, we can also register ElementUpdated and receive all AttributeUpdated events as well.

class gaphor.core.eventmanager.EventManager[source]

Upravljač događaja.

handle(*events: object) None[source]

Send event notifications to registered handlers.

priority_subscribe(handler: Callable[[object], None]) None[source]

Register a handler.

Priority handlers are executed directly. They should not raise other events, cause that can cause a problem in the exection order.

U osnovi osigurava da sve događaje snima upravljač poništavanja.

shutdown() None[source]

Shutdown the services, free resources.

subscribe(handler: Callable[[object], None]) None[source]

Register a handler.

Handlers are triggered (executed) when specific events are emitted through the handle() method.

unsubscribe(handler: Callable[[object], None]) None[source]

Unregister a previously registered handler.

Under the hood events are handled by the Generics library. For more information about how the Generic library handles events see the Generic documentation.

Modeling Languages

Since version 2.0, Gaphor supports the concept of Modeling languages. This allows for development of separate modeling languages separate from the Gaphor core application.

The main language was, and will be UML. Gaphor now also supports a subset of SysML, RAAML and the C4 model.

A modeling language in Gaphor is defined by a class implementing the gaphor.abc.ModelingLanguage abstract base class. The modeling language should be registered as a gaphor.modelinglanguage entry point.

The ModelingLanguage interface is fairly minimal. It allows other services to look up elements and diagram items, as well as a toolbox, and diagram types. However, the responsibilities of a modeling language do not stop there. Parts of functionality will be implemented by registering handlers to a set of generic functions.

But let’s not get ahead of ourselves. What is the functionality a modeling language implementation can offer?

Three functionalities are exposed by a ModelingLanguage instance:

  • A data model (elements) and diagram items

  • Vrste dijagrma

  • Definicija kutija alata

Other functionalities can be extended by adding handlers to the respective generic functions:

  • Spojnice omogućuju spajanje elemenata dijagrama

  • Format/parse model elements to and from a textual representation

  • Copy/paste behavior when element copying is not trivial, for example with more than one element is involved

  • Grupiranje, omogućuje međusobno ugniježđivanje elemenata

  • Ispuštanje, omogućuje povlačenje elemenata iz prikaza stabla u dijagram

  • Pravila automatskog čišćenja za održavanje dosljednosti modela

Modeling languages can also provide new UI components. Those components are not loaded directly when you import a modeling language package. Instead, they should be imported via the gaphor.modules entrypoint.

Modeling language

class gaphor.abc.ModelingLanguage[source]

A model provider is a special service that provides an entrypoint to a model implementation, such as UML, SysML, RAAML.

abstract property diagram_types: Iterable[DiagramType]

Iterate diagram types.

abstract property element_types: Iterable[ElementCreateInfo]

Iterate element types.

abstract lookup_element(name: str) type[Element] | None[source]

Look up a model element type by (class) name.

abstract property name: str

Human-readable name of the modeling language.

abstract property toolbox_definition: ToolboxDefinition

Get structure for the toolbox.

Spojnice

Spojnice se koriste za spajanje elemenata.

Connectors should adhere to the ConnectorProtocol. Normally you would inherit from BaseConnector.

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

Connection adapter for Gaphor diagram items.

Line item line connects with a handle to a connectable item element.

Parametri:
  • line – spojni element

  • element – spojiv element

By convention the adapters are registered by (element, line) – in that order.

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

Determine if items can be connected.

Is the connection allowed at all (during mouse movement for example)?

Returns True if connection is allowed.

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

Connect to an element.

Establish a connection between element and line. Also takes care of disconnects, if required (e.g. 1:1 relationships).

Note that at this point the line may be connected to some other, or the same element. The connection at model level also still exists.

Returns True if a connection is established.

disconnect(handle: Handle) None[source]

Disconnect model level connections.

Break connection, called when dropping a handle on a point where it can not connect.

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

Get item connected to a handle.

Format and parse

Model elements can be formatted to a simple text representation. For example, This is used in the Model Browser. It isn’t a full serialization of the model element.

In some cases it’s useful to parse a text back into an object. This is done when you edit attributes and operations on a class.

Not every format() needs to have an equivalent parse() function.

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

Returns a human readable representation of the model element. In most cases this is just the name, however, properties (attributes) and operations are formatted more extensively:

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

Parse text and populate element. The element is populated with elements from the text. This may mean that new model elements are created as part of the parse process.

Kopiraj i umetni

Copy and paste works out of the box for simple items: one diagram item with one model element (the subject). It leverages the load() and save() methods of the elements to ensure all relevant data is copied.

Sometimes items need more than one model element to work. For example an Association: it has two association ends.

In those specific cases you need to implement your own copy and paste functions. To create such a thing you’ll need to create two functions: one for copying and one for pasting.

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

Create a copy of an element (or list of elements). The returned type should be distinct, so the paste() function can properly dispatch. A copy function normally copies only the element and mandatory related elements. E.g. an Association needs two association ends.

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

Paste previously copied data. Based on the data type created in the copy() function, try to duplicate the copied elements. Returns the newly created item or element.

Gaphor provides some convenience functions:

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

Copy items. The lookup function is used to look up owned elements (shown as child nodes in the Model Browser).

Paste a copy of the Presentation element to the diagram, but try to link the underlying model element. A shallow copy.

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

Paste a copy of both Presentation and model element. A deep copy.

Grupiranje

Grouping is done by dragging one item on top of another, in a diagram or in the tree view.

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

Group an element in a parent element. The grouping can be based on ownership, but other types of grouping are also possible.

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

Remove the grouping from an element. The function needs to check if the provided parent node is the right one.

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

This function tries to determine if grouping is possible, without actually performing a group operation. This is not 100% accurate.

Dropping

Dropping is performed by dragging an element from the tree view and drop it on a diagram. This is an easy way to extend a diagram with already existing model elements.

Alternatively, a presentation item can be dropped on top of another element.

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

The drop function creates a new presentation for an element on the diagram, if the element is not a presentation yet. For relationships, a drop only works if both connected elements are present in the same diagram.

While grouping deals with connecting model elements, dropping deals with creating and placing presentation elements on the right item in a diagram.

Automated model cleanup

Gaphor wants to keep the model in sync with the diagrams.

A little dispatch function is used to determine if a model element can be removed.

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

Determine if a model element can safely be removed.

Property Editor pages

The editor page is constructed from snippets. For example: almost each element has a name, so there is a UI snippet that allows you to edit a name.

Each property page (snippet) should inherit from PropertyPageBase.

class gaphor.diagram.propertypages.PropertyPageBase[source]

A property page which can display itself in a notebook.

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

Create the page (Gtk.Widget) that belongs to the Property page.

Returns the page’s toplevel widget (Gtk.Widget).

Instant (diagram) editor popups

When you double-click on an item in a diagram, a popup can show up, so you can easily change the name.

By default, this works for any named element. You can register your own inline editor function if you need to.

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

Show a small editor popup in the diagram. Makes for easy editing without resorting to the Element editor.

In case of a mouse press event, the mouse position (relative to the element) are also provided.

Protokol povezivanja

U Gaphoru, ako se u dijagramu uspostavi veza između elementa i odnosa, veza se također uspostavlja na semantičkoj razini (model). Iz GUI točke gledišta, događaj otpuštanja gumba je ono što pokreće odluku o dopuštenju veze.

_images/a26e1ce5-0092-11eb-8f71-7fd0147881a5.svg

Provjera dopuštenja veze također treba provjeriti je li valjano stvoriti odnos prema/od istog elementa (poput asocijacija, ali ne generalizacija).

Datotečni format

The root element of Gaphor models is the Gaphor tag, all other elements are contained in this. The Gaphor element delimits the beginning and the end of an Gaphor model.

The idea is to keep the file format as simple and extensible as possible: UML elements (including Diagram) are at the top level with no nesting. A UML element can have two tags: references (ref) and values (val). References are used to point to other UML elements. Values have a value inside (an integer or a string).

Since many references are bi-directional, you’ll find both ends defined in the file (e.g. Package.ownedType - Actor.package, and Diagram.ownedPresentation and UseCaseItem.diagram).

<?xml version="1.0" ?>
<Gaphor version="1.0" gaphor_version="0.3">
  <Package id="1">
    <ownedClassifier>
      <reflist>
        <ref refid="2"/>
        <ref refid="3"/>
        <ref refid="4"/>
      </reflist>
    </ownedClassifier>
  </Package>
  <Diagram id="2">
    <package>
      <ref refid="1"/>
    </package>
    <ownedPresentation>
      <reflist>
        <ref refid="5"/>
        <ref refid="6"/>
      </reflist>
    </ownedPresentation>
  </Diagram>
  <ActorItem id="5">
    <matrix>
      <val>(1.0, 0.0, 0.0, 1.0, 147.0, 132.0)</val>
    </matrix>
    <width>
      <val>38.0</val>
    </width>
    <height>
      <val>60.0</val>
    </height>
    <diagram>
      <ref refid="2"/>
    </diagram>
    <subject>
      <ref refid="3"/>
    </subject>
  </ActorItem>
  <UseCaseItem id="6">
    <matrix>
      <val>(1.0, 0.0, 0.0, 1.0, 341.0, 144.0)</val>
    </matrix>
    <width>
      <val>98.0</val>
    </width>
    <height>
      <val>30.0</val>
    </height>
    <diagram>
      <ref refid="2"/>
    </diagram>
    <subject>
      <ref refid="4"/>
    </subject>
  </UseCaseItem>
  <Actor id="3">
    <name>
      <val>Actor></val>
    </name>
    <package>
      <ref refid="1"/>
    </package>
  </Actor>
  <UseCase id="4">
    <package>
      <ref refid="1"/>
    </package>
  </UseCase>
</Gaphor>

Undo Manager

Undo is a required feature in modern applications. Gaphor is no exception. Having an undo function in place means you can change the model and easily revert to an older state.

Overview of Transactions

The recording and playback of changes in Gaphor is handled by the the Undo Manager. The Undo Manager works transactionally. A transaction must succeed or fail as a complete unit. If the transaction fails in the middle, it is rolled back. In Gaphor this is achieved by the transaction module, which provides a context manager Transaction and a decorator called @transactional.

When transactions take place, they emit events when the top-most transaction begins and is finished. The event notifications are for the begin of the transaction, and the commit of the transaction if it is successful or the rollback of the transaction if it fails.

The Undo Manager only allows changes to be made in a transaction. If a change is made outside a transaction, an exception is raised.

Start of a Transaction

  1. A Transaction object is created.

  2. TransactionBegin event is emitted.

  3. The UndoManager instantiates a new ActionStack which is the transaction object, and adds the undo action to the stack.

Nested transactions are supported to allow a transaction to be added inside of another transaction that is already in progress.

Uspjela transakcija

  1. A TransactionCommit event is emitted

  2. The UndoManager closes and stores the transaction.

Neuspjela transakcija

  1. A TransactionRollback event is emitted.

  2. The UndoManager plays back all the recorded actions, but does not store it.

Transaction API

Napomena

You only require transactions if the undo manager is active.

class gaphor.transaction.Transaction(event_manager, context=None)[source]

The transaction.

On start and end of a transaction an event is emitted. Transactions can be nested. Events are only emitted when the outermost transaction begins and finishes.

Note that transactions are a global construct.

>>> import gaphor.core.eventmanager
>>> event_manager = gaphor.core.eventmanager.EventManager()

Transactions can be nested. If the outermost transaction is committed or rolled back, an event is emitted.

It’s most convenient to use Transaction as a context manager:

>>> with Transaction(event_manager) as ctx:
...     ... # do actions
...     # in case the transaction should be rolled back:
...     ctx.rollback()

Events can be handled programmatically, although this is discouraged:

>>> tx = Transaction(event_manager)
>>> tx.commit()
commit()[source]

Commit the transaction.

The transaction is closed. A TransactionCommit event is emitted. If the transaction needs to be rolled back, a TransactionRollback event is emitted instead.

classmethod in_transaction() bool[source]

Are you running inside a transaction?

classmethod mark_rollback()[source]

Mark the transaction for rollback.

This operation itself will not close the transaction, instead it will allow you to elegantly revert changes.

rollback()[source]

Roll-back the transaction.

First, the transaction is closed. A TransactionRollback event is emitted.

gaphor.transaction.transactional(func)[source]

The transactional decorator makes a function transactional. Events are emitted through the (global) subscribers set.

>>> @transactional
... def foo():
...     pass

It is preferred to use the Transaction context manager. The context manager emits events in the context of the session in scope, whereas the @transactional decorator emits a global event which is sent to the active session.

class gaphor.event.TransactionBegin(context)[source]

This event denotes the beginning of a transaction.

Nested (sub-) transactions should not emit this signal.

class gaphor.event.TransactionCommit(context)[source]

This event is emitted when a transaction (toplevel) is successfully committed.

class gaphor.event.TransactionRollback(context)[source]

This event is emitted to tell the operation has failed.

If a set of operations fail (e.i. due to an exception) the transaction should be marked for rollback.

Reference