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.

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.
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.
Raspored sučelja Gaphora je podijeljen u četiri odjeljka, naime:
Preglednik modela
Kutija alata za elemente dijagrama
Dijagrami
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:
Elementi
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:
Spajanje s elementom – crvena ručka
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
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
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 ()
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).
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 ().
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.
Optionally you can run the auto-layout ( → 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” () u traci zaglavlja.
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.
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 theexamples
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.
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.
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:
Double-click on the actor to pop up the rename dialog, and replace User with Barista.
Update the name of the oval Use Case from Use Case #1 to Brew espresso.
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.
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.
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.
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.
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.
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.
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.
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.
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
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:
Here is a simple example of how to change the background color of a class:
class {
background-color: beige;
}
Or change the color of a component, only when it’s nested in a node:
node component {
background-color: skyblue;
}
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;
}
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. |
|
Any component item which is a descendant of a node. |
|
A component item which is a child of a node. |
|
A generalization item with a subject present. |
|
A class with name „Foo”. |
|
A diagram with a name starting with „draft”. |
|
A diagram with a name ends with „draft”. |
|
A diagram with a name containing the text „draft”. |
|
A diagram with a name of „draft” or „item”. |
|
A diagram with a name is „draft” or starts with „draft-„. |
|
The focused item. Other pseudo classes are:
|
|
A node containing no child nodes in the diagram. |
|
Refers to the diagram itself. This is only applicable for the diagram |
|
A node is the first element among a group of sibling. |
|
The item contains any of the provided selectors. E.g. |
|
Match any of the provided selectors. E.g. |
|
Negate the selector. E.g. |
|
Provide extra content after a text.
Only the |
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-familyon
diagram`, it will propagate down
to the items contained in the diagram.
Boje¶
|
Primjeri:
|
|
Color used for lines. (inherited) |
|
Color for text. (inherited) Zastarijelo od verzije 2.23.0: Use color if possible. |
|
Faktor neprozirnosti 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¶
|
A single font name (e.g. |
|
An absolute size (e.g. |
|
Either |
|
Either |
|
Either |
|
|
|
Okomiti položaj teksta.
|
|
Postavi okomiti razmak za ikonske elemente (aktori, početno stanje). Primjer: |
|
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 vrijednostix-small
,small
,medium
,large
ix-large
.
Crtanje i razmaci¶
|
Radijus pravokutnika: |
|
Stil crtica: |
|
Poravnanje sadržaja za okvire.
|
|
|
|
Set the width for lines: |
|
Postavi minimalnu visinu elementa: |
|
Postavi minimalnu širinu elementa: |
|
CSS odmak (gore, desno, dolje, lijevo). Primjer: |
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 udijagramu
. Iskrivljenost linija se može odrediti faktorom u rasponu od -2 do 2.
Pseudo elements¶
Currently, only the ::after
pseudo element is supported.
|
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. |
UML |
Klase |
sve veze |
|
UML |
Komponente |
Uređaj/čvor |
|
UML |
Radnje |
Čvor odluke/sjedinjavanja |
|
UML |
Radnje |
Čvor grananja/spajanja |
|
UML |
Radnje |
Traka |
|
UML |
Interakcije |
Refleksivna poruka |
|
UML |
Stanja |
Početno pseudostanje |
|
UML |
Stanja |
Povijest pseudostanja |
|
UML |
Profili |
Metaklasa |
|
C4 model |
C4 model |
Osoba |
|
C4 model |
C4 model |
Softverski sustav |
|
C4 model |
C4 model |
Komponenta |
|
C4 model |
C4 model |
Kontejner |
|
C4 model |
C4 model |
Kontejner: Baza podataka |
|
SysML |
Blokovi |
Vrsta vrijednosti |
|
SysML |
Blokovi |
Osnovni element |
|
SysML |
Uvjeti |
Izvedeni uvjet |
|
RAAML |
FTA (analiza kvarova) |
jedan od I/ILI/… sklopova |
|
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 ( → 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 */
}
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;
}
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;
}
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;
}
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}"
}
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
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
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")
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")
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")
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")
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:
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.
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.
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 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:
Application (global) services (
gaphor.appservices
)Session specific services (
gaphor.services
)Modeling languages (
gaphor.modelinglanguages
)(Sub)command line parsers (
gaphor.argparsers
)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
Since plugins are installed with your system Python version, it’s important that plugins are pure Python and do not contain compiled C code.
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 ( → 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
As stated before, a plugin should implement the
Service
interface. It also implementsActionProvider
, saying it has some actions to be performed by the user.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.
All action defined in this service are registered.
Each service has a
shutdown()
method. This allows the service to perform some cleanup when it’s shut down.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:
GNOME Builder, ideal for “drive by” contributions.
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
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.
Instaliraj flatpak-builder
sudo apt-get install flatpak-builder
Instaliraj GNOME SDK
flatpak install flathub org.gnome.Sdk 43
Kopiraj Flathub repozitorij i instaliraj potrebni SDK:
git clone https://github.com/flathub/org.gaphor.Gaphor.git cd org.gaphor.Gaphor make setup
Izgradi Gaphor Flatpak
make
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:
Instaliraj Homebrew
Otvori terminal i izvrši:
brew install python3 gobject-introspection gtk4 gtksourceview5 libadwaita adwaita-icon-theme graphviz
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.
Follow the instructions for settings up a development environment above
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:
Visual C++ build tools workload for Visual Studio 2022 Build Tools
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.
Install the latest Python version using the official installer.
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.
Install from Chocolately with administrator PowerShell:
choco install graphviz
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:
Open
__main__.py
file fromgaphor
folderAdd a breakpoint on line
main(sys.argv)
In the menu, select Run → Start debugging
Choose Select module from the list
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:
Idi na https://github.com/gaphor/gaphor
Pritisni padajući izbornik za kod i odaberi opciju Otvori pomoću Codespaces.
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¶
Download and install VNC viewer of your choice (e.g. realvnc)
Specify remote hostname as localhost and port as 5901 and connect VNC. The port number should be same as specified in attribute vncPort
Upon debugging/running Gaphor the familiar Graphic window should be displayed in VNC view
Using noVNC viewer on the Browser¶
This is based on noVNC application
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
A noVNC window will open, click on Connect and provide password as vscode. The password should be same as specified in attribute password
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.
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 model: RepositoryProtocol¶
The owning model, raises
TypeError
when model is not set.
- unlink() None [source]¶
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¶
OCL-style methods¶
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 aDiagram
.Subtypes of
Presentation
should implement thegaphas.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 anupdate(context: UpdateContext)
method, it’s invoked. Constraints are solved.
Protocols¶
- class gaphor.core.modeling.element.RepositoryProtocol(*args, **kwargs)[source]¶
-
- 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_
.
- class gaphor.core.modeling.element.EventWatcherProtocol(*args, **kwargs)[source]¶
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.
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.
01. Uobičajena struktura¶
1. Osnova¶
2. Predlošci¶
Nije implementirano.
3. Imenski prostori¶
4. Vrste i mnogostrukost¶
5. Ograničenja¶
6. Zavisnosti¶
02. Vrijednosti¶
1. Literals¶
2. Izrazi¶
03. Klasifikacija¶
1. Klasifikatori¶
3. Značajke¶
4. Svojstva¶
5. Operacije¶
7. Instance¶
04. Jednostavni klasifikatori¶
1. Vrste podataka¶
3. Sučelja¶
05. Strukturirani klasifikatori¶
1. Strukturirani klasifikatori¶
2. Ugrađeni klasifikatori¶
3. Klase¶
4. Veze¶
5. Komponente¶
6. Suradnje¶
06. Paketiranje¶
1. Paketi¶
2. Profili¶
07. Opća ponašanja¶
1. Ponašanja¶
2. Događaji¶
08. State Machines¶
1. Behavior State Machines¶
09. Aktivnosti¶
1. Aktivnosti¶
2. Control Nodes¶
3. Object Nodes¶
4. Executable Nodes¶
5. Activity Groups¶
10. Radnje¶
1. Radnje¶
2. Invocation Actions¶
7. Structural Feature Actions¶
9. Accept Event Actions¶
11. Interakcije¶
1. Interakcije¶
2. Lifelines¶
3. Poruke¶
4. Pojavljivanja¶
12. Use Cases¶
UseCases¶
13. Deployments¶
1. Deployments¶
2. Artifacts¶
3. Čvorovi¶
14. Information Flows¶
A. Gaphor Specific Constructs¶
1. Aplikacije stereotipa¶
B. Gaphor profil¶
In order to provide extra information to the diagram elements (mainly association ends), the Gaphor model has been extended with stereotypes.
Jezici modeliranja sustava¶
Gaphor implementira dio SysML 1.6 specifikacije.
Aktivnosti¶
Dodjeljivanja¶
AllocatedActivityPartition¶
Allocation¶
Blokovi¶
Adjunt and Classifier Behavior Properties¶
Bound References¶
Krajevi spojnica¶
Svojstva¶
Staze svojstava¶
Vrste svojstava¶
Izrazi svojstava¶
Vrste vrijednosti¶
ConstraintBlocks¶
Biblioteke¶
ModelElements¶
PortsAndFlows¶
Actions on Nested Ports¶
Port Stereotypes¶
Property Value Change Events¶
Postojeće i potrebne značajke¶
Tok elementa¶
Uvjeti¶
Risk Analysis and Assessment Modeling Language¶
Gaphor implements parts of the RAAML 1.0 specification.
Core¶
Core Library/Any Situation¶
Core Profile/Controlling Measure¶
Core Profile/Relevant To¶
Core Profile/Situation¶
Core Profile/Violates¶
Opće¶
Osnovni događaj¶
General Concepts Library/Abstract Cause¶
General Concepts Library/Abstract Effect¶
General Concepts Library/Abstract Event¶
General Concepts Library/Abstract Failure Mode¶
General Concepts Library/Abstract Risk¶
General Concepts Library/Activation¶
General Concepts Library/Cause¶
General Concepts Library/Dysfunctional Event¶
General Concepts Library/Effect¶
General Concepts Library/Error Propagation¶
General Concepts Library/Error Realization¶
General Concepts Library/Harm Potential¶
General Concepts Library/Hazard¶
General Concepts Library/Scenario¶
General Concepts Library/Undesired State¶
General Concepts Profile/Detection¶
General Concepts Profile/Failure State¶
General Concepts Profile/Mitigation¶
General Concepts Profile/Prevention¶
General Concepts Profile/Recommendation¶
General Concepts Profile/Undeveloped¶
Metode¶
FTA/FTA biblioteka/Događaji/Osnovni događaj¶
FTA/FTA biblioteka/Događaji/Uvjetni događaj¶
FTA/FTA biblioteka/Događaji/Neaktivni događaj¶
FTA/FTA biblioteka/Događaji/Događaji¶
FTA/FTA biblioteka/Događaji/Događaj¶
FTA/FTA biblioteka/Događaji/Kućni događaj¶
FTA/FTA biblioteka/Događaji/Međudogađaj¶
FTA/FTA biblioteka/Događaji/Glavni događaj¶
FTA/FTA biblioteka/Događaji/Nerazvijeni događaj¶
FTA/FTA biblioteka/Događaji/Nula događaja¶
FTA/FTA biblioteka/FTA element¶
FTA/FTA biblioteka/FTA biblioteka¶
FTA/FTA biblioteka/FTA stablo¶
FTA/FTA biblioteka/Sklopke/I¶
FTA/FTA biblioteka/Sklopke/Sklopka¶
FTA/FTA biblioteka/Sklopke/S preduvjetom¶
FTA/FTA biblioteka/Sklopke/VEĆINA_GLASOVA¶
FTA/FTA biblioteka/Sklopke/NE¶
FTA/FTA biblioteka/Sklopke/ILI¶
FTA/FTA biblioteka/Sklopke/sprovođenje slijeda¶
FTA/FTA biblioteka/Sklopke/XILI¶
FTA/FTA profil/I¶
FTA/FTA profil/Uvjetni događaj¶
FTA/FTA profil/Neaktivni događaj¶
FTA/FTA profil/Događaj¶
FTA/FTA profil/Sklopka¶
FTA/FTA profil/Domaći događaj¶
FTA/FTA profil/UVJET¶
FTA/FTA profil/Međudogađaj¶
FTA/FTA profil/VEĆINA_GLASOVA¶
FTA/FTA profil/NE¶
FTA/FTA profil/ILI¶
FTA/FTA profil/SPROVOĐENJE_SLIJDA¶
FTA/FTA profil/Glavni događaj¶
FTA/FTA profil/Prijenos u¶
FTA/FTA profil/Prijenos iz¶
FTA/FTA profil/Stablo¶
FTA/FTA profil/XILI¶
FTA/FTA profil/Nula događaj¶
FTA/FTA¶
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.
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.
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.Element
s. 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:
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.
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:
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.
- 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.
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.
Editor pages, shown in the collapsible pane on the right side
Special diagram interactions
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 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 itemelement
.- 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.
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.
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
. Thelookup
function is used to look up owned elements (shown as child nodes in the Model Browser).
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.
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.
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
.
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.
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¶
A
Transaction
object is created.TransactionBegin
event is emitted.The
UndoManager
instantiates a newActionStack
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¶
A
TransactionCommit
event is emittedThe
UndoManager
closes and stores the transaction.
Neuspjela transakcija¶
A
TransactionRollback
event is emitted.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, aTransactionRollback
event is emitted instead.
- 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.
Reference¶
A Framework for Undoing Actions in Collaborative Systems (Okruženje za poništavanje radnji u kolaborativnim sustavima)
Undoing Actions in Collaborative Work: Framework and Experience (Poništavanje radnji u zajedničkom radu: okruženje i iskustvo)
Implementing a Selective Undo Framework in Python (Implementacija okruženja selektivnog poništavanja u Pythonu)