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",
|
||||
"properties",
|
||||
"undo_manager",
|
||||
"import_menu",
|
||||
"export_menu",
|
||||
"tools_menu",
|
||||
]
|
||||
|
@ -73,7 +73,7 @@ def hamburger_menu(hamburger_model):
|
||||
return button
|
||||
|
||||
|
||||
def create_hamburger_model(import_menu, export_menu, tools_menu):
|
||||
def create_hamburger_model(export_menu, tools_menu):
|
||||
model = 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.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)
|
||||
model.append_section(None, part)
|
||||
|
||||
@ -134,7 +130,6 @@ class MainWindow(Service, ActionProvider):
|
||||
component_registry,
|
||||
element_factory,
|
||||
properties,
|
||||
import_menu,
|
||||
export_menu,
|
||||
tools_menu,
|
||||
):
|
||||
@ -142,7 +137,6 @@ class MainWindow(Service, ActionProvider):
|
||||
self.component_registry = component_registry
|
||||
self.element_factory = element_factory
|
||||
self.properties = properties
|
||||
self.import_menu = import_menu
|
||||
self.export_menu = export_menu
|
||||
self.tools_menu = tools_menu
|
||||
|
||||
@ -213,9 +207,7 @@ class MainWindow(Service, ActionProvider):
|
||||
|
||||
header.pack_end(
|
||||
hamburger_menu(
|
||||
create_hamburger_model(
|
||||
self.import_menu.menu, self.export_menu.menu, self.tools_menu.menu
|
||||
)
|
||||
create_hamburger_model(self.export_menu.menu, self.tools_menu.menu)
|
||||
)
|
||||
)
|
||||
header.pack_end(button(_("Save"), "win.file-save"))
|
||||
|
@ -21,7 +21,6 @@ class DiagramPageTestCase(unittest.TestCase):
|
||||
"diagrams",
|
||||
"toolbox",
|
||||
"elementeditor",
|
||||
"import_menu",
|
||||
"export_menu",
|
||||
"tools_menu",
|
||||
]
|
||||
|
@ -21,7 +21,6 @@ class DiagramToolboxTestCase(TestCase):
|
||||
"element_factory",
|
||||
"properties",
|
||||
"main_window",
|
||||
"import_menu",
|
||||
"export_menu",
|
||||
"tools_menu",
|
||||
]
|
||||
|
@ -18,7 +18,6 @@ class DiagramItemConnectorTestCase(TestCase):
|
||||
"namespace",
|
||||
"diagrams",
|
||||
"toolbox",
|
||||
"import_menu",
|
||||
"export_menu",
|
||||
"tools_menu",
|
||||
"elementeditor",
|
||||
|
@ -33,7 +33,6 @@ def application():
|
||||
"diagrams",
|
||||
"toolbox",
|
||||
"elementeditor",
|
||||
"import_menu",
|
||||
"export_menu",
|
||||
"tools_menu",
|
||||
]
|
||||
@ -114,7 +113,6 @@ class HandleToolTestCase(unittest.TestCase):
|
||||
"diagrams",
|
||||
"toolbox",
|
||||
"elementeditor",
|
||||
"import_menu",
|
||||
"export_menu",
|
||||
"tools_menu",
|
||||
]
|
||||
|
@ -19,7 +19,6 @@ def application():
|
||||
"diagrams",
|
||||
"toolbox",
|
||||
"elementeditor",
|
||||
"import_menu",
|
||||
"export_menu",
|
||||
"tools_menu",
|
||||
]
|
||||
|
@ -69,7 +69,6 @@ gaphorconvert = 'gaphor.tools.gaphorconvert:main'
|
||||
"main_window" = "gaphor.ui.mainwindow:MainWindow"
|
||||
"preferences" = "gaphor.ui.preferences:Preferences"
|
||||
"export_menu" = "gaphor.ui.menufragment:MenuFragment"
|
||||
"import_menu" = "gaphor.ui.menufragment:MenuFragment"
|
||||
"tools_menu" = "gaphor.ui.menufragment:MenuFragment"
|
||||
"copy" = "gaphor.services.copyservice:CopyService"
|
||||
"sanitizer" = "gaphor.services.sanitizerservice:SanitizerService"
|
||||
@ -81,8 +80,6 @@ gaphorconvert = 'gaphor.tools.gaphorconvert:main'
|
||||
"elementeditor" = "gaphor.ui.elementeditor:ElementEditor"
|
||||
"diagram_export" = "gaphor.plugins.diagramexport:DiagramExport"
|
||||
"xmi_export" = "gaphor.plugins.xmiexport:XMIExport"
|
||||
"diagram_layout" = "gaphor.plugins.diagramlayout:DiagramLayout"
|
||||
"pynsource" = "gaphor.plugins.pynsource:PyNSource"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry==1.0.0b2"]
|
||||
|
3
setup.py
3
setup.py
@ -100,7 +100,6 @@ setup(
|
||||
"main_window = gaphor.ui.mainwindow:MainWindow",
|
||||
"preferences = gaphor.ui.preferences:Preferences",
|
||||
"export_menu = gaphor.ui.menufragment:MenuFragment",
|
||||
"import_menu = gaphor.ui.menufragment:MenuFragment",
|
||||
"tools_menu = gaphor.ui.menufragment:MenuFragment",
|
||||
"copy = gaphor.services.copyservice:CopyService",
|
||||
"sanitizer = gaphor.services.sanitizerservice:SanitizerService",
|
||||
@ -112,8 +111,6 @@ setup(
|
||||
"elementeditor = gaphor.ui.elementeditor:ElementEditor",
|
||||
"diagram_export = gaphor.plugins.diagramexport:DiagramExport",
|
||||
"xmi_export = gaphor.plugins.xmiexport:XMIExport",
|
||||
"diagram_layout = gaphor.plugins.diagramlayout:DiagramLayout",
|
||||
"pynsource = gaphor.plugins.pynsource:PyNSource",
|
||||
],
|
||||
},
|
||||
cmdclass={
|
||||
|
@ -3,8 +3,6 @@ if __name__ == "__main__":
|
||||
from gaphor import core
|
||||
from gaphor.UML.elementfactory import ElementFactory
|
||||
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.services.componentregistry import ComponentRegistry
|
||||
from gaphor.services.copyservice import CopyService
|
||||
|
Loading…
Reference in New Issue
Block a user