src/eric7/MultiProject/MultiProject.py

branch
eric7
changeset 10410
da82156f44e9
parent 10403
ea3320d5e8e9
child 10412
883e4bc9bd07
equal deleted inserted replaced
10409:8d888f38bf89 10410:da82156f44e9
24 from eric7.EricXML.MultiProjectReader import MultiProjectReader 24 from eric7.EricXML.MultiProjectReader import MultiProjectReader
25 from eric7.Globals import recentNameMultiProject 25 from eric7.Globals import recentNameMultiProject
26 from eric7.SystemUtilities import FileSystemUtilities, OSUtilities 26 from eric7.SystemUtilities import FileSystemUtilities, OSUtilities
27 27
28 from .MultiProjectFile import MultiProjectFile 28 from .MultiProjectFile import MultiProjectFile
29 from .MultiProjectProjectMeta import MultiProjectProjectMeta
29 30
30 31
31 class MultiProject(QObject): 32 class MultiProject(QObject):
32 """ 33 """
33 Class implementing the project management functionality. 34 Class implementing the project management functionality.
38 @signal multiProjectClosed() emitted after a multi project was closed 39 @signal multiProjectClosed() emitted after a multi project was closed
39 @signal multiProjectPropertiesChanged() emitted after the multi project 40 @signal multiProjectPropertiesChanged() emitted after the multi project
40 properties were changed 41 properties were changed
41 @signal showMenu(string, QMenu) emitted when a menu is about to be shown. 42 @signal showMenu(string, QMenu) emitted when a menu is about to be shown.
42 The name of the menu and a reference to the menu are given. 43 The name of the menu and a reference to the menu are given.
43 @signal projectDataChanged(project data dict) emitted after a project entry 44 @signal projectDataChanged(project metadata) emitted after a project entry
44 has been changed 45 has been changed
45 @signal projectAdded(project data dict) emitted after a project entry 46 @signal projectAdded(project metadata) emitted after a project entry
46 has been added 47 has been added
47 @signal projectRemoved(project data dict) emitted after a project entry 48 @signal projectRemoved(project metadata) emitted after a project entry
48 has been removed 49 has been removed
49 @signal projectOpened(filename) emitted after the project has been opened 50 @signal projectOpened(filename) emitted after the project has been opened
50 """ 51 """
51 52
52 dirty = pyqtSignal(bool) 53 dirty = pyqtSignal(bool)
53 newMultiProject = pyqtSignal() 54 newMultiProject = pyqtSignal()
54 multiProjectOpened = pyqtSignal() 55 multiProjectOpened = pyqtSignal()
55 multiProjectClosed = pyqtSignal() 56 multiProjectClosed = pyqtSignal()
56 multiProjectPropertiesChanged = pyqtSignal() 57 multiProjectPropertiesChanged = pyqtSignal()
57 showMenu = pyqtSignal(str, QMenu) 58 showMenu = pyqtSignal(str, QMenu)
58 projectDataChanged = pyqtSignal(dict) 59 projectDataChanged = pyqtSignal(MultiProjectProjectMeta)
59 projectAdded = pyqtSignal(dict) 60 projectAdded = pyqtSignal(MultiProjectProjectMeta)
60 projectRemoved = pyqtSignal(dict) 61 projectRemoved = pyqtSignal(MultiProjectProjectMeta)
61 projectOpened = pyqtSignal(str) 62 projectOpened = pyqtSignal(str)
62 63
63 def __init__(self, project, parent=None, filename=None): 64 def __init__(self, project, parent=None, filename=None):
64 """ 65 """
65 Constructor 66 Constructor
66 67
67 @param project reference to the project object (Project.Project) 68 @param project reference to the project object
68 @param parent parent widget (usually the ui object) (QWidget) 69 @type Project.Project
70 @param parent parent widget (usually the ui object)
71 @type QWidget
69 @param filename optional filename of a multi project file to open 72 @param filename optional filename of a multi project file to open
70 (string) 73 @type str
71 """ 74 """
72 super().__init__(parent) 75 super().__init__(parent)
73 76
74 self.ui = parent 77 self.ui = parent
75 self.projectObject = project 78 self.projectObject = project
94 self.ppath = "" # name of the multi project directory 97 self.ppath = "" # name of the multi project directory
95 self.description = "" # description of the multi project 98 self.description = "" # description of the multi project
96 self.name = "" 99 self.name = ""
97 self.opened = False 100 self.opened = False
98 self.__projects = {} 101 self.__projects = {}
99 # dict of project info keyed by 'uid'; each info entry is a dictionary 102 # dict of project info keyed by 'uid'; each info entry is a MultiProjectProject
100 # 'name' : name of the project
101 # 'file' : project file name
102 # 'master' : flag indicating the main project
103 # 'description' : description of the project
104 # 'category' : name of the group
105 # 'uid' : unique identifier
106 self.categories = [] 103 self.categories = []
107 104
108 def __loadRecent(self): 105 def __loadRecent(self):
109 """ 106 """
110 Private method to load the recently opened multi project filenames. 107 Private method to load the recently opened multi project filenames.
126 123
127 def getMostRecent(self): 124 def getMostRecent(self):
128 """ 125 """
129 Public method to get the most recently opened multiproject. 126 Public method to get the most recently opened multiproject.
130 127
131 @return path of the most recently opened multiproject (string) 128 @return path of the most recently opened multiproject
129 @rtype str
132 """ 130 """
133 if len(self.recent): 131 if len(self.recent):
134 return self.recent[0] 132 return self.recent[0]
135 else: 133 else:
136 return None 134 return None
139 """ 137 """
140 Public method to set the dirty state. 138 Public method to set the dirty state.
141 139
142 It emits the signal dirty(int). 140 It emits the signal dirty(int).
143 141
144 @param b dirty state (boolean) 142 @param b dirty state
143 @type bool
145 """ 144 """
146 self.__dirty = b 145 self.__dirty = b
147 self.saveAct.setEnabled(b) 146 self.saveAct.setEnabled(b)
148 self.dirty.emit(bool(b)) 147 self.dirty.emit(bool(b))
149 148
150 def isDirty(self): 149 def isDirty(self):
151 """ 150 """
152 Public method to return the dirty state. 151 Public method to return the dirty state.
153 152
154 @return dirty state (boolean) 153 @return dirty state
154 @rtype bool
155 """ 155 """
156 return self.__dirty 156 return self.__dirty
157 157
158 def isOpen(self): 158 def isOpen(self):
159 """ 159 """
160 Public method to return the opened state. 160 Public method to return the opened state.
161 161
162 @return open state (boolean) 162 @return open state
163 @rtype bool
163 """ 164 """
164 return self.opened 165 return self.opened
165 166
166 def getMultiProjectPath(self): 167 def getMultiProjectPath(self):
167 """ 168 """
168 Public method to get the multi project path. 169 Public method to get the multi project path.
169 170
170 @return multi project path (string) 171 @return multi project path
172 @rtype str
171 """ 173 """
172 return self.ppath 174 return self.ppath
173 175
174 def getMultiProjectFile(self): 176 def getMultiProjectFile(self):
175 """ 177 """
176 Public method to get the path of the multi project file. 178 Public method to get the path of the multi project file.
177 179
178 @return path of the multi project file (string) 180 @return path of the multi project file
181 @rtype str
179 """ 182 """
180 return self.pfile 183 return self.pfile
181 184
182 def __checkFilesExist(self):
183 """
184 Private method to check, if the files in a list exist.
185
186 The project files are checked for existance in the
187 filesystem. Non existant projects are removed from the list and the
188 dirty state of the multi project is changed accordingly.
189 """
190 removelist = []
191 for key, project in self.__projects.items():
192 if not os.path.exists(project["file"]):
193 removelist.append(key)
194
195 if removelist:
196 for key in removelist:
197 del self.__projects[key]
198 self.setDirty(True)
199
200 def __extractCategories(self): 185 def __extractCategories(self):
201 """ 186 """
202 Private slot to extract the categories used in the project definitions. 187 Private slot to extract the categories used in the project definitions.
203 """ 188 """
204 for project in self.__projects.values(): 189 for project in self.__projects.values():
205 if project["category"] and project["category"] not in self.categories: 190 if project.category and project.category not in self.categories:
206 self.categories.append(project["category"]) 191 self.categories.append(project.category)
207 192
208 def getCategories(self): 193 def getCategories(self):
209 """ 194 """
210 Public method to get the list of defined categories. 195 Public method to get the list of defined categories.
211 196
212 @return list of categories (list of string) 197 @return list of categories
198 @rtype list of str
213 """ 199 """
214 return [c for c in self.categories if c] 200 return [c for c in self.categories if c]
215 201
216 def __readMultiProject(self, fn): 202 def __readMultiProject(self, fn):
217 """ 203 """
218 Private method to read in a multi project (.emj, .e4m, .e5m) file. 204 Private method to read in a multi project (.emj, .e4m, .e5m) file.
219 205
220 @param fn filename of the multi project file to be read (string) 206 @param fn filename of the multi project file to be read
207 @type str
221 @return flag indicating success 208 @return flag indicating success
209 @rtype bool
222 """ 210 """
223 if os.path.splitext(fn)[1] == ".emj": 211 if os.path.splitext(fn)[1] == ".emj":
224 # new JSON based format 212 # new JSON based format
225 with EricOverrideCursor(): 213 with EricOverrideCursor():
226 res = self.__multiProjectFile.readFile(fn) 214 res = self.__multiProjectFile.readFile(fn)
266 @param fn optional filename of the multi project file to be written. 254 @param fn optional filename of the multi project file to be written.
267 If fn is None, the filename stored in the multi project object 255 If fn is None, the filename stored in the multi project object
268 is used. This is the 'save' action. If fn is given, this filename 256 is used. This is the 'save' action. If fn is given, this filename
269 is used instead of the one in the multi project object. This is the 257 is used instead of the one in the multi project object. This is the
270 'save as' action. 258 'save as' action.
259 @type str
271 @return flag indicating success 260 @return flag indicating success
261 @rtype bool
272 """ 262 """
273 if fn is None: 263 if fn is None:
274 fn = self.pfile 264 fn = self.pfile
275 265
276 res = self.__multiProjectFile.writeFile(fn) 266 res = self.__multiProjectFile.writeFile(fn)
288 def addProject(self, project): 278 def addProject(self, project):
289 """ 279 """
290 Public method to add a project to the multi-project. 280 Public method to add a project to the multi-project.
291 281
292 @param project dictionary containing the project data to be added 282 @param project dictionary containing the project data to be added
293 @type dict 283 @type MultiProjectProjectMeta
294 """ 284 """
295 self.__projects[project["uid"]] = project 285 self.__projects[project.uid] = project
296 286
297 @pyqtSlot() 287 @pyqtSlot()
298 def addNewProject(self, startdir="", category=""): 288 def addNewProject(self, startdir="", category=""):
299 """ 289 """
300 Public slot used to add a new project to the multi-project. 290 Public slot used to add a new project to the multi-project.
312 startdir = Preferences.getMultiProject("Workspace") 302 startdir = Preferences.getMultiProject("Workspace")
313 dlg = AddProjectDialog( 303 dlg = AddProjectDialog(
314 self.ui, startdir=startdir, categories=self.categories, category=category 304 self.ui, startdir=startdir, categories=self.categories, category=category
315 ) 305 )
316 if dlg.exec() == QDialog.DialogCode.Accepted: 306 if dlg.exec() == QDialog.DialogCode.Accepted:
317 name, filename, isMain, description, category, uid = dlg.getData() 307 newProject = dlg.getProjectMetadata()
318 308
319 # step 1: check, if project was already added 309 # step 1: check, if project was already added
320 for project in self.__projects.values(): 310 for project in self.__projects.values():
321 if project["file"] == filename: 311 if project.file == newProject.file:
322 return 312 return
323 313
324 # step 2: check, if main should be changed 314 # step 2: check, if main should be changed
325 if isMain: 315 if newProject.master:
326 for project in self.__projects.values(): 316 for project in self.__projects.values():
327 if project["master"]: 317 if project.master:
328 project["master"] = False 318 project.master = False
329 self.projectDataChanged.emit(project) 319 self.projectDataChanged.emit(project)
330 self.setDirty(True) 320 self.setDirty(True)
331 break 321 break
332 322
333 # step 3: add the project entry 323 # step 3: add the project entry
334 project = { 324 self.__projects[newProject.uid] = newProject
335 "name": name,
336 "file": filename,
337 "master": isMain,
338 "description": description,
339 "category": category,
340 "uid": uid,
341 }
342 self.__projects[uid] = project
343 if category not in self.categories: 325 if category not in self.categories:
344 self.categories.append(category) 326 self.categories.append(category)
345 self.projectAdded.emit(project) 327 self.projectAdded.emit(newProject)
346 self.setDirty(True) 328 self.setDirty(True)
347 329
348 def copyProject(self, uid): 330 def copyProject(self, uid):
349 """ 331 """
350 Public method to copy the project with given UID on disk. 332 Public method to copy the project with given UID on disk.
355 if uid in self.__projects: 337 if uid in self.__projects:
356 startdir = self.ppath 338 startdir = self.ppath
357 if not startdir: 339 if not startdir:
358 startdir = Preferences.getMultiProject("Workspace") 340 startdir = Preferences.getMultiProject("Workspace")
359 srcProject = self.__projects[uid] 341 srcProject = self.__projects[uid]
360 srcProjectDirectory = os.path.dirname(srcProject["file"]) 342 srcProjectDirectory = os.path.dirname(srcProject.file)
361 dstProjectDirectory, ok = EricPathPickerDialog.getStrPath( 343 dstProjectDirectory, ok = EricPathPickerDialog.getStrPath(
362 self.parent(), 344 self.parent(),
363 self.tr("Copy Project"), 345 self.tr("Copy Project"),
364 self.tr( 346 self.tr(
365 "Enter directory for the new project (must not exist already):" 347 "Enter directory for the new project (must not exist already):"
382 ).format(srcProjectDirectory, dstProjectDirectory), 364 ).format(srcProjectDirectory, dstProjectDirectory),
383 ) 365 )
384 return 366 return
385 367
386 dstUid = QUuid.createUuid().toString() 368 dstUid = QUuid.createUuid().toString()
387 dstProject = { 369 dstProject = MultiProjectProjectMeta(
388 "name": self.tr("{0} - Copy").format(srcProject["name"]), 370 name=self.tr("{0} - Copy").format(srcProject["name"]),
389 "file": os.path.join( 371 file=os.path.join(
390 dstProjectDirectory, os.path.basename(srcProject["file"]) 372 dstProjectDirectory, os.path.basename(srcProject["file"])
391 ), 373 ),
392 "master": False, 374 master=False,
393 "description": srcProject["description"], 375 description=srcProject.description,
394 "category": srcProject["category"], 376 category=srcProject.category,
395 "uid": dstUid, 377 uid=dstUid,
396 } 378 )
397 self.__projects[dstUid] = dstProject 379 self.__projects[dstUid] = dstProject
398 self.projectAdded.emit(dstProject) 380 self.projectAdded.emit(dstProject)
399 self.setDirty(True) 381 self.setDirty(True)
400 382
401 def changeProjectProperties(self, pro): 383 def changeProjectProperties(self, pro):
402 """ 384 """
403 Public method to change the data of a project entry. 385 Public method to change the data of a project entry.
404 386
405 @param pro dictionary with the project data (string) 387 @param pro dictionary with the project data
388 @type str
406 """ 389 """
407 # step 1: check, if main should be changed 390 # step 1: check, if main should be changed
408 if pro["master"]: 391 if pro.master:
409 for project in self.__projects.values(): 392 for project in self.__projects.values():
410 if project["master"]: 393 if project.master:
411 if project["uid"] != pro["uid"]: 394 if project.uid != pro.uid:
412 project["master"] = False 395 project.master = False
413 self.projectDataChanged.emit(project) 396 self.projectDataChanged.emit(project)
414 self.setDirty(True) 397 self.setDirty(True)
415 break 398 break
416 399
417 # step 2: change the entry 400 # step 2: change the entry
418 project = self.__projects[pro["uid"]] 401 project = self.__projects[pro.uid]
419 # project UID is not changeable via interface 402 # project UID is not changeable via interface
420 project["file"] = pro["file"] 403 project.file = pro.file
421 project["name"] = pro["name"] 404 project.name = pro.name
422 project["master"] = pro["master"] 405 project.master = pro.master
423 project["description"] = pro["description"] 406 project.description = pro.description
424 project["category"] = pro["category"] 407 project.category = pro.category
425 if project["category"] not in self.categories: 408 if project.category not in self.categories:
426 self.categories.append(project["category"]) 409 self.categories.append(project.category)
427 self.projectDataChanged.emit(project) 410 self.projectDataChanged.emit(project)
428 self.setDirty(True) 411 self.setDirty(True)
429 412
430 def getProjects(self): 413 def getProjects(self):
431 """ 414 """
432 Public method to get all project entries. 415 Public method to get all project entries.
433 416
434 @return list of all project entries (list of dictionaries) 417 @return list of all project entries
418 @rtype list of MultiProjectProjectMeta
435 """ 419 """
436 return self.__projects.values() 420 return self.__projects.values()
437 421
438 def getProject(self, uid): 422 def getProject(self, uid):
439 """ 423 """
440 Public method to get a reference to a project entry. 424 Public method to get a reference to a project entry.
441 425
442 @param uid UID of the project to get 426 @param uid UID of the project to get
443 @type str 427 @type str
444 @return dictionary containing the project data 428 @return project metadata
445 @rtype dict 429 @rtype MultiProjectProjectMeta
446 """ 430 """
447 if uid in self.__projects: 431 if uid in self.__projects:
448 return self.__projects[uid] 432 return self.__projects[uid]
449 else: 433 else:
450 return None 434 return None
471 project 455 project
472 @type str 456 @type str
473 """ 457 """
474 if uid in self.__projects: 458 if uid in self.__projects:
475 project = self.__projects[uid] 459 project = self.__projects[uid]
476 projectPath = os.path.dirname(project["file"]) 460 projectPath = os.path.dirname(project.file)
477 shutil.rmtree(projectPath, ignore_errors=True) 461 shutil.rmtree(projectPath, ignore_errors=True)
478 462
479 self.removeProject(uid) 463 self.removeProject(uid)
480 464
481 def __newMultiProject(self): 465 def __newMultiProject(self):
632 616
633 def checkDirty(self): 617 def checkDirty(self):
634 """ 618 """
635 Public method to check the dirty status and open a message window. 619 Public method to check the dirty status and open a message window.
636 620
637 @return flag indicating whether this operation was successful (boolean) 621 @return flag indicating whether this operation was successful
622 @rtype bool
638 """ 623 """
639 if self.isDirty(): 624 if self.isDirty():
640 res = EricMessageBox.okToClearData( 625 res = EricMessageBox.okToClearData(
641 self.parent(), 626 self.parent(),
642 self.tr("Close Multiproject"), 627 self.tr("Close Multiproject"),
651 636
652 def closeMultiProject(self): 637 def closeMultiProject(self):
653 """ 638 """
654 Public slot to close the current multi project. 639 Public slot to close the current multi project.
655 640
656 @return flag indicating success (boolean) 641 @return flag indicating success
642 @rtype bool
657 """ 643 """
658 # save the list of recently opened projects 644 # save the list of recently opened projects
659 self.__saveRecent() 645 self.__saveRecent()
660 646
661 if not self.isOpen(): 647 if not self.isOpen():
666 652
667 # now close the current project, if it belongs to the multi project 653 # now close the current project, if it belongs to the multi project
668 pfile = self.projectObject.getProjectFile() 654 pfile = self.projectObject.getProjectFile()
669 if pfile: 655 if pfile:
670 for project in self.__projects.values(): 656 for project in self.__projects.values():
671 if project["file"] == pfile: 657 if project.file == pfile:
672 if not self.projectObject.closeProject(): 658 if not self.projectObject.closeProject():
673 return False 659 return False
674 break 660 break
675 661
676 self.__initData() 662 self.__initData()
821 ) 807 )
822 ) 808 )
823 self.propsAct.triggered.connect(self.__showProperties) 809 self.propsAct.triggered.connect(self.__showProperties)
824 self.actions.append(self.propsAct) 810 self.actions.append(self.propsAct)
825 811
812 self.clearRemovedAct = EricAction(
813 self.tr("Clear Out"),
814 EricPixmapCache.getIcon("clear"),
815 self.tr("Clear Out"),
816 0,
817 0,
818 self,
819 "multi_project_clearout",
820 )
821 self.clearRemovedAct.setStatusTip(
822 self.tr("Remove all projects marked as removed")
823 )
824 self.clearRemovedAct.setWhatsThis(
825 self.tr(
826 """<b>Clear Out...</b>"""
827 """<p>This removes all projects marked as removed.</p>"""
828 )
829 )
830 self.clearRemovedAct.triggered.connect(self.clearRemovedProjects)
831 self.actions.append(self.clearRemovedAct)
832
826 self.closeAct.setEnabled(False) 833 self.closeAct.setEnabled(False)
827 self.saveAct.setEnabled(False) 834 self.saveAct.setEnabled(False)
828 self.saveasAct.setEnabled(False) 835 self.saveasAct.setEnabled(False)
829 self.addProjectAct.setEnabled(False) 836 self.addProjectAct.setEnabled(False)
830 self.propsAct.setEnabled(False) 837 self.propsAct.setEnabled(False)
838 self.clearRemovedAct.setEnabled(False)
831 839
832 def initMenu(self): 840 def initMenu(self):
833 """ 841 """
834 Public slot to initialize the multi project menu. 842 Public slot to initialize the multi project menu.
835 843
836 @return the menu generated (QMenu) 844 @return the menu generated
845 @rtype QMenu
837 """ 846 """
838 menu = QMenu(self.tr("&Multiproject"), self.parent()) 847 menu = QMenu(self.tr("&Multiproject"), self.parent())
839 self.recentMenu = QMenu(self.tr("Open &Recent Multiprojects"), menu) 848 self.recentMenu = QMenu(self.tr("Open &Recent Multiprojects"), menu)
840 849
841 self.__menus = { 850 self.__menus = {
858 menu.addAction(self.saveAct) 867 menu.addAction(self.saveAct)
859 menu.addAction(self.saveasAct) 868 menu.addAction(self.saveasAct)
860 menu.addSeparator() 869 menu.addSeparator()
861 menu.addAction(self.addProjectAct) 870 menu.addAction(self.addProjectAct)
862 menu.addSeparator() 871 menu.addSeparator()
872 menu.addAction(self.clearRemovedAct)
873 menu.addSeparator()
863 menu.addAction(self.propsAct) 874 menu.addAction(self.propsAct)
864 875
865 self.menu = menu 876 self.menu = menu
866 return menu 877 return menu
867 878
868 def initToolbar(self, toolbarManager): 879 def initToolbar(self, toolbarManager):
869 """ 880 """
870 Public slot to initialize the multi project toolbar. 881 Public slot to initialize the multi project toolbar.
871 882
872 @param toolbarManager reference to a toolbar manager object 883 @param toolbarManager reference to a toolbar manager object
873 (EricToolBarManager) 884 @type EricToolBarManager
874 @return the toolbar generated (QToolBar) 885 @return the toolbar generated
886 @rtype QToolBar
875 """ 887 """
876 tb = QToolBar(self.tr("Multiproject"), self.ui) 888 tb = QToolBar(self.tr("Multiproject"), self.ui)
877 tb.setObjectName("MultiProjectToolbar") 889 tb.setObjectName("MultiProjectToolbar")
878 tb.setToolTip(self.tr("Multiproject")) 890 tb.setToolTip(self.tr("Multiproject"))
879 891
884 tb.addAction(self.saveasAct) 896 tb.addAction(self.saveasAct)
885 897
886 toolbarManager.addToolBar(tb, tb.windowTitle()) 898 toolbarManager.addToolBar(tb, tb.windowTitle())
887 toolbarManager.addAction(self.addProjectAct, tb.windowTitle()) 899 toolbarManager.addAction(self.addProjectAct, tb.windowTitle())
888 toolbarManager.addAction(self.propsAct, tb.windowTitle()) 900 toolbarManager.addAction(self.propsAct, tb.windowTitle())
901 toolbarManager.addAction(self.clearRemovedAct, tb.windowTitle())
889 902
890 return tb 903 return tb
891 904
892 def __showMenu(self): 905 def __showMenu(self):
893 """ 906 """
935 def __openRecent(self, act): 948 def __openRecent(self, act):
936 """ 949 """
937 Private method to open a multi project from the list of rencently 950 Private method to open a multi project from the list of rencently
938 opened multi projects. 951 opened multi projects.
939 952
940 @param act reference to the action that triggered (QAction) 953 @param act reference to the action that triggered
954 @type QAction
941 """ 955 """
942 file = act.data() 956 file = act.data()
943 if file: 957 if file:
944 self.openMultiProject(file) 958 self.openMultiProject(file)
945 959
952 966
953 def getActions(self): 967 def getActions(self):
954 """ 968 """
955 Public method to get a list of all actions. 969 Public method to get a list of all actions.
956 970
957 @return list of all actions (list of EricAction) 971 @return list of all actions
972 @rtype list of EricAction
958 """ 973 """
959 return self.actions[:] 974 return self.actions[:]
960 975
961 def addEricActions(self, actions): 976 def addEricActions(self, actions):
962 """ 977 """
963 Public method to add actions to the list of actions. 978 Public method to add actions to the list of actions.
964 979
965 @param actions list of actions (list of EricAction) 980 @param actions list of actions
981 @type list of EricAction
966 """ 982 """
967 self.actions.extend(actions) 983 self.actions.extend(actions)
968 984
969 def removeEricActions(self, actions): 985 def removeEricActions(self, actions):
970 """ 986 """
971 Public method to remove actions from the list of actions. 987 Public method to remove actions from the list of actions.
972 988
973 @param actions list of actions (list of EricAction) 989 @param actions list of actions
990 @type list of EricAction
974 """ 991 """
975 for act in actions: 992 for act in actions:
976 with contextlib.suppress(ValueError): 993 with contextlib.suppress(ValueError):
977 self.actions.remove(act) 994 self.actions.remove(act)
978 995
979 def getMenu(self, menuName): 996 def getMenu(self, menuName):
980 """ 997 """
981 Public method to get a reference to the main menu or a submenu. 998 Public method to get a reference to the main menu or a submenu.
982 999
983 @param menuName name of the menu (string) 1000 @param menuName name of the menu
984 @return reference to the requested menu (QMenu) or None 1001 @type str
1002 @return reference to the requested menu
1003 @rtype QMenu or None
985 """ 1004 """
986 try: 1005 try:
987 return self.__menus[menuName] 1006 return self.__menus[menuName]
988 except KeyError: 1007 except KeyError:
989 return None 1008 return None
990 1009
991 def openProject(self, filename): 1010 def openProject(self, filename):
992 """ 1011 """
993 Public slot to open a project. 1012 Public slot to open a project.
994 1013
995 @param filename filename of the project file (string) 1014 @param filename filename of the project file
1015 @type str
996 """ 1016 """
997 self.projectObject.openProject(filename) 1017 self.projectObject.openProject(filename)
998 self.projectOpened.emit(filename) 1018 self.projectOpened.emit(filename)
999 1019
1000 def __openMainProject(self, reopen=True): 1020 def __openMainProject(self, reopen=True):
1001 """ 1021 """
1002 Private slot to open the main project. 1022 Private slot to open the main project.
1003 1023
1004 @param reopen flag indicating, that the main project should be 1024 @param reopen flag indicating, that the main project should be
1005 reopened, if it has been opened already (boolean) 1025 reopened, if it has been opened already
1026 @type bool
1006 """ 1027 """
1007 for project in self.__projects.values(): 1028 for project in self.__projects.values():
1008 if project["master"] and ( 1029 if project.master and not project.removed and (
1009 reopen 1030 reopen
1010 or not self.projectObject.isOpen() 1031 or not self.projectObject.isOpen()
1011 or self.projectObject.getProjectFile() != project["file"] 1032 or self.projectObject.getProjectFile() != project.file
1012 ): 1033 ):
1013 self.openProject(project["file"]) 1034 self.openProject(project.file)
1014 return 1035 return
1015 1036
1016 def getMainProjectFile(self): 1037 def getMainProjectFile(self):
1017 """ 1038 """
1018 Public method to get the filename of the main project. 1039 Public method to get the filename of the main project.
1019 1040
1020 @return name of the main project file 1041 @return name of the main project file
1021 @rtype str 1042 @rtype str
1022 """ 1043 """
1023 for project in self.__projects: 1044 for project in self.__projects:
1024 if project["master"]: 1045 if project.master:
1025 return project["file"] 1046 return project.file
1026 1047
1027 return None 1048 return None
1028 1049
1029 def getDependantProjectFiles(self): 1050 def getDependantProjectFiles(self):
1030 """ 1051 """
1031 Public method to get the filenames of the dependent projects. 1052 Public method to get the filenames of the dependent projects.
1032 1053
1033 @return names of the dependent project files (list of strings) 1054 @return names of the dependent project files
1055 @rtype list of str
1034 """ 1056 """
1035 files = [] 1057 files = []
1036 for project in self.__projects.values(): 1058 for project in self.__projects.values():
1037 if not project["master"]: 1059 if not project.master:
1038 files.append(project["file"]) 1060 files.append(project.file)
1039 return files 1061 return files
1062
1063 def __checkFilesExist(self):
1064 """
1065 Private method to check, if the files in a list exist.
1066
1067 The project files are checked for existance in the
1068 filesystem. Non existant projects are removed from the list and the
1069 dirty state of the multi project is changed accordingly.
1070 """
1071 for project in self.__projects.values():
1072 project.removed = not os.path.exists(project.file)
1073 self.clearRemovedAct.setEnabled(True)
1074
1075 @pyqtSlot()
1076 def clearRemovedProjects(self):
1077 """
1078 Public slot to clear out all projects marked as removed.
1079 """
1080 for key in list(self.__projects.keys()):
1081 if self.__projects[key].removed:
1082 self.removeProject(key)
1083
1084 self.clearRemovedAct.setEnabled(False)
1085
1086 def hasRemovedProjects(self):
1087 """
1088 Public method to check for removed projects.
1089
1090 @return flag indicating the existence of a removed project
1091 @rtype bool
1092 """
1093 return any(p.removed for p in self.__projects.values())

eric ide

mercurial