Graphics/ImportsDiagramBuilder.py

changeset 2031
c36c2eb62a75
parent 2030
db11a2fe9bbc
child 2033
4b99609f6a87
equal deleted inserted replaced
2030:db11a2fe9bbc 2031:c36c2eb62a75
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2007 - 2012 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a dialog showing an imports diagram of a package.
8 """
9
10 import glob
11 import os
12
13 from PyQt4.QtGui import QProgressDialog, QApplication, QGraphicsTextItem
14
15 from .UMLDiagramBuilder import UMLDiagramBuilder
16 from .ModuleItem import ModuleItem, ModuleModel
17 from .AssociationItem import AssociationItem, Imports
18
19 import Utilities.ModuleParser
20 import Utilities
21 import Preferences
22
23
24 class ImportsDiagramBuilder(UMLDiagramBuilder):
25 """
26 Class implementing a builder for imports diagrams of a package.
27
28 Note: Only package internal imports are shown in order to maintain
29 some readability.
30 """
31 def __init__(self, dialog, view, project, package, showExternalImports=False):
32 """
33 Constructor
34
35 @param dialog reference to the UML dialog (UMLDialog)
36 @param view reference to the view object (UMLGraphicsView)
37 @param project reference to the project object (Project)
38 @param package name of a python package to show the import
39 relationships (string)
40 @keyparam showExternalImports flag indicating to show exports from outside
41 the package (boolean)
42 """
43 super().__init__(dialog, view, project)
44 self.setObjectName("ImportsDiagram")
45
46 self.showExternalImports = showExternalImports
47 self.packagePath = Utilities.normabspath(package)
48 self.package = os.path.splitdrive(self.packagePath)[1].replace(os.sep, '.')[1:]
49 hasInit = True
50 ppath = self.packagePath
51 while hasInit:
52 ppath = os.path.dirname(ppath)
53 hasInit = len(glob.glob(os.path.join(ppath, '__init__.*'))) > 0
54 self.shortPackage = self.packagePath.replace(ppath, '').replace(os.sep, '.')[1:]
55
56 self.umlView.setPersistenceData("package={0}".format(self.packagePath))
57
58 pname = project.getProjectName()
59 if pname:
60 name = self.trUtf8("Imports Diagramm {0}: {1}").format(
61 pname, project.getRelativePath(self.packagePath))
62 else:
63 name = self.trUtf8("Imports Diagramm: {0}").format(self.packagePath)
64 self.umlView.setDiagramName(name)
65
66 def __buildModulesDict(self):
67 """
68 Private method to build a dictionary of modules contained in the package.
69
70 @return dictionary of modules contained in the package.
71 """
72 extensions = Preferences.getPython("PythonExtensions") + \
73 Preferences.getPython("Python3Extensions")
74 moduleDict = {}
75 modules = []
76 for ext in Preferences.getPython("PythonExtensions") + \
77 Preferences.getPython("Python3Extensions"):
78 modules.extend(
79 glob.glob(Utilities.normjoinpath(self.packagePath, '*{0}'.format(ext))))
80
81 tot = len(modules)
82 try:
83 prog = 0
84 progress = QProgressDialog(self.trUtf8("Parsing modules..."),
85 None, 0, tot, self.parent())
86 progress.show()
87 QApplication.processEvents()
88 for module in modules:
89 progress.setValue(prog)
90 QApplication.processEvents()
91 prog = prog + 1
92 try:
93 mod = Utilities.ModuleParser.readModule(module, extensions=extensions,
94 caching=False)
95 except ImportError:
96 continue
97 else:
98 name = mod.name
99 if name.startswith(self.package):
100 name = name[len(self.package) + 1:]
101 moduleDict[name] = mod
102 finally:
103 progress.setValue(tot)
104 return moduleDict
105
106 def buildDiagram(self):
107 """
108 Public method to build the modules shapes of the diagram.
109 """
110 initlist = glob.glob(os.path.join(self.packagePath, '__init__.*'))
111 if len(initlist) == 0:
112 ct = QGraphicsTextItem(None, self.scene)
113 ct.setHtml(
114 self.trUtf8("The directory <b>'{0}'</b> is not a Python package.")\
115 .format(self.package))
116 return
117
118 shapes = {}
119 p = 10
120 y = 10
121 maxHeight = 0
122 sceneRect = self.umlView.sceneRect()
123
124 modules = self.__buildModulesDict()
125 sortedkeys = sorted(modules.keys())
126 externalMods = []
127 packageList = self.shortPackage.split('.')
128 packageListLen = len(packageList)
129 for module in sortedkeys:
130 impLst = []
131 for i in modules[module].imports:
132 if i.startswith(self.package):
133 n = i[len(self.package) + 1:]
134 else:
135 n = i
136 if i in modules:
137 impLst.append(n)
138 elif self.showExternalImports:
139 impLst.append(n)
140 if not n in externalMods:
141 externalMods.append(n)
142 for i in list(modules[module].from_imports.keys()):
143 if i.startswith('.'):
144 dots = len(i) - len(i.lstrip('.'))
145 if dots == 1:
146 n = i[1:]
147 i = n
148 else:
149 if self.showExternalImports:
150 n = '.'.join(
151 packageList[:packageListLen - dots + 1] + [i[dots:]])
152 else:
153 n = i
154 elif i.startswith(self.package):
155 n = i[len(self.package) + 1:]
156 else:
157 n = i
158 if i in modules:
159 impLst.append(n)
160 elif self.showExternalImports:
161 impLst.append(n)
162 if not n in externalMods:
163 externalMods.append(n)
164 classNames = []
165 for cls in list(modules[module].classes.keys()):
166 className = modules[module].classes[cls].name
167 if className not in classNames:
168 classNames.append(className)
169 shape = self.__addModule(module, classNames, 0.0, 0.0)
170 shapeRect = shape.sceneBoundingRect()
171 shapes[module] = (shape, impLst)
172 pn = p + shapeRect.width() + 10
173 maxHeight = max(maxHeight, shapeRect.height())
174 if pn > sceneRect.width():
175 p = 10
176 y += maxHeight + 10
177 maxHeight = shapeRect.height()
178 shape.setPos(p, y)
179 p += shapeRect.width() + 10
180 else:
181 shape.setPos(p, y)
182 p = pn
183
184 for module in externalMods:
185 shape = self.__addModule(module, [], 0.0, 0.0)
186 shapeRect = shape.sceneBoundingRect()
187 shapes[module] = (shape, [])
188 pn = p + shapeRect.width() + 10
189 maxHeight = max(maxHeight, shapeRect.height())
190 if pn > sceneRect.width():
191 p = 10
192 y += maxHeight + 10
193 maxHeight = shapeRect.height()
194 shape.setPos(p, y)
195 p += shapeRect.width() + 10
196 else:
197 shape.setPos(p, y)
198 p = pn
199
200 rect = self.umlView._getDiagramRect(10)
201 sceneRect = self.umlView.sceneRect()
202 if rect.width() > sceneRect.width():
203 sceneRect.setWidth(rect.width())
204 if rect.height() > sceneRect.height():
205 sceneRect.setHeight(rect.height())
206 self.umlView.setSceneSize(sceneRect.width(), sceneRect.height())
207
208 self.__createAssociations(shapes)
209 self.umlView.autoAdjustSceneSize(limit=True)
210
211 def __addModule(self, name, classes, x, y):
212 """
213 Private method to add a module to the diagram.
214
215 @param name module name to be shown (string)
216 @param classes list of class names contained in the module
217 (list of strings)
218 @param x x-coordinate (float)
219 @param y y-coordinate (float)
220 """
221 classes.sort()
222 impM = ModuleModel(name, classes)
223 impW = ModuleItem(impM, x, y, scene=self.scene)
224 impW.setId(self.umlView.getItemId())
225 return impW
226
227 def __createAssociations(self, shapes):
228 """
229 Private method to generate the associations between the module shapes.
230
231 @param shapes list of shapes
232 """
233 for module in list(shapes.keys()):
234 for rel in shapes[module][1]:
235 assoc = AssociationItem(
236 shapes[module][0], shapes[rel][0],
237 Imports)
238 self.scene.addItem(assoc)

eric ide

mercurial