13 import glob |
13 import glob |
14 import fnmatch |
14 import fnmatch |
15 import copy |
15 import copy |
16 import zipfile |
16 import zipfile |
17 import contextlib |
17 import contextlib |
|
18 import pathlib |
18 |
19 |
19 from PyQt6.QtCore import ( |
20 from PyQt6.QtCore import ( |
20 pyqtSlot, QFile, QFileInfo, pyqtSignal, QCryptographicHash, QIODevice, |
21 pyqtSlot, QFile, pyqtSignal, QCryptographicHash, QIODevice, QByteArray, |
21 QByteArray, QObject, QProcess |
22 QObject, QProcess |
22 ) |
23 ) |
23 from PyQt6.QtGui import QKeySequence, QAction |
24 from PyQt6.QtGui import QKeySequence, QAction |
24 from PyQt6.QtWidgets import ( |
25 from PyQt6.QtWidgets import ( |
25 QLineEdit, QToolBar, QDialog, QInputDialog, QMenu |
26 QLineEdit, QToolBar, QDialog, QInputDialog, QMenu |
26 ) |
27 ) |
689 self.recent = [] |
691 self.recent = [] |
690 Preferences.Prefs.rsettings.sync() |
692 Preferences.Prefs.rsettings.sync() |
691 rp = Preferences.Prefs.rsettings.value(recentNameProject) |
693 rp = Preferences.Prefs.rsettings.value(recentNameProject) |
692 if rp is not None: |
694 if rp is not None: |
693 for f in rp: |
695 for f in rp: |
694 if QFileInfo(f).exists(): |
696 if pathlib.Path(f).exists(): |
695 self.recent.append(f) |
697 self.recent.append(f) |
696 |
698 |
697 def __saveRecent(self): |
699 def __saveRecent(self): |
698 """ |
700 """ |
699 Private method to save the list of recently opened filenames. |
701 Private method to save the list of recently opened filenames. |
1188 fn1, ext = os.path.splitext(os.path.basename(self.pfile)) |
1190 fn1, ext = os.path.splitext(os.path.basename(self.pfile)) |
1189 fn = os.path.join(self.getProjectManagementDir(), |
1191 fn = os.path.join(self.getProjectManagementDir(), |
1190 '{0}.edj'.format(fn1)) |
1192 '{0}.edj'.format(fn1)) |
1191 if os.path.exists(fn): |
1193 if os.path.exists(fn): |
1192 # try the new JSON based format first |
1194 # try the new JSON based format first |
1193 self.__debuggerPropertiesFile.readFile(fn) |
1195 if self.__debuggerPropertiesFile.readFile(fn): |
|
1196 self.debugPropertiesLoaded = True |
|
1197 self.debugPropertiesChanged = False |
1194 else: |
1198 else: |
1195 # try the old XML based format second |
1199 # try the old XML based format second |
1196 fn = os.path.join(self.getProjectManagementDir(), |
1200 fn = os.path.join(self.getProjectManagementDir(), |
1197 '{0}.e4d'.format(fn1)) |
1201 '{0}.e4d'.format(fn1)) |
1198 |
1202 |
2339 bool(self.pdata["TRANSLATIONPATTERN"])) |
2343 bool(self.pdata["TRANSLATIONPATTERN"])) |
2340 self.makeGrp.setEnabled( |
2344 self.makeGrp.setEnabled( |
2341 self.pdata["MAKEPARAMS"]["MakeEnabled"]) |
2345 self.pdata["MAKEPARAMS"]["MakeEnabled"]) |
2342 self.menuMakeAct.setEnabled( |
2346 self.menuMakeAct.setEnabled( |
2343 self.pdata["MAKEPARAMS"]["MakeEnabled"]) |
2347 self.pdata["MAKEPARAMS"]["MakeEnabled"]) |
|
2348 self.menuOtherToolsAct.setEnabled(True) |
2344 |
2349 |
2345 self.projectAboutToBeCreated.emit() |
2350 self.projectAboutToBeCreated.emit() |
2346 |
2351 |
2347 hashStr = str(QCryptographicHash.hash( |
2352 hashStr = str(QCryptographicHash.hash( |
2348 QByteArray(self.ppath.encode("utf-8")), |
2353 QByteArray(self.ppath.encode("utf-8")), |
3031 bool(self.pdata["TRANSLATIONPATTERN"])) |
3036 bool(self.pdata["TRANSLATIONPATTERN"])) |
3032 self.makeGrp.setEnabled( |
3037 self.makeGrp.setEnabled( |
3033 self.pdata["MAKEPARAMS"]["MakeEnabled"]) |
3038 self.pdata["MAKEPARAMS"]["MakeEnabled"]) |
3034 self.menuMakeAct.setEnabled( |
3039 self.menuMakeAct.setEnabled( |
3035 self.pdata["MAKEPARAMS"]["MakeEnabled"]) |
3040 self.pdata["MAKEPARAMS"]["MakeEnabled"]) |
|
3041 self.menuOtherToolsAct.setEnabled(True) |
3036 |
3042 |
3037 # open a project debugger properties file being quiet |
3043 # open a project debugger properties file being quiet |
3038 # about errors |
3044 # about errors |
3039 if Preferences.getProject("AutoLoadDbgProperties"): |
3045 if Preferences.getProject("AutoLoadDbgProperties"): |
3040 self.__readDebugProperties(True) |
3046 self.__readDebugProperties(True) |
3122 self.tr("Project Files (*.epj)"), |
3128 self.tr("Project Files (*.epj)"), |
3123 defaultFilter, |
3129 defaultFilter, |
3124 EricFileDialog.DontConfirmOverwrite) |
3130 EricFileDialog.DontConfirmOverwrite) |
3125 |
3131 |
3126 if fn: |
3132 if fn: |
3127 ext = QFileInfo(fn).suffix() |
3133 fpath = pathlib.Path(fn) |
3128 if not ext: |
3134 if not fpath.suffix: |
3129 ex = selectedFilter.split("(*")[1].split(")")[0] |
3135 ex = selectedFilter.split("(*")[1].split(")")[0] |
3130 if ex: |
3136 if ex: |
3131 fn += ex |
3137 fpath = fpath.with_suffix(ex) |
3132 if QFileInfo(fn).exists(): |
3138 if fpath.exists(): |
3133 res = EricMessageBox.yesNo( |
3139 res = EricMessageBox.yesNo( |
3134 self.ui, |
3140 self.ui, |
3135 self.tr("Save File"), |
3141 self.tr("Save File"), |
3136 self.tr("""<p>The file <b>{0}</b> already exists.""" |
3142 self.tr("""<p>The file <b>{0}</b> already exists.""" |
3137 """ Overwrite it?</p>""").format(fn), |
3143 """ Overwrite it?</p>""").format(fpath), |
3138 icon=EricMessageBox.Warning) |
3144 icon=EricMessageBox.Warning) |
3139 if not res: |
3145 if not res: |
3140 return False |
3146 return False |
3141 |
3147 |
3142 self.name = QFileInfo(fn).baseName() |
3148 self.name = fpath.stem |
3143 ok = self.__writeProject(fn) |
3149 ok = self.__writeProject(str(fpath)) |
3144 |
3150 |
3145 if ok: |
3151 if ok: |
3146 # create management directory if not present |
3152 # create management directory if not present |
3147 self.createProjectManagementDir() |
3153 self.createProjectManagementDir() |
3148 |
3154 |
3287 self.menuApidocAct.setEnabled(False) |
3293 self.menuApidocAct.setEnabled(False) |
3288 self.menuPackagersAct.setEnabled(False) |
3294 self.menuPackagersAct.setEnabled(False) |
3289 self.pluginGrp.setEnabled(False) |
3295 self.pluginGrp.setEnabled(False) |
3290 self.makeGrp.setEnabled(False) |
3296 self.makeGrp.setEnabled(False) |
3291 self.menuMakeAct.setEnabled(False) |
3297 self.menuMakeAct.setEnabled(False) |
|
3298 self.menuOtherToolsAct.setEnabled(False) |
3292 |
3299 |
3293 self.__model.projectClosed() |
3300 self.__model.projectClosed() |
3294 self.projectClosedHooks.emit() |
3301 self.projectClosedHooks.emit() |
3295 self.projectClosed.emit(shutdown) |
3302 self.projectClosed.emit(shutdown) |
3296 |
3303 |
3779 @return testing framework name of the project |
3786 @return testing framework name of the project |
3780 @rtype str |
3787 @rtype str |
3781 """ |
3788 """ |
3782 try: |
3789 try: |
3783 return self.pdata["TESTING_FRAMEWORK"] |
3790 return self.pdata["TESTING_FRAMEWORK"] |
|
3791 except KeyError: |
|
3792 return "" |
|
3793 |
|
3794 def getProjectLicense(self): |
|
3795 """ |
|
3796 Public method to get the license type used by the project. |
|
3797 |
|
3798 @return license type of the project |
|
3799 @rtype str |
|
3800 """ |
|
3801 try: |
|
3802 return self.pdata["LICENSE"] |
3784 except KeyError: |
3803 except KeyError: |
3785 return "" |
3804 return "" |
3786 |
3805 |
3787 def __isInPdata(self, fn): |
3806 def __isInPdata(self, fn): |
3788 """ |
3807 """ |
4384 """ target is necessary.</p>""" |
4403 """ target is necessary.</p>""" |
4385 )) |
4404 )) |
4386 self.makeTestAct.triggered.connect( |
4405 self.makeTestAct.triggered.connect( |
4387 lambda: self.__executeMake(questionOnly=True)) |
4406 lambda: self.__executeMake(questionOnly=True)) |
4388 self.actions.append(self.makeTestAct) |
4407 self.actions.append(self.makeTestAct) |
|
4408 |
|
4409 self.createSBOMAct = EricAction( |
|
4410 self.tr('Create SBOM File'), |
|
4411 self.tr('Create &SBOM File'), 0, 0, |
|
4412 self, 'project_create_sbom') |
|
4413 self.createSBOMAct.setStatusTip( |
|
4414 self.tr("Create a SBOM file of the project dependencies.")) |
|
4415 self.createSBOMAct.setWhatsThis(self.tr( |
|
4416 """<b>Create SBOM File</b>""" |
|
4417 """<p>This allows the creation of a SBOM file of the project""" |
|
4418 """ dependencies. This may be based on various input sources""" |
|
4419 """ and will be saved as a CycloneDX SBOM file.</p>""" |
|
4420 )) |
|
4421 self.createSBOMAct.triggered.connect(self.__createSBOMFile) |
|
4422 self.actions.append(self.createSBOMAct) |
4389 |
4423 |
4390 self.closeAct.setEnabled(False) |
4424 self.closeAct.setEnabled(False) |
4391 self.saveAct.setEnabled(False) |
4425 self.saveAct.setEnabled(False) |
4392 self.saveasAct.setEnabled(False) |
4426 self.saveasAct.setEnabled(False) |
4393 self.actGrp2.setEnabled(False) |
4427 self.actGrp2.setEnabled(False) |
4422 self.graphicsMenu = QMenu(self.tr('&Diagrams'), toolsMenu) |
4456 self.graphicsMenu = QMenu(self.tr('&Diagrams'), toolsMenu) |
4423 self.packagersMenu = QMenu(self.tr('Pac&kagers'), toolsMenu) |
4457 self.packagersMenu = QMenu(self.tr('Pac&kagers'), toolsMenu) |
4424 self.apidocMenu = QMenu(self.tr('Source &Documentation'), toolsMenu) |
4458 self.apidocMenu = QMenu(self.tr('Source &Documentation'), toolsMenu) |
4425 self.apidocMenu.setTearOffEnabled(True) |
4459 self.apidocMenu.setTearOffEnabled(True) |
4426 self.makeMenu = QMenu(self.tr('Make'), toolsMenu) |
4460 self.makeMenu = QMenu(self.tr('Make'), toolsMenu) |
|
4461 self.othersMenu = QMenu(self.tr('Other Tools'), toolsMenu) |
4427 |
4462 |
4428 self.__menus = { |
4463 self.__menus = { |
4429 "Main": menu, |
4464 "Main": menu, |
4430 "Recent": self.recentMenu, |
4465 "Recent": self.recentMenu, |
4431 "VCS": self.vcsMenu, |
4466 "VCS": self.vcsMenu, |
4435 "Session": self.sessionMenu, |
4470 "Session": self.sessionMenu, |
4436 "Apidoc": self.apidocMenu, |
4471 "Apidoc": self.apidocMenu, |
4437 "Debugger": self.debuggerMenu, |
4472 "Debugger": self.debuggerMenu, |
4438 "Packagers": self.packagersMenu, |
4473 "Packagers": self.packagersMenu, |
4439 "Make": self.makeMenu, |
4474 "Make": self.makeMenu, |
|
4475 "OtherTools": self.othersMenu, |
4440 } |
4476 } |
4441 |
4477 |
4442 # connect the aboutToShow signals |
4478 # connect the aboutToShow signals |
4443 self.recentMenu.aboutToShow.connect(self.__showContextMenuRecent) |
4479 self.recentMenu.aboutToShow.connect(self.__showContextMenuRecent) |
4444 self.recentMenu.triggered.connect(self.__openRecent) |
4480 self.recentMenu.triggered.connect(self.__openRecent) |
4449 self.apidocMenu.aboutToShow.connect(self.__showContextMenuApiDoc) |
4485 self.apidocMenu.aboutToShow.connect(self.__showContextMenuApiDoc) |
4450 self.packagersMenu.aboutToShow.connect(self.__showContextMenuPackagers) |
4486 self.packagersMenu.aboutToShow.connect(self.__showContextMenuPackagers) |
4451 self.sessionMenu.aboutToShow.connect(self.__showContextMenuSession) |
4487 self.sessionMenu.aboutToShow.connect(self.__showContextMenuSession) |
4452 self.debuggerMenu.aboutToShow.connect(self.__showContextMenuDebugger) |
4488 self.debuggerMenu.aboutToShow.connect(self.__showContextMenuDebugger) |
4453 self.makeMenu.aboutToShow.connect(self.__showContextMenuMake) |
4489 self.makeMenu.aboutToShow.connect(self.__showContextMenuMake) |
|
4490 self.othersMenu.aboutToShow.connect(self.__showContextMenuOthers) |
4454 menu.aboutToShow.connect(self.__showMenu) |
4491 menu.aboutToShow.connect(self.__showMenu) |
4455 |
4492 |
4456 # build the show menu |
4493 # build the show menu |
4457 self.menuShow.setTearOffEnabled(True) |
4494 self.menuShow.setTearOffEnabled(True) |
4458 self.menuShow.addAction(self.codeMetricsAct) |
4495 self.menuShow.addAction(self.codeMetricsAct) |
4480 |
4517 |
4481 # build the make menu |
4518 # build the make menu |
4482 self.makeMenu.setTearOffEnabled(True) |
4519 self.makeMenu.setTearOffEnabled(True) |
4483 self.makeMenu.addActions(self.makeGrp.actions()) |
4520 self.makeMenu.addActions(self.makeGrp.actions()) |
4484 self.makeMenu.addSeparator() |
4521 self.makeMenu.addSeparator() |
|
4522 |
|
4523 # build the 'Other Tools' menu |
|
4524 self.othersMenu.setTearOffEnabled(True) |
|
4525 self.othersMenu.addAction(self.createSBOMAct) |
4485 |
4526 |
4486 # build the project main menu |
4527 # build the project main menu |
4487 menu.setTearOffEnabled(True) |
4528 menu.setTearOffEnabled(True) |
4488 menu.addActions(self.actGrp1.actions()) |
4529 menu.addActions(self.actGrp1.actions()) |
4489 self.menuRecentAct = menu.addMenu(self.recentMenu) |
4530 self.menuRecentAct = menu.addMenu(self.recentMenu) |
4517 self.menuShowAct = toolsMenu.addMenu(self.menuShow) |
4558 self.menuShowAct = toolsMenu.addMenu(self.menuShow) |
4518 toolsMenu.addSeparator() |
4559 toolsMenu.addSeparator() |
4519 self.menuApidocAct = toolsMenu.addMenu(self.apidocMenu) |
4560 self.menuApidocAct = toolsMenu.addMenu(self.apidocMenu) |
4520 toolsMenu.addSeparator() |
4561 toolsMenu.addSeparator() |
4521 self.menuPackagersAct = toolsMenu.addMenu(self.packagersMenu) |
4562 self.menuPackagersAct = toolsMenu.addMenu(self.packagersMenu) |
|
4563 toolsMenu.addSeparator() |
|
4564 self.menuOtherToolsAct = toolsMenu.addMenu(self.othersMenu) |
4522 |
4565 |
4523 self.menuCheckAct.setEnabled(False) |
4566 self.menuCheckAct.setEnabled(False) |
4524 self.menuShowAct.setEnabled(False) |
4567 self.menuShowAct.setEnabled(False) |
4525 self.menuDiagramAct.setEnabled(False) |
4568 self.menuDiagramAct.setEnabled(False) |
4526 self.menuSessionAct.setEnabled(False) |
4569 self.menuSessionAct.setEnabled(False) |
4527 self.menuDebuggerAct.setEnabled(False) |
4570 self.menuDebuggerAct.setEnabled(False) |
4528 self.menuApidocAct.setEnabled(False) |
4571 self.menuApidocAct.setEnabled(False) |
4529 self.menuPackagersAct.setEnabled(False) |
4572 self.menuPackagersAct.setEnabled(False) |
4530 self.menuMakeAct.setEnabled(False) |
4573 self.menuMakeAct.setEnabled(False) |
|
4574 self.menuOtherToolsAct.setEnabled(False) |
4531 |
4575 |
4532 self.__menu = menu |
4576 self.__menu = menu |
4533 self.__toolsMenu = toolsMenu |
4577 self.__toolsMenu = toolsMenu |
4534 |
4578 |
4535 return menu, toolsMenu |
4579 return menu, toolsMenu |
4602 act = self.recentMenu.addAction( |
4646 act = self.recentMenu.addAction( |
4603 formatStr.format( |
4647 formatStr.format( |
4604 idx, |
4648 idx, |
4605 Utilities.compactPath(rp, self.ui.maxMenuFilePathLen))) |
4649 Utilities.compactPath(rp, self.ui.maxMenuFilePathLen))) |
4606 act.setData(rp) |
4650 act.setData(rp) |
4607 act.setEnabled(QFileInfo(rp).exists()) |
4651 act.setEnabled(pathlib.Path(rp).exists()) |
4608 |
4652 |
4609 self.recentMenu.addSeparator() |
4653 self.recentMenu.addSeparator() |
4610 self.recentMenu.addAction(self.tr('&Clear'), self.clearRecent) |
4654 self.recentMenu.addAction(self.tr('&Clear'), self.clearRecent) |
4611 |
4655 |
4612 def __openRecent(self, act): |
4656 def __openRecent(self, act): |
5395 """ selected. Aborting...</p>""")) |
5439 """ selected. Aborting...</p>""")) |
5396 return |
5440 return |
5397 |
5441 |
5398 progress = EricProgressDialog( |
5442 progress = EricProgressDialog( |
5399 self.tr("Creating plugin archives..."), self.tr("Abort"), |
5443 self.tr("Creating plugin archives..."), self.tr("Abort"), |
5400 0, len(selectedLists), self.tr("%v/%m Archives")) |
5444 0, len(selectedLists), self.tr("%v/%m Archives"), self.ui) |
5401 progress.setMinimumDuration(0) |
5445 progress.setMinimumDuration(0) |
5402 progress.setWindowTitle(self.tr("Create Plugin Archives")) |
5446 progress.setWindowTitle(self.tr("Create Plugin Archives")) |
5403 errors = 0 |
5447 errors = 0 |
5404 for count, pkglist in enumerate(selectedLists): |
5448 for count, pkglist in enumerate(selectedLists): |
5405 progress.setValue(count) |
5449 progress.setValue(count) |
5912 |
5956 |
5913 @return configured docstring style |
5957 @return configured docstring style |
5914 @rtype str |
5958 @rtype str |
5915 """ |
5959 """ |
5916 return self.pdata["DOCSTRING"] |
5960 return self.pdata["DOCSTRING"] |
|
5961 |
|
5962 ######################################################################### |
|
5963 ## Below are methods implementing the 'SBOM' support |
|
5964 ######################################################################### |
|
5965 |
|
5966 def __showContextMenuOthers(self): |
|
5967 """ |
|
5968 Private slot called before the 'Other Tools' menu is shown. |
|
5969 """ |
|
5970 self.showMenu.emit("OtherTools", self.othersMenu) |
|
5971 |
|
5972 @pyqtSlot() |
|
5973 def __createSBOMFile(self): |
|
5974 """ |
|
5975 Private slot to create a SBOM file of the project dependencies. |
|
5976 """ |
|
5977 import CycloneDXInterface |
|
5978 |
|
5979 CycloneDXInterface.createCycloneDXFile("<project>") |
5917 |
5980 |
5918 # |
5981 # |
5919 # eflag: noqa = M601 |
5982 # eflag: noqa = M601 |