diff -r 0f023e61a9b5 -r d73c3a1e432b src/eric7/Project/Project.py --- a/src/eric7/Project/Project.py Wed Nov 16 18:11:52 2022 +0100 +++ b/src/eric7/Project/Project.py Thu Nov 17 18:11:42 2022 +0100 @@ -67,26 +67,11 @@ Class implementing the project management functionality. @signal dirty(bool) emitted when the dirty state changes - @signal projectLanguageAdded(str) emitted after a new language was added + @signal projectFileAdded(str, str) emitted after a new file was added + @signal projectFileRemoved(str, str) emitted after a file of the project was removed + @signal projectFileCompiled(str, str) emitted after a form was compiled @signal projectLanguageAddedByCode(str) emitted after a new language was added. The language code is sent by this signal. - @signal projectLanguageRemoved(str) emitted after a language was removed - @signal projectFormAdded(str) emitted after a new form was added - @signal projectFormRemoved(str) emitted after a form was removed - @signal projectFormCompiled(str) emitted after a form was compiled - @signal projectSourceAdded(str) emitted after a new source file was added - @signal projectSourceRemoved(str) emitted after a source was removed - @signal projectInterfaceAdded(str) emitted after a new IDL file was added - @signal projectInterfaceRemoved(str) emitted after a IDL file was removed - @signal projectProtocolAdded(str) emitted after a new proto file was added - @signal projectProtocolRemoved(str) emitted after a proto file was removed - @signal projectResourceAdded(str) emitted after a new resource file was - added - @signal projectResourceRemoved(str) emitted after a resource was removed - @signal projectOthersAdded(str) emitted after a file or directory was added - to the OTHERS project data area - @signal projectOthersRemoved(str) emitted after a file was removed from the - OTHERS project data area @signal projectAboutToBeCreated() emitted just before the project will be created @signal newProjectHooks() emitted after a new project was generated but @@ -131,25 +116,15 @@ a QProcess on stdout @signal appendStderr(str) emitted after something was received from a QProcess on stderr + @signal processChangedProjectFiles() emitted to indicate, that changed project files + should be processed """ dirty = pyqtSignal(bool) - projectLanguageAdded = pyqtSignal(str) + projectFileAdded = pyqtSignal(str, str) + projectFileRemoved = pyqtSignal(str, str) + projectFileCompiled = pyqtSignal(str, str) projectLanguageAddedByCode = pyqtSignal(str) - projectLanguageRemoved = pyqtSignal(str) - projectFormAdded = pyqtSignal(str) - projectFormRemoved = pyqtSignal(str) - projectFormCompiled = pyqtSignal(str) - projectSourceAdded = pyqtSignal(str) - projectSourceRemoved = pyqtSignal(str) - projectInterfaceAdded = pyqtSignal(str) - projectInterfaceRemoved = pyqtSignal(str) - projectProtocolAdded = pyqtSignal(str) - projectProtocolRemoved = pyqtSignal(str) - projectResourceAdded = pyqtSignal(str) - projectResourceRemoved = pyqtSignal(str) - projectOthersAdded = pyqtSignal(str) - projectOthersRemoved = pyqtSignal(str) projectAboutToBeCreated = pyqtSignal() newProjectHooks = pyqtSignal() newProject = pyqtSignal() @@ -176,6 +151,7 @@ projectChanged = pyqtSignal() appendStdout = pyqtSignal(str) appendStderr = pyqtSignal(str) + processChangedProjectFiles = pyqtSignal() eols = [os.linesep, "\n", "\r", "\r\n"] @@ -236,6 +212,8 @@ self.loadedDiagram = None self.__findProjectFileDialog = None + self.processChangedProjectFiles.connect(self.__autoExecuteMake) + def __sourceExtensions(self, language): """ Private method to get the source extensions of a programming language. @@ -548,7 +526,8 @@ "LICENSE": "", "EMBEDDED_VENV": False, } - # TODO: Move these to a file categories repository + # TODO: Move these to a file categories repository populated through the + # project browsers self.__knownFileCategories = [ "FORMS", "OTHERS", @@ -585,6 +564,16 @@ "INTERFACES": self.tr("Interfaces"), "PROTOCOLS": self.tr("Protocols"), } + self.__fileCategoryExtensions = { + "FORMS": ["*.ui"], + "OTHERS": [], + "RESOURCES": ["*.qrc"], + "SOURCES": ["*.py", "*.pyw"], # Python files as default + "TRANSLATIONS": ["*.ts", "*.qm"], + "INTERFACES": ["*.idl"], + "PROTOCOLS": ["*.proto"], + } + # until here self.__initDebugProperties() @@ -623,7 +612,7 @@ def setProjectData(self, data, dataKey=None, setDirty=True): """ Public method to set data associated with the given data key in the project - dictionary + dictionary. Note: If no data key is given or is None, the data must be a dictionary used to update the project data. @@ -781,6 +770,27 @@ """ return self.__fileCategoryTyeStrings[category] + def getFileCategoryExtension(self, category, reverse=False): + """ + Public method to get a list of default file extensions for the given category. + + @param category file type category + @type str + @param reverse flag indicating to get all other extensions except the one of + the given category + @type bool + @return list of default file extensions for the category + @rtype list of str + """ + if reverse: + extensions = [] + for cat, ext in self.__fileCategoryExtensions.items(): + if cat != category: + extensions += ext + return extensions + else: + return self.__fileCategoryExtensions[category][:] + def initFileTypes(self): """ Public method to initialize the filetype associations with default @@ -1077,26 +1087,12 @@ # project directory for fileCategory in self.__knownFileCategories: self.__checkFilesExist(fileCategory) - ##self.__checkFilesExist("SOURCES") - ##self.__checkFilesExist("FORMS") - ##self.__checkFilesExist("INTERFACES") - ##self.__checkFilesExist("PROTOCOLS") - ##self.__checkFilesExist("TRANSLATIONS") - ##self.__checkFilesExist("RESOURCES") - ##self.__checkFilesExist("OTHERS") # get the names of subdirectories the files are stored in for fileCategory in [ c for c in self.__knownFileCategories if c != "OTHERS" ]: for fn in self.__pdata[fileCategory]: - ##self.__pdata["SOURCES"] - ##+ self.__pdata["FORMS"] - ##+ self.__pdata["INTERFACES"] - ##+ self.__pdata["PROTOCOLS"] - ##+ self.__pdata["RESOURCES"] - ##+ self.__pdata["TRANSLATIONS"] - ##): dn = os.path.dirname(fn) if dn not in self.subdirs: self.subdirs.append(dn) @@ -1819,59 +1815,29 @@ if filetype == "__IGNORE__": return - # TODO: change this logic to be more generic (use fileCategory) - if filetype in ["SOURCES", "FORMS", "INTERFACES", "PROTOCOLS", "RESOURCES"]: - if filetype == "SOURCES": - if newfn not in self.__pdata["SOURCES"]: - self.__pdata["SOURCES"].append(newfn) - self.projectSourceAdded.emit(newfn) - updateModel and self.__model.addNewItem("SOURCES", newfn) - dirty = True - else: - updateModel and self.repopulateItem(newfn) - elif filetype == "FORMS": - if newfn not in self.__pdata["FORMS"]: - self.__pdata["FORMS"].append(newfn) - self.projectFormAdded.emit(newfn) - updateModel and self.__model.addNewItem("FORMS", newfn) - dirty = True - else: - updateModel and self.repopulateItem(newfn) - elif filetype == "INTERFACES": - if newfn not in self.__pdata["INTERFACES"]: - self.__pdata["INTERFACES"].append(newfn) - self.projectInterfaceAdded.emit(newfn) - (updateModel and self.__model.addNewItem("INTERFACES", newfn)) - dirty = True - else: - updateModel and self.repopulateItem(newfn) - elif filetype == "PROTOCOLS": - if newfn not in self.__pdata["PROTOCOLS"]: - self.__pdata["PROTOCOLS"].append(newfn) - self.projectProtocolAdded.emit(newfn) - (updateModel and self.__model.addNewItem("PROTOCOLS", newfn)) - dirty = True - else: - updateModel and self.repopulateItem(newfn) - elif filetype == "RESOURCES": - if newfn not in self.__pdata["RESOURCES"]: - self.__pdata["RESOURCES"].append(newfn) - self.projectResourceAdded.emit(newfn) - updateModel and self.__model.addNewItem("RESOURCES", newfn) - dirty = True - else: - updateModel and self.repopulateItem(newfn) + if filetype in ( + category + for category in self.getFileCategories() + if category not in ("TRANSLATIONS", "OTHERS") + ): + if newfn not in self.__pdata[filetype]: + self.__pdata[filetype].append(newfn) + self.projectFileAdded.emit(newfn, filetype) + updateModel and self.__model.addNewItem(filetype, newfn) + dirty = True + else: + updateModel and self.repopulateItem(newfn) if newdir not in self.subdirs: self.subdirs.append(newdir) elif filetype == "TRANSLATIONS": if newfn not in self.__pdata["TRANSLATIONS"]: self.__pdata["TRANSLATIONS"].append(newfn) updateModel and self.__model.addNewItem("TRANSLATIONS", newfn) - self.projectLanguageAdded.emit(newfn) + self.projectFileAdded.emit(newfn, "TRANSLATIONS") dirty = True else: updateModel and self.repopulateItem(newfn) - else: # filetype == "OTHERS" + elif filetype == "OTHERS": if newfn not in self.__pdata["OTHERS"]: self.__pdata["OTHERS"].append(newfn) self.othersAdded(newfn, updateModel) @@ -2121,82 +2087,6 @@ if os.path.isdir(fn) and fn not in self.otherssubdirs: self.otherssubdirs.append(fn) - ##def addSourceFiles(self): - ##""" - ##Public slot to add source files to the current project. - ##""" - ##self.addFiles("source") -## - ##def addUiFiles(self): - ##""" - ##Public slot to add forms to the current project. - ##""" - ##self.addFiles("form") -## - ##def addIdlFiles(self): - ##""" - ##Public slot to add IDL interfaces to the current project. - ##""" - ##self.addFiles("interface") -## - ##def addProtoFiles(self): - ##""" - ##Public slot to add protocol files to the current project. - ##""" - ##self.addFiles("protocol") -## - ##def addResourceFiles(self): - ##""" - ##Public slot to add Qt resources to the current project. - ##""" - ##self.addFiles("resource") -## - ##def addOthersFiles(self): - ##""" - ##Public slot to add files to the OTHERS project data. - ##""" - ##self.addFiles("others") -## - ##def addSourceDir(self): - ##""" - ##Public slot to add all source files of a directory to the current - ##project. - ##""" - ##self.addDirectory("source") -## - ##def addUiDir(self): - ##""" - ##Public slot to add all forms of a directory to the current project. - ##""" - ##self.addDirectory("form") -## - ##def addIdlDir(self): - ##""" - ##Public slot to add all IDL interfaces of a directory to the current - ##project. - ##""" - ##self.addDirectory("interface") -## - ##def addProtoDir(self): - ##""" - ##Public slot to add all protocol files of a directory to the current - ##project. - ##""" - ##self.addDirectory("protocol") -## - ##def addResourceDir(self): - ##""" - ##Public slot to add all Qt resource files of a directory to the current - ##project. - ##""" - ##self.addDirectory("resource") -## - ##def addOthersDir(self): - ##""" - ##Public slot to add a directory to the OTHERS project data. - ##""" - ##self.addDirectory("others") -## def renameMainScript(self, oldfn, newfn): """ Public method to rename the main script. @@ -2262,15 +2152,6 @@ ) return False - ##if ( - ##fn in self.__pdata["SOURCES"] - ##or fn in self.__pdata["FORMS"] - ##or fn in self.__pdata["TRANSLATIONS"] - ##or fn in self.__pdata["INTERFACES"] - ##or fn in self.__pdata["PROTOCOLS"] - ##or fn in self.__pdata["RESOURCES"] - ##or fn in self.__pdata["OTHERS"] - ##): if any(fn in self.__pdata[category] for category in self.__knownFileCategories): self.renameFileInPdata(oldfn, newfn, isSourceFile) @@ -2307,14 +2188,6 @@ """ filelist = [] start = self.getRelativePath(start) - ##for key in [ - ##"SOURCES", - ##"FORMS", - ##"INTERFACES", - ##"PROTOCOLS", - ##"RESOURCES", - ##"OTHERS", - ##]: for fileCategory in [ c for c in self.__knownFileCategories if c != "TRANSLATIONS" ]: @@ -2331,28 +2204,10 @@ # init data store for the reorganization newPdata = {} - ##for key in [ - ##"SOURCES", - ##"FORMS", - ##"INTERFACES", - ##"PROTOCOLS", - ##"RESOURCES", - ##"OTHERS", - ##"TRANSLATIONS", - ##]: for fileCategory in self.__knownFileCategories: newPdata[fileCategory] = [] # iterate over all files checking for a reassignment - ##for key in [ - ##"SOURCES", - ##"FORMS", - ##"INTERFACES", - ##"PROTOCOLS", - ##"RESOURCES", - ##"OTHERS", - ##"TRANSLATIONS", - ##]: for fileCategory in self.__knownFileCategories: for fn in self.__pdata[fileCategory][:]: filetype = fileCategory @@ -2469,34 +2324,14 @@ requested (boolean) """ fn = self.getRelativePath(fn) - dirty = True - # TODO: change this logic to be more generic (use fileCategory) - if fn in self.__pdata["SOURCES"]: - self.__pdata["SOURCES"].remove(fn) - self.projectSourceRemoved.emit(fn) - elif fn in self.__pdata["FORMS"]: - self.__pdata["FORMS"].remove(fn) - self.projectFormRemoved.emit(fn) - elif fn in self.__pdata["INTERFACES"]: - self.__pdata["INTERFACES"].remove(fn) - self.projectInterfaceRemoved.emit(fn) - elif fn in self.__pdata["PROTOCOLS"]: - self.__pdata["PROTOCOLS"].remove(fn) - self.projectProtocolRemoved.emit(fn) - elif fn in self.__pdata["RESOURCES"]: - self.__pdata["RESOURCES"].remove(fn) - self.projectResourceRemoved.emit(fn) - elif fn in self.__pdata["OTHERS"]: - self.__pdata["OTHERS"].remove(fn) - self.projectOthersRemoved.emit(fn) - elif fn in self.__pdata["TRANSLATIONS"]: - self.__pdata["TRANSLATIONS"].remove(fn) - self.projectLanguageRemoved.emit(fn) - else: - dirty = False - updateModel and self.__model.removeItem(fn) - if dirty: - self.setDirty(True) + for fileCategory in self.getFileCategories(): + if fn in self.__pdata[fileCategory]: + self.__pdata[fileCategory].remove(fn) + self.projectFileRemoved(fn, fileCategory) + self.setDirty(True) + if updateModel: + self.__model.removeItem(fn) + break def removeDirectory(self, dn): """ @@ -2513,14 +2348,6 @@ self.__pdata["OTHERS"].remove(entry) dirty = True dn2 = dn if dn.endswith(os.sep) else dn + os.sep - ##for key in [ - ##"SOURCES", - ##"FORMS", - ##"INTERFACES", - ##"PROTOCOLS", - ##"RESOURCES", - ##"TRANSLATIONS", - ##]: for fileCategory in [c for c in self.__knownFileCategories if c != "OTHERS"]: for entry in self.__pdata[fileCategory][:]: if entry.startswith(dn2): @@ -2619,13 +2446,6 @@ for category in self.__knownFileCategories if category != "TRANSLATIONS" ) - ##fn in self.__pdata["SOURCES"] - ##or fn in self.__pdata["FORMS"] - ##or fn in self.__pdata["INTERFACES"] - ##or fn in self.__pdata["PROTOCOLS"] - ##or fn in self.__pdata["RESOURCES"] - ##or fn in self.__pdata["OTHERS"] - ##) def createNewProject(self): """ @@ -3049,7 +2869,7 @@ for ts in tslist: if fnmatch.fnmatch(ts, pattern): self.__pdata["TRANSLATIONS"].append(ts) - self.projectLanguageAdded.emit(ts) + self.projectFileAdded.emit(ts, "TRANSLATIONS") if self.__pdata["TRANSLATIONSBINPATH"]: tpd = os.path.join( self.ppath, self.__pdata["TRANSLATIONSBINPATH"] @@ -3061,7 +2881,7 @@ qmlist = Utilities.direntries(tpd, True, pattern) for qm in qmlist: self.__pdata["TRANSLATIONS"].append(qm) - self.projectLanguageAdded.emit(qm) + self.projectFileAdded.emit(qm, "TRANSLATIONS") if not self.__pdata["MAINSCRIPT"] and bool(mainscriptname): if self.__pdata["PROGLANGUAGE"] in ["Python3", "MicroPython"]: self.__pdata["MAINSCRIPT"] = "{0}.py".format(mainscriptname) @@ -3773,15 +3593,6 @@ @exception ValueError raised when an unsupported file type is given """ if fileType not in self.__knownFileCategories: - ##[ - ##"SOURCES", - ##"FORMS", - ##"RESOURCES", - ##"INTERFACES", - ##"PROTOCOLS", - ##"OTHERS", - ##"TRANSLATIONS", - ##]: raise ValueError("Given file type has incorrect value.") if normalized: @@ -4197,15 +4008,6 @@ return any( newfn in self.__pdata[category] for category in self.__knownFileCategories - ##[ - ##"SOURCES", - ##"FORMS", - ##"INTERFACES", - ##"PROTOCOLS", - ##"RESOURCES", - ##"TRANSLATIONS", - ##"OTHERS", - ##] ) def isProjectFile(self, fn): @@ -4219,15 +4021,6 @@ return any( self.__checkProjectFileGroup(fn, category) for category in self.__knownFileCategories - ##[ - ##"SOURCES", - ##"FORMS", - ##"INTERFACES", - ##"PROTOCOLS", - ##"RESOURCES", - ##"TRANSLATIONS", - ##"OTHERS", - ##] ) def __checkProjectFileGroup(self, fn, group): @@ -4260,58 +4053,22 @@ return False - # TODO: change the following methods to a more generic logic using fileCategories - def isProjectSource(self, fn): - """ - Public method used to check, if the passed in filename belongs to the - project sources. - - @param fn filename to be checked (string) - @return flag indicating membership (boolean) - """ - return self.__checkProjectFileGroup(fn, "SOURCES") - - def isProjectForm(self, fn): - """ - Public method used to check, if the passed in filename belongs to the - project forms. - - @param fn filename to be checked (string) - @return flag indicating membership (boolean) - """ - return self.__checkProjectFileGroup(fn, "FORMS") - - def isProjectInterface(self, fn): - """ - Public method used to check, if the passed in filename belongs to the - project interfaces. - - @param fn filename to be checked (string) - @return flag indicating membership (boolean) - """ - return self.__checkProjectFileGroup(fn, "INTERFACES") - - def isProjectProtocol(self, fn): - """ - Public method used to check, if the passed in filename belongs to the - project protocols. + def isProjectCategory(self, fn, category): + """ + Public method to check, if the passed in filename belongs to the given + category. @param fn filename to be checked @type str + @param category file category to check against + @type str @return flag indicating membership @rtype bool """ - return self.__checkProjectFileGroup(fn, "PROTOCOLS") - - def isProjectResource(self, fn): - """ - Public method used to check, if the passed in filename belongs to the - project resources. - - @param fn filename to be checked (string) - @return flag indicating membership (boolean) - """ - return self.__checkProjectFileGroup(fn, "RESOURCES") + if category in self.getFileCategories(): + return self.__checkProjectFileGroup(fn, category) + else: + return False # unknown category always returns False def initActions(self): """ @@ -5767,21 +5524,6 @@ ) ) ): - ##(filetype == "SOURCES" and fn not in self.__pdata["SOURCES"]) - ##or (filetype == "FORMS" and fn not in self.__pdata["FORMS"]) - ##or (filetype == "INTERFACES" and fn not in self.__pdata["INTERFACES"]) - ##or (filetype == "PROTOCOLS" and fn not in self.__pdata["PROTOCOLS"]) - ##or (filetype == "RESOURCES" and fn not in self.__pdata["RESOURCES"]) - ##or (filetype == "OTHERS" and fn not in self.__pdata["OTHERS"]) - ##or ( - ##filetype == "TRANSLATIONS" - ##and fn not in self.__pdata["TRANSLATIONS"] - ##and ( - ##fnmatch.fnmatch(ns, pattern) - ##or fnmatch.fnmatch(ns, binpattern) - ##) - ##) - ##): if autoInclude and AI: self.appendFile(ns) else: @@ -5825,7 +5567,7 @@ @param updateModel flag indicating an update of the model is requested (boolean) """ - self.projectOthersAdded.emit(fn) + self.projectFileAdded.emit(fn, "OTHERS") updateModel and self.__model.addNewItem("OTHERS", fn) def getActions(self): @@ -6352,15 +6094,6 @@ # build the list of entries lst_ = [] for key in self.__knownFileCategories: - ##[ - ##"SOURCES", - ##"FORMS", - ##"RESOURCES", - ##"TRANSLATIONS", - ##"INTERFACES", - ##"PROTOCOLS", - ##"OTHERS", - ##]: lst_.extend(self.__pdata[key]) lst = [] for entry in lst_: @@ -6746,14 +6479,16 @@ return self.__pdata["MAKEPARAMS"]["MakeEnabled"] @pyqtSlot() - def executeMake(self): - """ - Public slot to execute a project specific make run (auto-run) + def __autoExecuteMake(self): + """ + Private slot to execute a project specific make run (auto-run) (execute or question). """ - self.__executeMake( - questionOnly=self.__pdata["MAKEPARAMS"]["MakeTestOnly"], interactive=False - ) + if Preferences.getProject("AutoExecuteMake"): + self.__executeMake( + questionOnly=self.__pdata["MAKEPARAMS"]["MakeTestOnly"], + interactive=False, + ) @pyqtSlot() def __executeMake(self, questionOnly=False, interactive=True):