src/eric7/Project/Project.py

branch
eric7
changeset 9520
e12589f1d408
parent 9517
d73c3a1e432b
child 9525
477545eef9f4
equal deleted inserted replaced
9519:d7ab0653bc63 9520:e12589f1d408
55 from eric7.Tasks.TasksFile import TasksFile 55 from eric7.Tasks.TasksFile import TasksFile
56 from eric7.UI import Config 56 from eric7.UI import Config
57 from eric7.UI.NotificationWidget import NotificationTypes 57 from eric7.UI.NotificationWidget import NotificationTypes
58 58
59 from .DebuggerPropertiesFile import DebuggerPropertiesFile 59 from .DebuggerPropertiesFile import DebuggerPropertiesFile
60 from .FileCategoryRepositoryItem import FileCategoryRepositoryItem
60 from .ProjectBrowserModel import ProjectBrowserModel 61 from .ProjectBrowserModel import ProjectBrowserModel
61 from .ProjectFile import ProjectFile 62 from .ProjectFile import ProjectFile
62 from .UserProjectFile import UserProjectFile 63 from .UserProjectFile import UserProjectFile
63 64
64 65
180 "Python3": self.tr( 181 "Python3": self.tr(
181 "Python3 Files (*.py *.py3);;" "Python3 GUI Files (*.pyw *.pyw3);;" 182 "Python3 Files (*.py *.py3);;" "Python3 GUI Files (*.pyw *.pyw3);;"
182 ), 183 ),
183 } 184 }
184 185
186 self.__fileCategoriesRepository = {}
187 # This dictionary will be populated by the various project browsers with
188 # classes of type 'FileCategoryRepositoryItem' using the 'addFileCategory()
189 # and removeFileCategory() methods.
190
185 self.vcsMenu = None 191 self.vcsMenu = None
186 self.__makeProcess = None 192 self.__makeProcess = None
187 193
188 self.__initProjectTypes() 194 self.__initProjectTypes()
189 195
211 self.applicationDiagram = None 217 self.applicationDiagram = None
212 self.loadedDiagram = None 218 self.loadedDiagram = None
213 self.__findProjectFileDialog = None 219 self.__findProjectFileDialog = None
214 220
215 self.processChangedProjectFiles.connect(self.__autoExecuteMake) 221 self.processChangedProjectFiles.connect(self.__autoExecuteMake)
222
223 def addFileCategory(self, category, categoryItem):
224 """
225 Public method to add a file category to the categories repository.
226
227 Note: The given category must not be contained in the repository already.
228
229 @param category file category (must be unique)
230 @type str
231 @param categoryItem data class instance containing the category data
232 @type FileCategoryRepositoryItem
233 @exception TypeError raised to signal a wrong type for the category item
234 """
235 if not isinstance(categoryItem, FileCategoryRepositoryItem):
236 raise TypeError(
237 "'categoryItem' must be an instance of 'FileCategoryRepositoryItem'."
238 )
239
240 if category in self.__fileCategoriesRepository:
241 EricMessageBox.critical(
242 self.ui,
243 self.tr("Add File Category"),
244 self.tr(
245 "<p>The file category <b>{0}</b> has already been added. This"
246 " attempt will be ignored.</p>"
247 ),
248 )
249 else:
250 self.__fileCategoriesRepository[category] = categoryItem
251 with contextlib.suppress(AttributeError):
252 self.__pdata[category] = []
253
254 def removeFileCategory(self, category):
255 """
256 Public method to remove a category from the categories repository.
257
258 Note: If the category is not contained in the repository, the request to
259 remove it will be ignored silently.
260
261 @param category file category
262 @type str
263 """
264 with contextlib.suppress(KeyError):
265 del self.__fileCategoriesRepository[category]
216 266
217 def __sourceExtensions(self, language): 267 def __sourceExtensions(self, language):
218 """ 268 """
219 Private method to get the source extensions of a programming language. 269 Private method to get the source extensions of a programming language.
220 270
463 } 513 }
464 514
465 self.__pdata = { 515 self.__pdata = {
466 "DESCRIPTION": "", 516 "DESCRIPTION": "",
467 "VERSION": "", 517 "VERSION": "",
468 "SOURCES": [],
469 "FORMS": [],
470 "RESOURCES": [],
471 "INTERFACES": [],
472 "PROTOCOLS": [],
473 "OTHERS": [],
474 "TRANSLATIONS": [],
475 "TRANSLATIONEXCEPTIONS": [], 518 "TRANSLATIONEXCEPTIONS": [],
476 "TRANSLATIONPATTERN": "", 519 "TRANSLATIONPATTERN": "",
477 "TRANSLATIONSBINPATH": "", 520 "TRANSLATIONSBINPATH": "",
478 "TRANSLATIONSOURCESTARTPATH": "", 521 "TRANSLATIONSOURCESTARTPATH": "",
479 "MAINSCRIPT": "", 522 "MAINSCRIPT": "",
524 "DOCSTRING": "", 567 "DOCSTRING": "",
525 "TESTING_FRAMEWORK": "", 568 "TESTING_FRAMEWORK": "",
526 "LICENSE": "", 569 "LICENSE": "",
527 "EMBEDDED_VENV": False, 570 "EMBEDDED_VENV": False,
528 } 571 }
529 # TODO: Move these to a file categories repository populated through the 572 for category in self.__fileCategoriesRepository:
530 # project browsers 573 self.__pdata[category] = []
531 self.__knownFileCategories = [
532 "FORMS",
533 "OTHERS",
534 "RESOURCES",
535 "SOURCES",
536 "TRANSLATIONS",
537 "INTERFACES",
538 "PROTOCOLS",
539 ]
540 self.__fileCategoryFilterTemplates = {
541 "FORMS": self.tr("Form Files ({0})"),
542 "OTHERS": self.tr("Other Files ({0})"),
543 "RESOURCES": self.tr("Resource Files ({0})"),
544 "SOURCES": self.tr("Source Files ({0})"),
545 "TRANSLATIONS": self.tr("Translation Files ({0})"),
546 "INTERFACES": self.tr("Interface Files ({0})"),
547 "PROTOCOLS": self.tr("Protocol Files ({0})"),
548 }
549 self.__fileCategoryUserStrings = {
550 "FORMS": self.tr("Form Files"),
551 "OTHERS": self.tr("Other Files"),
552 "RESOURCES": self.tr("Resource Files"),
553 "SOURCES": self.tr("Source Files"),
554 "TRANSLATIONS": self.tr("Translation Files"),
555 "INTERFACES": self.tr("Interface Files"),
556 "PROTOCOLS": self.tr("Protocol Files"),
557 }
558 self.__fileCategoryTyeStrings = {
559 "FORMS": self.tr("Forms"),
560 "OTHERS": self.tr("Others"),
561 "RESOURCES": self.tr("Resources"),
562 "SOURCES": self.tr("Sources"),
563 "TRANSLATIONS": self.tr("Translations"),
564 "INTERFACES": self.tr("Interfaces"),
565 "PROTOCOLS": self.tr("Protocols"),
566 }
567 self.__fileCategoryExtensions = {
568 "FORMS": ["*.ui"],
569 "OTHERS": [],
570 "RESOURCES": ["*.qrc"],
571 "SOURCES": ["*.py", "*.pyw"], # Python files as default
572 "TRANSLATIONS": ["*.ts", "*.qm"],
573 "INTERFACES": ["*.idl"],
574 "PROTOCOLS": ["*.proto"],
575 }
576 # until here
577 574
578 self.__initDebugProperties() 575 self.__initDebugProperties()
579 576
580 self.pudata = { 577 self.pudata = {
581 "VCSOVERRIDE": "", 578 "VCSOVERRIDE": "",
703 Public method to get the list of known file categories. 700 Public method to get the list of known file categories.
704 701
705 @return list of known file categories 702 @return list of known file categories
706 @rtype list of str 703 @rtype list of str
707 """ 704 """
708 return self.__knownFileCategories[:] 705 return list(self.__fileCategoriesRepository.keys())
709 706
710 def getFileCategoryFilterString( 707 def getFileCategoryFilterString(
711 self, categories=None, withOthers=False, withAll=True 708 self, categories=None, withOthers=False, withAll=True
712 ): 709 ):
713 """ 710 """
724 @type bool (optional) 721 @type bool (optional)
725 @return file selection filter string 722 @return file selection filter string
726 @rtype str 723 @rtype str
727 """ 724 """
728 if categories is None: 725 if categories is None:
729 categories = [c for c in self.__knownFileCategories if c != "OTHERS"] 726 categories = [c for c in self.__fileCategoriesRepository if c != "OTHERS"]
730 if withOthers: 727 if withOthers:
731 categories.append("OTHERS") 728 categories.append("OTHERS")
732 729
733 patterns = collections.defaultdict(list) 730 patterns = collections.defaultdict(list)
734 for pattern, filetype in self.__pdata["FILETYPES"].items(): 731 for pattern, filetype in self.__pdata["FILETYPES"].items():
735 if filetype in categories and filetype in self.__knownFileCategories: 732 if filetype in categories and filetype in self.__fileCategoriesRepository:
736 patterns[filetype].append(pattern) 733 patterns[filetype].append(pattern)
737 734
738 filters = [] 735 filters = []
739 for filetype in patterns: 736 for filetype in patterns:
740 filters.append( 737 filters.append(
741 self.__fileCategoryFilterTemplates[filetype].format( 738 self.__fileCategoriesRepository[
739 filetype
740 ].fileCategoryFilterTemplate.format(
742 " ".join(sorted(patterns[filetype])) 741 " ".join(sorted(patterns[filetype]))
743 ) 742 )
744 ) 743 )
745 filterString = ";;".join(sorted(filters)) 744 filterString = ";;".join(sorted(filters))
746 if withAll: 745 if withAll:
755 @param category file type category 754 @param category file type category
756 @type str 755 @type str
757 @return user string for the category 756 @return user string for the category
758 @rtype str 757 @rtype str
759 """ 758 """
760 return self.__fileCategoryUserStrings[category] 759 return self.__fileCategoriesRepository[category].fileCategoryUserString
761 760
762 def getFileCategoryType(self, category): 761 def getFileCategoryType(self, category):
763 """ 762 """
764 Public method to get a user type string for the given category. 763 Public method to get a user type string for the given category.
765 764
766 @param category file type category 765 @param category file type category
767 @type str 766 @type str
768 @return user type string for the category 767 @return user type string for the category
769 @rtype str 768 @rtype str
770 """ 769 """
771 return self.__fileCategoryTyeStrings[category] 770 return self.__fileCategoriesRepository[category].fileCategoryTyeString
772 771
773 def getFileCategoryExtension(self, category, reverse=False): 772 def getFileCategoryExtension(self, category, reverse=False):
774 """ 773 """
775 Public method to get a list of default file extensions for the given category. 774 Public method to get a list of default file extensions for the given category.
776 775
782 @return list of default file extensions for the category 781 @return list of default file extensions for the category
783 @rtype list of str 782 @rtype list of str
784 """ 783 """
785 if reverse: 784 if reverse:
786 extensions = [] 785 extensions = []
787 for cat, ext in self.__fileCategoryExtensions.items(): 786 for cat, item in self.__fileCategoriesRepository.items():
788 if cat != category: 787 if cat != category:
789 extensions += ext 788 extensions += item.fileCategoryExtensions[:]
790 return extensions 789 return extensions
791 else: 790 else:
792 return self.__fileCategoryExtensions[category][:] 791 return self.__fileCategoriesRepository[category].fileCategoryExtensions[:]
793 792
794 def initFileTypes(self): 793 def initFileTypes(self):
795 """ 794 """
796 Public method to initialize the filetype associations with default 795 Public method to initialize the filetype associations with default
797 values. 796 values.
1083 1082
1084 self.name = os.path.splitext(os.path.basename(fn))[0] 1083 self.name = os.path.splitext(os.path.basename(fn))[0]
1085 1084
1086 # check, if the files of the project still exist in the 1085 # check, if the files of the project still exist in the
1087 # project directory 1086 # project directory
1088 for fileCategory in self.__knownFileCategories: 1087 for fileCategory in self.getFileCategories():
1089 self.__checkFilesExist(fileCategory) 1088 self.__checkFilesExist(fileCategory)
1090 1089
1091 # get the names of subdirectories the files are stored in 1090 # get the names of subdirectories the files are stored in
1092 for fileCategory in [ 1091 for fileCategory in [c for c in self.getFileCategories() if c != "OTHERS"]:
1093 c for c in self.__knownFileCategories if c != "OTHERS"
1094 ]:
1095 for fn in self.__pdata[fileCategory]: 1092 for fn in self.__pdata[fileCategory]:
1096 dn = os.path.dirname(fn) 1093 dn = os.path.dirname(fn)
1097 if dn not in self.subdirs: 1094 if dn not in self.subdirs:
1098 self.subdirs.append(dn) 1095 self.subdirs.append(dn)
1099 1096
2150 """Reason: {1}</p>""" 2147 """Reason: {1}</p>"""
2151 ).format(oldfn, str(msg)), 2148 ).format(oldfn, str(msg)),
2152 ) 2149 )
2153 return False 2150 return False
2154 2151
2155 if any(fn in self.__pdata[category] for category in self.__knownFileCategories): 2152 if any(fn in self.__pdata[category] for category in self.getFileCategories()):
2156 self.renameFileInPdata(oldfn, newfn, isSourceFile) 2153 self.renameFileInPdata(oldfn, newfn, isSourceFile)
2157 2154
2158 return True 2155 return True
2159 2156
2160 def renameFileInPdata(self, oldname, newname, isSourceFile=False): 2157 def renameFileInPdata(self, oldname, newname, isSourceFile=False):
2187 @return list of files starting with a common prefix (list of strings) 2184 @return list of files starting with a common prefix (list of strings)
2188 """ 2185 """
2189 filelist = [] 2186 filelist = []
2190 start = self.getRelativePath(start) 2187 start = self.getRelativePath(start)
2191 for fileCategory in [ 2188 for fileCategory in [
2192 c for c in self.__knownFileCategories if c != "TRANSLATIONS" 2189 c for c in self.getFileCategories() if c != "TRANSLATIONS"
2193 ]: 2190 ]:
2194 for entry in self.__pdata[fileCategory][:]: 2191 for entry in self.__pdata[fileCategory][:]:
2195 if entry.startswith(start): 2192 if entry.startswith(start):
2196 filelist.append(os.path.join(self.ppath, entry)) 2193 filelist.append(os.path.join(self.ppath, entry))
2197 return filelist 2194 return filelist
2202 """ 2199 """
2203 reorganized = False 2200 reorganized = False
2204 2201
2205 # init data store for the reorganization 2202 # init data store for the reorganization
2206 newPdata = {} 2203 newPdata = {}
2207 for fileCategory in self.__knownFileCategories: 2204 for fileCategory in self.getFileCategories():
2208 newPdata[fileCategory] = [] 2205 newPdata[fileCategory] = []
2209 2206
2210 # iterate over all files checking for a reassignment 2207 # iterate over all files checking for a reassignment
2211 for fileCategory in self.__knownFileCategories: 2208 for fileCategory in self.getFileCategories():
2212 for fn in self.__pdata[fileCategory][:]: 2209 for fn in self.__pdata[fileCategory][:]:
2213 filetype = fileCategory 2210 filetype = fileCategory
2214 bfn = os.path.basename(fn) 2211 bfn = os.path.basename(fn)
2215 for pattern in sorted(self.__pdata["FILETYPES"].keys(), reverse=True): 2212 for pattern in sorted(self.__pdata["FILETYPES"].keys(), reverse=True):
2216 if fnmatch.fnmatch(bfn, pattern): 2213 if fnmatch.fnmatch(bfn, pattern):
2231 ##"PROTOCOLS", 2228 ##"PROTOCOLS",
2232 ##"RESOURCES", 2229 ##"RESOURCES",
2233 ##"OTHERS", 2230 ##"OTHERS",
2234 ##"TRANSLATIONS", 2231 ##"TRANSLATIONS",
2235 ##]: 2232 ##]:
2236 for fileCategory in self.__knownFileCategories: 2233 for fileCategory in self.getFileCategories():
2237 self.__pdata[fileCategory] = newPdata[fileCategory][:] 2234 self.__pdata[fileCategory] = newPdata[fileCategory][:]
2238 2235
2239 # repopulate the model 2236 # repopulate the model
2240 self.__model.projectClosed(False) 2237 self.__model.projectClosed(False)
2241 self.__model.projectOpened() 2238 self.__model.projectOpened()
2256 ##"PROTOCOLS", 2253 ##"PROTOCOLS",
2257 ##"RESOURCES", 2254 ##"RESOURCES",
2258 ##"OTHERS", 2255 ##"OTHERS",
2259 ##]: 2256 ##]:
2260 for fileCategory in [ 2257 for fileCategory in [
2261 c for c in self.__knownFileCategories if c != "TRANSLATIONS" 2258 c for c in self.getFileCategories() if c != "TRANSLATIONS"
2262 ]: 2259 ]:
2263 for entry in self.__pdata[fileCategory][:]: 2260 for entry in self.__pdata[fileCategory][:]:
2264 if entry.startswith(olddn): 2261 if entry.startswith(olddn):
2265 entry = entry.replace(olddn, newdn) 2262 entry = entry.replace(olddn, newdn)
2266 self.appendFile( 2263 self.appendFile(
2285 ##"PROTOCOLS", 2282 ##"PROTOCOLS",
2286 ##"RESOURCES", 2283 ##"RESOURCES",
2287 ##"OTHERS", 2284 ##"OTHERS",
2288 ##]: 2285 ##]:
2289 for fileCategory in [ 2286 for fileCategory in [
2290 c for c in self.__knownFileCategories if c != "TRANSLATIONS" 2287 c for c in self.getFileCategories() if c != "TRANSLATIONS"
2291 ]: 2288 ]:
2292 for entry in self.__pdata[fileCategory][:]: 2289 for entry in self.__pdata[fileCategory][:]:
2293 if entry.startswith(olddn): 2290 if entry.startswith(olddn):
2294 if fileCategory not in typeStrings: 2291 if fileCategory not in typeStrings:
2295 typeStrings.append(fileCategory) 2292 typeStrings.append(fileCategory)
2346 for entry in self.__pdata["OTHERS"][:]: 2343 for entry in self.__pdata["OTHERS"][:]:
2347 if entry.startswith(dn): 2344 if entry.startswith(dn):
2348 self.__pdata["OTHERS"].remove(entry) 2345 self.__pdata["OTHERS"].remove(entry)
2349 dirty = True 2346 dirty = True
2350 dn2 = dn if dn.endswith(os.sep) else dn + os.sep 2347 dn2 = dn if dn.endswith(os.sep) else dn + os.sep
2351 for fileCategory in [c for c in self.__knownFileCategories if c != "OTHERS"]: 2348 for fileCategory in [c for c in self.getFileCategories() if c != "OTHERS"]:
2352 for entry in self.__pdata[fileCategory][:]: 2349 for entry in self.__pdata[fileCategory][:]:
2353 if entry.startswith(dn2): 2350 if entry.startswith(dn2):
2354 self.__pdata[fileCategory].remove(entry) 2351 self.__pdata[fileCategory].remove(entry)
2355 dirty = True 2352 dirty = True
2356 self.__model.removeItem(dn) 2353 self.__model.removeItem(dn)
2441 @return flag indicating, if the project contains the file (boolean) 2438 @return flag indicating, if the project contains the file (boolean)
2442 """ 2439 """
2443 fn = self.getRelativePath(fn) 2440 fn = self.getRelativePath(fn)
2444 return any( 2441 return any(
2445 fn in self.__pdata[category] 2442 fn in self.__pdata[category]
2446 for category in self.__knownFileCategories 2443 for category in self.getFileCategories()
2447 if category != "TRANSLATIONS" 2444 if category != "TRANSLATIONS"
2448 ) 2445 )
2449 2446
2450 def createNewProject(self): 2447 def createNewProject(self):
2451 """ 2448 """
3590 @type boolean 3587 @type boolean
3591 @return list of file names 3588 @return list of file names
3592 @rtype list of str 3589 @rtype list of str
3593 @exception ValueError raised when an unsupported file type is given 3590 @exception ValueError raised when an unsupported file type is given
3594 """ 3591 """
3595 if fileType not in self.__knownFileCategories: 3592 if fileType not in self.getFileCategories():
3596 raise ValueError("Given file type has incorrect value.") 3593 raise ValueError("Given file type has incorrect value.")
3597 3594
3598 if normalized: 3595 if normalized:
3599 return [os.path.join(self.ppath, fn) for fn in self.__pdata[fileType]] 3596 return [os.path.join(self.ppath, fn) for fn in self.__pdata[fileType]]
3600 else: 3597 else:
4004 @rtype bool 4001 @rtype bool
4005 """ 4002 """
4006 newfn = os.path.abspath(fn) 4003 newfn = os.path.abspath(fn)
4007 newfn = self.getRelativePath(newfn) 4004 newfn = self.getRelativePath(newfn)
4008 return any( 4005 return any(
4009 newfn in self.__pdata[category] 4006 newfn in self.__pdata[category] for category in self.getFileCategories()
4010 for category in self.__knownFileCategories
4011 ) 4007 )
4012 4008
4013 def isProjectFile(self, fn): 4009 def isProjectFile(self, fn):
4014 """ 4010 """
4015 Public method used to check, if the passed in filename belongs to the 4011 Public method used to check, if the passed in filename belongs to the
4018 @param fn filename to be checked (string) 4014 @param fn filename to be checked (string)
4019 @return flag indicating membership (boolean) 4015 @return flag indicating membership (boolean)
4020 """ 4016 """
4021 return any( 4017 return any(
4022 self.__checkProjectFileGroup(fn, category) 4018 self.__checkProjectFileGroup(fn, category)
4023 for category in self.__knownFileCategories 4019 for category in self.getFileCategories()
4024 ) 4020 )
4025 4021
4026 def __checkProjectFileGroup(self, fn, group): 4022 def __checkProjectFileGroup(self, fn, group):
4027 """ 4023 """
4028 Private method to check, if a file is in a specific file group of the 4024 Private method to check, if a file is in a specific file group of the
5509 if fnmatch.fnmatch(bfn, pattern): 5505 if fnmatch.fnmatch(bfn, pattern):
5510 filetype = self.__pdata["FILETYPES"][pattern] 5506 filetype = self.__pdata["FILETYPES"][pattern]
5511 break 5507 break
5512 5508
5513 if ( 5509 if (
5514 filetype in self.__knownFileCategories 5510 filetype in self.getFileCategories()
5515 and fn not in self.__pdata[filetype] 5511 and fn not in self.__pdata[filetype]
5516 and ( 5512 and (
5517 filetype != "TRANSLATIONS" 5513 filetype != "TRANSLATIONS"
5518 or ( 5514 or (
5519 filetype == "TRANSLATIONS" 5515 filetype == "TRANSLATIONS"
6091 if not res: 6087 if not res:
6092 return # don't overwrite 6088 return # don't overwrite
6093 6089
6094 # build the list of entries 6090 # build the list of entries
6095 lst_ = [] 6091 lst_ = []
6096 for key in self.__knownFileCategories: 6092 for key in self.getFileCategories():
6097 lst_.extend(self.__pdata[key]) 6093 lst_.extend(self.__pdata[key])
6098 lst = [] 6094 lst = []
6099 for entry in lst_: 6095 for entry in lst_:
6100 if os.path.isdir(self.getAbsolutePath(entry)): 6096 if os.path.isdir(self.getAbsolutePath(entry)):
6101 lst.extend( 6097 lst.extend(

eric ide

mercurial