Merge branch 'master' into i18n
7
.flake8
Normal file
@ -0,0 +1,7 @@
|
||||
[flake8]
|
||||
ignore = E203, E266, E501, W503, F403, F401
|
||||
max-line-length = 88
|
||||
max-complexity = 18
|
||||
select = B,C,E,F,W,T4,B9
|
||||
exclude = uml2.py, msgfmt.py, pygettext.py, .venv, flatpak, build, dist, .eggs
|
||||
show-source = True
|
1
.gitignore
vendored
@ -16,6 +16,7 @@ tmp.gaphor
|
||||
pip-wheel-metadata
|
||||
|
||||
# Virtual env stuff
|
||||
.env/
|
||||
.venv/
|
||||
.vscode/
|
||||
.python-version
|
||||
|
@ -8,3 +8,21 @@ repos:
|
||||
rev: v0.740
|
||||
hooks:
|
||||
- id: mypy
|
||||
# - repo: https://gitlab.com/pycqa/flake8
|
||||
# rev: 3.7.9
|
||||
# hooks:
|
||||
# - id: flake8
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v2.4.0
|
||||
hooks:
|
||||
- id: check-toml
|
||||
- id: check-yaml
|
||||
- repo: https://github.com/asottile/seed-isort-config
|
||||
rev: v1.9.3
|
||||
hooks:
|
||||
- id: seed-isort-config
|
||||
- repo: https://github.com/pre-commit/mirrors-isort
|
||||
rev: v4.3.21
|
||||
hooks:
|
||||
- id: isort
|
||||
additional_dependencies: [toml]
|
||||
|
@ -4,7 +4,4 @@ sphinx:
|
||||
configuration: docs/conf.py
|
||||
python:
|
||||
install:
|
||||
- method: pip
|
||||
path: .
|
||||
extra_requirements:
|
||||
- docs
|
||||
- requirements: docs/requirements.txt
|
||||
|
22
README.md
@ -104,17 +104,23 @@ To setup a development environment in Windows:
|
||||
1) Run ``C:\msys64\mingw64.exe`` - a terminal window should pop up
|
||||
```bash
|
||||
$ pacman -Suy
|
||||
$ pacman -S mingw-w64-x86_64-gtk3 mingw-w64-x86_64-python3-gobject mingw-w64-x86_64-python3-cairo
|
||||
$ pacman -S mingw-w64-x86_64-python3-pip mingw-w64-x86_64-python3-lxml
|
||||
$ pacman -S git mingw-w64-x86-64-gcc mingw-w64-x86-64-gtk3 \
|
||||
mingw-w64-x86-64-pkg-config mingw-w64-x86-64-cairo \
|
||||
mingw-w64-x86-64-gobject-introspection mingw-w64-x86-64-python3 \
|
||||
mingw-w64-x86-64-python3-importlib-metadata mingw-w64-x86-64-python3-gobject \
|
||||
mingw-w64-x86-64-python3-cairo mingw-w64-x86-64-python3-pip \
|
||||
mingw-w64-x86-64-python3-setuptools mingw-w64-x86-64-python3-zope.interface \
|
||||
mingw-w64-x86-64-python3-coverage mingw-w64-x86-64-python3-pytest
|
||||
```
|
||||
Install git if it isn't already installed in msys2 with `pacman -S git`
|
||||
|
||||
Ensure `/mingw64/bin` is added to your `PATH`:
|
||||
```bash
|
||||
$ export PATH=/mingw64/bin:$PATH
|
||||
```
|
||||
|
||||
[Clone the repository](https://help.github.com/en/articles/cloning-a-repository).
|
||||
[Clone the
|
||||
repository](https://help.github.com/en/github/creating-cloning-and-archiving-repositories/cloning-a-repository).
|
||||
|
||||
```bash
|
||||
$ cd gaphor
|
||||
$ source ./venv -S
|
||||
@ -143,13 +149,14 @@ $ pyenv install 3.x.x
|
||||
Where 3.x.x is replaced by the latest stable version of Python.
|
||||
|
||||
Next install the Gaphor prerequisites by installing the gobject introspection
|
||||
and cairo build dependencies, for example in Ubuntu execute:
|
||||
and cairo build dependencies, for example, in Ubuntu execute:
|
||||
|
||||
```bash
|
||||
$ sudo apt-get install -y python3-dev python3-gi python3-gi-cairo
|
||||
gir1.2-gtk-3.0 libgirepository1.0-dev libcairo2-dev
|
||||
```
|
||||
[Clone the repository](https://help.github.com/en/articles/cloning-a-repository).
|
||||
[Clone the
|
||||
repository](https://help.github.com/en/github/creating-cloning-and-archiving-repositories/cloning-a-repository).
|
||||
|
||||
```
|
||||
$ cd gaphor
|
||||
@ -164,7 +171,8 @@ To setup a development environment with macOS:
|
||||
```bash
|
||||
$ brew install python3 gobject-introspection gtk+3
|
||||
```
|
||||
[Clone the repository](https://help.github.com/en/articles/cloning-a-repository).
|
||||
[Clone the
|
||||
repository](https://help.github.com/en/github/creating-cloning-and-archiving-repositories/cloning-a-repository).
|
||||
```
|
||||
$ cd gaphor
|
||||
$ source ./venv
|
||||
|
@ -22,6 +22,12 @@ jobs:
|
||||
pre-commit install-hooks
|
||||
pre-commit run --all-files
|
||||
|
||||
# flake8 pre-commit hook is causing errors
|
||||
# try again once new version of pyflakes is released (> 2.1.1)
|
||||
pip install git+https://github.com/PyCQA/pyflakes.git@1911c203a13826d2eb03d582d60874b91e36f4fc --upgrade
|
||||
pip install flake8==3.7.9
|
||||
flake8 gaphor
|
||||
|
||||
- job: Windows
|
||||
dependsOn: Lint
|
||||
condition: succeeded()
|
||||
|
@ -17,8 +17,8 @@
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
from pathlib import Path
|
||||
|
||||
from tomlkit import parse
|
||||
from recommonmark.transform import AutoStructify
|
||||
from tomlkit import parse
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
@ -99,7 +99,7 @@ html_theme = "sphinx_rtd_theme"
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ["_static"]
|
||||
html_static_path = []
|
||||
|
||||
# Custom sidebar templates, must be a dictionary that maps document names
|
||||
# to template names.
|
||||
@ -193,6 +193,7 @@ epub_exclude_files = ["search.html"]
|
||||
# Example configuration for intersphinx: refer to the Python standard library.
|
||||
intersphinx_mapping = {"https://docs.python.org/": None}
|
||||
|
||||
|
||||
# AutoStructify for Recommonmark
|
||||
def setup(app):
|
||||
app.add_config_value(
|
||||
|
@ -1,11 +1,10 @@
|
||||
# Connection Protocol
|
||||
|
||||
In Gaphor, if a connection is made on a diagram between an element and a
|
||||
relationship, the connection is also made at semantic level (the model).
|
||||
From a GUI point of view it all starts with a button release event.
|
||||
|
||||
With "item" I refer to objects in a diagram (graphical), with
|
||||
"element" I refer to semantic (model) objects.
|
||||
relationship, the connection is also made at semantic level (the model). From a
|
||||
GUI point of view, a button release event is what kicks of the decision whether
|
||||
the connection is allowed. Please reference the page on [Items and
|
||||
Elements](item.md) if you need a reminder on the difference between the two.
|
||||
|
||||
```eval_rst
|
||||
Is relation with this element allowed?
|
||||
|
@ -1,80 +0,0 @@
|
||||
# Custom Python Installation Location
|
||||
|
||||
This page is based on custom installation locations
|
||||
<http://peak.telecommunity.com/DevCenter/EasyInstall>, from the PEAK site.
|
||||
|
||||
## Unix/Linux
|
||||
|
||||
1. Create `$HOME/.pydistutils.cfg`:
|
||||
|
||||
[install]
|
||||
install_lib = ~/.py-site-packages
|
||||
install_scripts = ~/bin
|
||||
|
||||
2. Create (or extend) the PYTHONPATH environment variable (for (ba)sh):
|
||||
:
|
||||
|
||||
export PYTHONPATH=~/.py-site-packages
|
||||
|
||||
3. Run setup.py script to fetch and install dependencies :
|
||||
|
||||
python setup.py install
|
||||
|
||||
Prefix ~/.py-site-packages can be changed to something more suitable
|
||||
for your setup.
|
||||
|
||||
``` note:: For Linux users: Make sure you have the python-dev package installed for your Python version, as some code needs to be compiled (those are packages Gaphor depends on, not Gaphor itself).
|
||||
```
|
||||
|
||||
``` note:: For Ubuntu Linux users: Make sure you have the build-essential package installed. This package installs header files and what more, required to compile the C-extensions of zope.interface.
|
||||
```
|
||||
|
||||
## Windows
|
||||
|
||||
``` note:: For Windows users it may be simpler to just forget about custom installation locations. Just follow the instructions on [wiki:Win32] and you should be set.
|
||||
```
|
||||
|
||||
The Windows installation is almost the same as for Unix.
|
||||
|
||||
Replace yourname with your login name.
|
||||
|
||||
1. Distutils requires a HOME variable where it can find the
|
||||
configuration file. So in your Control Panel -> System -> Advanced
|
||||
-> Environment Variables add the following:
|
||||
|
||||
HOME=C:\Documents and Settings\yourname\Home
|
||||
|
||||
2. Create a directory C:\Documents and Settings\yourname\Home. Also
|
||||
create %HOME%\py-site-packages.
|
||||
3. Eventually add the Python directory to your PATH (default is
|
||||
C:\\Python26)
|
||||
4. Create (or extend) PYTHONPATH variable:
|
||||
|
||||
PYTHONPATH=%HOME%\py-site-packages
|
||||
|
||||
5. Create a file %HOME%\pydistutils.cfg with the following content:
|
||||
|
||||
[install]
|
||||
install_lib=$home\py-site-packages
|
||||
install_scripts=$home\bin
|
||||
|
||||
[build]
|
||||
compiler=mingw32
|
||||
|
||||
Now you should be able to do python setup.py install from the command
|
||||
line.
|
||||
|
||||
If you are a developer you should definitely install MinGW from
|
||||
<http://mingw.org> and add MinGW's bin directory to your path.
|
||||
|
||||
For a good Subversion client for Windows have a look at
|
||||
[TortoiseSVN](http://tortoisesvn.tigris.org/).
|
||||
|
||||
## Mac OS X
|
||||
|
||||
Mac OS X is quite simple: place the following in your
|
||||
`$HOME/.pydistutils.cfg`:
|
||||
|
||||
[install]
|
||||
install_lib = ~/Library/Python/$py_version_short/site-packages
|
||||
install_scripts = ~/bin
|
@ -1,65 +1,72 @@
|
||||
# Description of Gaphors data model
|
||||
# Data Model
|
||||
|
||||
Gaphor is an UML tool. In order to keep as close as possible to the UML
|
||||
specification the data model is based on the UML Metamodel. Since the
|
||||
OMG has an XMI (XML) specification of the metamodel, the easiest way to
|
||||
do that is to generate the code directly from the model. Doing this
|
||||
raises two issues:
|
||||
specification the Gaphor data model is based on the UML Metamodel. The Object
|
||||
Management Group (OMG), the not-for-profit technology standards consortium that
|
||||
governs UML, has a XML Metadata Interchange (XMI) file describing the
|
||||
metamodel. Therefore, the easiest way to keep Gaphor consistent with UML would
|
||||
be to to generate Gaphor's data model code directly from this UML metamodel in
|
||||
XMI. There are two challenges with this approach:
|
||||
|
||||
1. There are more attributes defined in the data model than we will
|
||||
use.
|
||||
2. How do we check if the model is consistent?
|
||||
1. There are more attributes defined in the data model than we will use,
|
||||
unless Gaphor got to the point where it 100% implemented the UML specification.
|
||||
2. There are no consistency rules in the [UML XMI
|
||||
definition](https://www.omg.org/spec/UML/20131001/UML.xmi).
|
||||
|
||||
The first point is not such a problem: attributes we don't use don't
|
||||
consume memory.
|
||||
The first point ends up not being much of a problem: attributes we don't use
|
||||
don't consume memory.
|
||||
|
||||
There are no consistency rules in the XML definition, we have to get
|
||||
them from the UML syntax description. It is probably best to create a
|
||||
special consistency module that checks the model and reports errors.
|
||||
For the second point, we have to get the model consistency rules directly from
|
||||
the [UML Specification](https://www.omg.org/spec/UML/2.5/PDF). Our approach is
|
||||
to create a special consistency module that checks the model and reports
|
||||
errors.
|
||||
|
||||
In the UML metamodel all classes are derived from Element <uml_element>. So all
|
||||
we have to do is create a substitute for Element <uml_element> that gives some
|
||||
behaviour to the data objects.
|
||||
In the UML metamodel all classes are derived from `Element`. So we have created
|
||||
a substitute for `Element` that gives some behaviour to the data objects.
|
||||
|
||||
The data model is described in Python. Since the Python language
|
||||
doesn't make a difference between classes and objects, we can define
|
||||
the possible attributes that an object of a particular kind can have in
|
||||
a dictionary (name-value map) at class level. If a value is set, the
|
||||
object checks if an attribute exists in the class' dictionary (and the
|
||||
parents dictionary). If it does, the value is assigned, if it doesn't
|
||||
an exception is raised.
|
||||
Gaphor's data model is implemented in Python like the rest of the
|
||||
application. Since the Python language doesn't make a difference between
|
||||
classes and objects, we can define the possible attributes that an object of
|
||||
a particular kind can have in a dictionary (name-value map) at class level.
|
||||
If a value is set, the object checks if an attribute exists in the class'
|
||||
dictionary (and the parent's dictionary). If the attribute exists, the value
|
||||
is assigned, if it doesn't exist then an exception is raised.
|
||||
|
||||
## Bidirectional References
|
||||
|
||||
But how, you might wonder, do you handle bidirectional references
|
||||
(object one references object two and vice versa)? Well, this is
|
||||
basically the same as the uni-directional reference. Only now we need to
|
||||
add some extra information to the dictionary at class level. We just
|
||||
define an extra field that gives us the name of the opposite reference
|
||||
and voila, we can create bi-directional references. You should check out
|
||||
the code in `gaphor/UML/element.py` for more details.
|
||||
If two objects need to reference each other, a bidirectional reference
|
||||
is needed to be supported in Gaphor. This works very similar to the
|
||||
uni-directional reference that is stored in a dictionary at the class level.
|
||||
We add some extra information to the dictionary at class
|
||||
level to make relationship bidirectional. The information is stored in an
|
||||
extra field that gives us the name of the opposite reference. and voila, we
|
||||
can create bi-directional references. Please reference
|
||||
`gaphor/UML/element.py` for more details.
|
||||
|
||||
## Implementation
|
||||
|
||||
This will allow the user to assign a value to an instance of `Element`
|
||||
with name `name`. If no value is assigned before the value is requested,
|
||||
it returns and empty string '':
|
||||
Below is an example of the implementation that allows the user to assign a
|
||||
value to an instance of `Element` with name `name`. If no value is assigned
|
||||
before the value is requested, it returns and empty string '':
|
||||
|
||||
m = Class()
|
||||
print(m.name) # Returns ''
|
||||
m.name = 'MyName'
|
||||
print(m.name) # Returns 'MyName'
|
||||
```python
|
||||
m = Class()
|
||||
print(m.name) # Returns ''
|
||||
m.name = 'MyName'
|
||||
print(m.name) # Returns 'MyName'
|
||||
|
||||
m = Element()
|
||||
c = Comment()
|
||||
print(m.comment) # Returns an empty list '[]'
|
||||
print(c.annotatedElement) # Returns an empty list '[]'
|
||||
m.comment = c # Add 'c' to 'm.comment' and add 'm' to 'c.annotatedElement'
|
||||
print(m.comment) # Returns a list '[c]'
|
||||
print(c.annotatedElement) # Returns a list '[m]'
|
||||
m = Element()
|
||||
c = Comment()
|
||||
print(m.comment) # Returns an empty list '[]'
|
||||
print(c.annotatedElement) # Returns an empty list '[]'
|
||||
m.comment = c # Add 'c' to 'm.comment' and add 'm' to 'c.annotatedElement'
|
||||
print(m.comment) # Returns a list '[c]'
|
||||
print(c.annotatedElement) # Returns a list '[m]'
|
||||
```
|
||||
|
||||
All this wisdom is defined in the data-models base class: `Element`. The
|
||||
datamodel itself code is generated.
|
||||
This behavior is defined in the data model's base class: `Element`. The code
|
||||
for the data model is stored in uml2.py and is generated from the
|
||||
`uml2.gaphor` Gaphor model file.
|
||||
|
||||
## Extensions to the Data Model
|
||||
|
||||
|
@ -1,45 +1,9 @@
|
||||
# Event system
|
||||
# Event System
|
||||
|
||||
Generic library provides `generic.event` module which helps you
|
||||
implement event systems in your application. By event system I mean an
|
||||
API for *subscribing* for some types of events and to *handle* those
|
||||
events so previously subscribed *handlers* are being executed.
|
||||
The Generic library provides the `generic.event` module which is used to
|
||||
implement the event system in Gaphor. This event system in Gaphor provides an
|
||||
API to *subscribe* to events and to then *handle* those events so previously
|
||||
subscribed *handlers* are being executed.
|
||||
|
||||
## Basic usage
|
||||
|
||||
First you need to describe event types you want to use in your
|
||||
application, `generic.event` dispatches events to corresponding handlers
|
||||
by inspecting events' types, so it's natural to model those as
|
||||
classes:
|
||||
|
||||
class CommentAdded:
|
||||
def __init__(self, post_id, comment):
|
||||
self.post_id = post_id
|
||||
self.comment = comment
|
||||
|
||||
Now you want to register handler for your event type:
|
||||
|
||||
from generic.event import subscriber
|
||||
|
||||
@subscriber(CommentAdded)
|
||||
def print_comment(ev):
|
||||
print "Got new comment: %s" % ev.comment
|
||||
|
||||
Then you just call `generic.event.handle` function with `CommentAdded`
|
||||
instance as its argument:
|
||||
|
||||
from generic.event import handle
|
||||
|
||||
handle(CommentAdded(167, "Hello!")) # prints `Got new comment: Hello!`
|
||||
|
||||
This is how it works.
|
||||
|
||||
## Event inheritance
|
||||
|
||||
## Using per-application event API
|
||||
|
||||
## API reference
|
||||
|
||||
```eval_rst
|
||||
.. autoclass:: gaphor.misc.generic.event.Manager
|
||||
```
|
||||
For more information about how the Generic library handles events see the
|
||||
[Generic documentation](https://generic.readthedocs.io).
|
||||
|
@ -1,27 +1,40 @@
|
||||
# Gaphor's Framework
|
||||
# Framework
|
||||
|
||||
Gaphor is built in a light, service oriented fashion. The application is
|
||||
split in a series of services, such as a file manager, undo manager and
|
||||
GUI manager. Those services are loaded based on entry points defined in
|
||||
Python Eggs (see [Service Oriented Architecture](so.md)).
|
||||
## Overview
|
||||
|
||||
Objects communicate with each other through events. Whenever something
|
||||
of importance happens (e.g. an attribute of a model element changes) an
|
||||
event is sent. Whoever is interested (a diagram item for example)
|
||||
receive notification once it has registered an event handler for that
|
||||
event type. Events are emitted though a central broken (zope.component
|
||||
in our case), so you do not have to register on every individual element
|
||||
that can send an event it's're interested in (so the diagram item should
|
||||
check if the element that sent the event is actually the event the item
|
||||
is representing).
|
||||
Gaphor is built in a light, service oriented fashion. The application is split
|
||||
in a series of services, such as a file, event, and undo managers. Those
|
||||
services are loaded based on entry points defined in the `pyproject.toml` file.
|
||||
To learn more about the architecture, please see the description about the
|
||||
[Service Oriented Architecture](so.md).
|
||||
|
||||
Gaphor is transactional. Transactions work simply by sending an event
|
||||
when a transaction starts and sending another when a transaction ends.
|
||||
E.g. undo management is transactional.
|
||||
## Events
|
||||
|
||||
It all starts with an Application. Only one Application instance is
|
||||
permitted. The Application will look for services defined as
|
||||
[gaphor.services](services.md). Those services are loaded and initialized.
|
||||
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](event_system.md).
|
||||
|
||||
## Transactional
|
||||
|
||||
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.
|
||||
|
||||
## Main Components
|
||||
|
||||
The main portion of Gaphor that executes first is called the `Application`.
|
||||
Only one Application instance is permitted. The Application will look for
|
||||
services defined as [gaphor.services](services.md). Those services are loaded
|
||||
and initialized.
|
||||
|
||||
The most notable services are:
|
||||
|
||||
@ -31,32 +44,17 @@ Loading and saving a model is done through this service.
|
||||
|
||||
### element_factory
|
||||
|
||||
The [data model](datamodel.md) itself is maintained in the element factory.
|
||||
This service is used to create model elements and can be used to lookup
|
||||
elements or query for a set of elements.
|
||||
The [data model](datamodel.md) itself is maintained in the element factory
|
||||
(`gaphor.UML.elementfactory`). This service is used to create model elements,
|
||||
as well as to lookup elements or query for a set of elements.
|
||||
|
||||
### [undo_manager](undo.md)
|
||||
### undo_manager
|
||||
|
||||
One of the most appreciated services. It allows users to make a mistake every
|
||||
now and then!
|
||||
|
||||
The undo manager is transactional. Actions performed by a user are
|
||||
only stored if a transaction is active. If a transaction is
|
||||
completed (committed) a new undo action is stored. Transactions can
|
||||
also be rolled back, in which case all changes are played back
|
||||
directly.
|
||||
|
||||
### element_dispatcher
|
||||
|
||||
Although Gaphor makes use of a central dispatch engine, this
|
||||
solution is not efficient when it comes to dispatching events of UML
|
||||
model elements. For this purpose the element_dispatcher can help
|
||||
out. It maintains a path of elements reaching from the root (e.g.
|
||||
from a diagram item) to the element of interest and will only signal
|
||||
in case this element changes. This makes complex dispatching very
|
||||
efficient. The dispatcher functionality is available through
|
||||
Element.watcher().
|
||||
|
||||
```eval_rst
|
||||
.. autoclass:: gaphor.UML.elementdispatcher.ElementDispatcher
|
||||
```
|
||||
The undo manager is transactional. Actions performed by a user are only stored
|
||||
if a transaction is active. If a transaction is completed (committed) a new
|
||||
undo action is stored. Transactions can also be rolled back, in which case all
|
||||
changes are played back directly. For more information see the full description
|
||||
of the [undo manager](undo.md)
|
||||
|
@ -1,21 +1,23 @@
|
||||
Welcome to Gaphor's documentation!
|
||||
The Gaphor Technical Documentation
|
||||
==================================
|
||||
|
||||
Some highlights of the documentation:
|
||||
This documentation is aimed at those who would be interested in making
|
||||
contributions to Gaphor. For tutorials and how-to information, please visit the
|
||||
`Gaphor Website <https://gaphor.org>`_.
|
||||
|
||||
* A :doc:`manual <manual/index>`. It outlines some of the ideas in and behind Gaphor.
|
||||
* The :ref:`tech-section` contains some interesting articles about the technology that drives Gaphor and Gaphas, Gaphor's canvas widget.
|
||||
In the future, we would like to split the documentation in to sections that
|
||||
focus on **explanation** (understanding-oriented) and **reference**
|
||||
(information-oriented). For now, this information is all together.
|
||||
|
||||
If you're into writing plug-ins for Gaphor you should have a look at our
|
||||
fabulous `Hello world <http://github.com/amolenaar/gaphor.plugins.helloworld>`_
|
||||
fabulous `Hello world <https://github.com/gaphor/gaphor.plugins.helloworld>`_
|
||||
plug-in.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
Platforms
|
||||
---------
|
||||
|
||||
manual/index
|
||||
|
||||
Running Gaphor on different platforms:
|
||||
Setting up a development environment, and packaging Gaphor on different
|
||||
platforms:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
@ -23,8 +25,6 @@ Running Gaphor on different platforms:
|
||||
windows
|
||||
linux
|
||||
macos
|
||||
custominstall
|
||||
|
||||
|
||||
.. _tech-section:
|
||||
|
||||
@ -46,7 +46,6 @@ Tech section
|
||||
undo
|
||||
xml-format
|
||||
event_system
|
||||
multidispatching
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
@ -54,10 +53,8 @@ Tech section
|
||||
External links
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
* You should definitely check out http://www.agilemodeling.com.
|
||||
|
||||
* The `UML diagrams <http://www.agilemodeling.com/essays/umlDiagrams.htm>`_ (although Gaphor does not see it that black-and-white).
|
||||
* http://www.agilemodeling.com/essays/
|
||||
* The `official UML specification <https://www.omg.org/spec/UML>`_. This ''is'' our data model.
|
||||
|
||||
* You should definitely check out `Agile Modeling <http://www.agilemodeling.com>`_ including these pages:
|
||||
|
||||
1. `UML Diagrams <http://www.agilemodeling.com/essays/umlDiagrams.htm>`_ (although Gaphor does not see it that black-and-white).
|
||||
2. http://www.agilemodeling.com/essays/
|
||||
* The `official UML specification <https://www.omg.org/spec/UML>`_. This ''is'' our data model.
|
@ -1,9 +1,8 @@
|
||||
# Gaphor Diagram Items
|
||||
# Items and Elements
|
||||
|
||||
The diagram items (or in short items) represent UML metamodel on a
|
||||
diagram. The following sections present the basic items.
|
||||
Diagram items, or just items for short, represent the UML metamodel on a
|
||||
diagram. In other words, they are the graphical objects in a diagram.
|
||||
`DiagramItem` supports item style, text elements, and stereotypes.
|
||||
|
||||
DiagramItem
|
||||
===========
|
||||
|
||||
Basic diagram item supporting item style, text elements and stereotypes.
|
||||
Elements refer to the semantic model objects. `Element` is the base class
|
||||
for the UML data classes.
|
||||
|
@ -1,15 +1,5 @@
|
||||
# Gaphor on Linux
|
||||
|
||||
Examples of Gaphor and Gaphas RPM spec files can be found in [PLD
|
||||
Linux](https://www.pld-linux.org/)
|
||||
[repository](https://github.com/pld-linux/):
|
||||
|
||||
> - <https://github.com/pld-linux/python-gaphas>
|
||||
> - <https://github.com/pld-linux/gaphor>
|
||||
|
||||
Please, do not hesitate to contact us if you need help to create a Linux
|
||||
package for Gaphor or Gaphas.
|
||||
|
||||
## Development Environment
|
||||
|
||||
To setup a development environment with Linux, you first need the latest
|
||||
@ -19,24 +9,80 @@ Install the pyenv
|
||||
[prerequisites](https://github.com/pyenv/pyenv/wiki/Common-build-problems)
|
||||
first, and then install pyenv:
|
||||
|
||||
> $ curl <https://pyenv.run> | bash
|
||||
$ 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. Finally install
|
||||
the latest version of Python by executing:
|
||||
|
||||
> $ pyenv install 3.x.x
|
||||
$ pyenv install 3.x.x
|
||||
|
||||
Where 3.x.x is replaced by the latest stable version of Python.
|
||||
|
||||
Next install the Gaphor prerequisites by installing the gobject
|
||||
introspection and cairo build dependencies, for example in Ubuntu
|
||||
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-3.0 libgirepository1.0-dev libcairo2-dev
|
||||
```bash
|
||||
$ sudo apt-get install -y python3-dev python3-gi python3-gi-cairo
|
||||
gir1.2-gtk-3.0 libgirepository1.0-dev libcairo2-dev
|
||||
```
|
||||
|
||||
[Clone the
|
||||
repository](https://help.github.com/en/articles/cloning-a-repository).
|
||||
repository](https://help.github.com/en/github/creating-cloning-and-archiving-repositories/cloning-a-repository).
|
||||
|
||||
> $ cd gaphor $ source ./venv $ poetry run gaphor
|
||||
```bash
|
||||
$ cd gaphor
|
||||
$ source venv
|
||||
$ poetry run gaphor
|
||||
```
|
||||
|
||||
## Create a Flatpak Package
|
||||
|
||||
The main method that Gaphor is packaged for Linux is with a Flatpak package.
|
||||
[Flatpak](https://flatpak.org) is a software utility for software deployment
|
||||
and package management for Linux. It offer a sandbox environment in which
|
||||
users can run application software in isolation from the rest of the system.
|
||||
|
||||
We distribute the offical Flatpak using [Flathub](https://flathub.org), and
|
||||
building of the image is done at the [Gaphor Flathub
|
||||
repository](https://github.com/flathub/org.gaphor.Gaphor).
|
||||
|
||||
1. [Install Flatpak](https://flatpak.org/setup)
|
||||
|
||||
1. Install flatpak-builder
|
||||
|
||||
$ sudo apt-get install flatpak-builder
|
||||
|
||||
1. Install the GNOME SDK
|
||||
|
||||
$ flatpak install flathub org.gnome.Sdk 3.34
|
||||
|
||||
1. Add the Flathub repository and install the necessary SDK:
|
||||
|
||||
$ cd flatpak
|
||||
$ make setup
|
||||
|
||||
1. Build Gaphor Flatpak
|
||||
|
||||
$ make
|
||||
|
||||
1. Install the Flatpak
|
||||
|
||||
$ make install
|
||||
|
||||
In order to update the Gaphor dependencies in the yaml manifest files:
|
||||
|
||||
$ make python-modules
|
||||
|
||||
## Linux Distribution Packages
|
||||
|
||||
Examples of Gaphor and Gaphas RPM spec files can be found in [PLD
|
||||
Linux](https://www.pld-linux.org/)
|
||||
[repository](https://github.com/pld-linux/):
|
||||
|
||||
- https://github.com/pld-linux/python-gaphas
|
||||
- https://github.com/pld-linux/gaphor
|
||||
|
||||
Please, do not hesitate to contact us if you need help to create a Linux
|
||||
package for Gaphor or Gaphas.
|
@ -1,3 +1,30 @@
|
||||
# Gaphor on macOS
|
||||
|
||||
## Development Environment
|
||||
|
||||
To setup a development environment with macOS:
|
||||
1. Install [homebrew](https://brew.sh)
|
||||
1. Open a terminal and execute:
|
||||
```bash
|
||||
$ brew install python3 gobject-introspection gtk+3
|
||||
```
|
||||
[Clone the
|
||||
repository](https://help.github.com/en/github/creating-cloning-and-archiving-repositories/cloning-a-repository).
|
||||
```
|
||||
$ cd gaphor
|
||||
$ source ./venv
|
||||
$ poetry run gaphor
|
||||
```
|
||||
|
||||
## Packaging for macOS
|
||||
|
||||
In order to create a dmg package for macOS, we utilize a custom bash script
|
||||
that picks up the required files, drops them in a bundle, and changes the
|
||||
link references.
|
||||
|
||||
1. Follow the instructions for settings up a development environment above
|
||||
1. Open a terminal and execute the following from the repository directory:
|
||||
```bash
|
||||
$ cd macos-dmg
|
||||
$ source package.sh
|
||||
```
|
||||
|
Before Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 4.6 KiB |
@ -1,17 +0,0 @@
|
||||
The Gaphor manual
|
||||
=====================
|
||||
|
||||
This section of the site is dedicated to some user documentation. Although most
|
||||
users will have previous experience with Gaphor, it is evident that some
|
||||
aspects of the application need a little more explanation.
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
introduction
|
||||
working
|
||||
modeling
|
||||
plugins
|
||||
stereotypes
|
||||
|
@ -1,22 +0,0 @@
|
||||
# Introduction
|
||||
|
||||
Welcome to the Gaphor!
|
||||
|
||||
Gaphor is a UML 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 designed around the following principles:
|
||||
|
||||
- Simplicity The application should be easy to use. Only some basic knowledge
|
||||
of UML is required.
|
||||
- Consistency UML is a graphical modeling language, so all modeling is done
|
||||
in a diagram. For example, even stereotypes are modeled in diagrams.
|
||||
- Workability The application should not bother the user every time they do
|
||||
something non-UML-ish.
|
||||
|
||||
This manual serves as a reference for all Gaphor has to offer. So, you
|
||||
may read it from start to finish, or jump to a section that interests
|
||||
you.
|
@ -1,47 +0,0 @@
|
||||
# Creating models
|
||||
|
||||
Once Gaphor is started a new empty model is automatically created. The
|
||||
main diagram is already open in the Diagram section.
|
||||
|
||||
Select an element you want to place (e.g. 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 Navigation.
|
||||
The selected tool will reset itself to the Pointer tool if the option ''Diagram
|
||||
-> Reset tool'' is selected.
|
||||
|
||||

|
||||
|
||||
It's simple to add elements to a diagram.
|
||||
|
||||
Some elements are not directly visible. The section in the toolbox is
|
||||
collapsed and needs to be clicked first to reveal its contents.
|
||||
|
||||
Gaphor does not make any assumptions about which elements should be
|
||||
placed on a diagram. A diagram is a diagram. UML defines all different
|
||||
kinds of diagrams, such as Class diagrams, Component diagrams, Action
|
||||
diagrams, Sequence diagrams. But Gaphor does not restrict the user.
|
||||
|
||||
## Creating New Diagrams
|
||||
|
||||

|
||||
|
||||
To create a new diagram, use the Navigation. Select an element that can
|
||||
contain a diagram (a Package or Profile) and right-click (command-click in
|
||||
macOS). Select New diagram and a new diagram is created.
|
||||
|
||||
## Copy and Paste
|
||||
|
||||
Items in a diagram can be copied and pasted (in the same diagram or
|
||||
another). A paste will create new items in the diagrams, the items they
|
||||
represent (e.g. the Class that's shown in the Navigation) is *not*
|
||||
copied (call it a shallow copy if you like).
|
||||
|
||||
## Drag and Drop
|
||||
|
||||
Adding an existing element to a diagram is simple: drag the element from
|
||||
the Navigation section onto a diagram. Not all elements that show up in
|
||||
the Navigation can be added: Diagrams and attribute/operations of a
|
||||
Class can not be added.
|
||||
|
||||
Elements can also be dragged within the Navigation section. This way
|
||||
classes and packages can be rearranged for example.
|
Before Width: | Height: | Size: 9.6 KiB |
Before Width: | Height: | Size: 13 KiB |
@ -1,65 +0,0 @@
|
||||
# Writing plugins and services
|
||||
|
||||
There is little difference in writing a plugin or a service.
|
||||
|
||||
## Accessing Model Related Data
|
||||
|
||||
The datamodel classes are located in the gaphor.UML module. Data objects
|
||||
can be accessed through the ElementFactory. This is a special class for
|
||||
creating and managing data objects. Items can be queried using this
|
||||
element factory. It's registered in the application as
|
||||
element_factory. When writing a service or plugin the element factory
|
||||
can be injected into the service like this:
|
||||
|
||||
```Python
|
||||
class MyThing:
|
||||
def do_something(self):
|
||||
element_factory = Application.get_service('element_factory')
|
||||
items = element_factory.select()
|
||||
```
|
||||
|
||||
``` note:: In the console window services can be retrieved by using the service() function. E.g.: ef = service('element_factory')
|
||||
```
|
||||
|
||||
## Querying the Data Model
|
||||
|
||||
Two methods are used for querying:
|
||||
|
||||
- select(query=None) -> returns an iterator
|
||||
- lselect(query=None) -> returns a list
|
||||
|
||||
query is a lambda function with the element as parameter. E.g.:
|
||||
|
||||
element_factory.select(lambda e: e.isKindOf(UML.Class))
|
||||
|
||||
will fetch all Class instances from the element factory.
|
||||
|
||||
## Traversing Data Instances
|
||||
|
||||
Once some classes are obtained It's common to traverse through a few
|
||||
related objects in order to obtain the required information. For
|
||||
example: to iterate through all parameters related to class'
|
||||
operations, one can write:
|
||||
|
||||
```Python
|
||||
for o in classes.ownedOperation:
|
||||
for p in o.ownedParameter:
|
||||
do_something(p)
|
||||
```
|
||||
|
||||
Using the `[:]` operator items can be traversed more easily:
|
||||
|
||||
```Python
|
||||
for o in classes.ownedOperation\[:\].ownedParameter:
|
||||
do_something(p)
|
||||
```
|
||||
|
||||
It's also possible to provide a query as part of the selection:
|
||||
|
||||
```Python
|
||||
for o in classes.ownedOperation['it.returnParameter'].ownedParameter:
|
||||
do_something(p)
|
||||
```
|
||||
|
||||
The variable `it` in the query refers to the evaluated object (in this
|
||||
case all operations with a return parameter are taken into account).
|
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 8.7 KiB |
Before Width: | Height: | Size: 32 KiB |
@ -1,30 +0,0 @@
|
||||
# Extending models
|
||||
|
||||
The UML method to extend UML (basic) components with a special meaning
|
||||
is by using stereotypes. A stereotype defines a special usage of a model
|
||||
element. For example: a class that's used as a controller can be
|
||||
assigned a stereotype:
|
||||
|
||||
«controller»
|
||||
|
||||
Creating a stereotype starts by the creation of a Profile normally.
|
||||
Although stereotypes can be created in every package, it's a good habit
|
||||
to use Profiles for that. Next a Metaclass has to be created. The
|
||||
metaclass will tell the stereotype on which kind of elements it is
|
||||
applicable. A Stereotype can be connected to the Metaclass by means of
|
||||
an Extension relationship.
|
||||
|
||||

|
||||
|
||||
Stereotypes can be applied to basically all elements in a model.
|
||||
|
||||

|
||||
|
||||
Stereotypes can contain attributes, as shown in the diagram above. Those
|
||||
attributes can be filled in the Element Editor. This allows for enormous
|
||||
flexibility. In most cases, especially if some sort of program logic has
|
||||
to be generated from the models, it is very handy to define special
|
||||
behaviours to classes and other elements by means of stereotypes.
|
||||
|
||||

|
||||
|
@ -1,77 +0,0 @@
|
||||
# Working with Gaphor
|
||||
|
||||
Once Gaphor is launched, it provides you an almost empty user interface.
|
||||
A new model is already created and the diagram is opened.
|
||||
|
||||
The layout of the Gaphor interface is divided into four sections,
|
||||
namely:
|
||||
|
||||
- Navigation
|
||||
- Main Canvas Diagram
|
||||
- Diagram Element Toolbox
|
||||
- Properties pane
|
||||
|
||||
Each section has its own specific function.
|
||||
|
||||
## Navigation
|
||||
|
||||
The navigation section of the interface displays a hierarchical view of
|
||||
your model. Every model element you create will be inserted into the
|
||||
navigation section. 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 on the left, you will see that there are three elements in
|
||||
the navigation view. 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
|
||||
NewClass element is a class and the main element is a diagram.
|
||||
|
||||
In the navigation view, you can also right-click the model elements to
|
||||
get a context menu. This context menu allows you to delete model
|
||||
elements, and to refresh the navigation view.
|
||||
|
||||
Double clicking on a diagram element will open it in the Diagram
|
||||
section. Elements such as classes and packages can be dragged from the
|
||||
tree view on the diagrams.
|
||||
|
||||
## Diagram Section
|
||||
|
||||
The diagram section takes up the most space. Multiple diagrams can be
|
||||
opened at once: they are shown in tabs. Tabs can be closed from the file
|
||||
menu (Close) and by pressing Ctrl+w.
|
||||
|
||||
Most elements have hot zones, shown as gray rectangles that are only
|
||||
visible when the item is selected. Double clicking on those rectangles
|
||||
allows you to directly edit the item. E.g. change its name or change the
|
||||
name of an association end.
|
||||
|
||||
## Toolbox
|
||||
|
||||
The toolbox is mainly used to add new diagram 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 clicking on them. By default the pointer
|
||||
tool is selected after every item placement. This can be changed by
|
||||
disabling the Diagram > Reset tool option. Tools can also be selected
|
||||
by a keyboard shortcut. The actual character is displayed as part of the
|
||||
tooltip. Finally it is also possible to drag elements on the canvas from
|
||||
the toolbox.
|
||||
|
||||
## Element Editor
|
||||
|
||||
The Element editor is not really part of the main screen, it's a
|
||||
utility window that shows all characteristics of the selected element.
|
||||
Things like name, attributes and stereotypes. It can be opened with
|
||||
Ctrl+e.
|
||||
|
||||

|
||||
|
||||
The properties shown depend on the element that is selected.
|
@ -1,24 +1,24 @@
|
||||
# UML Datamodel
|
||||
# UML Data Model
|
||||
|
||||
Gaphor uses the UML metamodel specs as guidelines for its data storage.
|
||||
In fact, the datamodel is generated for a model file.
|
||||
Gaphor uses the UML Specification as a guideline for its own data storage.
|
||||
The Python data model is generated from a Gaphor model file that describes
|
||||
the relationships between the supported UML elements.
|
||||
|
||||
The model is built using smart properties (descriptors). Those
|
||||
descriptors handle events when they're changed. This allows the rest of
|
||||
the application (visuals, undo system) to update their state
|
||||
accordingly. The events are send using Zope (3)'s signalling mechanism,
|
||||
called Handlers.
|
||||
The model is built using smart properties (descriptors). These properties
|
||||
emit events when they're changed. This allows the rest of the application,
|
||||
for example, the visuals and undo system, to update their state accordingly.
|
||||
The events are sent using a signaling mechanism, called handlers.
|
||||
|
||||
## Model details
|
||||
|
||||
Pay attention to the following changes/additions with respect to the
|
||||
official model:
|
||||
official Gaphor model, in the `uml2.gaphor` file:
|
||||
|
||||
- Additions to the model have been put in the package
|
||||
AuxilaryConstructs.Presentations and .Stereotypes.
|
||||
- A Diagram element is added in order to model the diagrams.
|
||||
- A special construct has been put into place in order to apply
|
||||
stereotypes to model elements. The current specs (2.2) are not
|
||||
stereotypes to model elements. The current UML Specification is not
|
||||
clear on that subject.
|
||||
- The Slot.value reference is singular.
|
||||
|
||||
|
@ -1,94 +0,0 @@
|
||||
# Multi-Dispatching
|
||||
|
||||
Multidispatching allows you to define methods and functions which should
|
||||
behave differently based on arguments' types without cluttering
|
||||
`if-elif-else` chains and `isinstance` calls.
|
||||
|
||||
All you need is inside `generic.multidispatch` module. See examples
|
||||
below to learn how to use it to define multifunctions and multimethods.
|
||||
|
||||
## Multifunctions
|
||||
|
||||
Suppose we want to define a function which behaves differently based on
|
||||
arguments' types. The naive solution is to inspect argument types with
|
||||
`isinstance` function calls but generic provides us with
|
||||
`@multidispatch` decorator which can easily reduce the amount of
|
||||
boilerplate and provide desired level of extensibility:
|
||||
|
||||
from generic.multidispatching import multidispatch
|
||||
|
||||
@multidispatch(Dog)
|
||||
def sound(o):
|
||||
print "Woof!"
|
||||
|
||||
@sound.when(Cat)
|
||||
def sound(o):
|
||||
print "Meow!"
|
||||
|
||||
Each separate definition of `sound` function works for different
|
||||
argument types, we will call each such definition *a multidispatch case*
|
||||
or simply *a case*. We can test if our `sound` multidispatch works as
|
||||
expected:
|
||||
|
||||
>>> sound(Dog())
|
||||
Woof!
|
||||
>>> sound(Cat())
|
||||
Meow!
|
||||
>>> sound(Duck())
|
||||
Traceback
|
||||
...
|
||||
TypeError
|
||||
|
||||
The main advantage of using multifunctions over single function with a
|
||||
bunch of `isinstance` checks is extensibility \-- you can add more cases
|
||||
for other types even in separate module:
|
||||
|
||||
from somemodule import sound
|
||||
|
||||
@sound.when
|
||||
def sound(o)
|
||||
print "Quack!"
|
||||
|
||||
When behaviour of multidispatch depends on some argument we will say
|
||||
that this multidispatch *dispatches* on this argument.
|
||||
|
||||
## Multifunctions of Several Arguments
|
||||
|
||||
You can also define multifunctions of several arguments and even decide
|
||||
on which of first arguments you want to dispatch. For example the
|
||||
following function will only dispatch on its first argument while
|
||||
requiring both of them:
|
||||
|
||||
@multidispatch(Dog)
|
||||
def walk(dog, meters):
|
||||
print "Dog walks for %d meters" % meters
|
||||
|
||||
But sometimes you want multifunctions to dispatch on more than one
|
||||
argument, then you just have to provide several arguments to
|
||||
`multidispatch` decorator and to subsequent `when` decorators:
|
||||
|
||||
@multidispatch(Dog, Cat)
|
||||
def chases(dog, cat):
|
||||
return True
|
||||
|
||||
@chases.when(Dog, Dog)
|
||||
def chases(dog, dog):
|
||||
return None
|
||||
|
||||
@chases.when(Cat, Dog)
|
||||
def chases(cat, dog):
|
||||
return False
|
||||
|
||||
You can have any number of arguments to dispatch on but they should be
|
||||
all positional, keyword arguments are allowed for multifunctions only if
|
||||
they're not used for dispatch.
|
||||
|
||||
## API reference
|
||||
|
||||
```eval_rst
|
||||
.. autoclass:: gaphor.misc.generic.multidispatch.multidispatch
|
||||
```
|
||||
|
||||
```eval_rst
|
||||
.. autoclass:: gaphor.misc.generic.multidispatch.FunctionDispatcher
|
||||
```
|
8
docs/requirements.txt
Normal file
@ -0,0 +1,8 @@
|
||||
# Keep until RTD fully support pyproject.toml
|
||||
docutils==0.15.2
|
||||
Pygments==2.4.2
|
||||
recommonmark==0.6.0
|
||||
Sphinx==2.2.1
|
||||
sphinx-rtd-theme==0.4.3
|
||||
sphinxcontrib-websupport==1.1.2
|
||||
tomlkit==0.5.8
|
@ -4,9 +4,6 @@ Gaphor is modeled around the concept of Services. Each service can be
|
||||
registered with the application and then be used by other services or
|
||||
other objects living within the application.
|
||||
|
||||
Since Gaphor already uses the zope.component adapters, it seems like a
|
||||
good choice to use zope.interface too as starting point for services.
|
||||
|
||||
Each service should implement the Service interface. This interface
|
||||
defines one method:
|
||||
|
||||
@ -18,25 +15,22 @@ service itself and therefore implements Service too.
|
||||
Each service is allowed to define its own interface, as long as Service
|
||||
is implemented too.
|
||||
|
||||
Services should be defined as entry_points in the Egg info file.
|
||||
Services should be defined as entry_points in the `pyproject.toml` file.
|
||||
|
||||
Typically a service does some work in the background.
|
||||
|
||||
## Example: ElementFactory
|
||||
|
||||
A nice example is the ElementFactory. Currently it is tightly bound to
|
||||
the gaphor.UML module. A default factory is created in __init__.py.
|
||||
A nice example of a service in use is the ElementFactory. Currently it is
|
||||
tightly bound to the `gaphor.UML` module. A default factory is created in
|
||||
`__init__.py`.
|
||||
|
||||
It depends on the undo_manager. However, on important events, events
|
||||
are emitted. (That is when an element is created/destroyed).
|
||||
|
||||
What you want to do is create an event handler for ElementFactory that
|
||||
stores the add/remove signals in the undo system.
|
||||
|
||||
The same goes for UML.Elements. Those classes (or more specific the
|
||||
properties) send notifications every time their state changes.
|
||||
|
||||
But.. where to put such information?
|
||||
The undo_manager depends on the events emitted by the ElementFactory. When an
|
||||
important events occurs, like an element is created or destroyed, that event is
|
||||
emitted. We then use an event handler for ElementFactory that stores the
|
||||
add/remove signals in the undo system. Another example of events that are
|
||||
emitted are with `UML.Elements`. Those classes, or more specifically, the
|
||||
properties, send notifications every time their state changes.
|
||||
|
||||
```eval_rst
|
||||
.. autoclass:: gaphor.UML.elementfactory.ElementFactory
|
||||
@ -48,4 +42,5 @@ Currently a plugin is defined by an XML file. This will change as
|
||||
plugins should be distributable as Eggs too. A plugin will contain user
|
||||
interface information along with its service definition.
|
||||
|
||||
See also: [Writing plugins](manual/plugins.md)
|
||||
For more information, please also see our how-to on [writing a
|
||||
plugin](https://gaphor.org/pages/writing-a-plugin.html)
|
||||
|
108
docs/so.md
@ -1,58 +1,46 @@
|
||||
# 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: a service is
|
||||
responsible for loading/saving models, another for the menu structure,
|
||||
yet another service handles the undo system.
|
||||
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, separate services are used to load/save
|
||||
models, provide the menu structure, and handle the undo system.
|
||||
|
||||
Service are defined as [Egg entry points](http://peak.telecommunity.com/
|
||||
DevCenter/setuptools\#dynamic-discovery-of-services-and-plugins).
|
||||
With entry points applications can register functionality for specific
|
||||
purposes. Entry points are grouped in so called *entry point groups*.
|
||||
For example the console_scripts entry point group is used to start an
|
||||
application from the command line.
|
||||
Service are defined as entry points in the `pyproject.toml`. With entry points
|
||||
applications can register functionality for specific purposes. Entry points are
|
||||
grouped in so called *entry point groups*. For example the console_scripts
|
||||
entry point group is used to start an application from the command line.
|
||||
|
||||
Entry points, as well as all major components in Gaphor, are defined
|
||||
around [Zope interfaces](http://www.zope.org/Products/ZopeInterface). An
|
||||
interface defines specific methods and attributes that should be
|
||||
implemented by the class.
|
||||
Entry Points
|
||||
============
|
||||
|
||||
Entry point groups
|
||||
==================
|
||||
Gaphor uses a main entry point group called `gaphor.services`.
|
||||
|
||||
Gaphor defines two entry point groups:
|
||||
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.
|
||||
|
||||
1. gaphor.services
|
||||
1. gaphor.uicomponents
|
||||
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`:
|
||||
|
||||
Services are used to add functionality to the application. Plug-ins
|
||||
should also be defined as services. E.g.:
|
||||
|
||||
entry_points = {
|
||||
'gaphor.services': [
|
||||
'xmi_export = gaphor.plugins.xmiexport:XMIExport',
|
||||
],
|
||||
},
|
||||
|
||||
There is a thin line between a service and a plug-in. A service
|
||||
typically performs some basic need for the applications (such as the
|
||||
element factory or the undo mechanism). A plug-in is more of an add-on.
|
||||
For example a plug-in can depend on external libraries or provide a
|
||||
cross-over function between two applications.
|
||||
```toml
|
||||
[tool.poetry.plugins."gaphor.services"]
|
||||
"xmi_export" = "gaphor.plugins.xmiexport:XMIExport"
|
||||
```
|
||||
|
||||
## Interfaces
|
||||
|
||||
Each service (and plug-in) should implement the gaphor.abc.Service
|
||||
interface:
|
||||
Each service (and plugin) should implement the `gaphor.abc.Service` interface:
|
||||
|
||||
```eval_rst
|
||||
.. autoclass:: gaphor.abc.Service
|
||||
```
|
||||
|
||||
UI components is another piece of cake. The `gaphor.uicomponents` entry
|
||||
point group is used to define windows and user interface functionality.
|
||||
A UI component should implement the gaphor.ui.abc.UIComponent 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:
|
||||
|
||||
```eval_rst
|
||||
.. autoclass:: gaphor.ui.abc.UIComponent
|
||||
@ -69,9 +57,9 @@ interface:
|
||||
|
||||
## Example plugin
|
||||
|
||||
A small example is provided by means of the Hello world plugin. Take a
|
||||
look at the files at
|
||||
[Github](https://github.com/gaphor/gaphor.plugins.helloworld).
|
||||
A small example is provided by means of the Hello world plugin. Take a look at
|
||||
the files at [GitHub](https://github.com/gaphor/gaphor.plugins.helloworld). The
|
||||
example plugin needs to be updated to support versions 1.0.0 and later of Gaphor.
|
||||
|
||||
The
|
||||
[setup.py](https://github.com/gaphor/gaphor.plugins.helloworld/blob/master/setup.py)
|
||||
@ -86,36 +74,38 @@ file contains an entry point:
|
||||
This refers to the class `HelloWorldPlugin` in package/module
|
||||
[gaphor.plugins.helloworld](https://github.com/gaphor/gaphor.plugins.helloworld/blob/master/gaphor/plugins/helloworld/__init__.py).
|
||||
|
||||
Here is a stripped version of the hello world class:
|
||||
Here is a stripped version of the hello world plugin:
|
||||
|
||||
from gaphor.abc import Service, ActionProvider
|
||||
from gaphor.core import _, action
|
||||
```python
|
||||
from gaphor.abc import Service, ActionProvider
|
||||
from gaphor.core import _, action
|
||||
|
||||
class HelloWorldPlugin(Service, ActionProvider): # 1.
|
||||
class HelloWorldPlugin(Service, ActionProvider): # 1.
|
||||
|
||||
def __init__(self, tools_menu): # 2.
|
||||
self.tools_menu = tools_menu
|
||||
tools_menu.add_actions(self) # 3.
|
||||
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)
|
||||
def shutdown(self): # 4.
|
||||
self.tools_menu.remove_actions(self)
|
||||
|
||||
@action(name='helloworld', # 5.
|
||||
label=_('Hello world'),
|
||||
tooltip=_('Every application ...'))
|
||||
def helloworld_action(self):
|
||||
main_window = self.main_window
|
||||
pass # gtk code left out
|
||||
@action(name='helloworld', # 5.
|
||||
label=_('Hello world'),
|
||||
tooltip=_('Every application ...'))
|
||||
def helloworld_action(self):
|
||||
main_window = self.main_window
|
||||
pass # gtk code left out
|
||||
```
|
||||
|
||||
1. As stated before, a plugin should implement the `Service` interface.
|
||||
It also implements `ActionProvider`, saying it has some actions to
|
||||
be performed by the user.
|
||||
2. The menu entry will be part of the \"Tools\" extension menu. This
|
||||
2. The menu entry will be part of the "Tools" extension menu. This
|
||||
extension point is created as a service. Other services can also be
|
||||
passed as dependencies. Services can get references to other
|
||||
services by defining them as arguments of the constructor.
|
||||
3. All action defined in this service are registered.
|
||||
4. Each servoce has a `shutdown()` method. This allows the servie to
|
||||
4. Each service has a `shutdown()` method. This allows the service to
|
||||
perform some cleanup when it's shut down.
|
||||
5. The action that can be invoked. The action is defined and will be
|
||||
picked up by `add_actions()` method (see 3.)
|
||||
|
@ -1,54 +1,32 @@
|
||||
# Stereotypes
|
||||
|
||||
Stereotypes are quite another story. In order to create a stereotype one
|
||||
should create a Profile. Within this profile a Diagram can be created.
|
||||
This diagram should accept only items that are useful within a profile:
|
||||
UML defines a concept called a stereotype, which is how you can extend an
|
||||
existing metaclass. This allows you to create new notation that can add to or
|
||||
replace existing elements. In order to create a stereotype in Gaphor, first you
|
||||
need to create a Profile. A profile is a collection of stereotypes. Next a
|
||||
Diagram can be created within the profile. Although a diagram in Gaphor can
|
||||
accept any type of element, by convention, the profile diagram should only
|
||||
contain items that are useful within a profile:
|
||||
|
||||
- Classes, which will function as <<metaclass>>.
|
||||
- Stereotype, which will be the defined <<stereotypes>>.
|
||||
- Classes, which will function as a `<<metaclass>>`.
|
||||
- Stereotype, which will be defined as `<<stereotype>>`.
|
||||
- Extensions, connecting metaclasses and stereotypes.
|
||||
|
||||
and of course the usual: Comment, Association, Generalization and
|
||||
Dependency.
|
||||
Comment, Association, Generalization, and Dependency can also be used within a
|
||||
profile diagram, just like other UML diagrams.
|
||||
|
||||
Thoughts:
|
||||
Some things to keep in mind when working with profiles:
|
||||
|
||||
- Profiles are reusable and its common to share them with different
|
||||
models.
|
||||
- Stereotypes can only be owned by Profiles, not by (normal) Packages.
|
||||
- We have to do a lookup if a MetaClass is actually part of the model.
|
||||
- a stereotype can contain an image, that can be used instead of its
|
||||
name
|
||||
- Profiles should be saved with the model too. And it should be
|
||||
possible to \"update\" a profile within a model.
|
||||
|
||||
Maybe it would be nice to create Stereotypes without creating the
|
||||
diagrams. Via a dialog once can select which class (Operation, Class,
|
||||
etc.) is stereotyped, which extra constraints apply and/or if you
|
||||
inherit from an already existing stereotype. This way it's easy to save
|
||||
your stereotypes apart from the model (and possibly in the model too) so
|
||||
they can be reused in other models.
|
||||
|
||||
I could create a special diagram window too that can be used to create
|
||||
profiles. Profiles should be added to packages within the model.
|
||||
|
||||
This window should contain:
|
||||
1. Name of the stereotype
|
||||
2. Metaclass it applies to (Class, Operation, etc.)
|
||||
3. If it is a subclass of an already existing metaclass
|
||||
4. Constraints
|
||||
5. Description
|
||||
6. The profile it belongs to.
|
||||
|
||||
When a stereotype is used, an instance is created of the Stereotype
|
||||
(meta)class. This is not really possible for our application. Gaphor
|
||||
should figure out another way to do this. Should check XMI and see how
|
||||
they do it (maybe a property is enough).
|
||||
|
||||
It looks like the stereotypes are more a concept than something that is
|
||||
implementable in an application. The point is that the stereotypes you
|
||||
define (instances of Stereotype) should be instantiated in your model
|
||||
when you create a stereotyped class.
|
||||
- Profiles are reusable and its common to share them across different models.
|
||||
- A stereotype can only be owned by a profile, it can not be in a normal
|
||||
Package.
|
||||
- In order to make use of a stereotype, Gaphor has to perform a lookup if the
|
||||
MetaClass it is extended from is part of the model.
|
||||
- A stereotype can contain an image, which can be used instead of its name.
|
||||
- Profiles should be saved with the model too. It should also be possible to
|
||||
"update" a profile within a model.
|
||||
- Stereotypes that you define should be instantiated in your model when you
|
||||
create a stereotyped class.
|
||||
|
||||
``` note:: There is no way to connect a stereotype with a class other than an Association.
|
||||
```
|
||||
|
@ -1,12 +1,13 @@
|
||||
# Saving and Loading Gaphor Diagrams
|
||||
|
||||
Everything interesting is an ancestor of the Gaphor root tag.
|
||||
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 top level, no nesting.
|
||||
A UML element can have two tags: `Reference` and `Value`. Reference is
|
||||
used to point to other UML elements, Value has a value inside (an
|
||||
integer or a string).
|
||||
possible: UML elements (including Diagram) are at the top level with no nesting.
|
||||
A UML element can have two tags: `Reference` and `Value`. Reference is used to
|
||||
point to other UML elements, Value has a value inside (an integer or a string).
|
||||
|
||||
Diagram is a special case. Since this element contains a diagram canvas
|
||||
inside, it may become pretty big (with lots of nested elements). This is
|
||||
|
166
docs/undo.md
@ -1,135 +1,65 @@
|
||||
# Undo Manager
|
||||
|
||||
Fine grained undo: undo specific properties.
|
||||
Undo is implemented in order to erase the last change done, reverting it to an
|
||||
older state or reversing the command that was done to the model being edited.
|
||||
With the possibility of undo, users can explore and work without fear of making
|
||||
mistakes, because they can easily be undone.
|
||||
|
||||
Add undo-operation, e.g. Item.request_update() should be executed as
|
||||
part of an undo action. Such actions are normally called after the
|
||||
properties have been set right though. This is not a problem for idle
|
||||
tasks, but for directly executed tasks it is.
|
||||
Gaphor makes use of the [undo
|
||||
system](https://gaphas.readthedocs.io/en/latest/undo.html) in Gaphas, which is
|
||||
Gaphor's Canvas widget. Gaphas implements this undo system by storing the
|
||||
reverse operations to an undo list. For example, if you add a new item to the
|
||||
screen, it will save the remove operation to the undo list. If undo is then
|
||||
called, Gaphas will then remove the item from the screen that was added.
|
||||
|
||||
Should only need to save the originals, values calculated, e.g. during
|
||||
an update shouldn't have to be calculated -> use undo-operations to
|
||||
trigger updates.
|
||||
## Overview of Transactions
|
||||
|
||||
To update:
|
||||
Gaphor adds on to what Gaphas provides with an undo service, called the Undo
|
||||
Manager. The Undo Manager works transactionally. This means that if something is
|
||||
being updated by the user in a model, each change is divided into operations
|
||||
called transactions. Each operation 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 decorator called
|
||||
`@transactional`. Methods then make use of this decorator, and the undo data is
|
||||
stored in a transaction once the method is called. For example, pasting data in
|
||||
the model using the `copyservice` module and setting a value on an object's
|
||||
property page both create new transactions.
|
||||
|
||||
> Handle:
|
||||
>
|
||||
> : x, y (solvable) connectable (attr) visible (attr) movable (attr)
|
||||
> connection status (solver?)
|
||||
>
|
||||
> Item:
|
||||
>
|
||||
> : matrix canvas is managed from Canvas
|
||||
>
|
||||
> Element:
|
||||
>
|
||||
> : handles width, height min_width, min_height (solvable?)
|
||||
>
|
||||
> Line:
|
||||
>
|
||||
> : handles line_width fuzzyness (attr) orthogonal (boolean)
|
||||
> horizontal (boolean)
|
||||
>
|
||||
> Canvas:
|
||||
>
|
||||
> :
|
||||
>
|
||||
> tree:
|
||||
>
|
||||
> : add() remove()
|
||||
>
|
||||
> request_update() (should be performed as part of undo action when
|
||||
> called)
|
||||
>
|
||||
> Solver (?):
|
||||
>
|
||||
> : add_constraint() remove_constraint() Variable state
|
||||
>
|
||||
In Gaphor, connecting two diagram items is considered an atomic task,
|
||||
performed by a IConnect adapter. This operation results in a set of
|
||||
primitive tasks (properties set and a constraint created).
|
||||
When transactions take place, they also emit event notifications on the key
|
||||
transaction milestones so that other services can make use of the events. 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.
|
||||
Please see the next sections for more detail on how these event notifications
|
||||
work during a transaction.
|
||||
|
||||
For methods, it should be possible to create a decorator (@reversible)
|
||||
that schedules a method with the same signature as the calling
|
||||
operation, but with the inverse effect (e.g. the gaphas.tree module):
|
||||
## Start of a Transaction
|
||||
|
||||
class Tree:
|
||||
1. A `Transaction` object is created.
|
||||
2. `TransactionBegin` event is emitted.
|
||||
3. The `UndoManager` instantiates a new `ActionStack` which is the transaction
|
||||
object, and adds the undo action to the stack.
|
||||
|
||||
@reversable(lambda s, n, p: s.remove(n))
|
||||
def add(self, node, parent=None):
|
||||
... add
|
||||
Nested transactions are supported to allow a transaction to be added
|
||||
inside of another transaction that is already in progress.
|
||||
|
||||
@reversable(add, self='self', node='node', parent='self.get_parent(node)')
|
||||
def remove(self, node):
|
||||
... remove
|
||||
## Successful Transaction
|
||||
|
||||
Okay, so the second case is tougher...
|
||||
1. A `TransactionCommit` event is emitted
|
||||
2. The `UndoManager` closes and stores the transaction.
|
||||
|
||||
So what we did: Add a StateManager to gaphas. All changes are sent to
|
||||
the statemanager. Gaphor should implement its own state manager.
|
||||
## Failed Transaction
|
||||
|
||||
- all state changes can easily be recorded
|
||||
- fix it in one place
|
||||
- reusable throughout Gaphas and subtypes.
|
||||
|
||||
## Transactions
|
||||
|
||||
Gaphor's Undo manager works transactionally. Typically, methods can be
|
||||
decorated with @transactional and undo data is stored in the current
|
||||
transaction. A new tx is created when none exists.
|
||||
|
||||
Although undo functionality is at the core of Gaphor (diagram items and
|
||||
model elements have been adapted to provide proper undo information),
|
||||
the UndoManager itself is just a service.
|
||||
|
||||
Transaction support though is a real core functionality. By letting
|
||||
elements and items emit event notifications on important changed other
|
||||
(yet to be defined) services can take benefit of those events. The UML
|
||||
module already works this way. Gaphas (the Gaphor canvas) also emits
|
||||
state changes.
|
||||
|
||||
When state changes happen in model elements and diagram items an event
|
||||
is emitted. Those events are handled by special handlers that produce
|
||||
\"reverse-events\". Reverse events are functions that perform exactly
|
||||
the opposite operation. Those functions are stored in a list (which
|
||||
technically is the Transaction). When an undo request is done the list
|
||||
is executed in LIFO order.
|
||||
|
||||
To start a transaction:
|
||||
|
||||
1. A Transaction object has been created.
|
||||
2. This results in the emission of a TransactionBegin event.
|
||||
3. The TransactionBegin event is a trigger for the UndoManager to start
|
||||
listening for IUndoEvent actions.
|
||||
|
||||
Now, that should be done when a model element or diagram item sends a
|
||||
state change:
|
||||
|
||||
1. The event is handled by the \"reverse-handler\"
|
||||
2. Reverse handler generates a IUndoEvent signal
|
||||
3. The signal is received and stored as part of the undo-transaction.
|
||||
|
||||
(Maybe step 2 and 3 can be merged, since only one function is not of any
|
||||
interest to the rest of the application - creates nasty dependencies)
|
||||
|
||||
If nested transaction are created a simple counter is incremented.
|
||||
|
||||
When the topmost Transaction is committed:
|
||||
|
||||
1. A TransactionCommit event is emitted
|
||||
2. This triggers the UndoManager to close and store the transaction.
|
||||
|
||||
When a transaction is rolled back:
|
||||
|
||||
1. The main transaction is marked for rollback
|
||||
2. When the toplevel tx is rolled back or committed a
|
||||
1. A `TransactionRollback` event is emitted.
|
||||
TransactionRollback event is emitted
|
||||
3. This triggers the UndoManager to play back all recorded actions and
|
||||
stop listening.
|
||||
2. The `UndoManager` plays back all the recorded actions and
|
||||
then stops listening.
|
||||
|
||||
|
||||
## References
|
||||
|
||||
- [A Framework for Undoing Actions in Collaborative Systems](http://web.eecs.umich.edu/~aprakash/papers/undo-tochi94.pdf)
|
||||
- [Undoing Actions in Collaborative Work: Framework and Experience](https://www.eecs.umich.edu/techreports/cse/94/CSE-TR-196-94.pdf)
|
||||
- [Implementing a Selective Undo Framework in Python](https://legacy.python.org/workshops/1997-10/proceedings/zukowski.html)
|
||||
- [A Framework for Undoing Actions in Collaborative
|
||||
Systems](http://web.eecs.umich.edu/~aprakash/papers/undo-tochi94.pdf)
|
||||
- [Undoing Actions in Collaborative Work: Framework and
|
||||
Experience](https://www.eecs.umich.edu/techreports/cse/94/CSE-TR-196-94.pdf)
|
||||
- [Implementing a Selective Undo Framework in
|
||||
Python](https://legacy.python.org/workshops/1997-10/proceedings/zukowski.html)
|
||||
|
@ -1,3 +1,42 @@
|
||||
# Gaphor on Windows
|
||||
|
||||
## Development Environment
|
||||
|
||||
To setup a development environment in Windows:
|
||||
1) Go to http://www.msys2.org/ and download the x86_64 installer
|
||||
1) Follow the instructions on the page for setting up the basic environment
|
||||
1) Run ``C:\msys64\mingw64.exe`` - a terminal window should pop up
|
||||
```bash
|
||||
$ pacman -Suy
|
||||
$ pacman -S git mingw-w64-x86-64-gcc mingw-w64-x86-64-gtk3 \
|
||||
mingw-w64-x86-64-pkg-config mingw-w64-x86-64-cairo \
|
||||
mingw-w64-x86-64-gobject-introspection mingw-w64-x86-64-python3 \
|
||||
mingw-w64-x86-64-python3-importlib-metadata mingw-w64-x86-64-python3-gobject \
|
||||
mingw-w64-x86-64-python3-cairo mingw-w64-x86-64-python3-pip \
|
||||
mingw-w64-x86-64-python3-setuptools mingw-w64-x86-64-python3-zope.interface \
|
||||
mingw-w64-x86-64-python3-coverage mingw-w64-x86-64-python3-pytest
|
||||
```
|
||||
|
||||
[Clone the
|
||||
repository](https://help.github.com/en/github/creating-cloning-and-archiving-repositories/cloning-a-repository).
|
||||
```bash
|
||||
$ cd gaphor
|
||||
$ source venv
|
||||
$ poetry run gaphor
|
||||
```
|
||||
|
||||
## Packaging for Windows
|
||||
|
||||
In order to create an exe installation package for Windows, we utilize
|
||||
[PyInstaller](https://pyinstaller.org) 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](https://nsis.sourceforge.io/Main_Page) and a portable installer using
|
||||
[7-Zip](https://www.7-zip.org).
|
||||
|
||||
1. Follow the instructions for settings up a development environment above
|
||||
1. Run ``C:\msys64\mingw64.exe`` - a terminal window should pop up
|
||||
```bash
|
||||
$ cd win-installer
|
||||
$ source build-installer.sh
|
||||
```
|
||||
|
@ -1,76 +1,75 @@
|
||||
# Gaphor XML format
|
||||
# File Format (XML)
|
||||
|
||||
This format is meant to be a shorter and more obvious version of
|
||||
Gaphor's file format. The current format makes it pretty hard to do
|
||||
some decent XSLT parsing. In the current file format one has to compare
|
||||
the @name attribute with the model element name one wishes.
|
||||
Since Gaphor generates the Python data model from a Gaphor model file, it
|
||||
would be possible to also generate a Document Type Definition (DTD) as well.
|
||||
|
||||
Since the data model is generated from a Gaphor (0.2) model it would be
|
||||
a piece of cake to generate a DTD too.
|
||||
These are the things that should be distinguished:
|
||||
- Model elements
|
||||
- Associations with other model elements (referenced by ID):
|
||||
- Relations with multiplicty of zero to one (0..1)
|
||||
- Relations with multiplicty of zero or more (0..*)
|
||||
- Attributes, which always have a multiplicity of zero to one (0..1)
|
||||
- Diagrams
|
||||
- One canvas
|
||||
- Several canvas items
|
||||
- Derived attributes and associations are not saved
|
||||
- Model elements should have their class name as tag name:
|
||||
|
||||
These are the things that should be distinguished: - model elements -
|
||||
associations with other model elements (referenced by ID):
|
||||
```xml
|
||||
<Class id="DCE:xxx.xxx...">
|
||||
...
|
||||
</Class>
|
||||
<Package id="DCE:xxx...">
|
||||
...
|
||||
</Package>
|
||||
```
|
||||
|
||||
- 0..1 relations
|
||||
- 0..\* relations
|
||||
- Support for the two types of Associations, single and multiple:
|
||||
|
||||
- attributes (always have a multiplicity of 0..1)
|
||||
- diagrams
|
||||
- one canvas
|
||||
- several canvas items
|
||||
- derived attributes and associations are not saved of course.
|
||||
```xml
|
||||
<Class id="DCE:xxx.xxx...">
|
||||
<package>
|
||||
<ref refid="DCE:xxx.../>
|
||||
</package>
|
||||
</Class>
|
||||
<Package id="DCE:xxx...">
|
||||
<ownedClassifier>
|
||||
<reflist>
|
||||
<ref refid="DCE:xxx.xxx..."/>
|
||||
...
|
||||
</reflist>
|
||||
</ownedClassifier>
|
||||
</Package>
|
||||
```
|
||||
|
||||
Model elements should have their class name as tag name, e.g.:
|
||||
|
||||
<Class id="DCE:xxx.xxx...">
|
||||
...
|
||||
</Class>
|
||||
<Package id="DCE:xxx...">
|
||||
...
|
||||
</Package>
|
||||
|
||||
Associations are in two flavors: single and multiple:
|
||||
|
||||
<Class id="DCE:xxx.xxx...">
|
||||
<package>
|
||||
<ref refid="DCE:xxx.../>
|
||||
</package>
|
||||
</Class>
|
||||
<Package id="DCE:xxx...">
|
||||
<ownedClassifier>
|
||||
<reflist>
|
||||
<ref refid="DCE:xxx.xxx..."/>
|
||||
...
|
||||
</reflist>
|
||||
</ownedClassifier>
|
||||
</Package>
|
||||
|
||||
Associations contain primitive data, this can always be displayed as
|
||||
- Associations contain primitive data, this can always be displayed as
|
||||
strings:
|
||||
|
||||
<Class id="DCE:xxx.xxx...">
|
||||
<name>
|
||||
<![CDATA[My name]]>
|
||||
</name>
|
||||
<intvar>4</intvar>
|
||||
</Class>
|
||||
```xml
|
||||
<Class id="DCE:xxx.xxx...">
|
||||
<name>
|
||||
<![CDATA[My name]]>
|
||||
</name>
|
||||
<intvar>4</intvar>
|
||||
</Class>
|
||||
```
|
||||
|
||||
Canvas is the tag in which all canvas related stuff is placed. This is
|
||||
- Canvas is the tag in which all canvas related stuff is placed. This is
|
||||
the same way it is done now:
|
||||
|
||||
<Diagram id="...">
|
||||
<canvas>
|
||||
<item type="AssociationItem">
|
||||
<subject>
|
||||
<ref refid="DCE:..."/>
|
||||
</subject>
|
||||
<width><val>100.0</val></width>
|
||||
```xml
|
||||
<Diagram id="...">
|
||||
<canvas>
|
||||
<item type="AssociationItem">
|
||||
<subject> <ref refid="DCE:..."/> </subject> <width><val>100.0</val></width>
|
||||
</item>
|
||||
</canvas>
|
||||
</Diagram>
|
||||
</canvas>
|
||||
</Diagram>
|
||||
```
|
||||
|
||||
Most of the time you do not want to have anything to do with the canvas. The
|
||||
data stored there is specific to Gaphor. The model elements however, are
|
||||
interesting for other things such as code generators and conversion tools.
|
||||
Gaphor is also able to export diagrams into a SVG image, so that they can be
|
||||
used outside of the tool.
|
||||
|
||||
Most of the time you do not want to have anything to do with the canvas.
|
||||
The data stored there is specific to Gaphor. The model elements however,
|
||||
are interesting for other things such as code generators and conversion
|
||||
tools. Gaphor has export filters for SVG graphics, so diagrams can be
|
||||
exported in a independant way.
|
||||
|
@ -4,11 +4,11 @@
|
||||
with Gaphor.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import optparse
|
||||
import sys
|
||||
|
||||
from gaphor import Application
|
||||
import gaphor.UML as UML
|
||||
from gaphor import Application
|
||||
|
||||
# Setup command line options.
|
||||
usage = "usage: %prog [options] file.gaphor"
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
1. Install the GNOME SDK
|
||||
|
||||
$ flatpak install flathub org.gnome.Sdk 3.30
|
||||
$ flatpak install flathub org.gnome.Sdk 3.34
|
||||
|
||||
1. Add the Flathub repository and install the necessary SDK:
|
||||
|
||||
|
@ -3,7 +3,8 @@
|
||||
"""
|
||||
|
||||
import inspect
|
||||
from typing import overload, Generic, List, Type, TypeVar, Union
|
||||
from typing import Generic, List, Type, TypeVar, Union, overload
|
||||
|
||||
from gaphor.UML.event import AssociationUpdated
|
||||
from gaphor.UML.listmixins import querymixin, recursemixin
|
||||
|
||||
@ -72,11 +73,11 @@ class collection(Generic[T]):
|
||||
def __getitem__(self, key: int) -> T:
|
||||
...
|
||||
|
||||
@overload
|
||||
@overload # noqa: F811
|
||||
def __getitem__(self, key: slice) -> List[T]:
|
||||
...
|
||||
|
||||
def __getitem__(self, key: Union[int, slice]):
|
||||
def __getitem__(self, key: Union[int, slice]): # noqa: F811
|
||||
return self.items.__getitem__(key)
|
||||
|
||||
def __contains__(self, obj) -> bool:
|
||||
@ -93,9 +94,6 @@ class collection(Generic[T]):
|
||||
def __bool__(self):
|
||||
return self.items != []
|
||||
|
||||
# Maintains Python2 Compatibility
|
||||
__nonzero__ = __bool__
|
||||
|
||||
def append(self, value: T) -> None:
|
||||
if isinstance(value, self.type):
|
||||
self.property._set(self.object, value)
|
||||
@ -171,7 +169,7 @@ class collection(Generic[T]):
|
||||
|
||||
self.object.handle(AssociationUpdated(self.object, self.property))
|
||||
return True
|
||||
except IndexError as ex:
|
||||
except IndexError:
|
||||
return False
|
||||
except ValueError as ex:
|
||||
except ValueError:
|
||||
return False
|
||||
|
@ -1,10 +1,10 @@
|
||||
# vim: sw=4
|
||||
"""This module contains a model element Diagram which is the abstract
|
||||
representation of a UML diagram. Diagrams can be visualized and edited.
|
||||
|
||||
The DiagramCanvas class extends the gaphas.Canvas class.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import uuid
|
||||
|
||||
import gaphas
|
||||
@ -12,6 +12,8 @@ import gaphas
|
||||
from gaphor.UML.properties import umlproperty
|
||||
from gaphor.UML.uml2 import Namespace, PackageableElement
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DiagramCanvas(gaphas.Canvas):
|
||||
"""DiagramCanvas extends the gaphas.Canvas class. Updates to the canvas
|
||||
@ -108,7 +110,7 @@ class Diagram(Namespace, PackageableElement):
|
||||
for item in self.canvas.get_all_items():
|
||||
try:
|
||||
item.unlink()
|
||||
except:
|
||||
except (AttributeError, KeyError):
|
||||
pass
|
||||
|
||||
super().unlink()
|
||||
|
@ -5,19 +5,23 @@ Base class for UML model elements.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
__all__ = ["Element"]
|
||||
|
||||
import logging
|
||||
import uuid
|
||||
from typing import TYPE_CHECKING, Optional, Sequence, Type, Union
|
||||
|
||||
from typing import Optional, Sequence, TYPE_CHECKING, Type, Union
|
||||
from gaphor.UML.properties import relation_one, relation_many, umlproperty
|
||||
from gaphor.UML.elementdispatcher import EventWatcher
|
||||
from gaphor.UML.properties import relation_many, relation_one, umlproperty
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from gaphor.UML.elementfactory import ElementFactory # noqa
|
||||
from gaphor.UML.presentation import Presentation # noqa
|
||||
|
||||
|
||||
__all__ = ["Element"]
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class UnlinkEvent:
|
||||
"""Used to tell event handlers this element should be unlinked.
|
||||
"""
|
||||
@ -99,8 +103,8 @@ class Element:
|
||||
"""
|
||||
try:
|
||||
prop = getattr(type(self), name)
|
||||
except AttributeError as e:
|
||||
raise AttributeError(f"'{type(self).__name__}' has no property '{name}'")
|
||||
except AttributeError:
|
||||
log.exception(f"'{type(self).__name__}' has no property '{name}'")
|
||||
else:
|
||||
prop.load(self, value)
|
||||
|
||||
|
@ -1,15 +1,16 @@
|
||||
"""
|
||||
"""
|
||||
|
||||
from typing import Callable, Dict, List, Optional, Set, Tuple
|
||||
from logging import getLogger
|
||||
from typing import Callable, Dict, List, Optional, Set, Tuple
|
||||
|
||||
from gaphor.core import event_handler
|
||||
from gaphor.UML import uml2
|
||||
from gaphor.UML.event import (
|
||||
ElementUpdated,
|
||||
AssociationSet,
|
||||
AssociationAdded,
|
||||
AssociationDeleted,
|
||||
AssociationSet,
|
||||
ElementUpdated,
|
||||
ModelReady,
|
||||
)
|
||||
from gaphor.UML.properties import umlproperty
|
||||
|
@ -1,6 +1,10 @@
|
||||
"""Factory for and registration of model elements."""
|
||||
|
||||
import uuid
|
||||
from collections import OrderedDict
|
||||
from contextlib import contextmanager
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Callable,
|
||||
Dict,
|
||||
Iterator,
|
||||
@ -8,17 +12,13 @@ from typing import (
|
||||
Optional,
|
||||
Type,
|
||||
TypeVar,
|
||||
TYPE_CHECKING,
|
||||
)
|
||||
import uuid
|
||||
from contextlib import contextmanager
|
||||
from collections import OrderedDict
|
||||
|
||||
from gaphor.abc import Service
|
||||
from gaphor.UML.diagram import Diagram
|
||||
from gaphor.UML.element import Element, UnlinkEvent
|
||||
from gaphor.UML.elementdispatcher import ElementDispatcher
|
||||
from gaphor.UML.event import ElementCreated, ElementDeleted, ModelFlushed, ModelReady
|
||||
from gaphor.abc import Service
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from gaphor.services.eventmanager import EventManager # noqa
|
||||
|
@ -14,7 +14,6 @@ __all__ = ["querymixin", "recursemixin"]
|
||||
|
||||
from typing import Callable, List, TypeVar
|
||||
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
@ -57,7 +56,6 @@ def matcher(expr) -> Callable[[T], bool]:
|
||||
return bool(eval(compiled, {}, {"it": element}))
|
||||
except (AttributeError, NameError):
|
||||
# attribute does not (yet) exist
|
||||
# print 'No attribute', expr, d
|
||||
return False
|
||||
|
||||
return real_matcher
|
||||
@ -91,7 +89,7 @@ class querymixin:
|
||||
def __getitem__(self, key):
|
||||
try:
|
||||
# See if the list can deal with it (don't change default behaviour)
|
||||
return super().__getitem__(key) # type: ignore[misc]
|
||||
return super().__getitem__(key) # type: ignore[misc] # noqa: F821
|
||||
except TypeError:
|
||||
# Nope, try our matcher trick
|
||||
if isinstance(key, tuple):
|
||||
@ -99,8 +97,8 @@ class querymixin:
|
||||
else:
|
||||
remainder = None
|
||||
|
||||
matched = list(filter(matcher(key), self)) # type: ignore[call-overload]
|
||||
new_list = type(self)(matched) # type: ignore[call-arg]
|
||||
matched = list(filter(matcher(key), self)) # type: ignore[call-overload] # noqa: F821
|
||||
new_list = type(self)(matched) # type: ignore[call-arg] # noqa: F821
|
||||
return new_list.__getitem__(*remainder) if remainder else new_list
|
||||
|
||||
|
||||
@ -253,4 +251,4 @@ class recursemixin:
|
||||
if key == self._recursemixin_trigger:
|
||||
return self.proxy_class()(self)
|
||||
else:
|
||||
return super().__getitem__(key) # type: ignore[misc]
|
||||
return super().__getitem__(key) # type: ignore[misc] # noqa: F821
|
||||
|
@ -8,9 +8,30 @@ Functions collected in this module allow to
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Iterable
|
||||
import itertools
|
||||
from gaphor.UML.uml2 import *
|
||||
from typing import Iterable, Sequence
|
||||
|
||||
from gaphor.UML.uml2 import (
|
||||
Association,
|
||||
Class,
|
||||
Classifier,
|
||||
Component,
|
||||
Dependency,
|
||||
Element,
|
||||
Extension,
|
||||
ExtensionEnd,
|
||||
Generalization,
|
||||
Implementation,
|
||||
InstanceSpecification,
|
||||
Interface,
|
||||
Message,
|
||||
MessageOccurrenceSpecification,
|
||||
Property,
|
||||
Realization,
|
||||
Slot,
|
||||
Stereotype,
|
||||
Usage,
|
||||
)
|
||||
|
||||
|
||||
def stereotypes_str(element: Element, stereotypes: Sequence[str] = ()):
|
||||
|
@ -5,6 +5,7 @@ Base code for presentation elements
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
Callable,
|
||||
Generic,
|
||||
@ -12,10 +13,10 @@ from typing import (
|
||||
Optional,
|
||||
TypeVar,
|
||||
Union,
|
||||
TYPE_CHECKING,
|
||||
)
|
||||
from gaphor.UML.properties import relation_one, association
|
||||
|
||||
from gaphor.UML.element import Element
|
||||
from gaphor.UML.properties import association, relation_one
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from gaphas.canvas import Canvas # noqa
|
||||
|
@ -26,41 +26,42 @@ methods:
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
__all__ = ["attribute", "enumeration", "association", "derivedunion", "redefine"]
|
||||
|
||||
import logging
|
||||
from typing import (
|
||||
overload,
|
||||
Sequence,
|
||||
Type,
|
||||
Generic,
|
||||
TypeVar,
|
||||
Optional,
|
||||
Callable,
|
||||
List,
|
||||
Set,
|
||||
Union,
|
||||
TYPE_CHECKING,
|
||||
Callable,
|
||||
Generic,
|
||||
List,
|
||||
Optional,
|
||||
Sequence,
|
||||
Set,
|
||||
Type,
|
||||
TypeVar,
|
||||
Union,
|
||||
overload,
|
||||
)
|
||||
|
||||
from typing_extensions import Literal, Protocol
|
||||
|
||||
from gaphor.UML.collection import collection, collectionlist
|
||||
from gaphor.UML.event import (
|
||||
AssociationAdded,
|
||||
AssociationUpdated,
|
||||
AssociationDeleted,
|
||||
AttributeUpdated,
|
||||
AssociationSet,
|
||||
AssociationUpdated,
|
||||
AttributeUpdated,
|
||||
DerivedAdded,
|
||||
DerivedDeleted,
|
||||
DerivedUpdated,
|
||||
DerivedSet,
|
||||
DerivedUpdated,
|
||||
ElementUpdated,
|
||||
RedefinedSet,
|
||||
RedefinedAdded,
|
||||
RedefinedDeleted,
|
||||
RedefinedSet,
|
||||
)
|
||||
|
||||
__all__ = ["attribute", "enumeration", "association", "derivedunion", "redefine"]
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from gaphor.UML.element import Element
|
||||
@ -80,7 +81,7 @@ class relation_one(Protocol[E]):
|
||||
def __get__(self, obj: None, class_=None) -> relation_one[E]:
|
||||
...
|
||||
|
||||
@overload
|
||||
@overload # noqa: F811
|
||||
def __get__(self, obj, class_=None) -> E:
|
||||
...
|
||||
|
||||
@ -99,7 +100,7 @@ class relation_many(Protocol[E]):
|
||||
def __get__(self, obj: None, class_=None) -> relation_many[E]:
|
||||
...
|
||||
|
||||
@overload
|
||||
@overload # noqa: F811
|
||||
def __get__(self, obj, class_=None) -> collection[E]:
|
||||
...
|
||||
|
||||
@ -129,7 +130,7 @@ class umlproperty(Generic[T]):
|
||||
`propagate(self, event)`.
|
||||
|
||||
In some cases properties call out and delegate actions to the ElementFactory,
|
||||
for example in the case of event handling.
|
||||
for example, in the case of event handling.
|
||||
"""
|
||||
|
||||
lower: Lower = 0
|
||||
@ -170,11 +171,11 @@ class umlproperty(Generic[T]):
|
||||
def _get(self, obj: Literal[1]) -> Optional[T]:
|
||||
...
|
||||
|
||||
@overload
|
||||
@overload # noqa: F811
|
||||
def _get(self, obj: Literal["*"]) -> collection[T]:
|
||||
...
|
||||
|
||||
def _get(self, obj):
|
||||
def _get(self, obj): # noqa: F811
|
||||
raise NotImplementedError()
|
||||
|
||||
def _set(self, obj, value: Optional[T]) -> None:
|
||||
@ -281,12 +282,12 @@ class enumeration(umlproperty[str]):
|
||||
return self.default
|
||||
|
||||
def load(self, obj, value):
|
||||
if not value in self.values:
|
||||
if value not in self.values:
|
||||
raise AttributeError("Value should be one of %s" % str(self.values))
|
||||
setattr(obj, self._name, value)
|
||||
|
||||
def _set(self, obj, value):
|
||||
if not value in self.values:
|
||||
if value not in self.values:
|
||||
raise AttributeError("Value should be one of %s" % str(self.values))
|
||||
old = self._get(obj)
|
||||
if value == old:
|
||||
@ -462,8 +463,8 @@ class association(umlproperty[T]):
|
||||
|
||||
try:
|
||||
delattr(obj, self._name)
|
||||
except:
|
||||
pass
|
||||
except AttributeError:
|
||||
log.exception(f"Delete attribute failed for {obj} with {self._name}")
|
||||
else:
|
||||
if do_notify:
|
||||
self.handle(AssociationSet(obj, self, value, None))
|
||||
@ -479,8 +480,8 @@ class association(umlproperty[T]):
|
||||
items = c.items
|
||||
try:
|
||||
items.remove(value)
|
||||
except:
|
||||
pass
|
||||
except ValueError:
|
||||
log.exception(f"Removing {value} from list {items} failed")
|
||||
else:
|
||||
if do_notify:
|
||||
self.handle(AssociationDeleted(obj, self, value))
|
||||
@ -547,7 +548,7 @@ class associationstub(umlproperty[T]):
|
||||
try:
|
||||
values = getattr(obj, self._name)
|
||||
except AttributeError:
|
||||
pass
|
||||
log.exception(f"Failed to unlink {self._name} from {obj}")
|
||||
else:
|
||||
for value in set(values):
|
||||
self.association.__delete__(value, obj)
|
||||
|
@ -3,6 +3,7 @@ Test if the collection's list supports all trickery.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
from gaphor.UML.collection import collection, collectionlist
|
||||
|
||||
|
||||
|
@ -1,13 +1,12 @@
|
||||
import pytest
|
||||
|
||||
from gaphor import UML
|
||||
from gaphor.UML import Element
|
||||
from gaphor.UML.elementdispatcher import ElementDispatcher
|
||||
from gaphor.UML.elementdispatcher import EventWatcher
|
||||
from gaphor.UML.elementfactory import ElementFactory
|
||||
from gaphor.UML.properties import association
|
||||
from gaphor.services.eventmanager import EventManager
|
||||
from gaphor.tests import TestCase
|
||||
from gaphor.UML import Element
|
||||
from gaphor.UML.elementdispatcher import ElementDispatcher, EventWatcher
|
||||
from gaphor.UML.elementfactory import ElementFactory
|
||||
from gaphor.UML.properties import association
|
||||
|
||||
|
||||
class Event:
|
||||
@ -301,7 +300,7 @@ class ElementDispatcherAsServiceTestCase(TestCase):
|
||||
dispatcher = self.dispatcher
|
||||
element = self.element_factory.create(UML.Association)
|
||||
p1 = element.memberEnd = self.element_factory.create(UML.Property)
|
||||
p2 = element.memberEnd = self.element_factory.create(UML.Property)
|
||||
element.memberEnd = self.element_factory.create(UML.Property)
|
||||
|
||||
assert len(element.memberEnd) == 2
|
||||
dispatcher.subscribe(self._handler, element, "memberEnd.name")
|
||||
|
@ -2,11 +2,18 @@ import gc
|
||||
|
||||
import pytest
|
||||
|
||||
from gaphor.UML import *
|
||||
from gaphor.UML.event import *
|
||||
from gaphor.application import Application
|
||||
from gaphor.core import event_handler
|
||||
from gaphor.services.eventmanager import EventManager
|
||||
from gaphor.UML import Parameter
|
||||
from gaphor.UML.elementfactory import ElementFactory
|
||||
from gaphor.UML.event import (
|
||||
ElementCreated,
|
||||
ElementDeleted,
|
||||
ModelFlushed,
|
||||
ModelReady,
|
||||
ServiceEvent,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@ -16,7 +23,7 @@ def factory():
|
||||
|
||||
|
||||
def test_create(factory):
|
||||
p = factory.create(Parameter)
|
||||
factory.create(Parameter)
|
||||
assert len(list(factory.values())) == 1
|
||||
|
||||
|
||||
@ -32,7 +39,7 @@ def test_flush(factory):
|
||||
|
||||
|
||||
def test_without_application(factory):
|
||||
p = factory.create(Parameter)
|
||||
factory.create(Parameter)
|
||||
assert factory.size() == 1, factory.size()
|
||||
|
||||
factory.flush()
|
||||
@ -103,7 +110,7 @@ def element_factory():
|
||||
|
||||
|
||||
def test_create_event(element_factory):
|
||||
p = element_factory.create(Parameter)
|
||||
element_factory.create(Parameter)
|
||||
assert isinstance(last_event, ElementCreated)
|
||||
assert handled
|
||||
|
||||
|
@ -1,9 +1,16 @@
|
||||
import pytest
|
||||
from gaphor.UML.element import Element
|
||||
from gaphor.UML.event import AssociationUpdated
|
||||
from gaphor.UML.properties import *
|
||||
|
||||
from gaphor.application import Application
|
||||
from gaphor.core import event_handler
|
||||
from gaphor.UML.element import Element
|
||||
from gaphor.UML.event import AssociationUpdated
|
||||
from gaphor.UML.properties import (
|
||||
association,
|
||||
attribute,
|
||||
derivedunion,
|
||||
enumeration,
|
||||
redefine,
|
||||
)
|
||||
|
||||
|
||||
def test_association_1_x():
|
||||
@ -91,8 +98,8 @@ def test_association_1_1():
|
||||
c = C()
|
||||
try:
|
||||
a.one = c
|
||||
except Exception as e:
|
||||
pass # ok
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
assert a.one is not c
|
||||
|
||||
@ -404,7 +411,7 @@ def test_notify():
|
||||
A.enum = enumeration("enum", ("one", "two"), "one")
|
||||
|
||||
a = A()
|
||||
assert a.notified == None
|
||||
assert a.notified is None
|
||||
a.assoc = A()
|
||||
assert a.notified == "assoc", a.notified
|
||||
a.attr = "newval"
|
||||
@ -413,7 +420,7 @@ def test_notify():
|
||||
assert a.notified == "enum", a.notified
|
||||
a.notified = None
|
||||
a.enum = "two" # should not notify since value hasn't changed.
|
||||
assert a.notified == None
|
||||
assert a.notified is None
|
||||
|
||||
|
||||
def test_derivedunion():
|
||||
@ -463,9 +470,9 @@ def test_derivedunion_notify():
|
||||
E.u = derivedunion(E, "u", A, 0, "*", E.a)
|
||||
|
||||
e = E()
|
||||
assert e.notified == False
|
||||
assert e.notified is False
|
||||
e.a = A()
|
||||
assert e.notified == True
|
||||
assert e.notified is True
|
||||
|
||||
|
||||
def test_derivedunion_listmixins():
|
||||
@ -668,7 +675,7 @@ def test_redefine_subclass():
|
||||
class A(Element):
|
||||
is_unlinked = False
|
||||
|
||||
def unlink():
|
||||
def unlink(self):
|
||||
self.is_unlinked = True
|
||||
Element.unlink()
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import pytest
|
||||
|
||||
from gaphor.services.eventmanager import EventManager
|
||||
import gaphor.UML as UML
|
||||
from gaphor.services.eventmanager import EventManager
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@ -131,42 +131,52 @@ def test_dependency(factory):
|
||||
|
||||
def test_element_import(factory):
|
||||
element = factory.create(UML.ElementImport)
|
||||
assert element
|
||||
|
||||
|
||||
def test_enumeration(factory):
|
||||
element = factory.create(UML.Enumeration)
|
||||
assert element
|
||||
|
||||
|
||||
def test_generalization(factory):
|
||||
element = factory.create(UML.Generalization)
|
||||
assert element
|
||||
|
||||
|
||||
def test_interface(factory):
|
||||
element = factory.create(UML.Interface)
|
||||
assert element
|
||||
|
||||
|
||||
def test_namespace(factory):
|
||||
element = factory.create(UML.Namespace)
|
||||
assert element
|
||||
|
||||
|
||||
def test_operation(factory):
|
||||
element = factory.create(UML.Operation)
|
||||
assert element
|
||||
|
||||
|
||||
def test_package(factory):
|
||||
element = factory.create(UML.Package)
|
||||
assert element
|
||||
|
||||
|
||||
def test_parameter(factory):
|
||||
element = factory.create(UML.Parameter)
|
||||
assert element
|
||||
|
||||
|
||||
def test_property(factory):
|
||||
element = factory.create(UML.Property)
|
||||
assert element
|
||||
|
||||
|
||||
def test_realization(factory):
|
||||
element = factory.create(UML.Realization)
|
||||
assert element
|
||||
|
||||
|
||||
def test_ids(factory):
|
||||
@ -195,35 +205,13 @@ def testOwnedMember_Unlink(factory):
|
||||
assert [p] == factory.lselect()
|
||||
|
||||
|
||||
# def test_lower_upper(self):
|
||||
# """
|
||||
# Test MultiplicityElement.{lower|upper}
|
||||
# """
|
||||
# assert UML.MultiplicityElement.lowerValue in UML.MultiplicityElement.lower.subsets
|
||||
#
|
||||
# e = UML.MultiplicityElement()
|
||||
# e.lowerValue = '2'
|
||||
# assert e.lower == '2', e.lower
|
||||
#
|
||||
# assert UML.MultiplicityElement.upperValue in UML.MultiplicityElement.upper.subsets
|
||||
#
|
||||
# e.upperValue = 'up'
|
||||
# assert UML.MultiplicityElement.upper.version == 4, UML.MultiplicityElement.upper.version
|
||||
# assert e.upper == 'up'
|
||||
# e.upperValue = 'down'
|
||||
# assert UML.MultiplicityElement.upper.version == 5, UML.MultiplicityElement.upper.version
|
||||
# assert e.upper == 'down', e.upper
|
||||
#
|
||||
# # TODO: test signal handling
|
||||
|
||||
|
||||
def test_property_is_composite(factory):
|
||||
p = UML.Property()
|
||||
assert p.isComposite == False, p.isComposite
|
||||
assert p.isComposite is False, p.isComposite
|
||||
p.aggregation = "shared"
|
||||
assert p.isComposite == False, p.isComposite
|
||||
assert p.isComposite is False, p.isComposite
|
||||
p.aggregation = "composite"
|
||||
assert p.isComposite == True, p.isComposite
|
||||
assert p.isComposite is True, p.isComposite
|
||||
|
||||
|
||||
def test_association_endType(factory):
|
||||
|
@ -4,10 +4,10 @@ Formatting of UML model elements into text tests.
|
||||
|
||||
import pytest
|
||||
|
||||
import gaphor.UML.uml2 as UML
|
||||
from gaphor.services.eventmanager import EventManager
|
||||
from gaphor.UML.elementfactory import ElementFactory
|
||||
from gaphor.UML.umlfmt import format
|
||||
import gaphor.UML.uml2 as UML
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
@ -3,15 +3,15 @@ Parsing of UML model elements from string tests.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
from gaphor import UML
|
||||
from gaphor.services.eventmanager import EventManager
|
||||
from gaphor.UML.elementfactory import ElementFactory
|
||||
from gaphor.UML.umllex import attribute_pat, operation_pat, parameter_pat
|
||||
from gaphor import UML
|
||||
|
||||
|
||||
def dump_prop(prop):
|
||||
m = attribute_pat.match(prop)
|
||||
# print m.groupdict()
|
||||
attribute_pat.match(prop)
|
||||
|
||||
|
||||
def dump_oper(oper):
|
||||
@ -21,13 +21,11 @@ def dump_oper(oper):
|
||||
else:
|
||||
# set name to oper
|
||||
return
|
||||
# print g('vis'), g('name'), g('type'), g('mult_l'), g('mult_u'), g('tags')
|
||||
if g("params"):
|
||||
params = g("params")
|
||||
while params:
|
||||
m = parameter_pat.match(params)
|
||||
g = m.group
|
||||
# print ' ', g('dir') or 'in', g('name'), g('type'), g('mult_l'), g('mult_u'), g('default'), g('tags')
|
||||
params = g("rest")
|
||||
|
||||
|
||||
|
@ -8,8 +8,8 @@ for normal properties.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
import itertools
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import List, Optional, Tuple, Union
|
||||
|
@ -4,7 +4,6 @@ Formatting of UML elements like attributes, operations, stereotypes, etc.
|
||||
|
||||
import io
|
||||
import re
|
||||
|
||||
from functools import singledispatch
|
||||
|
||||
from gaphor.UML import uml2 as UML
|
||||
|
@ -11,6 +11,7 @@ __all__ = ["parse_property", "parse_operation"]
|
||||
|
||||
import re
|
||||
from functools import singledispatch
|
||||
|
||||
import gaphor.UML.uml2 as uml2
|
||||
|
||||
|
||||
@ -159,7 +160,7 @@ def parse_attribute(el: uml2.Property, s: str) -> None:
|
||||
el.defaultValue = None
|
||||
else:
|
||||
g = m.group
|
||||
create = el.model.create
|
||||
el.model.create
|
||||
_set_visibility(el, g("vis"))
|
||||
el.isDerived = g("derived") and True or False
|
||||
el.name = g("name")
|
||||
@ -167,13 +168,6 @@ def parse_attribute(el: uml2.Property, s: str) -> None:
|
||||
el.lowerValue = g("mult_l")
|
||||
el.upperValue = g("mult_u")
|
||||
el.defaultValue = g("default")
|
||||
# Skip tags: should do something with stereotypes?
|
||||
# tags = g('tags')
|
||||
# if tags:
|
||||
# for t in map(str.strip, tags.split(',')):
|
||||
# tv = create(UML.LiteralSpecification)
|
||||
# tv.value = t
|
||||
# el.taggedValue = tv
|
||||
|
||||
|
||||
def parse_association_end(el: uml2.Property, s: str) -> None:
|
||||
@ -182,7 +176,7 @@ def parse_association_end(el: uml2.Property, s: str) -> None:
|
||||
two strings. It is automatically figured out which string is fed to the
|
||||
parser.
|
||||
"""
|
||||
create = el.model.create
|
||||
el.model.create
|
||||
|
||||
# if no name, then clear as there could be some garbage
|
||||
# due to previous parsing (i.e. '[1'
|
||||
@ -200,12 +194,6 @@ def parse_association_end(el: uml2.Property, s: str) -> None:
|
||||
g = m.group
|
||||
el.lowerValue = g("mult_l")
|
||||
el.upperValue = g("mult_u")
|
||||
# tags = g('tags')
|
||||
# if tags:
|
||||
# for t in map(str.strip, tags.split(',')):
|
||||
# tv = create(UML.LiteralSpecification)
|
||||
# tv.value = t
|
||||
# el.taggedValue = tv
|
||||
else:
|
||||
m = association_end_name_pat.match(s)
|
||||
g = m.group
|
||||
@ -226,15 +214,6 @@ def parse_association_end(el: uml2.Property, s: str) -> None:
|
||||
el.lowerValue = None
|
||||
el.upperValue = g("mult_u")
|
||||
|
||||
# tags = g('tags')
|
||||
# if tags:
|
||||
# while el.taggedValue:
|
||||
# el.taggedValue[0].unlink()
|
||||
# for t in map(str.strip, tags.split(',')):
|
||||
# tv = create(UML.LiteralSpecification)
|
||||
# tv.value = t
|
||||
# el.taggedValue = tv
|
||||
|
||||
|
||||
@parse.register(uml2.Property)
|
||||
def parse_property(el: uml2.Property, s: str) -> None:
|
||||
@ -268,13 +247,6 @@ def parse_operation(el: uml2.Operation, s: str) -> None:
|
||||
p.typeValue = g("type")
|
||||
p.lowerValue = g("mult_l")
|
||||
p.upperValue = g("mult_u")
|
||||
# FIXME: Maybe add to Operation.ownedRule?
|
||||
# tags = g('tags')
|
||||
# if tags:
|
||||
# for t in map(str.strip, tags.split(',')):
|
||||
# tv = create(UML.LiteralSpecification)
|
||||
# tv.value = t
|
||||
# p.taggedValue = tv
|
||||
|
||||
pindex = 0
|
||||
params = g("params")
|
||||
@ -293,12 +265,6 @@ def parse_operation(el: uml2.Operation, s: str) -> None:
|
||||
p.lowerValue = g("mult_l")
|
||||
p.upperValue = g("mult_u")
|
||||
p.defaultValue = g("default")
|
||||
# tags = g('tags')
|
||||
# if tags:
|
||||
# for t in map(str.strip, tags.split(',')):
|
||||
# tv = create(UML.LiteralSpecification)
|
||||
# tv.value = t
|
||||
# p.taggedValue = tv
|
||||
el.formalParameter = p
|
||||
|
||||
# Do the next parameter:
|
||||
|
@ -18,8 +18,8 @@ passes them to the main Application instance.
|
||||
__version__ = "1.1.1"
|
||||
__all__ = ["main"]
|
||||
|
||||
from optparse import OptionParser
|
||||
import logging
|
||||
from optparse import OptionParser
|
||||
|
||||
from gaphor.application import Application
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
from gaphor import main
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
@ -2,9 +2,8 @@
|
||||
|
||||
"""
|
||||
|
||||
from typing import Optional, Sequence, get_type_hints
|
||||
import platform
|
||||
|
||||
from typing import Optional, Sequence, get_type_hints
|
||||
|
||||
_primary = "⌘" if platform.system() == "Darwin" else "Ctrl"
|
||||
|
||||
|
@ -9,13 +9,14 @@ All important services are present in the application object:
|
||||
- action sets
|
||||
"""
|
||||
|
||||
from typing import Dict, Type
|
||||
import logging
|
||||
import inspect
|
||||
import logging
|
||||
from typing import Dict, Type
|
||||
|
||||
import importlib_metadata
|
||||
|
||||
from gaphor.event import ServiceInitializedEvent, ServiceShutdownEvent
|
||||
from gaphor.abc import Service
|
||||
from gaphor.event import ServiceInitializedEvent, ServiceShutdownEvent
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -4,8 +4,8 @@ The Core module provides an entry point for Gaphor's core constructs.
|
||||
An average module should only need to import this module.
|
||||
"""
|
||||
|
||||
from gaphor.action import action, primary
|
||||
from gaphor.application import Application
|
||||
from gaphor.i18n import gettext
|
||||
from gaphor.services.eventmanager import event_handler
|
||||
from gaphor.transaction import Transaction, transactional
|
||||
from gaphor.action import primary, action
|
||||
from gaphor.i18n import _
|
||||
|
@ -6,8 +6,6 @@ diagram).
|
||||
|
||||
import gi
|
||||
|
||||
gi.require_version("PangoCairo", "1.0")
|
||||
|
||||
import gaphor.diagram.actions
|
||||
import gaphor.diagram.classes
|
||||
import gaphor.diagram.components
|
||||
@ -16,3 +14,5 @@ import gaphor.diagram.interactions
|
||||
import gaphor.diagram.profiles
|
||||
import gaphor.diagram.states
|
||||
import gaphor.diagram.usecases
|
||||
|
||||
gi.require_version("PangoCairo", "1.0")
|
||||
|
@ -1,14 +1,14 @@
|
||||
from gaphor.diagram.actions.action import (
|
||||
AcceptEventActionItem,
|
||||
ActionItem,
|
||||
SendSignalActionItem,
|
||||
AcceptEventActionItem,
|
||||
)
|
||||
from gaphor.diagram.actions.activitynodes import (
|
||||
ActivityNodeItem,
|
||||
ActivityFinalNodeItem,
|
||||
ActivityNodeItem,
|
||||
DecisionNodeItem,
|
||||
FlowFinalNodeItem,
|
||||
ForkNodeItem,
|
||||
DecisionNodeItem,
|
||||
InitialNodeItem,
|
||||
)
|
||||
from gaphor.diagram.actions.flow import FlowItem
|
||||
|
@ -3,10 +3,10 @@ Action diagram item.
|
||||
"""
|
||||
|
||||
from gaphor import UML
|
||||
from gaphor.UML.modelfactory import stereotypes_str
|
||||
from gaphor.diagram.presentation import ElementPresentation, Named
|
||||
from gaphor.diagram.support import represents
|
||||
from gaphor.diagram.shapes import Box, EditableText, Text, draw_border
|
||||
from gaphor.diagram.support import represents
|
||||
from gaphor.UML.modelfactory import stereotypes_str
|
||||
|
||||
|
||||
@represents(UML.Action)
|
||||
|
@ -1,6 +1,6 @@
|
||||
from gaphor.core import transactional
|
||||
from gaphor.diagram.inlineeditors import InlineEditor, popup_entry, show_popover
|
||||
from gaphor.diagram.actions.activitynodes import ForkNodeItem
|
||||
from gaphor.diagram.inlineeditors import InlineEditor, popup_entry, show_popover
|
||||
|
||||
|
||||
@InlineEditor.register(ForkNodeItem)
|
||||
|
@ -1,9 +1,9 @@
|
||||
from gaphor import UML
|
||||
from gaphor.diagram.grouping import Group, AbstractGroup
|
||||
from gaphor.diagram.actions.partition import PartitionItem
|
||||
from gaphor.diagram.actions.action import ActionItem
|
||||
from gaphor.diagram.actions.activitynodes import ActivityNodeItem, ForkNodeItem
|
||||
from gaphor.diagram.actions.objectnode import ObjectNodeItem
|
||||
from gaphor.diagram.actions.action import ActionItem
|
||||
from gaphor.diagram.actions.partition import PartitionItem
|
||||
from gaphor.diagram.grouping import AbstractGroup, Group
|
||||
|
||||
|
||||
@Group.register(PartitionItem, PartitionItem)
|
||||
|
@ -1,15 +1,16 @@
|
||||
import math
|
||||
|
||||
from gi.repository import Gtk
|
||||
|
||||
from gaphor import UML
|
||||
from gaphor.core import _, transactional
|
||||
from gaphor.core import gettext, transactional
|
||||
from gaphor.diagram.actions import ForkNodeItem, ObjectNodeItem
|
||||
from gaphor.diagram.propertypages import (
|
||||
PropertyPages,
|
||||
NamedElementPropertyPage,
|
||||
NamedItemPropertyPage,
|
||||
PropertyPages,
|
||||
create_hbox_label,
|
||||
)
|
||||
from gaphor.diagram.actions import ObjectNodeItem, ForkNodeItem
|
||||
|
||||
|
||||
@PropertyPages.register(ObjectNodeItem)
|
||||
@ -29,7 +30,7 @@ class ObjectNodePropertyPage(NamedItemPropertyPage):
|
||||
if not subject:
|
||||
return page
|
||||
|
||||
hbox = create_hbox_label(self, page, _("Upper bound"))
|
||||
hbox = create_hbox_label(self, page, gettext("Upper bound"))
|
||||
entry = Gtk.Entry()
|
||||
entry.set_text(subject.upperBound or "")
|
||||
entry.connect("changed", self._on_upper_bound_change)
|
||||
@ -44,7 +45,7 @@ class ObjectNodePropertyPage(NamedItemPropertyPage):
|
||||
hbox.pack_start(combo, False, True, 0)
|
||||
|
||||
hbox = create_hbox_label(self, page, "")
|
||||
button = Gtk.CheckButton(_("Ordering"))
|
||||
button = Gtk.CheckButton(gettext("Ordering"))
|
||||
button.set_active(self.item.show_ordering)
|
||||
button.connect("toggled", self._on_ordering_show_change)
|
||||
hbox.pack_start(button, False, True, 0)
|
||||
@ -85,13 +86,13 @@ class JoinNodePropertyPage(NamedItemPropertyPage):
|
||||
page.pack_start(hbox, False, True, 0)
|
||||
|
||||
if isinstance(subject, UML.JoinNode):
|
||||
hbox = create_hbox_label(self, page, _("Join specification"))
|
||||
hbox = create_hbox_label(self, page, gettext("Join specification"))
|
||||
entry = Gtk.Entry()
|
||||
entry.set_text(subject.joinSpec or "")
|
||||
entry.connect("changed", self._on_join_spec_change)
|
||||
hbox.pack_start(entry, True, True, 0)
|
||||
|
||||
button = Gtk.CheckButton(_("Horizontal"))
|
||||
button = Gtk.CheckButton(gettext("Horizontal"))
|
||||
button.set_active(self.item.matrix[2] != 0)
|
||||
button.connect("toggled", self._on_horizontal_change)
|
||||
page.pack_start(button, False, True, 0)
|
||||
@ -128,7 +129,7 @@ class FlowPropertyPageAbstract(NamedElementPropertyPage):
|
||||
if not subject:
|
||||
return page
|
||||
|
||||
hbox = create_hbox_label(self, page, _("Guard"))
|
||||
hbox = create_hbox_label(self, page, gettext("Guard"))
|
||||
entry = Gtk.Entry()
|
||||
entry.set_text(subject.guard or "")
|
||||
changed_id = entry.connect("changed", self._on_guard_change)
|
||||
|
@ -2,21 +2,20 @@
|
||||
Activity control nodes.
|
||||
"""
|
||||
|
||||
import math
|
||||
import ast
|
||||
import math
|
||||
|
||||
from gaphas.util import path_ellipse
|
||||
from gaphas.state import observed, reversible_property
|
||||
from gaphas.item import Handle, Item, LinePort
|
||||
from gaphas.constraint import EqualsConstraint, LessThanConstraint
|
||||
from gaphas.geometry import Rectangle, distance_line_point
|
||||
from gaphas.item import Handle, Item, LinePort
|
||||
from gaphas.state import observed, reversible_property
|
||||
from gaphas.util import path_ellipse
|
||||
|
||||
from gaphor import UML
|
||||
from gaphor.UML.modelfactory import stereotypes_str
|
||||
from gaphor.diagram.presentation import ElementPresentation, Named
|
||||
from gaphor.diagram.support import represents
|
||||
from gaphor.diagram.shapes import Box, EditableText, IconBox, Text
|
||||
|
||||
from gaphor.diagram.support import represents
|
||||
from gaphor.UML.modelfactory import stereotypes_str
|
||||
|
||||
DEFAULT_JOIN_SPEC = "and"
|
||||
|
||||
|
@ -6,10 +6,10 @@ Contains also implementation to split flows using activity edge connectors.
|
||||
|
||||
|
||||
from gaphor import UML
|
||||
from gaphor.UML.modelfactory import stereotypes_str
|
||||
from gaphor.diagram.presentation import LinePresentation
|
||||
from gaphor.diagram.shapes import Box, EditableText, Text, draw_arrow_tail
|
||||
from gaphor.diagram.support import represents
|
||||
from gaphor.diagram.shapes import Box, Text, EditableText, draw_arrow_tail
|
||||
from gaphor.UML.modelfactory import stereotypes_str
|
||||
|
||||
|
||||
@represents(UML.ControlFlow)
|
||||
|
@ -5,22 +5,22 @@ Flow item adapter connections.
|
||||
from typing import Type, Union
|
||||
|
||||
from gaphor import UML
|
||||
from gaphor.diagram.connectors import IConnect, UnaryRelationshipConnect
|
||||
from gaphor.diagram.actions.action import (
|
||||
AcceptEventActionItem,
|
||||
ActionItem,
|
||||
SendSignalActionItem,
|
||||
AcceptEventActionItem,
|
||||
)
|
||||
from gaphor.diagram.actions.activitynodes import (
|
||||
ForkNodeItem,
|
||||
ActivityNodeItem,
|
||||
InitialNodeItem,
|
||||
ActivityFinalNodeItem,
|
||||
FlowFinalNodeItem,
|
||||
ActivityNodeItem,
|
||||
DecisionNodeItem,
|
||||
FlowFinalNodeItem,
|
||||
ForkNodeItem,
|
||||
InitialNodeItem,
|
||||
)
|
||||
from gaphor.diagram.actions.flow import FlowItem
|
||||
from gaphor.diagram.actions.objectnode import ObjectNodeItem
|
||||
from gaphor.diagram.connectors import IConnect, UnaryRelationshipConnect
|
||||
|
||||
|
||||
class FlowConnect(UnaryRelationshipConnect):
|
||||
@ -60,7 +60,6 @@ class FlowConnect(UnaryRelationshipConnect):
|
||||
|
||||
def connect_subject(self, handle):
|
||||
line = self.line
|
||||
element = self.element
|
||||
|
||||
# TODO: connect opposite side again (in case it's a join/fork or
|
||||
# decision/merge node)
|
||||
@ -133,7 +132,6 @@ class FlowForkDecisionNodeConnect(FlowConnect):
|
||||
"""
|
||||
fork_node_cls = self.fork_node_cls
|
||||
join_node_cls = self.join_node_cls
|
||||
line = self.line
|
||||
element = self.element
|
||||
subject = element.subject
|
||||
if len(subject.incoming) > 1 and len(subject.outgoing) < 2:
|
||||
@ -171,7 +169,6 @@ class FlowForkDecisionNodeConnect(FlowConnect):
|
||||
"""
|
||||
fork_node_cls = self.fork_node_cls
|
||||
join_node_cls = self.join_node_cls
|
||||
line = self.line
|
||||
element = self.element
|
||||
if element.combined:
|
||||
join_node = element.subject
|
||||
|
@ -5,12 +5,12 @@ Object node item.
|
||||
import ast
|
||||
|
||||
from gaphas.state import observed, reversible_property
|
||||
from gaphor import UML
|
||||
from gaphor.UML.modelfactory import stereotypes_str
|
||||
from gaphor.diagram.presentation import ElementPresentation, Named
|
||||
from gaphor.diagram.shapes import Box, IconBox, EditableText, Text, draw_border
|
||||
from gaphor.diagram.support import represents
|
||||
|
||||
from gaphor import UML
|
||||
from gaphor.diagram.presentation import ElementPresentation, Named
|
||||
from gaphor.diagram.shapes import Box, EditableText, IconBox, Text, draw_border
|
||||
from gaphor.diagram.support import represents
|
||||
from gaphor.UML.modelfactory import stereotypes_str
|
||||
|
||||
DEFAULT_UPPER_BOUND = "*"
|
||||
|
||||
|
@ -10,11 +10,11 @@ TODO: partition can be resized only horizontally or vertically, therefore
|
||||
from typing import List
|
||||
|
||||
from gaphor import UML
|
||||
from gaphor.UML.modelfactory import stereotypes_str
|
||||
from gaphor.diagram.presentation import ElementPresentation, Named
|
||||
from gaphor.diagram.shapes import Box, Text, draw_highlight
|
||||
from gaphor.diagram.support import represents
|
||||
from gaphor.diagram.text import VerticalAlign
|
||||
from gaphor.diagram.shapes import Box, Text, draw_highlight
|
||||
from gaphor.UML.modelfactory import stereotypes_str
|
||||
|
||||
|
||||
@represents(UML.ActivityPartition)
|
||||
|
@ -2,10 +2,9 @@
|
||||
|
||||
from gi.repository import Gtk
|
||||
|
||||
|
||||
from gaphor.core import _, transactional
|
||||
from gaphor.diagram.propertypages import PropertyPages, NamedItemPropertyPage
|
||||
from gaphor.core import gettext, transactional
|
||||
from gaphor.diagram.actions.partition import PartitionItem
|
||||
from gaphor.diagram.propertypages import NamedItemPropertyPage, PropertyPages
|
||||
|
||||
|
||||
@PropertyPages.register(PartitionItem)
|
||||
@ -20,7 +19,7 @@ class PartitionPropertyPage(NamedItemPropertyPage):
|
||||
if item.subject:
|
||||
if not item._toplevel:
|
||||
hbox = Gtk.HBox(spacing=12)
|
||||
button = Gtk.CheckButton(_("External"))
|
||||
button = Gtk.CheckButton(gettext("External"))
|
||||
button.set_active(item.subject.isExternal)
|
||||
button.connect("toggled", self._on_external_change)
|
||||
hbox.pack_start(button, True, True, 0)
|
||||
|
@ -1,6 +1,6 @@
|
||||
import gaphor.UML as UML
|
||||
from gaphor.diagram.actions.activitynodes import DecisionNodeItem, ForkNodeItem
|
||||
from gaphor.tests.testcase import TestCase
|
||||
from gaphor.diagram.actions.activitynodes import ForkNodeItem, DecisionNodeItem
|
||||
|
||||
|
||||
class ActivityNodesTestCase(TestCase):
|
||||
|
@ -1,7 +1,8 @@
|
||||
from gaphas.canvas import Context, instant_cairo_context
|
||||
|
||||
import gaphor.UML as UML
|
||||
from gaphor.tests.testcase import TestCase
|
||||
from gaphor.diagram.actions.flow import FlowItem
|
||||
from gaphor.tests.testcase import TestCase
|
||||
|
||||
|
||||
class FlowTestCase(TestCase):
|
||||
|
@ -3,19 +3,20 @@ Flow item connection adapters tests.
|
||||
"""
|
||||
|
||||
from typing import Type
|
||||
from gaphor.tests import TestCase
|
||||
|
||||
from gaphor import UML
|
||||
from gaphor.diagram.actions.action import ActionItem
|
||||
from gaphor.diagram.actions.flow import FlowItem
|
||||
from gaphor.diagram.actions.activitynodes import (
|
||||
InitialNodeItem,
|
||||
ForkNodeItem,
|
||||
FlowFinalNodeItem,
|
||||
ActivityFinalNodeItem,
|
||||
ActivityNodeItem,
|
||||
DecisionNodeItem,
|
||||
FlowFinalNodeItem,
|
||||
ForkNodeItem,
|
||||
InitialNodeItem,
|
||||
)
|
||||
from gaphor.diagram.actions.flow import FlowItem
|
||||
from gaphor.diagram.actions.objectnode import ObjectNodeItem
|
||||
from gaphor.tests import TestCase
|
||||
|
||||
|
||||
class FlowItemBasicNodesConnectionTestCase(TestCase):
|
||||
@ -334,7 +335,6 @@ class FlowItemDesisionAndForkNodes:
|
||||
flow3 = self.create(FlowItem)
|
||||
a1 = self.create(ActionItem, UML.Action)
|
||||
a2 = self.create(ActionItem, UML.Action)
|
||||
a3 = self.create(ActionItem, UML.Action)
|
||||
jn = self.create(self.item_cls, self.fork_node_cls)
|
||||
|
||||
assert isinstance(jn.subject, self.fork_node_cls)
|
||||
@ -367,7 +367,6 @@ class FlowItemDesisionAndForkNodes:
|
||||
flow3 = self.create(FlowItem)
|
||||
a1 = self.create(ActionItem, UML.Action)
|
||||
a2 = self.create(ActionItem, UML.Action)
|
||||
a3 = self.create(ActionItem, UML.Action)
|
||||
jn = self.create(self.item_cls, self.join_node_cls)
|
||||
|
||||
# connect actions first
|
||||
@ -409,7 +408,6 @@ class FlowItemDesisionAndForkNodes:
|
||||
flow4 = self.create(FlowItem)
|
||||
a1 = self.create(ActionItem, UML.Action)
|
||||
a2 = self.create(ActionItem, UML.Action)
|
||||
a3 = self.create(ActionItem, UML.Action)
|
||||
a4 = self.create(ActionItem, UML.Action)
|
||||
jn = self.create(self.item_cls, self.join_node_cls)
|
||||
|
||||
@ -444,15 +442,12 @@ class FlowItemDesisionAndForkNodes:
|
||||
|
||||
Flow `flow4` will force the node to become a combined node.
|
||||
"""
|
||||
canvas = self.diagram.canvas
|
||||
|
||||
flow1 = self.create(FlowItem)
|
||||
flow2 = self.create(FlowItem)
|
||||
flow3 = self.create(FlowItem)
|
||||
flow4 = self.create(FlowItem)
|
||||
a1 = self.create(ActionItem, UML.Action)
|
||||
a2 = self.create(ActionItem, UML.Action)
|
||||
a3 = self.create(ActionItem, UML.Action)
|
||||
a4 = self.create(ActionItem, UML.Action)
|
||||
jn = self.create(self.item_cls, self.join_node_cls)
|
||||
|
||||
|
@ -3,8 +3,8 @@ Tests for grouping functionality in Gaphor.
|
||||
"""
|
||||
|
||||
from gaphor import UML
|
||||
from gaphor.diagram.actions import ActionItem, PartitionItem
|
||||
from gaphor.tests import TestCase
|
||||
from gaphor.diagram.actions import PartitionItem, ActionItem
|
||||
|
||||
|
||||
class PartitionGroupTestCase(TestCase):
|
||||
|
@ -1,6 +1,6 @@
|
||||
import gaphor.UML as UML
|
||||
from gaphor.tests.testcase import TestCase
|
||||
from gaphor.diagram.actions.objectnode import ObjectNodeItem
|
||||
from gaphor.tests.testcase import TestCase
|
||||
|
||||
|
||||
class ObjectNodeTestCase(TestCase):
|
||||
|
@ -2,7 +2,7 @@ from gaphor.diagram.classes.association import AssociationItem
|
||||
from gaphor.diagram.classes.dependency import DependencyItem
|
||||
from gaphor.diagram.classes.generalization import GeneralizationItem
|
||||
from gaphor.diagram.classes.implementation import ImplementationItem
|
||||
from gaphor.diagram.classes.interface import InterfaceItem, Folded
|
||||
from gaphor.diagram.classes.interface import Folded, InterfaceItem
|
||||
from gaphor.diagram.classes.klass import ClassItem
|
||||
from gaphor.diagram.classes.package import PackageItem
|
||||
|
||||
|
@ -13,14 +13,12 @@ Plan:
|
||||
|
||||
|
||||
import ast
|
||||
from math import pi, atan2
|
||||
from math import atan2, pi
|
||||
|
||||
from gaphas.geometry import Rectangle
|
||||
from gaphas.geometry import distance_rectangle_point
|
||||
from gaphas.geometry import Rectangle, distance_rectangle_point
|
||||
from gaphas.state import reversible_property
|
||||
|
||||
from gaphor import UML
|
||||
from gaphor.UML.modelfactory import stereotypes_str
|
||||
from gaphor.diagram.presentation import LinePresentation, Named
|
||||
from gaphor.diagram.shapes import (
|
||||
Box,
|
||||
@ -29,13 +27,14 @@ from gaphor.diagram.shapes import (
|
||||
draw_default_head,
|
||||
draw_default_tail,
|
||||
)
|
||||
from gaphor.diagram.support import represents
|
||||
from gaphor.diagram.text import (
|
||||
text_size,
|
||||
middle_segment,
|
||||
text_draw,
|
||||
text_draw_focus_box,
|
||||
middle_segment,
|
||||
text_size,
|
||||
)
|
||||
from gaphor.diagram.support import represents
|
||||
from gaphor.UML.modelfactory import stereotypes_str
|
||||
|
||||
|
||||
@represents(UML.Association)
|
||||
|
@ -1,16 +1,16 @@
|
||||
"""Classes related (dependency, implementation) adapter connections."""
|
||||
|
||||
from gaphor import UML
|
||||
from gaphor.diagram.classes.association import AssociationItem
|
||||
from gaphor.diagram.classes.dependency import DependencyItem
|
||||
from gaphor.diagram.classes.generalization import GeneralizationItem
|
||||
from gaphor.diagram.classes.implementation import ImplementationItem
|
||||
from gaphor.diagram.connectors import (
|
||||
IConnect,
|
||||
UnaryRelationshipConnect,
|
||||
RelationshipConnect,
|
||||
UnaryRelationshipConnect,
|
||||
)
|
||||
from gaphor.diagram.presentation import Named, Classified, ElementPresentation
|
||||
from gaphor.diagram.classes.dependency import DependencyItem
|
||||
from gaphor.diagram.classes.implementation import ImplementationItem
|
||||
from gaphor.diagram.classes.generalization import GeneralizationItem
|
||||
from gaphor.diagram.classes.association import AssociationItem
|
||||
from gaphor.diagram.presentation import Classified, ElementPresentation, Named
|
||||
|
||||
|
||||
@IConnect.register(Named, DependencyItem)
|
||||
@ -18,7 +18,6 @@ class DependencyConnect(RelationshipConnect):
|
||||
"""Connect two Named elements using a Dependency."""
|
||||
|
||||
def allow(self, handle, port):
|
||||
line = self.line
|
||||
element = self.element
|
||||
|
||||
# Element should be a NamedElement
|
||||
@ -49,7 +48,6 @@ class DependencyConnect(RelationshipConnect):
|
||||
line = self.line
|
||||
|
||||
if line.auto_dependency:
|
||||
canvas = line.canvas
|
||||
opposite = line.opposite(handle)
|
||||
|
||||
other = self.get_connected(opposite)
|
||||
|
@ -1,17 +1,17 @@
|
||||
from typing import Optional
|
||||
|
||||
from gi.repository import Gtk
|
||||
from gaphas.geometry import Rectangle, distance_point_point_fast
|
||||
from gi.repository import Gtk
|
||||
|
||||
from gaphor import UML
|
||||
from gaphor.core import transactional
|
||||
from gaphor.diagram.classes.association import AssociationItem
|
||||
from gaphor.diagram.inlineeditors import (
|
||||
InlineEditor,
|
||||
editable_text_box,
|
||||
popup_entry,
|
||||
show_popover,
|
||||
editable_text_box,
|
||||
)
|
||||
from gaphor.diagram.classes.association import AssociationItem
|
||||
|
||||
|
||||
@InlineEditor.register(AssociationItem)
|
||||
|
@ -1,27 +1,28 @@
|
||||
import logging
|
||||
from gi.repository import Gtk
|
||||
|
||||
from gaphas.decorators import AsyncIO
|
||||
from gi.repository import Gtk
|
||||
|
||||
from gaphor import UML
|
||||
from gaphor.core import _, transactional
|
||||
from gaphor.diagram.propertypages import PropertyPages, PropertyPageBase
|
||||
from gaphor.diagram.propertypages import (
|
||||
NamedElementPropertyPage,
|
||||
NamedItemPropertyPage,
|
||||
EditableTreeModel,
|
||||
create_tree_view,
|
||||
create_hbox_label,
|
||||
create_uml_combo,
|
||||
)
|
||||
from gaphor.core import gettext, transactional
|
||||
from gaphor.diagram.classes import (
|
||||
ClassItem,
|
||||
InterfaceItem,
|
||||
AssociationItem,
|
||||
ClassItem,
|
||||
DependencyItem,
|
||||
ImplementationItem,
|
||||
Folded,
|
||||
InterfaceItem,
|
||||
)
|
||||
from gaphor.diagram.components.connector import ConnectorItem
|
||||
from gaphor.diagram.propertypages import (
|
||||
EditableTreeModel,
|
||||
NamedElementPropertyPage,
|
||||
NamedItemPropertyPage,
|
||||
PropertyPageBase,
|
||||
PropertyPages,
|
||||
create_hbox_label,
|
||||
create_tree_view,
|
||||
create_uml_combo,
|
||||
)
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@ -116,7 +117,7 @@ class ClassPropertyPage(NamedElementPropertyPage):
|
||||
label.set_justify(Gtk.Justification.LEFT)
|
||||
self.size_group.add_widget(label)
|
||||
hbox.pack_start(label, False, True, 0)
|
||||
button = Gtk.CheckButton(label=_("Abstract"))
|
||||
button = Gtk.CheckButton(label=gettext("Abstract"))
|
||||
button.set_active(self.subject.isAbstract)
|
||||
|
||||
button.connect("toggled", self._on_abstract_change)
|
||||
@ -145,7 +146,7 @@ class InterfacePropertyPage(NamedItemPropertyPage):
|
||||
self.size_group.add_widget(label)
|
||||
hbox.pack_start(label, False, True, 0)
|
||||
|
||||
button = Gtk.CheckButton(_("Folded"))
|
||||
button = Gtk.CheckButton(gettext("Folded"))
|
||||
button.set_active(item.folded != Folded.NONE)
|
||||
button.connect("toggled", self._on_fold_change)
|
||||
|
||||
@ -195,7 +196,7 @@ class AttributesPage(PropertyPageBase):
|
||||
label = Gtk.Label(label="")
|
||||
label.set_justify(Gtk.Justification.LEFT)
|
||||
hbox.pack_start(label, False, True, 0)
|
||||
button = Gtk.CheckButton(label=_("Show attributes"))
|
||||
button = Gtk.CheckButton(label=gettext("Show attributes"))
|
||||
button.set_active(self.item.show_attributes)
|
||||
button.connect("toggled", self._on_show_attributes_change)
|
||||
hbox.pack_start(button, True, True, 0)
|
||||
@ -212,7 +213,9 @@ Add and edit class attributes according to UML syntax. Attribute syntax examples
|
||||
- + attr: int
|
||||
- # /attr: int
|
||||
"""
|
||||
tree_view = create_tree_view(self.model, (_("Attributes"), _("S")), tip)
|
||||
tree_view = create_tree_view(
|
||||
self.model, (gettext("Attributes"), gettext("S")), tip
|
||||
)
|
||||
page.pack_start(tree_view, True, True, 0)
|
||||
|
||||
@AsyncIO(single=True)
|
||||
@ -268,7 +271,7 @@ class OperationsPage(PropertyPageBase):
|
||||
label = Gtk.Label(label="")
|
||||
label.set_justify(Gtk.Justification.LEFT)
|
||||
hbox.pack_start(label, False, True, 0)
|
||||
button = Gtk.CheckButton(label=_("Show operations"))
|
||||
button = Gtk.CheckButton(label=gettext("Show operations"))
|
||||
button.set_active(self.item.show_operations)
|
||||
button.connect("toggled", self._on_show_operations_change)
|
||||
hbox.pack_start(button, True, True, 0)
|
||||
@ -284,7 +287,9 @@ Add and edit class operations according to UML syntax. Operation syntax examples
|
||||
- + call(a: int, b: str)
|
||||
- # call(a: int: b: str): bool
|
||||
"""
|
||||
tree_view = create_tree_view(self.model, (_("Operation"), _("A"), _("S")), tip)
|
||||
tree_view = create_tree_view(
|
||||
self.model, (gettext("Operation"), gettext("A"), gettext("S")), tip
|
||||
)
|
||||
page.pack_start(tree_view, True, True, 0)
|
||||
|
||||
@AsyncIO(single=True)
|
||||
@ -327,10 +332,10 @@ class DependencyPropertyPage(PropertyPageBase):
|
||||
order = 0
|
||||
|
||||
DEPENDENCY_TYPES = (
|
||||
(_("Dependency"), UML.Dependency),
|
||||
(_("Usage"), UML.Usage),
|
||||
(_("Realization"), UML.Realization),
|
||||
(_("Implementation"), UML.Implementation),
|
||||
(gettext("Dependency"), UML.Dependency),
|
||||
(gettext("Usage"), UML.Usage),
|
||||
(gettext("Realization"), UML.Realization),
|
||||
(gettext("Implementation"), UML.Implementation),
|
||||
)
|
||||
|
||||
def __init__(self, item):
|
||||
@ -342,7 +347,7 @@ class DependencyPropertyPage(PropertyPageBase):
|
||||
def construct(self):
|
||||
page = Gtk.VBox()
|
||||
|
||||
hbox = create_hbox_label(self, page, _("Dependency type"))
|
||||
hbox = create_hbox_label(self, page, gettext("Dependency type"))
|
||||
|
||||
self.combo = create_uml_combo(
|
||||
self.DEPENDENCY_TYPES, self._on_dependency_type_change
|
||||
@ -351,7 +356,7 @@ class DependencyPropertyPage(PropertyPageBase):
|
||||
|
||||
hbox = create_hbox_label(self, page, "")
|
||||
|
||||
button = Gtk.CheckButton(_("Automatic"))
|
||||
button = Gtk.CheckButton(gettext("Automatic"))
|
||||
button.set_active(self.item.auto_dependency)
|
||||
button.connect("toggled", self._on_auto_dependency_change)
|
||||
hbox.pack_start(button, True, True, 0)
|
||||
@ -426,7 +431,7 @@ class AssociationPropertyPage(NamedItemPropertyPage):
|
||||
self.size_group.add_widget(label)
|
||||
hbox.pack_start(label, False, True, 0)
|
||||
|
||||
button = Gtk.CheckButton(label=_("Show direction"))
|
||||
button = Gtk.CheckButton(label=gettext("Show direction"))
|
||||
button.set_active(self.item.show_direction)
|
||||
button.connect("toggled", self._on_show_direction_change)
|
||||
hbox.pack_start(button, True, True, 0)
|
||||
@ -439,11 +444,11 @@ class AssociationPropertyPage(NamedItemPropertyPage):
|
||||
|
||||
page.pack_start(hbox, False, True, 0)
|
||||
|
||||
box = self.construct_end(_("Head"), self.item.head_end)
|
||||
box = self.construct_end(gettext("Head"), self.item.head_end)
|
||||
if box:
|
||||
page.pack_start(box, False, True, 0)
|
||||
|
||||
box = self.construct_end(_("Tail"), self.item.tail_end)
|
||||
box = self.construct_end(gettext("Tail"), self.item.tail_end)
|
||||
if box:
|
||||
page.pack_start(box, False, True, 0)
|
||||
|
||||
@ -498,7 +503,7 @@ class AssociationPropertyPage(NamedItemPropertyPage):
|
||||
expander.add(page)
|
||||
expander.show_all()
|
||||
vbox.pack_start(expander, False, True, 0)
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
log.error(
|
||||
"Could not construct property page for " + name, exc_info=True
|
||||
)
|
||||
|
@ -18,12 +18,13 @@ type of a dependency in automatic way.
|
||||
import ast
|
||||
|
||||
import gaphas
|
||||
|
||||
from gaphor import UML
|
||||
from gaphor.UML.modelfactory import stereotypes_str
|
||||
from gaphor.diagram.classes.interface import Folded, InterfacePort
|
||||
from gaphor.diagram.presentation import LinePresentation
|
||||
from gaphor.diagram.shapes import Text
|
||||
from gaphor.diagram.support import represents
|
||||
from gaphor.diagram.classes.interface import Folded, InterfacePort
|
||||
from gaphor.UML.modelfactory import stereotypes_str
|
||||
|
||||
|
||||
@represents(UML.Dependency)
|
||||
|
@ -4,10 +4,10 @@ Generalization --
|
||||
|
||||
|
||||
from gaphor import UML
|
||||
from gaphor.UML.modelfactory import stereotypes_str
|
||||
from gaphor.diagram.presentation import LinePresentation
|
||||
from gaphor.diagram.shapes import Box, Text
|
||||
from gaphor.diagram.support import represents
|
||||
from gaphor.UML.modelfactory import stereotypes_str
|
||||
|
||||
|
||||
@represents(UML.Generalization)
|
||||
|
@ -5,11 +5,11 @@ Implementation of interface.
|
||||
import gaphas
|
||||
|
||||
from gaphor import UML
|
||||
from gaphor.UML.modelfactory import stereotypes_str
|
||||
from gaphor.diagram.classes.interface import Folded, InterfacePort
|
||||
from gaphor.diagram.presentation import LinePresentation
|
||||
from gaphor.diagram.shapes import Text
|
||||
from gaphor.diagram.support import represents
|
||||
from gaphor.diagram.classes.interface import Folded, InterfacePort
|
||||
from gaphor.UML.modelfactory import stereotypes_str
|
||||
|
||||
|
||||
@represents(UML.Implementation)
|
||||
|
@ -69,31 +69,30 @@ Folding and unfolding is performed by `InterfacePropertyPage` class.
|
||||
"""
|
||||
|
||||
import ast
|
||||
from math import pi
|
||||
from enum import Enum
|
||||
from math import pi
|
||||
|
||||
from gaphas.canvas import Canvas
|
||||
from gaphas.connector import LinePort
|
||||
from gaphas.geometry import distance_line_point, distance_point_point
|
||||
from gaphas.item import NW, NE, SE, SW
|
||||
from gaphas.canvas import Canvas
|
||||
from gaphas.item import NE, NW, SE, SW
|
||||
|
||||
from gaphor import UML
|
||||
from gaphor.diagram.presentation import (
|
||||
ElementPresentation,
|
||||
Classified,
|
||||
from_package_str,
|
||||
)
|
||||
from gaphor.diagram.shapes import Box, IconBox, EditableText, Text, draw_border
|
||||
from gaphor.diagram.text import FontWeight, VerticalAlign
|
||||
from gaphor.diagram.support import represents
|
||||
|
||||
from gaphor.diagram.classes.klass import (
|
||||
attribute_watches,
|
||||
operation_watches,
|
||||
attributes_compartment,
|
||||
operation_watches,
|
||||
operations_compartment,
|
||||
)
|
||||
from gaphor.diagram.classes.stereotype import stereotype_compartments
|
||||
from gaphor.diagram.presentation import (
|
||||
Classified,
|
||||
ElementPresentation,
|
||||
from_package_str,
|
||||
)
|
||||
from gaphor.diagram.shapes import Box, EditableText, IconBox, Text, draw_border
|
||||
from gaphor.diagram.support import represents
|
||||
from gaphor.diagram.text import FontWeight, VerticalAlign
|
||||
|
||||
|
||||
class Folded(Enum):
|
||||
@ -147,7 +146,6 @@ class InterfacePort(LinePort):
|
||||
d = distance_point_point((px, py), pos)
|
||||
return (px, py), d
|
||||
else:
|
||||
p2 = self.end
|
||||
d, pl = distance_line_point(self.start, self.end, pos)
|
||||
return pl, d
|
||||
|
||||
|
@ -7,11 +7,11 @@ for details.
|
||||
"""
|
||||
|
||||
|
||||
from gaphor.diagram.connectors import IConnect
|
||||
from gaphor.diagram.classes.classconnect import DependencyConnect, ImplementationConnect
|
||||
from gaphor.diagram.classes.interface import InterfaceItem, Folded
|
||||
from gaphor.diagram.classes.implementation import ImplementationItem
|
||||
from gaphor.diagram.classes.dependency import DependencyItem
|
||||
from gaphor.diagram.classes.implementation import ImplementationItem
|
||||
from gaphor.diagram.classes.interface import Folded, InterfaceItem
|
||||
from gaphor.diagram.connectors import IConnect
|
||||
|
||||
|
||||
@IConnect.register(InterfaceItem, ImplementationItem)
|
||||
|
@ -3,8 +3,8 @@ import logging
|
||||
from gaphor import UML
|
||||
from gaphor.diagram.classes.stereotype import stereotype_compartments
|
||||
from gaphor.diagram.presentation import (
|
||||
ElementPresentation,
|
||||
Classified,
|
||||
ElementPresentation,
|
||||
from_package_str,
|
||||
)
|
||||
from gaphor.diagram.shapes import (
|
||||
@ -14,15 +14,14 @@ from gaphor.diagram.shapes import (
|
||||
draw_border,
|
||||
draw_top_separator,
|
||||
)
|
||||
from gaphor.diagram.support import represents
|
||||
from gaphor.diagram.text import (
|
||||
TextAlign,
|
||||
VerticalAlign,
|
||||
FontStyle,
|
||||
FontWeight,
|
||||
TextAlign,
|
||||
TextDecoration,
|
||||
VerticalAlign,
|
||||
)
|
||||
from gaphor.diagram.support import represents
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -3,11 +3,11 @@ Package diagram item.
|
||||
"""
|
||||
|
||||
from gaphor import UML
|
||||
from gaphor.UML.modelfactory import stereotypes_str
|
||||
from gaphor.diagram.presentation import ElementPresentation, Named, from_package_str
|
||||
from gaphor.diagram.shapes import Box, EditableText, Text
|
||||
from gaphor.diagram.text import FontWeight
|
||||
from gaphor.diagram.support import represents
|
||||
from gaphor.diagram.text import FontWeight
|
||||
from gaphor.UML.modelfactory import stereotypes_str
|
||||
|
||||
|
||||
@represents(UML.Package)
|
||||
|
@ -2,10 +2,10 @@
|
||||
Unnit tests for AssociationItem.
|
||||
"""
|
||||
|
||||
from gaphor.tests import TestCase
|
||||
from gaphor import UML
|
||||
from gaphor.diagram.classes.klass import ClassItem
|
||||
from gaphor.diagram.classes.association import AssociationItem
|
||||
from gaphor.diagram.classes.klass import ClassItem
|
||||
from gaphor.tests import TestCase
|
||||
|
||||
|
||||
class AssociationItemTestCase(TestCase):
|
||||
@ -46,9 +46,6 @@ class AssociationItemTestCase(TestCase):
|
||||
|
||||
def test_association_end_updates(self):
|
||||
"""Test association end navigability connected to a class"""
|
||||
from gaphas.canvas import Canvas
|
||||
|
||||
canvas = Canvas()
|
||||
c1 = self.create(ClassItem, UML.Class)
|
||||
c2 = self.create(ClassItem, UML.Class)
|
||||
a = self.create(AssociationItem)
|
||||
|
@ -3,9 +3,10 @@ Test classes.
|
||||
"""
|
||||
|
||||
from gaphas.canvas import instant_cairo_context
|
||||
|
||||
from gaphor import UML
|
||||
from gaphor.tests.testcase import TestCase
|
||||
from gaphor.diagram.classes.klass import ClassItem
|
||||
from gaphor.tests.testcase import TestCase
|
||||
|
||||
|
||||
def compartments(item):
|
||||
|
@ -2,14 +2,14 @@
|
||||
Classes related adapter connection tests.
|
||||
"""
|
||||
|
||||
from gaphor.tests import TestCase
|
||||
from gaphor import UML
|
||||
from gaphor.diagram.usecases.actor import ActorItem
|
||||
from gaphor.diagram.classes.dependency import DependencyItem
|
||||
from gaphor.diagram.classes.klass import ClassItem
|
||||
from gaphor.diagram.classes.interface import InterfaceItem
|
||||
from gaphor.diagram.classes.association import AssociationItem
|
||||
from gaphor.diagram.classes.dependency import DependencyItem
|
||||
from gaphor.diagram.classes.generalization import GeneralizationItem
|
||||
from gaphor.diagram.classes.interface import InterfaceItem
|
||||
from gaphor.diagram.classes.klass import ClassItem
|
||||
from gaphor.diagram.usecases.actor import ActorItem
|
||||
from gaphor.tests import TestCase
|
||||
|
||||
|
||||
class DependencyTestCase(TestCase):
|
||||
|
@ -3,10 +3,10 @@ Test implementation (interface realization) item connectors.
|
||||
"""
|
||||
|
||||
from gaphor import UML
|
||||
from gaphor.tests import TestCase
|
||||
from gaphor.diagram.classes.klass import ClassItem
|
||||
from gaphor.diagram.classes.implementation import ImplementationItem
|
||||
from gaphor.diagram.classes.interface import InterfaceItem
|
||||
from gaphor.diagram.classes.klass import ClassItem
|
||||
from gaphor.tests import TestCase
|
||||
|
||||
|
||||
class ImplementationTestCase(TestCase):
|
||||
|
@ -2,9 +2,9 @@
|
||||
Test classes.
|
||||
"""
|
||||
|
||||
from gaphor.tests import TestCase
|
||||
from gaphor import UML
|
||||
from gaphor.diagram.classes.interface import InterfaceItem, Folded
|
||||
from gaphor.diagram.classes.interface import Folded, InterfaceItem
|
||||
from gaphor.tests import TestCase
|
||||
|
||||
|
||||
class InterfaceTestCase(TestCase):
|
||||
|
@ -3,14 +3,14 @@ Test connections to folded interface.
|
||||
"""
|
||||
|
||||
from gaphor import UML
|
||||
from gaphor.tests import TestCase
|
||||
from gaphor.diagram.classes.implementation import ImplementationItem
|
||||
from gaphor.diagram.classes.klass import ClassItem
|
||||
from gaphor.diagram.classes.interface import InterfaceItem, Folded
|
||||
from gaphor.diagram.classes.association import AssociationItem
|
||||
from gaphor.diagram.classes.dependency import DependencyItem
|
||||
from gaphor.diagram.classes.generalization import GeneralizationItem
|
||||
from gaphor.diagram.classes.implementation import ImplementationItem
|
||||
from gaphor.diagram.classes.interface import Folded, InterfaceItem
|
||||
from gaphor.diagram.classes.klass import ClassItem
|
||||
from gaphor.diagram.general.commentline import CommentLineItem
|
||||
from gaphor.tests import TestCase
|
||||
|
||||
|
||||
class ImplementationTestCase(TestCase):
|
||||
|