src/eric7/Project/ProjectSourcesBrowser.py

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

eric ide

mercurial