diff -r ed2f37c1f6b6 -r 1faa0780ae1d Tasks/TaskViewer.py --- a/Tasks/TaskViewer.py Tue Aug 30 20:13:21 2016 +0200 +++ b/Tasks/TaskViewer.py Wed Aug 31 18:20:40 2016 +0200 @@ -15,8 +15,9 @@ import os import fnmatch +import threading -from PyQt5.QtCore import pyqtSignal, Qt +from PyQt5.QtCore import pyqtSignal, Qt, QThread from PyQt5.QtWidgets import QHeaderView, QLineEdit, QTreeWidget, QDialog, \ QInputDialog, QApplication, QMenu, QAbstractItemView, QTreeWidgetItem @@ -77,6 +78,8 @@ self.taskFilter.setActive(False) self.__projectTasksSaveTimer = AutoSaver(self, self.saveProjectTasks) + self.__projectTaskExtractionThread = ProjectTaskExtractionThread() + self.__projectTaskExtractionThread.taskFound.connect(self.addFileTask) self.__projectTasksMenu = QMenu( self.tr("P&roject Tasks"), self) @@ -690,56 +693,56 @@ self.clearProjectTasks(fileOnly=True) # now process them - if not quiet: + if quiet: + ppath = self.project.getProjectPath() + self.__projectTaskExtractionThread.scan( + markers, [os.path.join(ppath, f) for f in files]) + else: progress = E5ProgressDialog( self.tr("Extracting project tasks..."), self.tr("Abort"), 0, len(files), self.tr("%v/%m Files")) progress.setMinimumDuration(0) progress.setWindowTitle(self.tr("Tasks")) - count = 0 - - for file in files: - if not quiet: + count = 0 + + ppath = self.project.getProjectPath() + for file in files: progress.setLabelText( self.tr("Extracting project tasks...\n{0}").format(file)) progress.setValue(count) QApplication.processEvents() if progress.wasCanceled(): break - - QApplication.processEvents() - - fn = os.path.join(self.project.ppath, file) - # read the file and split it into textlines - try: - text, encoding = Utilities.readEncodedFile(fn) - lines = text.splitlines() - except (UnicodeError, IOError): + + fn = os.path.join(ppath, file) + # read the file and split it into textlines + try: + text, encoding = Utilities.readEncodedFile(fn) + lines = text.splitlines() + except (UnicodeError, IOError): + count += 1 + progress.setValue(count) + continue + + # now search tasks and record them + lineIndex = 0 + for line in lines: + lineIndex += 1 + shouldBreak = False + + for taskType, taskMarkers in markers.items(): + for taskMarker in taskMarkers: + index = line.find(taskMarker) + if index > -1: + task = line[index:] + self.addFileTask(task, fn, lineIndex, taskType) + shouldBreak = True + break + if shouldBreak: + break + count += 1 - if not quiet: - progress.setValue(count) - continue - # now search tasks and record them - lineIndex = 0 - for line in lines: - lineIndex += 1 - shouldBreak = False - - for taskType, taskMarkers in markers.items(): - for taskMarker in taskMarkers: - index = line.find(taskMarker) - if index > -1: - task = line[index:] - self.addFileTask(task, fn, lineIndex, taskType) - shouldBreak = True - break - if shouldBreak: - break - - count += 1 - - if not quiet: progress.setValue(len(files)) def __configure(self): @@ -754,3 +757,101 @@ """ if self.projectOpen and Preferences.getProject("TasksProjectAutoSave"): self.project.writeTasks() + + def stopProjectTaskExtraction(self): + """ + Public method to stop the project task extraction thread. + """ + self.__projectTaskExtractionThread.requestInterrupt() + self.__projectTaskExtractionThread.wait() + + +class ProjectTaskExtractionThread(QThread): + """ + Class implementing a thread to extract tasks related to a project. + + @signal taskFound(str, str, int, int) emitted with the task description, + the file name, the line number and task type to signal the presence of + a task + """ + taskFound = pyqtSignal(str, str, int, int) + + def __init__(self, parent=None): + """ + Constructor + + @param parent reference to the parent object (QObject) + """ + super(ProjectTaskExtractionThread, self).__init__() + + self.__lock = threading.Lock() + self.__interrupt = False + + def requestInterrupt(self): + """ + Public method to request iterruption of the thread. + """ + if self.isRunning(): + self.__interrupt = True + + def scan(self, markers, files): + """ + Public method to scan the given list of files for tasks. + + @param markers dictionary of defined task markers + @type dict of lists of str + @param files list of file names to be scanned + @type list of str + """ + with self.__lock: + self.__interrupt = False + self.__files = files[:] + self.__markers = {} + for markerType in markers: + self.__markers[markerType] = markers[markerType][:] + + if not self.isRunning(): + self.start(QThread.LowPriority) + + def run(self): + """ + Public thread method to scan the given files. + """ + with self.__lock: + files = self.__files[:] + markers = {} + for markerType in self.__markers: + markers[markerType] = self.__markers[markerType][:] + + for fn in files: + if self.__interrupt: + break + + # read the file and split it into textlines + try: + text, encoding = Utilities.readEncodedFile(fn) + lines = text.splitlines() + except (UnicodeError, IOError): + continue + + # now search tasks and record them + lineIndex = 0 + for line in lines: + if self.__interrupt: + break + + lineIndex += 1 + found = False + + for taskType, taskMarkers in markers.items(): + for taskMarker in taskMarkers: + index = line.find(taskMarker) + if index > -1: + task = line[index:] + with self.__lock: + self.taskFound.emit(task, fn, lineIndex, + taskType) + found = True + break + if found: + break