Project/Project.py

branch
maintenance
changeset 6273
0daf79d65080
parent 6265
56bd09c4c297
child 6291
94e0e688dcad
equal deleted inserted replaced
6207:0a74c1efab70 6273:0daf79d65080
20 import fnmatch 20 import fnmatch
21 import copy 21 import copy
22 import zipfile 22 import zipfile
23 23
24 from PyQt5.QtCore import pyqtSlot, QFile, QFileInfo, pyqtSignal, \ 24 from PyQt5.QtCore import pyqtSlot, QFile, QFileInfo, pyqtSignal, \
25 QCryptographicHash, QIODevice, QByteArray, QObject, Qt 25 QCryptographicHash, QIODevice, QByteArray, QObject, Qt, QProcess
26 from PyQt5.QtGui import QCursor, QKeySequence 26 from PyQt5.QtGui import QCursor, QKeySequence
27 from PyQt5.QtWidgets import QLineEdit, QToolBar, QDialog, QInputDialog, \ 27 from PyQt5.QtWidgets import QLineEdit, QToolBar, QDialog, QInputDialog, \
28 QApplication, QMenu, QAction 28 QApplication, QMenu, QAction
29 from PyQt5.Qsci import QsciScintilla 29 from PyQt5.Qsci import QsciScintilla
30 30
99 @signal showMenu(str, QMenu) emitted when a menu is about to be shown. The 99 @signal showMenu(str, QMenu) emitted when a menu is about to be shown. The
100 name of the menu and a reference to the menu are given. 100 name of the menu and a reference to the menu are given.
101 @signal lexerAssociationsChanged() emitted after the lexer associations 101 @signal lexerAssociationsChanged() emitted after the lexer associations
102 have been changed 102 have been changed
103 @signal projectChanged() emitted to signal a change of the project 103 @signal projectChanged() emitted to signal a change of the project
104 @signal appendStdout(str) emitted after something was received from
105 a QProcess on stdout
106 @signal appendStderr(str) emitted after something was received from
107 a QProcess on stderr
104 """ 108 """
105 dirty = pyqtSignal(int) 109 dirty = pyqtSignal(int)
106 projectLanguageAdded = pyqtSignal(str) 110 projectLanguageAdded = pyqtSignal(str)
107 projectLanguageAddedByCode = pyqtSignal(str) 111 projectLanguageAddedByCode = pyqtSignal(str)
108 projectLanguageRemoved = pyqtSignal(str) 112 projectLanguageRemoved = pyqtSignal(str)
137 vcsStatusMonitorStatus = pyqtSignal(str, str) 141 vcsStatusMonitorStatus = pyqtSignal(str, str)
138 reinitVCS = pyqtSignal() 142 reinitVCS = pyqtSignal()
139 showMenu = pyqtSignal(str, QMenu) 143 showMenu = pyqtSignal(str, QMenu)
140 lexerAssociationsChanged = pyqtSignal() 144 lexerAssociationsChanged = pyqtSignal()
141 projectChanged = pyqtSignal() 145 projectChanged = pyqtSignal()
146 appendStdout = pyqtSignal(str)
147 appendStderr = pyqtSignal(str)
142 148
143 eols = [os.linesep, "\n", "\r", "\r\n"] 149 eols = [os.linesep, "\n", "\r", "\r\n"]
150
151 DefaultMake = "make"
152 DefaultMakefile = "makefile"
144 153
145 def __init__(self, parent=None, filename=None): 154 def __init__(self, parent=None, filename=None):
146 """ 155 """
147 Constructor 156 Constructor
148 157
168 "Python3 Files (*.py *.py3);;" 177 "Python3 Files (*.py *.py3);;"
169 "Python3 GUI Files (*.pyw *.pyw3);;"), 178 "Python3 GUI Files (*.pyw *.pyw3);;"),
170 } 179 }
171 180
172 self.vcsMenu = None 181 self.vcsMenu = None
182 self.__makeProcess = None
173 183
174 self.__initProjectTypes() 184 self.__initProjectTypes()
175 185
176 self.__initData() 186 self.__initData()
177 187
457 "PROJECTTYPESPECIFICDATA": {}, 467 "PROJECTTYPESPECIFICDATA": {},
458 "CHECKERSPARMS": {}, 468 "CHECKERSPARMS": {},
459 "PACKAGERSPARMS": {}, 469 "PACKAGERSPARMS": {},
460 "DOCUMENTATIONPARMS": {}, 470 "DOCUMENTATIONPARMS": {},
461 "OTHERTOOLSPARMS": {}, 471 "OTHERTOOLSPARMS": {},
472 "MAKEPARAMS": {
473 "MakeEnabled": False,
474 "MakeExecutable": "",
475 "MakeFile": "",
476 "MakeTarget": "",
477 "MakeParameters": "",
478 "MakeTestOnly": True,
479 },
462 "EOL": -1, 480 "EOL": -1,
463 } 481 }
464 482
465 self.__initDebugProperties() 483 self.__initDebugProperties()
466 484
534 "*.md": "OTHERS", 552 "*.md": "OTHERS",
535 "*.rst": "OTHERS", 553 "*.rst": "OTHERS",
536 "README": "OTHERS", 554 "README": "OTHERS",
537 "README.*": "OTHERS", 555 "README.*": "OTHERS",
538 "*.e4p": "OTHERS", 556 "*.e4p": "OTHERS",
557 "GNUmakefile": "OTHERS",
558 "makefile": "OTHERS",
559 "Makefile": "OTHERS",
539 } 560 }
540 561
541 # Sources 562 # Sources
542 if self.pdata["MIXEDLANGUAGE"]: 563 if self.pdata["MIXEDLANGUAGE"]:
543 sourceKey = "Mixed" 564 sourceKey = "Mixed"
1399 1420
1400 try: 1421 try:
1401 fn = os.path.join(self.ppath, langFile) 1422 fn = os.path.join(self.ppath, langFile)
1402 if os.path.exists(fn): 1423 if os.path.exists(fn):
1403 s2t(fn) 1424 s2t(fn)
1404 except EnvironmentError: 1425 except EnvironmentError as err:
1405 E5MessageBox.critical( 1426 E5MessageBox.critical(
1406 self.ui, 1427 self.ui,
1407 self.tr("Delete translation"), 1428 self.tr("Delete translation"),
1408 self.tr( 1429 self.tr(
1409 "<p>The selected translation file <b>{0}</b> could not be" 1430 "<p>The selected translation file <b>{0}</b> could not be"
1410 " deleted.</p>").format(langFile)) 1431 " deleted.</p><p>Reason: {1}</p>").format(
1432 langFile, str(err)))
1411 return 1433 return
1412 1434
1413 self.removeLanguageFile(langFile) 1435 self.removeLanguageFile(langFile)
1414 1436
1415 # now get rid of the .qm file 1437 # now get rid of the .qm file
1420 os.path.join(self.pdata["TRANSLATIONSBINPATH"], 1442 os.path.join(self.pdata["TRANSLATIONSBINPATH"],
1421 os.path.basename(qmFile))) 1443 os.path.basename(qmFile)))
1422 fn = os.path.join(self.ppath, qmFile) 1444 fn = os.path.join(self.ppath, qmFile)
1423 if os.path.exists(fn): 1445 if os.path.exists(fn):
1424 s2t(fn) 1446 s2t(fn)
1425 except EnvironmentError: 1447 except EnvironmentError as err:
1426 E5MessageBox.critical( 1448 E5MessageBox.critical(
1427 self.ui, 1449 self.ui,
1428 self.tr("Delete translation"), 1450 self.tr("Delete translation"),
1429 self.tr( 1451 self.tr(
1430 "<p>The selected translation file <b>{0}</b> could" 1452 "<p>The selected translation file <b>{0}</b> could"
1431 " not be deleted.</p>").format(qmFile)) 1453 " not be deleted.</p><p>Reason: {1}</p>").format(
1454 qmFile, str(err)))
1432 return 1455 return
1433 1456
1434 def appendFile(self, fn, isSourceFile=False, updateModel=True): 1457 def appendFile(self, fn, isSourceFile=False, updateModel=True):
1435 """ 1458 """
1436 Public method to append a file to the project. 1459 Public method to append a file to the project.
2117 pat = os.path.join( 2140 pat = os.path.join(
2118 self.ppath, head, 2141 self.ppath, head,
2119 "__pycache__", "{0}.*{1}".format(tail, ext)) 2142 "__pycache__", "{0}.*{1}".format(tail, ext))
2120 for f in glob.glob(pat): 2143 for f in glob.glob(pat):
2121 s2t(f) 2144 s2t(f)
2122 except EnvironmentError: 2145 except EnvironmentError as err:
2123 E5MessageBox.critical( 2146 E5MessageBox.critical(
2124 self.ui, 2147 self.ui,
2125 self.tr("Delete file"), 2148 self.tr("Delete file"),
2126 self.tr( 2149 self.tr(
2127 "<p>The selected file <b>{0}</b> could not be" 2150 "<p>The selected file <b>{0}</b> could not be"
2128 " deleted.</p>").format(fn)) 2151 " deleted.</p><p>Reason: {1}</p>").format(
2152 fn, str(err)))
2129 return False 2153 return False
2130 2154
2131 self.removeFile(fn) 2155 self.removeFile(fn)
2132 if ext == '.ui': 2156 if ext == '.ui':
2133 self.removeFile(fn + '.h') 2157 self.removeFile(fn + '.h')
2146 try: 2170 try:
2147 from ThirdParty.Send2Trash.send2trash import send2trash 2171 from ThirdParty.Send2Trash.send2trash import send2trash
2148 send2trash(dn) 2172 send2trash(dn)
2149 except ImportError: 2173 except ImportError:
2150 shutil.rmtree(dn, True) 2174 shutil.rmtree(dn, True)
2151 except EnvironmentError: 2175 except EnvironmentError as err:
2152 E5MessageBox.critical( 2176 E5MessageBox.critical(
2153 self.ui, 2177 self.ui,
2154 self.tr("Delete directory"), 2178 self.tr("Delete directory"),
2155 self.tr( 2179 self.tr(
2156 "<p>The selected directory <b>{0}</b> could not be" 2180 "<p>The selected directory <b>{0}</b> could not be"
2157 " deleted.</p>").format(dn)) 2181 " deleted.</p><p>Reason: {1}</p>").format(dn, str(err)))
2158 return False 2182 return False
2159 2183
2160 self.removeDirectory(dn) 2184 self.removeDirectory(dn)
2161 return True 2185 return True
2162 2186
2216 self.menuPackagersAct.setEnabled(True) 2240 self.menuPackagersAct.setEnabled(True)
2217 self.pluginGrp.setEnabled( 2241 self.pluginGrp.setEnabled(
2218 self.pdata["PROJECTTYPE"] in ["E6Plugin"]) 2242 self.pdata["PROJECTTYPE"] in ["E6Plugin"])
2219 self.addLanguageAct.setEnabled( 2243 self.addLanguageAct.setEnabled(
2220 bool(self.pdata["TRANSLATIONPATTERN"])) 2244 bool(self.pdata["TRANSLATIONPATTERN"]))
2245 self.makeGrp.setEnabled(
2246 self.pdata["MAKEPARAMS"]["MakeEnabled"])
2247 self.menuMakeAct.setEnabled(
2248 self.pdata["MAKEPARAMS"]["MakeEnabled"])
2221 2249
2222 self.projectAboutToBeCreated.emit() 2250 self.projectAboutToBeCreated.emit()
2223 2251
2224 hashStr = str(QCryptographicHash.hash( 2252 hashStr = str(QCryptographicHash.hash(
2225 QByteArray(self.ppath.encode("utf-8")), 2253 QByteArray(self.ppath.encode("utf-8")),
2239 "<p>The project directory <b>{0}</b> could not" 2267 "<p>The project directory <b>{0}</b> could not"
2240 " be created.</p>") 2268 " be created.</p>")
2241 .format(self.ppath)) 2269 .format(self.ppath))
2242 self.vcs = self.initVCS() 2270 self.vcs = self.initVCS()
2243 return 2271 return
2272
2244 # create an empty __init__.py file to make it a Python package 2273 # create an empty __init__.py file to make it a Python package
2245 # (only for Python and Python3) 2274 # (only for Python and Python3)
2246 if self.pdata["PROGLANGUAGE"] in \ 2275 if self.pdata["PROGLANGUAGE"] in \
2247 ["Python", "Python2", "Python3"]: 2276 ["Python", "Python2", "Python3"]:
2248 fn = os.path.join(self.ppath, "__init__.py") 2277 fn = os.path.join(self.ppath, "__init__.py")
2249 f = open(fn, "w", encoding="utf-8") 2278 f = open(fn, "w", encoding="utf-8")
2250 f.close() 2279 f.close()
2251 self.appendFile(fn, True) 2280 self.appendFile(fn, True)
2281
2252 # create an empty main script file, if a name was given 2282 # create an empty main script file, if a name was given
2253 if self.pdata["MAINSCRIPT"]: 2283 if self.pdata["MAINSCRIPT"]:
2254 if not os.path.isabs(self.pdata["MAINSCRIPT"]): 2284 if not os.path.isabs(self.pdata["MAINSCRIPT"]):
2255 ms = os.path.join( 2285 ms = os.path.join(
2256 self.ppath, self.pdata["MAINSCRIPT"]) 2286 self.ppath, self.pdata["MAINSCRIPT"])
2257 else: 2287 else:
2258 ms = self.pdata["MAINSCRIPT"] 2288 ms = self.pdata["MAINSCRIPT"]
2259 f = open(ms, "w") 2289 f = open(ms, "w")
2260 f.close() 2290 f.close()
2261 self.appendFile(ms, True) 2291 self.appendFile(ms, True)
2292
2293 if self.pdata["MAKEPARAMS"]["MakeEnabled"]:
2294 mf = self.pdata["MAKEPARAMS"]["MakeFile"]
2295 if mf:
2296 if not os.path.isabs(mf):
2297 mf = os.path.join(self.ppath, mf)
2298 else:
2299 mf = os.path.join(self.ppath, Project.DefaultMakefile)
2300 f = open(mf, "w")
2301 f.close()
2302 self.appendFile(mf)
2303
2262 tpd = os.path.join(self.ppath, self.translationsRoot) 2304 tpd = os.path.join(self.ppath, self.translationsRoot)
2263 if not self.translationsRoot.endswith(os.sep): 2305 if not self.translationsRoot.endswith(os.sep):
2264 tpd = os.path.dirname(tpd) 2306 tpd = os.path.dirname(tpd)
2265 if not os.path.isdir(tpd): 2307 if not os.path.isdir(tpd):
2266 os.makedirs(tpd) 2308 os.makedirs(tpd)
2303 self.ui, 2345 self.ui,
2304 self.tr("Create main script"), 2346 self.tr("Create main script"),
2305 self.tr( 2347 self.tr(
2306 "<p>The mainscript <b>{0}</b> could not" 2348 "<p>The mainscript <b>{0}</b> could not"
2307 " be created.<br/>Reason: {1}</p>") 2349 " be created.<br/>Reason: {1}</p>")
2308 .format(self.ppath, str(err))) 2350 .format(ms, str(err)))
2309 self.appendFile(ms) 2351 self.appendFile(ms, True)
2310 else: 2352 else:
2311 ms = "" 2353 ms = ""
2354
2355 if self.pdata["MAKEPARAMS"]["MakeEnabled"]:
2356 mf = self.pdata["MAKEPARAMS"]["MakeFile"]
2357 if mf:
2358 if not os.path.isabs(mf):
2359 mf = os.path.join(self.ppath, mf)
2360 else:
2361 mf = os.path.join(self.ppath, Project.DefaultMakefile)
2362 if not os.path.exists(mf):
2363 try:
2364 f = open(mf, "w")
2365 f.close()
2366 except EnvironmentError as err:
2367 E5MessageBox.critical(
2368 self.ui,
2369 self.tr("Create Makefile"),
2370 self.tr(
2371 "<p>The makefile <b>{0}</b> could not"
2372 " be created.<br/>Reason: {1}</p>")
2373 .format(mf, str(err)))
2374 self.appendFile(mf)
2312 2375
2313 # add existing files to the project 2376 # add existing files to the project
2314 res = E5MessageBox.yesNo( 2377 res = E5MessageBox.yesNo(
2315 self.ui, 2378 self.ui,
2316 self.tr("New Project"), 2379 self.tr("New Project"),
2565 else: 2628 else:
2566 ms = self.pdata["MAINSCRIPT"] 2629 ms = self.pdata["MAINSCRIPT"]
2567 if os.path.exists(ms): 2630 if os.path.exists(ms):
2568 self.appendFile(ms) 2631 self.appendFile(ms)
2569 2632
2633 if self.pdata["MAKEPARAMS"]["MakeEnabled"]:
2634 mf = self.pdata["MAKEPARAMS"]["MakeFile"]
2635 if mf:
2636 if not os.path.isabs(mf):
2637 mf = os.path.join(self.ppath, mf)
2638 else:
2639 mf = os.path.join(self.ppath, Project.DefaultMakefile)
2640 if not os.path.exists(mf):
2641 try:
2642 f = open(mf, "w")
2643 f.close()
2644 except EnvironmentError as err:
2645 E5MessageBox.critical(
2646 self.ui,
2647 self.tr("Create Makefile"),
2648 self.tr(
2649 "<p>The makefile <b>{0}</b> could not"
2650 " be created.<br/>Reason: {1}</p>")
2651 .format(mf, str(err)))
2652 self.appendFile(mf)
2653
2570 if self.pdata["PROJECTTYPE"] != projectType: 2654 if self.pdata["PROJECTTYPE"] != projectType:
2571 # reinitialize filetype associations 2655 # reinitialize filetype associations
2572 self.initFileTypes() 2656 self.initFileTypes()
2573 2657
2574 if self.translationsRoot: 2658 if self.translationsRoot:
2809 self.menuPackagersAct.setEnabled(True) 2893 self.menuPackagersAct.setEnabled(True)
2810 self.pluginGrp.setEnabled( 2894 self.pluginGrp.setEnabled(
2811 self.pdata["PROJECTTYPE"] in ["E6Plugin"]) 2895 self.pdata["PROJECTTYPE"] in ["E6Plugin"])
2812 self.addLanguageAct.setEnabled( 2896 self.addLanguageAct.setEnabled(
2813 bool(self.pdata["TRANSLATIONPATTERN"])) 2897 bool(self.pdata["TRANSLATIONPATTERN"]))
2898 self.makeGrp.setEnabled(
2899 self.pdata["MAKEPARAMS"]["MakeEnabled"])
2900 self.menuMakeAct.setEnabled(
2901 self.pdata["MAKEPARAMS"]["MakeEnabled"])
2814 2902
2815 self.__model.projectOpened() 2903 self.__model.projectOpened()
2816 self.projectOpenedHooks.emit() 2904 self.projectOpenedHooks.emit()
2817 self.projectOpened.emit() 2905 self.projectOpened.emit()
2818 2906
3006 if Preferences.getProject("AutoSaveDbgProperties") and \ 3094 if Preferences.getProject("AutoSaveDbgProperties") and \
3007 self.isDebugPropertiesLoaded() and \ 3095 self.isDebugPropertiesLoaded() and \
3008 not noSave: 3096 not noSave:
3009 self.__writeDebugProperties(True) 3097 self.__writeDebugProperties(True)
3010 3098
3011 # now save all open modified files of the project
3012 vm = e5App().getObject("ViewManager") 3099 vm = e5App().getObject("ViewManager")
3100
3101 # check dirty status of all project files first
3102 for fn in vm.getOpenFilenames():
3103 if self.isProjectFile(fn):
3104 reset = vm.checkFileDirty(fn)
3105 if not reset:
3106 # abort shutting down
3107 return False
3108
3109 # close all project related editors
3013 success = True 3110 success = True
3014 for fn in vm.getOpenFilenames(): 3111 for fn in vm.getOpenFilenames():
3015 if self.isProjectFile(fn): 3112 if self.isProjectFile(fn):
3016 success &= vm.closeWindow(fn) 3113 success &= vm.closeWindow(fn, ignoreDirty=True)
3017
3018 if not success: 3114 if not success:
3019 return False 3115 return False
3020 3116
3021 # stop the VCS monitor thread 3117 # stop the VCS monitor thread
3022 if self.vcs is not None: 3118 if self.vcs is not None:
3033 self.vcs.vcsShutdown() 3129 self.vcs.vcsShutdown()
3034 self.vcs.deleteLater() 3130 self.vcs.deleteLater()
3035 self.vcs = None 3131 self.vcs = None
3036 e5App().getObject("PluginManager").deactivateVcsPlugins() 3132 e5App().getObject("PluginManager").deactivateVcsPlugins()
3037 3133
3038 # now close all project related windows 3134 # now close all project related tool windows
3039 self.__closeAllWindows() 3135 self.__closeAllWindows()
3040 3136
3041 self.__initData() 3137 self.__initData()
3042 self.closeAct.setEnabled(False) 3138 self.closeAct.setEnabled(False)
3043 self.saveasAct.setEnabled(False) 3139 self.saveasAct.setEnabled(False)
3055 self.menuShowAct.setEnabled(False) 3151 self.menuShowAct.setEnabled(False)
3056 self.menuDiagramAct.setEnabled(False) 3152 self.menuDiagramAct.setEnabled(False)
3057 self.menuApidocAct.setEnabled(False) 3153 self.menuApidocAct.setEnabled(False)
3058 self.menuPackagersAct.setEnabled(False) 3154 self.menuPackagersAct.setEnabled(False)
3059 self.pluginGrp.setEnabled(False) 3155 self.pluginGrp.setEnabled(False)
3156 self.makeGrp.setEnabled(False)
3157 self.menuMakeAct.setEnabled(False)
3060 3158
3061 self.__model.projectClosed() 3159 self.__model.projectClosed()
3062 self.projectClosedHooks.emit() 3160 self.projectClosedHooks.emit()
3063 self.projectClosed.emit() 3161 self.projectClosed.emit()
3064 3162
4029 )) 4127 ))
4030 self.pluginSArchiveAct.triggered.connect( 4128 self.pluginSArchiveAct.triggered.connect(
4031 self.__pluginCreateSnapshotArchives) 4129 self.__pluginCreateSnapshotArchives)
4032 self.actions.append(self.pluginSArchiveAct) 4130 self.actions.append(self.pluginSArchiveAct)
4033 4131
4132 self.makeGrp = createActionGroup(self)
4133
4134 self.makeExecuteAct = E5Action(
4135 self.tr('Execute Make'),
4136 self.tr('&Execute Make'), 0, 0,
4137 self.makeGrp, 'project_make_execute')
4138 self.makeExecuteAct.setStatusTip(
4139 self.tr("Perform a 'make' run."))
4140 self.makeExecuteAct.setWhatsThis(self.tr(
4141 """<b>Execute Make</b>"""
4142 """<p>This performs a 'make' run to rebuild the configured"""
4143 """ target.</p>"""
4144 ))
4145 self.makeExecuteAct.triggered.connect(self.__executeMake)
4146 self.actions.append(self.makeExecuteAct)
4147
4148 self.makeTestAct = E5Action(
4149 self.tr('Test for Changes'),
4150 self.tr('&Test for Changes'), 0, 0,
4151 self.makeGrp, 'project_make_test')
4152 self.makeTestAct.setStatusTip(
4153 self.tr("Question 'make', if a rebuild is needed."))
4154 self.makeTestAct.setWhatsThis(self.tr(
4155 """<b>Test for Changes</b>"""
4156 """<p>This questions 'make', if a rebuild of the configured"""
4157 """ target is necessary.</p>"""
4158 ))
4159 self.makeTestAct.triggered.connect(
4160 lambda: self.__executeMake(questionOnly=True))
4161 self.actions.append(self.makeTestAct)
4162
4034 self.closeAct.setEnabled(False) 4163 self.closeAct.setEnabled(False)
4035 self.saveAct.setEnabled(False) 4164 self.saveAct.setEnabled(False)
4036 self.saveasAct.setEnabled(False) 4165 self.saveasAct.setEnabled(False)
4037 self.actGrp2.setEnabled(False) 4166 self.actGrp2.setEnabled(False)
4038 self.propsAct.setEnabled(False) 4167 self.propsAct.setEnabled(False)
4062 self.sessionMenu = QMenu(self.tr('Session'), menu) 4191 self.sessionMenu = QMenu(self.tr('Session'), menu)
4063 self.apidocMenu = QMenu(self.tr('Source &Documentation'), menu) 4192 self.apidocMenu = QMenu(self.tr('Source &Documentation'), menu)
4064 self.apidocMenu.setTearOffEnabled(True) 4193 self.apidocMenu.setTearOffEnabled(True)
4065 self.debuggerMenu = QMenu(self.tr('Debugger'), menu) 4194 self.debuggerMenu = QMenu(self.tr('Debugger'), menu)
4066 self.packagersMenu = QMenu(self.tr('Pac&kagers'), menu) 4195 self.packagersMenu = QMenu(self.tr('Pac&kagers'), menu)
4067 self.packagersMenu.setTearOffEnabled(True) 4196 self.makeMenu = QMenu(self.tr('Make'), menu)
4068 4197
4069 self.__menus = { 4198 self.__menus = {
4070 "Main": menu, 4199 "Main": menu,
4071 "Recent": self.recentMenu, 4200 "Recent": self.recentMenu,
4072 "VCS": self.vcsMenu, 4201 "VCS": self.vcsMenu,
4075 "Graphics": self.graphicsMenu, 4204 "Graphics": self.graphicsMenu,
4076 "Session": self.sessionMenu, 4205 "Session": self.sessionMenu,
4077 "Apidoc": self.apidocMenu, 4206 "Apidoc": self.apidocMenu,
4078 "Debugger": self.debuggerMenu, 4207 "Debugger": self.debuggerMenu,
4079 "Packagers": self.packagersMenu, 4208 "Packagers": self.packagersMenu,
4209 "Make": self.makeMenu,
4080 } 4210 }
4081 4211
4082 # connect the aboutToShow signals 4212 # connect the aboutToShow signals
4083 self.recentMenu.aboutToShow.connect(self.__showContextMenuRecent) 4213 self.recentMenu.aboutToShow.connect(self.__showContextMenuRecent)
4084 self.recentMenu.triggered.connect(self.__openRecent) 4214 self.recentMenu.triggered.connect(self.__openRecent)
4088 self.graphicsMenu.aboutToShow.connect(self.__showContextMenuGraphics) 4218 self.graphicsMenu.aboutToShow.connect(self.__showContextMenuGraphics)
4089 self.apidocMenu.aboutToShow.connect(self.__showContextMenuApiDoc) 4219 self.apidocMenu.aboutToShow.connect(self.__showContextMenuApiDoc)
4090 self.packagersMenu.aboutToShow.connect(self.__showContextMenuPackagers) 4220 self.packagersMenu.aboutToShow.connect(self.__showContextMenuPackagers)
4091 self.sessionMenu.aboutToShow.connect(self.__showContextMenuSession) 4221 self.sessionMenu.aboutToShow.connect(self.__showContextMenuSession)
4092 self.debuggerMenu.aboutToShow.connect(self.__showContextMenuDebugger) 4222 self.debuggerMenu.aboutToShow.connect(self.__showContextMenuDebugger)
4223 self.makeMenu.aboutToShow.connect(self.__showContextMenuMake)
4093 menu.aboutToShow.connect(self.__showMenu) 4224 menu.aboutToShow.connect(self.__showMenu)
4094 4225
4095 # build the show menu 4226 # build the show menu
4096 self.menuShow.setTearOffEnabled(True) 4227 self.menuShow.setTearOffEnabled(True)
4097 self.menuShow.addAction(self.codeMetricsAct) 4228 self.menuShow.addAction(self.codeMetricsAct)
4111 # build the debugger menu 4242 # build the debugger menu
4112 self.debuggerMenu.setTearOffEnabled(True) 4243 self.debuggerMenu.setTearOffEnabled(True)
4113 self.debuggerMenu.addActions(self.dbgActGrp.actions()) 4244 self.debuggerMenu.addActions(self.dbgActGrp.actions())
4114 4245
4115 # build the packagers menu 4246 # build the packagers menu
4247 self.packagersMenu.setTearOffEnabled(True)
4116 self.packagersMenu.addActions(self.pluginGrp.actions()) 4248 self.packagersMenu.addActions(self.pluginGrp.actions())
4117 self.packagersMenu.addSeparator() 4249 self.packagersMenu.addSeparator()
4250
4251 # build the make menu
4252 self.makeMenu.setTearOffEnabled(True)
4253 self.makeMenu.addActions(self.makeGrp.actions())
4254 self.makeMenu.addSeparator()
4118 4255
4119 # build the main menu 4256 # build the main menu
4120 menu.setTearOffEnabled(True) 4257 menu.setTearOffEnabled(True)
4121 menu.addActions(self.actGrp1.actions()) 4258 menu.addActions(self.actGrp1.actions())
4122 self.menuRecentAct = menu.addMenu(self.recentMenu) 4259 self.menuRecentAct = menu.addMenu(self.recentMenu)
4128 menu.addSeparator() 4265 menu.addSeparator()
4129 self.menuDebuggerAct = menu.addMenu(self.debuggerMenu) 4266 self.menuDebuggerAct = menu.addMenu(self.debuggerMenu)
4130 self.menuSessionAct = menu.addMenu(self.sessionMenu) 4267 self.menuSessionAct = menu.addMenu(self.sessionMenu)
4131 menu.addSeparator() 4268 menu.addSeparator()
4132 menu.addActions(self.actGrp2.actions()) 4269 menu.addActions(self.actGrp2.actions())
4270 menu.addSeparator()
4271 self.menuMakeAct = menu.addMenu(self.makeMenu)
4133 menu.addSeparator() 4272 menu.addSeparator()
4134 self.menuDiagramAct = menu.addMenu(self.graphicsMenu) 4273 self.menuDiagramAct = menu.addMenu(self.graphicsMenu)
4135 menu.addSeparator() 4274 menu.addSeparator()
4136 self.menuCheckAct = menu.addMenu(self.checksMenu) 4275 self.menuCheckAct = menu.addMenu(self.checksMenu)
4137 menu.addSeparator() 4276 menu.addSeparator()
4153 self.menuDiagramAct.setEnabled(False) 4292 self.menuDiagramAct.setEnabled(False)
4154 self.menuSessionAct.setEnabled(False) 4293 self.menuSessionAct.setEnabled(False)
4155 self.menuDebuggerAct.setEnabled(False) 4294 self.menuDebuggerAct.setEnabled(False)
4156 self.menuApidocAct.setEnabled(False) 4295 self.menuApidocAct.setEnabled(False)
4157 self.menuPackagersAct.setEnabled(False) 4296 self.menuPackagersAct.setEnabled(False)
4297 self.menuMakeAct.setEnabled(False)
4158 4298
4159 self.menu = menu 4299 self.menu = menu
4160 return menu 4300 return menu
4161 4301
4162 def initToolbars(self, toolbarManager): 4302 def initToolbars(self, toolbarManager):
5256 version = sourceline.replace("version = ", "").strip()\ 5396 version = sourceline.replace("version = ", "").strip()\
5257 .replace('"', "").replace("'", "") 5397 .replace('"', "").replace("'", "")
5258 break 5398 break
5259 5399
5260 return version 5400 return version
5401
5402 #########################################################################
5403 ## Below are methods implementing the 'make' support
5404 #########################################################################
5405
5406 def __showContextMenuMake(self):
5407 """
5408 Private slot called before the make menu is shown.
5409 """
5410 self.showMenu.emit("Make", self.makeMenu)
5411
5412 def hasDefaultMakeParameters(self):
5413 """
5414 Public method to test, if the project contains the default make
5415 parameters.
5416
5417 @return flag indicating default parameter set
5418 @rtype bool
5419 """
5420 return self.pdata["MAKEPARAMS"] == {
5421 "MakeEnabled": False,
5422 "MakeExecutable": "",
5423 "MakeFile": "",
5424 "MakeTarget": "",
5425 "MakeParameters": "",
5426 "MakeTestOnly": True,
5427 }
5428
5429 def isMakeEnabled(self):
5430 """
5431 Public method to test, if make is enabled for the project.
5432
5433 @return flag indicating enabled make support
5434 @rtype bool
5435 """
5436 return self.pdata["MAKEPARAMS"]["MakeEnabled"]
5437
5438 @pyqtSlot()
5439 def executeMake(self):
5440 """
5441 Public slot to execute a project specific make run (auto-run)
5442 (execute or question).
5443 """
5444 self.__executeMake(
5445 questionOnly=self.pdata["MAKEPARAMS"]["MakeTestOnly"],
5446 interactive=False)
5447
5448 @pyqtSlot()
5449 def __executeMake(self, questionOnly=False, interactive=True):
5450 """
5451 Private method to execute a project specific make run.
5452
5453 @param questionOnly flag indicating to ask make for changes only
5454 @type bool
5455 @param interactive flag indicating an interactive invocation (i.e.
5456 through a menu action)
5457 @type bool
5458 """
5459 if not self.pdata["MAKEPARAMS"]["MakeEnabled"] or \
5460 self.__makeProcess is not None:
5461 return
5462
5463 if self.pdata["MAKEPARAMS"]["MakeExecutable"]:
5464 prog = self.pdata["MAKEPARAMS"]["MakeExecutable"]
5465 else:
5466 prog = Project.DefaultMake
5467
5468 args = []
5469 if self.pdata["MAKEPARAMS"]["MakeParameters"]:
5470 args.extend(Utilities.parseOptionString(
5471 self.pdata["MAKEPARAMS"]["MakeParameters"]))
5472
5473 if self.pdata["MAKEPARAMS"]["MakeFile"]:
5474 args.append("--makefile={0}".format(
5475 self.pdata["MAKEPARAMS"]["MakeFile"]))
5476
5477 if questionOnly:
5478 args.append("--question")
5479
5480 if self.pdata["MAKEPARAMS"]["MakeTarget"]:
5481 args.append(self.pdata["MAKEPARAMS"]["MakeTarget"])
5482
5483 self.__makeProcess = QProcess(self)
5484 self.__makeProcess.readyReadStandardOutput.connect(
5485 self.__makeReadStdOut)
5486 self.__makeProcess.readyReadStandardError.connect(
5487 self.__makeReadStdErr)
5488 self.__makeProcess.finished.connect(
5489 lambda exitCode, exitStatus: self.__makeFinished(
5490 exitCode, exitStatus, questionOnly, interactive))
5491 self.__makeProcess.setWorkingDirectory(self.getProjectPath())
5492 self.__makeProcess.start(prog, args)
5493
5494 if not self.__makeProcess.waitForStarted():
5495 E5MessageBox.critical(
5496 self.ui,
5497 self.tr("Execute Make"),
5498 self.tr("""The make process did not start."""))
5499
5500 self.__cleanupMake()
5501
5502 @pyqtSlot()
5503 def __makeReadStdOut(self):
5504 """
5505 Private slot to process process output received via stdout.
5506 """
5507 if self.__makeProcess is not None:
5508 output = str(self.__makeProcess.readAllStandardOutput(),
5509 Preferences.getSystem("IOEncoding"),
5510 'replace')
5511 self.appendStdout.emit(output)
5512
5513 @pyqtSlot()
5514 def __makeReadStdErr(self):
5515 """
5516 Private slot to process process output received via stderr.
5517 """
5518 if self.__makeProcess is not None:
5519 error = str(self.__makeProcess.readAllStandardError(),
5520 Preferences.getSystem("IOEncoding"),
5521 'replace')
5522 self.appendStderr.emit(error)
5523
5524 def __makeFinished(self, exitCode, exitStatus, questionOnly,
5525 interactive=True):
5526 """
5527 Private slot handling the make process finished signal.
5528
5529 @param exitCode exit code of the make process
5530 @type int
5531 @param exitStatus exit status of the make process
5532 @type QProcess.ExitStatus
5533 @param questionOnly flag indicating a test only run
5534 @type bool
5535 @param interactive flag indicating an interactive invocation (i.e.
5536 through a menu action)
5537 @type bool
5538 """
5539 if exitStatus == QProcess.CrashExit:
5540 E5MessageBox.critical(
5541 self.ui,
5542 self.tr("Execute Make"),
5543 self.tr("""The make process crashed."""))
5544 else:
5545 if questionOnly and exitCode == 1:
5546 # a rebuild is needed
5547 title = self.tr("Test for Changes")
5548
5549 if self.pdata["MAKEPARAMS"]["MakeTarget"]:
5550 message = self.tr(
5551 """<p>There are changes that require the configured"""
5552 """ make target <b>{0}</b> to be rebuilt.</p>""")\
5553 .format(self.pdata["MAKEPARAMS"]["MakeTarget"])
5554 else:
5555 message = self.tr(
5556 """<p>There are changes that require the default"""
5557 """ make target to be rebuilt.</p>""")
5558
5559 if self.ui.notificationsEnabled() and not interactive:
5560 self.ui.showNotification(
5561 UI.PixmapCache.getPixmap("makefile48.png"),
5562 title,
5563 message)
5564 else:
5565 E5MessageBox.information(self.ui, title, message)
5566 elif exitCode > 1:
5567 E5MessageBox.critical(
5568 self.ui,
5569 self.tr("Execute Make"),
5570 self.tr("""The makefile contains errors."""))
5571
5572 self.__cleanupMake()
5573
5574 def __cleanupMake(self):
5575 """
5576 Private method to clean up make related stuff.
5577 """
5578 self.__makeProcess.readyReadStandardOutput.disconnect()
5579 self.__makeProcess.readyReadStandardError.disconnect()
5580 self.__makeProcess.finished.disconnect()
5581 self.__makeProcess.deleteLater()
5582 self.__makeProcess = None

eric ide

mercurial