Project/ProjectProtocolsBrowser.py

changeset 5968
c5112b5cb7a9
parent 5726
e1dbd217214a
child 5969
584c21b6587a
equal deleted inserted replaced
5967:da72832f7c22 5968:c5112b5cb7a9
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2017 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the a class used to display the protocols (protobuf) part
8 of the project.
9 """
10
11 from __future__ import unicode_literals
12 try:
13 str = unicode
14 except NameError:
15 pass
16
17 import os
18 import glob
19
20 from PyQt5.QtCore import QThread, pyqtSignal, QProcess
21 from PyQt5.QtWidgets import QDialog, QApplication, QMenu
22
23 from E5Gui.E5Application import e5App
24 from E5Gui import E5MessageBox
25 from E5Gui.E5ProgressDialog import E5ProgressDialog
26
27 from .ProjectBrowserModel import ProjectBrowserFileItem, \
28 ProjectBrowserSimpleDirectoryItem, ProjectBrowserDirectoryItem, \
29 ProjectBrowserProtocolsType
30 from .ProjectBaseBrowser import ProjectBaseBrowser
31
32 from UI.BrowserModel import BrowserFileItem, BrowserClassItem, \
33 BrowserMethodItem, BrowserClassAttributeItem
34 import UI.PixmapCache
35
36 import Preferences
37 import Utilities
38
39
40 class ProjectProtocolsBrowser(ProjectBaseBrowser):
41 """
42 A class used to display the protocols (protobuf) part of the project.
43
44 @signal appendStdout(str) emitted after something was received from
45 a QProcess on stdout
46 @signal appendStderr(str) emitted after something was received from
47 a QProcess on stderr
48 @signal showMenu(str, QMenu) emitted when a menu is about to be shown.
49 The name of the menu and a reference to the menu are given.
50 """
51 appendStdout = pyqtSignal(str)
52 appendStderr = pyqtSignal(str)
53 showMenu = pyqtSignal(str, QMenu)
54
55 def __init__(self, project, parent=None):
56 """
57 Constructor
58
59 @param project reference to the project object
60 @type Project
61 @param parent parent widget of this browser
62 @type QWidget
63 """
64 self.__protoc = Preferences.getProtobuf("protoc")
65 if self.__protoc == "":
66 self.__protoc = Utilities.isWindowsPlatform() and \
67 "protoc.exe" or "protoc"
68 if not Utilities.isinpath(self.__protoc):
69 self.__protoc = None
70 self.omniidl = self.__protoc # TODO: remove this line
71
72 ProjectBaseBrowser.__init__(self, project,
73 ProjectBrowserProtocolsType, parent)
74
75 self.selectedItemsFilter = \
76 [ProjectBrowserFileItem, ProjectBrowserSimpleDirectoryItem]
77
78 self.setWindowTitle(self.tr('Protocols (protobuf)'))
79
80 self.setWhatsThis(self.tr(
81 """<b>Project Protocols Browser</b>"""
82 """<p>This allows to easily see all protocols (protobuf files)"""
83 """ contained in the current project. Several actions can be"""
84 """ executed via the context menu.</p>"""
85 ))
86
87 project.prepareRepopulateItem.connect(self._prepareRepopulateItem)
88 project.completeRepopulateItem.connect(self._completeRepopulateItem)
89
90 # TODO: continue from here
91 def _createPopupMenus(self):
92 """
93 Protected overloaded method to generate the popup menu.
94 """
95 self.menuActions = []
96 self.multiMenuActions = []
97 self.dirMenuActions = []
98 self.dirMultiMenuActions = []
99
100 self.sourceMenu = QMenu(self)
101 if self.omniidl is not None:
102 self.sourceMenu.addAction(
103 self.tr('Compile interface'), self.__compileInterface)
104 self.sourceMenu.addAction(
105 self.tr('Compile all interfaces'),
106 self.__compileAllInterfaces)
107 self.sourceMenu.addAction(self.tr('Open'), self._openItem)
108 self.sourceMenu.addSeparator()
109 act = self.sourceMenu.addAction(
110 self.tr('Rename file'), self._renameFile)
111 self.menuActions.append(act)
112 act = self.sourceMenu.addAction(
113 self.tr('Remove from project'), self._removeFile)
114 self.menuActions.append(act)
115 act = self.sourceMenu.addAction(
116 self.tr('Delete'), self.__deleteFile)
117 self.menuActions.append(act)
118 self.sourceMenu.addSeparator()
119 self.sourceMenu.addAction(
120 self.tr('Add interfaces...'), self.__addInterfaceFiles)
121 self.sourceMenu.addAction(
122 self.tr('Add interfaces directory...'),
123 self.__addInterfacesDirectory)
124 self.sourceMenu.addSeparator()
125 self.sourceMenu.addAction(
126 self.tr('Copy Path to Clipboard'), self._copyToClipboard)
127 self.sourceMenu.addSeparator()
128 self.sourceMenu.addAction(
129 self.tr('Expand all directories'), self._expandAllDirs)
130 self.sourceMenu.addAction(
131 self.tr('Collapse all directories'), self._collapseAllDirs)
132 self.sourceMenu.addSeparator()
133 self.sourceMenu.addAction(self.tr('Configure...'), self._configure)
134 self.sourceMenu.addAction(
135 self.tr('Configure CORBA...'), self.__configureCorba)
136
137 self.menu = QMenu(self)
138 if self.omniidl is not None:
139 self.menu.addAction(
140 self.tr('Compile interface'), self.__compileInterface)
141 self.menu.addAction(
142 self.tr('Compile all interfaces'),
143 self.__compileAllInterfaces)
144 self.menu.addAction(self.tr('Open'), self._openItem)
145 self.menu.addSeparator()
146 self.menu.addAction(
147 self.tr('Add interfaces...'), self.__addInterfaceFiles)
148 self.menu.addAction(
149 self.tr('Add interfaces directory...'),
150 self.__addInterfacesDirectory)
151 self.menu.addSeparator()
152 self.menu.addAction(
153 self.tr('Expand all directories'), self._expandAllDirs)
154 self.menu.addAction(
155 self.tr('Collapse all directories'), self._collapseAllDirs)
156 self.menu.addSeparator()
157 self.menu.addAction(self.tr('Configure...'), self._configure)
158 self.menu.addAction(
159 self.tr('Configure CORBA...'), self.__configureCorba)
160
161 self.backMenu = QMenu(self)
162 if self.omniidl is not None:
163 self.backMenu.addAction(
164 self.tr('Compile all interfaces'),
165 self.__compileAllInterfaces)
166 self.backMenu.addSeparator()
167 self.backMenu.addAction(
168 self.tr('Add interfaces...'), self.project.addIdlFiles)
169 self.backMenu.addAction(
170 self.tr('Add interfaces directory...'), self.project.addIdlDir)
171 self.backMenu.addSeparator()
172 self.backMenu.addAction(
173 self.tr('Expand all directories'), self._expandAllDirs)
174 self.backMenu.addAction(
175 self.tr('Collapse all directories'), self._collapseAllDirs)
176 self.backMenu.addSeparator()
177 self.backMenu.addAction(self.tr('Configure...'), self._configure)
178 self.backMenu.addAction(
179 self.tr('Configure CORBA...'), self.__configureCorba)
180 self.backMenu.setEnabled(False)
181
182 # create the menu for multiple selected files
183 self.multiMenu = QMenu(self)
184 if self.omniidl is not None:
185 self.multiMenu.addAction(
186 self.tr('Compile interfaces'),
187 self.__compileSelectedInterfaces)
188 self.multiMenu.addAction(self.tr('Open'), self._openItem)
189 self.multiMenu.addSeparator()
190 act = self.multiMenu.addAction(
191 self.tr('Remove from project'), self._removeFile)
192 self.multiMenuActions.append(act)
193 act = self.multiMenu.addAction(
194 self.tr('Delete'), self.__deleteFile)
195 self.multiMenuActions.append(act)
196 self.multiMenu.addSeparator()
197 self.multiMenu.addAction(
198 self.tr('Expand all directories'), self._expandAllDirs)
199 self.multiMenu.addAction(
200 self.tr('Collapse all directories'), self._collapseAllDirs)
201 self.multiMenu.addSeparator()
202 self.multiMenu.addAction(self.tr('Configure...'), self._configure)
203 self.multiMenu.addAction(
204 self.tr('Configure CORBA...'), self.__configureCorba)
205
206 self.dirMenu = QMenu(self)
207 if self.omniidl is not None:
208 self.dirMenu.addAction(
209 self.tr('Compile all interfaces'),
210 self.__compileAllInterfaces)
211 self.dirMenu.addSeparator()
212 act = self.dirMenu.addAction(
213 self.tr('Remove from project'), self._removeFile)
214 self.dirMenuActions.append(act)
215 act = self.dirMenu.addAction(
216 self.tr('Delete'), self._deleteDirectory)
217 self.dirMenuActions.append(act)
218 self.dirMenu.addSeparator()
219 self.dirMenu.addAction(
220 self.tr('Add interfaces...'), self.__addInterfaceFiles)
221 self.dirMenu.addAction(
222 self.tr('Add interfaces directory...'),
223 self.__addInterfacesDirectory)
224 self.dirMenu.addSeparator()
225 self.dirMenu.addAction(
226 self.tr('Copy Path to Clipboard'), self._copyToClipboard)
227 self.dirMenu.addSeparator()
228 self.dirMenu.addAction(
229 self.tr('Expand all directories'), self._expandAllDirs)
230 self.dirMenu.addAction(
231 self.tr('Collapse all directories'), self._collapseAllDirs)
232 self.dirMenu.addSeparator()
233 self.dirMenu.addAction(self.tr('Configure...'), self._configure)
234 self.dirMenu.addAction(
235 self.tr('Configure CORBA...'), self.__configureCorba)
236
237 self.dirMultiMenu = QMenu(self)
238 if self.omniidl is not None:
239 self.dirMultiMenu.addAction(
240 self.tr('Compile all interfaces'),
241 self.__compileAllInterfaces)
242 self.dirMultiMenu.addSeparator()
243 self.dirMultiMenu.addAction(
244 self.tr('Add interfaces...'), self.project.addIdlFiles)
245 self.dirMultiMenu.addAction(
246 self.tr('Add interfaces directory...'), self.project.addIdlDir)
247 self.dirMultiMenu.addSeparator()
248 self.dirMultiMenu.addAction(
249 self.tr('Expand all directories'), self._expandAllDirs)
250 self.dirMultiMenu.addAction(
251 self.tr('Collapse all directories'), self._collapseAllDirs)
252 self.dirMultiMenu.addSeparator()
253 self.dirMultiMenu.addAction(
254 self.tr('Configure...'), self._configure)
255 self.dirMultiMenu.addAction(self.tr('Configure CORBA...'),
256 self.__configureCorba)
257
258 self.sourceMenu.aboutToShow.connect(self.__showContextMenu)
259 self.multiMenu.aboutToShow.connect(self.__showContextMenuMulti)
260 self.dirMenu.aboutToShow.connect(self.__showContextMenuDir)
261 self.dirMultiMenu.aboutToShow.connect(self.__showContextMenuDirMulti)
262 self.backMenu.aboutToShow.connect(self.__showContextMenuBack)
263 self.mainMenu = self.sourceMenu
264
265 def _contextMenuRequested(self, coord):
266 """
267 Protected slot to show the context menu.
268
269 @param coord the position of the mouse pointer (QPoint)
270 """
271 if not self.project.isOpen():
272 return
273
274 try:
275 categories = self.getSelectedItemsCountCategorized(
276 [ProjectBrowserFileItem, BrowserClassItem,
277 BrowserMethodItem, ProjectBrowserSimpleDirectoryItem])
278 cnt = categories["sum"]
279 if cnt <= 1:
280 index = self.indexAt(coord)
281 if index.isValid():
282 self._selectSingleItem(index)
283 categories = self.getSelectedItemsCountCategorized(
284 [ProjectBrowserFileItem, BrowserClassItem,
285 BrowserMethodItem, ProjectBrowserSimpleDirectoryItem])
286 cnt = categories["sum"]
287
288 bfcnt = categories[str(ProjectBrowserFileItem)]
289 cmcnt = categories[str(BrowserClassItem)] + \
290 categories[str(BrowserMethodItem)]
291 sdcnt = categories[str(ProjectBrowserSimpleDirectoryItem)]
292 if cnt > 1 and cnt == bfcnt:
293 self.multiMenu.popup(self.mapToGlobal(coord))
294 elif cnt > 1 and cnt == sdcnt:
295 self.dirMultiMenu.popup(self.mapToGlobal(coord))
296 else:
297 index = self.indexAt(coord)
298 if cnt == 1 and index.isValid():
299 if bfcnt == 1 or cmcnt == 1:
300 itm = self.model().item(index)
301 if isinstance(itm, ProjectBrowserFileItem):
302 self.sourceMenu.popup(self.mapToGlobal(coord))
303 elif isinstance(itm, BrowserClassItem) or \
304 isinstance(itm, BrowserMethodItem):
305 self.menu.popup(self.mapToGlobal(coord))
306 else:
307 self.backMenu.popup(self.mapToGlobal(coord))
308 elif sdcnt == 1:
309 self.dirMenu.popup(self.mapToGlobal(coord))
310 else:
311 self.backMenu.popup(self.mapToGlobal(coord))
312 else:
313 self.backMenu.popup(self.mapToGlobal(coord))
314 except Exception:
315 pass
316
317 def __showContextMenu(self):
318 """
319 Private slot called by the menu aboutToShow signal.
320 """
321 ProjectBaseBrowser._showContextMenu(self, self.menu)
322
323 self.showMenu.emit("Main", self.menu)
324
325 def __showContextMenuMulti(self):
326 """
327 Private slot called by the multiMenu aboutToShow signal.
328 """
329 ProjectBaseBrowser._showContextMenuMulti(self, self.multiMenu)
330
331 self.showMenu.emit("MainMulti", self.multiMenu)
332
333 def __showContextMenuDir(self):
334 """
335 Private slot called by the dirMenu aboutToShow signal.
336 """
337 ProjectBaseBrowser._showContextMenuDir(self, self.dirMenu)
338
339 self.showMenu.emit("MainDir", self.dirMenu)
340
341 def __showContextMenuDirMulti(self):
342 """
343 Private slot called by the dirMultiMenu aboutToShow signal.
344 """
345 ProjectBaseBrowser._showContextMenuDirMulti(self, self.dirMultiMenu)
346
347 self.showMenu.emit("MainDirMulti", self.dirMultiMenu)
348
349 def __showContextMenuBack(self):
350 """
351 Private slot called by the backMenu aboutToShow signal.
352 """
353 ProjectBaseBrowser._showContextMenuBack(self, self.backMenu)
354
355 self.showMenu.emit("MainBack", self.backMenu)
356
357 def _openItem(self):
358 """
359 Protected slot to handle the open popup menu entry.
360 """
361 itmList = self.getSelectedItems(
362 [BrowserFileItem, BrowserClassItem, BrowserMethodItem,
363 BrowserClassAttributeItem])
364
365 for itm in itmList:
366 if isinstance(itm, BrowserFileItem):
367 self.sourceFile[str].emit(itm.fileName())
368 elif isinstance(itm, BrowserClassItem):
369 self.sourceFile[str, int].emit(
370 itm.fileName(), itm.classObject().lineno)
371 elif isinstance(itm, BrowserMethodItem):
372 self.sourceFile[str, int].emit(
373 itm.fileName(), itm.functionObject().lineno)
374 elif isinstance(itm, BrowserClassAttributeItem):
375 self.sourceFile[str, int].emit(
376 itm.fileName(), itm.attributeObject().lineno)
377
378 def __addInterfaceFiles(self):
379 """
380 Private method to add interface files to the project.
381 """
382 itm = self.model().item(self.currentIndex())
383 if isinstance(itm, ProjectBrowserFileItem) or \
384 isinstance(itm, BrowserClassItem) or \
385 isinstance(itm, BrowserMethodItem):
386 dn = os.path.dirname(itm.fileName())
387 elif isinstance(itm, ProjectBrowserSimpleDirectoryItem) or \
388 isinstance(itm, ProjectBrowserDirectoryItem):
389 dn = itm.dirName()
390 else:
391 dn = None
392 self.project.addFiles('interface', dn)
393
394 def __addInterfacesDirectory(self):
395 """
396 Private method to add interface files of a directory to the project.
397 """
398 itm = self.model().item(self.currentIndex())
399 if isinstance(itm, ProjectBrowserFileItem) or \
400 isinstance(itm, BrowserClassItem) or \
401 isinstance(itm, BrowserMethodItem):
402 dn = os.path.dirname(itm.fileName())
403 elif isinstance(itm, ProjectBrowserSimpleDirectoryItem) or \
404 isinstance(itm, ProjectBrowserDirectoryItem):
405 dn = itm.dirName()
406 else:
407 dn = None
408 self.project.addDirectory('interface', dn)
409
410 def __deleteFile(self):
411 """
412 Private method to delete files from the project.
413 """
414 itmList = self.getSelectedItems()
415
416 files = []
417 fullNames = []
418 for itm in itmList:
419 fn2 = itm.fileName()
420 fullNames.append(fn2)
421 fn = self.project.getRelativePath(fn2)
422 files.append(fn)
423
424 from UI.DeleteFilesConfirmationDialog import \
425 DeleteFilesConfirmationDialog
426 dlg = DeleteFilesConfirmationDialog(
427 self.parent(),
428 self.tr("Delete interfaces"),
429 self.tr("Do you really want to delete these interfaces from"
430 " the project?"),
431 files)
432
433 if dlg.exec_() == QDialog.Accepted:
434 for fn2, fn in zip(fullNames, files):
435 self.closeSourceWindow.emit(fn2)
436 self.project.deleteFile(fn)
437
438 ###########################################################################
439 ## Methods to handle the various compile commands
440 ###########################################################################
441
442 def __readStdout(self):
443 """
444 Private slot to handle the readyReadStandardOutput signal of the
445 omniidl process.
446 """
447 if self.compileProc is None:
448 return
449
450 ioEncoding = Preferences.getSystem("IOEncoding")
451
452 self.compileProc.setReadChannel(QProcess.StandardOutput)
453 while self.compileProc and self.compileProc.canReadLine():
454 s = 'omniidl: '
455 output = str(self.compileProc.readLine(), ioEncoding, 'replace')
456 s += output
457 self.appendStdout.emit(s)
458
459 def __readStderr(self):
460 """
461 Private slot to handle the readyReadStandardError signal of the
462 omniidl process.
463 """
464 if self.compileProc is None:
465 return
466
467 ioEncoding = Preferences.getSystem("IOEncoding")
468
469 self.compileProc.setReadChannel(QProcess.StandardError)
470 while self.compileProc and self.compileProc.canReadLine():
471 s = 'omniidl: '
472 error = str(self.compileProc.readLine(), ioEncoding, 'replace')
473 s += error
474 self.appendStderr.emit(s)
475
476 def __compileIDLDone(self, exitCode, exitStatus):
477 """
478 Private slot to handle the finished signal of the omniidl process.
479
480 @param exitCode exit code of the process (integer)
481 @param exitStatus exit status of the process (QProcess.ExitStatus)
482 """
483 self.compileRunning = False
484 ui = e5App().getObject("UserInterface")
485 if exitStatus == QProcess.NormalExit and exitCode == 0:
486 path = os.path.dirname(self.idlFile)
487 poaList = glob.glob(os.path.join(path, "*__POA"))
488 npoaList = [f.replace("__POA", "") for f in poaList]
489 fileList = glob.glob(os.path.join(path, "*_idl.py"))
490 for directory in poaList + npoaList:
491 fileList += Utilities.direntries(directory, True, "*.py")
492 for file in fileList:
493 self.project.appendFile(file)
494 if not self.noDialog and not ui.notificationsEnabled():
495 E5MessageBox.information(
496 self,
497 self.tr("Interface Compilation"),
498 self.tr(
499 "The compilation of the interface file was"
500 " successful."))
501 else:
502 ui.showNotification(
503 UI.PixmapCache.getPixmap("corba48.png"),
504 self.tr("Interface Compilation"),
505 self.tr(
506 "The compilation of the interface file was"
507 " successful."))
508 else:
509 if not self.noDialog:
510 E5MessageBox.information(
511 self,
512 self.tr("Interface Compilation"),
513 self.tr(
514 "The compilation of the interface file failed."))
515 else:
516 ui.showNotification(
517 UI.PixmapCache.getPixmap("corba48.png"),
518 self.tr("Interface Compilation"),
519 self.tr(
520 "The compilation of the interface file failed."))
521 self.compileProc = None
522
523 def __compileIDL(self, fn, noDialog=False, progress=None):
524 """
525 Private method to compile a .idl file to python.
526
527 @param fn filename of the .idl file to be compiled (string)
528 @param noDialog flag indicating silent operations (boolean)
529 @param progress reference to the progress dialog (E5ProgressDialog)
530 @return reference to the compile process (QProcess)
531 """
532 self.compileProc = QProcess()
533 args = []
534
535 args.append("-bpython")
536 args.append("-I.")
537
538 fn = os.path.join(self.project.ppath, fn)
539 self.idlFile = fn
540 args.append("-C{0}".format(os.path.dirname(fn)))
541 args.append(fn)
542
543 self.compileProc.finished.connect(self.__compileIDLDone)
544 self.compileProc.readyReadStandardOutput.connect(self.__readStdout)
545 self.compileProc.readyReadStandardError.connect(self.__readStderr)
546
547 self.noDialog = noDialog
548 self.compileProc.start(self.omniidl, args)
549 procStarted = self.compileProc.waitForStarted(5000)
550 if procStarted:
551 self.compileRunning = True
552 return self.compileProc
553 else:
554 self.compileRunning = False
555 if progress is not None:
556 progress.cancel()
557 E5MessageBox.critical(
558 self,
559 self.tr('Process Generation Error'),
560 self.tr(
561 '<p>Could not start {0}.<br>'
562 'Ensure that it is in the search path.</p>'
563 ).format(self.omniidl))
564 return None
565
566 def __compileInterface(self):
567 """
568 Private method to compile an interface to python.
569 """
570 if self.omniidl is not None:
571 itm = self.model().item(self.currentIndex())
572 fn2 = itm.fileName()
573 fn = self.project.getRelativePath(fn2)
574 self.__compileIDL(fn)
575
576 def __compileAllInterfaces(self):
577 """
578 Private method to compile all interfaces to python.
579 """
580 if self.omniidl is not None:
581 numIDLs = len(self.project.pdata["INTERFACES"])
582 progress = E5ProgressDialog(
583 self.tr("Compiling interfaces..."),
584 self.tr("Abort"), 0, numIDLs,
585 self.tr("%v/%m Interfaces"), self)
586 progress.setModal(True)
587 progress.setMinimumDuration(0)
588 progress.setWindowTitle(self.tr("Interfaces"))
589 i = 0
590
591 for fn in self.project.pdata["INTERFACES"]:
592 progress.setValue(i)
593 if progress.wasCanceled():
594 break
595 proc = self.__compileIDL(fn, True, progress)
596 if proc is not None:
597 while proc.state() == QProcess.Running:
598 QApplication.processEvents()
599 QThread.msleep(300)
600 QApplication.processEvents()
601 else:
602 break
603 i += 1
604
605 progress.setValue(numIDLs)
606
607 def __compileSelectedInterfaces(self):
608 """
609 Private method to compile selected interfaces to python.
610 """
611 if self.omniidl is not None:
612 items = self.getSelectedItems()
613
614 files = [self.project.getRelativePath(itm.fileName())
615 for itm in items]
616 numIDLs = len(files)
617 progress = E5ProgressDialog(
618 self.tr("Compiling interfaces..."),
619 self.tr("Abort"), 0, numIDLs,
620 self.tr("%v/%m Interfaces"), self)
621 progress.setModal(True)
622 progress.setMinimumDuration(0)
623 progress.setWindowTitle(self.tr("Interfaces"))
624 i = 0
625
626 for fn in files:
627 progress.setValue(i)
628 if progress.wasCanceled():
629 break
630 proc = self.__compileIDL(fn, True, progress)
631 if proc is not None:
632 while proc.state() == QProcess.Running:
633 QApplication.processEvents()
634 QThread.msleep(300)
635 QApplication.processEvents()
636 else:
637 break
638 i += 1
639
640 progress.setValue(numIDLs)
641
642 def __configureCorba(self):
643 """
644 Private method to open the configuration dialog.
645 """
646 e5App().getObject("UserInterface").showPreferences("corbaPage")

eric ide

mercurial