eric7/Project/ProjectSourcesBrowser.py

branch
eric7
changeset 8312
800c432b34c8
parent 8277
ea734702ae94
child 8318
962bce857696
equal deleted inserted replaced
8311:4e8b98454baa 8312:800c432b34c8
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2002 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a class used to display the Sources part of the project.
8 """
9
10 import os
11 import contextlib
12
13 from PyQt5.QtCore import pyqtSignal
14 from PyQt5.QtWidgets import QDialog, QInputDialog, QMenu
15
16 from E5Gui import E5MessageBox
17
18 from UI.BrowserModel import (
19 BrowserFileItem, BrowserClassItem, BrowserMethodItem,
20 BrowserClassAttributeItem, BrowserImportItem
21 )
22
23 from .ProjectBrowserModel import (
24 ProjectBrowserFileItem, ProjectBrowserSimpleDirectoryItem,
25 ProjectBrowserDirectoryItem, ProjectBrowserSourceType
26 )
27 from .ProjectBaseBrowser import ProjectBaseBrowser
28
29 import Utilities
30 import UI.PixmapCache
31
32
33 class ProjectSourcesBrowser(ProjectBaseBrowser):
34 """
35 A class used to display the Sources part of the project.
36
37 @signal showMenu(str, QMenu) emitted when a menu is about to be shown.
38 The name of the menu and a reference to the menu are given.
39 """
40 showMenu = pyqtSignal(str, QMenu)
41
42 def __init__(self, project, parent=None):
43 """
44 Constructor
45
46 @param project reference to the project object
47 @param parent parent widget of this browser (QWidget)
48 """
49 ProjectBaseBrowser.__init__(self, project, ProjectBrowserSourceType,
50 parent)
51
52 self.selectedItemsFilter = [ProjectBrowserFileItem,
53 ProjectBrowserSimpleDirectoryItem]
54
55 self.setWindowTitle(self.tr('Sources'))
56
57 self.setWhatsThis(self.tr(
58 """<b>Project Sources Browser</b>"""
59 """<p>This allows to easily see all sources contained in the"""
60 """ current project. Several actions can be executed via the"""
61 """ context menu.</p>"""
62 ))
63
64 project.prepareRepopulateItem.connect(self._prepareRepopulateItem)
65 project.completeRepopulateItem.connect(self._completeRepopulateItem)
66
67 self.codemetrics = None
68 self.codecoverage = None
69 self.profiledata = None
70 self.classDiagram = None
71 self.importsDiagram = None
72 self.packageDiagram = None
73 self.applicationDiagram = None
74 self.loadedDiagram = None
75
76 def __closeAllWindows(self):
77 """
78 Private method to close all project related windows.
79 """
80 self.codemetrics and self.codemetrics.close()
81 self.codecoverage and self.codecoverage.close()
82 self.profiledata and self.profiledata.close()
83 self.classDiagram and self.classDiagram.close()
84 self.importsDiagram and self.importsDiagram.close()
85 self.packageDiagram and self.packageDiagram.close()
86 self.applicationDiagram and self.applicationDiagram.close()
87 self.loadedDiagram and self.loadedDiagram.close()
88
89 def _projectClosed(self):
90 """
91 Protected slot to handle the projectClosed signal.
92 """
93 self.__closeAllWindows()
94 ProjectBaseBrowser._projectClosed(self)
95
96 def _createPopupMenus(self):
97 """
98 Protected overloaded method to generate the popup menu.
99 """
100 ProjectBaseBrowser._createPopupMenus(self)
101 self.sourceMenuActions = {}
102
103 if self.project.isPythonProject():
104 self.__createPythonPopupMenus()
105 elif self.project.isRubyProject():
106 self.__createRubyPopupMenus()
107 elif self.project.isJavaScriptProject():
108 self.__createJavaScriptPopupMenus()
109
110 def __createPythonPopupMenus(self):
111 """
112 Private method to generate the popup menus for a Python project.
113 """
114 self.checksMenu = QMenu(self.tr('Check'))
115 self.checksMenu.aboutToShow.connect(self.__showContextMenuCheck)
116
117 self.menuShow = QMenu(self.tr('Show'))
118 self.menuShow.addAction(
119 self.tr('Code metrics...'), self.__showCodeMetrics)
120 self.coverageMenuAction = self.menuShow.addAction(
121 self.tr('Code coverage...'), self.__showCodeCoverage)
122 self.profileMenuAction = self.menuShow.addAction(
123 self.tr('Profile data...'), self.__showProfileData)
124 self.menuShow.aboutToShow.connect(self.__showContextMenuShow)
125
126 self.graphicsMenu = QMenu(self.tr('Diagrams'))
127 self.classDiagramAction = self.graphicsMenu.addAction(
128 self.tr("Class Diagram..."), self.__showClassDiagram)
129 self.graphicsMenu.addAction(
130 self.tr("Package Diagram..."), self.__showPackageDiagram)
131 self.importsDiagramAction = self.graphicsMenu.addAction(
132 self.tr("Imports Diagram..."), self.__showImportsDiagram)
133 self.graphicsMenu.addAction(
134 self.tr("Application Diagram..."),
135 self.__showApplicationDiagram)
136 self.graphicsMenu.addSeparator()
137 self.graphicsMenu.addAction(
138 UI.PixmapCache.getIcon("open"),
139 self.tr("Load Diagram..."), self.__loadDiagram)
140 self.graphicsMenu.aboutToShow.connect(self.__showContextMenuGraphics)
141
142 self.unittestAction = self.sourceMenu.addAction(
143 self.tr('Run unittest...'), self.handleUnittest)
144 self.sourceMenu.addSeparator()
145 act = self.sourceMenu.addAction(
146 self.tr('Rename file'), self._renameFile)
147 self.menuActions.append(act)
148 act = self.sourceMenu.addAction(
149 self.tr('Remove from project'), self._removeFile)
150 self.menuActions.append(act)
151 act = self.sourceMenu.addAction(
152 self.tr('Delete'), self.__deleteFile)
153 self.menuActions.append(act)
154 self.sourceMenu.addSeparator()
155 self.sourceMenu.addAction(
156 self.tr('New package...'), self.__addNewPackage)
157 self.sourceMenu.addAction(
158 self.tr('Add source files...'), self.__addSourceFiles)
159 self.sourceMenu.addAction(
160 self.tr('Add source directory...'), self.__addSourceDirectory)
161 self.sourceMenu.addSeparator()
162 act = self.sourceMenu.addMenu(self.graphicsMenu)
163 self.sourceMenu.addSeparator()
164 self.sourceMenu.addMenu(self.checksMenu)
165 self.sourceMenu.addSeparator()
166 self.sourceMenuActions["Show"] = self.sourceMenu.addMenu(self.menuShow)
167 self.sourceMenu.addSeparator()
168 self.sourceMenu.addAction(
169 self.tr('Copy Path to Clipboard'), self._copyToClipboard)
170 self.sourceMenu.addSeparator()
171 self.sourceMenu.addAction(
172 self.tr('Expand all directories'), self._expandAllDirs)
173 self.sourceMenu.addAction(
174 self.tr('Collapse all directories'), self._collapseAllDirs)
175 self.sourceMenu.addSeparator()
176 self.sourceMenu.addAction(self.tr('Configure...'), self._configure)
177
178 self.menu.addSeparator()
179 self.menu.addAction(
180 self.tr('New package...'), self.__addNewPackage)
181 self.menu.addAction(
182 self.tr('Add source files...'), self.__addSourceFiles)
183 self.menu.addAction(
184 self.tr('Add source directory...'), self.__addSourceDirectory)
185 self.menu.addSeparator()
186 self.menu.addAction(
187 self.tr('Expand all directories'), self._expandAllDirs)
188 self.menu.addAction(
189 self.tr('Collapse all directories'), self._collapseAllDirs)
190 self.menu.addSeparator()
191 self.menu.addAction(self.tr('Configure...'), self._configure)
192
193 # create the attribute menu
194 self.gotoMenu = QMenu(self.tr("Goto"), self)
195 self.gotoMenu.aboutToShow.connect(self._showGotoMenu)
196 self.gotoMenu.triggered.connect(self._gotoAttribute)
197
198 self.attributeMenu = QMenu(self)
199 self.attributeMenu.addMenu(self.gotoMenu)
200 self.attributeMenu.addSeparator()
201 self.attributeMenu.addAction(
202 self.tr('New package...'), self.__addNewPackage)
203 self.attributeMenu.addAction(
204 self.tr('Add source files...'), self.project.addSourceFiles)
205 self.attributeMenu.addAction(
206 self.tr('Add source directory...'), self.project.addSourceDir)
207 self.attributeMenu.addSeparator()
208 self.attributeMenu.addAction(
209 self.tr('Expand all directories'), self._expandAllDirs)
210 self.attributeMenu.addAction(
211 self.tr('Collapse all directories'), self._collapseAllDirs)
212 self.attributeMenu.addSeparator()
213 self.attributeMenu.addAction(
214 self.tr('Configure...'), self._configure)
215
216 self.backMenu = QMenu(self)
217 self.backMenu.addAction(
218 self.tr('New package...'), self.__addNewPackage)
219 self.backMenu.addAction(
220 self.tr('Add source files...'), self.project.addSourceFiles)
221 self.backMenu.addAction(
222 self.tr('Add source directory...'), self.project.addSourceDir)
223 self.backMenu.addSeparator()
224 self.backMenu.addAction(
225 self.tr('Expand all directories'), self._expandAllDirs)
226 self.backMenu.addAction(
227 self.tr('Collapse all directories'), self._collapseAllDirs)
228 self.backMenu.addSeparator()
229 self.backMenu.addAction(self.tr('Configure...'), self._configure)
230 self.backMenu.setEnabled(False)
231
232 self.multiMenu.addSeparator()
233 act = self.multiMenu.addAction(
234 self.tr('Remove from project'), self._removeFile)
235 self.multiMenuActions.append(act)
236 act = self.multiMenu.addAction(
237 self.tr('Delete'), self.__deleteFile)
238 self.multiMenuActions.append(act)
239 self.multiMenu.addSeparator()
240 self.multiMenu.addMenu(self.checksMenu)
241 self.multiMenu.addSeparator()
242 self.multiMenu.addAction(
243 self.tr('Expand all directories'), self._expandAllDirs)
244 self.multiMenu.addAction(
245 self.tr('Collapse all directories'), self._collapseAllDirs)
246 self.multiMenu.addSeparator()
247 self.multiMenu.addAction(self.tr('Configure...'), self._configure)
248
249 self.dirMenu = QMenu(self)
250 act = self.dirMenu.addAction(
251 self.tr('Remove from project'), self._removeDir)
252 self.dirMenuActions.append(act)
253 act = self.dirMenu.addAction(
254 self.tr('Delete'), self._deleteDirectory)
255 self.dirMenuActions.append(act)
256 self.dirMenu.addSeparator()
257 self.dirMenu.addAction(
258 self.tr('New package...'), self.__addNewPackage)
259 self.dirMenu.addAction(
260 self.tr('Add source files...'), self.__addSourceFiles)
261 self.dirMenu.addAction(
262 self.tr('Add source directory...'), self.__addSourceDirectory)
263 self.dirMenu.addSeparator()
264 act = self.dirMenu.addMenu(self.graphicsMenu)
265 self.dirMenu.addSeparator()
266 self.dirMenu.addMenu(self.checksMenu)
267 self.dirMenu.addSeparator()
268 self.dirMenu.addAction(
269 self.tr('Copy Path to Clipboard'), self._copyToClipboard)
270 self.dirMenu.addSeparator()
271 self.dirMenu.addAction(
272 self.tr('Expand all directories'), self._expandAllDirs)
273 self.dirMenu.addAction(
274 self.tr('Collapse all directories'), self._collapseAllDirs)
275 self.dirMenu.addSeparator()
276 self.dirMenu.addAction(self.tr('Configure...'), self._configure)
277
278 self.dirMultiMenu = QMenu(self)
279 self.dirMultiMenu.addAction(
280 self.tr('Expand all directories'), self._expandAllDirs)
281 self.dirMultiMenu.addAction(
282 self.tr('Collapse all directories'), self._collapseAllDirs)
283 self.dirMultiMenu.addSeparator()
284 self.dirMultiMenu.addAction(
285 self.tr('Configure...'), self._configure)
286
287 self.sourceMenu.aboutToShow.connect(self.__showContextMenu)
288 self.multiMenu.aboutToShow.connect(self.__showContextMenuMulti)
289 self.dirMenu.aboutToShow.connect(self.__showContextMenuDir)
290 self.dirMultiMenu.aboutToShow.connect(self.__showContextMenuDirMulti)
291 self.backMenu.aboutToShow.connect(self.__showContextMenuBack)
292 self.mainMenu = self.sourceMenu
293
294 def __createRubyPopupMenus(self):
295 """
296 Private method to generate the popup menus for a Ruby project.
297 """
298 self.graphicsMenu = QMenu(self.tr('Diagrams'))
299 self.classDiagramAction = self.graphicsMenu.addAction(
300 self.tr("Class Diagram..."), self.__showClassDiagram)
301 self.graphicsMenu.addAction(
302 self.tr("Package Diagram..."), self.__showPackageDiagram)
303 self.graphicsMenu.addAction(
304 self.tr("Application Diagram..."),
305 self.__showApplicationDiagram)
306 self.graphicsMenu.addSeparator()
307 self.graphicsMenu.addAction(
308 UI.PixmapCache.getIcon("fileOpen"),
309 self.tr("Load Diagram..."), self.__loadDiagram)
310
311 self.sourceMenu.addSeparator()
312 act = self.sourceMenu.addAction(
313 self.tr('Rename file'), self._renameFile)
314 self.menuActions.append(act)
315 act = self.sourceMenu.addAction(
316 self.tr('Remove from project'), self._removeFile)
317 self.menuActions.append(act)
318 act = self.sourceMenu.addAction(
319 self.tr('Delete'), self.__deleteFile)
320 self.menuActions.append(act)
321 self.sourceMenu.addSeparator()
322 self.sourceMenu.addAction(
323 self.tr('Add source files...'), self.__addSourceFiles)
324 self.sourceMenu.addAction(
325 self.tr('Add source directory...'), self.__addSourceDirectory)
326 self.sourceMenu.addSeparator()
327 act = self.sourceMenu.addMenu(self.graphicsMenu)
328 self.sourceMenu.addSeparator()
329 self.sourceMenu.addAction(
330 self.tr('Expand all directories'), self._expandAllDirs)
331 self.sourceMenu.addAction(
332 self.tr('Collapse all directories'), self._collapseAllDirs)
333 self.sourceMenu.addSeparator()
334 self.sourceMenu.addAction(self.tr('Configure...'), self._configure)
335
336 self.menu.addSeparator()
337 self.menu.addAction(
338 self.tr('Add source files...'), self.__addSourceFiles)
339 self.menu.addAction(
340 self.tr('Add source directory...'), self.__addSourceDirectory)
341 self.menu.addSeparator()
342 self.menu.addAction(
343 self.tr('Expand all directories'), self._expandAllDirs)
344 self.menu.addAction(
345 self.tr('Collapse all directories'), self._collapseAllDirs)
346 self.menu.addSeparator()
347 self.menu.addAction(self.tr('Configure...'), self._configure)
348
349 # create the attribute menu
350 self.gotoMenu = QMenu(self.tr("Goto"), self)
351 self.gotoMenu.aboutToShow.connect(self._showGotoMenu)
352 self.gotoMenu.triggered.connect(self._gotoAttribute)
353
354 self.attributeMenu = QMenu(self)
355 self.attributeMenu.addMenu(self.gotoMenu)
356 self.attributeMenu.addSeparator()
357 self.attributeMenu.addAction(
358 self.tr('Add source files...'), self.project.addSourceFiles)
359 self.attributeMenu.addAction(
360 self.tr('Add source directory...'), self.project.addSourceDir)
361 self.attributeMenu.addSeparator()
362 self.attributeMenu.addAction(
363 self.tr('Expand all directories'), self._expandAllDirs)
364 self.attributeMenu.addAction(
365 self.tr('Collapse all directories'), self._collapseAllDirs)
366 self.attributeMenu.addSeparator()
367 self.attributeMenu.addAction(
368 self.tr('Configure...'), self._configure)
369
370 self.backMenu = QMenu(self)
371 self.backMenu.addAction(
372 self.tr('Add source files...'), self.project.addSourceFiles)
373 self.backMenu.addAction(
374 self.tr('Add source directory...'), self.project.addSourceDir)
375 self.backMenu.addSeparator()
376 self.backMenu.addAction(
377 self.tr('Expand all directories'), self._expandAllDirs)
378 self.backMenu.addAction(
379 self.tr('Collapse all directories'), self._collapseAllDirs)
380 self.backMenu.setEnabled(False)
381 self.backMenu.addSeparator()
382 self.backMenu.addAction(self.tr('Configure...'), self._configure)
383
384 self.multiMenu.addSeparator()
385 act = self.multiMenu.addAction(
386 self.tr('Remove from project'), self._removeFile)
387 self.multiMenuActions.append(act)
388 act = self.multiMenu.addAction(
389 self.tr('Delete'), self.__deleteFile)
390 self.multiMenuActions.append(act)
391 self.multiMenu.addSeparator()
392 self.multiMenu.addAction(
393 self.tr('Expand all directories'), self._expandAllDirs)
394 self.multiMenu.addAction(
395 self.tr('Collapse all directories'), self._collapseAllDirs)
396 self.multiMenu.addSeparator()
397 self.multiMenu.addAction(self.tr('Configure...'), self._configure)
398
399 self.dirMenu = QMenu(self)
400 act = self.dirMenu.addAction(
401 self.tr('Remove from project'), self._removeDir)
402 self.dirMenuActions.append(act)
403 self.dirMenu.addSeparator()
404 self.dirMenu.addAction(
405 self.tr('Add source files...'), self.__addSourceFiles)
406 self.dirMenu.addAction(
407 self.tr('Add source directory...'), self.__addSourceDirectory)
408 self.dirMenu.addSeparator()
409 act = self.dirMenu.addMenu(self.graphicsMenu)
410 self.dirMenu.addSeparator()
411 self.dirMenu.addAction(
412 self.tr('Expand all directories'), self._expandAllDirs)
413 self.dirMenu.addAction(
414 self.tr('Collapse all directories'), self._collapseAllDirs)
415 self.dirMenu.addSeparator()
416 self.dirMenu.addAction(self.tr('Configure...'), self._configure)
417
418 self.dirMultiMenu = QMenu(self)
419 self.dirMultiMenu.addAction(
420 self.tr('Expand all directories'), self._expandAllDirs)
421 self.dirMultiMenu.addAction(
422 self.tr('Collapse all directories'), self._collapseAllDirs)
423 self.dirMultiMenu.addSeparator()
424 self.dirMultiMenu.addAction(
425 self.tr('Configure...'), self._configure)
426
427 self.sourceMenu.aboutToShow.connect(self.__showContextMenu)
428 self.multiMenu.aboutToShow.connect(self.__showContextMenuMulti)
429 self.dirMenu.aboutToShow.connect(self.__showContextMenuDir)
430 self.dirMultiMenu.aboutToShow.connect(self.__showContextMenuDirMulti)
431 self.backMenu.aboutToShow.connect(self.__showContextMenuBack)
432 self.mainMenu = self.sourceMenu
433
434 def __createJavaScriptPopupMenus(self):
435 """
436 Private method to generate the popup menus for a Python project.
437 """
438 self.checksMenu = QMenu(self.tr('Check'))
439 self.checksMenu.aboutToShow.connect(self.__showContextMenuCheck)
440
441 self.sourceMenu.addSeparator()
442 act = self.sourceMenu.addAction(
443 self.tr('Rename file'), self._renameFile)
444 self.menuActions.append(act)
445 act = self.sourceMenu.addAction(
446 self.tr('Remove from project'), self._removeFile)
447 self.menuActions.append(act)
448 act = self.sourceMenu.addAction(
449 self.tr('Delete'), self.__deleteFile)
450 self.menuActions.append(act)
451 self.sourceMenu.addSeparator()
452 self.sourceMenu.addAction(
453 self.tr('Add source files...'), self.__addSourceFiles)
454 self.sourceMenu.addAction(
455 self.tr('Add source directory...'), self.__addSourceDirectory)
456 self.sourceMenu.addSeparator()
457 self.sourceMenu.addMenu(self.checksMenu)
458 self.sourceMenu.addSeparator()
459 self.sourceMenu.addAction(
460 self.tr('Copy Path to Clipboard'), self._copyToClipboard)
461 self.sourceMenu.addSeparator()
462 self.sourceMenu.addAction(
463 self.tr('Expand all directories'), self._expandAllDirs)
464 self.sourceMenu.addAction(
465 self.tr('Collapse all directories'), self._collapseAllDirs)
466 self.sourceMenu.addSeparator()
467 self.sourceMenu.addAction(self.tr('Configure...'), self._configure)
468
469 self.menu.addSeparator()
470 self.menu.addAction(
471 self.tr('Add source files...'), self.__addSourceFiles)
472 self.menu.addAction(
473 self.tr('Add source directory...'), self.__addSourceDirectory)
474 self.menu.addSeparator()
475 self.menu.addAction(
476 self.tr('Expand all directories'), self._expandAllDirs)
477 self.menu.addAction(
478 self.tr('Collapse all directories'), self._collapseAllDirs)
479 self.menu.addSeparator()
480 self.menu.addAction(self.tr('Configure...'), self._configure)
481
482 # create the attribute menu
483 self.gotoMenu = QMenu(self.tr("Goto"), self)
484 self.gotoMenu.aboutToShow.connect(self._showGotoMenu)
485 self.gotoMenu.triggered.connect(self._gotoAttribute)
486
487 self.attributeMenu = QMenu(self)
488 self.attributeMenu.addMenu(self.gotoMenu)
489 self.attributeMenu.addSeparator()
490 self.attributeMenu.addAction(
491 self.tr('Add source files...'), self.project.addSourceFiles)
492 self.attributeMenu.addAction(
493 self.tr('Add source directory...'), self.project.addSourceDir)
494 self.attributeMenu.addSeparator()
495 self.attributeMenu.addAction(
496 self.tr('Expand all directories'), self._expandAllDirs)
497 self.attributeMenu.addAction(
498 self.tr('Collapse all directories'), self._collapseAllDirs)
499 self.attributeMenu.addSeparator()
500 self.attributeMenu.addAction(
501 self.tr('Configure...'), self._configure)
502
503 self.backMenu = QMenu(self)
504 self.backMenu.addAction(
505 self.tr('Add source files...'), self.project.addSourceFiles)
506 self.backMenu.addAction(
507 self.tr('Add source directory...'), self.project.addSourceDir)
508 self.backMenu.addSeparator()
509 self.backMenu.addAction(
510 self.tr('Expand all directories'), self._expandAllDirs)
511 self.backMenu.addAction(
512 self.tr('Collapse all directories'), self._collapseAllDirs)
513 self.backMenu.addSeparator()
514 self.backMenu.addAction(self.tr('Configure...'), self._configure)
515 self.backMenu.setEnabled(False)
516
517 self.multiMenu.addSeparator()
518 act = self.multiMenu.addAction(
519 self.tr('Remove from project'), self._removeFile)
520 self.multiMenuActions.append(act)
521 act = self.multiMenu.addAction(
522 self.tr('Delete'), self.__deleteFile)
523 self.multiMenuActions.append(act)
524 self.multiMenu.addSeparator()
525 self.multiMenu.addMenu(self.checksMenu)
526 self.multiMenu.addSeparator()
527 self.multiMenu.addAction(
528 self.tr('Expand all directories'), self._expandAllDirs)
529 self.multiMenu.addAction(
530 self.tr('Collapse all directories'), self._collapseAllDirs)
531 self.multiMenu.addSeparator()
532 self.multiMenu.addAction(self.tr('Configure...'), self._configure)
533
534 self.dirMenu = QMenu(self)
535 act = self.dirMenu.addAction(
536 self.tr('Remove from project'), self._removeDir)
537 self.dirMenuActions.append(act)
538 act = self.dirMenu.addAction(
539 self.tr('Delete'), self._deleteDirectory)
540 self.dirMenuActions.append(act)
541 self.dirMenu.addSeparator()
542 self.dirMenu.addAction(
543 self.tr('Add source files...'), self.__addSourceFiles)
544 self.dirMenu.addAction(
545 self.tr('Add source directory...'), self.__addSourceDirectory)
546 self.dirMenu.addSeparator()
547 self.dirMenu.addMenu(self.checksMenu)
548 self.dirMenu.addSeparator()
549 self.dirMenu.addAction(
550 self.tr('Copy Path to Clipboard'), self._copyToClipboard)
551 self.dirMenu.addSeparator()
552 self.dirMenu.addAction(
553 self.tr('Expand all directories'), self._expandAllDirs)
554 self.dirMenu.addAction(
555 self.tr('Collapse all directories'), self._collapseAllDirs)
556 self.dirMenu.addSeparator()
557 self.dirMenu.addAction(self.tr('Configure...'), self._configure)
558
559 self.dirMultiMenu = QMenu(self)
560 self.dirMultiMenu.addAction(
561 self.tr('Expand all directories'), self._expandAllDirs)
562 self.dirMultiMenu.addAction(
563 self.tr('Collapse all directories'), self._collapseAllDirs)
564 self.dirMultiMenu.addSeparator()
565 self.dirMultiMenu.addAction(
566 self.tr('Configure...'), self._configure)
567
568 self.sourceMenu.aboutToShow.connect(self.__showContextMenu)
569 self.multiMenu.aboutToShow.connect(self.__showContextMenuMulti)
570 self.dirMenu.aboutToShow.connect(self.__showContextMenuDir)
571 self.dirMultiMenu.aboutToShow.connect(self.__showContextMenuDirMulti)
572 self.backMenu.aboutToShow.connect(self.__showContextMenuBack)
573 self.mainMenu = self.sourceMenu
574
575 def _contextMenuRequested(self, coord):
576 """
577 Protected slot to show the context menu.
578
579 @param coord the position of the mouse pointer (QPoint)
580 """
581 if not self.project.isOpen():
582 return
583
584 with contextlib.suppress(Exception):
585 categories = self.getSelectedItemsCountCategorized(
586 [ProjectBrowserFileItem, BrowserClassItem,
587 BrowserMethodItem, ProjectBrowserSimpleDirectoryItem,
588 BrowserClassAttributeItem, BrowserImportItem])
589 cnt = categories["sum"]
590 if cnt <= 1:
591 index = self.indexAt(coord)
592 if index.isValid():
593 self._selectSingleItem(index)
594 categories = self.getSelectedItemsCountCategorized(
595 [ProjectBrowserFileItem, BrowserClassItem,
596 BrowserMethodItem, ProjectBrowserSimpleDirectoryItem,
597 BrowserClassAttributeItem, BrowserImportItem])
598 cnt = categories["sum"]
599
600 bfcnt = categories[str(ProjectBrowserFileItem)]
601 cmcnt = (
602 categories[str(BrowserClassItem)] +
603 categories[str(BrowserMethodItem)] +
604 categories[str(BrowserClassAttributeItem)] +
605 categories[str(BrowserImportItem)]
606 )
607 sdcnt = categories[str(ProjectBrowserSimpleDirectoryItem)]
608 if cnt > 1 and cnt == bfcnt:
609 self.multiMenu.popup(self.mapToGlobal(coord))
610 elif cnt > 1 and cnt == sdcnt:
611 self.dirMultiMenu.popup(self.mapToGlobal(coord))
612 else:
613 index = self.indexAt(coord)
614 if cnt == 1 and index.isValid():
615 if bfcnt == 1 or cmcnt == 1:
616 itm = self.model().item(index)
617 if isinstance(itm, ProjectBrowserFileItem):
618 fn = itm.fileName()
619 if self.project.isPythonProject():
620 if fn.endswith('.ptl'):
621 for act in self.sourceMenuActions.values():
622 act.setEnabled(False)
623 self.classDiagramAction.setEnabled(True)
624 self.importsDiagramAction.setEnabled(True)
625 self.unittestAction.setEnabled(False)
626 self.checksMenu.menuAction().setEnabled(
627 False)
628 elif fn.endswith('.rb'):
629 # entry for mixed mode programs
630 for act in self.sourceMenuActions.values():
631 act.setEnabled(False)
632 self.classDiagramAction.setEnabled(True)
633 self.importsDiagramAction.setEnabled(False)
634 self.unittestAction.setEnabled(False)
635 self.checksMenu.menuAction().setEnabled(
636 False)
637 elif fn.endswith('.js'):
638 # entry for mixed mode programs
639 for act in self.sourceMenuActions.values():
640 act.setEnabled(False)
641 self.unittestAction.setEnabled(False)
642 self.checksMenu.menuAction().setEnabled(
643 False)
644 self.graphicsMenu.menuAction().setEnabled(
645 False)
646 else:
647 # assume the source file is a Python file
648 for act in self.sourceMenuActions.values():
649 act.setEnabled(True)
650 self.classDiagramAction.setEnabled(True)
651 self.importsDiagramAction.setEnabled(True)
652 self.unittestAction.setEnabled(True)
653 self.checksMenu.menuAction().setEnabled(
654 True)
655 self.sourceMenu.popup(self.mapToGlobal(coord))
656 elif isinstance(
657 itm,
658 (BrowserClassItem, BrowserMethodItem,
659 BrowserImportItem)
660 ):
661 self.menu.popup(self.mapToGlobal(coord))
662 elif isinstance(itm, BrowserClassAttributeItem):
663 self.attributeMenu.popup(self.mapToGlobal(coord))
664 else:
665 self.backMenu.popup(self.mapToGlobal(coord))
666 elif sdcnt == 1:
667 self.classDiagramAction.setEnabled(False)
668 self.dirMenu.popup(self.mapToGlobal(coord))
669 else:
670 self.backMenu.popup(self.mapToGlobal(coord))
671 else:
672 self.backMenu.popup(self.mapToGlobal(coord))
673
674 def __showContextMenu(self):
675 """
676 Private slot called by the sourceMenu aboutToShow signal.
677 """
678 ProjectBaseBrowser._showContextMenu(self, self.sourceMenu)
679
680 self.showMenu.emit("Main", self.sourceMenu)
681
682 def __showContextMenuMulti(self):
683 """
684 Private slot called by the multiMenu aboutToShow signal.
685 """
686 ProjectBaseBrowser._showContextMenuMulti(self, self.multiMenu)
687
688 self.showMenu.emit("MainMulti", self.multiMenu)
689
690 def __showContextMenuDir(self):
691 """
692 Private slot called by the dirMenu aboutToShow signal.
693 """
694 ProjectBaseBrowser._showContextMenuDir(self, self.dirMenu)
695
696 self.showMenu.emit("MainDir", self.dirMenu)
697
698 def __showContextMenuDirMulti(self):
699 """
700 Private slot called by the dirMultiMenu aboutToShow signal.
701 """
702 ProjectBaseBrowser._showContextMenuDirMulti(self, self.dirMultiMenu)
703
704 self.showMenu.emit("MainDirMulti", self.dirMultiMenu)
705
706 def __showContextMenuBack(self):
707 """
708 Private slot called by the backMenu aboutToShow signal.
709 """
710 ProjectBaseBrowser._showContextMenuBack(self, self.backMenu)
711
712 self.showMenu.emit("MainBack", self.backMenu)
713
714 def __showContextMenuShow(self):
715 """
716 Private slot called before the show menu is shown.
717 """
718 prEnable = False
719 coEnable = False
720
721 # first check if the file belongs to a project and there is
722 # a project coverage file
723 fn = self.project.getMainScript(True)
724 if fn is not None:
725 tfn = Utilities.getTestFileName(fn)
726 basename = os.path.splitext(fn)[0]
727 tbasename = os.path.splitext(tfn)[0]
728 prEnable = (
729 prEnable or
730 os.path.isfile("{0}.profile".format(basename)) or
731 os.path.isfile("{0}.profile".format(tbasename))
732 )
733 coEnable = (
734 (coEnable or
735 os.path.isfile("{0}.coverage".format(basename)) or
736 os.path.isfile("{0}.coverage".format(tbasename))) and
737 self.project.isPy3Project()
738 )
739
740 # now check the selected item
741 itm = self.model().item(self.currentIndex())
742 fn = itm.fileName()
743 if fn is not None:
744 basename = os.path.splitext(fn)[0]
745 prEnable = (
746 prEnable or
747 os.path.isfile("{0}.profile".format(basename))
748 )
749 coEnable = (
750 (coEnable or
751 os.path.isfile("{0}.coverage".format(basename))) and
752 itm.isPython3File()
753 )
754
755 self.profileMenuAction.setEnabled(prEnable)
756 self.coverageMenuAction.setEnabled(coEnable)
757
758 self.showMenu.emit("Show", self.menuShow)
759
760 def _openItem(self):
761 """
762 Protected slot to handle the open popup menu entry.
763 """
764 itmList = self.getSelectedItems(
765 [BrowserFileItem, BrowserClassItem, BrowserMethodItem,
766 BrowserClassAttributeItem, BrowserImportItem])
767
768 for itm in itmList:
769 if isinstance(itm, BrowserFileItem):
770 if itm.isPython3File():
771 self.sourceFile[str].emit(itm.fileName())
772 elif itm.isRubyFile():
773 self.sourceFile[str, int, str].emit(
774 itm.fileName(), -1, "Ruby")
775 elif itm.isDFile():
776 self.sourceFile[str, int, str].emit(
777 itm.fileName(), -1, "D")
778 else:
779 self.sourceFile[str].emit(itm.fileName())
780 elif isinstance(itm, BrowserClassItem):
781 self.sourceFile[str, int].emit(
782 itm.fileName(), itm.classObject().lineno)
783 elif isinstance(itm, BrowserMethodItem):
784 self.sourceFile[str, int].emit(
785 itm.fileName(), itm.functionObject().lineno)
786 elif isinstance(itm, BrowserClassAttributeItem):
787 self.sourceFile[str, int].emit(
788 itm.fileName(), itm.attributeObject().lineno)
789 elif isinstance(itm, BrowserImportItem):
790 self.sourceFile[str, list].emit(
791 itm.fileName(), itm.linenos())
792
793 def __addNewPackage(self):
794 """
795 Private method to add a new package to the project.
796 """
797 itm = self.model().item(self.currentIndex())
798 if isinstance(
799 itm,
800 (ProjectBrowserFileItem, BrowserClassItem, BrowserMethodItem)
801 ):
802 dn = os.path.dirname(itm.fileName())
803 elif isinstance(
804 itm,
805 (ProjectBrowserSimpleDirectoryItem, ProjectBrowserDirectoryItem)
806 ):
807 dn = itm.dirName()
808 else:
809 dn = ""
810
811 dn = self.project.getRelativePath(dn)
812 if dn.startswith(os.sep):
813 dn = dn[1:]
814 from .NewPythonPackageDialog import NewPythonPackageDialog
815 dlg = NewPythonPackageDialog(dn, self)
816 if dlg.exec() == QDialog.DialogCode.Accepted:
817 packageName = dlg.getData()
818 nameParts = packageName.split(".")
819 packagePath = self.project.ppath
820 packageFile = ""
821 for name in nameParts:
822 packagePath = os.path.join(packagePath, name)
823 if not os.path.exists(packagePath):
824 try:
825 os.mkdir(packagePath)
826 except OSError as err:
827 E5MessageBox.critical(
828 self,
829 self.tr("Add new Python package"),
830 self.tr(
831 """<p>The package directory <b>{0}</b> could"""
832 """ not be created. Aborting...</p>"""
833 """<p>Reason: {1}</p>""")
834 .format(packagePath, str(err)))
835 return
836 packageFile = os.path.join(packagePath, "__init__.py")
837 if not os.path.exists(packageFile):
838 try:
839 with open(packageFile, "w", encoding="utf-8"):
840 pass
841 except OSError as err:
842 E5MessageBox.critical(
843 self,
844 self.tr("Add new Python package"),
845 self.tr(
846 """<p>The package file <b>{0}</b> could"""
847 """ not be created. Aborting...</p>"""
848 """<p>Reason: {1}</p>""")
849 .format(packageFile, str(err)))
850 return
851 self.project.appendFile(packageFile)
852 if packageFile:
853 self.sourceFile[str].emit(packageFile)
854
855 def __addSourceFiles(self):
856 """
857 Private method to add a source file to the project.
858 """
859 itm = self.model().item(self.currentIndex())
860 if isinstance(
861 itm,
862 (ProjectBrowserFileItem, BrowserClassItem, BrowserMethodItem)
863 ):
864 dn = os.path.dirname(itm.fileName())
865 elif isinstance(
866 itm,
867 (ProjectBrowserSimpleDirectoryItem, ProjectBrowserDirectoryItem)
868 ):
869 dn = itm.dirName()
870 else:
871 dn = None
872 self.project.addFiles('source', dn)
873
874 def __addSourceDirectory(self):
875 """
876 Private method to add source files of a directory to the project.
877 """
878 itm = self.model().item(self.currentIndex())
879 if isinstance(
880 itm,
881 (ProjectBrowserFileItem, BrowserClassItem, BrowserMethodItem)
882 ):
883 dn = os.path.dirname(itm.fileName())
884 elif isinstance(
885 itm,
886 (ProjectBrowserSimpleDirectoryItem, ProjectBrowserDirectoryItem)
887 ):
888 dn = itm.dirName()
889 else:
890 dn = None
891 self.project.addDirectory('source', dn)
892
893 def __deleteFile(self):
894 """
895 Private method to delete files from the project.
896 """
897 itmList = self.getSelectedItems()
898
899 files = []
900 fullNames = []
901 for itm in itmList:
902 fn2 = itm.fileName()
903 fullNames.append(fn2)
904 fn = self.project.getRelativePath(fn2)
905 files.append(fn)
906
907 from UI.DeleteFilesConfirmationDialog import (
908 DeleteFilesConfirmationDialog
909 )
910 dlg = DeleteFilesConfirmationDialog(
911 self.parent(),
912 self.tr("Delete files"),
913 self.tr(
914 "Do you really want to delete these files from the project?"),
915 files)
916
917 if dlg.exec() == QDialog.DialogCode.Accepted:
918 for fn2, fn in zip(fullNames, files):
919 self.closeSourceWindow.emit(fn2)
920 self.project.deleteFile(fn)
921
922 ###########################################################################
923 ## Methods for the Checks submenu
924 ###########################################################################
925
926 def __showContextMenuCheck(self):
927 """
928 Private slot called before the checks menu is shown.
929 """
930 self.showMenu.emit("Checks", self.checksMenu)
931
932 ###########################################################################
933 ## Methods for the Show submenu
934 ###########################################################################
935
936 def __showCodeMetrics(self):
937 """
938 Private method to handle the code metrics context menu action.
939 """
940 itm = self.model().item(self.currentIndex())
941 fn = itm.fileName()
942
943 from DataViews.CodeMetricsDialog import CodeMetricsDialog
944 self.codemetrics = CodeMetricsDialog()
945 self.codemetrics.show()
946 self.codemetrics.start(fn)
947
948 def __showCodeCoverage(self):
949 """
950 Private method to handle the code coverage context menu action.
951 """
952 itm = self.model().item(self.currentIndex())
953 fn = itm.fileName()
954 pfn = self.project.getMainScript(True)
955
956 files = []
957
958 if pfn is not None:
959 tpfn = Utilities.getTestFileName(pfn)
960 basename = os.path.splitext(pfn)[0]
961 tbasename = os.path.splitext(tpfn)[0]
962
963 f = "{0}.coverage".format(basename)
964 tf = "{0}.coverage".format(tbasename)
965 if os.path.isfile(f):
966 files.append(f)
967 if os.path.isfile(tf):
968 files.append(tf)
969
970 if fn is not None:
971 tfn = Utilities.getTestFileName(fn)
972 basename = os.path.splitext(fn)[0]
973 tbasename = os.path.splitext(tfn)[0]
974
975 f = "{0}.coverage".format(basename)
976 tf = "{0}.coverage".format(tbasename)
977 if os.path.isfile(f) and f not in files:
978 files.append(f)
979 if os.path.isfile(tf) and tf not in files:
980 files.append(tf)
981
982 if files:
983 if len(files) > 1:
984 pfn, ok = QInputDialog.getItem(
985 None,
986 self.tr("Code Coverage"),
987 self.tr("Please select a coverage file"),
988 files,
989 0, False)
990 if not ok:
991 return
992 else:
993 pfn = files[0]
994 else:
995 return
996
997 from DataViews.PyCoverageDialog import PyCoverageDialog
998 self.codecoverage = PyCoverageDialog()
999 self.codecoverage.show()
1000 self.codecoverage.start(pfn, fn)
1001
1002 def __showProfileData(self):
1003 """
1004 Private method to handle the show profile data context menu action.
1005 """
1006 itm = self.model().item(self.currentIndex())
1007 fn = itm.fileName()
1008 pfn = self.project.getMainScript(True)
1009
1010 files = []
1011
1012 if pfn is not None:
1013 tpfn = Utilities.getTestFileName(pfn)
1014 basename = os.path.splitext(pfn)[0]
1015 tbasename = os.path.splitext(tpfn)[0]
1016
1017 f = "{0}.profile".format(basename)
1018 tf = "{0}.profile".format(tbasename)
1019 if os.path.isfile(f):
1020 files.append(f)
1021 if os.path.isfile(tf):
1022 files.append(tf)
1023
1024 if fn is not None:
1025 tfn = Utilities.getTestFileName(fn)
1026 basename = os.path.splitext(fn)[0]
1027 tbasename = os.path.splitext(tfn)[0]
1028
1029 f = "{0}.profile".format(basename)
1030 tf = "{0}.profile".format(tbasename)
1031 if os.path.isfile(f) and f not in files:
1032 files.append(f)
1033 if os.path.isfile(tf) and tf not in files:
1034 files.append(tf)
1035
1036 if files:
1037 if len(files) > 1:
1038 pfn, ok = QInputDialog.getItem(
1039 None,
1040 self.tr("Profile Data"),
1041 self.tr("Please select a profile file"),
1042 files,
1043 0, False)
1044 if not ok:
1045 return
1046 else:
1047 pfn = files[0]
1048 else:
1049 return
1050
1051 from DataViews.PyProfileDialog import PyProfileDialog
1052 self.profiledata = PyProfileDialog()
1053 self.profiledata.show()
1054 self.profiledata.start(pfn, fn)
1055
1056 ###########################################################################
1057 ## Methods for the Graphics submenu
1058 ###########################################################################
1059
1060 def __showContextMenuGraphics(self):
1061 """
1062 Private slot called before the checks menu is shown.
1063 """
1064 self.showMenu.emit("Graphics", self.graphicsMenu)
1065
1066 def __showClassDiagram(self):
1067 """
1068 Private method to handle the class diagram context menu action.
1069 """
1070 itm = self.model().item(self.currentIndex())
1071 try:
1072 fn = itm.fileName()
1073 except AttributeError:
1074 fn = itm.dirName()
1075 res = E5MessageBox.yesNo(
1076 self,
1077 self.tr("Class Diagram"),
1078 self.tr("""Include class attributes?"""),
1079 yesDefault=True)
1080
1081 from Graphics.UMLDialog import UMLDialog, UMLDialogType
1082 self.classDiagram = UMLDialog(
1083 UMLDialogType.CLASS_DIAGRAM, self.project, fn,
1084 self, noAttrs=not res)
1085 self.classDiagram.show()
1086
1087 def __showImportsDiagram(self):
1088 """
1089 Private method to handle the imports diagram context menu action.
1090 """
1091 itm = self.model().item(self.currentIndex())
1092 try:
1093 fn = itm.fileName()
1094 except AttributeError:
1095 fn = itm.dirName()
1096 package = fn if os.path.isdir(fn) else os.path.dirname(fn)
1097 res = E5MessageBox.yesNo(
1098 self,
1099 self.tr("Imports Diagram"),
1100 self.tr("""Include imports from external modules?"""))
1101
1102 from Graphics.UMLDialog import UMLDialog, UMLDialogType
1103 self.importsDiagram = UMLDialog(
1104 UMLDialogType.IMPORTS_DIAGRAM, self.project, package,
1105 self, showExternalImports=res)
1106 self.importsDiagram.show()
1107
1108 def __showPackageDiagram(self):
1109 """
1110 Private method to handle the package diagram context menu action.
1111 """
1112 itm = self.model().item(self.currentIndex())
1113 try:
1114 fn = itm.fileName()
1115 except AttributeError:
1116 fn = itm.dirName()
1117 package = fn if os.path.isdir(fn) else os.path.dirname(fn)
1118 res = E5MessageBox.yesNo(
1119 self,
1120 self.tr("Package Diagram"),
1121 self.tr("""Include class attributes?"""),
1122 yesDefault=True)
1123
1124 from Graphics.UMLDialog import UMLDialog, UMLDialogType
1125 self.packageDiagram = UMLDialog(
1126 UMLDialogType.PACKAGE_DIAGRAM, self.project, package,
1127 self, noAttrs=not res)
1128 self.packageDiagram.show()
1129
1130 def __showApplicationDiagram(self):
1131 """
1132 Private method to handle the application diagram context menu action.
1133 """
1134 res = E5MessageBox.yesNo(
1135 self,
1136 self.tr("Application Diagram"),
1137 self.tr("""Include module names?"""),
1138 yesDefault=True)
1139
1140 from Graphics.UMLDialog import UMLDialog, UMLDialogType
1141 self.applicationDiagram = UMLDialog(
1142 UMLDialogType.APPLICATION_DIAGRAM, self.project,
1143 self, noModules=not res)
1144 self.applicationDiagram.show()
1145
1146 def __loadDiagram(self):
1147 """
1148 Private slot to load a diagram from file.
1149 """
1150 from Graphics.UMLDialog import UMLDialog, UMLDialogType
1151 self.loadedDiagram = None
1152 loadedDiagram = UMLDialog(
1153 UMLDialogType.NO_DIAGRAM, self.project, parent=self)
1154 if loadedDiagram.load():
1155 self.loadedDiagram = loadedDiagram
1156 self.loadedDiagram.show(fromFile=True)

eric ide

mercurial