|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2002 - 2019 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing the project management functionality. |
|
8 """ |
|
9 |
|
10 from __future__ import unicode_literals |
|
11 try: |
|
12 str = unicode # __IGNORE_EXCEPTION__ |
|
13 except NameError: |
|
14 pass |
|
15 |
|
16 import os |
|
17 import time |
|
18 import shutil |
|
19 import glob |
|
20 import fnmatch |
|
21 import copy |
|
22 import zipfile |
|
23 |
|
24 from PyQt5.QtCore import pyqtSlot, QFile, QFileInfo, pyqtSignal, \ |
|
25 QCryptographicHash, QIODevice, QByteArray, QObject, Qt, QProcess |
|
26 from PyQt5.QtGui import QCursor, QKeySequence |
|
27 from PyQt5.QtWidgets import QLineEdit, QToolBar, QDialog, QInputDialog, \ |
|
28 QApplication, QMenu, QAction |
|
29 from PyQt5.Qsci import QsciScintilla |
|
30 |
|
31 from E5Gui.E5Application import e5App |
|
32 from E5Gui import E5FileDialog, E5MessageBox |
|
33 from E5Gui.E5ListSelectionDialog import E5ListSelectionDialog |
|
34 from E5Gui.E5ProgressDialog import E5ProgressDialog |
|
35 |
|
36 from Globals import recentNameProject |
|
37 |
|
38 import UI.PixmapCache |
|
39 |
|
40 from E5Gui.E5Action import E5Action, createActionGroup |
|
41 |
|
42 import Preferences |
|
43 import Utilities |
|
44 |
|
45 |
|
46 class Project(QObject): |
|
47 """ |
|
48 Class implementing the project management functionality. |
|
49 |
|
50 @signal dirty(bool) emitted when the dirty state changes |
|
51 @signal projectLanguageAdded(str) emitted after a new language was added |
|
52 @signal projectLanguageAddedByCode(str) emitted after a new language was |
|
53 added. The language code is sent by this signal. |
|
54 @signal projectLanguageRemoved(str) emitted after a language was removed |
|
55 @signal projectFormAdded(str) emitted after a new form was added |
|
56 @signal projectFormRemoved(str) emitted after a form was removed |
|
57 @signal projectFormCompiled(str) emitted after a form was compiled |
|
58 @signal projectSourceAdded(str) emitted after a new source file was added |
|
59 @signal projectSourceRemoved(str) emitted after a source was removed |
|
60 @signal projectInterfaceAdded(str) emitted after a new IDL file was added |
|
61 @signal projectInterfaceRemoved(str) emitted after a IDL file was removed |
|
62 @signal projectProtocolAdded(str) emitted after a new proto file was added |
|
63 @signal projectProtocolRemoved(str) emitted after a proto file was removed |
|
64 @signal projectResourceAdded(str) emitted after a new resource file was |
|
65 added |
|
66 @signal projectResourceRemoved(str) emitted after a resource was removed |
|
67 @signal projectOthersAdded(str) emitted after a file or directory was added |
|
68 to the OTHERS project data area |
|
69 @signal projectOthersRemoved(str) emitted after a file was removed from the |
|
70 OTHERS project data area |
|
71 @signal projectAboutToBeCreated() emitted just before the project will be |
|
72 created |
|
73 @signal newProjectHooks() emitted after a new project was generated but |
|
74 before the newProject() signal is sent |
|
75 @signal newProject() emitted after a new project was generated |
|
76 @signal sourceFile(str) emitted after a project file was read to |
|
77 open the main script |
|
78 @signal designerFile(str) emitted to open a found designer file |
|
79 @signal linguistFile(str) emitted to open a found translation file |
|
80 @signal projectOpenedHooks() emitted after a project file was read but |
|
81 before the projectOpened() signal is sent |
|
82 @signal projectOpened() emitted after a project file was read |
|
83 @signal projectClosedHooks() emitted after a project file was closed but |
|
84 before the projectClosed() signal is sent |
|
85 @signal projectClosed() emitted after a project was closed |
|
86 @signal projectFileRenamed(str, str) emitted after a file of the project |
|
87 has been renamed |
|
88 @signal projectPropertiesChanged() emitted after the project properties |
|
89 were changed |
|
90 @signal directoryRemoved(str) emitted after a directory has been removed |
|
91 from the project |
|
92 @signal prepareRepopulateItem(str) emitted before an item of the model is |
|
93 repopulated |
|
94 @signal completeRepopulateItem(str) emitted after an item of the model was |
|
95 repopulated |
|
96 @signal vcsStatusMonitorStatus(str, str) emitted to signal the status of |
|
97 the monitoring thread (ok, nok, op, off) and a status message |
|
98 @signal vcsStatusMonitorInfo(str) emitted to signal some info of the |
|
99 monitoring thread |
|
100 @signal reinitVCS() emitted after the VCS has been reinitialized |
|
101 @signal showMenu(str, QMenu) emitted when a menu is about to be shown. The |
|
102 name of the menu and a reference to the menu are given. |
|
103 @signal lexerAssociationsChanged() emitted after the lexer associations |
|
104 have been changed |
|
105 @signal projectChanged() emitted to signal a change of the project |
|
106 @signal appendStdout(str) emitted after something was received from |
|
107 a QProcess on stdout |
|
108 @signal appendStderr(str) emitted after something was received from |
|
109 a QProcess on stderr |
|
110 """ |
|
111 dirty = pyqtSignal(bool) |
|
112 projectLanguageAdded = pyqtSignal(str) |
|
113 projectLanguageAddedByCode = pyqtSignal(str) |
|
114 projectLanguageRemoved = pyqtSignal(str) |
|
115 projectFormAdded = pyqtSignal(str) |
|
116 projectFormRemoved = pyqtSignal(str) |
|
117 projectFormCompiled = pyqtSignal(str) |
|
118 projectSourceAdded = pyqtSignal(str) |
|
119 projectSourceRemoved = pyqtSignal(str) |
|
120 projectInterfaceAdded = pyqtSignal(str) |
|
121 projectInterfaceRemoved = pyqtSignal(str) |
|
122 projectProtocolAdded = pyqtSignal(str) |
|
123 projectProtocolRemoved = pyqtSignal(str) |
|
124 projectResourceAdded = pyqtSignal(str) |
|
125 projectResourceRemoved = pyqtSignal(str) |
|
126 projectOthersAdded = pyqtSignal(str) |
|
127 projectOthersRemoved = pyqtSignal(str) |
|
128 projectAboutToBeCreated = pyqtSignal() |
|
129 newProjectHooks = pyqtSignal() |
|
130 newProject = pyqtSignal() |
|
131 sourceFile = pyqtSignal(str) |
|
132 designerFile = pyqtSignal(str) |
|
133 linguistFile = pyqtSignal(str) |
|
134 projectOpenedHooks = pyqtSignal() |
|
135 projectOpened = pyqtSignal() |
|
136 projectClosedHooks = pyqtSignal() |
|
137 projectClosed = pyqtSignal() |
|
138 projectFileRenamed = pyqtSignal(str, str) |
|
139 projectPropertiesChanged = pyqtSignal() |
|
140 directoryRemoved = pyqtSignal(str) |
|
141 prepareRepopulateItem = pyqtSignal(str) |
|
142 completeRepopulateItem = pyqtSignal(str) |
|
143 vcsStatusMonitorStatus = pyqtSignal(str, str) |
|
144 vcsStatusMonitorInfo = pyqtSignal(str) |
|
145 reinitVCS = pyqtSignal() |
|
146 showMenu = pyqtSignal(str, QMenu) |
|
147 lexerAssociationsChanged = pyqtSignal() |
|
148 projectChanged = pyqtSignal() |
|
149 appendStdout = pyqtSignal(str) |
|
150 appendStderr = pyqtSignal(str) |
|
151 |
|
152 eols = [os.linesep, "\n", "\r", "\r\n"] |
|
153 |
|
154 DefaultMake = "make" |
|
155 DefaultMakefile = "makefile" |
|
156 |
|
157 def __init__(self, parent=None, filename=None): |
|
158 """ |
|
159 Constructor |
|
160 |
|
161 @param parent parent widget (usually the ui object) (QWidget) |
|
162 @param filename optional filename of a project file to open (string) |
|
163 """ |
|
164 super(Project, self).__init__(parent) |
|
165 |
|
166 self.ui = parent |
|
167 |
|
168 self.__progLanguages = [ |
|
169 "Python2", |
|
170 "Python3", |
|
171 "Ruby", |
|
172 "JavaScript", |
|
173 ] |
|
174 |
|
175 self.__dbgFilters = { |
|
176 "Python2": self.tr( |
|
177 "Python2 Files (*.py2);;" |
|
178 "Python2 GUI Files (*.pyw2);;"), |
|
179 "Python3": self.tr( |
|
180 "Python3 Files (*.py *.py3);;" |
|
181 "Python3 GUI Files (*.pyw *.pyw3);;"), |
|
182 } |
|
183 |
|
184 self.vcsMenu = None |
|
185 self.__makeProcess = None |
|
186 |
|
187 self.__initProjectTypes() |
|
188 |
|
189 self.__initData() |
|
190 |
|
191 self.recent = [] |
|
192 self.__loadRecent() |
|
193 |
|
194 if filename is not None: |
|
195 self.openProject(filename) |
|
196 else: |
|
197 self.vcs = self.initVCS() |
|
198 |
|
199 from .ProjectBrowserModel import ProjectBrowserModel |
|
200 self.__model = ProjectBrowserModel(self) |
|
201 |
|
202 self.codemetrics = None |
|
203 self.codecoverage = None |
|
204 self.profiledata = None |
|
205 self.applicationDiagram = None |
|
206 self.loadedDiagram = None |
|
207 self.__findProjectFileDialog = None |
|
208 |
|
209 def __sourceExtensions(self, language): |
|
210 """ |
|
211 Private method to get the source extensions of a programming language. |
|
212 |
|
213 @param language programming language (string) |
|
214 @return source extensions (list of string) |
|
215 """ |
|
216 if language == "Python2": |
|
217 extensions = Preferences.getPython("PythonExtensions") |
|
218 # *.py and *.pyw should always be associated with source files |
|
219 for ext in [".py", ".pyw"]: |
|
220 if ext not in extensions: |
|
221 extensions.append(ext) |
|
222 return extensions |
|
223 elif language == "Python3": |
|
224 extensions = Preferences.getPython("Python3Extensions") |
|
225 # *.py and *.pyw should always be associated with source files |
|
226 for ext in [".py", ".pyw"]: |
|
227 if ext not in extensions: |
|
228 extensions.append(ext) |
|
229 return extensions |
|
230 elif language == "Ruby": |
|
231 return ['.rb'] |
|
232 elif language == "JavaScript": |
|
233 return ['.js'] |
|
234 elif language == "Mixed": |
|
235 return (Preferences.getPython("Python3Extensions") + |
|
236 Preferences.getPython("PythonExtensions") + |
|
237 ['.rb', '.js']) |
|
238 else: |
|
239 return [""] |
|
240 |
|
241 def getProgrammingLanguages(self): |
|
242 """ |
|
243 Public method to get the programming languages supported by project. |
|
244 |
|
245 @return list of supported programming languages (list of string) |
|
246 """ |
|
247 return self.__progLanguages[:] |
|
248 |
|
249 def getDebuggerFilters(self, language): |
|
250 """ |
|
251 Public method to get the debugger filters for a programming language. |
|
252 |
|
253 @param language programming language |
|
254 @type str |
|
255 @return filter string |
|
256 @rtype str |
|
257 """ |
|
258 try: |
|
259 return self.__dbgFilters[language] |
|
260 except KeyError: |
|
261 return "" |
|
262 |
|
263 def __initProjectTypes(self): |
|
264 """ |
|
265 Private method to initialize the list of supported project types. |
|
266 """ |
|
267 self.__fileTypeCallbacks = {} |
|
268 self.__lexerAssociationCallbacks = {} |
|
269 self.__binaryTranslationsCallbacks = {} |
|
270 |
|
271 self.__projectTypes = { |
|
272 "Qt4": self.tr("PyQt4 GUI"), |
|
273 "Qt4C": self.tr("PyQt4 Console"), |
|
274 "PyQt5": self.tr("PyQt5 GUI"), |
|
275 "PyQt5C": self.tr("PyQt5 Console"), |
|
276 "E6Plugin": self.tr("Eric6 Plugin"), |
|
277 "Console": self.tr("Console"), |
|
278 "Other": self.tr("Other"), |
|
279 } |
|
280 |
|
281 self.__projectProgLanguages = { |
|
282 "Python2": ["Qt4", "Qt4C", "PyQt5", "PyQt5C", |
|
283 "E6Plugin", "Console", "Other"], |
|
284 "Python3": ["Qt4", "Qt4C", "PyQt5", "PyQt5C", |
|
285 "E6Plugin", "Console", "Other"], |
|
286 "Ruby": ["Qt4", "Qt4C", "Console", "Other"], |
|
287 "JavaScript": ["Other"], |
|
288 } |
|
289 |
|
290 pyside_py2, pyside_py3 = Utilities.checkPyside("1") |
|
291 if pyside_py2 or pyside_py3: |
|
292 self.__projectTypes["PySide"] = self.tr("PySide GUI") |
|
293 self.__projectTypes["PySideC"] = self.tr("PySide Console") |
|
294 if pyside_py2: |
|
295 self.__projectProgLanguages["Python2"].extend( |
|
296 ["PySide", "PySideC"]) |
|
297 if pyside_py3: |
|
298 self.__projectProgLanguages["Python3"].extend( |
|
299 ["PySide", "PySideC"]) |
|
300 |
|
301 pyside2_py2, pyside2_py3 = Utilities.checkPyside("2") |
|
302 if pyside2_py2 or pyside2_py3: |
|
303 self.__projectTypes["PySide2"] = self.tr("PySide2 GUI") |
|
304 self.__projectTypes["PySide2C"] = self.tr("PySide2 Console") |
|
305 if pyside2_py2: |
|
306 self.__projectProgLanguages["Python2"].extend( |
|
307 ["PySide2", "PySide2C"]) |
|
308 if pyside2_py3: |
|
309 self.__projectProgLanguages["Python3"].extend( |
|
310 ["PySide2", "PySide2C"]) |
|
311 |
|
312 def getProjectTypes(self, progLanguage=""): |
|
313 """ |
|
314 Public method to get the list of supported project types. |
|
315 |
|
316 @param progLanguage programming language to get project types for |
|
317 (string) |
|
318 @return reference to the dictionary of project types. |
|
319 """ |
|
320 if progLanguage and progLanguage in self.__projectProgLanguages: |
|
321 ptypes = {} |
|
322 for ptype in self.__projectProgLanguages[progLanguage]: |
|
323 ptypes[ptype] = self.__projectTypes[ptype] |
|
324 return ptypes |
|
325 else: |
|
326 return self.__projectTypes |
|
327 |
|
328 def hasProjectType(self, type_, progLanguage=""): |
|
329 """ |
|
330 Public method to check, if a project type is already registered. |
|
331 |
|
332 @param type_ internal type designator (string) |
|
333 @param progLanguage programming language of the project type (string) |
|
334 @return flag indicating presence of the project type (boolean) |
|
335 """ |
|
336 if progLanguage: |
|
337 return progLanguage in self.__projectProgLanguages and \ |
|
338 type_ in self.__projectProgLanguages[progLanguage] |
|
339 else: |
|
340 return type_ in self.__projectTypes |
|
341 |
|
342 def registerProjectType(self, type_, description, fileTypeCallback=None, |
|
343 binaryTranslationsCallback=None, |
|
344 lexerAssociationCallback=None, progLanguages=None): |
|
345 """ |
|
346 Public method to register a project type. |
|
347 |
|
348 @param type_ internal type designator to be registered (string) |
|
349 @param description more verbose type name (display string) (string) |
|
350 @keyparam fileTypeCallback reference to a method returning a dictionary |
|
351 of filetype associations. |
|
352 @keyparam binaryTranslationsCallback reference to a method returning |
|
353 the name of the binary translation file given the name of the raw |
|
354 translation file |
|
355 @keyparam lexerAssociationCallback reference to a method returning the |
|
356 lexer type to be used for syntax highlighting given the name of |
|
357 a file |
|
358 @keyparam progLanguages programming languages supported by the |
|
359 project type (list of string) |
|
360 """ |
|
361 if progLanguages: |
|
362 for progLanguage in progLanguages: |
|
363 if progLanguage not in self.__projectProgLanguages: |
|
364 E5MessageBox.critical( |
|
365 self.ui, |
|
366 self.tr("Registering Project Type"), |
|
367 self.tr( |
|
368 """<p>The Programming Language <b>{0}</b> is not""" |
|
369 """ supported.</p>""") |
|
370 .format(progLanguage) |
|
371 ) |
|
372 return |
|
373 |
|
374 if type_ in self.__projectProgLanguages[progLanguage]: |
|
375 E5MessageBox.critical( |
|
376 self.ui, |
|
377 self.tr("Registering Project Type"), |
|
378 self.tr( |
|
379 """<p>The Project type <b>{0}</b> is already""" |
|
380 """ registered with Programming Language""" |
|
381 """ <b>{1}</b>.</p>""") |
|
382 .format(type_, progLanguage) |
|
383 ) |
|
384 return |
|
385 |
|
386 if type_ in self.__projectTypes: |
|
387 E5MessageBox.critical( |
|
388 self.ui, |
|
389 self.tr("Registering Project Type"), |
|
390 self.tr("""<p>The Project type <b>{0}</b> is already""" |
|
391 """ registered.</p>""").format(type_) |
|
392 ) |
|
393 else: |
|
394 self.__projectTypes[type_] = description |
|
395 self.__fileTypeCallbacks[type_] = fileTypeCallback |
|
396 self.__lexerAssociationCallbacks[type_] = lexerAssociationCallback |
|
397 self.__binaryTranslationsCallbacks[type_] = \ |
|
398 binaryTranslationsCallback |
|
399 if progLanguages: |
|
400 for progLanguage in progLanguages: |
|
401 self.__projectProgLanguages[progLanguage].append(type_) |
|
402 else: |
|
403 # no specific programming languages given -> add to all |
|
404 for progLanguage in self.__projectProgLanguages: |
|
405 self.__projectProgLanguages[progLanguage].append(type_) |
|
406 |
|
407 def unregisterProjectType(self, type_): |
|
408 """ |
|
409 Public method to unregister a project type. |
|
410 |
|
411 @param type_ internal type designator to be unregistered (string) |
|
412 """ |
|
413 for progLanguage in self.__projectProgLanguages: |
|
414 if type_ in self.__projectProgLanguages[progLanguage]: |
|
415 self.__projectProgLanguages[progLanguage].remove(type_) |
|
416 if type_ in self.__projectTypes: |
|
417 del self.__projectTypes[type_] |
|
418 if type_ in self.__fileTypeCallbacks: |
|
419 del self.__fileTypeCallbacks[type_] |
|
420 if type_ in self.__lexerAssociationCallbacks: |
|
421 del self.__lexerAssociationCallbacks[type_] |
|
422 if type_ in self.__binaryTranslationsCallbacks: |
|
423 del self.__binaryTranslationsCallbacks[type_] |
|
424 |
|
425 def __initData(self): |
|
426 """ |
|
427 Private method to initialize the project data part. |
|
428 """ |
|
429 self.loaded = False # flag for the loaded status |
|
430 self.__dirty = False # dirty flag |
|
431 self.pfile = "" # name of the project file |
|
432 self.ppath = "" # name of the project directory |
|
433 self.translationsRoot = "" # the translations prefix |
|
434 self.name = "" |
|
435 self.opened = False |
|
436 self.subdirs = [""] |
|
437 # record the project dir as a relative path (i.e. empty path) |
|
438 self.otherssubdirs = [] |
|
439 self.vcs = None |
|
440 self.vcsRequested = False |
|
441 self.dbgVirtualEnv = '' |
|
442 self.dbgCmdline = '' |
|
443 self.dbgWd = '' |
|
444 self.dbgEnv = '' |
|
445 self.dbgReportExceptions = True |
|
446 self.dbgExcList = [] |
|
447 self.dbgExcIgnoreList = [] |
|
448 self.dbgAutoClearShell = True |
|
449 self.dbgTracePython = False |
|
450 self.dbgAutoContinue = True |
|
451 |
|
452 self.pdata = { |
|
453 "DESCRIPTION": "", |
|
454 "VERSION": "", |
|
455 "SOURCES": [], |
|
456 "FORMS": [], |
|
457 "RESOURCES": [], |
|
458 "INTERFACES": [], |
|
459 "PROTOCOLS": [], |
|
460 "OTHERS": [], |
|
461 "TRANSLATIONS": [], |
|
462 "TRANSLATIONEXCEPTIONS": [], |
|
463 "TRANSLATIONPATTERN": "", |
|
464 "TRANSLATIONSBINPATH": "", |
|
465 "MAINSCRIPT": "", |
|
466 "VCS": "None", |
|
467 "VCSOPTIONS": {}, |
|
468 "VCSOTHERDATA": {}, |
|
469 "AUTHOR": '', |
|
470 "EMAIL": '', |
|
471 "HASH": '', |
|
472 "PROGLANGUAGE": "Python3", |
|
473 "MIXEDLANGUAGE": False, |
|
474 "PROJECTTYPE": "PyQt5", |
|
475 "SPELLLANGUAGE": |
|
476 Preferences.getEditor("SpellCheckingDefaultLanguage"), |
|
477 "SPELLWORDS": '', |
|
478 "SPELLEXCLUDES": '', |
|
479 "FILETYPES": {}, |
|
480 "LEXERASSOCS": {}, |
|
481 "PROJECTTYPESPECIFICDATA": {}, |
|
482 "CHECKERSPARMS": {}, |
|
483 "PACKAGERSPARMS": {}, |
|
484 "DOCUMENTATIONPARMS": {}, |
|
485 "OTHERTOOLSPARMS": {}, |
|
486 "MAKEPARAMS": { |
|
487 "MakeEnabled": False, |
|
488 "MakeExecutable": "", |
|
489 "MakeFile": "", |
|
490 "MakeTarget": "", |
|
491 "MakeParameters": "", |
|
492 "MakeTestOnly": True, |
|
493 }, |
|
494 "IDLPARAMS": { |
|
495 "IncludeDirs": [], |
|
496 "DefinedNames": [], |
|
497 "UndefinedNames": [], |
|
498 }, |
|
499 "UICPARAMS": { |
|
500 "Package": "", |
|
501 "RcSuffix": "", |
|
502 }, |
|
503 "RCCPARAMS": { |
|
504 "CompressionThreshold": 70, # default value |
|
505 "CompressLevel": 0, # use zlib default |
|
506 "CompressionDisable": False, |
|
507 "PathPrefix": "", |
|
508 }, |
|
509 "EOL": -1, |
|
510 } |
|
511 |
|
512 self.__initDebugProperties() |
|
513 |
|
514 self.pudata = { |
|
515 "VCSOVERRIDE": "", |
|
516 "VCSSTATUSMONITORINTERVAL": 0, |
|
517 } |
|
518 |
|
519 self.vcs = self.initVCS() |
|
520 |
|
521 def getData(self, category, key): |
|
522 """ |
|
523 Public method to get data out of the project data store. |
|
524 |
|
525 @param category category of the data to get (string, one of |
|
526 PROJECTTYPESPECIFICDATA, CHECKERSPARMS, PACKAGERSPARMS, |
|
527 DOCUMENTATIONPARMS or OTHERTOOLSPARMS) |
|
528 @param key key of the data entry to get (string). |
|
529 @return a copy of the requested data or None |
|
530 """ |
|
531 if category in ["PROJECTTYPESPECIFICDATA", "CHECKERSPARMS", |
|
532 "PACKAGERSPARMS", "DOCUMENTATIONPARMS", |
|
533 "OTHERTOOLSPARMS"] and \ |
|
534 key in self.pdata[category]: |
|
535 return copy.deepcopy(self.pdata[category][key]) |
|
536 else: |
|
537 return None |
|
538 |
|
539 def setData(self, category, key, data): |
|
540 """ |
|
541 Public method to store data in the project data store. |
|
542 |
|
543 @param category category of the data to get (string, one of |
|
544 PROJECTTYPESPECIFICDATA, CHECKERSPARMS, PACKAGERSPARMS, |
|
545 DOCUMENTATIONPARMS or OTHERTOOLSPARMS) |
|
546 @param key key of the data entry to get (string). |
|
547 @param data data to be stored |
|
548 @return flag indicating success (boolean) |
|
549 """ |
|
550 if category not in ["PROJECTTYPESPECIFICDATA", "CHECKERSPARMS", |
|
551 "PACKAGERSPARMS", "DOCUMENTATIONPARMS", |
|
552 "OTHERTOOLSPARMS"]: |
|
553 return False |
|
554 |
|
555 # test for changes of data and save them in the project |
|
556 # 1. there were none, now there are |
|
557 if key not in self.pdata[category] and len(data) > 0: |
|
558 self.pdata[category][key] = copy.deepcopy(data) |
|
559 self.setDirty(True) |
|
560 # 2. there were some, now there aren't |
|
561 elif key in self.pdata[category] and len(data) == 0: |
|
562 del self.pdata[category][key] |
|
563 self.setDirty(True) |
|
564 # 3. there were some and still are |
|
565 elif key in self.pdata[category] and len(data) > 0: |
|
566 if data != self.pdata[category][key]: |
|
567 self.pdata[category][key] = copy.deepcopy(data) |
|
568 self.setDirty(True) |
|
569 # 4. there were none and none are given |
|
570 else: |
|
571 return False |
|
572 return True |
|
573 |
|
574 def initFileTypes(self): |
|
575 """ |
|
576 Public method to initialize the filetype associations with default |
|
577 values. |
|
578 """ |
|
579 self.pdata["FILETYPES"] = { |
|
580 "*.txt": "OTHERS", |
|
581 "*.md": "OTHERS", |
|
582 "*.rst": "OTHERS", |
|
583 "README": "OTHERS", |
|
584 "README.*": "OTHERS", |
|
585 "*.e4p": "OTHERS", |
|
586 "GNUmakefile": "OTHERS", |
|
587 "makefile": "OTHERS", |
|
588 "Makefile": "OTHERS", |
|
589 } |
|
590 |
|
591 # Sources |
|
592 if self.pdata["MIXEDLANGUAGE"]: |
|
593 sourceKey = "Mixed" |
|
594 else: |
|
595 sourceKey = self.pdata["PROGLANGUAGE"] |
|
596 for ext in self.__sourceExtensions(sourceKey): |
|
597 self.pdata["FILETYPES"]["*{0}".format(ext)] = "SOURCES" |
|
598 |
|
599 # IDL interfaces |
|
600 self.pdata["FILETYPES"]["*.idl"] = "INTERFACES" |
|
601 |
|
602 # Protobuf Files |
|
603 self.pdata["FILETYPES"]["*.proto"] = "PROTOCOLS" |
|
604 |
|
605 # Forms |
|
606 if self.pdata["PROJECTTYPE"] in ["Qt4", "PyQt5", |
|
607 "E6Plugin", "PySide", |
|
608 "PySide2"]: |
|
609 self.pdata["FILETYPES"]["*.ui"] = "FORMS" |
|
610 |
|
611 # Resources |
|
612 if self.pdata["PROJECTTYPE"] in ["Qt4", "Qt4C", |
|
613 "E6Plugin", |
|
614 "PyQt5", "PyQt5C", |
|
615 "PySide", "PySideC", |
|
616 "PySide2", "PySide2C"]: |
|
617 self.pdata["FILETYPES"]["*.qrc"] = "RESOURCES" |
|
618 |
|
619 # Translations |
|
620 if self.pdata["PROJECTTYPE"] in ["Qt4", "Qt4C", |
|
621 "E6Plugin", |
|
622 "PyQt5", "PyQt5C", |
|
623 "PySide", "PySideC", |
|
624 "PySide2", "PySide2C"]: |
|
625 self.pdata["FILETYPES"]["*.ts"] = "TRANSLATIONS" |
|
626 self.pdata["FILETYPES"]["*.qm"] = "TRANSLATIONS" |
|
627 |
|
628 # Project type specific ones |
|
629 try: |
|
630 if self.__fileTypeCallbacks[ |
|
631 self.pdata["PROJECTTYPE"]] is not None: |
|
632 ftypes = \ |
|
633 self.__fileTypeCallbacks[self.pdata["PROJECTTYPE"]]() |
|
634 self.pdata["FILETYPES"].update(ftypes) |
|
635 except KeyError: |
|
636 pass |
|
637 |
|
638 self.setDirty(True) |
|
639 |
|
640 def updateFileTypes(self): |
|
641 """ |
|
642 Public method to update the filetype associations with new default |
|
643 values. |
|
644 """ |
|
645 if self.pdata["PROJECTTYPE"] in ["Qt4", "Qt4C", |
|
646 "E6Plugin", |
|
647 "PyQt5", "PyQt5C", |
|
648 "PySide", "PySideC", |
|
649 "PySide2", "PySide2C"]: |
|
650 if "*.ts" not in self.pdata["FILETYPES"]: |
|
651 self.pdata["FILETYPES"]["*.ts"] = "TRANSLATIONS" |
|
652 if "*.qm" not in self.pdata["FILETYPES"]: |
|
653 self.pdata["FILETYPES"]["*.qm"] = "TRANSLATIONS" |
|
654 try: |
|
655 if self.__fileTypeCallbacks[ |
|
656 self.pdata["PROJECTTYPE"]] is not None: |
|
657 ftypes = \ |
|
658 self.__fileTypeCallbacks[self.pdata["PROJECTTYPE"]]() |
|
659 for pattern, ftype in list(ftypes.items()): |
|
660 if pattern not in self.pdata["FILETYPES"]: |
|
661 self.pdata["FILETYPES"][pattern] = ftype |
|
662 self.setDirty(True) |
|
663 except KeyError: |
|
664 pass |
|
665 |
|
666 def __loadRecent(self): |
|
667 """ |
|
668 Private method to load the recently opened project filenames. |
|
669 """ |
|
670 self.recent = [] |
|
671 Preferences.Prefs.rsettings.sync() |
|
672 rp = Preferences.Prefs.rsettings.value(recentNameProject) |
|
673 if rp is not None: |
|
674 for f in rp: |
|
675 if QFileInfo(f).exists(): |
|
676 self.recent.append(f) |
|
677 |
|
678 def __saveRecent(self): |
|
679 """ |
|
680 Private method to save the list of recently opened filenames. |
|
681 """ |
|
682 Preferences.Prefs.rsettings.setValue(recentNameProject, self.recent) |
|
683 Preferences.Prefs.rsettings.sync() |
|
684 |
|
685 def getMostRecent(self): |
|
686 """ |
|
687 Public method to get the most recently opened project. |
|
688 |
|
689 @return path of the most recently opened project (string) |
|
690 """ |
|
691 if len(self.recent): |
|
692 return self.recent[0] |
|
693 else: |
|
694 return None |
|
695 |
|
696 def getModel(self): |
|
697 """ |
|
698 Public method to get a reference to the project browser model. |
|
699 |
|
700 @return reference to the project browser model (ProjectBrowserModel) |
|
701 """ |
|
702 return self.__model |
|
703 |
|
704 def getVcs(self): |
|
705 """ |
|
706 Public method to get a reference to the VCS object. |
|
707 |
|
708 @return reference to the VCS object |
|
709 """ |
|
710 return self.vcs |
|
711 |
|
712 def handlePreferencesChanged(self): |
|
713 """ |
|
714 Public slot used to handle the preferencesChanged signal. |
|
715 """ |
|
716 if self.pudata["VCSSTATUSMONITORINTERVAL"]: |
|
717 self.setStatusMonitorInterval( |
|
718 self.pudata["VCSSTATUSMONITORINTERVAL"]) |
|
719 else: |
|
720 self.setStatusMonitorInterval( |
|
721 Preferences.getVCS("StatusMonitorInterval")) |
|
722 |
|
723 self.__model.preferencesChanged() |
|
724 |
|
725 def setDirty(self, dirty): |
|
726 """ |
|
727 Public method to set the dirty state. |
|
728 |
|
729 It emits the signal dirty(bool). |
|
730 |
|
731 @param dirty dirty state |
|
732 @type bool |
|
733 """ |
|
734 self.__dirty = dirty |
|
735 self.saveAct.setEnabled(dirty) |
|
736 self.dirty.emit(dirty) |
|
737 if self.__dirty: |
|
738 self.projectChanged.emit() |
|
739 |
|
740 def isDirty(self): |
|
741 """ |
|
742 Public method to return the dirty state. |
|
743 |
|
744 @return dirty state (boolean) |
|
745 """ |
|
746 return self.__dirty |
|
747 |
|
748 def isOpen(self): |
|
749 """ |
|
750 Public method to return the opened state. |
|
751 |
|
752 @return open state (boolean) |
|
753 """ |
|
754 return self.opened |
|
755 |
|
756 def __checkFilesExist(self, index): |
|
757 """ |
|
758 Private method to check, if the files in a list exist. |
|
759 |
|
760 The files in the indicated list are checked for existance in the |
|
761 filesystem. Non existant files are removed from the list and the |
|
762 dirty state of the project is changed accordingly. |
|
763 |
|
764 @param index key of the list to be checked (string) |
|
765 """ |
|
766 removed = False |
|
767 removelist = [] |
|
768 for file in self.pdata[index]: |
|
769 if not os.path.exists(os.path.join(self.ppath, file)): |
|
770 removelist.append(file) |
|
771 removed = True |
|
772 |
|
773 if removed: |
|
774 for file in removelist: |
|
775 self.pdata[index].remove(file) |
|
776 self.setDirty(True) |
|
777 |
|
778 def __readProject(self, fn): |
|
779 """ |
|
780 Private method to read in a project (.e4p) file. |
|
781 |
|
782 @param fn filename of the project file to be read (string) |
|
783 @return flag indicating success |
|
784 """ |
|
785 f = QFile(fn) |
|
786 if f.open(QIODevice.ReadOnly): |
|
787 from E5XML.ProjectReader import ProjectReader |
|
788 reader = ProjectReader(f, self) |
|
789 reader.readXML() |
|
790 res = not reader.hasError() |
|
791 f.close() |
|
792 else: |
|
793 QApplication.restoreOverrideCursor() |
|
794 E5MessageBox.critical( |
|
795 self.ui, |
|
796 self.tr("Read project file"), |
|
797 self.tr( |
|
798 "<p>The project file <b>{0}</b> could not be read.</p>") |
|
799 .format(fn)) |
|
800 return False |
|
801 |
|
802 self.pfile = os.path.abspath(fn) |
|
803 self.ppath = os.path.abspath(os.path.dirname(fn)) |
|
804 |
|
805 # insert filename into list of recently opened projects |
|
806 self.__syncRecent() |
|
807 |
|
808 if res: |
|
809 if self.pdata["TRANSLATIONPATTERN"]: |
|
810 self.translationsRoot = \ |
|
811 self.pdata["TRANSLATIONPATTERN"].split("%language%")[0] |
|
812 elif self.pdata["MAINSCRIPT"]: |
|
813 self.translationsRoot = os.path.splitext( |
|
814 self.pdata["MAINSCRIPT"])[0] |
|
815 if os.path.isdir(os.path.join(self.ppath, self.translationsRoot)): |
|
816 dn = self.translationsRoot |
|
817 else: |
|
818 dn = os.path.dirname(self.translationsRoot) |
|
819 if dn not in self.subdirs: |
|
820 self.subdirs.append(dn) |
|
821 |
|
822 self.name = os.path.splitext(os.path.basename(fn))[0] |
|
823 |
|
824 # check, if the files of the project still exist in the |
|
825 # project directory |
|
826 self.__checkFilesExist("SOURCES") |
|
827 self.__checkFilesExist("FORMS") |
|
828 self.__checkFilesExist("INTERFACES") |
|
829 self.__checkFilesExist("PROTOCOLS") |
|
830 self.__checkFilesExist("TRANSLATIONS") |
|
831 self.__checkFilesExist("RESOURCES") |
|
832 self.__checkFilesExist("OTHERS") |
|
833 |
|
834 # get the names of subdirectories the files are stored in |
|
835 for fn in self.pdata["SOURCES"] + \ |
|
836 self.pdata["FORMS"] + \ |
|
837 self.pdata["INTERFACES"] + \ |
|
838 self.pdata["PROTOCOLS"] + \ |
|
839 self.pdata["RESOURCES"] + \ |
|
840 self.pdata["TRANSLATIONS"]: |
|
841 dn = os.path.dirname(fn) |
|
842 if dn not in self.subdirs: |
|
843 self.subdirs.append(dn) |
|
844 |
|
845 # get the names of other subdirectories |
|
846 for fn in self.pdata["OTHERS"]: |
|
847 dn = os.path.dirname(fn) |
|
848 if dn not in self.otherssubdirs: |
|
849 self.otherssubdirs.append(dn) |
|
850 |
|
851 # create hash value, if it doesn't have one |
|
852 if reader.version.startswith("5.") and not self.pdata["HASH"]: |
|
853 hashStr = str(QCryptographicHash.hash( |
|
854 QByteArray(self.ppath.encode("utf-8")), |
|
855 QCryptographicHash.Sha1).toHex(), |
|
856 encoding="utf-8") |
|
857 self.pdata["HASH"] = hashStr |
|
858 self.setDirty(True) |
|
859 |
|
860 return res |
|
861 |
|
862 def __writeProject(self, fn=None): |
|
863 """ |
|
864 Private method to save the project infos to a project file. |
|
865 |
|
866 @param fn optional filename of the project file to be written (string). |
|
867 If fn is None, the filename stored in the project object |
|
868 is used. This is the 'save' action. If fn is given, this filename |
|
869 is used instead of the one in the project object. This is the |
|
870 'save as' action. |
|
871 @return flag indicating success |
|
872 """ |
|
873 if self.vcs is not None: |
|
874 self.pdata["VCSOPTIONS"] = \ |
|
875 copy.deepcopy(self.vcs.vcsGetOptions()) |
|
876 self.pdata["VCSOTHERDATA"] = \ |
|
877 copy.deepcopy(self.vcs.vcsGetOtherData()) |
|
878 |
|
879 if not self.pdata["HASH"]: |
|
880 hashStr = str(QCryptographicHash.hash( |
|
881 QByteArray(self.ppath.encode("utf-8")), |
|
882 QCryptographicHash.Sha1).toHex(), |
|
883 encoding="utf-8") |
|
884 self.pdata["HASH"] = hashStr |
|
885 |
|
886 if fn is None: |
|
887 fn = self.pfile |
|
888 |
|
889 f = QFile(fn) |
|
890 if f.open(QIODevice.WriteOnly): |
|
891 from E5XML.ProjectWriter import ProjectWriter |
|
892 ProjectWriter(f, os.path.splitext( |
|
893 os.path.basename(fn))[0]).writeXML() |
|
894 res = True |
|
895 else: |
|
896 E5MessageBox.critical( |
|
897 self.ui, |
|
898 self.tr("Save project file"), |
|
899 self.tr( |
|
900 "<p>The project file <b>{0}</b> could not be" |
|
901 " written.</p>").format(fn)) |
|
902 res = False |
|
903 |
|
904 if res: |
|
905 self.pfile = os.path.abspath(fn) |
|
906 self.ppath = os.path.abspath(os.path.dirname(fn)) |
|
907 self.name = os.path.splitext(os.path.basename(fn))[0] |
|
908 self.setDirty(False) |
|
909 |
|
910 # insert filename into list of recently opened projects |
|
911 self.__syncRecent() |
|
912 |
|
913 return res |
|
914 |
|
915 def __readUserProperties(self): |
|
916 """ |
|
917 Private method to read in the user specific project file (.e4q). |
|
918 """ |
|
919 if self.pfile is None: |
|
920 return |
|
921 |
|
922 fn, ext = os.path.splitext(os.path.basename(self.pfile)) |
|
923 fn = os.path.join(self.getProjectManagementDir(), '{0}.e4q'.format(fn)) |
|
924 if os.path.exists(fn): |
|
925 f = QFile(fn) |
|
926 if f.open(QIODevice.ReadOnly): |
|
927 from E5XML.UserProjectReader import UserProjectReader |
|
928 reader = UserProjectReader(f, self) |
|
929 reader.readXML() |
|
930 f.close() |
|
931 else: |
|
932 E5MessageBox.critical( |
|
933 self.ui, |
|
934 self.tr("Read user project properties"), |
|
935 self.tr( |
|
936 "<p>The user specific project properties file" |
|
937 " <b>{0}</b> could not be read.</p>").format(fn)) |
|
938 |
|
939 def __writeUserProperties(self): |
|
940 """ |
|
941 Private method to write the project data to an XML file. |
|
942 """ |
|
943 if self.pfile is None: |
|
944 return |
|
945 |
|
946 fn, ext = os.path.splitext(os.path.basename(self.pfile)) |
|
947 fn = os.path.join(self.getProjectManagementDir(), '{0}.e4q'.format(fn)) |
|
948 |
|
949 f = QFile(fn) |
|
950 if f.open(QIODevice.WriteOnly): |
|
951 from E5XML.UserProjectWriter import UserProjectWriter |
|
952 UserProjectWriter( |
|
953 f, os.path.splitext(os.path.basename(fn))[0]).writeXML() |
|
954 f.close() |
|
955 else: |
|
956 E5MessageBox.critical( |
|
957 self.ui, |
|
958 self.tr("Save user project properties"), |
|
959 self.tr( |
|
960 "<p>The user specific project properties file <b>{0}</b>" |
|
961 " could not be written.</p>").format(fn)) |
|
962 |
|
963 def __showContextMenuSession(self): |
|
964 """ |
|
965 Private slot called before the Session menu is shown. |
|
966 """ |
|
967 enable = True |
|
968 if self.pfile is None: |
|
969 enable = False |
|
970 else: |
|
971 fn, ext = os.path.splitext(os.path.basename(self.pfile)) |
|
972 fn_new = os.path.join(self.getProjectManagementDir(), |
|
973 '{0}.e5s'.format(fn)) |
|
974 fn_old = os.path.join(self.getProjectManagementDir(), |
|
975 '{0}.e4s'.format(fn)) |
|
976 enable = os.path.exists(fn_new) or os.path.exists(fn_old) |
|
977 self.sessActGrp.findChild( |
|
978 QAction, "project_load_session").setEnabled(enable) |
|
979 self.sessActGrp.findChild( |
|
980 QAction, "project_delete_session").setEnabled(enable) |
|
981 |
|
982 @pyqtSlot() |
|
983 def __readSession(self, quiet=False, indicator=""): |
|
984 """ |
|
985 Private method to read in the project session file (.e5s or .e4s). |
|
986 |
|
987 @param quiet flag indicating quiet operations. |
|
988 If this flag is true, no errors are reported. |
|
989 @keyparam indicator indicator string (string) |
|
990 """ |
|
991 if self.pfile is None: |
|
992 if not quiet: |
|
993 E5MessageBox.critical( |
|
994 self.ui, |
|
995 self.tr("Read project session"), |
|
996 self.tr("Please save the project first.")) |
|
997 return |
|
998 |
|
999 fn1, ext = os.path.splitext(os.path.basename(self.pfile)) |
|
1000 fn = os.path.join(self.getProjectManagementDir(), |
|
1001 '{0}{1}.e5s'.format(fn1, indicator)) |
|
1002 if not os.path.exists(fn): |
|
1003 fn = os.path.join(self.getProjectManagementDir(), |
|
1004 '{0}{1}.e4s'.format(fn1, indicator)) |
|
1005 |
|
1006 f = QFile(fn) |
|
1007 if f.open(QIODevice.ReadOnly): |
|
1008 from E5XML.SessionReader import SessionReader |
|
1009 reader = SessionReader(f, False) |
|
1010 reader.readXML(quiet=quiet) |
|
1011 f.close() |
|
1012 else: |
|
1013 if not quiet: |
|
1014 E5MessageBox.critical( |
|
1015 self.ui, |
|
1016 self.tr("Read project session"), |
|
1017 self.tr( |
|
1018 "<p>The project session file <b>{0}</b> could not be" |
|
1019 " read.</p>").format(fn)) |
|
1020 |
|
1021 @pyqtSlot() |
|
1022 def __writeSession(self, quiet=False, indicator=""): |
|
1023 """ |
|
1024 Private method to write the session data to an XML file (.e5s). |
|
1025 |
|
1026 @param quiet flag indicating quiet operations. |
|
1027 If this flag is true, no errors are reported. |
|
1028 @keyparam indicator indicator string (string) |
|
1029 """ |
|
1030 if self.pfile is None: |
|
1031 if not quiet: |
|
1032 E5MessageBox.critical( |
|
1033 self.ui, |
|
1034 self.tr("Save project session"), |
|
1035 self.tr("Please save the project first.")) |
|
1036 return |
|
1037 |
|
1038 fn, ext = os.path.splitext(os.path.basename(self.pfile)) |
|
1039 fn = os.path.join(self.getProjectManagementDir(), |
|
1040 '{0}{1}.e5s'.format(fn, indicator)) |
|
1041 |
|
1042 f = QFile(fn) |
|
1043 if f.open(QIODevice.WriteOnly): |
|
1044 from E5XML.SessionWriter import SessionWriter |
|
1045 SessionWriter( |
|
1046 f, os.path.splitext(os.path.basename(fn))[0]).writeXML() |
|
1047 f.close() |
|
1048 else: |
|
1049 if not quiet: |
|
1050 E5MessageBox.critical( |
|
1051 self.ui, |
|
1052 self.tr("Save project session"), |
|
1053 self.tr( |
|
1054 "<p>The project session file <b>{0}</b> could not be" |
|
1055 " written.</p>").format(fn)) |
|
1056 |
|
1057 def __deleteSession(self): |
|
1058 """ |
|
1059 Private method to delete the session file. |
|
1060 """ |
|
1061 if self.pfile is None: |
|
1062 E5MessageBox.critical( |
|
1063 self.ui, |
|
1064 self.tr("Delete project session"), |
|
1065 self.tr("Please save the project first.")) |
|
1066 return |
|
1067 |
|
1068 fname, ext = os.path.splitext(os.path.basename(self.pfile)) |
|
1069 |
|
1070 for fn in [ |
|
1071 os.path.join( |
|
1072 self.getProjectManagementDir(), "{0}.e5s".format(fname)), |
|
1073 os.path.join( |
|
1074 self.getProjectManagementDir(), "{0}.e4s".format(fname))]: |
|
1075 if os.path.exists(fn): |
|
1076 try: |
|
1077 os.remove(fn) |
|
1078 except OSError: |
|
1079 E5MessageBox.critical( |
|
1080 self.ui, |
|
1081 self.tr("Delete project session"), |
|
1082 self.tr( |
|
1083 "<p>The project session file <b>{0}</b> could" |
|
1084 " not be deleted.</p>").format(fn)) |
|
1085 |
|
1086 def __readTasks(self): |
|
1087 """ |
|
1088 Private method to read in the project tasks file (.e6t). |
|
1089 """ |
|
1090 if self.pfile is None: |
|
1091 E5MessageBox.critical( |
|
1092 self.ui, |
|
1093 self.tr("Read tasks"), |
|
1094 self.tr("Please save the project first.")) |
|
1095 return |
|
1096 |
|
1097 base, ext = os.path.splitext(os.path.basename(self.pfile)) |
|
1098 fn = os.path.join(self.getProjectManagementDir(), |
|
1099 '{0}.e6t'.format(base)) |
|
1100 if not os.path.exists(fn): |
|
1101 # try again with the old extension |
|
1102 fn = os.path.join(self.getProjectManagementDir(), |
|
1103 '{0}.e4t'.format(base)) |
|
1104 if not os.path.exists(fn): |
|
1105 return |
|
1106 f = QFile(fn) |
|
1107 if f.open(QIODevice.ReadOnly): |
|
1108 from E5XML.TasksReader import TasksReader |
|
1109 reader = TasksReader(f, True) |
|
1110 reader.readXML() |
|
1111 f.close() |
|
1112 else: |
|
1113 E5MessageBox.critical( |
|
1114 self.ui, |
|
1115 self.tr("Read tasks"), |
|
1116 self.tr( |
|
1117 "<p>The tasks file <b>{0}</b> could not be read.</p>") |
|
1118 .format(fn)) |
|
1119 |
|
1120 def writeTasks(self): |
|
1121 """ |
|
1122 Public method to write the tasks data to an XML file (.e6t). |
|
1123 """ |
|
1124 if self.pfile is None: |
|
1125 return |
|
1126 |
|
1127 fn, ext = os.path.splitext(os.path.basename(self.pfile)) |
|
1128 |
|
1129 fn = os.path.join(self.getProjectManagementDir(), '{0}.e6t'.format(fn)) |
|
1130 f = QFile(fn) |
|
1131 ok = f.open(QIODevice.WriteOnly) |
|
1132 if not ok: |
|
1133 E5MessageBox.critical( |
|
1134 self.ui, |
|
1135 self.tr("Save tasks"), |
|
1136 self.tr( |
|
1137 "<p>The tasks file <b>{0}</b> could not be written.</p>") |
|
1138 .format(fn)) |
|
1139 return |
|
1140 |
|
1141 from E5XML.TasksWriter import TasksWriter |
|
1142 TasksWriter( |
|
1143 f, True, os.path.splitext(os.path.basename(fn))[0]).writeXML() |
|
1144 f.close() |
|
1145 |
|
1146 def __showContextMenuDebugger(self): |
|
1147 """ |
|
1148 Private slot called before the Debugger menu is shown. |
|
1149 """ |
|
1150 enable = True |
|
1151 if self.pfile is None: |
|
1152 enable = False |
|
1153 else: |
|
1154 fn, ext = os.path.splitext(os.path.basename(self.pfile)) |
|
1155 fn = os.path.join(self.getProjectManagementDir(), |
|
1156 '{0}.e4d'.format(fn)) |
|
1157 enable = os.path.exists(fn) |
|
1158 self.dbgActGrp.findChild( |
|
1159 QAction, "project_debugger_properties_load").setEnabled(enable) |
|
1160 self.dbgActGrp.findChild( |
|
1161 QAction, "project_debugger_properties_delete").setEnabled(enable) |
|
1162 |
|
1163 @pyqtSlot() |
|
1164 def __readDebugProperties(self, quiet=False): |
|
1165 """ |
|
1166 Private method to read in the project debugger properties file (.e4d). |
|
1167 |
|
1168 @param quiet flag indicating quiet operations. |
|
1169 If this flag is true, no errors are reported. |
|
1170 """ |
|
1171 if self.pfile is None: |
|
1172 if not quiet: |
|
1173 E5MessageBox.critical( |
|
1174 self.ui, |
|
1175 self.tr("Read debugger properties"), |
|
1176 self.tr("Please save the project first.")) |
|
1177 return |
|
1178 |
|
1179 fn, ext = os.path.splitext(os.path.basename(self.pfile)) |
|
1180 fn = os.path.join(self.getProjectManagementDir(), '{0}.e4d'.format(fn)) |
|
1181 |
|
1182 f = QFile(fn) |
|
1183 if f.open(QIODevice.ReadOnly): |
|
1184 from E5XML.DebuggerPropertiesReader import DebuggerPropertiesReader |
|
1185 reader = DebuggerPropertiesReader(f, self) |
|
1186 reader.readXML(quiet=quiet) |
|
1187 f.close() |
|
1188 self.debugPropertiesLoaded = True |
|
1189 self.debugPropertiesChanged = False |
|
1190 else: |
|
1191 if not quiet: |
|
1192 E5MessageBox.critical( |
|
1193 self.ui, |
|
1194 self.tr("Read debugger properties"), |
|
1195 self.tr( |
|
1196 "<p>The project debugger properties file <b>{0}</b>" |
|
1197 " could not be read.</p>").format(fn)) |
|
1198 |
|
1199 @pyqtSlot() |
|
1200 def __writeDebugProperties(self, quiet=False): |
|
1201 """ |
|
1202 Private method to write the project debugger properties file (.e4d). |
|
1203 |
|
1204 @param quiet flag indicating quiet operations. |
|
1205 If this flag is true, no errors are reported. |
|
1206 """ |
|
1207 if self.pfile is None: |
|
1208 if not quiet: |
|
1209 E5MessageBox.critical( |
|
1210 self.ui, |
|
1211 self.tr("Save debugger properties"), |
|
1212 self.tr("Please save the project first.")) |
|
1213 return |
|
1214 |
|
1215 fn, ext = os.path.splitext(os.path.basename(self.pfile)) |
|
1216 fn = os.path.join(self.getProjectManagementDir(), '{0}.e4d'.format(fn)) |
|
1217 |
|
1218 f = QFile(fn) |
|
1219 if f.open(QIODevice.WriteOnly): |
|
1220 from E5XML.DebuggerPropertiesWriter import DebuggerPropertiesWriter |
|
1221 DebuggerPropertiesWriter( |
|
1222 f, os.path.splitext(os.path.basename(fn))[0]).writeXML() |
|
1223 f.close() |
|
1224 self.debugPropertiesChanged = False |
|
1225 else: |
|
1226 if not quiet: |
|
1227 E5MessageBox.critical( |
|
1228 self.ui, |
|
1229 self.tr("Save debugger properties"), |
|
1230 self.tr( |
|
1231 "<p>The project debugger properties file <b>{0}</b>" |
|
1232 " could not be written.</p>").format(fn)) |
|
1233 |
|
1234 def __deleteDebugProperties(self): |
|
1235 """ |
|
1236 Private method to delete the project debugger properties file (.e4d). |
|
1237 """ |
|
1238 if self.pfile is None: |
|
1239 E5MessageBox.critical( |
|
1240 self.ui, |
|
1241 self.tr("Delete debugger properties"), |
|
1242 self.tr("Please save the project first.")) |
|
1243 return |
|
1244 |
|
1245 fname, ext = os.path.splitext(os.path.basename(self.pfile)) |
|
1246 |
|
1247 for fn in [os.path.join(self.getProjectManagementDir(), |
|
1248 "{0}.e4d".format(fname))]: |
|
1249 if os.path.exists(fn): |
|
1250 try: |
|
1251 os.remove(fn) |
|
1252 except OSError: |
|
1253 E5MessageBox.critical( |
|
1254 self.ui, |
|
1255 self.tr("Delete debugger properties"), |
|
1256 self.tr( |
|
1257 "<p>The project debugger properties file" |
|
1258 " <b>{0}</b> could not be deleted.</p>") |
|
1259 .format(fn)) |
|
1260 |
|
1261 def __initDebugProperties(self): |
|
1262 """ |
|
1263 Private method to initialize the debug properties. |
|
1264 """ |
|
1265 self.debugPropertiesLoaded = False |
|
1266 self.debugPropertiesChanged = False |
|
1267 self.debugProperties = { |
|
1268 "VIRTUALENV": "", |
|
1269 "DEBUGCLIENT": "", |
|
1270 "ENVIRONMENTOVERRIDE": False, |
|
1271 "ENVIRONMENTSTRING": "", |
|
1272 "REMOTEDEBUGGER": False, |
|
1273 "REMOTEHOST": "", |
|
1274 "REMOTECOMMAND": "", |
|
1275 "PATHTRANSLATION": False, |
|
1276 "REMOTEPATH": "", |
|
1277 "LOCALPATH": "", |
|
1278 "CONSOLEDEBUGGER": False, |
|
1279 "CONSOLECOMMAND": "", |
|
1280 "REDIRECT": False, |
|
1281 "NOENCODING": False, |
|
1282 } |
|
1283 |
|
1284 def isDebugPropertiesLoaded(self): |
|
1285 """ |
|
1286 Public method to return the status of the debug properties. |
|
1287 |
|
1288 @return load status of debug properties (boolean) |
|
1289 """ |
|
1290 return self.debugPropertiesLoaded |
|
1291 |
|
1292 def __showDebugProperties(self): |
|
1293 """ |
|
1294 Private slot to display the debugger properties dialog. |
|
1295 """ |
|
1296 from .DebuggerPropertiesDialog import DebuggerPropertiesDialog |
|
1297 dlg = DebuggerPropertiesDialog(self) |
|
1298 if dlg.exec_() == QDialog.Accepted: |
|
1299 dlg.storeData() |
|
1300 |
|
1301 def getDebugProperty(self, key): |
|
1302 """ |
|
1303 Public method to retrieve a debugger property. |
|
1304 |
|
1305 @param key key of the property (string) |
|
1306 @return value of the property |
|
1307 """ |
|
1308 if key == "INTERPRETER": |
|
1309 return e5App().getObject("VirtualEnvManager")\ |
|
1310 .getVirtualenvInterpreter(self.debugProperties["VIRTUALENV"]) |
|
1311 else: |
|
1312 return self.debugProperties[key] |
|
1313 |
|
1314 def setDbgInfo(self, venvName, argv, wd, env, excReporting, excList, |
|
1315 excIgnoreList, autoClearShell, tracePython=None, |
|
1316 autoContinue=None): |
|
1317 """ |
|
1318 Public method to set the debugging information. |
|
1319 |
|
1320 @param venvName name of the virtual environment used |
|
1321 @type str |
|
1322 @param argv command line arguments to be used |
|
1323 @type str |
|
1324 @param wd working directory |
|
1325 @type str |
|
1326 @param env environment setting |
|
1327 @type str |
|
1328 @param excReporting flag indicating the highlighting of exceptions |
|
1329 @type bool |
|
1330 @param excList list of exceptions to be highlighted |
|
1331 @type list of str |
|
1332 @param excIgnoreList list of exceptions to be ignored |
|
1333 @type list of str |
|
1334 @param autoClearShell flag indicating, that the interpreter window |
|
1335 should be cleared |
|
1336 @type bool |
|
1337 @keyparam tracePython flag to indicate if the Python library should be |
|
1338 traced as well |
|
1339 @type bool |
|
1340 @keyparam autoContinue flag indicating, that the debugger should not |
|
1341 stop at the first executable line |
|
1342 @type bool |
|
1343 """ |
|
1344 self.dbgVirtualEnv = venvName |
|
1345 self.dbgCmdline = argv |
|
1346 self.dbgWd = wd |
|
1347 self.dbgEnv = env |
|
1348 self.dbgReportExceptions = excReporting |
|
1349 self.dbgExcList = excList[:] # keep a copy of the list |
|
1350 self.dbgExcIgnoreList = excIgnoreList[:] # keep a copy of the list |
|
1351 self.dbgAutoClearShell = autoClearShell |
|
1352 if tracePython is not None: |
|
1353 self.dbgTracePython = tracePython |
|
1354 if autoContinue is not None: |
|
1355 self.dbgAutoContinue = autoContinue |
|
1356 |
|
1357 def getTranslationPattern(self): |
|
1358 """ |
|
1359 Public method to get the translation pattern. |
|
1360 |
|
1361 @return translation pattern (string) |
|
1362 """ |
|
1363 return self.pdata["TRANSLATIONPATTERN"] |
|
1364 |
|
1365 def setTranslationPattern(self, pattern): |
|
1366 """ |
|
1367 Public method to set the translation pattern. |
|
1368 |
|
1369 @param pattern translation pattern |
|
1370 @type str |
|
1371 """ |
|
1372 self.pdata["TRANSLATIONPATTERN"] = pattern |
|
1373 |
|
1374 def addLanguage(self): |
|
1375 """ |
|
1376 Public slot used to add a language to the project. |
|
1377 """ |
|
1378 if not self.pdata["TRANSLATIONPATTERN"]: |
|
1379 E5MessageBox.critical( |
|
1380 self.ui, |
|
1381 self.tr("Add Language"), |
|
1382 self.tr( |
|
1383 "You have to specify a translation pattern first.")) |
|
1384 return |
|
1385 |
|
1386 from .AddLanguageDialog import AddLanguageDialog |
|
1387 dlg = AddLanguageDialog(self.parent()) |
|
1388 if dlg.exec_() == QDialog.Accepted: |
|
1389 lang = dlg.getSelectedLanguage() |
|
1390 if self.pdata["PROJECTTYPE"] in \ |
|
1391 ["Qt4", "Qt4C", "PyQt5", "PyQt5C", "E6Plugin", |
|
1392 "PySide", "PySideC", "PySide2", "PySide2C"]: |
|
1393 langFile = self.pdata["TRANSLATIONPATTERN"]\ |
|
1394 .replace("%language%", lang) |
|
1395 self.appendFile(langFile) |
|
1396 self.projectLanguageAddedByCode.emit(lang) |
|
1397 |
|
1398 def __binaryTranslationFile(self, langFile): |
|
1399 """ |
|
1400 Private method to calculate the filename of the binary translations |
|
1401 file given the name of the raw translations file. |
|
1402 |
|
1403 @param langFile name of the raw translations file (string) |
|
1404 @return name of the binary translations file (string) |
|
1405 """ |
|
1406 qmFile = "" |
|
1407 try: |
|
1408 if self.__binaryTranslationsCallbacks[ |
|
1409 self.pdata["PROJECTTYPE"]] is not None: |
|
1410 qmFile = self.__binaryTranslationsCallbacks[ |
|
1411 self.pdata["PROJECTTYPE"]](langFile) |
|
1412 except KeyError: |
|
1413 qmFile = langFile.replace('.ts', '.qm') |
|
1414 if qmFile == langFile: |
|
1415 qmFile = "" |
|
1416 return qmFile |
|
1417 |
|
1418 def checkLanguageFiles(self): |
|
1419 """ |
|
1420 Public slot to check the language files after a release process. |
|
1421 """ |
|
1422 tbPath = self.pdata["TRANSLATIONSBINPATH"] |
|
1423 for langFile in self.pdata["TRANSLATIONS"][:]: |
|
1424 qmFile = self.__binaryTranslationFile(langFile) |
|
1425 if qmFile: |
|
1426 if qmFile not in self.pdata["TRANSLATIONS"] and \ |
|
1427 os.path.exists(os.path.join(self.ppath, qmFile)): |
|
1428 self.appendFile(qmFile) |
|
1429 if tbPath: |
|
1430 qmFile = os.path.join(tbPath, os.path.basename(qmFile)) |
|
1431 if qmFile not in self.pdata["TRANSLATIONS"] and \ |
|
1432 os.path.exists(os.path.join(self.ppath, qmFile)): |
|
1433 self.appendFile(qmFile) |
|
1434 |
|
1435 def removeLanguageFile(self, langFile): |
|
1436 """ |
|
1437 Public slot to remove a translation from the project. |
|
1438 |
|
1439 The translation file is not deleted from the project directory. |
|
1440 |
|
1441 @param langFile the translation file to be removed (string) |
|
1442 """ |
|
1443 langFile = self.getRelativePath(langFile) |
|
1444 qmFile = self.__binaryTranslationFile(langFile) |
|
1445 self.pdata["TRANSLATIONS"].remove(langFile) |
|
1446 self.__model.removeItem(langFile) |
|
1447 if qmFile: |
|
1448 try: |
|
1449 if self.pdata["TRANSLATIONSBINPATH"]: |
|
1450 qmFile = self.getRelativePath( |
|
1451 os.path.join(self.pdata["TRANSLATIONSBINPATH"], |
|
1452 os.path.basename(qmFile))) |
|
1453 self.pdata["TRANSLATIONS"].remove(qmFile) |
|
1454 self.__model.removeItem(qmFile) |
|
1455 except ValueError: |
|
1456 pass |
|
1457 self.setDirty(True) |
|
1458 |
|
1459 def deleteLanguageFile(self, langFile): |
|
1460 """ |
|
1461 Public slot to delete a translation from the project directory. |
|
1462 |
|
1463 @param langFile the translation file to be removed (string) |
|
1464 """ |
|
1465 try: |
|
1466 from ThirdParty.Send2Trash.send2trash import send2trash as s2t |
|
1467 except ImportError: |
|
1468 s2t = os.remove |
|
1469 |
|
1470 langFile = self.getRelativePath(langFile) |
|
1471 qmFile = self.__binaryTranslationFile(langFile) |
|
1472 |
|
1473 try: |
|
1474 fn = os.path.join(self.ppath, langFile) |
|
1475 if os.path.exists(fn): |
|
1476 s2t(fn) |
|
1477 except EnvironmentError as err: |
|
1478 E5MessageBox.critical( |
|
1479 self.ui, |
|
1480 self.tr("Delete translation"), |
|
1481 self.tr( |
|
1482 "<p>The selected translation file <b>{0}</b> could not be" |
|
1483 " deleted.</p><p>Reason: {1}</p>").format( |
|
1484 langFile, str(err))) |
|
1485 return |
|
1486 |
|
1487 self.removeLanguageFile(langFile) |
|
1488 |
|
1489 # now get rid of the .qm file |
|
1490 if qmFile: |
|
1491 try: |
|
1492 if self.pdata["TRANSLATIONSBINPATH"]: |
|
1493 qmFile = self.getRelativePath( |
|
1494 os.path.join(self.pdata["TRANSLATIONSBINPATH"], |
|
1495 os.path.basename(qmFile))) |
|
1496 fn = os.path.join(self.ppath, qmFile) |
|
1497 if os.path.exists(fn): |
|
1498 s2t(fn) |
|
1499 except EnvironmentError as err: |
|
1500 E5MessageBox.critical( |
|
1501 self.ui, |
|
1502 self.tr("Delete translation"), |
|
1503 self.tr( |
|
1504 "<p>The selected translation file <b>{0}</b> could" |
|
1505 " not be deleted.</p><p>Reason: {1}</p>").format( |
|
1506 qmFile, str(err))) |
|
1507 return |
|
1508 |
|
1509 def appendFile(self, fn, isSourceFile=False, updateModel=True): |
|
1510 """ |
|
1511 Public method to append a file to the project. |
|
1512 |
|
1513 @param fn filename to be added to the project (string) |
|
1514 @param isSourceFile flag indicating that this is a source file |
|
1515 even if it doesn't have the source extension (boolean) |
|
1516 @param updateModel flag indicating an update of the model is |
|
1517 requested (boolean) |
|
1518 """ |
|
1519 dirty = False |
|
1520 |
|
1521 if os.path.isabs(fn): |
|
1522 # make it relative to the project root, if it starts with that path |
|
1523 newfn = self.getRelativePath(fn) |
|
1524 else: |
|
1525 # assume relative paths are relative to the project root |
|
1526 newfn = fn |
|
1527 newdir = os.path.dirname(newfn) |
|
1528 |
|
1529 if isSourceFile: |
|
1530 filetype = "SOURCES" |
|
1531 else: |
|
1532 filetype = "OTHERS" |
|
1533 bfn = os.path.basename(newfn) |
|
1534 if fnmatch.fnmatch(bfn, '*.ts') or fnmatch.fnmatch(bfn, '*.qm'): |
|
1535 filetype = "TRANSLATIONS" |
|
1536 else: |
|
1537 for pattern in reversed( |
|
1538 sorted(self.pdata["FILETYPES"].keys())): |
|
1539 if fnmatch.fnmatch(bfn, pattern): |
|
1540 filetype = self.pdata["FILETYPES"][pattern] |
|
1541 break |
|
1542 |
|
1543 if filetype == "__IGNORE__": |
|
1544 return |
|
1545 |
|
1546 if filetype in ["SOURCES", "FORMS", "INTERFACES", "PROTOCOLS", |
|
1547 "RESOURCES"]: |
|
1548 if filetype == "SOURCES": |
|
1549 if newfn not in self.pdata["SOURCES"]: |
|
1550 self.pdata["SOURCES"].append(newfn) |
|
1551 self.projectSourceAdded.emit(newfn) |
|
1552 updateModel and self.__model.addNewItem("SOURCES", newfn) |
|
1553 dirty = True |
|
1554 else: |
|
1555 updateModel and self.repopulateItem(newfn) |
|
1556 elif filetype == "FORMS": |
|
1557 if newfn not in self.pdata["FORMS"]: |
|
1558 self.pdata["FORMS"].append(newfn) |
|
1559 self.projectFormAdded.emit(newfn) |
|
1560 updateModel and self.__model.addNewItem("FORMS", newfn) |
|
1561 dirty = True |
|
1562 else: |
|
1563 updateModel and self.repopulateItem(newfn) |
|
1564 elif filetype == "INTERFACES": |
|
1565 if newfn not in self.pdata["INTERFACES"]: |
|
1566 self.pdata["INTERFACES"].append(newfn) |
|
1567 self.projectInterfaceAdded.emit(newfn) |
|
1568 updateModel and \ |
|
1569 self.__model.addNewItem("INTERFACES", newfn) |
|
1570 dirty = True |
|
1571 else: |
|
1572 updateModel and self.repopulateItem(newfn) |
|
1573 elif filetype == "PROTOCOLS": |
|
1574 if newfn not in self.pdata["PROTOCOLS"]: |
|
1575 self.pdata["PROTOCOLS"].append(newfn) |
|
1576 self.projectProtocolAdded.emit(newfn) |
|
1577 updateModel and \ |
|
1578 self.__model.addNewItem("PROTOCOLS", newfn) |
|
1579 dirty = True |
|
1580 else: |
|
1581 updateModel and self.repopulateItem(newfn) |
|
1582 elif filetype == "RESOURCES": |
|
1583 if newfn not in self.pdata["RESOURCES"]: |
|
1584 self.pdata["RESOURCES"].append(newfn) |
|
1585 self.projectResourceAdded.emit(newfn) |
|
1586 updateModel and self.__model.addNewItem("RESOURCES", newfn) |
|
1587 dirty = True |
|
1588 else: |
|
1589 updateModel and self.repopulateItem(newfn) |
|
1590 if newdir not in self.subdirs: |
|
1591 self.subdirs.append(newdir) |
|
1592 elif filetype == "TRANSLATIONS": |
|
1593 if newfn not in self.pdata["TRANSLATIONS"]: |
|
1594 self.pdata["TRANSLATIONS"].append(newfn) |
|
1595 updateModel and self.__model.addNewItem("TRANSLATIONS", newfn) |
|
1596 self.projectLanguageAdded.emit(newfn) |
|
1597 dirty = True |
|
1598 else: |
|
1599 updateModel and self.repopulateItem(newfn) |
|
1600 else: # filetype == "OTHERS" |
|
1601 if newfn not in self.pdata["OTHERS"]: |
|
1602 self.pdata['OTHERS'].append(newfn) |
|
1603 self.othersAdded(newfn, updateModel) |
|
1604 dirty = True |
|
1605 else: |
|
1606 updateModel and self.repopulateItem(newfn) |
|
1607 if newdir not in self.otherssubdirs: |
|
1608 self.otherssubdirs.append(newdir) |
|
1609 |
|
1610 if dirty: |
|
1611 self.setDirty(True) |
|
1612 |
|
1613 @pyqtSlot() |
|
1614 def addFiles(self, fileTypeFilter=None, startdir=None): |
|
1615 """ |
|
1616 Public slot used to add files to the project. |
|
1617 |
|
1618 @param fileTypeFilter filter to be used by the add file dialog |
|
1619 @type str out of source, form, resource, interface, protocol, others |
|
1620 @param startdir start directory for the selection dialog |
|
1621 @type str |
|
1622 """ |
|
1623 if startdir is None: |
|
1624 startdir = self.ppath |
|
1625 from .AddFileDialog import AddFileDialog |
|
1626 dlg = AddFileDialog(self, self.parent(), fileTypeFilter, |
|
1627 startdir=startdir) |
|
1628 if dlg.exec_() == QDialog.Accepted: |
|
1629 fnames, target, isSource = dlg.getData() |
|
1630 if target != '': |
|
1631 for fn in fnames: |
|
1632 targetfile = os.path.join(target, os.path.basename(fn)) |
|
1633 if not Utilities.samepath(os.path.dirname(fn), target): |
|
1634 try: |
|
1635 if not os.path.isdir(target): |
|
1636 os.makedirs(target) |
|
1637 |
|
1638 if os.path.exists(targetfile): |
|
1639 res = E5MessageBox.yesNo( |
|
1640 self.ui, |
|
1641 self.tr("Add file"), |
|
1642 self.tr( |
|
1643 "<p>The file <b>{0}</b> already" |
|
1644 " exists.</p><p>Overwrite it?</p>") |
|
1645 .format(targetfile), |
|
1646 icon=E5MessageBox.Warning) |
|
1647 if not res: |
|
1648 return # don't overwrite |
|
1649 |
|
1650 shutil.copy(fn, target) |
|
1651 except IOError as why: |
|
1652 E5MessageBox.critical( |
|
1653 self.ui, |
|
1654 self.tr("Add file"), |
|
1655 self.tr( |
|
1656 "<p>The selected file <b>{0}</b> could" |
|
1657 " not be added to <b>{1}</b>.</p>" |
|
1658 "<p>Reason: {2}</p>") |
|
1659 .format(fn, target, str(why))) |
|
1660 continue |
|
1661 |
|
1662 self.appendFile(targetfile, |
|
1663 isSource or fileTypeFilter == 'source') |
|
1664 else: |
|
1665 E5MessageBox.critical( |
|
1666 self.ui, |
|
1667 self.tr("Add file"), |
|
1668 self.tr("The target directory must not be empty.")) |
|
1669 |
|
1670 def __addSingleDirectory(self, filetype, source, target, quiet=False): |
|
1671 """ |
|
1672 Private method used to add all files of a single directory to the |
|
1673 project. |
|
1674 |
|
1675 @param filetype type of files to add (string) |
|
1676 @param source source directory (string) |
|
1677 @param target target directory (string) |
|
1678 @param quiet flag indicating quiet operations (boolean) |
|
1679 """ |
|
1680 # get all relevant filename patterns |
|
1681 patterns = [] |
|
1682 ignorePatterns = [] |
|
1683 for pattern, patterntype in list(self.pdata["FILETYPES"].items()): |
|
1684 if patterntype == filetype: |
|
1685 patterns.append(pattern) |
|
1686 elif patterntype == "__IGNORE__": |
|
1687 ignorePatterns.append(pattern) |
|
1688 |
|
1689 files = [] |
|
1690 for pattern in patterns: |
|
1691 sstring = "{0}{1}{2}".format(source, os.sep, pattern) |
|
1692 files.extend(glob.glob(sstring)) |
|
1693 |
|
1694 if len(files) == 0: |
|
1695 if not quiet: |
|
1696 E5MessageBox.information( |
|
1697 self.ui, |
|
1698 self.tr("Add directory"), |
|
1699 self.tr( |
|
1700 "<p>The source directory doesn't contain" |
|
1701 " any files belonging to the selected category.</p>")) |
|
1702 return |
|
1703 |
|
1704 if not Utilities.samepath(target, source) and \ |
|
1705 not os.path.isdir(target): |
|
1706 try: |
|
1707 os.makedirs(target) |
|
1708 except IOError as why: |
|
1709 E5MessageBox.critical( |
|
1710 self.ui, |
|
1711 self.tr("Add directory"), |
|
1712 self.tr( |
|
1713 "<p>The target directory <b>{0}</b> could not be" |
|
1714 " created.</p><p>Reason: {1}</p>") |
|
1715 .format(target, str(why))) |
|
1716 return |
|
1717 |
|
1718 for file in files: |
|
1719 for pattern in ignorePatterns: |
|
1720 if fnmatch.fnmatch(file, pattern): |
|
1721 continue |
|
1722 |
|
1723 targetfile = os.path.join(target, os.path.basename(file)) |
|
1724 if not Utilities.samepath(target, source): |
|
1725 try: |
|
1726 if os.path.exists(targetfile): |
|
1727 res = E5MessageBox.yesNo( |
|
1728 self.ui, |
|
1729 self.tr("Add directory"), |
|
1730 self.tr( |
|
1731 "<p>The file <b>{0}</b> already exists.</p>" |
|
1732 "<p>Overwrite it?</p>") |
|
1733 .format(targetfile), |
|
1734 icon=E5MessageBox.Warning) |
|
1735 if not res: |
|
1736 continue |
|
1737 # don't overwrite, carry on with next file |
|
1738 |
|
1739 shutil.copy(file, target) |
|
1740 except EnvironmentError: |
|
1741 continue |
|
1742 self.appendFile(targetfile) |
|
1743 |
|
1744 def __addRecursiveDirectory(self, filetype, source, target): |
|
1745 """ |
|
1746 Private method used to add all files of a directory tree. |
|
1747 |
|
1748 The tree is rooted at source to another one rooted at target. This |
|
1749 method decents down to the lowest subdirectory. |
|
1750 |
|
1751 @param filetype type of files to add (string) |
|
1752 @param source source directory (string) |
|
1753 @param target target directory (string) |
|
1754 """ |
|
1755 # first perform the addition of source |
|
1756 self.__addSingleDirectory(filetype, source, target, True) |
|
1757 |
|
1758 ignore_patterns = [pattern for pattern, filetype in |
|
1759 self.pdata["FILETYPES"].items() |
|
1760 if filetype == '__IGNORE__'] |
|
1761 |
|
1762 # now recurse into subdirectories |
|
1763 for name in os.listdir(source): |
|
1764 ns = os.path.join(source, name) |
|
1765 if os.path.isdir(ns): |
|
1766 skip = False |
|
1767 for ignore_pattern in ignore_patterns: |
|
1768 if fnmatch.fnmatch(name, ignore_pattern): |
|
1769 skip = True |
|
1770 break |
|
1771 if skip: |
|
1772 continue |
|
1773 |
|
1774 nt = os.path.join(target, name) |
|
1775 self.__addRecursiveDirectory(filetype, ns, nt) |
|
1776 |
|
1777 @pyqtSlot() |
|
1778 def addDirectory(self, fileTypeFilter=None, startdir=None): |
|
1779 """ |
|
1780 Public method used to add all files of a directory to the project. |
|
1781 |
|
1782 @param fileTypeFilter filter to be used by the add directory dialog |
|
1783 @type str out of source, form, resource, interface, protocol, others |
|
1784 @param startdir start directory for the selection dialog |
|
1785 @type str |
|
1786 """ |
|
1787 if startdir is None: |
|
1788 startdir = self.ppath |
|
1789 from .AddDirectoryDialog import AddDirectoryDialog |
|
1790 dlg = AddDirectoryDialog( |
|
1791 self, fileTypeFilter, self.parent(), startdir=startdir) |
|
1792 if dlg.exec_() == QDialog.Accepted: |
|
1793 filetype, source, target, recursive = dlg.getData() |
|
1794 if target == '': |
|
1795 E5MessageBox.critical( |
|
1796 self.ui, |
|
1797 self.tr("Add directory"), |
|
1798 self.tr("The target directory must not be empty.")) |
|
1799 return |
|
1800 |
|
1801 if filetype == 'OTHERS': |
|
1802 self.addToOthers(source) |
|
1803 return |
|
1804 |
|
1805 if source == '': |
|
1806 E5MessageBox.critical( |
|
1807 self.ui, |
|
1808 self.tr("Add directory"), |
|
1809 self.tr("The source directory must not be empty.")) |
|
1810 return |
|
1811 |
|
1812 if recursive: |
|
1813 self.__addRecursiveDirectory(filetype, source, target) |
|
1814 else: |
|
1815 self.__addSingleDirectory(filetype, source, target) |
|
1816 |
|
1817 def addToOthers(self, fn): |
|
1818 """ |
|
1819 Public method to add a file/directory to the OTHERS project data. |
|
1820 |
|
1821 @param fn file name or directory name to add (string) |
|
1822 """ |
|
1823 if fn: |
|
1824 # if it is below the project directory, make it relative to that |
|
1825 fn = self.getRelativePath(fn) |
|
1826 |
|
1827 # if it ends with the directory separator character, remove it |
|
1828 if fn.endswith(os.sep): |
|
1829 fn = fn[:-1] |
|
1830 |
|
1831 if fn not in self.pdata["OTHERS"]: |
|
1832 self.pdata['OTHERS'].append(fn) |
|
1833 self.othersAdded(fn) |
|
1834 self.setDirty(True) |
|
1835 |
|
1836 if os.path.isdir(fn) and fn not in self.otherssubdirs: |
|
1837 self.otherssubdirs.append(fn) |
|
1838 |
|
1839 def addSourceFiles(self): |
|
1840 """ |
|
1841 Public slot to add source files to the current project. |
|
1842 """ |
|
1843 self.addFiles('source') |
|
1844 |
|
1845 def addUiFiles(self): |
|
1846 """ |
|
1847 Public slot to add forms to the current project. |
|
1848 """ |
|
1849 self.addFiles('form') |
|
1850 |
|
1851 def addIdlFiles(self): |
|
1852 """ |
|
1853 Public slot to add IDL interfaces to the current project. |
|
1854 """ |
|
1855 self.addFiles('interface') |
|
1856 |
|
1857 def addProtoFiles(self): |
|
1858 """ |
|
1859 Public slot to add protocol files to the current project. |
|
1860 """ |
|
1861 self.addFiles('protocol') |
|
1862 |
|
1863 def addResourceFiles(self): |
|
1864 """ |
|
1865 Public slot to add Qt resources to the current project. |
|
1866 """ |
|
1867 self.addFiles('resource') |
|
1868 |
|
1869 def addOthersFiles(self): |
|
1870 """ |
|
1871 Public slot to add files to the OTHERS project data. |
|
1872 """ |
|
1873 self.addFiles('others') |
|
1874 |
|
1875 def addSourceDir(self): |
|
1876 """ |
|
1877 Public slot to add all source files of a directory to the current |
|
1878 project. |
|
1879 """ |
|
1880 self.addDirectory('source') |
|
1881 |
|
1882 def addUiDir(self): |
|
1883 """ |
|
1884 Public slot to add all forms of a directory to the current project. |
|
1885 """ |
|
1886 self.addDirectory('form') |
|
1887 |
|
1888 def addIdlDir(self): |
|
1889 """ |
|
1890 Public slot to add all IDL interfaces of a directory to the current |
|
1891 project. |
|
1892 """ |
|
1893 self.addDirectory('interface') |
|
1894 |
|
1895 def addProtoDir(self): |
|
1896 """ |
|
1897 Public slot to add all protocol files of a directory to the current |
|
1898 project. |
|
1899 """ |
|
1900 self.addDirectory('protocol') |
|
1901 |
|
1902 def addResourceDir(self): |
|
1903 """ |
|
1904 Public slot to add all Qt resource files of a directory to the current |
|
1905 project. |
|
1906 """ |
|
1907 self.addDirectory('resource') |
|
1908 |
|
1909 def addOthersDir(self): |
|
1910 """ |
|
1911 Public slot to add a directory to the OTHERS project data. |
|
1912 """ |
|
1913 self.addDirectory('others') |
|
1914 |
|
1915 def renameMainScript(self, oldfn, newfn): |
|
1916 """ |
|
1917 Public method to rename the main script. |
|
1918 |
|
1919 @param oldfn old filename (string) |
|
1920 @param newfn new filename of the main script (string) |
|
1921 """ |
|
1922 if self.pdata["MAINSCRIPT"]: |
|
1923 ofn = self.getRelativePath(oldfn) |
|
1924 if ofn != self.pdata["MAINSCRIPT"]: |
|
1925 return |
|
1926 |
|
1927 fn = self.getRelativePath(newfn) |
|
1928 self.pdata["MAINSCRIPT"] = fn |
|
1929 self.setDirty(True) |
|
1930 |
|
1931 def renameFile(self, oldfn, newfn=None): |
|
1932 """ |
|
1933 Public slot to rename a file of the project. |
|
1934 |
|
1935 @param oldfn old filename of the file (string) |
|
1936 @param newfn new filename of the file (string) |
|
1937 @return flag indicating success |
|
1938 """ |
|
1939 fn = self.getRelativePath(oldfn) |
|
1940 isSourceFile = fn in self.pdata["SOURCES"] |
|
1941 |
|
1942 if newfn is None: |
|
1943 newfn = E5FileDialog.getSaveFileName( |
|
1944 None, |
|
1945 self.tr("Rename file"), |
|
1946 oldfn, |
|
1947 "", |
|
1948 E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite)) |
|
1949 if not newfn: |
|
1950 return False |
|
1951 newfn = Utilities.toNativeSeparators(newfn) |
|
1952 |
|
1953 if os.path.exists(newfn): |
|
1954 res = E5MessageBox.yesNo( |
|
1955 self.ui, |
|
1956 self.tr("Rename File"), |
|
1957 self.tr("""<p>The file <b>{0}</b> already exists.""" |
|
1958 """ Overwrite it?</p>""") |
|
1959 .format(newfn), |
|
1960 icon=E5MessageBox.Warning) |
|
1961 if not res: |
|
1962 return False |
|
1963 |
|
1964 try: |
|
1965 os.rename(oldfn, newfn) |
|
1966 except OSError as msg: |
|
1967 E5MessageBox.critical( |
|
1968 self.ui, |
|
1969 self.tr("Rename File"), |
|
1970 self.tr( |
|
1971 """<p>The file <b>{0}</b> could not be renamed.<br />""" |
|
1972 """Reason: {1}</p>""").format(oldfn, str(msg))) |
|
1973 return False |
|
1974 |
|
1975 if fn in self.pdata["SOURCES"] or \ |
|
1976 fn in self.pdata["FORMS"] or \ |
|
1977 fn in self.pdata["TRANSLATIONS"] or \ |
|
1978 fn in self.pdata["INTERFACES"] or \ |
|
1979 fn in self.pdata["PROTOCOLS"] or \ |
|
1980 fn in self.pdata["RESOURCES"] or \ |
|
1981 fn in self.pdata["OTHERS"]: |
|
1982 self.renameFileInPdata(oldfn, newfn, isSourceFile) |
|
1983 |
|
1984 return True |
|
1985 |
|
1986 def renameFileInPdata(self, oldname, newname, isSourceFile=False): |
|
1987 """ |
|
1988 Public method to rename a file in the pdata structure. |
|
1989 |
|
1990 @param oldname old filename (string) |
|
1991 @param newname new filename (string) |
|
1992 @param isSourceFile flag indicating that this is a source file |
|
1993 even if it doesn't have the source extension (boolean) |
|
1994 """ |
|
1995 fn = self.getRelativePath(oldname) |
|
1996 if os.path.dirname(oldname) == os.path.dirname(newname): |
|
1997 self.removeFile(oldname, False) |
|
1998 self.appendFile(newname, isSourceFile, False) |
|
1999 self.__model.renameItem(fn, newname) |
|
2000 else: |
|
2001 self.removeFile(oldname) |
|
2002 self.appendFile(newname, isSourceFile) |
|
2003 self.projectFileRenamed.emit(oldname, newname) |
|
2004 |
|
2005 self.renameMainScript(fn, newname) |
|
2006 |
|
2007 def getFiles(self, start): |
|
2008 """ |
|
2009 Public method to get all files starting with a common prefix. |
|
2010 |
|
2011 @param start prefix (string) |
|
2012 @return list of files starting with a common prefix (list of strings) |
|
2013 """ |
|
2014 filelist = [] |
|
2015 start = self.getRelativePath(start) |
|
2016 for key in ["SOURCES", "FORMS", "INTERFACES", "PROTOCOLS", "RESOURCES", |
|
2017 "OTHERS"]: |
|
2018 for entry in self.pdata[key][:]: |
|
2019 if entry.startswith(start): |
|
2020 filelist.append(os.path.join(self.ppath, entry)) |
|
2021 return filelist |
|
2022 |
|
2023 def __reorganizeFiles(self): |
|
2024 """ |
|
2025 Private method to reorganize files stored in the project. |
|
2026 """ |
|
2027 reorganized = False |
|
2028 |
|
2029 # init data store for the reorganization |
|
2030 newPdata = {} |
|
2031 for key in ["SOURCES", "FORMS", "INTERFACES", "PROTOCOLS", "RESOURCES", |
|
2032 "OTHERS", "TRANSLATIONS"]: |
|
2033 newPdata[key] = [] |
|
2034 |
|
2035 # iterate over all files checking for a reassignment |
|
2036 for key in ["SOURCES", "FORMS", "INTERFACES", "PROTOCOLS", "RESOURCES", |
|
2037 "OTHERS", "TRANSLATIONS"]: |
|
2038 for fn in self.pdata[key][:]: |
|
2039 filetype = key |
|
2040 bfn = os.path.basename(fn) |
|
2041 for pattern in reversed( |
|
2042 sorted(self.pdata["FILETYPES"].keys())): |
|
2043 if fnmatch.fnmatch(bfn, pattern): |
|
2044 filetype = self.pdata["FILETYPES"][pattern] |
|
2045 break |
|
2046 |
|
2047 if filetype != "__IGNORE__": |
|
2048 newPdata[filetype].append(fn) |
|
2049 if filetype != key: |
|
2050 reorganized = True |
|
2051 |
|
2052 if reorganized: |
|
2053 # copy the reorganized files back to the project |
|
2054 for key in ["SOURCES", "FORMS", "INTERFACES", "PROTOCOLS", |
|
2055 "RESOURCES", "OTHERS", "TRANSLATIONS"]: |
|
2056 self.pdata[key] = newPdata[key][:] |
|
2057 |
|
2058 # repopulate the model |
|
2059 self.__model.projectClosed() |
|
2060 self.__model.projectOpened() |
|
2061 |
|
2062 def copyDirectory(self, olddn, newdn): |
|
2063 """ |
|
2064 Public slot to copy a directory. |
|
2065 |
|
2066 @param olddn original directory name (string) |
|
2067 @param newdn new directory name (string) |
|
2068 """ |
|
2069 olddn = self.getRelativePath(olddn) |
|
2070 newdn = self.getRelativePath(newdn) |
|
2071 for key in ["SOURCES", "FORMS", "INTERFACES", "PROTOCOLS", "RESOURCES", |
|
2072 "OTHERS"]: |
|
2073 for entry in self.pdata[key][:]: |
|
2074 if entry.startswith(olddn): |
|
2075 entry = entry.replace(olddn, newdn) |
|
2076 self.appendFile(os.path.join(self.ppath, entry), |
|
2077 key == "SOURCES") |
|
2078 self.setDirty(True) |
|
2079 |
|
2080 def moveDirectory(self, olddn, newdn): |
|
2081 """ |
|
2082 Public slot to move a directory. |
|
2083 |
|
2084 @param olddn old directory name (string) |
|
2085 @param newdn new directory name (string) |
|
2086 """ |
|
2087 olddn = self.getRelativePath(olddn) |
|
2088 newdn = self.getRelativePath(newdn) |
|
2089 typeStrings = [] |
|
2090 for key in ["SOURCES", "FORMS", "INTERFACES", "PROTOCOLS", "RESOURCES", |
|
2091 "OTHERS"]: |
|
2092 for entry in self.pdata[key][:]: |
|
2093 if entry.startswith(olddn): |
|
2094 if key not in typeStrings: |
|
2095 typeStrings.append(key) |
|
2096 self.pdata[key].remove(entry) |
|
2097 entry = entry.replace(olddn, newdn) |
|
2098 self.pdata[key].append(entry) |
|
2099 if key == "OTHERS": |
|
2100 if newdn not in self.otherssubdirs: |
|
2101 self.otherssubdirs.append(newdn) |
|
2102 else: |
|
2103 if newdn not in self.subdirs: |
|
2104 self.subdirs.append(newdn) |
|
2105 self.setDirty(True) |
|
2106 typeString = typeStrings[0] |
|
2107 del typeStrings[0] |
|
2108 self.__model.removeItem(olddn) |
|
2109 self.__model.addNewItem(typeString, newdn, typeStrings) |
|
2110 self.directoryRemoved.emit(olddn) |
|
2111 |
|
2112 def removeFile(self, fn, updateModel=True): |
|
2113 """ |
|
2114 Public slot to remove a file from the project. |
|
2115 |
|
2116 The file is not deleted from the project directory. |
|
2117 |
|
2118 @param fn filename to be removed from the project |
|
2119 @param updateModel flag indicating an update of the model is |
|
2120 requested (boolean) |
|
2121 """ |
|
2122 fn = self.getRelativePath(fn) |
|
2123 dirty = True |
|
2124 if fn in self.pdata["SOURCES"]: |
|
2125 self.pdata["SOURCES"].remove(fn) |
|
2126 self.projectSourceRemoved.emit(fn) |
|
2127 elif fn in self.pdata["FORMS"]: |
|
2128 self.pdata["FORMS"].remove(fn) |
|
2129 self.projectFormRemoved.emit(fn) |
|
2130 elif fn in self.pdata["INTERFACES"]: |
|
2131 self.pdata["INTERFACES"].remove(fn) |
|
2132 self.projectInterfaceRemoved.emit(fn) |
|
2133 elif fn in self.pdata["PROTOCOLS"]: |
|
2134 self.pdata["PROTOCOLS"].remove(fn) |
|
2135 self.projectProtocolRemoved.emit(fn) |
|
2136 elif fn in self.pdata["RESOURCES"]: |
|
2137 self.pdata["RESOURCES"].remove(fn) |
|
2138 self.projectResourceRemoved.emit(fn) |
|
2139 elif fn in self.pdata["OTHERS"]: |
|
2140 self.pdata["OTHERS"].remove(fn) |
|
2141 self.projectOthersRemoved.emit(fn) |
|
2142 elif fn in self.pdata["TRANSLATIONS"]: |
|
2143 self.pdata["TRANSLATIONS"].remove(fn) |
|
2144 self.projectLanguageRemoved.emit(fn) |
|
2145 else: |
|
2146 dirty = False |
|
2147 updateModel and self.__model.removeItem(fn) |
|
2148 if dirty: |
|
2149 self.setDirty(True) |
|
2150 |
|
2151 def removeDirectory(self, dn): |
|
2152 """ |
|
2153 Public slot to remove a directory from the project. |
|
2154 |
|
2155 The directory is not deleted from the project directory. |
|
2156 |
|
2157 @param dn directory name to be removed from the project |
|
2158 """ |
|
2159 dirty = False |
|
2160 dn = self.getRelativePath(dn) |
|
2161 for entry in self.pdata["OTHERS"][:]: |
|
2162 if entry.startswith(dn): |
|
2163 self.pdata["OTHERS"].remove(entry) |
|
2164 dirty = True |
|
2165 if not dn.endswith(os.sep): |
|
2166 dn2 = dn + os.sep |
|
2167 else: |
|
2168 dn2 = dn |
|
2169 for key in ["SOURCES", "FORMS", "INTERFACES", "PROTOCOLS", "RESOURCES", |
|
2170 "TRANSLATIONS", ]: |
|
2171 for entry in self.pdata[key][:]: |
|
2172 if entry.startswith(dn2): |
|
2173 self.pdata[key].remove(entry) |
|
2174 dirty = True |
|
2175 self.__model.removeItem(dn) |
|
2176 if dirty: |
|
2177 self.setDirty(True) |
|
2178 self.directoryRemoved.emit(dn) |
|
2179 |
|
2180 def deleteFile(self, fn): |
|
2181 """ |
|
2182 Public slot to delete a file from the project directory. |
|
2183 |
|
2184 @param fn filename to be deleted from the project |
|
2185 @return flag indicating success (boolean) |
|
2186 """ |
|
2187 try: |
|
2188 from ThirdParty.Send2Trash.send2trash import send2trash as s2t |
|
2189 except ImportError: |
|
2190 s2t = os.remove |
|
2191 |
|
2192 try: |
|
2193 s2t(os.path.join(self.ppath, fn)) |
|
2194 path, ext = os.path.splitext(fn) |
|
2195 if ext == '.ui': |
|
2196 fn2 = os.path.join(self.ppath, '{0}.h'.format(fn)) |
|
2197 if os.path.isfile(fn2): |
|
2198 s2t(fn2) |
|
2199 head, tail = os.path.split(path) |
|
2200 for ext in ['.pyc', '.pyo']: |
|
2201 fn2 = os.path.join(self.ppath, path + ext) |
|
2202 if os.path.isfile(fn2): |
|
2203 s2t(fn2) |
|
2204 pat = os.path.join( |
|
2205 self.ppath, head, |
|
2206 "__pycache__", "{0}.*{1}".format(tail, ext)) |
|
2207 for f in glob.glob(pat): |
|
2208 s2t(f) |
|
2209 except EnvironmentError as err: |
|
2210 E5MessageBox.critical( |
|
2211 self.ui, |
|
2212 self.tr("Delete file"), |
|
2213 self.tr( |
|
2214 "<p>The selected file <b>{0}</b> could not be" |
|
2215 " deleted.</p><p>Reason: {1}</p>").format( |
|
2216 fn, str(err))) |
|
2217 return False |
|
2218 |
|
2219 self.removeFile(fn) |
|
2220 if ext == '.ui': |
|
2221 self.removeFile(fn + '.h') |
|
2222 return True |
|
2223 |
|
2224 def deleteDirectory(self, dn): |
|
2225 """ |
|
2226 Public slot to delete a directory from the project directory. |
|
2227 |
|
2228 @param dn directory name to be removed from the project |
|
2229 @return flag indicating success (boolean) |
|
2230 """ |
|
2231 if not os.path.isabs(dn): |
|
2232 dn = os.path.join(self.ppath, dn) |
|
2233 try: |
|
2234 try: |
|
2235 from ThirdParty.Send2Trash.send2trash import send2trash |
|
2236 send2trash(dn) |
|
2237 except ImportError: |
|
2238 shutil.rmtree(dn, True) |
|
2239 except EnvironmentError as err: |
|
2240 E5MessageBox.critical( |
|
2241 self.ui, |
|
2242 self.tr("Delete directory"), |
|
2243 self.tr( |
|
2244 "<p>The selected directory <b>{0}</b> could not be" |
|
2245 " deleted.</p><p>Reason: {1}</p>").format(dn, str(err))) |
|
2246 return False |
|
2247 |
|
2248 self.removeDirectory(dn) |
|
2249 return True |
|
2250 |
|
2251 def hasEntry(self, fn): |
|
2252 """ |
|
2253 Public method to check the project for a file. |
|
2254 |
|
2255 @param fn filename to be checked (string) |
|
2256 @return flag indicating, if the project contains the file (boolean) |
|
2257 """ |
|
2258 fn = self.getRelativePath(fn) |
|
2259 if fn in self.pdata["SOURCES"] or \ |
|
2260 fn in self.pdata["FORMS"] or \ |
|
2261 fn in self.pdata["INTERFACES"] or \ |
|
2262 fn in self.pdata["PROTOCOLS"] or \ |
|
2263 fn in self.pdata["RESOURCES"] or \ |
|
2264 fn in self.pdata["OTHERS"]: |
|
2265 return True |
|
2266 else: |
|
2267 return False |
|
2268 |
|
2269 def createNewProject(self): |
|
2270 """ |
|
2271 Public slot to built a new project. |
|
2272 |
|
2273 This method displays the new project dialog and initializes |
|
2274 the project object with the data entered. |
|
2275 """ |
|
2276 if not self.checkDirty(): |
|
2277 return |
|
2278 |
|
2279 from .PropertiesDialog import PropertiesDialog |
|
2280 dlg = PropertiesDialog(self, True) |
|
2281 if dlg.exec_() == QDialog.Accepted: |
|
2282 self.closeProject() |
|
2283 dlg.storeData() |
|
2284 self.pdata["VCS"] = 'None' |
|
2285 self.opened = True |
|
2286 if not self.pdata["FILETYPES"]: |
|
2287 self.initFileTypes() |
|
2288 self.setDirty(True) |
|
2289 self.closeAct.setEnabled(True) |
|
2290 self.saveasAct.setEnabled(True) |
|
2291 self.actGrp2.setEnabled(True) |
|
2292 self.propsAct.setEnabled(True) |
|
2293 self.userPropsAct.setEnabled(True) |
|
2294 self.filetypesAct.setEnabled(True) |
|
2295 self.lexersAct.setEnabled(True) |
|
2296 self.sessActGrp.setEnabled(False) |
|
2297 self.dbgActGrp.setEnabled(True) |
|
2298 self.menuDebuggerAct.setEnabled(True) |
|
2299 self.menuSessionAct.setEnabled(False) |
|
2300 self.menuCheckAct.setEnabled(True) |
|
2301 self.menuShowAct.setEnabled(True) |
|
2302 self.menuDiagramAct.setEnabled(True) |
|
2303 self.menuApidocAct.setEnabled(True) |
|
2304 self.menuPackagersAct.setEnabled(True) |
|
2305 self.pluginGrp.setEnabled( |
|
2306 self.pdata["PROJECTTYPE"] in ["E6Plugin"]) |
|
2307 self.addLanguageAct.setEnabled( |
|
2308 bool(self.pdata["TRANSLATIONPATTERN"])) |
|
2309 self.makeGrp.setEnabled( |
|
2310 self.pdata["MAKEPARAMS"]["MakeEnabled"]) |
|
2311 self.menuMakeAct.setEnabled( |
|
2312 self.pdata["MAKEPARAMS"]["MakeEnabled"]) |
|
2313 |
|
2314 self.projectAboutToBeCreated.emit() |
|
2315 |
|
2316 hashStr = str(QCryptographicHash.hash( |
|
2317 QByteArray(self.ppath.encode("utf-8")), |
|
2318 QCryptographicHash.Sha1).toHex(), |
|
2319 encoding="utf-8") |
|
2320 self.pdata["HASH"] = hashStr |
|
2321 |
|
2322 # create the project directory if it doesn't exist already |
|
2323 if not os.path.isdir(self.ppath): |
|
2324 try: |
|
2325 os.makedirs(self.ppath) |
|
2326 except EnvironmentError: |
|
2327 E5MessageBox.critical( |
|
2328 self.ui, |
|
2329 self.tr("Create project directory"), |
|
2330 self.tr( |
|
2331 "<p>The project directory <b>{0}</b> could not" |
|
2332 " be created.</p>") |
|
2333 .format(self.ppath)) |
|
2334 self.vcs = self.initVCS() |
|
2335 return |
|
2336 |
|
2337 # create an empty __init__.py file to make it a Python package |
|
2338 # (only for Python and Python3) |
|
2339 if self.pdata["PROGLANGUAGE"] in \ |
|
2340 ["Python", "Python2", "Python3"]: |
|
2341 fn = os.path.join(self.ppath, "__init__.py") |
|
2342 f = open(fn, "w", encoding="utf-8") |
|
2343 f.close() |
|
2344 self.appendFile(fn, True) |
|
2345 |
|
2346 # create an empty main script file, if a name was given |
|
2347 if self.pdata["MAINSCRIPT"]: |
|
2348 if not os.path.isabs(self.pdata["MAINSCRIPT"]): |
|
2349 ms = os.path.join( |
|
2350 self.ppath, self.pdata["MAINSCRIPT"]) |
|
2351 else: |
|
2352 ms = self.pdata["MAINSCRIPT"] |
|
2353 f = open(ms, "w") |
|
2354 f.close() |
|
2355 self.appendFile(ms, True) |
|
2356 |
|
2357 if self.pdata["MAKEPARAMS"]["MakeEnabled"]: |
|
2358 mf = self.pdata["MAKEPARAMS"]["MakeFile"] |
|
2359 if mf: |
|
2360 if not os.path.isabs(mf): |
|
2361 mf = os.path.join(self.ppath, mf) |
|
2362 else: |
|
2363 mf = os.path.join(self.ppath, Project.DefaultMakefile) |
|
2364 f = open(mf, "w") |
|
2365 f.close() |
|
2366 self.appendFile(mf) |
|
2367 |
|
2368 tpd = os.path.join(self.ppath, self.translationsRoot) |
|
2369 if not self.translationsRoot.endswith(os.sep): |
|
2370 tpd = os.path.dirname(tpd) |
|
2371 if not os.path.isdir(tpd): |
|
2372 os.makedirs(tpd) |
|
2373 if self.pdata["TRANSLATIONSBINPATH"]: |
|
2374 tpd = os.path.join( |
|
2375 self.ppath, self.pdata["TRANSLATIONSBINPATH"]) |
|
2376 if not os.path.isdir(tpd): |
|
2377 os.makedirs(tpd) |
|
2378 |
|
2379 # create management directory if not present |
|
2380 self.createProjectManagementDir() |
|
2381 |
|
2382 self.saveProject() |
|
2383 else: |
|
2384 try: |
|
2385 # create management directory if not present |
|
2386 self.createProjectManagementDir() |
|
2387 except EnvironmentError: |
|
2388 E5MessageBox.critical( |
|
2389 self.ui, |
|
2390 self.tr("Create project management directory"), |
|
2391 self.tr( |
|
2392 "<p>The project directory <b>{0}</b> is not" |
|
2393 " writable.</p>") |
|
2394 .format(self.ppath)) |
|
2395 return |
|
2396 |
|
2397 if self.pdata["MAINSCRIPT"]: |
|
2398 if not os.path.isabs(self.pdata["MAINSCRIPT"]): |
|
2399 ms = os.path.join( |
|
2400 self.ppath, self.pdata["MAINSCRIPT"]) |
|
2401 else: |
|
2402 ms = self.pdata["MAINSCRIPT"] |
|
2403 if not os.path.exists(ms): |
|
2404 try: |
|
2405 f = open(ms, "w") |
|
2406 f.close() |
|
2407 except EnvironmentError as err: |
|
2408 E5MessageBox.critical( |
|
2409 self.ui, |
|
2410 self.tr("Create main script"), |
|
2411 self.tr( |
|
2412 "<p>The mainscript <b>{0}</b> could not" |
|
2413 " be created.<br/>Reason: {1}</p>") |
|
2414 .format(ms, str(err))) |
|
2415 self.appendFile(ms, True) |
|
2416 else: |
|
2417 ms = "" |
|
2418 |
|
2419 if self.pdata["MAKEPARAMS"]["MakeEnabled"]: |
|
2420 mf = self.pdata["MAKEPARAMS"]["MakeFile"] |
|
2421 if mf: |
|
2422 if not os.path.isabs(mf): |
|
2423 mf = os.path.join(self.ppath, mf) |
|
2424 else: |
|
2425 mf = os.path.join(self.ppath, Project.DefaultMakefile) |
|
2426 if not os.path.exists(mf): |
|
2427 try: |
|
2428 f = open(mf, "w") |
|
2429 f.close() |
|
2430 except EnvironmentError as err: |
|
2431 E5MessageBox.critical( |
|
2432 self.ui, |
|
2433 self.tr("Create Makefile"), |
|
2434 self.tr( |
|
2435 "<p>The makefile <b>{0}</b> could not" |
|
2436 " be created.<br/>Reason: {1}</p>") |
|
2437 .format(mf, str(err))) |
|
2438 self.appendFile(mf) |
|
2439 |
|
2440 # add existing files to the project |
|
2441 res = E5MessageBox.yesNo( |
|
2442 self.ui, |
|
2443 self.tr("New Project"), |
|
2444 self.tr("""Add existing files to the project?"""), |
|
2445 yesDefault=True) |
|
2446 if res: |
|
2447 self.newProjectAddFiles(ms) |
|
2448 # create an empty __init__.py file to make it a Python package |
|
2449 # if none exists (only for Python and Python3) |
|
2450 if self.pdata["PROGLANGUAGE"] in \ |
|
2451 ["Python", "Python2", "Python3"]: |
|
2452 fn = os.path.join(self.ppath, "__init__.py") |
|
2453 if not os.path.exists(fn): |
|
2454 f = open(fn, "w", encoding="utf-8") |
|
2455 f.close() |
|
2456 self.appendFile(fn, True) |
|
2457 self.saveProject() |
|
2458 |
|
2459 # check, if the existing project directory is already under |
|
2460 # VCS control |
|
2461 pluginManager = e5App().getObject("PluginManager") |
|
2462 for indicator, vcsData in list( |
|
2463 pluginManager.getVcsSystemIndicators().items()): |
|
2464 if os.path.exists(os.path.join(self.ppath, indicator)): |
|
2465 if len(vcsData) > 1: |
|
2466 vcsList = [] |
|
2467 for _vcsSystemStr, vcsSystemDisplay in vcsData: |
|
2468 vcsList.append(vcsSystemDisplay) |
|
2469 res, vcs_ok = QInputDialog.getItem( |
|
2470 None, |
|
2471 self.tr("New Project"), |
|
2472 self.tr("Select Version Control System"), |
|
2473 vcsList, |
|
2474 0, False) |
|
2475 if vcs_ok: |
|
2476 for vcsSystemStr, vcsSystemDisplay in vcsData: |
|
2477 if res == vcsSystemDisplay: |
|
2478 vcsSystem = vcsSystemStr |
|
2479 break |
|
2480 else: |
|
2481 vcsSystem = "None" |
|
2482 else: |
|
2483 vcsSystem = "None" |
|
2484 else: |
|
2485 vcsSystem = vcsData[0][1] |
|
2486 self.pdata["VCS"] = vcsSystem |
|
2487 self.vcs = self.initVCS() |
|
2488 self.setDirty(True) |
|
2489 if self.vcs is not None: |
|
2490 # edit VCS command options |
|
2491 if self.vcs.vcsSupportCommandOptions(): |
|
2492 vcores = E5MessageBox.yesNo( |
|
2493 self.ui, |
|
2494 self.tr("New Project"), |
|
2495 self.tr( |
|
2496 """Would you like to edit the VCS""" |
|
2497 """ command options?""")) |
|
2498 else: |
|
2499 vcores = False |
|
2500 if vcores: |
|
2501 from VCS.CommandOptionsDialog import \ |
|
2502 VcsCommandOptionsDialog |
|
2503 codlg = VcsCommandOptionsDialog(self.vcs) |
|
2504 if codlg.exec_() == QDialog.Accepted: |
|
2505 self.vcs.vcsSetOptions(codlg.getOptions()) |
|
2506 # add project file to repository |
|
2507 if res == 0: |
|
2508 apres = E5MessageBox.yesNo( |
|
2509 self.ui, |
|
2510 self.tr("New project"), |
|
2511 self.tr( |
|
2512 "Shall the project file be added" |
|
2513 " to the repository?"), |
|
2514 yesDefault=True) |
|
2515 if apres: |
|
2516 self.saveProject() |
|
2517 self.vcs.vcsAdd(self.pfile) |
|
2518 else: |
|
2519 self.pdata["VCS"] = 'None' |
|
2520 self.saveProject() |
|
2521 break |
|
2522 |
|
2523 # put the project under VCS control |
|
2524 if self.vcs is None and self.vcsSoftwareAvailable() and \ |
|
2525 self.vcsRequested: |
|
2526 vcsSystemsDict = e5App().getObject("PluginManager")\ |
|
2527 .getPluginDisplayStrings("version_control") |
|
2528 vcsSystemsDisplay = [self.tr("None")] |
|
2529 keys = sorted(vcsSystemsDict.keys()) |
|
2530 for key in keys: |
|
2531 vcsSystemsDisplay.append(vcsSystemsDict[key]) |
|
2532 vcsSelected, ok = QInputDialog.getItem( |
|
2533 None, |
|
2534 self.tr("New Project"), |
|
2535 self.tr( |
|
2536 "Select version control system for the project"), |
|
2537 vcsSystemsDisplay, |
|
2538 0, False) |
|
2539 if ok and vcsSelected != self.tr("None"): |
|
2540 for vcsSystem, vcsSystemDisplay in vcsSystemsDict.items(): |
|
2541 if vcsSystemDisplay == vcsSelected: |
|
2542 self.pdata["VCS"] = vcsSystem |
|
2543 break |
|
2544 else: |
|
2545 self.pdata["VCS"] = 'None' |
|
2546 else: |
|
2547 self.pdata["VCS"] = 'None' |
|
2548 self.vcs = self.initVCS() |
|
2549 if self.vcs is not None: |
|
2550 vcsdlg = self.vcs.vcsOptionsDialog(self, self.name) |
|
2551 if vcsdlg.exec_() == QDialog.Accepted: |
|
2552 vcsDataDict = vcsdlg.getData() |
|
2553 else: |
|
2554 self.pdata["VCS"] = 'None' |
|
2555 self.vcs = self.initVCS() |
|
2556 self.setDirty(True) |
|
2557 if self.vcs is not None: |
|
2558 # edit VCS command options |
|
2559 if self.vcs.vcsSupportCommandOptions(): |
|
2560 vcores = E5MessageBox.yesNo( |
|
2561 self.ui, |
|
2562 self.tr("New Project"), |
|
2563 self.tr( |
|
2564 """Would you like to edit the VCS command""" |
|
2565 """ options?""")) |
|
2566 else: |
|
2567 vcores = False |
|
2568 if vcores: |
|
2569 from VCS.CommandOptionsDialog import \ |
|
2570 VcsCommandOptionsDialog |
|
2571 codlg = VcsCommandOptionsDialog(self.vcs) |
|
2572 if codlg.exec_() == QDialog.Accepted: |
|
2573 self.vcs.vcsSetOptions(codlg.getOptions()) |
|
2574 |
|
2575 # create the project in the VCS |
|
2576 self.vcs.vcsSetDataFromDict(vcsDataDict) |
|
2577 self.saveProject() |
|
2578 self.vcs.vcsConvertProject(vcsDataDict, self) |
|
2579 else: |
|
2580 self.newProjectHooks.emit() |
|
2581 self.newProject.emit() |
|
2582 |
|
2583 else: |
|
2584 self.newProjectHooks.emit() |
|
2585 self.newProject.emit() |
|
2586 |
|
2587 def newProjectAddFiles(self, mainscript): |
|
2588 """ |
|
2589 Public method to add files to a new project. |
|
2590 |
|
2591 @param mainscript name of the mainscript (string) |
|
2592 """ |
|
2593 # Show the file type associations for the user to change |
|
2594 self.__showFiletypeAssociations() |
|
2595 |
|
2596 QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) |
|
2597 QApplication.processEvents() |
|
2598 |
|
2599 # search the project directory for files with known extensions |
|
2600 filespecs = list(self.pdata["FILETYPES"].keys()) |
|
2601 for filespec in filespecs: |
|
2602 files = Utilities.direntries(self.ppath, True, filespec) |
|
2603 for file in files: |
|
2604 self.appendFile(file) |
|
2605 |
|
2606 # special handling for translation files |
|
2607 if self.translationsRoot: |
|
2608 tpd = os.path.join(self.ppath, self.translationsRoot) |
|
2609 if not self.translationsRoot.endswith(os.sep): |
|
2610 tpd = os.path.dirname(tpd) |
|
2611 else: |
|
2612 tpd = self.ppath |
|
2613 tslist = [] |
|
2614 if self.pdata["TRANSLATIONPATTERN"]: |
|
2615 pattern = os.path.basename(self.pdata["TRANSLATIONPATTERN"]) |
|
2616 if "%language%" in pattern: |
|
2617 pattern = pattern.replace("%language%", "*") |
|
2618 else: |
|
2619 tpd = self.pdata["TRANSLATIONPATTERN"].split( |
|
2620 "%language%")[0] |
|
2621 else: |
|
2622 pattern = "*.ts" |
|
2623 tslist.extend(Utilities.direntries(tpd, True, pattern)) |
|
2624 pattern = self.__binaryTranslationFile(pattern) |
|
2625 if pattern: |
|
2626 tslist.extend(Utilities.direntries(tpd, True, pattern)) |
|
2627 if tslist: |
|
2628 if '_' in os.path.basename(tslist[0]): |
|
2629 # the first entry determines the mainscript name |
|
2630 mainscriptname = os.path.splitext(mainscript)[0] or \ |
|
2631 os.path.basename(tslist[0]).split('_')[0] |
|
2632 self.pdata["TRANSLATIONPATTERN"] = os.path.join( |
|
2633 os.path.dirname(tslist[0]), |
|
2634 "{0}_%language%{1}".format( |
|
2635 os.path.basename(tslist[0]).split('_')[0], |
|
2636 os.path.splitext(tslist[0])[1])) |
|
2637 else: |
|
2638 mainscriptname = "" |
|
2639 pattern, ok = QInputDialog.getText( |
|
2640 None, |
|
2641 self.tr("Translation Pattern"), |
|
2642 self.tr( |
|
2643 "Enter the path pattern for translation files " |
|
2644 "(use '%language%' in place of the language code):"), |
|
2645 # __IGNORE_WARNING_M601__ |
|
2646 QLineEdit.Normal, |
|
2647 tslist[0]) |
|
2648 if pattern: |
|
2649 self.pdata["TRANSLATIONPATTERN"] = pattern |
|
2650 if self.pdata["TRANSLATIONPATTERN"]: |
|
2651 self.pdata["TRANSLATIONPATTERN"] = \ |
|
2652 self.getRelativePath(self.pdata["TRANSLATIONPATTERN"]) |
|
2653 pattern = self.pdata["TRANSLATIONPATTERN"]\ |
|
2654 .replace("%language%", "*") |
|
2655 for ts in tslist: |
|
2656 if fnmatch.fnmatch(ts, pattern): |
|
2657 self.pdata["TRANSLATIONS"].append(ts) |
|
2658 self.projectLanguageAdded.emit(ts) |
|
2659 if self.pdata["TRANSLATIONSBINPATH"]: |
|
2660 tpd = os.path.join(self.ppath, |
|
2661 self.pdata["TRANSLATIONSBINPATH"]) |
|
2662 pattern = os.path.basename( |
|
2663 self.pdata["TRANSLATIONPATTERN"])\ |
|
2664 .replace("%language%", "*") |
|
2665 pattern = self.__binaryTranslationFile(pattern) |
|
2666 qmlist = Utilities.direntries(tpd, True, pattern) |
|
2667 for qm in qmlist: |
|
2668 self.pdata["TRANSLATIONS"].append(qm) |
|
2669 self.projectLanguageAdded.emit(qm) |
|
2670 if not self.pdata["MAINSCRIPT"] and bool(mainscriptname): |
|
2671 if self.pdata["PROGLANGUAGE"] in \ |
|
2672 ["Python", "Python2", "Python3"]: |
|
2673 self.pdata["MAINSCRIPT"] = '{0}.py'.format(mainscriptname) |
|
2674 elif self.pdata["PROGLANGUAGE"] == "Ruby": |
|
2675 self.pdata["MAINSCRIPT"] = '{0}.rb'.format(mainscriptname) |
|
2676 self.setDirty(True) |
|
2677 QApplication.restoreOverrideCursor() |
|
2678 |
|
2679 def __showProperties(self): |
|
2680 """ |
|
2681 Private slot to display the properties dialog. |
|
2682 """ |
|
2683 from .PropertiesDialog import PropertiesDialog |
|
2684 dlg = PropertiesDialog(self, False) |
|
2685 if dlg.exec_() == QDialog.Accepted: |
|
2686 projectType = self.pdata["PROJECTTYPE"] |
|
2687 dlg.storeData() |
|
2688 self.setDirty(True) |
|
2689 if self.pdata["MAINSCRIPT"]: |
|
2690 if not os.path.isabs(self.pdata["MAINSCRIPT"]): |
|
2691 ms = os.path.join( |
|
2692 self.ppath, self.pdata["MAINSCRIPT"]) |
|
2693 else: |
|
2694 ms = self.pdata["MAINSCRIPT"] |
|
2695 if os.path.exists(ms): |
|
2696 self.appendFile(ms) |
|
2697 |
|
2698 if self.pdata["MAKEPARAMS"]["MakeEnabled"]: |
|
2699 mf = self.pdata["MAKEPARAMS"]["MakeFile"] |
|
2700 if mf: |
|
2701 if not os.path.isabs(mf): |
|
2702 mf = os.path.join(self.ppath, mf) |
|
2703 else: |
|
2704 mf = os.path.join(self.ppath, Project.DefaultMakefile) |
|
2705 if not os.path.exists(mf): |
|
2706 try: |
|
2707 f = open(mf, "w") |
|
2708 f.close() |
|
2709 except EnvironmentError as err: |
|
2710 E5MessageBox.critical( |
|
2711 self.ui, |
|
2712 self.tr("Create Makefile"), |
|
2713 self.tr( |
|
2714 "<p>The makefile <b>{0}</b> could not" |
|
2715 " be created.<br/>Reason: {1}</p>") |
|
2716 .format(mf, str(err))) |
|
2717 self.appendFile(mf) |
|
2718 |
|
2719 if self.pdata["PROJECTTYPE"] != projectType: |
|
2720 # reinitialize filetype associations |
|
2721 self.initFileTypes() |
|
2722 |
|
2723 if self.translationsRoot: |
|
2724 tp = os.path.join(self.ppath, self.translationsRoot) |
|
2725 if not self.translationsRoot.endswith(os.sep): |
|
2726 tp = os.path.dirname(tp) |
|
2727 else: |
|
2728 tp = self.ppath |
|
2729 if not os.path.isdir(tp): |
|
2730 os.makedirs(tp) |
|
2731 if tp != self.ppath and tp not in self.subdirs: |
|
2732 self.subdirs.append(tp) |
|
2733 |
|
2734 if self.pdata["TRANSLATIONSBINPATH"]: |
|
2735 tp = os.path.join( |
|
2736 self.ppath, self.pdata["TRANSLATIONSBINPATH"]) |
|
2737 if not os.path.isdir(tp): |
|
2738 os.makedirs(tp) |
|
2739 if tp != self.ppath and tp not in self.subdirs: |
|
2740 self.subdirs.append(tp) |
|
2741 |
|
2742 self.pluginGrp.setEnabled( |
|
2743 self.pdata["PROJECTTYPE"] in ["E6Plugin"]) |
|
2744 |
|
2745 self.__model.projectPropertiesChanged() |
|
2746 self.projectPropertiesChanged.emit() |
|
2747 |
|
2748 if self.pdata["PROJECTTYPE"] != projectType: |
|
2749 self.__reorganizeFiles() |
|
2750 |
|
2751 def __showUserProperties(self): |
|
2752 """ |
|
2753 Private slot to display the user specific properties dialog. |
|
2754 """ |
|
2755 vcsSystem = self.pdata["VCS"] or None |
|
2756 vcsSystemOverride = self.pudata["VCSOVERRIDE"] or None |
|
2757 |
|
2758 from .UserPropertiesDialog import UserPropertiesDialog |
|
2759 dlg = UserPropertiesDialog(self) |
|
2760 if dlg.exec_() == QDialog.Accepted: |
|
2761 dlg.storeData() |
|
2762 |
|
2763 if (self.pdata["VCS"] and |
|
2764 self.pdata["VCS"] != vcsSystem) or \ |
|
2765 (self.pudata["VCSOVERRIDE"] and |
|
2766 self.pudata["VCSOVERRIDE"] != vcsSystemOverride) or \ |
|
2767 (vcsSystemOverride is not None and |
|
2768 not self.pudata["VCSOVERRIDE"]): |
|
2769 # stop the VCS monitor thread and shutdown VCS |
|
2770 if self.vcs is not None: |
|
2771 self.vcs.stopStatusMonitor() |
|
2772 self.vcs.vcsShutdown() |
|
2773 self.vcs.deleteLater() |
|
2774 self.vcs = None |
|
2775 e5App().getObject("PluginManager").deactivateVcsPlugins() |
|
2776 # reinit VCS |
|
2777 self.vcs = self.initVCS() |
|
2778 # start the VCS monitor thread |
|
2779 if self.vcs is not None: |
|
2780 self.vcs.startStatusMonitor(self) |
|
2781 self.vcs.vcsStatusMonitorData.connect( |
|
2782 self.__model.changeVCSStates) |
|
2783 self.vcs.vcsStatusMonitorStatus.connect( |
|
2784 self.__statusMonitorStatus) |
|
2785 self.vcs.vcsStatusMonitorInfo.connect( |
|
2786 self.vcsStatusMonitorInfo) |
|
2787 self.vcs.vcsStatusChanged.connect(self.__vcsStatusChanged) |
|
2788 self.reinitVCS.emit() |
|
2789 |
|
2790 if self.pudata["VCSSTATUSMONITORINTERVAL"]: |
|
2791 self.setStatusMonitorInterval( |
|
2792 self.pudata["VCSSTATUSMONITORINTERVAL"]) |
|
2793 else: |
|
2794 self.setStatusMonitorInterval( |
|
2795 Preferences.getVCS("StatusMonitorInterval")) |
|
2796 |
|
2797 def __showFiletypeAssociations(self): |
|
2798 """ |
|
2799 Private slot to display the filetype association dialog. |
|
2800 """ |
|
2801 from .FiletypeAssociationDialog import FiletypeAssociationDialog |
|
2802 dlg = FiletypeAssociationDialog(self) |
|
2803 if dlg.exec_() == QDialog.Accepted: |
|
2804 dlg.transferData() |
|
2805 self.setDirty(True) |
|
2806 self.__reorganizeFiles() |
|
2807 |
|
2808 def __showLexerAssociations(self): |
|
2809 """ |
|
2810 Private slot to display the lexer association dialog. |
|
2811 """ |
|
2812 from .LexerAssociationDialog import LexerAssociationDialog |
|
2813 dlg = LexerAssociationDialog(self) |
|
2814 if dlg.exec_() == QDialog.Accepted: |
|
2815 dlg.transferData() |
|
2816 self.setDirty(True) |
|
2817 self.lexerAssociationsChanged.emit() |
|
2818 |
|
2819 def getEditorLexerAssoc(self, filename): |
|
2820 """ |
|
2821 Public method to retrieve a lexer association. |
|
2822 |
|
2823 @param filename filename used to determine the associated lexer |
|
2824 language (string) |
|
2825 @return the requested lexer language (string) |
|
2826 """ |
|
2827 # try user settings first |
|
2828 for pattern, language in list(self.pdata["LEXERASSOCS"].items()): |
|
2829 if fnmatch.fnmatch(filename, pattern): |
|
2830 return language |
|
2831 |
|
2832 # try project type specific defaults next |
|
2833 projectType = self.pdata["PROJECTTYPE"] |
|
2834 try: |
|
2835 if self.__lexerAssociationCallbacks[projectType] is not None: |
|
2836 return self.__lexerAssociationCallbacks[projectType](filename) |
|
2837 except KeyError: |
|
2838 pass |
|
2839 |
|
2840 # return empty string to signal to use the global setting |
|
2841 return "" |
|
2842 |
|
2843 @pyqtSlot() |
|
2844 @pyqtSlot(str) |
|
2845 def openProject(self, fn=None, restoreSession=True, reopen=False): |
|
2846 """ |
|
2847 Public slot to open a project. |
|
2848 |
|
2849 @param fn optional filename of the project file to be read |
|
2850 @param restoreSession flag indicating to restore the project |
|
2851 session (boolean) |
|
2852 @keyparam reopen flag indicating a reopening of the project (boolean) |
|
2853 """ |
|
2854 if not self.checkDirty(): |
|
2855 return |
|
2856 |
|
2857 if fn is None: |
|
2858 fn = E5FileDialog.getOpenFileName( |
|
2859 self.parent(), |
|
2860 self.tr("Open project"), |
|
2861 Preferences.getMultiProject("Workspace") or |
|
2862 Utilities.getHomeDir(), |
|
2863 self.tr("Project Files (*.e4p)")) |
|
2864 |
|
2865 QApplication.processEvents() |
|
2866 |
|
2867 if fn: |
|
2868 if self.closeProject(): |
|
2869 QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) |
|
2870 QApplication.processEvents() |
|
2871 if self.__readProject(fn): |
|
2872 self.opened = True |
|
2873 if not self.pdata["FILETYPES"]: |
|
2874 self.initFileTypes() |
|
2875 else: |
|
2876 self.updateFileTypes() |
|
2877 |
|
2878 QApplication.restoreOverrideCursor() |
|
2879 QApplication.processEvents() |
|
2880 |
|
2881 try: |
|
2882 # create management directory if not present |
|
2883 self.createProjectManagementDir() |
|
2884 except EnvironmentError: |
|
2885 E5MessageBox.critical( |
|
2886 self.ui, |
|
2887 self.tr("Create project management directory"), |
|
2888 self.tr( |
|
2889 "<p>The project directory <b>{0}</b> is not" |
|
2890 " writable.</p>") |
|
2891 .format(self.ppath)) |
|
2892 return |
|
2893 |
|
2894 # read a user specific project file |
|
2895 self.__readUserProperties() |
|
2896 |
|
2897 QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) |
|
2898 QApplication.processEvents() |
|
2899 |
|
2900 oldState = self.isDirty() |
|
2901 self.vcs = self.initVCS() |
|
2902 if self.vcs is None and self.isDirty() == oldState: |
|
2903 # check, if project is version controlled |
|
2904 pluginManager = e5App().getObject("PluginManager") |
|
2905 for indicator, vcsData in \ |
|
2906 pluginManager.getVcsSystemIndicators().items(): |
|
2907 if os.path.exists( |
|
2908 os.path.join(self.ppath, indicator)): |
|
2909 if len(vcsData) > 1: |
|
2910 vcsList = [] |
|
2911 for _vcsSystemStr, vcsSystemDisplay in \ |
|
2912 vcsData: |
|
2913 vcsList.append(vcsSystemDisplay) |
|
2914 QApplication.restoreOverrideCursor() |
|
2915 res, vcs_ok = QInputDialog.getItem( |
|
2916 None, |
|
2917 self.tr("New Project"), |
|
2918 self.tr( |
|
2919 "Select Version Control System"), |
|
2920 vcsList, |
|
2921 0, False) |
|
2922 QApplication.setOverrideCursor( |
|
2923 QCursor(Qt.WaitCursor)) |
|
2924 QApplication.processEvents() |
|
2925 if vcs_ok: |
|
2926 for vcsSystemStr, vcsSystemDisplay in \ |
|
2927 vcsData: |
|
2928 if res == vcsSystemDisplay: |
|
2929 vcsSystem = vcsSystemStr |
|
2930 break |
|
2931 else: |
|
2932 vcsSystem = "None" |
|
2933 else: |
|
2934 vcsSystem = "None" |
|
2935 else: |
|
2936 vcsSystem = vcsData[0][0] |
|
2937 self.pdata["VCS"] = vcsSystem |
|
2938 self.vcs = self.initVCS() |
|
2939 self.setDirty(True) |
|
2940 if self.vcs is not None and \ |
|
2941 (self.vcs.vcsRegisteredState(self.ppath) != |
|
2942 self.vcs.canBeCommitted): |
|
2943 self.pdata["VCS"] = 'None' |
|
2944 self.vcs = self.initVCS() |
|
2945 self.closeAct.setEnabled(True) |
|
2946 self.saveasAct.setEnabled(True) |
|
2947 self.actGrp2.setEnabled(True) |
|
2948 self.propsAct.setEnabled(True) |
|
2949 self.userPropsAct.setEnabled(True) |
|
2950 self.filetypesAct.setEnabled(True) |
|
2951 self.lexersAct.setEnabled(True) |
|
2952 self.sessActGrp.setEnabled(True) |
|
2953 self.dbgActGrp.setEnabled(True) |
|
2954 self.menuDebuggerAct.setEnabled(True) |
|
2955 self.menuSessionAct.setEnabled(True) |
|
2956 self.menuCheckAct.setEnabled(True) |
|
2957 self.menuShowAct.setEnabled(True) |
|
2958 self.menuDiagramAct.setEnabled(True) |
|
2959 self.menuApidocAct.setEnabled(True) |
|
2960 self.menuPackagersAct.setEnabled(True) |
|
2961 self.pluginGrp.setEnabled( |
|
2962 self.pdata["PROJECTTYPE"] in ["E6Plugin"]) |
|
2963 self.addLanguageAct.setEnabled( |
|
2964 bool(self.pdata["TRANSLATIONPATTERN"])) |
|
2965 self.makeGrp.setEnabled( |
|
2966 self.pdata["MAKEPARAMS"]["MakeEnabled"]) |
|
2967 self.menuMakeAct.setEnabled( |
|
2968 self.pdata["MAKEPARAMS"]["MakeEnabled"]) |
|
2969 |
|
2970 # open a project debugger properties file being quiet |
|
2971 # about errors |
|
2972 if Preferences.getProject("AutoLoadDbgProperties"): |
|
2973 self.__readDebugProperties(True) |
|
2974 |
|
2975 self.__model.projectOpened() |
|
2976 self.projectOpenedHooks.emit() |
|
2977 self.projectOpened.emit() |
|
2978 |
|
2979 QApplication.restoreOverrideCursor() |
|
2980 |
|
2981 if Preferences.getProject("SearchNewFiles"): |
|
2982 self.__doSearchNewFiles() |
|
2983 |
|
2984 # read a project tasks file |
|
2985 self.__readTasks() |
|
2986 self.ui.taskViewer.setProjectOpen(True) |
|
2987 # rescan project tasks |
|
2988 if Preferences.getProject("TasksProjectRescanOnOpen"): |
|
2989 e5App().getObject("TaskViewer")\ |
|
2990 .regenerateProjectTasks(quiet=True) |
|
2991 |
|
2992 if restoreSession: |
|
2993 # open the main script |
|
2994 if self.pdata["MAINSCRIPT"]: |
|
2995 if not os.path.isabs(self.pdata["MAINSCRIPT"]): |
|
2996 ms = os.path.join( |
|
2997 self.ppath, self.pdata["MAINSCRIPT"]) |
|
2998 else: |
|
2999 ms = self.pdata["MAINSCRIPT"] |
|
3000 self.sourceFile.emit(ms) |
|
3001 |
|
3002 # open a project session file being quiet about errors |
|
3003 if reopen: |
|
3004 self.__readSession(quiet=True, indicator="_tmp") |
|
3005 elif Preferences.getProject("AutoLoadSession"): |
|
3006 self.__readSession(quiet=True) |
|
3007 |
|
3008 # start the VCS monitor thread |
|
3009 if self.vcs is not None: |
|
3010 self.vcs.startStatusMonitor(self) |
|
3011 self.vcs.vcsStatusMonitorData.connect( |
|
3012 self.__model.changeVCSStates) |
|
3013 self.vcs.vcsStatusMonitorStatus.connect( |
|
3014 self.__statusMonitorStatus) |
|
3015 self.vcs.vcsStatusMonitorInfo.connect( |
|
3016 self.vcsStatusMonitorInfo) |
|
3017 self.vcs.vcsStatusChanged.connect( |
|
3018 self.__vcsStatusChanged) |
|
3019 else: |
|
3020 QApplication.restoreOverrideCursor() |
|
3021 |
|
3022 def reopenProject(self): |
|
3023 """ |
|
3024 Public slot to reopen the current project. |
|
3025 """ |
|
3026 projectFile = self.pfile |
|
3027 res = self.closeProject(reopen=True) |
|
3028 if res: |
|
3029 self.openProject(projectFile, reopen=True) |
|
3030 |
|
3031 def saveProject(self): |
|
3032 """ |
|
3033 Public slot to save the current project. |
|
3034 |
|
3035 @return flag indicating success |
|
3036 """ |
|
3037 if self.isDirty(): |
|
3038 if len(self.pfile) > 0: |
|
3039 ok = self.__writeProject() |
|
3040 else: |
|
3041 ok = self.saveProjectAs() |
|
3042 else: |
|
3043 ok = True |
|
3044 self.sessActGrp.setEnabled(ok) |
|
3045 self.menuSessionAct.setEnabled(ok) |
|
3046 return ok |
|
3047 |
|
3048 def saveProjectAs(self): |
|
3049 """ |
|
3050 Public slot to save the current project to a different file. |
|
3051 |
|
3052 @return flag indicating success (boolean) |
|
3053 """ |
|
3054 defaultFilter = self.tr("Project Files (*.e4p)") |
|
3055 if self.ppath: |
|
3056 defaultPath = self.ppath |
|
3057 else: |
|
3058 defaultPath = Preferences.getMultiProject("Workspace") or \ |
|
3059 Utilities.getHomeDir() |
|
3060 fn, selectedFilter = E5FileDialog.getSaveFileNameAndFilter( |
|
3061 self.parent(), |
|
3062 self.tr("Save project as"), |
|
3063 defaultPath, |
|
3064 self.tr("Project Files (*.e4p)"), |
|
3065 defaultFilter, |
|
3066 E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite)) |
|
3067 |
|
3068 if fn: |
|
3069 ext = QFileInfo(fn).suffix() |
|
3070 if not ext: |
|
3071 ex = selectedFilter.split("(*")[1].split(")")[0] |
|
3072 if ex: |
|
3073 fn += ex |
|
3074 if QFileInfo(fn).exists(): |
|
3075 res = E5MessageBox.yesNo( |
|
3076 self.ui, |
|
3077 self.tr("Save File"), |
|
3078 self.tr("""<p>The file <b>{0}</b> already exists.""" |
|
3079 """ Overwrite it?</p>""").format(fn), |
|
3080 icon=E5MessageBox.Warning) |
|
3081 if not res: |
|
3082 return False |
|
3083 |
|
3084 self.name = QFileInfo(fn).baseName() |
|
3085 ok = self.__writeProject(fn) |
|
3086 |
|
3087 if ok: |
|
3088 # create management directory if not present |
|
3089 self.createProjectManagementDir() |
|
3090 |
|
3091 # now save the tasks |
|
3092 self.writeTasks() |
|
3093 |
|
3094 self.sessActGrp.setEnabled(ok) |
|
3095 self.menuSessionAct.setEnabled(ok) |
|
3096 self.projectClosedHooks.emit() |
|
3097 self.projectClosed.emit() |
|
3098 self.projectOpenedHooks.emit() |
|
3099 self.projectOpened.emit() |
|
3100 return ok |
|
3101 else: |
|
3102 return False |
|
3103 |
|
3104 def checkDirty(self): |
|
3105 """ |
|
3106 Public method to check dirty status and open a message window. |
|
3107 |
|
3108 @return flag indicating whether this operation was successful (boolean) |
|
3109 """ |
|
3110 if self.isDirty(): |
|
3111 res = E5MessageBox.okToClearData( |
|
3112 self.parent(), |
|
3113 self.tr("Close Project"), |
|
3114 self.tr("The current project has unsaved changes."), |
|
3115 self.saveProject) |
|
3116 if res: |
|
3117 self.setDirty(False) |
|
3118 return res |
|
3119 |
|
3120 return True |
|
3121 |
|
3122 def __closeAllWindows(self): |
|
3123 """ |
|
3124 Private method to close all project related windows. |
|
3125 """ |
|
3126 self.codemetrics and self.codemetrics.close() |
|
3127 self.codecoverage and self.codecoverage.close() |
|
3128 self.profiledata and self.profiledata.close() |
|
3129 self.applicationDiagram and self.applicationDiagram.close() |
|
3130 self.loadedDiagram and self.loadedDiagram.close() |
|
3131 |
|
3132 @pyqtSlot() |
|
3133 def closeProject(self, reopen=False, noSave=False): |
|
3134 """ |
|
3135 Public slot to close the current project. |
|
3136 |
|
3137 @keyparam reopen flag indicating a reopening of the project (boolean) |
|
3138 @keyparam noSave flag indicating to not perform save actions (boolean) |
|
3139 @return flag indicating success (boolean) |
|
3140 """ |
|
3141 # save the list of recently opened projects |
|
3142 self.__saveRecent() |
|
3143 |
|
3144 if not self.isOpen(): |
|
3145 return True |
|
3146 |
|
3147 if not self.checkDirty(): |
|
3148 return False |
|
3149 |
|
3150 e5App().getObject("TaskViewer").stopProjectTaskExtraction() |
|
3151 |
|
3152 # save the user project properties |
|
3153 if not noSave: |
|
3154 self.__writeUserProperties() |
|
3155 |
|
3156 # save the project session file being quiet about error |
|
3157 if reopen: |
|
3158 self.__writeSession(quiet=True, indicator="_tmp") |
|
3159 elif Preferences.getProject("AutoSaveSession") and not noSave: |
|
3160 self.__writeSession(quiet=True) |
|
3161 |
|
3162 # save the project debugger properties file being quiet about error |
|
3163 if Preferences.getProject("AutoSaveDbgProperties") and \ |
|
3164 self.isDebugPropertiesLoaded() and \ |
|
3165 not noSave and self.debugPropertiesChanged: |
|
3166 self.__writeDebugProperties(True) |
|
3167 |
|
3168 vm = e5App().getObject("ViewManager") |
|
3169 |
|
3170 # check dirty status of all project files first |
|
3171 for fn in vm.getOpenFilenames(): |
|
3172 if self.isProjectFile(fn): |
|
3173 reset = vm.checkFileDirty(fn) |
|
3174 if not reset: |
|
3175 # abort shutting down |
|
3176 return False |
|
3177 |
|
3178 # close all project related editors |
|
3179 success = True |
|
3180 for fn in vm.getOpenFilenames(): |
|
3181 if self.isProjectFile(fn): |
|
3182 success &= vm.closeWindow(fn, ignoreDirty=True) |
|
3183 if not success: |
|
3184 return False |
|
3185 |
|
3186 # stop the VCS monitor thread |
|
3187 if self.vcs is not None: |
|
3188 self.vcs.stopStatusMonitor() |
|
3189 |
|
3190 # now save the tasks |
|
3191 if not noSave: |
|
3192 self.writeTasks() |
|
3193 self.ui.taskViewer.clearProjectTasks() |
|
3194 self.ui.taskViewer.setProjectOpen(False) |
|
3195 |
|
3196 # now shutdown the vcs interface |
|
3197 if self.vcs: |
|
3198 self.vcs.vcsShutdown() |
|
3199 self.vcs.deleteLater() |
|
3200 self.vcs = None |
|
3201 e5App().getObject("PluginManager").deactivateVcsPlugins() |
|
3202 |
|
3203 # now close all project related tool windows |
|
3204 self.__closeAllWindows() |
|
3205 |
|
3206 self.__initData() |
|
3207 self.closeAct.setEnabled(False) |
|
3208 self.saveasAct.setEnabled(False) |
|
3209 self.saveAct.setEnabled(False) |
|
3210 self.actGrp2.setEnabled(False) |
|
3211 self.propsAct.setEnabled(False) |
|
3212 self.userPropsAct.setEnabled(False) |
|
3213 self.filetypesAct.setEnabled(False) |
|
3214 self.lexersAct.setEnabled(False) |
|
3215 self.sessActGrp.setEnabled(False) |
|
3216 self.dbgActGrp.setEnabled(False) |
|
3217 self.menuDebuggerAct.setEnabled(False) |
|
3218 self.menuSessionAct.setEnabled(False) |
|
3219 self.menuCheckAct.setEnabled(False) |
|
3220 self.menuShowAct.setEnabled(False) |
|
3221 self.menuDiagramAct.setEnabled(False) |
|
3222 self.menuApidocAct.setEnabled(False) |
|
3223 self.menuPackagersAct.setEnabled(False) |
|
3224 self.pluginGrp.setEnabled(False) |
|
3225 self.makeGrp.setEnabled(False) |
|
3226 self.menuMakeAct.setEnabled(False) |
|
3227 |
|
3228 self.__model.projectClosed() |
|
3229 self.projectClosedHooks.emit() |
|
3230 self.projectClosed.emit() |
|
3231 |
|
3232 return True |
|
3233 |
|
3234 def saveAllScripts(self, reportSyntaxErrors=False): |
|
3235 """ |
|
3236 Public method to save all scripts belonging to the project. |
|
3237 |
|
3238 @keyparam reportSyntaxErrors flag indicating special reporting |
|
3239 for syntax errors (boolean) |
|
3240 @return flag indicating success (boolean) |
|
3241 """ |
|
3242 vm = e5App().getObject("ViewManager") |
|
3243 success = True |
|
3244 filesWithSyntaxErrors = 0 |
|
3245 for fn in vm.getOpenFilenames(): |
|
3246 rfn = self.getRelativePath(fn) |
|
3247 if rfn in self.pdata["SOURCES"] or rfn in self.pdata["OTHERS"]: |
|
3248 editor = vm.getOpenEditor(fn) |
|
3249 success &= vm.saveEditorEd(editor) |
|
3250 if reportSyntaxErrors and editor.hasSyntaxErrors(): |
|
3251 filesWithSyntaxErrors += 1 |
|
3252 |
|
3253 if reportSyntaxErrors and filesWithSyntaxErrors > 0: |
|
3254 E5MessageBox.critical( |
|
3255 self.ui, |
|
3256 self.tr("Syntax errors detected"), |
|
3257 self.tr( |
|
3258 """The project contains %n file(s) with syntax errors.""", |
|
3259 "", filesWithSyntaxErrors) |
|
3260 ) |
|
3261 return False |
|
3262 else: |
|
3263 return success |
|
3264 |
|
3265 def checkAllScriptsDirty(self, reportSyntaxErrors=False): |
|
3266 """ |
|
3267 Public method to check all scripts belonging to the project for |
|
3268 their dirty status. |
|
3269 |
|
3270 @keyparam reportSyntaxErrors flag indicating special reporting |
|
3271 for syntax errors (boolean) |
|
3272 @return flag indicating success (boolean) |
|
3273 """ |
|
3274 vm = e5App().getObject("ViewManager") |
|
3275 success = True |
|
3276 filesWithSyntaxErrors = 0 |
|
3277 for fn in vm.getOpenFilenames(): |
|
3278 rfn = self.getRelativePath(fn) |
|
3279 if rfn in self.pdata["SOURCES"] or rfn in self.pdata["OTHERS"]: |
|
3280 editor = vm.getOpenEditor(fn) |
|
3281 success &= editor.checkDirty() |
|
3282 if reportSyntaxErrors and editor.hasSyntaxErrors(): |
|
3283 filesWithSyntaxErrors += 1 |
|
3284 |
|
3285 if reportSyntaxErrors and filesWithSyntaxErrors > 0: |
|
3286 E5MessageBox.critical( |
|
3287 self.ui, |
|
3288 self.tr("Syntax errors detected"), |
|
3289 self.tr( |
|
3290 """The project contains %n file(s) with syntax errors.""", |
|
3291 "", filesWithSyntaxErrors) |
|
3292 ) |
|
3293 return False |
|
3294 else: |
|
3295 return success |
|
3296 |
|
3297 def getMainScript(self, normalized=False): |
|
3298 """ |
|
3299 Public method to return the main script filename. |
|
3300 |
|
3301 @param normalized flag indicating a normalized filename is wanted |
|
3302 (boolean) |
|
3303 @return filename of the projects main script (string) |
|
3304 """ |
|
3305 if self.pdata["MAINSCRIPT"]: |
|
3306 if normalized: |
|
3307 return os.path.join(self.ppath, self.pdata["MAINSCRIPT"]) |
|
3308 else: |
|
3309 return self.pdata["MAINSCRIPT"] |
|
3310 else: |
|
3311 return None |
|
3312 |
|
3313 def getSources(self, normalized=False): |
|
3314 """ |
|
3315 Public method to return the source script files. |
|
3316 |
|
3317 @param normalized flag indicating a normalized filename is wanted |
|
3318 (boolean) |
|
3319 @return list of the projects scripts (list of string) |
|
3320 """ |
|
3321 return self.getProjectFiles("SOURCES", normalized=normalized) |
|
3322 |
|
3323 def getProjectFiles(self, fileType, normalized=False): |
|
3324 """ |
|
3325 Public method to get the file entries of the given type. |
|
3326 |
|
3327 @param fileType project file type (one of SOURCES, FORMS, RESOURCES, |
|
3328 INTERFACES, PROTOCOLS, OTHERS, TRANSLATIONS) |
|
3329 @type str |
|
3330 @param normalized flag indicating normalized file names are wanted |
|
3331 @type boolean |
|
3332 @return list of file names |
|
3333 @rtype list of str |
|
3334 @exception ValueError raised when an unsupported file type is given |
|
3335 """ |
|
3336 if fileType not in ["SOURCES", "FORMS", "RESOURCES", "INTERFACES", |
|
3337 "PROTOCOLS", "OTHERS", "TRANSLATIONS"]: |
|
3338 raise ValueError("Given file type has incorrect value.") |
|
3339 |
|
3340 if normalized: |
|
3341 return [os.path.join(self.ppath, fn) for fn in |
|
3342 self.pdata[fileType]] |
|
3343 else: |
|
3344 return self.pdata[fileType] |
|
3345 |
|
3346 def getProjectType(self): |
|
3347 """ |
|
3348 Public method to get the type of the project. |
|
3349 |
|
3350 @return UI type of the project (string) |
|
3351 """ |
|
3352 return self.pdata["PROJECTTYPE"] |
|
3353 |
|
3354 def getProjectLanguage(self): |
|
3355 """ |
|
3356 Public method to get the project's programming language. |
|
3357 |
|
3358 @return programming language (string) |
|
3359 """ |
|
3360 return self.pdata["PROGLANGUAGE"] |
|
3361 |
|
3362 def isMixedLanguageProject(self): |
|
3363 """ |
|
3364 Public method to check, if this is a mixed language project. |
|
3365 |
|
3366 @return flag indicating a mixed language project |
|
3367 @rtype bool |
|
3368 """ |
|
3369 return self.pdata["MIXEDLANGUAGE"] |
|
3370 |
|
3371 def isPythonProject(self): |
|
3372 """ |
|
3373 Public method to check, if this project is a Python2 or Python3 |
|
3374 project. |
|
3375 |
|
3376 @return flag indicating a Python project (boolean) |
|
3377 """ |
|
3378 return self.pdata["PROGLANGUAGE"] in ["Python", "Python2", |
|
3379 "Python3"] |
|
3380 |
|
3381 def isPy3Project(self): |
|
3382 """ |
|
3383 Public method to check, if this project is a Python3 project. |
|
3384 |
|
3385 @return flag indicating a Python3 project (boolean) |
|
3386 """ |
|
3387 return self.pdata["PROGLANGUAGE"] == "Python3" |
|
3388 |
|
3389 def isPy2Project(self): |
|
3390 """ |
|
3391 Public method to check, if this project is a Python2 project. |
|
3392 |
|
3393 @return flag indicating a Python2 project (boolean) |
|
3394 """ |
|
3395 return self.pdata["PROGLANGUAGE"] in ["Python", "Python2"] |
|
3396 |
|
3397 def isRubyProject(self): |
|
3398 """ |
|
3399 Public method to check, if this project is a Ruby project. |
|
3400 |
|
3401 @return flag indicating a Ruby project (boolean) |
|
3402 """ |
|
3403 return self.pdata["PROGLANGUAGE"] == "Ruby" |
|
3404 |
|
3405 def isJavaScriptProject(self): |
|
3406 """ |
|
3407 Public method to check, if this project is a JavaScript project. |
|
3408 |
|
3409 @return flag indicating a JavaScript project (boolean) |
|
3410 """ |
|
3411 return self.pdata["PROGLANGUAGE"] == "JavaScript" |
|
3412 |
|
3413 def getProjectSpellLanguage(self): |
|
3414 """ |
|
3415 Public method to get the project's programming language. |
|
3416 |
|
3417 @return programming language (string) |
|
3418 """ |
|
3419 return self.pdata["SPELLLANGUAGE"] |
|
3420 |
|
3421 def getProjectDictionaries(self): |
|
3422 """ |
|
3423 Public method to get the names of the project specific dictionaries. |
|
3424 |
|
3425 @return tuple of two strings giving the absolute path names of the |
|
3426 project specific word and exclude list |
|
3427 """ |
|
3428 pwl = "" |
|
3429 if self.pdata["SPELLWORDS"]: |
|
3430 pwl = os.path.join(self.ppath, self.pdata["SPELLWORDS"]) |
|
3431 |
|
3432 pel = "" |
|
3433 if self.pdata["SPELLEXCLUDES"]: |
|
3434 pel = os.path.join(self.ppath, self.pdata["SPELLEXCLUDES"]) |
|
3435 |
|
3436 return (pwl, pel) |
|
3437 |
|
3438 def getDefaultSourceExtension(self): |
|
3439 """ |
|
3440 Public method to get the default extension for the project's |
|
3441 programming language. |
|
3442 |
|
3443 @return default extension (including the dot) (string) |
|
3444 """ |
|
3445 lang = self.pdata["PROGLANGUAGE"] |
|
3446 if lang == "": |
|
3447 lang = "Python3" |
|
3448 elif lang == "Python": |
|
3449 lang = "Python2" |
|
3450 return self.__sourceExtensions(lang)[0] |
|
3451 |
|
3452 def getProjectPath(self): |
|
3453 """ |
|
3454 Public method to get the project path. |
|
3455 |
|
3456 @return project path (string) |
|
3457 """ |
|
3458 return self.ppath |
|
3459 |
|
3460 def startswithProjectPath(self, path): |
|
3461 """ |
|
3462 Public method to check, if a path starts with the project path. |
|
3463 |
|
3464 @param path path to be checked (string) |
|
3465 @return flag indicating that the path starts with the project path |
|
3466 (boolean) |
|
3467 """ |
|
3468 if self.ppath: |
|
3469 if path == self.ppath: |
|
3470 return True |
|
3471 elif Utilities.normcasepath(Utilities.toNativeSeparators(path))\ |
|
3472 .startswith(Utilities.normcasepath( |
|
3473 Utilities.toNativeSeparators(self.ppath + "/"))): |
|
3474 return True |
|
3475 else: |
|
3476 return False |
|
3477 else: |
|
3478 return False |
|
3479 |
|
3480 def getProjectFile(self): |
|
3481 """ |
|
3482 Public method to get the path of the project file. |
|
3483 |
|
3484 @return path of the project file (string) |
|
3485 """ |
|
3486 return self.pfile |
|
3487 |
|
3488 def getProjectName(self): |
|
3489 """ |
|
3490 Public method to get the name of the project. |
|
3491 |
|
3492 The project name is determined from the name of the project file. |
|
3493 |
|
3494 @return name of the project (string) |
|
3495 """ |
|
3496 if self.pfile: |
|
3497 name = os.path.splitext(self.pfile)[0] |
|
3498 return os.path.basename(name) |
|
3499 else: |
|
3500 return "" |
|
3501 |
|
3502 def getProjectManagementDir(self): |
|
3503 """ |
|
3504 Public method to get the path of the management directory. |
|
3505 |
|
3506 @return path of the management directory (string) |
|
3507 """ |
|
3508 if Utilities.isWindowsPlatform(): |
|
3509 # migrate the old project management directory ( < v18.06) |
|
3510 oldDir = os.path.join(self.ppath, "_eric6project") |
|
3511 if os.path.exists(oldDir): |
|
3512 os.rename( |
|
3513 oldDir, |
|
3514 os.path.join(self.ppath, ".eric6project") |
|
3515 ) |
|
3516 |
|
3517 return os.path.join(self.ppath, ".eric6project") |
|
3518 |
|
3519 def createProjectManagementDir(self): |
|
3520 """ |
|
3521 Public method to create the project management directory. |
|
3522 |
|
3523 It does nothing, if it already exists. |
|
3524 """ |
|
3525 # create management directory if not present |
|
3526 mgmtDir = self.getProjectManagementDir() |
|
3527 if not os.path.exists(mgmtDir): |
|
3528 os.makedirs(mgmtDir) |
|
3529 |
|
3530 def getHash(self): |
|
3531 """ |
|
3532 Public method to get the project hash. |
|
3533 |
|
3534 @return project hash as a hex string (string) |
|
3535 """ |
|
3536 return self.pdata["HASH"] |
|
3537 |
|
3538 def getRelativePath(self, path): |
|
3539 """ |
|
3540 Public method to convert a file path to a project relative |
|
3541 file path. |
|
3542 |
|
3543 @param path file or directory name to convert (string) |
|
3544 @return project relative path or unchanged path, if path doesn't |
|
3545 belong to the project (string) |
|
3546 """ |
|
3547 if self.startswithProjectPath(path): |
|
3548 if self.ppath and path == self.ppath: |
|
3549 return "" |
|
3550 else: |
|
3551 relpath = path[len(self.ppath):] |
|
3552 if relpath.startswith(("/", "\\")): |
|
3553 relpath = relpath[1:] |
|
3554 return relpath |
|
3555 else: |
|
3556 return path |
|
3557 |
|
3558 def getRelativeUniversalPath(self, path): |
|
3559 """ |
|
3560 Public method to convert a file path to a project relative |
|
3561 file path with universal separators. |
|
3562 |
|
3563 @param path file or directory name to convert (string) |
|
3564 @return project relative path or unchanged path, if path doesn't |
|
3565 belong to the project (string) |
|
3566 """ |
|
3567 return Utilities.fromNativeSeparators(self.getRelativePath(path)) |
|
3568 |
|
3569 def getAbsolutePath(self, fn): |
|
3570 """ |
|
3571 Public method to convert a project relative file path to an absolute |
|
3572 file path. |
|
3573 |
|
3574 @param fn file or directory name to convert (string) |
|
3575 @return absolute path (string) |
|
3576 """ |
|
3577 if not os.path.isabs(fn): |
|
3578 fn = os.path.join(self.ppath, fn) |
|
3579 return fn |
|
3580 |
|
3581 def getAbsoluteUniversalPath(self, fn): |
|
3582 """ |
|
3583 Public method to convert a project relative file path with universal |
|
3584 separators to an absolute file path. |
|
3585 |
|
3586 @param fn file or directory name to convert (string) |
|
3587 @return absolute path (string) |
|
3588 """ |
|
3589 if not os.path.isabs(fn): |
|
3590 fn = os.path.join(self.ppath, Utilities.toNativeSeparators(fn)) |
|
3591 return fn |
|
3592 |
|
3593 def getEolString(self): |
|
3594 """ |
|
3595 Public method to get the EOL-string to be used by the project. |
|
3596 |
|
3597 @return eol string (string) |
|
3598 """ |
|
3599 if self.pdata["EOL"] >= 0: |
|
3600 return self.eols[self.pdata["EOL"]] |
|
3601 else: |
|
3602 eolMode = Preferences.getEditor("EOLMode") |
|
3603 if eolMode == QsciScintilla.EolWindows: |
|
3604 eol = '\r\n' |
|
3605 elif eolMode == QsciScintilla.EolUnix: |
|
3606 eol = '\n' |
|
3607 elif eolMode == QsciScintilla.EolMac: |
|
3608 eol = '\r' |
|
3609 else: |
|
3610 eol = os.linesep |
|
3611 return eol |
|
3612 |
|
3613 def useSystemEol(self): |
|
3614 """ |
|
3615 Public method to check, if the project uses the system eol setting. |
|
3616 |
|
3617 @return flag indicating the usage of system eol (boolean) |
|
3618 """ |
|
3619 return self.pdata["EOL"] == 0 |
|
3620 |
|
3621 def getProjectVersion(self): |
|
3622 """ |
|
3623 Public mehod to get the version number of the project. |
|
3624 |
|
3625 @return version number |
|
3626 @rtype str |
|
3627 """ |
|
3628 return self.pdata["VERSION"] |
|
3629 |
|
3630 def getProjectAuthor(self): |
|
3631 """ |
|
3632 Public method to get the author of the project. |
|
3633 |
|
3634 @return author name |
|
3635 @rtype str |
|
3636 """ |
|
3637 return self.pdata["AUTHOR"] |
|
3638 |
|
3639 def getProjectAuthorEmail(self): |
|
3640 """ |
|
3641 Public method to get the email address of the project author. |
|
3642 |
|
3643 @return project author email |
|
3644 @rtype str |
|
3645 """ |
|
3646 return self.pdata["EMAIL"] |
|
3647 |
|
3648 def getProjectDescription(self): |
|
3649 """ |
|
3650 Public method to get the description of the project. |
|
3651 |
|
3652 @return project description |
|
3653 @rtype str |
|
3654 """ |
|
3655 return self.pdata["DESCRIPTION"] |
|
3656 |
|
3657 def isProjectFile(self, fn): |
|
3658 """ |
|
3659 Public method used to check, if the passed in filename belongs to the |
|
3660 project. |
|
3661 |
|
3662 @param fn filename to be checked (string) |
|
3663 @return flag indicating membership (boolean) |
|
3664 """ |
|
3665 for group in ["SOURCES", "FORMS", "INTERFACES", "PROTOCOLS", |
|
3666 "RESOURCES", "TRANSLATIONS", "OTHERS"]: |
|
3667 if self.__checkProjectFileGroup(fn, group): |
|
3668 return True |
|
3669 |
|
3670 return False |
|
3671 |
|
3672 def __checkProjectFileGroup(self, fn, group): |
|
3673 """ |
|
3674 Private method to check, if a file is in a specific file group of the |
|
3675 project. |
|
3676 |
|
3677 @param fn filename to be checked (string) |
|
3678 @param group group to check (string) |
|
3679 @return flag indicating membership (boolean) |
|
3680 """ |
|
3681 newfn = os.path.abspath(fn) |
|
3682 newfn = self.getRelativePath(newfn) |
|
3683 if newfn in self.pdata[group]: |
|
3684 return True |
|
3685 elif group == "OTHERS": |
|
3686 for entry in self.pdata[group]: |
|
3687 if newfn.startswith(entry): |
|
3688 return True |
|
3689 |
|
3690 if Utilities.isWindowsPlatform(): |
|
3691 # try the above case-insensitive |
|
3692 newfn = newfn.lower() |
|
3693 for entry in self.pdata[group]: |
|
3694 if entry.lower() == newfn: |
|
3695 return True |
|
3696 elif group == "OTHERS": |
|
3697 for entry in self.pdata[group]: |
|
3698 if newfn.startswith(entry.lower()): |
|
3699 return True |
|
3700 |
|
3701 return False |
|
3702 |
|
3703 def isProjectSource(self, fn): |
|
3704 """ |
|
3705 Public method used to check, if the passed in filename belongs to the |
|
3706 project sources. |
|
3707 |
|
3708 @param fn filename to be checked (string) |
|
3709 @return flag indicating membership (boolean) |
|
3710 """ |
|
3711 return self.__checkProjectFileGroup(fn, "SOURCES") |
|
3712 |
|
3713 def isProjectForm(self, fn): |
|
3714 """ |
|
3715 Public method used to check, if the passed in filename belongs to the |
|
3716 project forms. |
|
3717 |
|
3718 @param fn filename to be checked (string) |
|
3719 @return flag indicating membership (boolean) |
|
3720 """ |
|
3721 return self.__checkProjectFileGroup(fn, "FORMS") |
|
3722 |
|
3723 def isProjectInterface(self, fn): |
|
3724 """ |
|
3725 Public method used to check, if the passed in filename belongs to the |
|
3726 project interfaces. |
|
3727 |
|
3728 @param fn filename to be checked (string) |
|
3729 @return flag indicating membership (boolean) |
|
3730 """ |
|
3731 return self.__checkProjectFileGroup(fn, "INTERFACES") |
|
3732 |
|
3733 def isProjectProtocol(self, fn): |
|
3734 """ |
|
3735 Public method used to check, if the passed in filename belongs to the |
|
3736 project protocols. |
|
3737 |
|
3738 @param fn filename to be checked |
|
3739 @type str |
|
3740 @return flag indicating membership |
|
3741 @rtype bool |
|
3742 """ |
|
3743 return self.__checkProjectFileGroup(fn, "PROTOCOLS") |
|
3744 |
|
3745 def isProjectResource(self, fn): |
|
3746 """ |
|
3747 Public method used to check, if the passed in filename belongs to the |
|
3748 project resources. |
|
3749 |
|
3750 @param fn filename to be checked (string) |
|
3751 @return flag indicating membership (boolean) |
|
3752 """ |
|
3753 return self.__checkProjectFileGroup(fn, "RESOURCES") |
|
3754 |
|
3755 def initActions(self): |
|
3756 """ |
|
3757 Public slot to initialize the project related actions. |
|
3758 """ |
|
3759 self.actions = [] |
|
3760 |
|
3761 self.actGrp1 = createActionGroup(self) |
|
3762 |
|
3763 act = E5Action( |
|
3764 self.tr('New project'), |
|
3765 UI.PixmapCache.getIcon("projectNew.png"), |
|
3766 self.tr('&New...'), 0, 0, |
|
3767 self.actGrp1, 'project_new') |
|
3768 act.setStatusTip(self.tr('Generate a new project')) |
|
3769 act.setWhatsThis(self.tr( |
|
3770 """<b>New...</b>""" |
|
3771 """<p>This opens a dialog for entering the info for a""" |
|
3772 """ new project.</p>""" |
|
3773 )) |
|
3774 act.triggered.connect(self.createNewProject) |
|
3775 self.actions.append(act) |
|
3776 |
|
3777 act = E5Action( |
|
3778 self.tr('Open project'), |
|
3779 UI.PixmapCache.getIcon("projectOpen.png"), |
|
3780 self.tr('&Open...'), 0, 0, |
|
3781 self.actGrp1, 'project_open') |
|
3782 act.setStatusTip(self.tr('Open an existing project')) |
|
3783 act.setWhatsThis(self.tr( |
|
3784 """<b>Open...</b>""" |
|
3785 """<p>This opens an existing project.</p>""" |
|
3786 )) |
|
3787 act.triggered.connect(self.openProject) |
|
3788 self.actions.append(act) |
|
3789 |
|
3790 self.closeAct = E5Action( |
|
3791 self.tr('Close project'), |
|
3792 UI.PixmapCache.getIcon("projectClose.png"), |
|
3793 self.tr('&Close'), 0, 0, self, 'project_close') |
|
3794 self.closeAct.setStatusTip(self.tr('Close the current project')) |
|
3795 self.closeAct.setWhatsThis(self.tr( |
|
3796 """<b>Close</b>""" |
|
3797 """<p>This closes the current project.</p>""" |
|
3798 )) |
|
3799 self.closeAct.triggered.connect(self.closeProject) |
|
3800 self.actions.append(self.closeAct) |
|
3801 |
|
3802 self.saveAct = E5Action( |
|
3803 self.tr('Save project'), |
|
3804 UI.PixmapCache.getIcon("projectSave.png"), |
|
3805 self.tr('&Save'), 0, 0, self, 'project_save') |
|
3806 self.saveAct.setStatusTip(self.tr('Save the current project')) |
|
3807 self.saveAct.setWhatsThis(self.tr( |
|
3808 """<b>Save</b>""" |
|
3809 """<p>This saves the current project.</p>""" |
|
3810 )) |
|
3811 self.saveAct.triggered.connect(self.saveProject) |
|
3812 self.actions.append(self.saveAct) |
|
3813 |
|
3814 self.saveasAct = E5Action( |
|
3815 self.tr('Save project as'), |
|
3816 UI.PixmapCache.getIcon("projectSaveAs.png"), |
|
3817 self.tr('Save &as...'), 0, 0, self, 'project_save_as') |
|
3818 self.saveasAct.setStatusTip(self.tr( |
|
3819 'Save the current project to a new file')) |
|
3820 self.saveasAct.setWhatsThis(self.tr( |
|
3821 """<b>Save as</b>""" |
|
3822 """<p>This saves the current project to a new file.</p>""" |
|
3823 )) |
|
3824 self.saveasAct.triggered.connect(self.saveProjectAs) |
|
3825 self.actions.append(self.saveasAct) |
|
3826 |
|
3827 self.actGrp2 = createActionGroup(self) |
|
3828 |
|
3829 self.addFilesAct = E5Action( |
|
3830 self.tr('Add files to project'), |
|
3831 UI.PixmapCache.getIcon("fileMisc.png"), |
|
3832 self.tr('Add &files...'), 0, 0, |
|
3833 self.actGrp2, 'project_add_file') |
|
3834 self.addFilesAct.setStatusTip(self.tr( |
|
3835 'Add files to the current project')) |
|
3836 self.addFilesAct.setWhatsThis(self.tr( |
|
3837 """<b>Add files...</b>""" |
|
3838 """<p>This opens a dialog for adding files""" |
|
3839 """ to the current project. The place to add is""" |
|
3840 """ determined by the file extension.</p>""" |
|
3841 )) |
|
3842 self.addFilesAct.triggered.connect(self.addFiles) |
|
3843 self.actions.append(self.addFilesAct) |
|
3844 |
|
3845 self.addDirectoryAct = E5Action( |
|
3846 self.tr('Add directory to project'), |
|
3847 UI.PixmapCache.getIcon("dirOpen.png"), |
|
3848 self.tr('Add directory...'), 0, 0, |
|
3849 self.actGrp2, 'project_add_directory') |
|
3850 self.addDirectoryAct.setStatusTip( |
|
3851 self.tr('Add a directory to the current project')) |
|
3852 self.addDirectoryAct.setWhatsThis(self.tr( |
|
3853 """<b>Add directory...</b>""" |
|
3854 """<p>This opens a dialog for adding a directory""" |
|
3855 """ to the current project.</p>""" |
|
3856 )) |
|
3857 self.addDirectoryAct.triggered.connect(self.addDirectory) |
|
3858 self.actions.append(self.addDirectoryAct) |
|
3859 |
|
3860 self.addLanguageAct = E5Action( |
|
3861 self.tr('Add translation to project'), |
|
3862 UI.PixmapCache.getIcon("linguist4.png"), |
|
3863 self.tr('Add &translation...'), 0, 0, |
|
3864 self.actGrp2, 'project_add_translation') |
|
3865 self.addLanguageAct.setStatusTip( |
|
3866 self.tr('Add a translation to the current project')) |
|
3867 self.addLanguageAct.setWhatsThis(self.tr( |
|
3868 """<b>Add translation...</b>""" |
|
3869 """<p>This opens a dialog for add a translation""" |
|
3870 """ to the current project.</p>""" |
|
3871 )) |
|
3872 self.addLanguageAct.triggered.connect(self.addLanguage) |
|
3873 self.actions.append(self.addLanguageAct) |
|
3874 |
|
3875 act = E5Action( |
|
3876 self.tr('Search new files'), |
|
3877 self.tr('Searc&h new files...'), 0, 0, |
|
3878 self.actGrp2, 'project_search_new_files') |
|
3879 act.setStatusTip(self.tr( |
|
3880 'Search new files in the project directory.')) |
|
3881 act.setWhatsThis(self.tr( |
|
3882 """<b>Search new files...</b>""" |
|
3883 """<p>This searches for new files (sources, *.ui, *.idl,""" |
|
3884 """ *.proto) in the project directory and registered""" |
|
3885 """ subdirectories.</p>""" |
|
3886 )) |
|
3887 act.triggered.connect(self.__searchNewFiles) |
|
3888 self.actions.append(act) |
|
3889 |
|
3890 act = E5Action( |
|
3891 self.tr('Search Project File'), |
|
3892 self.tr('Search Project File...'), |
|
3893 QKeySequence(self.tr("Alt+Ctrl+P", "Project|Search Project File")), |
|
3894 0, |
|
3895 self.actGrp2, 'project_search_project_file') |
|
3896 act.setStatusTip(self.tr( |
|
3897 'Search for a file in the project list of files.')) |
|
3898 act.setWhatsThis(self.tr( |
|
3899 """<b>Search Project File</b>""" |
|
3900 """<p>This searches for a file in the project list of files.</p>""" |
|
3901 )) |
|
3902 act.triggered.connect(self.__searchProjectFile) |
|
3903 self.actions.append(act) |
|
3904 |
|
3905 self.propsAct = E5Action( |
|
3906 self.tr('Project properties'), |
|
3907 UI.PixmapCache.getIcon("projectProps.png"), |
|
3908 self.tr('&Properties...'), 0, 0, self, |
|
3909 'project_properties') |
|
3910 self.propsAct.setStatusTip(self.tr('Show the project properties')) |
|
3911 self.propsAct.setWhatsThis(self.tr( |
|
3912 """<b>Properties...</b>""" |
|
3913 """<p>This shows a dialog to edit the project properties.</p>""" |
|
3914 )) |
|
3915 self.propsAct.triggered.connect(self.__showProperties) |
|
3916 self.actions.append(self.propsAct) |
|
3917 |
|
3918 self.userPropsAct = E5Action( |
|
3919 self.tr('User project properties'), |
|
3920 UI.PixmapCache.getIcon("projectUserProps.png"), |
|
3921 self.tr('&User Properties...'), 0, 0, self, |
|
3922 'project_user_properties') |
|
3923 self.userPropsAct.setStatusTip(self.tr( |
|
3924 'Show the user specific project properties')) |
|
3925 self.userPropsAct.setWhatsThis(self.tr( |
|
3926 """<b>User Properties...</b>""" |
|
3927 """<p>This shows a dialog to edit the user specific project""" |
|
3928 """ properties.</p>""" |
|
3929 )) |
|
3930 self.userPropsAct.triggered.connect(self.__showUserProperties) |
|
3931 self.actions.append(self.userPropsAct) |
|
3932 |
|
3933 self.filetypesAct = E5Action( |
|
3934 self.tr('Filetype Associations'), |
|
3935 self.tr('Filetype Associations...'), 0, 0, |
|
3936 self, 'project_filetype_associatios') |
|
3937 self.filetypesAct.setStatusTip( |
|
3938 self.tr('Show the project filetype associations')) |
|
3939 self.filetypesAct.setWhatsThis(self.tr( |
|
3940 """<b>Filetype Associations...</b>""" |
|
3941 """<p>This shows a dialog to edit the file type associations of""" |
|
3942 """ the project. These associations determine the type""" |
|
3943 """ (source, form, interface, protocol or others) with a""" |
|
3944 """ filename pattern. They are used when adding a file to the""" |
|
3945 """ project and when performing a search for new files.</p>""" |
|
3946 )) |
|
3947 self.filetypesAct.triggered.connect( |
|
3948 self.__showFiletypeAssociations) |
|
3949 self.actions.append(self.filetypesAct) |
|
3950 |
|
3951 self.lexersAct = E5Action( |
|
3952 self.tr('Lexer Associations'), |
|
3953 self.tr('Lexer Associations...'), 0, 0, |
|
3954 self, 'project_lexer_associatios') |
|
3955 self.lexersAct.setStatusTip(self.tr( |
|
3956 'Show the project lexer associations (overriding defaults)')) |
|
3957 self.lexersAct.setWhatsThis(self.tr( |
|
3958 """<b>Lexer Associations...</b>""" |
|
3959 """<p>This shows a dialog to edit the lexer associations of""" |
|
3960 """ the project. These associations override the global lexer""" |
|
3961 """ associations. Lexers are used to highlight the editor""" |
|
3962 """ text.</p>""" |
|
3963 )) |
|
3964 self.lexersAct.triggered.connect(self.__showLexerAssociations) |
|
3965 self.actions.append(self.lexersAct) |
|
3966 |
|
3967 self.dbgActGrp = createActionGroup(self) |
|
3968 |
|
3969 act = E5Action( |
|
3970 self.tr('Debugger Properties'), |
|
3971 self.tr('Debugger &Properties...'), 0, 0, |
|
3972 self.dbgActGrp, 'project_debugger_properties') |
|
3973 act.setStatusTip(self.tr('Show the debugger properties')) |
|
3974 act.setWhatsThis(self.tr( |
|
3975 """<b>Debugger Properties...</b>""" |
|
3976 """<p>This shows a dialog to edit project specific debugger""" |
|
3977 """ settings.</p>""" |
|
3978 )) |
|
3979 act.triggered.connect(self.__showDebugProperties) |
|
3980 self.actions.append(act) |
|
3981 |
|
3982 act = E5Action( |
|
3983 self.tr('Load'), |
|
3984 self.tr('&Load'), 0, 0, |
|
3985 self.dbgActGrp, 'project_debugger_properties_load') |
|
3986 act.setStatusTip(self.tr('Load the debugger properties')) |
|
3987 act.setWhatsThis(self.tr( |
|
3988 """<b>Load Debugger Properties</b>""" |
|
3989 """<p>This loads the project specific debugger settings.</p>""" |
|
3990 )) |
|
3991 act.triggered.connect(self.__readDebugProperties) |
|
3992 self.actions.append(act) |
|
3993 |
|
3994 act = E5Action( |
|
3995 self.tr('Save'), |
|
3996 self.tr('&Save'), 0, 0, |
|
3997 self.dbgActGrp, 'project_debugger_properties_save') |
|
3998 act.setStatusTip(self.tr('Save the debugger properties')) |
|
3999 act.setWhatsThis(self.tr( |
|
4000 """<b>Save Debugger Properties</b>""" |
|
4001 """<p>This saves the project specific debugger settings.</p>""" |
|
4002 )) |
|
4003 act.triggered.connect(self.__writeDebugProperties) |
|
4004 self.actions.append(act) |
|
4005 |
|
4006 act = E5Action( |
|
4007 self.tr('Delete'), |
|
4008 self.tr('&Delete'), 0, 0, |
|
4009 self.dbgActGrp, 'project_debugger_properties_delete') |
|
4010 act.setStatusTip(self.tr('Delete the debugger properties')) |
|
4011 act.setWhatsThis(self.tr( |
|
4012 """<b>Delete Debugger Properties</b>""" |
|
4013 """<p>This deletes the file containing the project specific""" |
|
4014 """ debugger settings.</p>""" |
|
4015 )) |
|
4016 act.triggered.connect(self.__deleteDebugProperties) |
|
4017 self.actions.append(act) |
|
4018 |
|
4019 act = E5Action( |
|
4020 self.tr('Reset'), |
|
4021 self.tr('&Reset'), 0, 0, |
|
4022 self.dbgActGrp, 'project_debugger_properties_resets') |
|
4023 act.setStatusTip(self.tr('Reset the debugger properties')) |
|
4024 act.setWhatsThis(self.tr( |
|
4025 """<b>Reset Debugger Properties</b>""" |
|
4026 """<p>This resets the project specific debugger settings.</p>""" |
|
4027 )) |
|
4028 act.triggered.connect(self.__initDebugProperties) |
|
4029 self.actions.append(act) |
|
4030 |
|
4031 self.sessActGrp = createActionGroup(self) |
|
4032 |
|
4033 act = E5Action( |
|
4034 self.tr('Load session'), |
|
4035 self.tr('Load session'), 0, 0, |
|
4036 self.sessActGrp, 'project_load_session') |
|
4037 act.setStatusTip(self.tr('Load the projects session file.')) |
|
4038 act.setWhatsThis(self.tr( |
|
4039 """<b>Load session</b>""" |
|
4040 """<p>This loads the projects session file. The session consists""" |
|
4041 """ of the following data.<br>""" |
|
4042 """- all open source files<br>""" |
|
4043 """- all breakpoint<br>""" |
|
4044 """- the commandline arguments<br>""" |
|
4045 """- the working directory<br>""" |
|
4046 """- the exception reporting flag</p>""" |
|
4047 )) |
|
4048 act.triggered.connect(self.__readSession) |
|
4049 self.actions.append(act) |
|
4050 |
|
4051 act = E5Action( |
|
4052 self.tr('Save session'), |
|
4053 self.tr('Save session'), 0, 0, |
|
4054 self.sessActGrp, 'project_save_session') |
|
4055 act.setStatusTip(self.tr('Save the projects session file.')) |
|
4056 act.setWhatsThis(self.tr( |
|
4057 """<b>Save session</b>""" |
|
4058 """<p>This saves the projects session file. The session consists""" |
|
4059 """ of the following data.<br>""" |
|
4060 """- all open source files<br>""" |
|
4061 """- all breakpoint<br>""" |
|
4062 """- the commandline arguments<br>""" |
|
4063 """- the working directory<br>""" |
|
4064 """- the exception reporting flag</p>""" |
|
4065 )) |
|
4066 act.triggered.connect(self.__writeSession) |
|
4067 self.actions.append(act) |
|
4068 |
|
4069 act = E5Action( |
|
4070 self.tr('Delete session'), |
|
4071 self.tr('Delete session'), 0, 0, |
|
4072 self.sessActGrp, 'project_delete_session') |
|
4073 act.setStatusTip(self.tr('Delete the projects session file.')) |
|
4074 act.setWhatsThis(self.tr( |
|
4075 """<b>Delete session</b>""" |
|
4076 """<p>This deletes the projects session file</p>""" |
|
4077 )) |
|
4078 act.triggered.connect(self.__deleteSession) |
|
4079 self.actions.append(act) |
|
4080 |
|
4081 self.chkGrp = createActionGroup(self) |
|
4082 |
|
4083 self.codeMetricsAct = E5Action( |
|
4084 self.tr('Code Metrics'), |
|
4085 self.tr('&Code Metrics...'), 0, 0, |
|
4086 self.chkGrp, 'project_code_metrics') |
|
4087 self.codeMetricsAct.setStatusTip( |
|
4088 self.tr('Show some code metrics for the project.')) |
|
4089 self.codeMetricsAct.setWhatsThis(self.tr( |
|
4090 """<b>Code Metrics...</b>""" |
|
4091 """<p>This shows some code metrics for all Python files in""" |
|
4092 """ the project.</p>""" |
|
4093 )) |
|
4094 self.codeMetricsAct.triggered.connect(self.__showCodeMetrics) |
|
4095 self.actions.append(self.codeMetricsAct) |
|
4096 |
|
4097 self.codeCoverageAct = E5Action( |
|
4098 self.tr('Python Code Coverage'), |
|
4099 self.tr('Code Co&verage...'), 0, 0, |
|
4100 self.chkGrp, 'project_code_coverage') |
|
4101 self.codeCoverageAct.setStatusTip( |
|
4102 self.tr('Show code coverage information for the project.')) |
|
4103 self.codeCoverageAct.setWhatsThis(self.tr( |
|
4104 """<b>Code Coverage...</b>""" |
|
4105 """<p>This shows the code coverage information for all Python""" |
|
4106 """ files in the project.</p>""" |
|
4107 )) |
|
4108 self.codeCoverageAct.triggered.connect(self.__showCodeCoverage) |
|
4109 self.actions.append(self.codeCoverageAct) |
|
4110 |
|
4111 self.codeProfileAct = E5Action( |
|
4112 self.tr('Profile Data'), |
|
4113 self.tr('&Profile Data...'), 0, 0, |
|
4114 self.chkGrp, 'project_profile_data') |
|
4115 self.codeProfileAct.setStatusTip( |
|
4116 self.tr('Show profiling data for the project.')) |
|
4117 self.codeProfileAct.setWhatsThis(self.tr( |
|
4118 """<b>Profile Data...</b>""" |
|
4119 """<p>This shows the profiling data for the project.</p>""" |
|
4120 )) |
|
4121 self.codeProfileAct.triggered.connect(self.__showProfileData) |
|
4122 self.actions.append(self.codeProfileAct) |
|
4123 |
|
4124 self.graphicsGrp = createActionGroup(self) |
|
4125 |
|
4126 self.applicationDiagramAct = E5Action( |
|
4127 self.tr('Application Diagram'), |
|
4128 self.tr('&Application Diagram...'), 0, 0, |
|
4129 self.graphicsGrp, 'project_application_diagram') |
|
4130 self.applicationDiagramAct.setStatusTip( |
|
4131 self.tr('Show a diagram of the project.')) |
|
4132 self.applicationDiagramAct.setWhatsThis(self.tr( |
|
4133 """<b>Application Diagram...</b>""" |
|
4134 """<p>This shows a diagram of the project.</p>""" |
|
4135 )) |
|
4136 self.applicationDiagramAct.triggered.connect( |
|
4137 self.handleApplicationDiagram) |
|
4138 self.actions.append(self.applicationDiagramAct) |
|
4139 |
|
4140 self.loadDiagramAct = E5Action( |
|
4141 self.tr('Load Diagram'), |
|
4142 self.tr('&Load Diagram...'), 0, 0, |
|
4143 self.graphicsGrp, 'project_load_diagram') |
|
4144 self.loadDiagramAct.setStatusTip( |
|
4145 self.tr('Load a diagram from file.')) |
|
4146 self.loadDiagramAct.setWhatsThis(self.tr( |
|
4147 """<b>Load Diagram...</b>""" |
|
4148 """<p>This loads a diagram from file.</p>""" |
|
4149 )) |
|
4150 self.loadDiagramAct.triggered.connect(self.__loadDiagram) |
|
4151 self.actions.append(self.loadDiagramAct) |
|
4152 |
|
4153 self.pluginGrp = createActionGroup(self) |
|
4154 |
|
4155 self.pluginPkgListAct = E5Action( |
|
4156 self.tr('Create Package List'), |
|
4157 UI.PixmapCache.getIcon("pluginArchiveList.png"), |
|
4158 self.tr('Create &Package List'), 0, 0, |
|
4159 self.pluginGrp, 'project_plugin_pkglist') |
|
4160 self.pluginPkgListAct.setStatusTip( |
|
4161 self.tr('Create an initial PKGLIST file for an eric6 plugin.')) |
|
4162 self.pluginPkgListAct.setWhatsThis(self.tr( |
|
4163 """<b>Create Package List</b>""" |
|
4164 """<p>This creates an initial list of files to include in an""" |
|
4165 """ eric6 plugin archive. The list is created from the project""" |
|
4166 """ file.</p>""" |
|
4167 )) |
|
4168 self.pluginPkgListAct.triggered.connect(self.__pluginCreatePkgList) |
|
4169 self.actions.append(self.pluginPkgListAct) |
|
4170 |
|
4171 self.pluginArchiveAct = E5Action( |
|
4172 self.tr('Create Plugin Archives'), |
|
4173 UI.PixmapCache.getIcon("pluginArchive.png"), |
|
4174 self.tr('Create Plugin &Archives'), 0, 0, |
|
4175 self.pluginGrp, 'project_plugin_archive') |
|
4176 self.pluginArchiveAct.setStatusTip( |
|
4177 self.tr('Create eric6 plugin archive files.')) |
|
4178 self.pluginArchiveAct.setWhatsThis(self.tr( |
|
4179 """<b>Create Plugin Archives</b>""" |
|
4180 """<p>This creates eric6 plugin archive files using the list""" |
|
4181 """ of files given in a PKGLIST* file. The archive name is""" |
|
4182 """ built from the main script name if not designated in""" |
|
4183 """ the package list file.</p>""" |
|
4184 )) |
|
4185 self.pluginArchiveAct.triggered.connect(self.__pluginCreateArchives) |
|
4186 self.actions.append(self.pluginArchiveAct) |
|
4187 |
|
4188 self.pluginSArchiveAct = E5Action( |
|
4189 self.tr('Create Plugin Archives (Snapshot)'), |
|
4190 UI.PixmapCache.getIcon("pluginArchiveSnapshot.png"), |
|
4191 self.tr('Create Plugin Archives (&Snapshot)'), 0, 0, |
|
4192 self.pluginGrp, 'project_plugin_sarchive') |
|
4193 self.pluginSArchiveAct.setStatusTip(self.tr( |
|
4194 'Create eric6 plugin archive files (snapshot releases).')) |
|
4195 self.pluginSArchiveAct.setWhatsThis(self.tr( |
|
4196 """<b>Create Plugin Archives (Snapshot)</b>""" |
|
4197 """<p>This creates eric6 plugin archive files using the list""" |
|
4198 """ of files given in the PKGLIST* file. The archive name is""" |
|
4199 """ built from the main script name if not designated in""" |
|
4200 """ the package list file. The version entry of the main script""" |
|
4201 """ is modified to reflect a snapshot release.</p>""" |
|
4202 )) |
|
4203 self.pluginSArchiveAct.triggered.connect( |
|
4204 self.__pluginCreateSnapshotArchives) |
|
4205 self.actions.append(self.pluginSArchiveAct) |
|
4206 |
|
4207 self.makeGrp = createActionGroup(self) |
|
4208 |
|
4209 self.makeExecuteAct = E5Action( |
|
4210 self.tr('Execute Make'), |
|
4211 self.tr('&Execute Make'), 0, 0, |
|
4212 self.makeGrp, 'project_make_execute') |
|
4213 self.makeExecuteAct.setStatusTip( |
|
4214 self.tr("Perform a 'make' run.")) |
|
4215 self.makeExecuteAct.setWhatsThis(self.tr( |
|
4216 """<b>Execute Make</b>""" |
|
4217 """<p>This performs a 'make' run to rebuild the configured""" |
|
4218 """ target.</p>""" |
|
4219 )) |
|
4220 self.makeExecuteAct.triggered.connect(self.__executeMake) |
|
4221 self.actions.append(self.makeExecuteAct) |
|
4222 |
|
4223 self.makeTestAct = E5Action( |
|
4224 self.tr('Test for Changes'), |
|
4225 self.tr('&Test for Changes'), 0, 0, |
|
4226 self.makeGrp, 'project_make_test') |
|
4227 self.makeTestAct.setStatusTip( |
|
4228 self.tr("Question 'make', if a rebuild is needed.")) |
|
4229 self.makeTestAct.setWhatsThis(self.tr( |
|
4230 """<b>Test for Changes</b>""" |
|
4231 """<p>This questions 'make', if a rebuild of the configured""" |
|
4232 """ target is necessary.</p>""" |
|
4233 )) |
|
4234 self.makeTestAct.triggered.connect( |
|
4235 lambda: self.__executeMake(questionOnly=True)) |
|
4236 self.actions.append(self.makeTestAct) |
|
4237 |
|
4238 self.closeAct.setEnabled(False) |
|
4239 self.saveAct.setEnabled(False) |
|
4240 self.saveasAct.setEnabled(False) |
|
4241 self.actGrp2.setEnabled(False) |
|
4242 self.propsAct.setEnabled(False) |
|
4243 self.userPropsAct.setEnabled(False) |
|
4244 self.filetypesAct.setEnabled(False) |
|
4245 self.lexersAct.setEnabled(False) |
|
4246 self.sessActGrp.setEnabled(False) |
|
4247 self.dbgActGrp.setEnabled(False) |
|
4248 self.pluginGrp.setEnabled(False) |
|
4249 |
|
4250 def initMenu(self): |
|
4251 """ |
|
4252 Public slot to initialize the project menu. |
|
4253 |
|
4254 @return the menu generated (QMenu) |
|
4255 """ |
|
4256 menu = QMenu(self.tr('&Project'), self.parent()) |
|
4257 self.recentMenu = QMenu(self.tr('Open &Recent Projects'), menu) |
|
4258 self.vcsMenu = QMenu(self.tr('&Version Control'), menu) |
|
4259 self.vcsMenu.setTearOffEnabled(True) |
|
4260 self.vcsProjectHelper.initMenu(self.vcsMenu) |
|
4261 self.vcsMenu.setEnabled(self.vcsSoftwareAvailable()) |
|
4262 self.checksMenu = QMenu(self.tr('Chec&k'), menu) |
|
4263 self.checksMenu.setTearOffEnabled(True) |
|
4264 self.menuShow = QMenu(self.tr('Sho&w'), menu) |
|
4265 self.graphicsMenu = QMenu(self.tr('&Diagrams'), menu) |
|
4266 self.sessionMenu = QMenu(self.tr('Session'), menu) |
|
4267 self.apidocMenu = QMenu(self.tr('Source &Documentation'), menu) |
|
4268 self.apidocMenu.setTearOffEnabled(True) |
|
4269 self.debuggerMenu = QMenu(self.tr('Debugger'), menu) |
|
4270 self.packagersMenu = QMenu(self.tr('Pac&kagers'), menu) |
|
4271 self.makeMenu = QMenu(self.tr('Make'), menu) |
|
4272 |
|
4273 self.__menus = { |
|
4274 "Main": menu, |
|
4275 "Recent": self.recentMenu, |
|
4276 "VCS": self.vcsMenu, |
|
4277 "Checks": self.checksMenu, |
|
4278 "Show": self.menuShow, |
|
4279 "Graphics": self.graphicsMenu, |
|
4280 "Session": self.sessionMenu, |
|
4281 "Apidoc": self.apidocMenu, |
|
4282 "Debugger": self.debuggerMenu, |
|
4283 "Packagers": self.packagersMenu, |
|
4284 "Make": self.makeMenu, |
|
4285 } |
|
4286 |
|
4287 # connect the aboutToShow signals |
|
4288 self.recentMenu.aboutToShow.connect(self.__showContextMenuRecent) |
|
4289 self.recentMenu.triggered.connect(self.__openRecent) |
|
4290 self.vcsMenu.aboutToShow.connect(self.__showContextMenuVCS) |
|
4291 self.checksMenu.aboutToShow.connect(self.__showContextMenuChecks) |
|
4292 self.menuShow.aboutToShow.connect(self.__showContextMenuShow) |
|
4293 self.graphicsMenu.aboutToShow.connect(self.__showContextMenuGraphics) |
|
4294 self.apidocMenu.aboutToShow.connect(self.__showContextMenuApiDoc) |
|
4295 self.packagersMenu.aboutToShow.connect(self.__showContextMenuPackagers) |
|
4296 self.sessionMenu.aboutToShow.connect(self.__showContextMenuSession) |
|
4297 self.debuggerMenu.aboutToShow.connect(self.__showContextMenuDebugger) |
|
4298 self.makeMenu.aboutToShow.connect(self.__showContextMenuMake) |
|
4299 menu.aboutToShow.connect(self.__showMenu) |
|
4300 |
|
4301 # build the show menu |
|
4302 self.menuShow.setTearOffEnabled(True) |
|
4303 self.menuShow.addAction(self.codeMetricsAct) |
|
4304 self.menuShow.addAction(self.codeCoverageAct) |
|
4305 self.menuShow.addAction(self.codeProfileAct) |
|
4306 |
|
4307 # build the diagrams menu |
|
4308 self.graphicsMenu.setTearOffEnabled(True) |
|
4309 self.graphicsMenu.addAction(self.applicationDiagramAct) |
|
4310 self.graphicsMenu.addSeparator() |
|
4311 self.graphicsMenu.addAction(self.loadDiagramAct) |
|
4312 |
|
4313 # build the session menu |
|
4314 self.sessionMenu.setTearOffEnabled(True) |
|
4315 self.sessionMenu.addActions(self.sessActGrp.actions()) |
|
4316 |
|
4317 # build the debugger menu |
|
4318 self.debuggerMenu.setTearOffEnabled(True) |
|
4319 self.debuggerMenu.addActions(self.dbgActGrp.actions()) |
|
4320 |
|
4321 # build the packagers menu |
|
4322 self.packagersMenu.setTearOffEnabled(True) |
|
4323 self.packagersMenu.addActions(self.pluginGrp.actions()) |
|
4324 self.packagersMenu.addSeparator() |
|
4325 |
|
4326 # build the make menu |
|
4327 self.makeMenu.setTearOffEnabled(True) |
|
4328 self.makeMenu.addActions(self.makeGrp.actions()) |
|
4329 self.makeMenu.addSeparator() |
|
4330 |
|
4331 # build the main menu |
|
4332 menu.setTearOffEnabled(True) |
|
4333 menu.addActions(self.actGrp1.actions()) |
|
4334 self.menuRecentAct = menu.addMenu(self.recentMenu) |
|
4335 menu.addSeparator() |
|
4336 menu.addAction(self.closeAct) |
|
4337 menu.addSeparator() |
|
4338 menu.addAction(self.saveAct) |
|
4339 menu.addAction(self.saveasAct) |
|
4340 menu.addSeparator() |
|
4341 menu.addActions(self.actGrp2.actions()) |
|
4342 menu.addSeparator() |
|
4343 self.menuMakeAct = menu.addMenu(self.makeMenu) |
|
4344 menu.addSeparator() |
|
4345 self.menuDiagramAct = menu.addMenu(self.graphicsMenu) |
|
4346 menu.addSeparator() |
|
4347 self.menuCheckAct = menu.addMenu(self.checksMenu) |
|
4348 menu.addSeparator() |
|
4349 menu.addMenu(self.vcsMenu) |
|
4350 menu.addSeparator() |
|
4351 self.menuShowAct = menu.addMenu(self.menuShow) |
|
4352 menu.addSeparator() |
|
4353 self.menuApidocAct = menu.addMenu(self.apidocMenu) |
|
4354 menu.addSeparator() |
|
4355 self.menuPackagersAct = menu.addMenu(self.packagersMenu) |
|
4356 menu.addSeparator() |
|
4357 menu.addAction(self.propsAct) |
|
4358 menu.addAction(self.userPropsAct) |
|
4359 menu.addAction(self.filetypesAct) |
|
4360 menu.addAction(self.lexersAct) |
|
4361 menu.addSeparator() |
|
4362 self.menuDebuggerAct = menu.addMenu(self.debuggerMenu) |
|
4363 self.menuSessionAct = menu.addMenu(self.sessionMenu) |
|
4364 |
|
4365 self.menuCheckAct.setEnabled(False) |
|
4366 self.menuShowAct.setEnabled(False) |
|
4367 self.menuDiagramAct.setEnabled(False) |
|
4368 self.menuSessionAct.setEnabled(False) |
|
4369 self.menuDebuggerAct.setEnabled(False) |
|
4370 self.menuApidocAct.setEnabled(False) |
|
4371 self.menuPackagersAct.setEnabled(False) |
|
4372 self.menuMakeAct.setEnabled(False) |
|
4373 |
|
4374 self.menu = menu |
|
4375 return menu |
|
4376 |
|
4377 def initToolbars(self, toolbarManager): |
|
4378 """ |
|
4379 Public slot to initialize the project toolbar and the basic VCS |
|
4380 toolbar. |
|
4381 |
|
4382 @param toolbarManager reference to a toolbar manager object |
|
4383 (E5ToolBarManager) |
|
4384 @return tuple of the generated toolbars (tuple of two QToolBar) |
|
4385 """ |
|
4386 tb = QToolBar(self.tr("Project"), self.ui) |
|
4387 tb.setIconSize(UI.Config.ToolBarIconSize) |
|
4388 tb.setObjectName("ProjectToolbar") |
|
4389 tb.setToolTip(self.tr('Project')) |
|
4390 |
|
4391 tb.addActions(self.actGrp1.actions()) |
|
4392 tb.addAction(self.closeAct) |
|
4393 tb.addSeparator() |
|
4394 tb.addAction(self.saveAct) |
|
4395 tb.addAction(self.saveasAct) |
|
4396 |
|
4397 toolbarManager.addToolBar(tb, tb.windowTitle()) |
|
4398 toolbarManager.addAction(self.addFilesAct, tb.windowTitle()) |
|
4399 toolbarManager.addAction(self.addDirectoryAct, tb.windowTitle()) |
|
4400 toolbarManager.addAction(self.addLanguageAct, tb.windowTitle()) |
|
4401 toolbarManager.addAction(self.propsAct, tb.windowTitle()) |
|
4402 toolbarManager.addAction(self.userPropsAct, tb.windowTitle()) |
|
4403 |
|
4404 import VCS |
|
4405 vcstb = VCS.getBasicHelper(self).initBasicToolbar( |
|
4406 self.ui, toolbarManager) |
|
4407 |
|
4408 return tb, vcstb |
|
4409 |
|
4410 def __showMenu(self): |
|
4411 """ |
|
4412 Private method to set up the project menu. |
|
4413 """ |
|
4414 self.menuRecentAct.setEnabled(len(self.recent) > 0) |
|
4415 |
|
4416 self.showMenu.emit("Main", self.__menus["Main"]) |
|
4417 |
|
4418 def __syncRecent(self): |
|
4419 """ |
|
4420 Private method to synchronize the list of recently opened projects |
|
4421 with the central store. |
|
4422 """ |
|
4423 for recent in self.recent[:]: |
|
4424 if Utilities.samepath(self.pfile, recent): |
|
4425 self.recent.remove(recent) |
|
4426 self.recent.insert(0, self.pfile) |
|
4427 maxRecent = Preferences.getProject("RecentNumber") |
|
4428 if len(self.recent) > maxRecent: |
|
4429 self.recent = self.recent[:maxRecent] |
|
4430 self.__saveRecent() |
|
4431 |
|
4432 def __showContextMenuRecent(self): |
|
4433 """ |
|
4434 Private method to set up the recent projects menu. |
|
4435 """ |
|
4436 self.__loadRecent() |
|
4437 |
|
4438 self.recentMenu.clear() |
|
4439 |
|
4440 idx = 1 |
|
4441 for rp in self.recent: |
|
4442 if idx < 10: |
|
4443 formatStr = '&{0:d}. {1}' |
|
4444 else: |
|
4445 formatStr = '{0:d}. {1}' |
|
4446 act = self.recentMenu.addAction( |
|
4447 formatStr.format( |
|
4448 idx, |
|
4449 Utilities.compactPath(rp, self.ui.maxMenuFilePathLen))) |
|
4450 act.setData(rp) |
|
4451 act.setEnabled(QFileInfo(rp).exists()) |
|
4452 idx += 1 |
|
4453 |
|
4454 self.recentMenu.addSeparator() |
|
4455 self.recentMenu.addAction(self.tr('&Clear'), self.clearRecent) |
|
4456 |
|
4457 def __openRecent(self, act): |
|
4458 """ |
|
4459 Private method to open a project from the list of rencently opened |
|
4460 projects. |
|
4461 |
|
4462 @param act reference to the action that triggered (QAction) |
|
4463 """ |
|
4464 file = act.data() |
|
4465 if file: |
|
4466 self.openProject(file) |
|
4467 |
|
4468 def clearRecent(self): |
|
4469 """ |
|
4470 Public method to clear the recent projects menu. |
|
4471 """ |
|
4472 self.recent = [] |
|
4473 self.__saveRecent() |
|
4474 |
|
4475 def clearHistories(self): |
|
4476 """ |
|
4477 Public method to clear the project related histories. |
|
4478 """ |
|
4479 self.clearRecent() |
|
4480 |
|
4481 for key in ["DebugClientsHistory", "DebuggerInterpreterHistory"]: |
|
4482 Preferences.setProject(key, []) |
|
4483 Preferences.syncPreferences() |
|
4484 |
|
4485 def __searchNewFiles(self): |
|
4486 """ |
|
4487 Private slot used to handle the search new files action. |
|
4488 """ |
|
4489 self.__doSearchNewFiles(False, True) |
|
4490 |
|
4491 def __searchProjectFile(self): |
|
4492 """ |
|
4493 Private slot to show the Find Project File dialog. |
|
4494 """ |
|
4495 if self.__findProjectFileDialog is None: |
|
4496 from .QuickFindFileDialog import QuickFindFileDialog |
|
4497 self.__findProjectFileDialog = QuickFindFileDialog(self) |
|
4498 self.__findProjectFileDialog.sourceFile.connect( |
|
4499 self.sourceFile) |
|
4500 self.__findProjectFileDialog.designerFile.connect( |
|
4501 self.designerFile) |
|
4502 self.__findProjectFileDialog.linguistFile.connect( |
|
4503 self.linguistFile) |
|
4504 self.__findProjectFileDialog.show() |
|
4505 self.__findProjectFileDialog.raise_() |
|
4506 self.__findProjectFileDialog.activateWindow() |
|
4507 |
|
4508 def __doSearchNewFiles(self, AI=True, onUserDemand=False): |
|
4509 """ |
|
4510 Private method to search for new files in the project directory. |
|
4511 |
|
4512 If new files were found, it shows a dialog listing these files and |
|
4513 gives the user the opportunity to select the ones he wants to |
|
4514 include. If 'Automatic Inclusion' is enabled, the new files are |
|
4515 automatically added to the project. |
|
4516 |
|
4517 @param AI flag indicating whether the automatic inclusion should |
|
4518 be honoured (boolean) |
|
4519 @param onUserDemand flag indicating whether this method was |
|
4520 requested by the user via a menu action (boolean) |
|
4521 """ |
|
4522 autoInclude = Preferences.getProject("AutoIncludeNewFiles") |
|
4523 recursiveSearch = Preferences.getProject("SearchNewFilesRecursively") |
|
4524 newFiles = [] |
|
4525 |
|
4526 ignore_patterns = [pattern for pattern, filetype in |
|
4527 self.pdata["FILETYPES"].items() |
|
4528 if filetype == '__IGNORE__'] |
|
4529 |
|
4530 dirs = self.subdirs[:] |
|
4531 for directory in dirs: |
|
4532 skip = False |
|
4533 for ignore_pattern in ignore_patterns: |
|
4534 if fnmatch.fnmatch(directory, ignore_pattern): |
|
4535 skip = True |
|
4536 break |
|
4537 if skip: |
|
4538 continue |
|
4539 |
|
4540 curpath = os.path.join(self.ppath, directory) |
|
4541 try: |
|
4542 newSources = os.listdir(curpath) |
|
4543 except OSError: |
|
4544 newSources = [] |
|
4545 if self.pdata["TRANSLATIONPATTERN"]: |
|
4546 pattern = self.pdata["TRANSLATIONPATTERN"]\ |
|
4547 .replace("%language%", "*") |
|
4548 else: |
|
4549 pattern = "*.ts" |
|
4550 binpattern = self.__binaryTranslationFile(pattern) |
|
4551 for ns in newSources: |
|
4552 # ignore hidden files and directories |
|
4553 if ns.startswith('.'): |
|
4554 continue |
|
4555 if Utilities.isWindowsPlatform() and \ |
|
4556 os.path.isdir(os.path.join(curpath, ns)) and \ |
|
4557 ns.startswith('_'): |
|
4558 # dot net hack |
|
4559 continue |
|
4560 |
|
4561 # set fn to project relative name |
|
4562 # then reset ns to fully qualified name for insertion, |
|
4563 # possibly. |
|
4564 if directory == "": |
|
4565 fn = ns |
|
4566 else: |
|
4567 fn = os.path.join(directory, ns) |
|
4568 ns = os.path.abspath(os.path.join(curpath, ns)) |
|
4569 |
|
4570 # do not bother with dirs here... |
|
4571 if os.path.isdir(ns): |
|
4572 if recursiveSearch: |
|
4573 d = self.getRelativePath(ns) |
|
4574 if d not in dirs: |
|
4575 dirs.append(d) |
|
4576 continue |
|
4577 |
|
4578 filetype = "" |
|
4579 bfn = os.path.basename(fn) |
|
4580 for pattern in reversed( |
|
4581 sorted(self.pdata["FILETYPES"].keys())): |
|
4582 if fnmatch.fnmatch(bfn, pattern): |
|
4583 filetype = self.pdata["FILETYPES"][pattern] |
|
4584 break |
|
4585 |
|
4586 if (filetype == "SOURCES" and |
|
4587 fn not in self.pdata["SOURCES"]) or \ |
|
4588 (filetype == "FORMS" and |
|
4589 fn not in self.pdata["FORMS"]) or \ |
|
4590 (filetype == "INTERFACES" and |
|
4591 fn not in self.pdata["INTERFACES"]) or \ |
|
4592 (filetype == "PROTOCOLS" and |
|
4593 fn not in self.pdata["PROTOCOLS"]) or \ |
|
4594 (filetype == "RESOURCES" and |
|
4595 fn not in self.pdata["RESOURCES"]) or \ |
|
4596 (filetype == "OTHERS" and fn not in self.pdata["OTHERS"]): |
|
4597 if autoInclude and AI: |
|
4598 self.appendFile(ns) |
|
4599 else: |
|
4600 newFiles.append(ns) |
|
4601 elif filetype == "TRANSLATIONS" and \ |
|
4602 fn not in self.pdata["TRANSLATIONS"]: |
|
4603 if fnmatch.fnmatch(ns, pattern) or \ |
|
4604 fnmatch.fnmatch(ns, binpattern): |
|
4605 if autoInclude and AI: |
|
4606 self.appendFile(ns) |
|
4607 else: |
|
4608 newFiles.append(ns) |
|
4609 |
|
4610 # if autoInclude is set there is no more work left |
|
4611 if (autoInclude and AI): |
|
4612 return |
|
4613 |
|
4614 # if newfiles is empty, put up message box informing user nothing found |
|
4615 if not newFiles: |
|
4616 if onUserDemand: |
|
4617 E5MessageBox.information( |
|
4618 self.ui, |
|
4619 self.tr("Search New Files"), |
|
4620 self.tr("There were no new files found to be added.")) |
|
4621 return |
|
4622 |
|
4623 # autoInclude is not set, show a dialog |
|
4624 from .AddFoundFilesDialog import AddFoundFilesDialog |
|
4625 dlg = AddFoundFilesDialog(newFiles, self.parent(), None) |
|
4626 res = dlg.exec_() |
|
4627 |
|
4628 # the 'Add All' button was pressed |
|
4629 if res == 1: |
|
4630 for file in newFiles: |
|
4631 self.appendFile(file) |
|
4632 |
|
4633 # the 'Add Selected' button was pressed |
|
4634 elif res == 2: |
|
4635 files = dlg.getSelection() |
|
4636 for file in files: |
|
4637 self.appendFile(file) |
|
4638 |
|
4639 def othersAdded(self, fn, updateModel=True): |
|
4640 """ |
|
4641 Public slot to be called, if something was added to the OTHERS project |
|
4642 data area. |
|
4643 |
|
4644 @param fn filename or directory name added (string) |
|
4645 @param updateModel flag indicating an update of the model is requested |
|
4646 (boolean) |
|
4647 """ |
|
4648 self.projectOthersAdded.emit(fn) |
|
4649 updateModel and self.__model.addNewItem("OTHERS", fn) |
|
4650 |
|
4651 def getActions(self): |
|
4652 """ |
|
4653 Public method to get a list of all actions. |
|
4654 |
|
4655 @return list of all actions (list of E5Action) |
|
4656 """ |
|
4657 return self.actions[:] |
|
4658 |
|
4659 def addE5Actions(self, actions): |
|
4660 """ |
|
4661 Public method to add actions to the list of actions. |
|
4662 |
|
4663 @param actions list of actions (list of E5Action) |
|
4664 """ |
|
4665 self.actions.extend(actions) |
|
4666 |
|
4667 def removeE5Actions(self, actions): |
|
4668 """ |
|
4669 Public method to remove actions from the list of actions. |
|
4670 |
|
4671 @param actions list of actions (list of E5Action) |
|
4672 """ |
|
4673 for act in actions: |
|
4674 try: |
|
4675 self.actions.remove(act) |
|
4676 except ValueError: |
|
4677 pass |
|
4678 |
|
4679 def getMenu(self, menuName): |
|
4680 """ |
|
4681 Public method to get a reference to the main menu or a submenu. |
|
4682 |
|
4683 @param menuName name of the menu (string) |
|
4684 @return reference to the requested menu (QMenu) or None |
|
4685 """ |
|
4686 try: |
|
4687 return self.__menus[menuName] |
|
4688 except KeyError: |
|
4689 return None |
|
4690 |
|
4691 def repopulateItem(self, fullname): |
|
4692 """ |
|
4693 Public slot to repopulate a named item. |
|
4694 |
|
4695 @param fullname full name of the item to repopulate (string) |
|
4696 """ |
|
4697 if not self.isOpen(): |
|
4698 return |
|
4699 |
|
4700 name = self.getRelativePath(fullname) |
|
4701 self.prepareRepopulateItem.emit(name) |
|
4702 self.__model.repopulateItem(name) |
|
4703 self.completeRepopulateItem.emit(name) |
|
4704 |
|
4705 ############################################################## |
|
4706 ## Below is the VCS interface |
|
4707 ############################################################## |
|
4708 |
|
4709 def initVCS(self, vcsSystem=None, nooverride=False): |
|
4710 """ |
|
4711 Public method used to instantiate a vcs system. |
|
4712 |
|
4713 @param vcsSystem type of VCS to be used (string) |
|
4714 @param nooverride flag indicating to ignore an override request |
|
4715 (boolean) |
|
4716 @return a reference to the vcs object |
|
4717 """ |
|
4718 vcs = None |
|
4719 forProject = True |
|
4720 override = False |
|
4721 |
|
4722 if vcsSystem is None: |
|
4723 if self.pdata["VCS"] and self.pdata["VCS"] != 'None': |
|
4724 vcsSystem = self.pdata["VCS"] |
|
4725 else: |
|
4726 forProject = False |
|
4727 |
|
4728 if forProject and self.pdata["VCS"] and self.pdata["VCS"] != 'None': |
|
4729 if self.pudata["VCSOVERRIDE"] and \ |
|
4730 not nooverride: |
|
4731 vcsSystem = self.pudata["VCSOVERRIDE"] |
|
4732 override = True |
|
4733 |
|
4734 if vcsSystem is not None: |
|
4735 import VCS |
|
4736 try: |
|
4737 vcs = VCS.factory(vcsSystem) |
|
4738 except ImportError: |
|
4739 if override: |
|
4740 # override failed, revert to original |
|
4741 self.pudata["VCSOVERRIDE"] = "" |
|
4742 return self.initVCS(nooverride=True) |
|
4743 |
|
4744 if vcs: |
|
4745 vcsExists, msg = vcs.vcsExists() |
|
4746 if not vcsExists: |
|
4747 if override: |
|
4748 # override failed, revert to original |
|
4749 QApplication.restoreOverrideCursor() |
|
4750 E5MessageBox.critical( |
|
4751 self.ui, |
|
4752 self.tr("Version Control System"), |
|
4753 self.tr( |
|
4754 "<p>The selected VCS <b>{0}</b> could not be" |
|
4755 " found. <br/>Reverting override.</p><p>{1}</p>") |
|
4756 .format(vcsSystem, msg)) |
|
4757 self.pudata["VCSOVERRIDE"] = "" |
|
4758 return self.initVCS(nooverride=True) |
|
4759 |
|
4760 QApplication.restoreOverrideCursor() |
|
4761 E5MessageBox.critical( |
|
4762 self.ui, |
|
4763 self.tr("Version Control System"), |
|
4764 self.tr( |
|
4765 "<p>The selected VCS <b>{0}</b> could not be" |
|
4766 " found.<br/>Disabling version control.</p>" |
|
4767 "<p>{1}</p>").format(vcsSystem, msg)) |
|
4768 vcs = None |
|
4769 if forProject: |
|
4770 self.pdata["VCS"] = 'None' |
|
4771 self.setDirty(True) |
|
4772 else: |
|
4773 vcs.vcsInitConfig(self) |
|
4774 |
|
4775 if vcs and forProject: |
|
4776 # set the vcs options |
|
4777 if vcs.vcsSupportCommandOptions(): |
|
4778 try: |
|
4779 vcsopt = copy.deepcopy(self.pdata["VCSOPTIONS"]) |
|
4780 vcs.vcsSetOptions(vcsopt) |
|
4781 except LookupError: |
|
4782 pass |
|
4783 # set vcs specific data |
|
4784 try: |
|
4785 vcsother = copy.deepcopy(self.pdata["VCSOTHERDATA"]) |
|
4786 vcs.vcsSetOtherData(vcsother) |
|
4787 except LookupError: |
|
4788 pass |
|
4789 |
|
4790 if forProject: |
|
4791 if vcs is None: |
|
4792 import VCS |
|
4793 self.vcsProjectHelper = VCS.getBasicHelper(self) |
|
4794 self.vcsBasicHelper = True |
|
4795 else: |
|
4796 self.vcsProjectHelper = vcs.vcsGetProjectHelper(self) |
|
4797 self.vcsBasicHelper = False |
|
4798 if self.vcsMenu is not None: |
|
4799 self.vcsProjectHelper.initMenu(self.vcsMenu) |
|
4800 self.vcsMenu.setEnabled(self.vcsSoftwareAvailable()) |
|
4801 |
|
4802 return vcs |
|
4803 |
|
4804 def resetVCS(self): |
|
4805 """ |
|
4806 Public method to reset the VCS. |
|
4807 """ |
|
4808 self.pdata["VCS"] = 'None' |
|
4809 self.vcs = self.initVCS() |
|
4810 e5App().getObject("PluginManager").deactivateVcsPlugins() |
|
4811 |
|
4812 def __showContextMenuVCS(self): |
|
4813 """ |
|
4814 Private slot called before the vcs menu is shown. |
|
4815 """ |
|
4816 self.vcsProjectHelper.showMenu() |
|
4817 if self.vcsBasicHelper: |
|
4818 self.showMenu.emit("VCS", self.vcsMenu) |
|
4819 |
|
4820 def vcsSoftwareAvailable(self): |
|
4821 """ |
|
4822 Public method to check, if some supported VCS software is available |
|
4823 to the IDE. |
|
4824 |
|
4825 @return flag indicating availability of VCS software (boolean) |
|
4826 """ |
|
4827 vcsSystemsDict = e5App().getObject("PluginManager")\ |
|
4828 .getPluginDisplayStrings("version_control") |
|
4829 return len(vcsSystemsDict) != 0 |
|
4830 |
|
4831 def __vcsStatusChanged(self): |
|
4832 """ |
|
4833 Private slot to handle a change of the overall VCS status. |
|
4834 """ |
|
4835 self.projectChanged.emit() |
|
4836 |
|
4837 ######################################################################### |
|
4838 ## Below is the interface to the checker tools |
|
4839 ######################################################################### |
|
4840 |
|
4841 def __showContextMenuChecks(self): |
|
4842 """ |
|
4843 Private slot called before the checks menu is shown. |
|
4844 """ |
|
4845 self.showMenu.emit("Checks", self.checksMenu) |
|
4846 |
|
4847 ######################################################################### |
|
4848 ## Below is the interface to the packagers tools |
|
4849 ######################################################################### |
|
4850 |
|
4851 def __showContextMenuPackagers(self): |
|
4852 """ |
|
4853 Private slot called before the packagers menu is shown. |
|
4854 """ |
|
4855 self.showMenu.emit("Packagers", self.packagersMenu) |
|
4856 |
|
4857 ######################################################################### |
|
4858 ## Below is the interface to the apidoc tools |
|
4859 ######################################################################### |
|
4860 |
|
4861 def __showContextMenuApiDoc(self): |
|
4862 """ |
|
4863 Private slot called before the apidoc menu is shown. |
|
4864 """ |
|
4865 self.showMenu.emit("Apidoc", self.apidocMenu) |
|
4866 |
|
4867 ######################################################################### |
|
4868 ## Below is the interface to the show tools |
|
4869 ######################################################################### |
|
4870 |
|
4871 def __showCodeMetrics(self): |
|
4872 """ |
|
4873 Private slot used to calculate some code metrics for the project files. |
|
4874 """ |
|
4875 files = [os.path.join(self.ppath, file) |
|
4876 for file in self.pdata["SOURCES"] if file.endswith(".py")] |
|
4877 from DataViews.CodeMetricsDialog import CodeMetricsDialog |
|
4878 self.codemetrics = CodeMetricsDialog() |
|
4879 self.codemetrics.show() |
|
4880 self.codemetrics.prepare(files, self) |
|
4881 |
|
4882 def __showCodeCoverage(self): |
|
4883 """ |
|
4884 Private slot used to show the code coverage information for the |
|
4885 project files. |
|
4886 """ |
|
4887 fn = self.getMainScript(True) |
|
4888 if fn is None: |
|
4889 E5MessageBox.critical( |
|
4890 self.ui, |
|
4891 self.tr("Coverage Data"), |
|
4892 self.tr( |
|
4893 "There is no main script defined for the" |
|
4894 " current project. Aborting")) |
|
4895 return |
|
4896 |
|
4897 tfn = Utilities.getTestFileName(fn) |
|
4898 basename = os.path.splitext(fn)[0] |
|
4899 tbasename = os.path.splitext(tfn)[0] |
|
4900 |
|
4901 # determine name of coverage file to be used |
|
4902 files = [] |
|
4903 f = "{0}.coverage".format(basename) |
|
4904 tf = "{0}.coverage".format(tbasename) |
|
4905 if os.path.isfile(f): |
|
4906 files.append(f) |
|
4907 if os.path.isfile(tf): |
|
4908 files.append(tf) |
|
4909 |
|
4910 if files: |
|
4911 if len(files) > 1: |
|
4912 fn, ok = QInputDialog.getItem( |
|
4913 None, |
|
4914 self.tr("Code Coverage"), |
|
4915 self.tr("Please select a coverage file"), |
|
4916 files, |
|
4917 0, False) |
|
4918 if not ok: |
|
4919 return |
|
4920 else: |
|
4921 fn = files[0] |
|
4922 else: |
|
4923 return |
|
4924 |
|
4925 files = [os.path.join(self.ppath, file) |
|
4926 for file in self.pdata["SOURCES"] |
|
4927 if os.path.splitext(file)[1].startswith(".py")] |
|
4928 from DataViews.PyCoverageDialog import PyCoverageDialog |
|
4929 self.codecoverage = PyCoverageDialog() |
|
4930 self.codecoverage.show() |
|
4931 self.codecoverage.start(fn, files) |
|
4932 |
|
4933 def __showProfileData(self): |
|
4934 """ |
|
4935 Private slot used to show the profiling information for the project. |
|
4936 """ |
|
4937 fn = self.getMainScript(True) |
|
4938 if fn is None: |
|
4939 E5MessageBox.critical( |
|
4940 self.ui, |
|
4941 self.tr("Profile Data"), |
|
4942 self.tr( |
|
4943 "There is no main script defined for the" |
|
4944 " current project. Aborting")) |
|
4945 return |
|
4946 |
|
4947 tfn = Utilities.getTestFileName(fn) |
|
4948 basename = os.path.splitext(fn)[0] |
|
4949 tbasename = os.path.splitext(tfn)[0] |
|
4950 |
|
4951 # determine name of profile file to be used |
|
4952 files = [] |
|
4953 f = "{0}.profile".format(basename) |
|
4954 tf = "{0}.profile".format(tbasename) |
|
4955 if os.path.isfile(f): |
|
4956 files.append(f) |
|
4957 if os.path.isfile(tf): |
|
4958 files.append(tf) |
|
4959 |
|
4960 if files: |
|
4961 if len(files) > 1: |
|
4962 fn, ok = QInputDialog.getItem( |
|
4963 None, |
|
4964 self.tr("Profile Data"), |
|
4965 self.tr("Please select a profile file"), |
|
4966 files, |
|
4967 0, False) |
|
4968 if not ok: |
|
4969 return |
|
4970 else: |
|
4971 fn = files[0] |
|
4972 else: |
|
4973 return |
|
4974 |
|
4975 from DataViews.PyProfileDialog import PyProfileDialog |
|
4976 self.profiledata = PyProfileDialog() |
|
4977 self.profiledata.show() |
|
4978 self.profiledata.start(fn) |
|
4979 |
|
4980 def __showContextMenuShow(self): |
|
4981 """ |
|
4982 Private slot called before the show menu is shown. |
|
4983 """ |
|
4984 fn = self.getMainScript(True) |
|
4985 if fn is not None: |
|
4986 tfn = Utilities.getTestFileName(fn) |
|
4987 basename = os.path.splitext(fn)[0] |
|
4988 tbasename = os.path.splitext(tfn)[0] |
|
4989 self.codeProfileAct.setEnabled( |
|
4990 os.path.isfile("{0}.profile".format(basename)) or |
|
4991 os.path.isfile("{0}.profile".format(tbasename))) |
|
4992 self.codeCoverageAct.setEnabled( |
|
4993 (self.isPy3Project() or self.isPy2Project()) and |
|
4994 (os.path.isfile("{0}.coverage".format(basename)) or |
|
4995 os.path.isfile("{0}.coverage".format(tbasename)))) |
|
4996 else: |
|
4997 self.codeProfileAct.setEnabled(False) |
|
4998 self.codeCoverageAct.setEnabled(False) |
|
4999 |
|
5000 self.showMenu.emit("Show", self.menuShow) |
|
5001 |
|
5002 ######################################################################### |
|
5003 ## Below is the interface to the diagrams |
|
5004 ######################################################################### |
|
5005 |
|
5006 def __showContextMenuGraphics(self): |
|
5007 """ |
|
5008 Private slot called before the graphics menu is shown. |
|
5009 """ |
|
5010 self.showMenu.emit("Graphics", self.graphicsMenu) |
|
5011 |
|
5012 def handleApplicationDiagram(self): |
|
5013 """ |
|
5014 Public method to handle the application diagram context menu action. |
|
5015 """ |
|
5016 res = E5MessageBox.yesNo( |
|
5017 self.ui, |
|
5018 self.tr("Application Diagram"), |
|
5019 self.tr("""Include module names?"""), |
|
5020 yesDefault=True) |
|
5021 |
|
5022 from Graphics.UMLDialog import UMLDialog |
|
5023 self.applicationDiagram = UMLDialog(UMLDialog.ApplicationDiagram, self, |
|
5024 self.parent(), noModules=not res) |
|
5025 self.applicationDiagram.show() |
|
5026 |
|
5027 def __loadDiagram(self): |
|
5028 """ |
|
5029 Private slot to load a diagram from file. |
|
5030 """ |
|
5031 from Graphics.UMLDialog import UMLDialog |
|
5032 self.loadedDiagram = None |
|
5033 loadedDiagram = UMLDialog(UMLDialog.NoDiagram, |
|
5034 self, parent=self.parent()) |
|
5035 if loadedDiagram.load(): |
|
5036 self.loadedDiagram = loadedDiagram |
|
5037 self.loadedDiagram.show(fromFile=True) |
|
5038 |
|
5039 ######################################################################### |
|
5040 ## Below is the interface to the VCS monitor thread |
|
5041 ######################################################################### |
|
5042 |
|
5043 def __statusMonitorStatus(self, status, statusMsg): |
|
5044 """ |
|
5045 Private method to receive the status monitor status. |
|
5046 |
|
5047 It simply reemits the received status. |
|
5048 |
|
5049 @param status status of the monitoring thread (string, ok, nok or off) |
|
5050 @param statusMsg explanotory text for the signaled status (string) |
|
5051 """ |
|
5052 self.vcsStatusMonitorStatus.emit(status, statusMsg) |
|
5053 |
|
5054 def setStatusMonitorInterval(self, interval): |
|
5055 """ |
|
5056 Public method to se the interval of the VCS status monitor thread. |
|
5057 |
|
5058 @param interval status monitor interval in seconds (integer) |
|
5059 """ |
|
5060 if self.vcs is not None: |
|
5061 self.vcs.setStatusMonitorInterval(interval, self) |
|
5062 |
|
5063 def getStatusMonitorInterval(self): |
|
5064 """ |
|
5065 Public method to get the monitor interval. |
|
5066 |
|
5067 @return interval in seconds (integer) |
|
5068 """ |
|
5069 if self.vcs is not None: |
|
5070 return self.vcs.getStatusMonitorInterval() |
|
5071 else: |
|
5072 return 0 |
|
5073 |
|
5074 def setStatusMonitorAutoUpdate(self, auto): |
|
5075 """ |
|
5076 Public method to enable the auto update function. |
|
5077 |
|
5078 @param auto status of the auto update function (boolean) |
|
5079 """ |
|
5080 if self.vcs is not None: |
|
5081 self.vcs.setStatusMonitorAutoUpdate(auto) |
|
5082 |
|
5083 def getStatusMonitorAutoUpdate(self): |
|
5084 """ |
|
5085 Public method to retrieve the status of the auto update function. |
|
5086 |
|
5087 @return status of the auto update function (boolean) |
|
5088 """ |
|
5089 if self.vcs is not None: |
|
5090 return self.vcs.getStatusMonitorAutoUpdate() |
|
5091 else: |
|
5092 return False |
|
5093 |
|
5094 def checkVCSStatus(self): |
|
5095 """ |
|
5096 Public method to wake up the VCS status monitor thread. |
|
5097 """ |
|
5098 if self.vcs is not None: |
|
5099 self.vcs.checkVCSStatus() |
|
5100 |
|
5101 def clearStatusMonitorCachedState(self, name): |
|
5102 """ |
|
5103 Public method to clear the cached VCS state of a file/directory. |
|
5104 |
|
5105 @param name name of the entry to be cleared (string) |
|
5106 """ |
|
5107 if self.vcs is not None: |
|
5108 self.vcs.clearStatusMonitorCachedState(name) |
|
5109 |
|
5110 def startStatusMonitor(self): |
|
5111 """ |
|
5112 Public method to start the VCS status monitor thread. |
|
5113 """ |
|
5114 if self.vcs is not None: |
|
5115 self.vcs.startStatusMonitor(self) |
|
5116 |
|
5117 def stopStatusMonitor(self): |
|
5118 """ |
|
5119 Public method to stop the VCS status monitor thread. |
|
5120 """ |
|
5121 if self.vcs is not None: |
|
5122 self.vcs.stopStatusMonitor() |
|
5123 |
|
5124 ######################################################################### |
|
5125 ## Below are the plugin development related methods |
|
5126 ######################################################################### |
|
5127 |
|
5128 def __pluginVersionToTuple(self, versionStr): |
|
5129 """ |
|
5130 Private method to convert a plug-in version string into a version |
|
5131 tuple. |
|
5132 |
|
5133 @param versionStr version string to be converted |
|
5134 @type str |
|
5135 @return version info as a tuple |
|
5136 @rtype tuple of int and str |
|
5137 """ |
|
5138 vParts = [] |
|
5139 if "-" in versionStr: |
|
5140 versionStr, additional = versionStr.split("-", 1) |
|
5141 else: |
|
5142 additional = "" |
|
5143 for part in versionStr.split("."): |
|
5144 try: |
|
5145 vParts.append(int(part)) |
|
5146 except ValueError: |
|
5147 vParts.append(part) |
|
5148 |
|
5149 if additional: |
|
5150 vParts.append(additional) |
|
5151 |
|
5152 return tuple(vParts) |
|
5153 |
|
5154 def __pluginCreatePkgList(self): |
|
5155 """ |
|
5156 Private slot to create a PKGLIST file needed for archive file creation. |
|
5157 """ |
|
5158 pkglist = os.path.join(self.ppath, "PKGLIST") |
|
5159 if os.path.exists(pkglist): |
|
5160 res = E5MessageBox.yesNo( |
|
5161 self.ui, |
|
5162 self.tr("Create Package List"), |
|
5163 self.tr( |
|
5164 "<p>The file <b>PKGLIST</b> already" |
|
5165 " exists.</p><p>Overwrite it?</p>"), |
|
5166 icon=E5MessageBox.Warning) |
|
5167 if not res: |
|
5168 return # don't overwrite |
|
5169 |
|
5170 # build the list of entries |
|
5171 lst_ = [] |
|
5172 for key in ["SOURCES", "FORMS", "RESOURCES", "TRANSLATIONS", |
|
5173 "INTERFACES", "PROTOCOLS", "OTHERS"]: |
|
5174 lst_.extend(self.pdata[key]) |
|
5175 lst = [] |
|
5176 for entry in lst_: |
|
5177 if os.path.isdir(self.getAbsolutePath(entry)): |
|
5178 lst.extend( |
|
5179 [self.getRelativePath(p) for p in |
|
5180 Utilities.direntries(self.getAbsolutePath(entry), True)]) |
|
5181 continue |
|
5182 else: |
|
5183 lst.append(entry) |
|
5184 lst.sort() |
|
5185 if "PKGLIST" in lst: |
|
5186 lst.remove("PKGLIST") |
|
5187 |
|
5188 # build the header to indicate a freshly generated list |
|
5189 header = [ |
|
5190 ";", |
|
5191 "; initial_list (REMOVE THIS LINE WHEN DONE)", |
|
5192 ";", |
|
5193 " ", |
|
5194 ] |
|
5195 |
|
5196 # write the file |
|
5197 try: |
|
5198 if self.pdata["EOL"] == 0: |
|
5199 newline = None |
|
5200 else: |
|
5201 newline = self.getEolString() |
|
5202 pkglistFile = open(pkglist, "w", encoding="utf-8", newline=newline) |
|
5203 pkglistFile.write("\n".join(header) + "\n") |
|
5204 pkglistFile.write( |
|
5205 "\n".join([Utilities.fromNativeSeparators(f) for f in lst])) |
|
5206 pkglistFile.write("\n") # ensure the file ends with an empty line |
|
5207 pkglistFile.close() |
|
5208 except IOError as why: |
|
5209 E5MessageBox.critical( |
|
5210 self.ui, |
|
5211 self.tr("Create Package List"), |
|
5212 self.tr( |
|
5213 """<p>The file <b>PKGLIST</b> could not be created.</p>""" |
|
5214 """<p>Reason: {0}</p>""").format(str(why))) |
|
5215 return |
|
5216 |
|
5217 if "PKGLIST" not in self.pdata["OTHERS"]: |
|
5218 self.appendFile("PKGLIST") |
|
5219 |
|
5220 @pyqtSlot() |
|
5221 def __pluginCreateArchives(self, snapshot=False): |
|
5222 """ |
|
5223 Private slot to create eric6 plugin archives. |
|
5224 |
|
5225 @param snapshot flag indicating snapshot archives (boolean) |
|
5226 """ |
|
5227 if not self.pdata["MAINSCRIPT"]: |
|
5228 E5MessageBox.critical( |
|
5229 self.ui, |
|
5230 self.tr("Create Plugin Archive"), |
|
5231 self.tr( |
|
5232 """The project does not have a main script defined. """ |
|
5233 """Aborting...""")) |
|
5234 return |
|
5235 |
|
5236 selectedLists = [] |
|
5237 pkglists = [os.path.basename(f) for f in |
|
5238 glob.glob(os.path.join(self.ppath, "PKGLIST*"))] |
|
5239 if len(pkglists) == 1: |
|
5240 selectedLists = [os.path.join(self.ppath, pkglists[0])] |
|
5241 elif len(pkglists) > 1: |
|
5242 dlg = E5ListSelectionDialog( |
|
5243 sorted(pkglists), title=self.tr("Create Plugin Archive"), |
|
5244 message=self.tr("Select package lists:"), |
|
5245 checkBoxSelection=True) |
|
5246 if dlg.exec_() == QDialog.Accepted: |
|
5247 selectedLists = [os.path.join(self.ppath, s) |
|
5248 for s in dlg.getSelection()] |
|
5249 else: |
|
5250 return |
|
5251 |
|
5252 if not selectedLists: |
|
5253 E5MessageBox.critical( |
|
5254 self.ui, |
|
5255 self.tr("Create Plugin Archive"), |
|
5256 self.tr("""<p>No package list files (PKGLIST*) available or""" |
|
5257 """ selected. Aborting...</p>""")) |
|
5258 return |
|
5259 |
|
5260 progress = E5ProgressDialog( |
|
5261 self.tr("Creating plugin archives..."), self.tr("Abort"), |
|
5262 0, len(selectedLists), self.tr("%v/%m Archives")) |
|
5263 progress.setMinimumDuration(0) |
|
5264 progress.setWindowTitle(self.tr("Create Plugin Archives")) |
|
5265 count = 0 |
|
5266 errors = 0 |
|
5267 for pkglist in selectedLists: |
|
5268 progress.setValue(count) |
|
5269 if progress.wasCanceled(): |
|
5270 break |
|
5271 |
|
5272 try: |
|
5273 pkglistFile = open(pkglist, "r", encoding="utf-8") |
|
5274 names = pkglistFile.read() |
|
5275 pkglistFile.close() |
|
5276 except IOError as why: |
|
5277 E5MessageBox.critical( |
|
5278 self.ui, |
|
5279 self.tr("Create Plugin Archive"), |
|
5280 self.tr( |
|
5281 """<p>The file <b>{0}</b> could not be read.</p>""" |
|
5282 """<p>Reason: {1}</p>""").format( |
|
5283 os.path.basename(pkglist), str(why))) |
|
5284 errors += 1 |
|
5285 count += 1 |
|
5286 continue |
|
5287 |
|
5288 lines = names.splitlines() |
|
5289 archiveName = "" |
|
5290 archiveVersion = "" |
|
5291 names = [] |
|
5292 listOK = True |
|
5293 for line in lines: |
|
5294 if line.startswith(";"): |
|
5295 line = line[1:].strip() |
|
5296 # it's a comment possibly containing a directive |
|
5297 # supported directives are: |
|
5298 # - archive_name= defines the name of the archive |
|
5299 # - archive_version= defines the version of the archive |
|
5300 if line.startswith("archive_name="): |
|
5301 archiveName = line.split("=")[1] |
|
5302 elif line.startswith("archive_version="): |
|
5303 archiveVersion = line.split("=")[1] |
|
5304 elif line.startswith("initial_list "): |
|
5305 E5MessageBox.critical( |
|
5306 self.ui, |
|
5307 self.tr("Create Plugin Archive"), |
|
5308 self.tr( |
|
5309 """<p>The file <b>{0}</b> is not ready yet.""" |
|
5310 """</p><p>Please rework it and delete the""" |
|
5311 """'; initial_list' line of the header.""" |
|
5312 """</p>""").format(os.path.basename(pkglist))) |
|
5313 errors += 1 |
|
5314 count += 1 |
|
5315 listOK = False |
|
5316 break |
|
5317 elif line.strip(): |
|
5318 names.append(line.strip()) |
|
5319 |
|
5320 if not listOK: |
|
5321 continue |
|
5322 |
|
5323 names = sorted(names) |
|
5324 if archiveName: |
|
5325 archive = os.path.join(self.ppath, archiveName) |
|
5326 else: |
|
5327 archive = os.path.join( |
|
5328 self.ppath, |
|
5329 self.pdata["MAINSCRIPT"].replace(".py", ".zip")) |
|
5330 try: |
|
5331 archiveFile = zipfile.ZipFile(archive, "w") |
|
5332 except IOError as why: |
|
5333 E5MessageBox.critical( |
|
5334 self.ui, |
|
5335 self.tr("Create Plugin Archive"), |
|
5336 self.tr( |
|
5337 """<p>The eric6 plugin archive file <b>{0}</b>""" |
|
5338 """ could not be created.</p>""" |
|
5339 """<p>Reason: {1}</p>""").format(archive, str(why))) |
|
5340 errors += 1 |
|
5341 count += 1 |
|
5342 continue |
|
5343 |
|
5344 for name in names: |
|
5345 if name: |
|
5346 try: |
|
5347 self.__createZipDirEntries( |
|
5348 os.path.split(name)[0], archiveFile) |
|
5349 if snapshot and name == self.pdata["MAINSCRIPT"]: |
|
5350 snapshotSource, version = \ |
|
5351 self.__createSnapshotSource( |
|
5352 os.path.join(self.ppath, |
|
5353 self.pdata["MAINSCRIPT"])) |
|
5354 archiveFile.writestr(name, snapshotSource) |
|
5355 else: |
|
5356 archiveFile.write(os.path.join(self.ppath, name), |
|
5357 name) |
|
5358 if name == self.pdata["MAINSCRIPT"]: |
|
5359 version = self.__pluginExtractVersion( |
|
5360 os.path.join(self.ppath, |
|
5361 self.pdata["MAINSCRIPT"])) |
|
5362 if archiveVersion and ( |
|
5363 self.__pluginVersionToTuple(version) < |
|
5364 self.__pluginVersionToTuple(archiveVersion) |
|
5365 ): |
|
5366 version = archiveVersion |
|
5367 except OSError as why: |
|
5368 E5MessageBox.critical( |
|
5369 self.ui, |
|
5370 self.tr("Create Plugin Archive"), |
|
5371 self.tr( |
|
5372 """<p>The file <b>{0}</b> could not be""" |
|
5373 """ stored in the archive. Ignoring it.</p>""" |
|
5374 """<p>Reason: {1}</p>""") |
|
5375 .format(os.path.join(self.ppath, name), str(why))) |
|
5376 archiveFile.writestr("VERSION", version.encode("utf-8")) |
|
5377 archiveFile.close() |
|
5378 |
|
5379 if archive not in self.pdata["OTHERS"]: |
|
5380 self.appendFile(archive) |
|
5381 |
|
5382 count += 1 |
|
5383 |
|
5384 progress.setValue(len(selectedLists)) |
|
5385 |
|
5386 if errors: |
|
5387 message = self.tr("<p>The eric6 plugin archive files were " |
|
5388 "created with some errors.</p>") |
|
5389 else: |
|
5390 message = self.tr("<p>The eric6 plugin archive files were " |
|
5391 "created successfully.</p>") |
|
5392 if self.ui.notificationsEnabled(): |
|
5393 self.ui.showNotification( |
|
5394 UI.PixmapCache.getPixmap("pluginArchive48.png"), |
|
5395 self.tr("Create Plugin Archive"), |
|
5396 message) |
|
5397 else: |
|
5398 E5MessageBox.information( |
|
5399 self.ui, |
|
5400 self.tr("Create Plugin Archive"), |
|
5401 message) |
|
5402 |
|
5403 def __pluginCreateSnapshotArchives(self): |
|
5404 """ |
|
5405 Private slot to create eric6 plugin archive snapshot releases. |
|
5406 """ |
|
5407 self.__pluginCreateArchives(True) |
|
5408 |
|
5409 def __createZipDirEntries(self, path, zipFile): |
|
5410 """ |
|
5411 Private method to create dir entries in the zip file. |
|
5412 |
|
5413 @param path name of the directory entry to create (string) |
|
5414 @param zipFile open ZipFile object (zipfile.ZipFile) |
|
5415 """ |
|
5416 if path == "" or path == "/" or path == "\\": |
|
5417 return |
|
5418 |
|
5419 if not path.endswith("/") and not path.endswith("\\"): |
|
5420 path = "{0}/".format(path) |
|
5421 |
|
5422 if path not in zipFile.namelist(): |
|
5423 self.__createZipDirEntries(os.path.split(path[:-1])[0], zipFile) |
|
5424 zipFile.writestr(path, b"") |
|
5425 |
|
5426 def __createSnapshotSource(self, filename): |
|
5427 """ |
|
5428 Private method to create a snapshot plugin version. |
|
5429 |
|
5430 The version entry in the plugin module is modified to signify |
|
5431 a snapshot version. This method appends the string "-snapshot-" |
|
5432 and date indicator to the version string. |
|
5433 |
|
5434 @param filename name of the plugin file to modify (string) |
|
5435 @return modified source (bytes), snapshot version string (string) |
|
5436 """ |
|
5437 try: |
|
5438 sourcelines, encoding = Utilities.readEncodedFile(filename) |
|
5439 sourcelines = sourcelines.splitlines(True) |
|
5440 except (IOError, UnicodeError) as why: |
|
5441 E5MessageBox.critical( |
|
5442 self.ui, |
|
5443 self.tr("Create Plugin Archive"), |
|
5444 self.tr("""<p>The plugin file <b>{0}</b> could """ |
|
5445 """not be read.</p>""" |
|
5446 """<p>Reason: {1}</p>""") |
|
5447 .format(filename, str(why))) |
|
5448 return b"", "" |
|
5449 |
|
5450 lineno = 0 |
|
5451 while lineno < len(sourcelines): |
|
5452 if sourcelines[lineno].startswith("version = "): |
|
5453 # found the line to modify |
|
5454 datestr = time.strftime("%Y%m%d") |
|
5455 lineend = sourcelines[lineno]\ |
|
5456 .replace(sourcelines[lineno].rstrip(), "") |
|
5457 sversion = "{0}-snapshot-{1}".format( |
|
5458 sourcelines[lineno].replace("version = ", "") |
|
5459 .strip()[1:-1], |
|
5460 datestr) |
|
5461 sourcelines[lineno] = '{0} + "-snapshot-{1}"{2}'.format( |
|
5462 sourcelines[lineno].rstrip(), datestr, lineend) |
|
5463 break |
|
5464 |
|
5465 lineno += 1 |
|
5466 |
|
5467 source = Utilities.encode("".join(sourcelines), encoding)[0] |
|
5468 return source, sversion |
|
5469 |
|
5470 def __pluginExtractVersion(self, filename): |
|
5471 """ |
|
5472 Private method to extract the version number entry. |
|
5473 |
|
5474 @param filename name of the plugin file (string) |
|
5475 @return version string (string) |
|
5476 """ |
|
5477 version = "0.0.0" |
|
5478 try: |
|
5479 sourcelines = Utilities.readEncodedFile(filename)[0] |
|
5480 sourcelines = sourcelines.splitlines(True) |
|
5481 except (IOError, UnicodeError) as why: |
|
5482 E5MessageBox.critical( |
|
5483 self.ui, |
|
5484 self.tr("Create Plugin Archive"), |
|
5485 self.tr( |
|
5486 """<p>The plugin file <b>{0}</b> could """ |
|
5487 """not be read.</p> <p>Reason: {1}</p>""") |
|
5488 .format(filename, str(why))) |
|
5489 return "" |
|
5490 |
|
5491 for sourceline in sourcelines: |
|
5492 if sourceline.startswith("version = "): |
|
5493 version = sourceline.replace("version = ", "").strip()\ |
|
5494 .replace('"', "").replace("'", "") |
|
5495 break |
|
5496 |
|
5497 return version |
|
5498 |
|
5499 ######################################################################### |
|
5500 ## Below are methods implementing the 'make' support |
|
5501 ######################################################################### |
|
5502 |
|
5503 def __showContextMenuMake(self): |
|
5504 """ |
|
5505 Private slot called before the make menu is shown. |
|
5506 """ |
|
5507 self.showMenu.emit("Make", self.makeMenu) |
|
5508 |
|
5509 def hasDefaultMakeParameters(self): |
|
5510 """ |
|
5511 Public method to test, if the project contains the default make |
|
5512 parameters. |
|
5513 |
|
5514 @return flag indicating default parameter set |
|
5515 @rtype bool |
|
5516 """ |
|
5517 return self.pdata["MAKEPARAMS"] == { |
|
5518 "MakeEnabled": False, |
|
5519 "MakeExecutable": "", |
|
5520 "MakeFile": "", |
|
5521 "MakeTarget": "", |
|
5522 "MakeParameters": "", |
|
5523 "MakeTestOnly": True, |
|
5524 } |
|
5525 |
|
5526 def isMakeEnabled(self): |
|
5527 """ |
|
5528 Public method to test, if make is enabled for the project. |
|
5529 |
|
5530 @return flag indicating enabled make support |
|
5531 @rtype bool |
|
5532 """ |
|
5533 return self.pdata["MAKEPARAMS"]["MakeEnabled"] |
|
5534 |
|
5535 @pyqtSlot() |
|
5536 def executeMake(self): |
|
5537 """ |
|
5538 Public slot to execute a project specific make run (auto-run) |
|
5539 (execute or question). |
|
5540 """ |
|
5541 self.__executeMake( |
|
5542 questionOnly=self.pdata["MAKEPARAMS"]["MakeTestOnly"], |
|
5543 interactive=False) |
|
5544 |
|
5545 @pyqtSlot() |
|
5546 def __executeMake(self, questionOnly=False, interactive=True): |
|
5547 """ |
|
5548 Private method to execute a project specific make run. |
|
5549 |
|
5550 @param questionOnly flag indicating to ask make for changes only |
|
5551 @type bool |
|
5552 @param interactive flag indicating an interactive invocation (i.e. |
|
5553 through a menu action) |
|
5554 @type bool |
|
5555 """ |
|
5556 if not self.pdata["MAKEPARAMS"]["MakeEnabled"] or \ |
|
5557 self.__makeProcess is not None: |
|
5558 return |
|
5559 |
|
5560 if self.pdata["MAKEPARAMS"]["MakeExecutable"]: |
|
5561 prog = self.pdata["MAKEPARAMS"]["MakeExecutable"] |
|
5562 else: |
|
5563 prog = Project.DefaultMake |
|
5564 |
|
5565 args = [] |
|
5566 if self.pdata["MAKEPARAMS"]["MakeParameters"]: |
|
5567 args.extend(Utilities.parseOptionString( |
|
5568 self.pdata["MAKEPARAMS"]["MakeParameters"])) |
|
5569 |
|
5570 if self.pdata["MAKEPARAMS"]["MakeFile"]: |
|
5571 args.append("--makefile={0}".format( |
|
5572 self.pdata["MAKEPARAMS"]["MakeFile"])) |
|
5573 |
|
5574 if questionOnly: |
|
5575 args.append("--question") |
|
5576 |
|
5577 if self.pdata["MAKEPARAMS"]["MakeTarget"]: |
|
5578 args.append(self.pdata["MAKEPARAMS"]["MakeTarget"]) |
|
5579 |
|
5580 self.__makeProcess = QProcess(self) |
|
5581 self.__makeProcess.readyReadStandardOutput.connect( |
|
5582 self.__makeReadStdOut) |
|
5583 self.__makeProcess.readyReadStandardError.connect( |
|
5584 self.__makeReadStdErr) |
|
5585 self.__makeProcess.finished.connect( |
|
5586 lambda exitCode, exitStatus: self.__makeFinished( |
|
5587 exitCode, exitStatus, questionOnly, interactive)) |
|
5588 self.__makeProcess.setWorkingDirectory(self.getProjectPath()) |
|
5589 self.__makeProcess.start(prog, args) |
|
5590 |
|
5591 if not self.__makeProcess.waitForStarted(): |
|
5592 E5MessageBox.critical( |
|
5593 self.ui, |
|
5594 self.tr("Execute Make"), |
|
5595 self.tr("""The make process did not start.""")) |
|
5596 |
|
5597 self.__cleanupMake() |
|
5598 |
|
5599 @pyqtSlot() |
|
5600 def __makeReadStdOut(self): |
|
5601 """ |
|
5602 Private slot to process process output received via stdout. |
|
5603 """ |
|
5604 if self.__makeProcess is not None: |
|
5605 output = str(self.__makeProcess.readAllStandardOutput(), |
|
5606 Preferences.getSystem("IOEncoding"), |
|
5607 'replace') |
|
5608 self.appendStdout.emit(output) |
|
5609 |
|
5610 @pyqtSlot() |
|
5611 def __makeReadStdErr(self): |
|
5612 """ |
|
5613 Private slot to process process output received via stderr. |
|
5614 """ |
|
5615 if self.__makeProcess is not None: |
|
5616 error = str(self.__makeProcess.readAllStandardError(), |
|
5617 Preferences.getSystem("IOEncoding"), |
|
5618 'replace') |
|
5619 self.appendStderr.emit(error) |
|
5620 |
|
5621 def __makeFinished(self, exitCode, exitStatus, questionOnly, |
|
5622 interactive=True): |
|
5623 """ |
|
5624 Private slot handling the make process finished signal. |
|
5625 |
|
5626 @param exitCode exit code of the make process |
|
5627 @type int |
|
5628 @param exitStatus exit status of the make process |
|
5629 @type QProcess.ExitStatus |
|
5630 @param questionOnly flag indicating a test only run |
|
5631 @type bool |
|
5632 @param interactive flag indicating an interactive invocation (i.e. |
|
5633 through a menu action) |
|
5634 @type bool |
|
5635 """ |
|
5636 if exitStatus == QProcess.CrashExit: |
|
5637 E5MessageBox.critical( |
|
5638 self.ui, |
|
5639 self.tr("Execute Make"), |
|
5640 self.tr("""The make process crashed.""")) |
|
5641 else: |
|
5642 if questionOnly and exitCode == 1: |
|
5643 # a rebuild is needed |
|
5644 title = self.tr("Test for Changes") |
|
5645 |
|
5646 if self.pdata["MAKEPARAMS"]["MakeTarget"]: |
|
5647 message = self.tr( |
|
5648 """<p>There are changes that require the configured""" |
|
5649 """ make target <b>{0}</b> to be rebuilt.</p>""")\ |
|
5650 .format(self.pdata["MAKEPARAMS"]["MakeTarget"]) |
|
5651 else: |
|
5652 message = self.tr( |
|
5653 """<p>There are changes that require the default""" |
|
5654 """ make target to be rebuilt.</p>""") |
|
5655 |
|
5656 if self.ui.notificationsEnabled() and not interactive: |
|
5657 self.ui.showNotification( |
|
5658 UI.PixmapCache.getPixmap("makefile48.png"), |
|
5659 title, |
|
5660 message) |
|
5661 else: |
|
5662 E5MessageBox.information(self.ui, title, message) |
|
5663 elif exitCode > 1: |
|
5664 E5MessageBox.critical( |
|
5665 self.ui, |
|
5666 self.tr("Execute Make"), |
|
5667 self.tr("""The makefile contains errors.""")) |
|
5668 |
|
5669 self.__cleanupMake() |
|
5670 |
|
5671 def __cleanupMake(self): |
|
5672 """ |
|
5673 Private method to clean up make related stuff. |
|
5674 """ |
|
5675 self.__makeProcess.readyReadStandardOutput.disconnect() |
|
5676 self.__makeProcess.readyReadStandardError.disconnect() |
|
5677 self.__makeProcess.finished.disconnect() |
|
5678 self.__makeProcess.deleteLater() |
|
5679 self.__makeProcess = None |
|
5680 |
|
5681 ######################################################################### |
|
5682 ## Below are methods implementing some 'IDL' support functions |
|
5683 ######################################################################### |
|
5684 |
|
5685 def hasDefaultIdlCompilerParameters(self): |
|
5686 """ |
|
5687 Public method to test, if the project contains the default IDL compiler |
|
5688 parameters. |
|
5689 |
|
5690 @return flag indicating default parameter set |
|
5691 @rtype bool |
|
5692 """ |
|
5693 return self.pdata["IDLPARAMS"] == { |
|
5694 "IncludeDirs": [], |
|
5695 "DefinedNames": [], |
|
5696 "UndefinedNames": [], |
|
5697 } |
|
5698 |
|
5699 ######################################################################### |
|
5700 ## Below are methods implementing some 'UIC' support functions |
|
5701 ######################################################################### |
|
5702 |
|
5703 def hasDefaultUicCompilerParameters(self): |
|
5704 """ |
|
5705 Public method to test, if the project contains the default uic compiler |
|
5706 parameters. |
|
5707 |
|
5708 @return flag indicating default parameter set |
|
5709 @rtype bool |
|
5710 """ |
|
5711 return self.pdata["UICPARAMS"] == { |
|
5712 "Package": "", |
|
5713 "RcSuffix": "", |
|
5714 } |
|
5715 |
|
5716 ######################################################################### |
|
5717 ## Below are methods implementing some 'RCC' support functions |
|
5718 ######################################################################### |
|
5719 |
|
5720 def hasDefaultRccCompilerParameters(self): |
|
5721 """ |
|
5722 Public method to test, if the project contains the default rcc compiler |
|
5723 parameters. |
|
5724 |
|
5725 @return flag indicating default parameter set |
|
5726 @rtype bool |
|
5727 """ |
|
5728 return self.pdata["RCCPARAMS"] == \ |
|
5729 self.getDefaultRccCompilerParameters() |
|
5730 |
|
5731 def getDefaultRccCompilerParameters(self): |
|
5732 """ |
|
5733 Public method to get the default rcc compiler parameters. |
|
5734 |
|
5735 @return dictionary containing the default rcc compiler parameters |
|
5736 @rtype dict |
|
5737 """ |
|
5738 return { |
|
5739 "CompressionThreshold": 70, # default value |
|
5740 "CompressLevel": 0, # use zlib default |
|
5741 "CompressionDisable": False, |
|
5742 "PathPrefix": "", |
|
5743 } |