Merge master
Signed-off-by: Dan Yeaw <dan@yeaw.me>
This commit is contained in:
commit
369ba17021
@ -1,244 +0,0 @@
|
|||||||
"""
|
|
||||||
This module provides a means to automatically layout diagrams.
|
|
||||||
|
|
||||||
The layout is done like this:
|
|
||||||
- First all nodes (Classes, packages, comments) on a diagram are determined
|
|
||||||
- A vertical ordering is determined based on the inheritance
|
|
||||||
- A horizontal ordering is determined based on associations and dependencies
|
|
||||||
- The nodes are moved to their place
|
|
||||||
- Lines are reconnected to the nodes, so everything looks pretty.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
import logging
|
|
||||||
import random
|
|
||||||
|
|
||||||
from gaphor.core import action, transactional
|
|
||||||
from gaphor.diagram.presentation import LinePresentation
|
|
||||||
from gaphor.diagram.classes import GeneralizationItem, ImplementationItem
|
|
||||||
from gaphor.abc import Service, ActionProvider
|
|
||||||
from gaphor.plugins.diagramlayout import toposort
|
|
||||||
from gaphor.ui.abc import UIComponent
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class DiagramLayout(Service, ActionProvider):
|
|
||||||
def __init__(self, component_registry):
|
|
||||||
self.component_registry = component_registry
|
|
||||||
|
|
||||||
def shutdown(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def get_current_diagram(self):
|
|
||||||
return self.component_registry.get(
|
|
||||||
UIComponent, "diagrams"
|
|
||||||
).get_current_diagram()
|
|
||||||
|
|
||||||
def update(self):
|
|
||||||
self.sensitive = bool(self.get_current_diagram())
|
|
||||||
|
|
||||||
@action(
|
|
||||||
name="diagram-layout", label="La_yout diagram", tooltip="simple diagram layout"
|
|
||||||
)
|
|
||||||
def execute(self):
|
|
||||||
d = self.get_current_diagram()
|
|
||||||
self.layout_diagram(d)
|
|
||||||
|
|
||||||
@transactional
|
|
||||||
def layout_diagram(self, diag):
|
|
||||||
layout_diagram(diag)
|
|
||||||
|
|
||||||
|
|
||||||
MARGIN = 100
|
|
||||||
|
|
||||||
|
|
||||||
def layout_diagram(diag):
|
|
||||||
"""
|
|
||||||
So an attempt to layout (order) the items on a diagram. The items
|
|
||||||
should already be placed on the diagram and the items should already be
|
|
||||||
connected.
|
|
||||||
|
|
||||||
This function works on the diagram items (hence it does not check relations
|
|
||||||
in the datamodel, only the ones drawn on the diagram) to produce a
|
|
||||||
decent layout.
|
|
||||||
"""
|
|
||||||
nodes = []
|
|
||||||
primary_nodes = []
|
|
||||||
relations = []
|
|
||||||
other_relations = []
|
|
||||||
|
|
||||||
# Make sure all items are updated
|
|
||||||
diag.canvas.update_now()
|
|
||||||
|
|
||||||
# First extract data from the diagram (which ones are the nodes, and
|
|
||||||
# the relationships).
|
|
||||||
for item in diag.canvas.get_root_items():
|
|
||||||
if isinstance(item, (GeneralizationItem, ImplementationItem)):
|
|
||||||
# Primary relationships, should be drawn top-down
|
|
||||||
try:
|
|
||||||
relations.append(
|
|
||||||
(item.handles[0].connected_to, item.handles[-1].connected_to)
|
|
||||||
)
|
|
||||||
primary_nodes.extend(relations[-1])
|
|
||||||
except Exception as e:
|
|
||||||
log.error(e)
|
|
||||||
elif isinstance(item, LinePresentation):
|
|
||||||
# Secondary (associations, dependencies) may be drawn top-down
|
|
||||||
# or left-right
|
|
||||||
try:
|
|
||||||
other_relations.append(
|
|
||||||
(item.handles[0].connected_to, item.handles[-1].connected_to)
|
|
||||||
)
|
|
||||||
# other_relations.append((item.handles[-1].connected_to,
|
|
||||||
# item.handles[0].connected_to))
|
|
||||||
except Exception as e:
|
|
||||||
log.error(e)
|
|
||||||
else:
|
|
||||||
nodes.append(item)
|
|
||||||
|
|
||||||
# Add some randomness:
|
|
||||||
random.shuffle(other_relations)
|
|
||||||
primary_nodes = uniq(primary_nodes)
|
|
||||||
|
|
||||||
# Find out our horizontal and vertical sorting
|
|
||||||
sorted = toposort.toposort(nodes, relations, 0)
|
|
||||||
other_sorted = toposort.toposort(nodes, other_relations, 0)
|
|
||||||
|
|
||||||
if not sorted:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Move nodes from the first (generalization) row to the other rows
|
|
||||||
# if they are not superclasses for some other class
|
|
||||||
# Run the list twice, just to ensure no items are left behind.
|
|
||||||
for item in list(sorted[0]) * 2:
|
|
||||||
if item not in primary_nodes and item in sorted[0]:
|
|
||||||
# Find nodes that do have a relation to this one
|
|
||||||
related = find_related_nodes(item, other_relations)
|
|
||||||
# Figure out what row(s) they're on
|
|
||||||
row = find_row(item, related, sorted[1:])
|
|
||||||
if row:
|
|
||||||
# print 'moving', item.subject.name, 'to row', sorted.index(row)
|
|
||||||
sorted[0].remove(item)
|
|
||||||
row.append(item)
|
|
||||||
|
|
||||||
# Order each row based on the sort order of other_sorted
|
|
||||||
# (the secondary sort alg.).
|
|
||||||
for row in sorted:
|
|
||||||
for other_row in other_sorted:
|
|
||||||
for other_item in other_row:
|
|
||||||
if other_item in row:
|
|
||||||
row.remove(other_item)
|
|
||||||
row.append(other_item)
|
|
||||||
|
|
||||||
# Place the nodes on the diagram.
|
|
||||||
y = MARGIN / 2
|
|
||||||
for row in sorted:
|
|
||||||
x = MARGIN / 2
|
|
||||||
maxy = 0
|
|
||||||
for item in row:
|
|
||||||
if not item:
|
|
||||||
continue
|
|
||||||
maxy = max(maxy, item.height)
|
|
||||||
a = item.matrix
|
|
||||||
a = (a[0], a[1], a[2], a[3], x, y)
|
|
||||||
item.matrix = a
|
|
||||||
item.request_update()
|
|
||||||
x += item.width + MARGIN
|
|
||||||
y += maxy + MARGIN
|
|
||||||
|
|
||||||
# Reattach the relationships to the nodes, in a way that it looks nice.
|
|
||||||
simple_layout_lines(diag)
|
|
||||||
|
|
||||||
|
|
||||||
def simple_layout_lines(diag):
|
|
||||||
"""
|
|
||||||
Just do the layout of the lines in a diagram. The nodes (class, package)
|
|
||||||
are left where they are (use layout_diagram() if you want to reorder
|
|
||||||
everything).
|
|
||||||
|
|
||||||
The line layout is basically very simple: just draw straight lines
|
|
||||||
between nodes on the diagram.
|
|
||||||
"""
|
|
||||||
lines = {}
|
|
||||||
for item in diag.canvas.get_root_items():
|
|
||||||
if isinstance(item, LinePresentation):
|
|
||||||
# Secondary (associations, dependencies) may be drawn top-down
|
|
||||||
# or left-right
|
|
||||||
try:
|
|
||||||
lines[item] = (
|
|
||||||
item.handles[0].connected_to,
|
|
||||||
item.handles[-1].connected_to,
|
|
||||||
)
|
|
||||||
except Exception as e:
|
|
||||||
log.error(e)
|
|
||||||
|
|
||||||
# Now we have the lines, let's first ensure we only have a begin and an
|
|
||||||
# end handle
|
|
||||||
for line in list(lines.keys()):
|
|
||||||
while len(line.handles) > 2:
|
|
||||||
line.set_property("del_segment", 0)
|
|
||||||
|
|
||||||
# Strategy 1:
|
|
||||||
# Now we have nice short lines. Let's move them to a point between
|
|
||||||
# both nodes and let the connect() do the work:
|
|
||||||
for line, nodes in list(lines.items()):
|
|
||||||
if not nodes[0] or not nodes[1]:
|
|
||||||
# loose end
|
|
||||||
continue
|
|
||||||
center0 = find_center(nodes[0])
|
|
||||||
center1 = find_center(nodes[1])
|
|
||||||
center = ((center0[0] + center1[0]) / 2.0, (center0[1] + center1[1]) / 2.0)
|
|
||||||
line.handles[0].set_pos_w(*center)
|
|
||||||
line.handles[-1].set_pos_w(*center)
|
|
||||||
nodes[0].connect_handle(line.handles[0])
|
|
||||||
nodes[1].connect_handle(line.handles[-1])
|
|
||||||
|
|
||||||
|
|
||||||
def uniq(lst):
|
|
||||||
d = {}
|
|
||||||
for l in lst:
|
|
||||||
d[l] = None
|
|
||||||
return list(d.keys())
|
|
||||||
|
|
||||||
|
|
||||||
def find_related_nodes(item, relations):
|
|
||||||
"""
|
|
||||||
Find related nodes of item, given a list of tuples.
|
|
||||||
References to itself are ignored.
|
|
||||||
"""
|
|
||||||
related = []
|
|
||||||
for pair in relations:
|
|
||||||
if pair[0] is item:
|
|
||||||
if pair[1] is not item:
|
|
||||||
related.append(pair[1])
|
|
||||||
elif pair[1] is item:
|
|
||||||
if pair[0] is not item:
|
|
||||||
related.append(pair[0])
|
|
||||||
return uniq(related)
|
|
||||||
|
|
||||||
|
|
||||||
def find_row(item, related_items, sorted):
|
|
||||||
"""
|
|
||||||
Find the row that contains the most references to item.
|
|
||||||
"""
|
|
||||||
max_refs = 0
|
|
||||||
max_row = None
|
|
||||||
for row in sorted:
|
|
||||||
cnt = len([i for i in row if i in related_items])
|
|
||||||
if cnt > max_refs:
|
|
||||||
max_row = row
|
|
||||||
max_refs = cnt
|
|
||||||
return max_row
|
|
||||||
|
|
||||||
|
|
||||||
def find_center(item):
|
|
||||||
"""
|
|
||||||
Find the center point of the item, in world coordinates
|
|
||||||
"""
|
|
||||||
x = item.width / 2.0
|
|
||||||
y = item.height / 2.0
|
|
||||||
return item.canvas.get_matrix_i2c(item).transform_point(x, y)
|
|
||||||
|
|
||||||
|
|
||||||
# vim:sw=4:et
|
|
@ -1,29 +0,0 @@
|
|||||||
from gaphor import UML
|
|
||||||
from gaphor.application import Application
|
|
||||||
from gaphor.diagram.classes import ClassItem
|
|
||||||
from gaphor.tests.testcase import TestCase
|
|
||||||
|
|
||||||
|
|
||||||
class DiagramLayoutTestCase(TestCase):
|
|
||||||
|
|
||||||
services = TestCase.services + [
|
|
||||||
"main_window",
|
|
||||||
"properties",
|
|
||||||
"diagram_layout",
|
|
||||||
"import_menu",
|
|
||||||
"export_menu",
|
|
||||||
"tools_menu",
|
|
||||||
]
|
|
||||||
|
|
||||||
def testDiagramLayout(self):
|
|
||||||
elemfact = Application.get_service("element_factory")
|
|
||||||
diagram_layout = Application.get_service("diagram_layout")
|
|
||||||
|
|
||||||
diagram = elemfact.create(UML.Diagram)
|
|
||||||
c1 = diagram.create(ClassItem, subject=elemfact.create(UML.Class))
|
|
||||||
c2 = diagram.create(ClassItem, subject=elemfact.create(UML.Class))
|
|
||||||
|
|
||||||
c2.matrix.translate(100, 100)
|
|
||||||
c2.request_update()
|
|
||||||
|
|
||||||
diagram_layout.layout_diagram(diagram)
|
|
@ -1,210 +0,0 @@
|
|||||||
class RecursionError(OverflowError, ValueError):
|
|
||||||
"""Unable to calculate result because of recursive structure"""
|
|
||||||
|
|
||||||
|
|
||||||
def sort(nodes, routes, noRecursion=1):
|
|
||||||
"""Passed a list of node IDs and a list of source,dest ID routes
|
|
||||||
attempt to create a list of stages where each sub list
|
|
||||||
is one stage in a process.
|
|
||||||
"""
|
|
||||||
children, parents = _buildChildrenLists(routes)
|
|
||||||
# first stage is those nodes
|
|
||||||
# having no incoming routes...
|
|
||||||
stage = []
|
|
||||||
stages = [stage]
|
|
||||||
taken = []
|
|
||||||
for node in nodes:
|
|
||||||
if not parents.get(node):
|
|
||||||
stage.append(node)
|
|
||||||
if nodes and not stage:
|
|
||||||
# there is no element which does not depend on
|
|
||||||
# some other element!!!
|
|
||||||
stage.append(nodes[0])
|
|
||||||
taken.extend(stage)
|
|
||||||
nodes = list(filter(lambda x, l=stage: x not in l, nodes))
|
|
||||||
while nodes:
|
|
||||||
previousStageChildren = []
|
|
||||||
nodelen = len(nodes)
|
|
||||||
# second stage are those nodes
|
|
||||||
# which are direct children of the first stage
|
|
||||||
for node in stage:
|
|
||||||
for child in children.get(node, []):
|
|
||||||
if child not in previousStageChildren and child not in taken:
|
|
||||||
previousStageChildren.append(child)
|
|
||||||
elif child in taken and noRecursion:
|
|
||||||
raise RecursionError((child, node))
|
|
||||||
# unless they are children of other direct children...
|
|
||||||
# TODO, actually do that...
|
|
||||||
stage = previousStageChildren
|
|
||||||
removes = []
|
|
||||||
for current in stage:
|
|
||||||
currentParents = parents.get(current, [])
|
|
||||||
for parent in currentParents:
|
|
||||||
if parent in stage and parent != current:
|
|
||||||
# might wind up removing current...
|
|
||||||
if not current in parents.get(parent, []):
|
|
||||||
# is not mutually dependent...
|
|
||||||
removes.append(current)
|
|
||||||
for remove in removes:
|
|
||||||
while remove in stage:
|
|
||||||
stage.remove(remove)
|
|
||||||
stages.append(stage)
|
|
||||||
taken.extend(stage)
|
|
||||||
nodes = list(filter(lambda x, l=stage: x not in l, nodes))
|
|
||||||
if nodelen == len(nodes):
|
|
||||||
if noRecursion:
|
|
||||||
raise RecursionError(nodes)
|
|
||||||
else:
|
|
||||||
stages.append(nodes[:])
|
|
||||||
nodes = []
|
|
||||||
return stages
|
|
||||||
|
|
||||||
|
|
||||||
def _buildChildrenLists(routes):
|
|
||||||
childrenTable = {}
|
|
||||||
parentTable = {}
|
|
||||||
for sourceID, destinationID in routes:
|
|
||||||
currentChildren = childrenTable.get(sourceID, [])
|
|
||||||
currentParents = parentTable.get(destinationID, [])
|
|
||||||
if not destinationID in currentChildren:
|
|
||||||
currentChildren.append(destinationID)
|
|
||||||
if not sourceID in currentParents:
|
|
||||||
currentParents.append(sourceID)
|
|
||||||
childrenTable[sourceID] = currentChildren
|
|
||||||
parentTable[destinationID] = currentParents
|
|
||||||
return childrenTable, parentTable
|
|
||||||
|
|
||||||
|
|
||||||
def toposort(nodes, routes, noRecursion=1):
|
|
||||||
"""Topological sort from Tim Peters, fairly efficient
|
|
||||||
in comparison (it seems)."""
|
|
||||||
# first calculate the recursion depth
|
|
||||||
|
|
||||||
dependencies = {}
|
|
||||||
inversedependencies = {}
|
|
||||||
if not nodes:
|
|
||||||
return []
|
|
||||||
if not routes:
|
|
||||||
return [nodes]
|
|
||||||
for node in nodes:
|
|
||||||
dependencies[node] = (0, node)
|
|
||||||
inversedependencies[node] = []
|
|
||||||
|
|
||||||
for depended, depends in routes:
|
|
||||||
# is it a null rule
|
|
||||||
try:
|
|
||||||
newdependencylevel, object = dependencies.get(depends, (0, depends))
|
|
||||||
except TypeError:
|
|
||||||
print(depends)
|
|
||||||
raise
|
|
||||||
dependencies[depends] = (newdependencylevel + 1, depends)
|
|
||||||
# "dependency (existence) of depended-on"
|
|
||||||
newdependencylevel, object = dependencies.get(depended, (0, depended))
|
|
||||||
dependencies[depended] = (newdependencylevel, depended)
|
|
||||||
# Inverse dependency set up
|
|
||||||
dependencieslist = inversedependencies.get(depended, [])
|
|
||||||
dependencieslist.append(depends)
|
|
||||||
inversedependencies[depended] = dependencieslist
|
|
||||||
### Now we do the actual sorting
|
|
||||||
# The first task is to create the sortable
|
|
||||||
# list of dependency-levels
|
|
||||||
sortinglist = sorted(dependencies.values())
|
|
||||||
output = []
|
|
||||||
while sortinglist:
|
|
||||||
deletelist = []
|
|
||||||
generation = []
|
|
||||||
output.append(generation)
|
|
||||||
while sortinglist and sortinglist[0][0] == 0:
|
|
||||||
number, object = sortinglist[0]
|
|
||||||
generation.append(object)
|
|
||||||
deletelist.append(object)
|
|
||||||
for inverse in inversedependencies.get(object, ()):
|
|
||||||
try:
|
|
||||||
oldcount, inverse = dependencies[inverse]
|
|
||||||
if oldcount > 0:
|
|
||||||
# will be dealt with on later pass
|
|
||||||
dependencies[inverse] = (oldcount - 1, inverse)
|
|
||||||
else:
|
|
||||||
# will be dealt with on this pass,
|
|
||||||
# so needs not to be in the sorting list next time
|
|
||||||
deletelist.append(inverse)
|
|
||||||
# just in case a loop comes through
|
|
||||||
inversedependencies[object] = []
|
|
||||||
except KeyError:
|
|
||||||
# dealing with a recursion-breaking run...
|
|
||||||
pass
|
|
||||||
del sortinglist[0]
|
|
||||||
# if no elements could be deleted, then
|
|
||||||
# there is something which depends upon itself
|
|
||||||
if not deletelist:
|
|
||||||
if noRecursion:
|
|
||||||
raise RecursionError(sortinglist)
|
|
||||||
else:
|
|
||||||
# hack so that something gets deleted...
|
|
||||||
## import pdb
|
|
||||||
## pdb.set_trace()
|
|
||||||
dependencies[sortinglist[0][1]] = (0, sortinglist[0][1])
|
|
||||||
# delete the items that were dealt with
|
|
||||||
for item in deletelist:
|
|
||||||
try:
|
|
||||||
del dependencies[item]
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
# need to recreate the sortinglist
|
|
||||||
sortinglist = list(dependencies.values())
|
|
||||||
if not generation:
|
|
||||||
output.remove(generation)
|
|
||||||
sortinglist.sort()
|
|
||||||
return output
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
nodes = [0, 1, 2, 3, 4, 5]
|
|
||||||
testingValues = [
|
|
||||||
[(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)],
|
|
||||||
[(0, 1), (0, 2), (1, 2), (3, 4), (4, 5)],
|
|
||||||
[(0, 1), (0, 2), (0, 2), (2, 4), (2, 5), (3, 2), (0, 3)],
|
|
||||||
[
|
|
||||||
(0, 1), # 3-element cycle test, no orphan nodes
|
|
||||||
(1, 2),
|
|
||||||
(2, 0),
|
|
||||||
(2, 4),
|
|
||||||
(2, 5),
|
|
||||||
(3, 2),
|
|
||||||
(0, 3),
|
|
||||||
],
|
|
||||||
[(0, 1), (1, 1), (1, 1), (1, 4), (1, 5), (1, 2), (3, 1), (2, 1), (2, 0)],
|
|
||||||
[(0, 1), (1, 0), (0, 2), (0, 3)],
|
|
||||||
[(0, 1), (1, 0), (0, 2), (3, 1)],
|
|
||||||
]
|
|
||||||
print("sort, no recursion allowed")
|
|
||||||
for index in range(len(testingValues)):
|
|
||||||
## print ' %s -- %s'%( index, testingValues[index])
|
|
||||||
try:
|
|
||||||
print(" ", sort(nodes, testingValues[index]))
|
|
||||||
except:
|
|
||||||
print("exception raised")
|
|
||||||
print("toposort, no recursion allowed")
|
|
||||||
for index in range(len(testingValues)):
|
|
||||||
## print ' %s -- %s'%( index, testingValues[index])
|
|
||||||
try:
|
|
||||||
print(" ", toposort(nodes, testingValues[index]))
|
|
||||||
except:
|
|
||||||
print("exception raised")
|
|
||||||
print("sort, recursion allowed")
|
|
||||||
for index in range(len(testingValues)):
|
|
||||||
## print ' %s -- %s'%( index, testingValues[index])
|
|
||||||
try:
|
|
||||||
print(" ", sort(nodes, testingValues[index], 0))
|
|
||||||
except:
|
|
||||||
print("exception raised")
|
|
||||||
print("toposort, recursion allowed")
|
|
||||||
for index in range(len(testingValues)):
|
|
||||||
## print ' %s -- %s'%( index, testingValues[index])
|
|
||||||
try:
|
|
||||||
print(" ", toposort(nodes, testingValues[index], 0))
|
|
||||||
except:
|
|
||||||
print("exception raised")
|
|
@ -1,109 +0,0 @@
|
|||||||
PyNSource
|
|
||||||
http://www.atug.com/andypatterns/pynsource.htm
|
|
||||||
|
|
||||||
(c) 2003, 2004, 2005, 2006 Andy Bulka
|
|
||||||
License: Free to use as long as acknowledgement is made in source code.
|
|
||||||
abulka@netspace.net.au
|
|
||||||
http://www.atug.com/andypatterns
|
|
||||||
|
|
||||||
|
|
||||||
Version 1.4c
|
|
||||||
- Fixed some parsing bugs.
|
|
||||||
- Parsing now more correct under python 2.4 (python changed token.py !!)
|
|
||||||
- Unit tests now all pass
|
|
||||||
|
|
||||||
|
|
||||||
Version 1.4b
|
|
||||||
- Added wxpython 2.5 compatibility (thanks Thomas Margraf!)
|
|
||||||
|
|
||||||
|
|
||||||
Version 1.4a
|
|
||||||
|
|
||||||
GUI changes:
|
|
||||||
- Right Click on a node to delete it.
|
|
||||||
- Run Layout anytime from menu.
|
|
||||||
- Left click on background will deselect any selected shapes
|
|
||||||
|
|
||||||
Version 1.4
|
|
||||||
|
|
||||||
- Fixed indentation error causing more output than normal in text ouput
|
|
||||||
- Module level functions not treated as classes any more
|
|
||||||
- Smarter detection of composition relationships, as long as classname
|
|
||||||
and variable name are the same (ignoring case) then PyNSource will detect e.g.
|
|
||||||
|
|
||||||
class Cat:
|
|
||||||
pass
|
|
||||||
|
|
||||||
class A:
|
|
||||||
def __init__(self, cat):
|
|
||||||
self.cats.append(Cat()) # always has worked, composition detected.
|
|
||||||
self.cats.append(cat) # new 1.4 feature, composition detected here too.
|
|
||||||
|
|
||||||
|
|
||||||
Version 1.3a
|
|
||||||
|
|
||||||
A reverse engineering tool for Python source code
|
|
||||||
- UML diagram models that you can layout, arrange and print out.
|
|
||||||
- UML text diagrams, which you can paste into your source code for documentation purposes.
|
|
||||||
- Java or Delphi code (which can be subsequently imported into more sophisticated UML
|
|
||||||
modeling tools, like Enterprise Architect or ESS-Model (free).)
|
|
||||||
|
|
||||||
Features
|
|
||||||
- Resilient: doesn't import the python files, thus will never get "stuck" when syntax is wrong.<2E>
|
|
||||||
- Fast
|
|
||||||
- Recognises inheritance and composition relationships
|
|
||||||
- Detects the cardinality of associations e.g. one to one or 1..* etc
|
|
||||||
- Optionally treat modules as classes - creating a pseudo class for each
|
|
||||||
module - module variables and functions are treated as attributes and methods of a class
|
|
||||||
- Has been developed using unit tests (supplied) so that you can trust it just that little bit more ;-)
|
|
||||||
|
|
||||||
|
|
||||||
GUI FRONT END
|
|
||||||
-------------
|
|
||||||
|
|
||||||
The PyNSource Gui is started in two ways:
|
|
||||||
* By running the standalone pynsourceGui.exe via the shortcut created by the standalone installer. or
|
|
||||||
* By running pynsourceGui.py ( you need wxpython installed. See http://www.wxpython.org )
|
|
||||||
e.g. \Python22\python.exe \Python22\Lib\site-packages\pynsource\pyNsourceGui.py
|
|
||||||
|
|
||||||
The PyNSource command line tool is pynsource.py
|
|
||||||
|
|
||||||
Command line Usage
|
|
||||||
------------------
|
|
||||||
|
|
||||||
<EFBFBD>pynsource -v -m [-j outdir] sourceDirOrListOfPythonFiles...<2E><><EFBFBD>
|
|
||||||
|
|
||||||
no options - generate Ascii UML
|
|
||||||
-j generate java files, specify output folder for java files
|
|
||||||
-d generate pascal files, specify output folder for pascal files
|
|
||||||
-v verbose
|
|
||||||
-m create psuedo class for each module,
|
|
||||||
module attrs/defs etc treated as class attrs/defs
|
|
||||||
|
|
||||||
Examples
|
|
||||||
|
|
||||||
e.g.
|
|
||||||
\python22\python.exe \Python22\Lib\site-packages\pynsource\pynsource.py -d c:\delphiouputdir c:\pythoninputdir\*.py
|
|
||||||
|
|
||||||
The above line will scan all the files in c:\pythoninputdir and generate
|
|
||||||
a bunch of delphi files in the folder c:\delphiouputdir
|
|
||||||
|
|
||||||
BASIC ASCII UML OUTPUT from PYTHON - EXAMPLES
|
|
||||||
e.g. pynsource Test/testmodule01.py
|
|
||||||
e.g. pynsource -m Test/testmodule03.py
|
|
||||||
|
|
||||||
GENERATE JAVA FILES from PYTHON - EXAMPLES
|
|
||||||
e.g. pynsource -j c:/try c:/try
|
|
||||||
e.g. pynsource -v -m -j c:/try c:/try
|
|
||||||
e.g. pynsource -v -m -j c:/try c:/try/s*.py
|
|
||||||
e.g. pynsource -j c:/try c:/try/s*.py Tests/u*.py
|
|
||||||
e.g. pynsource -v -m -j c:/try c:/try/s*.py Tests/u*.py c:\cc\Devel\Client\w*.py
|
|
||||||
|
|
||||||
GENERATE DELPHI FILES from PYTHON - EXAMPLE
|
|
||||||
e.g. pynsource -d c:/delphiouputdir c:/pythoninputdir/*.py
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
see http://www.atug.com/andypatterns/pynsource.htm for more documentation.
|
|
||||||
|
|
||||||
Bugs to abulka@netspace.net.au
|
|
@ -1,242 +0,0 @@
|
|||||||
"""
|
|
||||||
Code reverse engineer plugin for Python source code.
|
|
||||||
|
|
||||||
This plugin uses PyNSource, written by Andy Bulka
|
|
||||||
[http://www.atug.com/andypatterns/pynsource.htm].
|
|
||||||
|
|
||||||
Depends on the Diagram Layout plugin.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from gi.repository import GObject
|
|
||||||
from gi.repository import Gtk
|
|
||||||
|
|
||||||
from gaphor.core import action
|
|
||||||
from gaphor.abc import Service, ActionProvider
|
|
||||||
from gaphor.plugins.pynsource.engineer import Engineer
|
|
||||||
|
|
||||||
NAME_COLUMN = 0
|
|
||||||
|
|
||||||
|
|
||||||
class PyNSource(Service, ActionProvider):
|
|
||||||
def __init__(self, element_factory, diagram_layout, main_window):
|
|
||||||
self.element_factory = element_factory
|
|
||||||
self.diagram_layout = diagram_layout
|
|
||||||
self.main_window = main_window
|
|
||||||
self.win = None
|
|
||||||
|
|
||||||
def shutdown(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@action(
|
|
||||||
name="file-import-pynsource",
|
|
||||||
label="Python source code",
|
|
||||||
tooltip="Import Python source code",
|
|
||||||
)
|
|
||||||
def execute(self):
|
|
||||||
dialog = self.create_dialog()
|
|
||||||
response = dialog.run()
|
|
||||||
|
|
||||||
if response != Gtk.ResponseType.OK:
|
|
||||||
dialog.destroy()
|
|
||||||
self.reset()
|
|
||||||
return
|
|
||||||
|
|
||||||
files = []
|
|
||||||
for row in self.filelist:
|
|
||||||
files.append(row[0])
|
|
||||||
|
|
||||||
dialog.destroy()
|
|
||||||
|
|
||||||
self.process(files)
|
|
||||||
self.reset()
|
|
||||||
|
|
||||||
def process(self, files):
|
|
||||||
"""Create a diagram based on a list of files.
|
|
||||||
"""
|
|
||||||
engineer = Engineer(self.element_factory, self.diagram_layout)
|
|
||||||
engineer.process(files)
|
|
||||||
|
|
||||||
main_window = self.main_window
|
|
||||||
# Open and select the new diagram in the main window:
|
|
||||||
main_window.select_element(engineer.diagram)
|
|
||||||
main_window.show_diagram(engineer.diagram)
|
|
||||||
|
|
||||||
def create_dialog(self):
|
|
||||||
dialog = Gtk.Dialog(
|
|
||||||
"Import Python files",
|
|
||||||
self.main_window.window,
|
|
||||||
Gtk.DialogFlags.MODAL,
|
|
||||||
(
|
|
||||||
Gtk.STOCK_OK,
|
|
||||||
Gtk.ResponseType.OK,
|
|
||||||
Gtk.STOCK_CANCEL,
|
|
||||||
Gtk.ResponseType.CANCEL,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
dialog.set_default_response(Gtk.ResponseType.OK)
|
|
||||||
|
|
||||||
filelist = Gtk.ListStore(GObject.TYPE_STRING)
|
|
||||||
filelist.connect("row-inserted", self.on_view_rows_changed)
|
|
||||||
filelist.connect("row-deleted", self.on_view_rows_changed)
|
|
||||||
|
|
||||||
hbox = Gtk.HBox()
|
|
||||||
|
|
||||||
frame = Gtk.Frame.new("Files to reverse-engineer")
|
|
||||||
frame.set_border_width(8)
|
|
||||||
frame.set_size_request(500, 300)
|
|
||||||
frame.show()
|
|
||||||
hbox.pack_start(frame, True, True, 0)
|
|
||||||
|
|
||||||
treeview = Gtk.TreeView(filelist)
|
|
||||||
treeview.set_property("headers-visible", False)
|
|
||||||
selection = treeview.get_selection()
|
|
||||||
selection.set_mode(Gtk.SelectionMode.SINGLE)
|
|
||||||
treeview.set_size_request(200, -1)
|
|
||||||
treeview.connect_after("cursor_changed", self.on_view_cursor_changed)
|
|
||||||
|
|
||||||
scrolled_window = Gtk.ScrolledWindow()
|
|
||||||
scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
|
|
||||||
scrolled_window.set_shadow_type(Gtk.ShadowType.IN)
|
|
||||||
scrolled_window.set_border_width(4)
|
|
||||||
scrolled_window.add(treeview)
|
|
||||||
frame.add(scrolled_window)
|
|
||||||
scrolled_window.show()
|
|
||||||
|
|
||||||
cell = Gtk.CellRendererText()
|
|
||||||
column = Gtk.TreeViewColumn("Filename", cell, text=NAME_COLUMN)
|
|
||||||
treeview.append_column(column)
|
|
||||||
|
|
||||||
bbox = Gtk.VButtonBox()
|
|
||||||
bbox.set_layout(Gtk.ButtonBoxStyle.SPREAD)
|
|
||||||
bbox.set_border_width(10)
|
|
||||||
button = Gtk.Button(stock="gtk-add")
|
|
||||||
button.connect("clicked", self.on_add_dir_clicked)
|
|
||||||
bbox.add(button)
|
|
||||||
self.add_button = button
|
|
||||||
|
|
||||||
# button = Gtk.Button('Add dir...')
|
|
||||||
# button.connect('clicked', self.on_add_dir_clicked)
|
|
||||||
# bbox.add(button)
|
|
||||||
# self.add_dir_button = button
|
|
||||||
|
|
||||||
button = Gtk.Button(stock="gtk-remove")
|
|
||||||
button.connect("clicked", self.on_remove_clicked)
|
|
||||||
button.set_property("sensitive", False)
|
|
||||||
bbox.add(button)
|
|
||||||
self.remove_button = button
|
|
||||||
|
|
||||||
# button = Gtk.Button(stock='gtk-execute')
|
|
||||||
# button.connect('clicked', self.on_execute_clicked)
|
|
||||||
# button.set_property('sensitive', False)
|
|
||||||
# bbox.add(button)
|
|
||||||
# self.execute_button = button
|
|
||||||
|
|
||||||
hbox.pack_start(bbox, False, True, 0)
|
|
||||||
hbox.show_all()
|
|
||||||
|
|
||||||
dialog.vbox.pack_start(hbox, True, True, 0)
|
|
||||||
|
|
||||||
self.filelist = filelist
|
|
||||||
self.treeview = treeview
|
|
||||||
|
|
||||||
return dialog
|
|
||||||
|
|
||||||
def reset(self):
|
|
||||||
self.add_button = None
|
|
||||||
self.add_dir_button = None
|
|
||||||
self.remove_button = None
|
|
||||||
self.treeview = None
|
|
||||||
self.filelist = None
|
|
||||||
|
|
||||||
def Walk(self, root, recurse=0, pattern="*", return_folders=0):
|
|
||||||
import fnmatch
|
|
||||||
import os
|
|
||||||
import string
|
|
||||||
|
|
||||||
# initialize
|
|
||||||
result = []
|
|
||||||
|
|
||||||
# must have at least root folder
|
|
||||||
try:
|
|
||||||
names = os.listdir(root)
|
|
||||||
except os.error:
|
|
||||||
return result
|
|
||||||
|
|
||||||
# expand pattern
|
|
||||||
pattern = pattern or "*"
|
|
||||||
pat_list = string.splitfields(pattern, ";")
|
|
||||||
|
|
||||||
# check each file
|
|
||||||
for name in names:
|
|
||||||
fullname = os.path.normpath(os.path.join(root, name))
|
|
||||||
|
|
||||||
# grab if it matches our pattern and entry type
|
|
||||||
for pat in pat_list:
|
|
||||||
if fnmatch.fnmatch(name, pat):
|
|
||||||
if os.path.isfile(fullname) or (
|
|
||||||
return_folders and os.path.isdir(fullname)
|
|
||||||
):
|
|
||||||
result.append(fullname)
|
|
||||||
continue
|
|
||||||
|
|
||||||
# recursively scan other folders, appending results
|
|
||||||
if recurse:
|
|
||||||
if os.path.isdir(fullname) and not os.path.islink(fullname):
|
|
||||||
result = result + self.Walk(
|
|
||||||
fullname, recurse, pattern, return_folders
|
|
||||||
)
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
def on_view_cursor_changed(self, view):
|
|
||||||
selection = view.get_selection()
|
|
||||||
filelist, iter = selection.get_selected()
|
|
||||||
if not iter:
|
|
||||||
self.remove_button.set_property("sensitive", False)
|
|
||||||
return
|
|
||||||
# element = filelist.get_value(iter, NAME_COLUMN)
|
|
||||||
self.remove_button.set_property("sensitive", True)
|
|
||||||
# self.update_detail(element)
|
|
||||||
|
|
||||||
def on_view_rows_changed(self, view, *args):
|
|
||||||
iter = None
|
|
||||||
try:
|
|
||||||
iter = view.get_iter("0")
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
# self.execute_button.set_property('sensitive', bool(iter))
|
|
||||||
|
|
||||||
def on_add_dir_clicked(self, button):
|
|
||||||
import os
|
|
||||||
|
|
||||||
filesel = Gtk.FileChooserNative(
|
|
||||||
title="Add Source Code", action=Gtk.FileChooserAction.OPEN
|
|
||||||
)
|
|
||||||
|
|
||||||
filesel.set_select_multiple(True)
|
|
||||||
filesel.set_filename("~/")
|
|
||||||
|
|
||||||
response = filesel.run()
|
|
||||||
selection = filesel.get_filenames()
|
|
||||||
filesel.destroy()
|
|
||||||
|
|
||||||
if response == Gtk.ResponseType.ACCEPT:
|
|
||||||
for filename in selection:
|
|
||||||
if os.path.isdir(filename):
|
|
||||||
list = self.Walk(filename, 1, "*.py", 1)
|
|
||||||
for file in list:
|
|
||||||
iter = self.filelist.append()
|
|
||||||
self.filelist.set_value(iter, NAME_COLUMN, file)
|
|
||||||
else:
|
|
||||||
list = filename
|
|
||||||
iter = self.filelist.append()
|
|
||||||
self.filelist.set_value(iter, NAME_COLUMN, list)
|
|
||||||
|
|
||||||
def on_remove_clicked(self, button):
|
|
||||||
selection = self.treeview.get_selection()
|
|
||||||
filelist, iter = selection.get_selected()
|
|
||||||
if not iter:
|
|
||||||
return
|
|
||||||
element = filelist.remove(iter)
|
|
||||||
|
|
||||||
self.remove_button.set_property("sensitive", False)
|
|
@ -1,249 +0,0 @@
|
|||||||
"""The code reverse engineer.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from gaphas.aspect import ConnectionSink, Connector
|
|
||||||
|
|
||||||
from gaphor import UML
|
|
||||||
from gaphor.diagram.classes import AssociationItem, ClassItem, GeneralizationItem
|
|
||||||
from gaphor.diagram.connectors import IConnect
|
|
||||||
from gaphor.plugins.pynsource.pynsource import PySourceAsText
|
|
||||||
|
|
||||||
BASE_CLASSES = ("object", "type", "dict", "list", "tuple", "int", "float")
|
|
||||||
|
|
||||||
|
|
||||||
class Engineer:
|
|
||||||
"""
|
|
||||||
The Engineer class will create a Gaphor model based on a list of Python
|
|
||||||
files.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def process(self, element_factory, diagram_layout, files=None):
|
|
||||||
self.element_factory = element_factory
|
|
||||||
self.diagram_layout = diagram_layout
|
|
||||||
# these are tuples between class names.
|
|
||||||
# self.associations_generalisation = []
|
|
||||||
# self.associations_composition = []
|
|
||||||
|
|
||||||
p = PySourceAsText()
|
|
||||||
self.parser = p
|
|
||||||
|
|
||||||
if files:
|
|
||||||
# u = PythonToJava(None, treatmoduleasclass=0, verbose=0)
|
|
||||||
for f in files:
|
|
||||||
# Build a shape with all attrs and methods, and prepare association dict
|
|
||||||
p.Parse(f)
|
|
||||||
|
|
||||||
print(p)
|
|
||||||
|
|
||||||
try:
|
|
||||||
self._root_package = self.element_factory.lselect(
|
|
||||||
lambda e: isinstance(e, UML.Package) and not e.namespace
|
|
||||||
)[0]
|
|
||||||
except IndexError:
|
|
||||||
pass # running as test?
|
|
||||||
|
|
||||||
for m in p.modulemethods:
|
|
||||||
print("ModuleMethod:", m)
|
|
||||||
|
|
||||||
# Step 0: create a diagram to put the newly created elements on
|
|
||||||
self.diagram = self.element_factory.create(UML.Diagram)
|
|
||||||
self.diagram.name = "New classes"
|
|
||||||
self.diagram.package = self._root_package
|
|
||||||
|
|
||||||
# Step 1: create the classes
|
|
||||||
for name, clazz in list(p.classlist.items()):
|
|
||||||
print(type(clazz), dir(clazz))
|
|
||||||
self._create_class(clazz, name)
|
|
||||||
|
|
||||||
# Create generalization relationships:
|
|
||||||
for name, clazz in list(p.classlist.items()):
|
|
||||||
self._create_generalization(clazz)
|
|
||||||
|
|
||||||
# Create attributes (and associations) on the classes
|
|
||||||
for name, clazz in list(p.classlist.items()):
|
|
||||||
self._create_attributes(clazz)
|
|
||||||
|
|
||||||
# Create operations
|
|
||||||
for name, clazz in list(p.classlist.items()):
|
|
||||||
self._create_methods(clazz)
|
|
||||||
|
|
||||||
self.diagram_layout.layout_diagram(self.diagram)
|
|
||||||
|
|
||||||
def _create_class(self, clazz, name):
|
|
||||||
c = self.element_factory.create(UML.Class)
|
|
||||||
c.name = name
|
|
||||||
c.package = self.diagram.namespace
|
|
||||||
ci = self.diagram.create(ClassItem)
|
|
||||||
ci.subject = c
|
|
||||||
clazz.gaphor_class = c
|
|
||||||
clazz.gaphor_class_item = ci
|
|
||||||
|
|
||||||
def _create_generalization(self, clazz):
|
|
||||||
if not clazz.ismodulenotrealclass:
|
|
||||||
for superclassname in clazz.classesinheritsfrom:
|
|
||||||
if superclassname in BASE_CLASSES:
|
|
||||||
continue
|
|
||||||
try:
|
|
||||||
superclass = self.parser.classlist[superclassname].gaphor_class
|
|
||||||
superclass_item = self.parser.classlist[
|
|
||||||
superclassname
|
|
||||||
].gaphor_class_item
|
|
||||||
except KeyError as e:
|
|
||||||
print("No class found named", superclassname)
|
|
||||||
others = self.element_factory.lselect(
|
|
||||||
lambda e: isinstance(e, UML.Class) and e.name == superclassname
|
|
||||||
)
|
|
||||||
if others:
|
|
||||||
superclass = others[0]
|
|
||||||
print(f"Found class in factory: {superclass.name}")
|
|
||||||
superclass_item = self.diagram.create(ClassItem)
|
|
||||||
superclass_item.subject = superclass
|
|
||||||
else:
|
|
||||||
continue
|
|
||||||
# Finally, create the generalization relationship
|
|
||||||
print(f"Creating Generalization for {clazz}", superclass)
|
|
||||||
# gen = self.element_factory.create(UML.Generalization)
|
|
||||||
# gen.general = superclass
|
|
||||||
# gen.specific = clazz.gaphor_class
|
|
||||||
geni = self.diagram.create(GeneralizationItem)
|
|
||||||
# geni.subject = gen
|
|
||||||
|
|
||||||
self.connect(geni, geni.tail, clazz.gaphor_class_item)
|
|
||||||
self.connect(geni, geni.head, superclass_item)
|
|
||||||
|
|
||||||
# adapter = IConnect(superclass_item, geni)
|
|
||||||
# assert adapter
|
|
||||||
# handle = geni.handles()[0]
|
|
||||||
# adapter.connect(handle)
|
|
||||||
# clazz.gaphor_class_item.connect_handle(geni.handles[-1])
|
|
||||||
# adapter = IConnect(clazz.gaphor_class_item, geni)
|
|
||||||
# assert adapter
|
|
||||||
# handle = geni.handles()[-1]
|
|
||||||
# adapter.connect(handle)
|
|
||||||
|
|
||||||
def connect(self, line, handle, item, port=None):
|
|
||||||
"""
|
|
||||||
Connect line's handle to an item.
|
|
||||||
|
|
||||||
If port is not provided, then first port is used.
|
|
||||||
"""
|
|
||||||
canvas = line.canvas
|
|
||||||
|
|
||||||
if port is None and len(item.ports()) > 0:
|
|
||||||
port = item.ports()[0]
|
|
||||||
|
|
||||||
sink = ConnectionSink(item, port)
|
|
||||||
connector = Connector(line, handle)
|
|
||||||
|
|
||||||
connector.connect(sink)
|
|
||||||
|
|
||||||
def _create_attributes(self, clazz):
|
|
||||||
for attrobj in clazz.attrs:
|
|
||||||
# TODO: Check object type and figure out if it should be an
|
|
||||||
# attribute or an association.
|
|
||||||
self._create_attribute(clazz, attrobj)
|
|
||||||
|
|
||||||
def _create_methods(self, clazz):
|
|
||||||
for adef in clazz.defs:
|
|
||||||
op = self.element_factory.create(UML.Operation)
|
|
||||||
op.name = adef
|
|
||||||
clazz.gaphor_class.ownedOperation = op
|
|
||||||
|
|
||||||
def _find_class_by_name(self, classname):
|
|
||||||
try:
|
|
||||||
superclass = self.parser.classlist[classname].gaphor_class
|
|
||||||
superclass_item = self.parser.classlist[classname].gaphor_class_item
|
|
||||||
except KeyError as e:
|
|
||||||
print("No class found named", classname)
|
|
||||||
others = self.element_factory.lselect(
|
|
||||||
lambda e: isinstance(e, UML.Class) and e.name == classname
|
|
||||||
)
|
|
||||||
if others:
|
|
||||||
superclass = others[0]
|
|
||||||
print(f"Found class in factory: {superclass.name}")
|
|
||||||
superclass_item = self.diagram.create(ClassItem)
|
|
||||||
superclass_item.subject = superclass
|
|
||||||
else:
|
|
||||||
return None, None
|
|
||||||
return superclass, superclass_item
|
|
||||||
|
|
||||||
def _visibility(self, attrname):
|
|
||||||
if attrname.startswith("__"):
|
|
||||||
return "private"
|
|
||||||
elif attrname.startswith("_"):
|
|
||||||
return "protected"
|
|
||||||
return "public"
|
|
||||||
|
|
||||||
def _create_attribute(self, clazz, attr):
|
|
||||||
static = False
|
|
||||||
many = False
|
|
||||||
if "static" in attr.attrtype:
|
|
||||||
static = True
|
|
||||||
if "many" in attr.attrtype:
|
|
||||||
many = True
|
|
||||||
compositescreated = self.parser.GetCompositeClassesForAttr(attr.attrname, clazz)
|
|
||||||
tail_type = None
|
|
||||||
if compositescreated:
|
|
||||||
tail_type, tail_type_item = self._find_class_by_name(compositescreated[0])
|
|
||||||
|
|
||||||
if tail_type:
|
|
||||||
# Create an association:
|
|
||||||
# print "%s %s <@>----> %s" % (attr.attrname, static, str(compositescreated))
|
|
||||||
# The property on the tail of the association (tail_end) is owned
|
|
||||||
# by the class connected on the head_end (head_type)
|
|
||||||
head_type = clazz.gaphor_class
|
|
||||||
head_type_item = clazz.gaphor_class_item
|
|
||||||
|
|
||||||
# relation = self.element_factory.create(UML.Association)
|
|
||||||
# head_end = self.element_factory.create(UML.Property)
|
|
||||||
# head_end.lowerValue = self.element_factory.create(UML.LiteralSpecification)
|
|
||||||
# tail_end = self.element_factory.create(UML.Property)
|
|
||||||
# tail_end.name = attr.attrname
|
|
||||||
# tail_end.visibility = self._visibility(attr.attrname)
|
|
||||||
# tail_end.aggregation = 'composite'
|
|
||||||
# tail_end.lowerValue = self.element_factory.create(UML.LiteralSpecification)
|
|
||||||
# relation.package = self.diagram.namespace
|
|
||||||
# relation.memberEnd = head_end
|
|
||||||
# relation.memberEnd = tail_end
|
|
||||||
# head_end.type = head_type
|
|
||||||
# tail_end.type = tail_type
|
|
||||||
# head_type.ownedAttribute = tail_end
|
|
||||||
# tail_type.ownedAttribute = head_end
|
|
||||||
|
|
||||||
# Now the subject
|
|
||||||
# association.subject = relation
|
|
||||||
# association.head_end.subject = head_end
|
|
||||||
# association.tail_end.subject = tail_end
|
|
||||||
|
|
||||||
# Create the diagram item:
|
|
||||||
association = self.diagram.create(AssociationItem)
|
|
||||||
|
|
||||||
adapter = IConnect(head_type_item, association)
|
|
||||||
assert adapter
|
|
||||||
handle = association.handles()[0]
|
|
||||||
adapter.connect(handle)
|
|
||||||
|
|
||||||
adapter = IConnect(tail_type_item, association)
|
|
||||||
assert adapter
|
|
||||||
handle = association.handles()[-1]
|
|
||||||
adapter.connect(handle)
|
|
||||||
|
|
||||||
# Apply attribute information to the association (ends)
|
|
||||||
association.head_end.navigability = False
|
|
||||||
tail_prop = association.tail_end.subject
|
|
||||||
tail_prop.name = attr.attrname
|
|
||||||
tail_prop.visibility = self._visibility(attr.attrname)
|
|
||||||
tail_prop.aggregation = "composite"
|
|
||||||
else:
|
|
||||||
# Create a simple attribute:
|
|
||||||
# print "%s %s" % (attr.attrname, static)
|
|
||||||
prop = self.element_factory.create(UML.Property)
|
|
||||||
prop.name = attr.attrname
|
|
||||||
prop.visibility = self._visibility(attr.attrname)
|
|
||||||
prop.isStatic = static
|
|
||||||
clazz.gaphor_class.ownedAttribute = prop
|
|
||||||
# print many
|
|
||||||
import pprint
|
|
||||||
|
|
||||||
pprint.pprint(attr)
|
|
||||||
# print dir(attr)
|
|
@ -1,571 +0,0 @@
|
|||||||
"""Definitions of Python, Java and Delphi keywords.
|
|
||||||
|
|
||||||
The definitions are used so that pynsource can skip these and not treat them
|
|
||||||
like you are creating an instance of a locally defined class.
|
|
||||||
"""
|
|
||||||
|
|
||||||
pythonbuiltinfunctions_txt = """
|
|
||||||
ArithmeticError
|
|
||||||
AssertionError
|
|
||||||
AttributeError
|
|
||||||
DeprecationWarning
|
|
||||||
EOFError
|
|
||||||
EnvironmentError
|
|
||||||
Exception
|
|
||||||
FloatingPointError
|
|
||||||
FutureWarning
|
|
||||||
IOError
|
|
||||||
ImportError
|
|
||||||
IndentationError
|
|
||||||
IndexError
|
|
||||||
KeyError
|
|
||||||
KeyboardInterrupt
|
|
||||||
LookupError
|
|
||||||
MemoryError
|
|
||||||
NameError
|
|
||||||
NotImplementedError
|
|
||||||
OSError
|
|
||||||
OverflowError
|
|
||||||
OverflowWarning
|
|
||||||
PendingDeprecationWarning
|
|
||||||
ReferenceError
|
|
||||||
RuntimeError
|
|
||||||
RuntimeWarning
|
|
||||||
StandardError
|
|
||||||
StopIteration
|
|
||||||
SyntaxError
|
|
||||||
SyntaxWarning
|
|
||||||
SystemError
|
|
||||||
SystemExit
|
|
||||||
TabError
|
|
||||||
TypeError
|
|
||||||
UnboundLocalError
|
|
||||||
UnicodeError
|
|
||||||
UserWarning
|
|
||||||
ValueError
|
|
||||||
Warning
|
|
||||||
WindowsError
|
|
||||||
ZeroDivisionError
|
|
||||||
Ellipsis
|
|
||||||
False
|
|
||||||
None
|
|
||||||
NotImplemented
|
|
||||||
True
|
|
||||||
UnicodeDecodeError
|
|
||||||
UnicodeEncodeError
|
|
||||||
UnicodeTranslateError
|
|
||||||
__debug__
|
|
||||||
__import__
|
|
||||||
abs
|
|
||||||
apply
|
|
||||||
basestring
|
|
||||||
bool
|
|
||||||
buffer
|
|
||||||
callable
|
|
||||||
chr
|
|
||||||
classmethod
|
|
||||||
cmp
|
|
||||||
coerce
|
|
||||||
compile
|
|
||||||
complex
|
|
||||||
copyright
|
|
||||||
credits
|
|
||||||
delattr
|
|
||||||
dict
|
|
||||||
dir
|
|
||||||
divmod
|
|
||||||
enumerate
|
|
||||||
eval
|
|
||||||
execfile
|
|
||||||
exit
|
|
||||||
file
|
|
||||||
filter
|
|
||||||
float
|
|
||||||
getattr
|
|
||||||
globals
|
|
||||||
hasattr
|
|
||||||
hash
|
|
||||||
help
|
|
||||||
hex
|
|
||||||
id
|
|
||||||
input
|
|
||||||
int
|
|
||||||
intern
|
|
||||||
isinstance
|
|
||||||
issubclass
|
|
||||||
iter
|
|
||||||
len
|
|
||||||
license
|
|
||||||
list
|
|
||||||
locals
|
|
||||||
long
|
|
||||||
map
|
|
||||||
max
|
|
||||||
min
|
|
||||||
object
|
|
||||||
oct
|
|
||||||
open
|
|
||||||
ord
|
|
||||||
pow
|
|
||||||
property
|
|
||||||
quit
|
|
||||||
range
|
|
||||||
raw_input
|
|
||||||
reduce
|
|
||||||
reload
|
|
||||||
repr
|
|
||||||
round
|
|
||||||
setattr
|
|
||||||
slice
|
|
||||||
staticmethod
|
|
||||||
str
|
|
||||||
sum
|
|
||||||
super
|
|
||||||
tuple
|
|
||||||
type
|
|
||||||
unichr
|
|
||||||
unicode
|
|
||||||
vars
|
|
||||||
xrange
|
|
||||||
zip
|
|
||||||
__base__
|
|
||||||
__bases__
|
|
||||||
__basicsize__
|
|
||||||
__class__
|
|
||||||
__dict__
|
|
||||||
__dictoffset__
|
|
||||||
__doc__
|
|
||||||
__flags__
|
|
||||||
__itemsize__
|
|
||||||
__module__
|
|
||||||
__mro__
|
|
||||||
__name__
|
|
||||||
__self__
|
|
||||||
__weakrefoffset__
|
|
||||||
__abs__
|
|
||||||
__add__
|
|
||||||
__and__
|
|
||||||
__call__
|
|
||||||
__cmp__
|
|
||||||
__coerce__
|
|
||||||
__complex__
|
|
||||||
__contains__
|
|
||||||
__del__
|
|
||||||
__delattr__
|
|
||||||
__delitem__
|
|
||||||
__delslice__
|
|
||||||
__div__
|
|
||||||
__divmod__
|
|
||||||
__eq__
|
|
||||||
__float__
|
|
||||||
__floordiv__
|
|
||||||
__ge__
|
|
||||||
__get__
|
|
||||||
__getattribute__
|
|
||||||
__getitem__
|
|
||||||
__getnewargs__
|
|
||||||
__getslice__
|
|
||||||
__gt__
|
|
||||||
__hash__
|
|
||||||
__hex__
|
|
||||||
__iadd__
|
|
||||||
__iand__
|
|
||||||
__idiv__
|
|
||||||
__ifloordiv__
|
|
||||||
__ilshift__
|
|
||||||
__imod__
|
|
||||||
__imul__
|
|
||||||
__init__
|
|
||||||
__int__
|
|
||||||
__invert__
|
|
||||||
__ior__
|
|
||||||
__ipow__
|
|
||||||
__irshift__
|
|
||||||
__isub__
|
|
||||||
__iter__
|
|
||||||
__itruediv__
|
|
||||||
__ixor__
|
|
||||||
__le__
|
|
||||||
__len__
|
|
||||||
__long__
|
|
||||||
__lshift__
|
|
||||||
__lt__
|
|
||||||
__mod__
|
|
||||||
__mul__
|
|
||||||
__ne__
|
|
||||||
__neg__
|
|
||||||
__new__
|
|
||||||
__nonzero__
|
|
||||||
__oct__
|
|
||||||
__or__
|
|
||||||
__pos__
|
|
||||||
__pow__
|
|
||||||
__radd__
|
|
||||||
__rand__
|
|
||||||
__rdiv__
|
|
||||||
__rdivmod__
|
|
||||||
__reduce__
|
|
||||||
__reduce_ex__
|
|
||||||
__repr__
|
|
||||||
__rfloordiv__
|
|
||||||
__rlshift__
|
|
||||||
__rmod__
|
|
||||||
__rmul__
|
|
||||||
__ror__
|
|
||||||
__rpow__
|
|
||||||
__rrshift__
|
|
||||||
__rshift__
|
|
||||||
__rsub__
|
|
||||||
__rtruediv__
|
|
||||||
__rxor__
|
|
||||||
__setattr__
|
|
||||||
__setitem__
|
|
||||||
__setslice__
|
|
||||||
__str__
|
|
||||||
__sub__
|
|
||||||
__subclasses__
|
|
||||||
__truediv__
|
|
||||||
__xor__
|
|
||||||
append
|
|
||||||
capitalize
|
|
||||||
center
|
|
||||||
clear
|
|
||||||
close
|
|
||||||
conjugate
|
|
||||||
copy
|
|
||||||
count
|
|
||||||
decode
|
|
||||||
encode
|
|
||||||
endswith
|
|
||||||
expandtabs
|
|
||||||
extend
|
|
||||||
fileno
|
|
||||||
find
|
|
||||||
flush
|
|
||||||
fromkeys
|
|
||||||
get
|
|
||||||
has_key
|
|
||||||
index
|
|
||||||
indices
|
|
||||||
insert
|
|
||||||
isalnum
|
|
||||||
isalpha
|
|
||||||
isatty
|
|
||||||
isdecimal
|
|
||||||
isdigit
|
|
||||||
islower
|
|
||||||
isnumeric
|
|
||||||
isspace
|
|
||||||
istitle
|
|
||||||
isupper
|
|
||||||
items
|
|
||||||
iteritems
|
|
||||||
iterkeys
|
|
||||||
itervalues
|
|
||||||
join
|
|
||||||
keys
|
|
||||||
ljust
|
|
||||||
lower
|
|
||||||
lstrip
|
|
||||||
mro
|
|
||||||
next
|
|
||||||
pop
|
|
||||||
popitem
|
|
||||||
read
|
|
||||||
readinto
|
|
||||||
readline
|
|
||||||
readlines
|
|
||||||
remove
|
|
||||||
replace
|
|
||||||
reverse
|
|
||||||
rfind
|
|
||||||
rindex
|
|
||||||
rjust
|
|
||||||
rstrip
|
|
||||||
seek
|
|
||||||
setdefault
|
|
||||||
sort
|
|
||||||
split
|
|
||||||
splitlines
|
|
||||||
startswith
|
|
||||||
strip
|
|
||||||
swapcase
|
|
||||||
tell
|
|
||||||
title
|
|
||||||
translate
|
|
||||||
truncate
|
|
||||||
update
|
|
||||||
upper
|
|
||||||
values
|
|
||||||
write
|
|
||||||
writelines
|
|
||||||
xreadlines
|
|
||||||
zfill
|
|
||||||
closed
|
|
||||||
co_argcount
|
|
||||||
co_cellvars
|
|
||||||
co_code
|
|
||||||
co_consts
|
|
||||||
co_filename
|
|
||||||
co_firstlineno
|
|
||||||
co_flags
|
|
||||||
co_freevars
|
|
||||||
co_lnotab
|
|
||||||
co_name
|
|
||||||
co_names
|
|
||||||
co_nlocals
|
|
||||||
co_stacksize
|
|
||||||
co_varnames
|
|
||||||
f_back
|
|
||||||
f_builtins
|
|
||||||
f_code
|
|
||||||
f_exc_traceback
|
|
||||||
f_exc_type
|
|
||||||
f_exc_value
|
|
||||||
f_globals
|
|
||||||
f_lasti
|
|
||||||
f_lineno
|
|
||||||
f_locals
|
|
||||||
f_restricted
|
|
||||||
f_trace
|
|
||||||
func_closure
|
|
||||||
func_code
|
|
||||||
func_defaults
|
|
||||||
func_dict
|
|
||||||
func_doc
|
|
||||||
func_globals
|
|
||||||
func_name
|
|
||||||
gi_frame
|
|
||||||
gi_running
|
|
||||||
im_class
|
|
||||||
im_func
|
|
||||||
im_self
|
|
||||||
imag
|
|
||||||
mode
|
|
||||||
name
|
|
||||||
newlines
|
|
||||||
real
|
|
||||||
softspace
|
|
||||||
start
|
|
||||||
step
|
|
||||||
stop
|
|
||||||
BooleanType
|
|
||||||
BufferType
|
|
||||||
BuiltinFunctionType
|
|
||||||
BuiltinMethodType
|
|
||||||
ClassType
|
|
||||||
CodeType
|
|
||||||
ComplexType
|
|
||||||
DictProxyType
|
|
||||||
DictType
|
|
||||||
DictionaryType
|
|
||||||
EllipsisType
|
|
||||||
FileType
|
|
||||||
FloatType
|
|
||||||
FrameType
|
|
||||||
FunctionType
|
|
||||||
GeneratorType
|
|
||||||
InstanceType
|
|
||||||
IntType
|
|
||||||
LambdaType
|
|
||||||
ListType
|
|
||||||
LongType
|
|
||||||
MethodType
|
|
||||||
ModuleType
|
|
||||||
NoneType
|
|
||||||
NotImplementedType
|
|
||||||
ObjectType
|
|
||||||
SliceType
|
|
||||||
StringType
|
|
||||||
StringTypes
|
|
||||||
TracebackType
|
|
||||||
TupleType
|
|
||||||
TypeType
|
|
||||||
UnboundMethodType
|
|
||||||
UnicodeType
|
|
||||||
XRangeType
|
|
||||||
__builtins__
|
|
||||||
__file__
|
|
||||||
"""
|
|
||||||
pythonbuiltinfunctions = pythonbuiltinfunctions_txt.split()
|
|
||||||
|
|
||||||
javakeywords_txt = """
|
|
||||||
abstract
|
|
||||||
boolean
|
|
||||||
break
|
|
||||||
byte
|
|
||||||
case
|
|
||||||
catch
|
|
||||||
char
|
|
||||||
class
|
|
||||||
continue
|
|
||||||
default
|
|
||||||
delegate
|
|
||||||
do
|
|
||||||
double
|
|
||||||
else
|
|
||||||
extends
|
|
||||||
false
|
|
||||||
final
|
|
||||||
finally
|
|
||||||
float
|
|
||||||
for
|
|
||||||
if
|
|
||||||
implements
|
|
||||||
import
|
|
||||||
instanceof
|
|
||||||
int
|
|
||||||
interface
|
|
||||||
long
|
|
||||||
native
|
|
||||||
new
|
|
||||||
null
|
|
||||||
package
|
|
||||||
private
|
|
||||||
protected
|
|
||||||
public
|
|
||||||
return
|
|
||||||
short
|
|
||||||
static
|
|
||||||
super
|
|
||||||
switch
|
|
||||||
synchronized
|
|
||||||
this
|
|
||||||
throw
|
|
||||||
throws
|
|
||||||
transient
|
|
||||||
true
|
|
||||||
try
|
|
||||||
void
|
|
||||||
volatile
|
|
||||||
while
|
|
||||||
goto
|
|
||||||
const
|
|
||||||
strictfp
|
|
||||||
"""
|
|
||||||
javakeywords = javakeywords_txt.split()
|
|
||||||
|
|
||||||
|
|
||||||
delphikeywords_txt = """
|
|
||||||
And
|
|
||||||
Array
|
|
||||||
As
|
|
||||||
Begin
|
|
||||||
Case
|
|
||||||
Class
|
|
||||||
Const
|
|
||||||
Constructor
|
|
||||||
Destructor
|
|
||||||
Div
|
|
||||||
Do
|
|
||||||
DownTo
|
|
||||||
Else
|
|
||||||
End
|
|
||||||
Except
|
|
||||||
File
|
|
||||||
Finally
|
|
||||||
For
|
|
||||||
System
|
|
||||||
Goto
|
|
||||||
If
|
|
||||||
Implementation
|
|
||||||
In
|
|
||||||
Inherited
|
|
||||||
Interface
|
|
||||||
System
|
|
||||||
Is
|
|
||||||
Mod
|
|
||||||
Not
|
|
||||||
System
|
|
||||||
Of
|
|
||||||
On
|
|
||||||
Or
|
|
||||||
Packed
|
|
||||||
System
|
|
||||||
System
|
|
||||||
System
|
|
||||||
Raise
|
|
||||||
Record
|
|
||||||
Repeat
|
|
||||||
Set
|
|
||||||
Shl
|
|
||||||
Shr
|
|
||||||
Then
|
|
||||||
ThreadVar
|
|
||||||
To
|
|
||||||
Try
|
|
||||||
Type
|
|
||||||
Unit
|
|
||||||
Until
|
|
||||||
Uses
|
|
||||||
Var
|
|
||||||
While
|
|
||||||
With
|
|
||||||
Xor
|
|
||||||
"""
|
|
||||||
delphikeywords = delphikeywords_txt.split()
|
|
||||||
delphikeywords = [
|
|
||||||
x.lower() for x in delphikeywords
|
|
||||||
] # delphi is case insensitive, so convert everything to lowercase for comparisons
|
|
||||||
|
|
||||||
# See Token.py in \python2x\Lib
|
|
||||||
|
|
||||||
TOKEN_MEANINGS_FORDOCO_ONLY = """
|
|
||||||
AMPER = 19
|
|
||||||
AMPEREQUAL = 42
|
|
||||||
AT = 50
|
|
||||||
BACKQUOTE = 25
|
|
||||||
CIRCUMFLEX = 33
|
|
||||||
CIRCUMFLEXEQUAL = 44
|
|
||||||
COLON = 11
|
|
||||||
COMMA = 12
|
|
||||||
COMMENT = 53
|
|
||||||
DEDENT = 6
|
|
||||||
DOT = 23
|
|
||||||
DOUBLESLASH = 48
|
|
||||||
DOUBLESLASHEQUAL = 49
|
|
||||||
DOUBLESTAR = 36
|
|
||||||
DOUBLESTAREQUAL = 47
|
|
||||||
ENDMARKER = 0
|
|
||||||
EQEQUAL = 28
|
|
||||||
EQUAL = 22
|
|
||||||
ERRORTOKEN = 52
|
|
||||||
GREATER = 21
|
|
||||||
GREATEREQUAL = 31
|
|
||||||
INDENT = 5
|
|
||||||
LBRACE = 26
|
|
||||||
LEFTSHIFT = 34
|
|
||||||
LEFTSHIFTEQUAL = 45
|
|
||||||
LESS = 20
|
|
||||||
LESSEQUAL = 30
|
|
||||||
LPAR = 7
|
|
||||||
LSQB = 9
|
|
||||||
MINEQUAL = 38
|
|
||||||
MINUS = 15
|
|
||||||
NAME = 1
|
|
||||||
NEWLINE = 4
|
|
||||||
NL = 54
|
|
||||||
NOTEQUAL = 29
|
|
||||||
NT_OFFSET = 256
|
|
||||||
NUMBER = 2
|
|
||||||
N_TOKENS = 55
|
|
||||||
OP = 51
|
|
||||||
PERCENT = 24
|
|
||||||
PERCENTEQUAL = 41
|
|
||||||
PLUS = 14
|
|
||||||
PLUSEQUAL = 37
|
|
||||||
RBRACE = 27
|
|
||||||
RIGHTSHIFT = 35
|
|
||||||
RIGHTSHIFTEQUAL = 46
|
|
||||||
RPAR = 8
|
|
||||||
RSQB = 10
|
|
||||||
SEMI = 13
|
|
||||||
SLASH = 17
|
|
||||||
SLASHEQUAL = 40
|
|
||||||
STAR = 16
|
|
||||||
STAREQUAL = 39
|
|
||||||
STRING = 3
|
|
||||||
TILDE = 32
|
|
||||||
VBAR = 18
|
|
||||||
VBAREQUAL = 43
|
|
||||||
"""
|
|
File diff suppressed because it is too large
Load Diff
@ -12,7 +12,6 @@ class CopyServiceTestCase(TestCase):
|
|||||||
"main_window",
|
"main_window",
|
||||||
"properties",
|
"properties",
|
||||||
"undo_manager",
|
"undo_manager",
|
||||||
"import_menu",
|
|
||||||
"export_menu",
|
"export_menu",
|
||||||
"tools_menu",
|
"tools_menu",
|
||||||
]
|
]
|
||||||
|
@ -73,7 +73,7 @@ def hamburger_menu(hamburger_model):
|
|||||||
return button
|
return button
|
||||||
|
|
||||||
|
|
||||||
def create_hamburger_model(import_menu, export_menu, tools_menu):
|
def create_hamburger_model(export_menu, tools_menu):
|
||||||
model = Gio.Menu.new()
|
model = Gio.Menu.new()
|
||||||
|
|
||||||
part = Gio.Menu.new()
|
part = Gio.Menu.new()
|
||||||
@ -83,10 +83,6 @@ def create_hamburger_model(import_menu, export_menu, tools_menu):
|
|||||||
|
|
||||||
part = Gio.Menu.new()
|
part = Gio.Menu.new()
|
||||||
part.append(_("Save As..."), "win.file-save-as")
|
part.append(_("Save As..."), "win.file-save-as")
|
||||||
model.append_section(None, part)
|
|
||||||
|
|
||||||
part = Gio.Menu.new()
|
|
||||||
part.append_submenu(_("Import"), import_menu)
|
|
||||||
part.append_submenu(_("Export"), export_menu)
|
part.append_submenu(_("Export"), export_menu)
|
||||||
model.append_section(None, part)
|
model.append_section(None, part)
|
||||||
|
|
||||||
@ -134,7 +130,6 @@ class MainWindow(Service, ActionProvider):
|
|||||||
component_registry,
|
component_registry,
|
||||||
element_factory,
|
element_factory,
|
||||||
properties,
|
properties,
|
||||||
import_menu,
|
|
||||||
export_menu,
|
export_menu,
|
||||||
tools_menu,
|
tools_menu,
|
||||||
):
|
):
|
||||||
@ -142,7 +137,6 @@ class MainWindow(Service, ActionProvider):
|
|||||||
self.component_registry = component_registry
|
self.component_registry = component_registry
|
||||||
self.element_factory = element_factory
|
self.element_factory = element_factory
|
||||||
self.properties = properties
|
self.properties = properties
|
||||||
self.import_menu = import_menu
|
|
||||||
self.export_menu = export_menu
|
self.export_menu = export_menu
|
||||||
self.tools_menu = tools_menu
|
self.tools_menu = tools_menu
|
||||||
|
|
||||||
@ -213,9 +207,7 @@ class MainWindow(Service, ActionProvider):
|
|||||||
|
|
||||||
header.pack_end(
|
header.pack_end(
|
||||||
hamburger_menu(
|
hamburger_menu(
|
||||||
create_hamburger_model(
|
create_hamburger_model(self.export_menu.menu, self.tools_menu.menu)
|
||||||
self.import_menu.menu, self.export_menu.menu, self.tools_menu.menu
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
header.pack_end(button(_("Save"), "win.file-save"))
|
header.pack_end(button(_("Save"), "win.file-save"))
|
||||||
|
@ -21,7 +21,6 @@ class DiagramPageTestCase(unittest.TestCase):
|
|||||||
"diagrams",
|
"diagrams",
|
||||||
"toolbox",
|
"toolbox",
|
||||||
"elementeditor",
|
"elementeditor",
|
||||||
"import_menu",
|
|
||||||
"export_menu",
|
"export_menu",
|
||||||
"tools_menu",
|
"tools_menu",
|
||||||
]
|
]
|
||||||
|
@ -21,7 +21,6 @@ class DiagramToolboxTestCase(TestCase):
|
|||||||
"element_factory",
|
"element_factory",
|
||||||
"properties",
|
"properties",
|
||||||
"main_window",
|
"main_window",
|
||||||
"import_menu",
|
|
||||||
"export_menu",
|
"export_menu",
|
||||||
"tools_menu",
|
"tools_menu",
|
||||||
]
|
]
|
||||||
|
@ -18,7 +18,6 @@ class DiagramItemConnectorTestCase(TestCase):
|
|||||||
"namespace",
|
"namespace",
|
||||||
"diagrams",
|
"diagrams",
|
||||||
"toolbox",
|
"toolbox",
|
||||||
"import_menu",
|
|
||||||
"export_menu",
|
"export_menu",
|
||||||
"tools_menu",
|
"tools_menu",
|
||||||
"elementeditor",
|
"elementeditor",
|
||||||
|
@ -33,7 +33,6 @@ def application():
|
|||||||
"diagrams",
|
"diagrams",
|
||||||
"toolbox",
|
"toolbox",
|
||||||
"elementeditor",
|
"elementeditor",
|
||||||
"import_menu",
|
|
||||||
"export_menu",
|
"export_menu",
|
||||||
"tools_menu",
|
"tools_menu",
|
||||||
]
|
]
|
||||||
@ -114,7 +113,6 @@ class HandleToolTestCase(unittest.TestCase):
|
|||||||
"diagrams",
|
"diagrams",
|
||||||
"toolbox",
|
"toolbox",
|
||||||
"elementeditor",
|
"elementeditor",
|
||||||
"import_menu",
|
|
||||||
"export_menu",
|
"export_menu",
|
||||||
"tools_menu",
|
"tools_menu",
|
||||||
]
|
]
|
||||||
|
@ -19,7 +19,6 @@ def application():
|
|||||||
"diagrams",
|
"diagrams",
|
||||||
"toolbox",
|
"toolbox",
|
||||||
"elementeditor",
|
"elementeditor",
|
||||||
"import_menu",
|
|
||||||
"export_menu",
|
"export_menu",
|
||||||
"tools_menu",
|
"tools_menu",
|
||||||
]
|
]
|
||||||
|
@ -69,7 +69,6 @@ gaphorconvert = 'gaphor.tools.gaphorconvert:main'
|
|||||||
"main_window" = "gaphor.ui.mainwindow:MainWindow"
|
"main_window" = "gaphor.ui.mainwindow:MainWindow"
|
||||||
"preferences" = "gaphor.ui.preferences:Preferences"
|
"preferences" = "gaphor.ui.preferences:Preferences"
|
||||||
"export_menu" = "gaphor.ui.menufragment:MenuFragment"
|
"export_menu" = "gaphor.ui.menufragment:MenuFragment"
|
||||||
"import_menu" = "gaphor.ui.menufragment:MenuFragment"
|
|
||||||
"tools_menu" = "gaphor.ui.menufragment:MenuFragment"
|
"tools_menu" = "gaphor.ui.menufragment:MenuFragment"
|
||||||
"copy" = "gaphor.services.copyservice:CopyService"
|
"copy" = "gaphor.services.copyservice:CopyService"
|
||||||
"sanitizer" = "gaphor.services.sanitizerservice:SanitizerService"
|
"sanitizer" = "gaphor.services.sanitizerservice:SanitizerService"
|
||||||
@ -81,8 +80,6 @@ gaphorconvert = 'gaphor.tools.gaphorconvert:main'
|
|||||||
"elementeditor" = "gaphor.ui.elementeditor:ElementEditor"
|
"elementeditor" = "gaphor.ui.elementeditor:ElementEditor"
|
||||||
"diagram_export" = "gaphor.plugins.diagramexport:DiagramExport"
|
"diagram_export" = "gaphor.plugins.diagramexport:DiagramExport"
|
||||||
"xmi_export" = "gaphor.plugins.xmiexport:XMIExport"
|
"xmi_export" = "gaphor.plugins.xmiexport:XMIExport"
|
||||||
"diagram_layout" = "gaphor.plugins.diagramlayout:DiagramLayout"
|
|
||||||
"pynsource" = "gaphor.plugins.pynsource:PyNSource"
|
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["poetry==1.0.0b2"]
|
requires = ["poetry==1.0.0b2"]
|
||||||
|
3
setup.py
3
setup.py
@ -100,7 +100,6 @@ setup(
|
|||||||
"main_window = gaphor.ui.mainwindow:MainWindow",
|
"main_window = gaphor.ui.mainwindow:MainWindow",
|
||||||
"preferences = gaphor.ui.preferences:Preferences",
|
"preferences = gaphor.ui.preferences:Preferences",
|
||||||
"export_menu = gaphor.ui.menufragment:MenuFragment",
|
"export_menu = gaphor.ui.menufragment:MenuFragment",
|
||||||
"import_menu = gaphor.ui.menufragment:MenuFragment",
|
|
||||||
"tools_menu = gaphor.ui.menufragment:MenuFragment",
|
"tools_menu = gaphor.ui.menufragment:MenuFragment",
|
||||||
"copy = gaphor.services.copyservice:CopyService",
|
"copy = gaphor.services.copyservice:CopyService",
|
||||||
"sanitizer = gaphor.services.sanitizerservice:SanitizerService",
|
"sanitizer = gaphor.services.sanitizerservice:SanitizerService",
|
||||||
@ -112,8 +111,6 @@ setup(
|
|||||||
"elementeditor = gaphor.ui.elementeditor:ElementEditor",
|
"elementeditor = gaphor.ui.elementeditor:ElementEditor",
|
||||||
"diagram_export = gaphor.plugins.diagramexport:DiagramExport",
|
"diagram_export = gaphor.plugins.diagramexport:DiagramExport",
|
||||||
"xmi_export = gaphor.plugins.xmiexport:XMIExport",
|
"xmi_export = gaphor.plugins.xmiexport:XMIExport",
|
||||||
"diagram_layout = gaphor.plugins.diagramlayout:DiagramLayout",
|
|
||||||
"pynsource = gaphor.plugins.pynsource:PyNSource",
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
cmdclass={
|
cmdclass={
|
||||||
|
@ -3,8 +3,6 @@ if __name__ == "__main__":
|
|||||||
from gaphor import core
|
from gaphor import core
|
||||||
from gaphor.UML.elementfactory import ElementFactory
|
from gaphor.UML.elementfactory import ElementFactory
|
||||||
from gaphor.plugins.diagramexport import DiagramExport
|
from gaphor.plugins.diagramexport import DiagramExport
|
||||||
from gaphor.plugins.diagramlayout import DiagramLayout
|
|
||||||
from gaphor.plugins.pynsource import PyNSource
|
|
||||||
from gaphor.plugins.xmiexport import XMIExport
|
from gaphor.plugins.xmiexport import XMIExport
|
||||||
from gaphor.services.componentregistry import ComponentRegistry
|
from gaphor.services.componentregistry import ComponentRegistry
|
||||||
from gaphor.services.copyservice import CopyService
|
from gaphor.services.copyservice import CopyService
|
||||||
|
Loading…
Reference in New Issue
Block a user