--- a/AssistantEric/APIsManager.py Tue Apr 02 19:33:12 2013 +0200 +++ b/AssistantEric/APIsManager.py Wed Apr 03 18:34:47 2013 +0200 @@ -9,8 +9,7 @@ import os -from PyQt4.QtCore import QTimer, QThread, QFileInfo, pyqtSignal, QCoreApplication, \ - QEvent, QDateTime, QObject, Qt +from PyQt4.QtCore import QTimer, QThread, QFileInfo, pyqtSignal, QDateTime, QObject, Qt try: from PyQt4.QtSql import QSqlDatabase, QSqlQuery except ImportError: @@ -25,9 +24,10 @@ import Utilities import Preferences -WorkerStarted = QEvent.User + 2001 -WorkerFinished = QEvent.User + 2002 -WorkerAborted = QEvent.User + 2003 +WorkerStatusStarted = 2001 +WorkerStatusFinished = 2002 +WorkerStatusAborted = 2003 +WorkerStatusFile = 2004 ApisNameProject = "__Project__" @@ -35,7 +35,12 @@ class DbAPIsWorker(QThread): """ Class implementing a worker thread to prepare the API database. + + @signal processing(status, file) emitted to indicate the processing status + (one of WorkerStatus..., string) """ + processing = pyqtSignal(int, str) + populate_api_stmt = """ INSERT INTO api (acWord, context, fullContext, signature, fileId, pictureId) VALUES (:acWord, :context, :fullContext, :signature, :fileId, :pictureId) @@ -144,6 +149,7 @@ if modTimeBases > modTime: modTime = modTimeBases if loadTime < modTime: + self.processing.emit(WorkerStatusFile, apiFile) self.__loadApiFile(apiFile) def __classesAttributesApi(self, module): @@ -221,6 +227,37 @@ if len(apis) > 0: self.__storeApis(apis, bases, apiFile, language) + else: + # just store file info to avoid rereading it every time + self.__storeFileInfoOnly(apiFile) + + def __storeFileInfoOnly(self, apiFile): + """ + Private method to store file info only. + + Doing this avoids rereading the file whenever the API is initialized in case + the given file doesn't contain API data. + """ + db = QSqlDatabase.database(self.__language) + db.transaction() + try: + query = QSqlQuery(db) + # step 1: create entry in file table + query.prepare(self.populate_file_stmt) + query.bindValue(":file", apiFile) + query.exec_() + + # step 2: update the file entry + query.prepare(self.update_file_stmt) + query.bindValue(":lastRead", QDateTime.currentDateTime()) + query.bindValue(":file", apiFile) + query.exec_() + finally: + del query + if self.__aborted: + db.rollback() + else: + db.commit() def __storeApis(self, apis, bases, apiFile, language): """ @@ -341,7 +378,7 @@ query.exec_() if not self.__aborted: - # step 4: update the file entry + # step 5: update the file entry query.prepare(self.update_file_stmt) query.bindValue(":lastRead", QDateTime.currentDateTime()) query.bindValue(":file", apiFile) @@ -393,7 +430,7 @@ """ Public method to perform the threads work. """ - QCoreApplication.postEvent(self.__proxy, QEvent(QEvent.Type(WorkerStarted))) + self.processing.emit(WorkerStatusStarted, "") db = QSqlDatabase.database(self.__language) if db.isValid() and db.isOpen(): @@ -410,23 +447,19 @@ self.__loadApiFileIfNewer(apiFile) if self.__aborted: - QCoreApplication.postEvent(self.__proxy, QEvent(QEvent.Type(WorkerAborted))) + self.processing.emit(WorkerStatusAborted, "") else: - QCoreApplication.postEvent(self.__proxy, QEvent(QEvent.Type(WorkerFinished))) + self.processing.emit(WorkerStatusFinished, "") class DbAPIs(QObject): """ Class implementing an API storage entity. - @signal apiPreparationFinished(language) emitted after the API preparation has finished - @signal apiPreparationStarted(language) emitted after the API preparation has started - @signal apiPreparationCancelled(language) emitted after the API preparation has been - cancelled + @signal apiPreparationStatus(language, status, file) emitted to indicate the + API preparation status for a language """ - apiPreparationFinished = pyqtSignal(str) - apiPreparationStarted = pyqtSignal(str) - apiPreparationCancelled = pyqtSignal(str) + apiPreparationStatus = pyqtSignal(str, int, str) DB_VERSION = 4 @@ -910,6 +943,7 @@ else: apiFiles = Preferences.getEditorAPI(self.__language) self.__worker = DbAPIsWorker(self, self.__language, apiFiles, projectPath) + self.__worker.processing.connect(self.__processingStatus, Qt.QueuedConnection) self.__worker.start() def __processQueue(self): @@ -917,6 +951,7 @@ Private slot to process the queue of files to load. """ if self.__worker is not None and self.__worker.isFinished(): + self.__worker.deleteLater() self.__worker = None if self.__worker is None and len(self.__workerQueue) > 0: @@ -928,6 +963,7 @@ projectPath = "" self.__worker = DbAPIsWorker(self, self.__language, apiFiles, projectPath, refresh=True) + self.__worker.processing.connect(self.__processingStatus, Qt.QueuedConnection) self.__worker.start() def getLexer(self): @@ -948,32 +984,27 @@ return self.__lexer.autoCompletionWordSeparators() return None - def event(self, evt): + def __processingStatus(self, status, filename): """ - Protected method to handle events from the worker thread. - - @param evt reference to the event object (QEvent) - @return flag indicating, if the event was handled (boolean) - """ - if evt.type() == WorkerStarted: - self.__inPreparation = True - self.apiPreparationStarted.emit(self.__language) - return True + Private slot handling the processing signal of the API preparation + thread. - elif evt.type() == WorkerFinished: + @param status preparation status (integer, one of WorkerStatus...) + @param filename name of the file being processed (string) + """ + if status == WorkerStatusStarted: + self.__inPreparation = True + self.apiPreparationStatus.emit(self.__language, WorkerStatusStarted, "") + elif status == WorkerStatusFinished: self.__inPreparation = False - self.apiPreparationFinished.emit(self.__language) + self.apiPreparationStatus.emit(self.__language, WorkerStatusFinished, "") QTimer.singleShot(0, self.__processQueue) - return True - - elif evt.type() == WorkerAborted: + elif status == WorkerStatusAborted: self.__inPreparation = False - self.apiPreparationCancelled.emit(self.__language) + self.apiPreparationStatus.emit(self.__language, WorkerStatusAborted, "") QTimer.singleShot(0, self.__processQueue) - return True - - else: - return QObject.event(self, evt) + elif status == WorkerStatusFile: + self.apiPreparationStatus.emit(self.__language, WorkerStatusFile, filename) ######################################################## ## project related stuff below @@ -1060,9 +1091,7 @@ language == ApisNameProject: # create the api object api = DbAPIs(language) - api.apiPreparationFinished.connect(self.__apiPreparationFinished) - api.apiPreparationStarted.connect(self.__apiPreparationStarted) - api.apiPreparationCancelled.connect(self.__apiPreparationCancelled) + api.apiPreparationStatus.connect(self.__apiPreparationStatus) self.__apis[language] = api return self.__apis[language] else: @@ -1083,7 +1112,8 @@ @param msg message to be shown (string) """ - self.__mw.statusBar().showMessage(msg, 2000) + if msg: + self.__mw.statusBar().showMessage(msg, 2000) def __apiPreparationFinished(self, language): """ @@ -1117,3 +1147,27 @@ language = self.trUtf8("Project") self.__showMessage(self.trUtf8("Preparation of '{0}' APIs cancelled.") .format(language)) + + def __apiPreparationStatus(self, language, status, filename): + """ + Private slot handling the preparation status signal of an API object. + + @param language language of the API (string) + @param status preparation status (integer, one of WorkerStatus...) + @param filename name of the file being processed (string) + """ + if language == ApisNameProject: + language = self.trUtf8("Project") + + if status == WorkerStatusStarted: + self.__showMessage(self.trUtf8("Preparation of '{0}' APIs started.").format( + language)) + elif status == WorkerStatusFile: + self.__showMessage(self.trUtf8("'{0}' APIs: Processing '{1}'").format( + language, os.path.basename(filename))) + elif status == WorkerStatusFinished: + self.__showMessage(self.trUtf8("Preparation of '{0}' APIs finished.").format( + language)) + elif status == WorkerStatusAborted: + self.__showMessage(self.trUtf8("Preparation of '{0}' APIs cancelled.").format( + language))