eric6/Project/ProjectResourcesBrowser.py

changeset 6942
2602857055c5
parent 6645
ad476851d7e0
child 7192
a22eee00b052
equal deleted inserted replaced
6941:f99d60d6b59b 6942:2602857055c5
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2002 - 2019 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a class used to display the resources part of the project.
8 """
9
10 from __future__ import unicode_literals
11 try:
12 str = unicode
13 except NameError:
14 pass
15
16 import os
17
18 from PyQt5.QtCore import QThread, QFileInfo, pyqtSignal, PYQT_VERSION, QProcess
19 from PyQt5.QtWidgets import QDialog, QApplication, QMenu
20
21 from E5Gui.E5Application import e5App
22 from E5Gui import E5MessageBox, E5FileDialog
23 from E5Gui.E5ProgressDialog import E5ProgressDialog
24
25 from .ProjectBrowserModel import ProjectBrowserFileItem, \
26 ProjectBrowserSimpleDirectoryItem, ProjectBrowserDirectoryItem, \
27 ProjectBrowserResourceType
28 from .ProjectBaseBrowser import ProjectBaseBrowser
29
30 import UI.PixmapCache
31
32 import Preferences
33 import Utilities
34
35
36 class ProjectResourcesBrowser(ProjectBaseBrowser):
37 """
38 A class used to display the resources part of the project.
39
40 @signal appendStderr(str) emitted after something was received from
41 a QProcess on stderr
42 @signal showMenu(str, QMenu) emitted when a menu is about to be shown.
43 The name of the menu and a reference to the menu are given.
44 """
45 appendStderr = pyqtSignal(str)
46 showMenu = pyqtSignal(str, QMenu)
47
48 RCFilenameFormatPython = "{0}_rc.py"
49 RCFilenameFormatRuby = "{0}_rc.rb"
50
51 def __init__(self, project, parent=None):
52 """
53 Constructor
54
55 @param project reference to the project object
56 @param parent parent widget of this browser (QWidget)
57 """
58 ProjectBaseBrowser.__init__(self, project, ProjectBrowserResourceType,
59 parent)
60
61 self.selectedItemsFilter = \
62 [ProjectBrowserFileItem, ProjectBrowserSimpleDirectoryItem]
63
64 self.setWindowTitle(self.tr('Resources'))
65
66 self.setWhatsThis(self.tr(
67 """<b>Project Resources Browser</b>"""
68 """<p>This allows to easily see all resources contained in the"""
69 """ current project. Several actions can be executed via the"""
70 """ context menu.</p>"""
71 ))
72
73 self.compileProc = None
74
75 def _createPopupMenus(self):
76 """
77 Protected overloaded method to generate the popup menu.
78 """
79 self.menuActions = []
80 self.multiMenuActions = []
81 self.dirMenuActions = []
82 self.dirMultiMenuActions = []
83
84 self.menu = QMenu(self)
85 if self.project.getProjectType() in \
86 ["Qt4", "Qt4C", "PyQt5", "PyQt5C", "E6Plugin",
87 "PySide", "PySideC", "PySide2", "PySideC2"]:
88 self.menu.addAction(
89 self.tr('Compile resource'),
90 self.__compileResource)
91 self.menu.addAction(
92 self.tr('Compile all resources'),
93 self.__compileAllResources)
94 self.menu.addSeparator()
95 self.menu.addAction(
96 self.tr('Configure rcc Compiler'),
97 self.__configureRccCompiler)
98 self.menu.addSeparator()
99 else:
100 if self.hooks["compileResource"] is not None:
101 self.menu.addAction(
102 self.hooksMenuEntries.get(
103 "compileResource",
104 self.tr('Compile resource')),
105 self.__compileResource)
106 if self.hooks["compileAllResources"] is not None:
107 self.menu.addAction(
108 self.hooksMenuEntries.get(
109 "compileAllResources",
110 self.tr('Compile all resources')),
111 self.__compileAllResources)
112 if self.hooks["compileResource"] is not None or \
113 self.hooks["compileAllResources"] is not None:
114 self.menu.addSeparator()
115 self.menu.addAction(self.tr('Open'), self.__openFile)
116 self.menu.addSeparator()
117 act = self.menu.addAction(self.tr('Rename file'), self._renameFile)
118 self.menuActions.append(act)
119 act = self.menu.addAction(
120 self.tr('Remove from project'), self._removeFile)
121 self.menuActions.append(act)
122 act = self.menu.addAction(self.tr('Delete'), self.__deleteFile)
123 self.menuActions.append(act)
124 self.menu.addSeparator()
125 if self.project.getProjectType() in \
126 ["Qt4", "Qt4C", "PyQt5", "PyQt5C", "E6Plugin",
127 "PySide", "PySideC", "PySide2", "PySideC2"]:
128 self.menu.addAction(
129 self.tr('New resource...'), self.__newResource)
130 else:
131 if self.hooks["newResource"] is not None:
132 self.menu.addAction(
133 self.hooksMenuEntries.get(
134 "newResource",
135 self.tr('New resource...')), self.__newResource)
136 self.menu.addAction(
137 self.tr('Add resources...'), self.__addResourceFiles)
138 self.menu.addAction(
139 self.tr('Add resources directory...'),
140 self.__addResourcesDirectory)
141 self.menu.addSeparator()
142 self.menu.addAction(
143 self.tr('Copy Path to Clipboard'), self._copyToClipboard)
144 self.menu.addSeparator()
145 self.menu.addAction(
146 self.tr('Expand all directories'), self._expandAllDirs)
147 self.menu.addAction(
148 self.tr('Collapse all directories'), self._collapseAllDirs)
149 self.menu.addSeparator()
150 self.menu.addAction(self.tr('Configure...'), self._configure)
151
152 self.backMenu = QMenu(self)
153 if self.project.getProjectType() in \
154 ["Qt4", "Qt4C", "PyQt5", "PyQt5C", "E6Plugin",
155 "PySide", "PySideC", "PySide2", "PySideC2"]:
156 self.backMenu.addAction(
157 self.tr('Compile all resources'),
158 self.__compileAllResources)
159 self.backMenu.addSeparator()
160 self.backMenu.addAction(
161 self.tr('Configure rcc Compiler'),
162 self.__configureRccCompiler)
163 self.backMenu.addSeparator()
164 self.backMenu.addAction(
165 self.tr('New resource...'), self.__newResource)
166 else:
167 if self.hooks["compileAllResources"] is not None:
168 self.backMenu.addAction(
169 self.hooksMenuEntries.get(
170 "compileAllResources",
171 self.tr('Compile all resources')),
172 self.__compileAllResources)
173 self.backMenu.addSeparator()
174 if self.hooks["newResource"] is not None:
175 self.backMenu.addAction(
176 self.hooksMenuEntries.get(
177 "newResource",
178 self.tr('New resource...')), self.__newResource)
179 self.backMenu.addAction(
180 self.tr('Add resources...'), self.project.addResourceFiles)
181 self.backMenu.addAction(
182 self.tr('Add resources directory...'),
183 self.project.addResourceDir)
184 self.backMenu.addSeparator()
185 self.backMenu.addAction(
186 self.tr('Expand all directories'), self._expandAllDirs)
187 self.backMenu.addAction(
188 self.tr('Collapse all directories'), self._collapseAllDirs)
189 self.backMenu.addSeparator()
190 self.backMenu.addAction(self.tr('Configure...'), self._configure)
191 self.backMenu.setEnabled(False)
192
193 # create the menu for multiple selected files
194 self.multiMenu = QMenu(self)
195 if self.project.getProjectType() in \
196 ["Qt4", "Qt4C", "PyQt5", "PyQt5C", "E6Plugin",
197 "PySide", "PySideC", "PySide2", "PySideC2"]:
198 act = self.multiMenu.addAction(
199 self.tr('Compile resources'),
200 self.__compileSelectedResources)
201 self.multiMenu.addSeparator()
202 self.multiMenu.addAction(
203 self.tr('Configure rcc Compiler'),
204 self.__configureRccCompiler)
205 self.multiMenu.addSeparator()
206 else:
207 if self.hooks["compileSelectedResources"] is not None:
208 act = self.multiMenu.addAction(
209 self.hooksMenuEntries.get(
210 "compileSelectedResources",
211 self.tr('Compile resources')),
212 self.__compileSelectedResources)
213 self.multiMenu.addSeparator()
214 self.multiMenu.addAction(self.tr('Open'), self.__openFile)
215 self.multiMenu.addSeparator()
216 act = self.multiMenu.addAction(
217 self.tr('Remove from project'), self._removeFile)
218 self.multiMenuActions.append(act)
219 act = self.multiMenu.addAction(
220 self.tr('Delete'), self.__deleteFile)
221 self.multiMenuActions.append(act)
222 self.multiMenu.addSeparator()
223 self.multiMenu.addAction(
224 self.tr('Expand all directories'), self._expandAllDirs)
225 self.multiMenu.addAction(
226 self.tr('Collapse all directories'), self._collapseAllDirs)
227 self.multiMenu.addSeparator()
228 self.multiMenu.addAction(self.tr('Configure...'), self._configure)
229
230 self.dirMenu = QMenu(self)
231 if self.project.getProjectType() in \
232 ["Qt4", "Qt4C", "PyQt5", "PyQt5C", "E6Plugin",
233 "PySide", "PySideC", "PySide2", "PySideC2"]:
234 self.dirMenu.addAction(
235 self.tr('Compile all resources'),
236 self.__compileAllResources)
237 self.dirMenu.addSeparator()
238 self.dirMenu.addAction(
239 self.tr('Configure rcc Compiler'),
240 self.__configureRccCompiler)
241 self.dirMenu.addSeparator()
242 else:
243 if self.hooks["compileAllResources"] is not None:
244 self.dirMenu.addAction(
245 self.hooksMenuEntries.get(
246 "compileAllResources",
247 self.tr('Compile all resources')),
248 self.__compileAllResources)
249 self.dirMenu.addSeparator()
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 resource...'), self.__newResource)
259 self.dirMenu.addAction(
260 self.tr('Add resources...'), self.__addResourceFiles)
261 self.dirMenu.addAction(
262 self.tr('Add resources directory...'),
263 self.__addResourcesDirectory)
264 self.dirMenu.addSeparator()
265 self.dirMenu.addAction(
266 self.tr('Copy Path to Clipboard'), self._copyToClipboard)
267 self.dirMenu.addSeparator()
268 self.dirMenu.addAction(
269 self.tr('Expand all directories'), self._expandAllDirs)
270 self.dirMenu.addAction(
271 self.tr('Collapse all directories'), self._collapseAllDirs)
272 self.dirMenu.addSeparator()
273 self.dirMenu.addAction(self.tr('Configure...'), self._configure)
274
275 self.dirMultiMenu = QMenu(self)
276 if self.project.getProjectType() in \
277 ["Qt4", "Qt4C", "PyQt5", "PyQt5C", "E6Plugin",
278 "PySide", "PySideC", "PySide2", "PySideC2"]:
279 self.dirMultiMenu.addAction(
280 self.tr('Compile all resources'),
281 self.__compileAllResources)
282 self.dirMultiMenu.addSeparator()
283 self.dirMultiMenu.addAction(
284 self.tr('Configure rcc Compiler'),
285 self.__configureRccCompiler)
286 self.dirMultiMenu.addSeparator()
287 else:
288 if self.hooks["compileAllResources"] is not None:
289 self.dirMultiMenu.addAction(
290 self.hooksMenuEntries.get(
291 "compileAllResources",
292 self.tr('Compile all resources')),
293 self.__compileAllResources)
294 self.dirMultiMenu.addSeparator()
295 self.dirMultiMenu.addAction(
296 self.tr('Add resources...'),
297 self.project.addResourceFiles)
298 self.dirMultiMenu.addAction(
299 self.tr('Add resources directory...'),
300 self.project.addResourceDir)
301 self.dirMultiMenu.addSeparator()
302 self.dirMultiMenu.addAction(
303 self.tr('Expand all directories'), self._expandAllDirs)
304 self.dirMultiMenu.addAction(
305 self.tr('Collapse all directories'), self._collapseAllDirs)
306 self.dirMultiMenu.addSeparator()
307 self.dirMultiMenu.addAction(
308 self.tr('Configure...'), self._configure)
309
310 self.menu.aboutToShow.connect(self.__showContextMenu)
311 self.multiMenu.aboutToShow.connect(self.__showContextMenuMulti)
312 self.dirMenu.aboutToShow.connect(self.__showContextMenuDir)
313 self.dirMultiMenu.aboutToShow.connect(self.__showContextMenuDirMulti)
314 self.backMenu.aboutToShow.connect(self.__showContextMenuBack)
315 self.mainMenu = self.menu
316
317 def _contextMenuRequested(self, coord):
318 """
319 Protected slot to show the context menu.
320
321 @param coord the position of the mouse pointer (QPoint)
322 """
323 if not self.project.isOpen():
324 return
325
326 try:
327 categories = self.getSelectedItemsCountCategorized(
328 [ProjectBrowserFileItem, ProjectBrowserSimpleDirectoryItem])
329 cnt = categories["sum"]
330 if cnt <= 1:
331 index = self.indexAt(coord)
332 if index.isValid():
333 self._selectSingleItem(index)
334 categories = self.getSelectedItemsCountCategorized(
335 [ProjectBrowserFileItem,
336 ProjectBrowserSimpleDirectoryItem])
337 cnt = categories["sum"]
338
339 bfcnt = categories[str(ProjectBrowserFileItem)]
340 sdcnt = categories[str(ProjectBrowserSimpleDirectoryItem)]
341 if cnt > 1 and cnt == bfcnt:
342 self.multiMenu.popup(self.mapToGlobal(coord))
343 elif cnt > 1 and cnt == sdcnt:
344 self.dirMultiMenu.popup(self.mapToGlobal(coord))
345 else:
346 index = self.indexAt(coord)
347 if cnt == 1 and index.isValid():
348 if bfcnt == 1:
349 self.menu.popup(self.mapToGlobal(coord))
350 elif sdcnt == 1:
351 self.dirMenu.popup(self.mapToGlobal(coord))
352 else:
353 self.backMenu.popup(self.mapToGlobal(coord))
354 else:
355 self.backMenu.popup(self.mapToGlobal(coord))
356 except Exception:
357 pass
358
359 def __showContextMenu(self):
360 """
361 Private slot called by the menu aboutToShow signal.
362 """
363 ProjectBaseBrowser._showContextMenu(self, self.menu)
364
365 self.showMenu.emit("Main", self.menu)
366
367 def __showContextMenuMulti(self):
368 """
369 Private slot called by the multiMenu aboutToShow signal.
370 """
371 ProjectBaseBrowser._showContextMenuMulti(self, self.multiMenu)
372
373 self.showMenu.emit("MainMulti", self.multiMenu)
374
375 def __showContextMenuDir(self):
376 """
377 Private slot called by the dirMenu aboutToShow signal.
378 """
379 ProjectBaseBrowser._showContextMenuDir(self, self.dirMenu)
380
381 self.showMenu.emit("MainDir", self.dirMenu)
382
383 def __showContextMenuDirMulti(self):
384 """
385 Private slot called by the dirMultiMenu aboutToShow signal.
386 """
387 ProjectBaseBrowser._showContextMenuDirMulti(self, self.dirMultiMenu)
388
389 self.showMenu.emit("MainDirMulti", self.dirMultiMenu)
390
391 def __showContextMenuBack(self):
392 """
393 Private slot called by the backMenu aboutToShow signal.
394 """
395 ProjectBaseBrowser._showContextMenuBack(self, self.backMenu)
396
397 self.showMenu.emit("MainBack", self.backMenu)
398
399 def __addResourceFiles(self):
400 """
401 Private method to add resource files to the project.
402 """
403 itm = self.model().item(self.currentIndex())
404 if isinstance(itm, ProjectBrowserFileItem):
405 dn = os.path.dirname(itm.fileName())
406 elif isinstance(itm, ProjectBrowserSimpleDirectoryItem) or \
407 isinstance(itm, ProjectBrowserDirectoryItem):
408 dn = itm.dirName()
409 else:
410 dn = None
411 self.project.addFiles('resource', dn)
412
413 def __addResourcesDirectory(self):
414 """
415 Private method to add resource files of a directory to the project.
416 """
417 itm = self.model().item(self.currentIndex())
418 if isinstance(itm, ProjectBrowserFileItem):
419 dn = os.path.dirname(itm.fileName())
420 elif isinstance(itm, ProjectBrowserSimpleDirectoryItem) or \
421 isinstance(itm, ProjectBrowserDirectoryItem):
422 dn = itm.dirName()
423 else:
424 dn = None
425 self.project.addDirectory('resource', dn)
426
427 def _openItem(self):
428 """
429 Protected slot to handle the open popup menu entry.
430 """
431 self.__openFile()
432
433 def __openFile(self):
434 """
435 Private slot to handle the Open menu action.
436 """
437 itmList = self.getSelectedItems()
438 for itm in itmList[:]:
439 if isinstance(itm, ProjectBrowserFileItem):
440 self.sourceFile.emit(itm.fileName())
441
442 def __newResource(self):
443 """
444 Private slot to handle the New Resource menu action.
445 """
446 itm = self.model().item(self.currentIndex())
447 if itm is None:
448 path = self.project.ppath
449 else:
450 try:
451 path = os.path.dirname(itm.fileName())
452 except AttributeError:
453 try:
454 path = itm.dirName()
455 except AttributeError:
456 path = os.path.join(self.project.ppath, itm.data(0))
457
458 if self.hooks["newResource"] is not None:
459 self.hooks["newResource"](path)
460 else:
461 fname, selectedFilter = E5FileDialog.getSaveFileNameAndFilter(
462 self,
463 self.tr("New Resource"),
464 path,
465 self.tr("Qt Resource Files (*.qrc)"),
466 "",
467 E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite))
468
469 if not fname:
470 # user aborted or didn't enter a filename
471 return
472
473 ext = QFileInfo(fname).suffix()
474 if not ext:
475 ex = selectedFilter.split("(*")[1].split(")")[0]
476 if ex:
477 fname += ex
478
479 if os.path.exists(fname):
480 res = E5MessageBox.yesNo(
481 self,
482 self.tr("New Resource"),
483 self.tr("The file already exists! Overwrite it?"),
484 icon=E5MessageBox.Warning)
485 if not res:
486 # user selected to not overwrite
487 return
488
489 try:
490 if self.project.useSystemEol():
491 newline = None
492 else:
493 newline = self.project.getEolString()
494 rcfile = open(fname, 'w', encoding="utf-8", newline=newline)
495 rcfile.write('<!DOCTYPE RCC>\n')
496 rcfile.write('<RCC version="1.0">\n')
497 rcfile.write('<qresource>\n')
498 rcfile.write('</qresource>\n')
499 rcfile.write('</RCC>\n')
500 rcfile.close()
501 except IOError as e:
502 E5MessageBox.critical(
503 self,
504 self.tr("New Resource"),
505 self.tr(
506 "<p>The new resource file <b>{0}</b> could not"
507 " be created.<br>Problem: {1}</p>")
508 .format(fname, str(e)))
509 return
510
511 self.project.appendFile(fname)
512 self.sourceFile.emit(fname)
513
514 def __deleteFile(self):
515 """
516 Private method to delete a resource file from the project.
517 """
518 itmList = self.getSelectedItems()
519
520 files = []
521 fullNames = []
522 for itm in itmList:
523 fn2 = itm.fileName()
524 fullNames.append(fn2)
525 fn = self.project.getRelativePath(fn2)
526 files.append(fn)
527
528 from UI.DeleteFilesConfirmationDialog import \
529 DeleteFilesConfirmationDialog
530 dlg = DeleteFilesConfirmationDialog(
531 self.parent(),
532 self.tr("Delete resources"),
533 self.tr(
534 "Do you really want to delete these resources from the"
535 " project?"),
536 files)
537
538 if dlg.exec_() == QDialog.Accepted:
539 for fn2, fn in zip(fullNames, files):
540 self.closeSourceWindow.emit(fn2)
541 self.project.deleteFile(fn)
542
543 ###########################################################################
544 ## Methods to handle the various compile commands
545 ###########################################################################
546
547 def __readStdout(self):
548 """
549 Private slot to handle the readyReadStandardOutput signal of the
550 pyrcc4/pyrcc5/pyside-rcc/pyside2-rcc/rbrcc process.
551 """
552 if self.compileProc is None:
553 return
554 self.compileProc.setReadChannel(QProcess.StandardOutput)
555
556 while self.compileProc and self.compileProc.canReadLine():
557 self.buf += str(self.compileProc.readLine(),
558 Preferences.getSystem("IOEncoding"),
559 'replace')
560
561 def __readStderr(self):
562 """
563 Private slot to handle the readyReadStandardError signal of the
564 pyrcc4/pyrcc5/pyside-rcc/pyside2-rcc/rbrcc process.
565 """
566 if self.compileProc is None:
567 return
568
569 ioEncoding = Preferences.getSystem("IOEncoding")
570
571 self.compileProc.setReadChannel(QProcess.StandardError)
572 while self.compileProc and self.compileProc.canReadLine():
573 s = self.rccCompiler + ': '
574 error = str(self.compileProc.readLine(),
575 ioEncoding, 'replace')
576 s += error
577 self.appendStderr.emit(s)
578
579 def __compileQRCDone(self, exitCode, exitStatus):
580 """
581 Private slot to handle the finished signal of the compile process.
582
583 @param exitCode exit code of the process (integer)
584 @param exitStatus exit status of the process (QProcess.ExitStatus)
585 """
586 self.compileRunning = False
587 e5App().getObject("ViewManager").enableEditorsCheckFocusIn(True)
588 ui = e5App().getObject("UserInterface")
589 if exitStatus == QProcess.NormalExit and exitCode == 0 and self.buf:
590 ofn = os.path.join(self.project.ppath, self.compiledFile)
591 try:
592 if self.project.useSystemEol():
593 newline = None
594 else:
595 newline = self.project.getEolString()
596 f = open(ofn, "w", encoding="utf-8", newline=newline)
597 for line in self.buf.splitlines():
598 f.write(line + "\n")
599 f.close()
600 if self.compiledFile not in self.project.pdata["SOURCES"]:
601 self.project.appendFile(ofn)
602 if not self.noDialog and not ui.notificationsEnabled():
603 E5MessageBox.information(
604 self,
605 self.tr("Resource Compilation"),
606 self.tr("The compilation of the resource file"
607 " was successful."))
608 else:
609 ui.showNotification(
610 UI.PixmapCache.getPixmap("resourcesCompiler48.png"),
611 self.tr("Resource Compilation"),
612 self.tr("The compilation of the resource file"
613 " was successful."))
614 except IOError as msg:
615 if not self.noDialog:
616 E5MessageBox.information(
617 self,
618 self.tr("Resource Compilation"),
619 self.tr(
620 "<p>The compilation of the resource file"
621 " failed.</p><p>Reason: {0}</p>").format(str(msg)))
622 else:
623 if not self.noDialog:
624 E5MessageBox.information(
625 self,
626 self.tr("Resource Compilation"),
627 self.tr(
628 "The compilation of the resource file failed."))
629 else:
630 ui.showNotification(
631 UI.PixmapCache.getPixmap("resourcesCompiler48.png"),
632 self.tr("Resource Compilation"),
633 self.tr(
634 "The compilation of the resource file failed."))
635 self.compileProc = None
636
637 def __compileQRC(self, fn, noDialog=False, progress=None):
638 """
639 Private method to compile a .qrc file to a .py file.
640
641 @param fn filename of the .ui file to be compiled
642 @param noDialog flag indicating silent operations
643 @param progress reference to the progress dialog
644 @return reference to the compile process (QProcess)
645 """
646 self.compileProc = QProcess()
647 args = []
648 self.buf = ""
649
650 if self.project.getProjectLanguage() in \
651 ["Python", "Python2", "Python3"]:
652 if self.project.getProjectType() in ["Qt4", "Qt4C"]:
653 self.rccCompiler = Utilities.generatePyQtToolPath('pyrcc4')
654 if PYQT_VERSION >= 0x040500:
655 if self.project.getProjectLanguage() in \
656 ["Python", "Python2"]:
657 args.append("-py2")
658 else:
659 args.append("-py3")
660 elif self.project.getProjectType() in ["PyQt5", "PyQt5C"]:
661 self.rccCompiler = Utilities.generatePyQtToolPath('pyrcc5')
662 elif self.project.getProjectType() in ["E6Plugin"]:
663 if PYQT_VERSION < 0x050000:
664 self.rccCompiler = Utilities.generatePyQtToolPath('pyrcc4')
665 if self.project.getProjectLanguage() in \
666 ["Python", "Python2"]:
667 args.append("-py2")
668 else:
669 args.append("-py3")
670 else:
671 self.rccCompiler = Utilities.generatePyQtToolPath('pyrcc5')
672 elif self.project.getProjectType() in ["PySide", "PySideC"]:
673 self.rccCompiler = Utilities.generatePySideToolPath(
674 'pyside-rcc', "1")
675 if self.project.getProjectLanguage() in \
676 ["Python", "Python2"]:
677 args.append("-py2")
678 else:
679 args.append("-py3")
680 elif self.project.getProjectType() in ["PySide2", "PySide2C"]:
681 self.rccCompiler = Utilities.generatePySideToolPath(
682 'pyside2-rcc', "2")
683 if self.project.getProjectLanguage() in \
684 ["Python", "Python2"]:
685 args.append("-py2")
686 else:
687 args.append("-py3")
688 else:
689 return None
690 defaultParameters = self.project.getDefaultRccCompilerParameters()
691 rccParameters = self.project.pdata["RCCPARAMS"]
692 if rccParameters["CompressionThreshold"] != \
693 defaultParameters["CompressionThreshold"]:
694 args.append("-threshold")
695 args.append(str(rccParameters["CompressionThreshold"]))
696 if rccParameters["CompressLevel"] != \
697 defaultParameters["CompressLevel"]:
698 args.append("-compress")
699 args.append(str(rccParameters["CompressLevel"]))
700 if rccParameters["CompressionDisable"] != \
701 defaultParameters["CompressionDisable"]:
702 args.append("-no-compress")
703 if rccParameters["PathPrefix"] != \
704 defaultParameters["PathPrefix"]:
705 args.append("-root")
706 args.append(rccParameters["PathPrefix"])
707 elif self.project.getProjectLanguage() == "Ruby":
708 if self.project.getProjectType() == "Qt4":
709 self.rccCompiler = 'rbrcc'
710 if Utilities.isWindowsPlatform():
711 self.rccCompiler += '.exe'
712 else:
713 return None
714 else:
715 return None
716
717 rcc = self.rccCompiler
718
719 ofn, ext = os.path.splitext(fn)
720 fn = os.path.join(self.project.ppath, fn)
721
722 dirname, filename = os.path.split(ofn)
723 if self.project.getProjectLanguage() in \
724 ["Python", "Python2", "Python3"]:
725 self.compiledFile = os.path.join(
726 dirname, self.RCFilenameFormatPython.format(filename))
727 elif self.project.getProjectLanguage() == "Ruby":
728 self.compiledFile = os.path.join(
729 dirname, self.RCFilenameFormatRuby.format(filename))
730
731 args.append(fn)
732 self.compileProc.finished.connect(self.__compileQRCDone)
733 self.compileProc.readyReadStandardOutput.connect(self.__readStdout)
734 self.compileProc.readyReadStandardError.connect(self.__readStderr)
735
736 self.noDialog = noDialog
737 self.compileProc.start(rcc, args)
738 procStarted = self.compileProc.waitForStarted(5000)
739 if procStarted:
740 self.compileRunning = True
741 e5App().getObject("ViewManager").enableEditorsCheckFocusIn(False)
742 return self.compileProc
743 else:
744 self.compileRunning = False
745 if progress is not None:
746 progress.cancel()
747 E5MessageBox.critical(
748 self,
749 self.tr('Process Generation Error'),
750 self.tr(
751 'Could not start {0}.<br>'
752 'Ensure that it is in the search path.'
753 ).format(self.rccCompiler))
754 return None
755
756 def __compileResource(self):
757 """
758 Private method to compile a resource to a source file.
759 """
760 itm = self.model().item(self.currentIndex())
761 fn2 = itm.fileName()
762 fn = self.project.getRelativePath(fn2)
763 if self.hooks["compileResource"] is not None:
764 self.hooks["compileResource"](fn)
765 else:
766 self.__compileQRC(fn)
767
768 def __compileAllResources(self):
769 """
770 Private method to compile all resources to source files.
771 """
772 if self.hooks["compileAllResources"] is not None:
773 self.hooks["compileAllResources"](self.project.pdata["RESOURCES"])
774 else:
775 numResources = len(self.project.pdata["RESOURCES"])
776 progress = E5ProgressDialog(
777 self.tr("Compiling resources..."),
778 self.tr("Abort"), 0, numResources,
779 self.tr("%v/%m Resources"), self)
780 progress.setModal(True)
781 progress.setMinimumDuration(0)
782 progress.setWindowTitle(self.tr("Resources"))
783 i = 0
784
785 for fn in self.project.pdata["RESOURCES"]:
786 progress.setValue(i)
787 if progress.wasCanceled():
788 break
789 proc = self.__compileQRC(fn, True, progress)
790 if proc is not None:
791 while proc.state() == QProcess.Running:
792 QApplication.processEvents()
793 QThread.msleep(300)
794 QApplication.processEvents()
795 else:
796 break
797 i += 1
798
799 progress.setValue(numResources)
800
801 def __compileSelectedResources(self):
802 """
803 Private method to compile selected resources to source files.
804 """
805 items = self.getSelectedItems()
806 files = [self.project.getRelativePath(itm.fileName())
807 for itm in items]
808
809 if self.hooks["compileSelectedResources"] is not None:
810 self.hooks["compileSelectedResources"](files)
811 else:
812 numResources = len(files)
813 progress = E5ProgressDialog(
814 self.tr("Compiling resources..."),
815 self.tr("Abort"), 0, numResources,
816 self.tr("%v/%m Resources"), self)
817 progress.setModal(True)
818 progress.setMinimumDuration(0)
819 progress.setWindowTitle(self.tr("Resources"))
820 i = 0
821
822 for fn in files:
823 progress.setValue(i)
824 if progress.wasCanceled():
825 break
826 if not fn.endswith('.ui.h'):
827 proc = self.__compileQRC(fn, True, progress)
828 if proc is not None:
829 while proc.state() == QProcess.Running:
830 QApplication.processEvents()
831 QThread.msleep(300)
832 QApplication.processEvents()
833 else:
834 break
835 i += 1
836
837 progress.setValue(numResources)
838
839 def __checkResourcesNewer(self, filename, mtime):
840 """
841 Private method to check, if any file referenced in a resource
842 file is newer than a given time.
843
844 @param filename filename of the resource file (string)
845 @param mtime modification time to check against
846 @return flag indicating some file is newer (boolean)
847 """
848 try:
849 f = open(filename, "r", encoding="utf-8")
850 buf = f.read()
851 f.close()
852 except IOError:
853 return False
854
855 qrcDirName = os.path.dirname(filename)
856 lbuf = ""
857 for line in buf.splitlines():
858 line = line.strip()
859 if line.lower().startswith("<file>") or \
860 line.lower().startswith("<file "):
861 lbuf = line
862 elif lbuf:
863 lbuf = "{0}{1}".format(lbuf, line)
864 if lbuf.lower().endswith("</file>"):
865 rfile = lbuf.split(">", 1)[1].split("<", 1)[0]
866 if not os.path.isabs(rfile):
867 rfile = os.path.join(qrcDirName, rfile)
868 if os.path.exists(rfile) and \
869 os.stat(rfile).st_mtime > mtime:
870 return True
871
872 lbuf = ""
873
874 return False
875
876 def compileChangedResources(self):
877 """
878 Public method to compile all changed resources to source files.
879 """
880 if self.hooks["compileChangedResources"] is not None:
881 self.hooks["compileChangedResources"](
882 self.project.pdata["RESOURCES"])
883 else:
884 progress = E5ProgressDialog(
885 self.tr("Determining changed resources..."),
886 self.tr("Abort"), 0, 100, self.tr("%v/%m Resources"))
887 progress.setMinimumDuration(0)
888 progress.setWindowTitle(self.tr("Resources"))
889 i = 0
890
891 # get list of changed resources
892 changedResources = []
893 progress.setMaximum(len(self.project.pdata["RESOURCES"]))
894 for fn in self.project.pdata["RESOURCES"]:
895 progress.setValue(i)
896 QApplication.processEvents()
897 ifn = os.path.join(self.project.ppath, fn)
898 if self.project.getProjectLanguage() in \
899 ["Python", "Python2", "Python3"]:
900 dirname, filename = os.path.split(os.path.splitext(ifn)[0])
901 ofn = os.path.join(
902 dirname, self.RCFilenameFormatPython.format(filename))
903 elif self.project.getProjectLanguage() == "Ruby":
904 dirname, filename = os.path.split(os.path.splitext(ifn)[0])
905 ofn = os.path.join(
906 dirname, self.RCFilenameFormatRuby.format(filename))
907 else:
908 return
909 if not os.path.exists(ofn) or \
910 os.stat(ifn).st_mtime > os.stat(ofn).st_mtime:
911 changedResources.append(fn)
912 elif self.__checkResourcesNewer(ifn, os.stat(ofn).st_mtime):
913 changedResources.append(fn)
914 i += 1
915 progress.setValue(i)
916 QApplication.processEvents()
917
918 if changedResources:
919 progress.setLabelText(
920 self.tr("Compiling changed resources..."))
921 progress.setMaximum(len(changedResources))
922 i = 0
923 progress.setValue(i)
924 QApplication.processEvents()
925 for fn in changedResources:
926 progress.setValue(i)
927 if progress.wasCanceled():
928 break
929 proc = self.__compileQRC(fn, True, progress)
930 if proc is not None:
931 while proc.state() == QProcess.Running:
932 QApplication.processEvents()
933 QThread.msleep(300)
934 QApplication.processEvents()
935 else:
936 break
937 i += 1
938 progress.setValue(len(changedResources))
939 QApplication.processEvents()
940
941 def handlePreferencesChanged(self):
942 """
943 Public slot used to handle the preferencesChanged signal.
944 """
945 ProjectBaseBrowser.handlePreferencesChanged(self)
946
947 def __configureRccCompiler(self):
948 """
949 Private slot to configure some non-common rcc compiler options.
950 """
951 from .RccCompilerOptionsDialog import RccCompilerOptionsDialog
952
953 params = self.project.pdata["RCCPARAMS"]
954
955 dlg = RccCompilerOptionsDialog(params)
956 if dlg.exec_() == QDialog.Accepted:
957 threshold, compression, noCompression, root = dlg.getData()
958 if threshold != params["CompressionThreshold"]:
959 params["CompressionThreshold"] = threshold
960 self.project.setDirty(True)
961 if compression != params["CompressLevel"]:
962 params["CompressLevel"] = compression
963 self.project.setDirty(True)
964 if noCompression != params["CompressionDisable"]:
965 params["CompressionDisable"] = noCompression
966 self.project.setDirty(True)
967 if root != params["PathPrefix"]:
968 params["PathPrefix"] = root
969 self.project.setDirty(True)
970
971 ###########################################################################
972 ## Support for hooks below
973 ###########################################################################
974
975 def _initHookMethods(self):
976 """
977 Protected method to initialize the hooks dictionary.
978
979 Supported hook methods are:
980 <ul>
981 <li>compileResource: takes filename as parameter</li>
982 <li>compileAllResources: takes list of filenames as parameter</li>
983 <li>compileChangedResources: takes list of filenames as parameter</li>
984 <li>compileSelectedResources: takes list of all form filenames as
985 parameter</li>
986 <li>newResource: takes full directory path of new file as
987 parameter</li>
988 </ul>
989
990 <b>Note</b>: Filenames are relative to the project directory, if not
991 specified differently.
992 """
993 self.hooks = {
994 "compileResource": None,
995 "compileAllResources": None,
996 "compileChangedResources": None,
997 "compileSelectedResources": None,
998 "newResource": None,
999 }

eric ide

mercurial