Merge branch 'master' into i18n

This commit is contained in:
Arjan Molenaar
2019-11-17 22:26:43 +01:00
251 changed files with 1610 additions and 2032 deletions

7
.flake8 Normal file
View 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
View File

@ -16,6 +16,7 @@ tmp.gaphor
pip-wheel-metadata
# Virtual env stuff
.env/
.venv/
.vscode/
.python-version

View File

@ -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]

View File

@ -4,7 +4,4 @@ sphinx:
configuration: docs/conf.py
python:
install:
- method: pip
path: .
extra_requirements:
- docs
- requirements: docs/requirements.txt

View File

@ -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

View File

@ -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()

View File

@ -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(

View File

@ -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?

View File

@ -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

View File

@ -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

View File

@ -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).

View File

@ -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)

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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
```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

View File

@ -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

View File

@ -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.

View File

@ -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.
![image](oneclass.png)
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
![image](navpopup.png)
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.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

View File

@ -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).

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

View File

@ -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.
![image](simplestereotype.png)
Stereotypes can be applied to basically all elements in a model.
![image](stereotypedclass.png)
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.
![image](stereotypeedit.png)

View File

@ -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.
![image](gaphor-treeview.png)
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.
![image](elementeditor.png)
The properties shown depend on the element that is selected.

View File

@ -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.

View File

@ -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
View 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

View File

@ -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)

View File

@ -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.)

View File

@ -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.
```

View File

@ -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

View File

@ -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)

View File

@ -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
```

View File

@ -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.

View File

@ -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"

View File

@ -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:

View File

@ -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

View File

@ -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()

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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] = ()):

View File

@ -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

View File

@ -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)

View File

@ -3,6 +3,7 @@ Test if the collection's list supports all trickery.
"""
import pytest
from gaphor.UML.collection import collection, collectionlist

View File

@ -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")

View File

@ -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

View File

@ -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()

View File

@ -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):

View File

@ -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

View File

@ -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")

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -1,5 +1,4 @@
from gaphor import main
if __name__ == "__main__":
main()

View File

@ -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"

View File

@ -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__)

View File

@ -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 _

View File

@ -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")

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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"

View File

@ -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)

View File

@ -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

View File

@ -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 = "*"

View File

@ -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)

View File

@ -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)

View File

@ -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):

View File

@ -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):

View File

@ -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)

View File

@ -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):

View File

@ -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):

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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
)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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__)

View File

@ -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)

View File

@ -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)

View File

@ -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):

View File

@ -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):

View File

@ -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):

View File

@ -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):

View File

@ -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):

Some files were not shown because too many files have changed in this diff Show More