src/eric7/Tasks/TaskViewer.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9209
b99e7fd55fd3
child 9413
80c06d472826
equal deleted inserted replaced
9220:e9e7eca7efee 9221:bf71ee032bb4
16 import threading 16 import threading
17 import time 17 import time
18 18
19 from PyQt6.QtCore import pyqtSignal, Qt, QThread 19 from PyQt6.QtCore import pyqtSignal, Qt, QThread
20 from PyQt6.QtWidgets import ( 20 from PyQt6.QtWidgets import (
21 QHeaderView, QLineEdit, QTreeWidget, QDialog, QInputDialog, QApplication, 21 QHeaderView,
22 QMenu, QAbstractItemView, QTreeWidgetItem 22 QLineEdit,
23 QTreeWidget,
24 QDialog,
25 QInputDialog,
26 QApplication,
27 QMenu,
28 QAbstractItemView,
29 QTreeWidgetItem,
23 ) 30 )
24 31
25 from EricWidgets.EricApplication import ericApp 32 from EricWidgets.EricApplication import ericApp
26 from EricWidgets import EricMessageBox 33 from EricWidgets import EricMessageBox
27 from EricWidgets.EricProgressDialog import EricProgressDialog 34 from EricWidgets.EricProgressDialog import EricProgressDialog
32 39
33 import Preferences 40 import Preferences
34 import Utilities 41 import Utilities
35 42
36 from Utilities.AutoSaver import AutoSaver 43 from Utilities.AutoSaver import AutoSaver
37 44
38 45
39 class TaskViewer(QTreeWidget): 46 class TaskViewer(QTreeWidget):
40 """ 47 """
41 Class implementing the task viewer. 48 Class implementing the task viewer.
42 49
43 @signal displayFile(str, int) emitted to go to a file task 50 @signal displayFile(str, int) emitted to go to a file task
44 """ 51 """
52
45 displayFile = pyqtSignal(str, int) 53 displayFile = pyqtSignal(str, int)
46 54
47 def __init__(self, parent, project): 55 def __init__(self, parent, project):
48 """ 56 """
49 Constructor 57 Constructor
50 58
51 @param parent the parent (QWidget) 59 @param parent the parent (QWidget)
52 @param project reference to the project object 60 @param project reference to the project object
53 """ 61 """
54 super().__init__(parent) 62 super().__init__(parent)
55 63
56 self.setSortingEnabled(True) 64 self.setSortingEnabled(True)
57 self.setExpandsOnDoubleClick(False) 65 self.setExpandsOnDoubleClick(False)
58 66
59 self.__headerItem = QTreeWidgetItem( 67 self.__headerItem = QTreeWidgetItem(
60 ["", "", self.tr("Summary"), self.tr("Filename"), 68 ["", "", self.tr("Summary"), self.tr("Filename"), self.tr("Line"), ""]
61 self.tr("Line"), ""]) 69 )
62 self.__headerItem.setIcon( 70 self.__headerItem.setIcon(0, UI.PixmapCache.getIcon("taskCompleted"))
63 0, UI.PixmapCache.getIcon("taskCompleted")) 71 self.__headerItem.setIcon(1, UI.PixmapCache.getIcon("taskPriority"))
64 self.__headerItem.setIcon(
65 1, UI.PixmapCache.getIcon("taskPriority"))
66 self.setHeaderItem(self.__headerItem) 72 self.setHeaderItem(self.__headerItem)
67 73
68 self.header().setSortIndicator(2, Qt.SortOrder.AscendingOrder) 74 self.header().setSortIndicator(2, Qt.SortOrder.AscendingOrder)
69 self.__resizeColumns() 75 self.__resizeColumns()
70 76
71 self.tasks = [] 77 self.tasks = []
72 self.copyTask = None 78 self.copyTask = None
73 self.projectOpen = False 79 self.projectOpen = False
74 self.project = project 80 self.project = project
75 self.__projectTasksScanFilter = "" 81 self.__projectTasksScanFilter = ""
76 82
77 from .TaskFilter import TaskFilter 83 from .TaskFilter import TaskFilter
84
78 self.taskFilter = TaskFilter() 85 self.taskFilter = TaskFilter()
79 self.taskFilter.setActive(False) 86 self.taskFilter.setActive(False)
80 87
81 self.__projectTasksSaveTimer = AutoSaver(self, self.saveProjectTasks) 88 self.__projectTasksSaveTimer = AutoSaver(self, self.saveProjectTasks)
82 self.__projectTaskExtractionThread = ProjectTaskExtractionThread() 89 self.__projectTaskExtractionThread = ProjectTaskExtractionThread()
83 self.__projectTaskExtractionThread.taskFound.connect(self.addFileTask) 90 self.__projectTaskExtractionThread.taskFound.connect(self.addFileTask)
84 91
85 self.__projectTasksMenu = QMenu( 92 self.__projectTasksMenu = QMenu(self.tr("P&roject Tasks"), self)
86 self.tr("P&roject Tasks"), self)
87 self.__projectTasksMenu.addAction( 93 self.__projectTasksMenu.addAction(
88 self.tr("&Regenerate project tasks"), 94 self.tr("&Regenerate project tasks"), self.regenerateProjectTasks
89 self.regenerateProjectTasks) 95 )
90 self.__projectTasksMenu.addSeparator() 96 self.__projectTasksMenu.addSeparator()
91 self.__projectTasksMenu.addAction( 97 self.__projectTasksMenu.addAction(
92 self.tr("&Configure scan options"), 98 self.tr("&Configure scan options"), self.__configureProjectTasksScanOptions
93 self.__configureProjectTasksScanOptions) 99 )
94 100
95 self.__menu = QMenu(self) 101 self.__menu = QMenu(self)
96 self.__menu.addAction(self.tr("&New Task..."), self.__newTask) 102 self.__menu.addAction(self.tr("&New Task..."), self.__newTask)
97 self.subtaskItem = self.__menu.addAction( 103 self.subtaskItem = self.__menu.addAction(
98 self.tr("New &Sub-Task..."), self.__newSubTask) 104 self.tr("New &Sub-Task..."), self.__newSubTask
105 )
99 self.__menu.addSeparator() 106 self.__menu.addSeparator()
100 self.projectTasksMenuItem = self.__menu.addMenu( 107 self.projectTasksMenuItem = self.__menu.addMenu(self.__projectTasksMenu)
101 self.__projectTasksMenu)
102 self.__menu.addSeparator() 108 self.__menu.addSeparator()
103 self.gotoItem = self.__menu.addAction( 109 self.gotoItem = self.__menu.addAction(self.tr("&Go To"), self.__goToTask)
104 self.tr("&Go To"), self.__goToTask)
105 self.__menu.addSeparator() 110 self.__menu.addSeparator()
106 self.copyItem = self.__menu.addAction( 111 self.copyItem = self.__menu.addAction(self.tr("&Copy"), self.__copyTask)
107 self.tr("&Copy"), self.__copyTask) 112 self.pasteItem = self.__menu.addAction(self.tr("&Paste"), self.__pasteTask)
108 self.pasteItem = self.__menu.addAction(
109 self.tr("&Paste"), self.__pasteTask)
110 self.pasteMainItem = self.__menu.addAction( 113 self.pasteMainItem = self.__menu.addAction(
111 self.tr("Paste as &Main Task"), self.__pasteMainTask) 114 self.tr("Paste as &Main Task"), self.__pasteMainTask
112 self.deleteItem = self.__menu.addAction( 115 )
113 self.tr("&Delete"), self.__deleteTask) 116 self.deleteItem = self.__menu.addAction(self.tr("&Delete"), self.__deleteTask)
114 self.__menu.addSeparator() 117 self.__menu.addSeparator()
115 self.markCompletedItem = self.__menu.addAction( 118 self.markCompletedItem = self.__menu.addAction(
116 self.tr("&Mark Completed"), self.__markCompleted) 119 self.tr("&Mark Completed"), self.__markCompleted
120 )
117 self.__menu.addAction( 121 self.__menu.addAction(
118 self.tr("Delete Completed &Tasks"), self.__deleteCompleted) 122 self.tr("Delete Completed &Tasks"), self.__deleteCompleted
123 )
119 self.__menu.addSeparator() 124 self.__menu.addSeparator()
120 self.__menu.addAction( 125 self.__menu.addAction(self.tr("P&roperties..."), self.__editTaskProperties)
121 self.tr("P&roperties..."), self.__editTaskProperties)
122 self.__menu.addSeparator() 126 self.__menu.addSeparator()
123 self.__menuFilteredAct = self.__menu.addAction( 127 self.__menuFilteredAct = self.__menu.addAction(self.tr("&Filtered display"))
124 self.tr("&Filtered display"))
125 self.__menuFilteredAct.setCheckable(True) 128 self.__menuFilteredAct.setCheckable(True)
126 self.__menuFilteredAct.setChecked(False) 129 self.__menuFilteredAct.setChecked(False)
127 self.__menuFilteredAct.triggered[bool].connect(self.__activateFilter) 130 self.__menuFilteredAct.triggered[bool].connect(self.__activateFilter)
128 self.__menu.addAction( 131 self.__menu.addAction(
129 self.tr("Filter c&onfiguration..."), self.__configureFilter) 132 self.tr("Filter c&onfiguration..."), self.__configureFilter
133 )
130 self.__menu.addSeparator() 134 self.__menu.addSeparator()
131 self.__menu.addAction( 135 self.__menu.addAction(self.tr("Resi&ze columns"), self.__resizeColumns)
132 self.tr("Resi&ze columns"), self.__resizeColumns)
133 self.__menu.addSeparator() 136 self.__menu.addSeparator()
134 self.__menu.addAction(self.tr("Configure..."), self.__configure) 137 self.__menu.addAction(self.tr("Configure..."), self.__configure)
135 138
136 self.__backMenu = QMenu(self) 139 self.__backMenu = QMenu(self)
137 self.__backMenu.addAction(self.tr("&New Task..."), self.__newTask) 140 self.__backMenu.addAction(self.tr("&New Task..."), self.__newTask)
138 self.__backMenu.addSeparator() 141 self.__backMenu.addSeparator()
139 self.backProjectTasksMenuItem = self.__backMenu.addMenu( 142 self.backProjectTasksMenuItem = self.__backMenu.addMenu(self.__projectTasksMenu)
140 self.__projectTasksMenu)
141 self.__backMenu.addSeparator() 143 self.__backMenu.addSeparator()
142 self.backPasteItem = self.__backMenu.addAction( 144 self.backPasteItem = self.__backMenu.addAction(
143 self.tr("&Paste"), self.__pasteTask) 145 self.tr("&Paste"), self.__pasteTask
146 )
144 self.backPasteMainItem = self.__backMenu.addAction( 147 self.backPasteMainItem = self.__backMenu.addAction(
145 self.tr("Paste as &Main Task"), self.__pasteMainTask) 148 self.tr("Paste as &Main Task"), self.__pasteMainTask
149 )
146 self.__backMenu.addSeparator() 150 self.__backMenu.addSeparator()
147 self.backDeleteCompletedItem = self.__backMenu.addAction( 151 self.backDeleteCompletedItem = self.__backMenu.addAction(
148 self.tr("Delete Completed &Tasks"), self.__deleteCompleted) 152 self.tr("Delete Completed &Tasks"), self.__deleteCompleted
153 )
149 self.__backMenu.addSeparator() 154 self.__backMenu.addSeparator()
150 self.__backMenuFilteredAct = self.__backMenu.addAction( 155 self.__backMenuFilteredAct = self.__backMenu.addAction(
151 self.tr("&Filtered display")) 156 self.tr("&Filtered display")
157 )
152 self.__backMenuFilteredAct.setCheckable(True) 158 self.__backMenuFilteredAct.setCheckable(True)
153 self.__backMenuFilteredAct.setChecked(False) 159 self.__backMenuFilteredAct.setChecked(False)
154 self.__backMenuFilteredAct.triggered[bool].connect( 160 self.__backMenuFilteredAct.triggered[bool].connect(self.__activateFilter)
155 self.__activateFilter)
156 self.__backMenu.addAction( 161 self.__backMenu.addAction(
157 self.tr("Filter c&onfiguration..."), self.__configureFilter) 162 self.tr("Filter c&onfiguration..."), self.__configureFilter
163 )
158 self.__backMenu.addSeparator() 164 self.__backMenu.addSeparator()
159 self.__backMenu.addAction( 165 self.__backMenu.addAction(self.tr("Resi&ze columns"), self.__resizeColumns)
160 self.tr("Resi&ze columns"), self.__resizeColumns)
161 self.__backMenu.addSeparator() 166 self.__backMenu.addSeparator()
162 self.__backMenu.addAction( 167 self.__backMenu.addAction(self.tr("Configure..."), self.__configure)
163 self.tr("Configure..."), self.__configure) 168
164
165 self.__activating = False 169 self.__activating = False
166 170
167 self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) 171 self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
168 self.customContextMenuRequested.connect(self.__showContextMenu) 172 self.customContextMenuRequested.connect(self.__showContextMenu)
169 self.itemActivated.connect(self.__taskItemActivated) 173 self.itemActivated.connect(self.__taskItemActivated)
170 174
171 self.setWindowIcon(UI.PixmapCache.getIcon("eric")) 175 self.setWindowIcon(UI.PixmapCache.getIcon("eric"))
172 176
173 self.__generateTopLevelItems() 177 self.__generateTopLevelItems()
174 178
175 def __generateTopLevelItems(self): 179 def __generateTopLevelItems(self):
176 """ 180 """
177 Private method to generate the 'Extracted Tasks' item. 181 Private method to generate the 'Extracted Tasks' item.
178 """ 182 """
179 self.__extractedItem = QTreeWidgetItem(self, 183 self.__extractedItem = QTreeWidgetItem(self, [self.tr("Extracted Tasks")])
180 [self.tr("Extracted Tasks")]) 184 self.__manualItem = QTreeWidgetItem(self, [self.tr("Manual Tasks")])
181 self.__manualItem = QTreeWidgetItem(self,
182 [self.tr("Manual Tasks")])
183 for itm in [self.__extractedItem, self.__manualItem]: 185 for itm in [self.__extractedItem, self.__manualItem]:
184 itm.setFirstColumnSpanned(True) 186 itm.setFirstColumnSpanned(True)
185 itm.setExpanded(True) 187 itm.setExpanded(True)
186 itm.setHidden(True) 188 itm.setHidden(True)
187 font = itm.font(0) 189 font = itm.font(0)
188 font.setUnderline(True) 190 font.setUnderline(True)
189 itm.setFont(0, font) 191 itm.setFont(0, font)
190 192
191 def __checkTopLevelItems(self): 193 def __checkTopLevelItems(self):
192 """ 194 """
193 Private slot to check the 'Extracted Tasks' item for children. 195 Private slot to check the 'Extracted Tasks' item for children.
194 """ 196 """
195 for itm in [self.__extractedItem, self.__manualItem]: 197 for itm in [self.__extractedItem, self.__manualItem]:
196 visibleCount = itm.childCount() 198 visibleCount = itm.childCount()
197 for index in range(itm.childCount()): 199 for index in range(itm.childCount()):
198 if itm.child(index).isHidden(): 200 if itm.child(index).isHidden():
199 visibleCount -= 1 201 visibleCount -= 1
200 itm.setHidden(visibleCount == 0) 202 itm.setHidden(visibleCount == 0)
201 203
202 def __resort(self): 204 def __resort(self):
203 """ 205 """
204 Private method to resort the tree. 206 Private method to resort the tree.
205 """ 207 """
206 self.sortItems(self.sortColumn(), self.header().sortIndicatorOrder()) 208 self.sortItems(self.sortColumn(), self.header().sortIndicatorOrder())
207 209
208 def __resizeColumns(self): 210 def __resizeColumns(self):
209 """ 211 """
210 Private method to resize the list columns. 212 Private method to resize the list columns.
211 """ 213 """
212 self.header().resizeSections(QHeaderView.ResizeMode.ResizeToContents) 214 self.header().resizeSections(QHeaderView.ResizeMode.ResizeToContents)
213 self.header().setStretchLastSection(True) 215 self.header().setStretchLastSection(True)
214 216
215 def findParentTask(self, parentUid): 217 def findParentTask(self, parentUid):
216 """ 218 """
217 Public method to find a parent task by its ID. 219 Public method to find a parent task by its ID.
218 220
219 @param parentUid uid of the parent task (string) 221 @param parentUid uid of the parent task (string)
220 @return reference to the task (Task) 222 @return reference to the task (Task)
221 """ 223 """
222 if not parentUid: 224 if not parentUid:
223 return None 225 return None
224 226
225 parentTask = None 227 parentTask = None
226 for task in self.tasks: 228 for task in self.tasks:
227 if task.getUuid() == parentUid: 229 if task.getUuid() == parentUid:
228 parentTask = task 230 parentTask = task
229 break 231 break
230 232
231 return parentTask 233 return parentTask
232 234
233 def containsTask(self, taskToTest): 235 def containsTask(self, taskToTest):
234 """ 236 """
235 Public method to test, if a task is already in the tasks list. 237 Public method to test, if a task is already in the tasks list.
236 238
237 @param taskToTest task to look for 239 @param taskToTest task to look for
238 @type Task 240 @type Task
239 @return flag indicating the existence of the task 241 @return flag indicating the existence of the task
240 @rtype bool 242 @rtype bool
241 """ 243 """
242 if taskToTest is None: 244 if taskToTest is None:
243 # play it safe 245 # play it safe
244 return False 246 return False
245 247
246 return any( 248 return any(
247 (task.summary == taskToTest.summary) and 249 (task.summary == taskToTest.summary)
248 (task.filename == taskToTest.filename) and 250 and (task.filename == taskToTest.filename)
249 (task.lineno == taskToTest.lineno) 251 and (task.lineno == taskToTest.lineno)
250 for task in self.tasks 252 for task in self.tasks
251 ) 253 )
252 254
253 def __refreshDisplay(self): 255 def __refreshDisplay(self):
254 """ 256 """
255 Private method to refresh the display. 257 Private method to refresh the display.
256 """ 258 """
257 for task in self.tasks: 259 for task in self.tasks:
258 task.setHidden(not self.taskFilter.showTask(task)) 260 task.setHidden(not self.taskFilter.showTask(task))
259 261
260 self.__checkTopLevelItems() 262 self.__checkTopLevelItems()
261 self.__resort() 263 self.__resort()
262 self.__resizeColumns() 264 self.__resizeColumns()
263 265
264 def __taskItemActivated(self, itm, col): 266 def __taskItemActivated(self, itm, col):
265 """ 267 """
266 Private slot to handle the activation of an item. 268 Private slot to handle the activation of an item.
267 269
268 @param itm reference to the activated item (QTreeWidgetItem) 270 @param itm reference to the activated item (QTreeWidgetItem)
269 @param col column the item was activated in (integer) 271 @param col column the item was activated in (integer)
270 """ 272 """
271 if ( 273 if (
272 not self.__activating and 274 not self.__activating
273 itm is not self.__extractedItem and 275 and itm is not self.__extractedItem
274 itm is not self.__manualItem 276 and itm is not self.__manualItem
275 ): 277 ):
276 self.__activating = True 278 self.__activating = True
277 fn = itm.getFilename() 279 fn = itm.getFilename()
278 if fn: 280 if fn:
279 if os.path.exists(fn): 281 if os.path.exists(fn):
286 self.__activating = False 288 self.__activating = False
287 289
288 def __showContextMenu(self, coord): 290 def __showContextMenu(self, coord):
289 """ 291 """
290 Private slot to show the context menu of the list. 292 Private slot to show the context menu of the list.
291 293
292 @param coord the position of the mouse pointer (QPoint) 294 @param coord the position of the mouse pointer (QPoint)
293 """ 295 """
294 itm = self.itemAt(coord) 296 itm = self.itemAt(coord)
295 coord = self.mapToGlobal(coord) 297 coord = self.mapToGlobal(coord)
296 if ( 298 if itm is None or itm is self.__extractedItem or itm is self.__manualItem:
297 itm is None or
298 itm is self.__extractedItem or
299 itm is self.__manualItem
300 ):
301 self.backProjectTasksMenuItem.setEnabled(self.projectOpen) 299 self.backProjectTasksMenuItem.setEnabled(self.projectOpen)
302 if self.copyTask: 300 if self.copyTask:
303 self.backPasteItem.setEnabled(True) 301 self.backPasteItem.setEnabled(True)
304 self.backPasteMainItem.setEnabled(True) 302 self.backPasteMainItem.setEnabled(True)
305 else: 303 else:
306 self.backPasteItem.setEnabled(False) 304 self.backPasteItem.setEnabled(False)
307 self.backPasteMainItem.setEnabled(False) 305 self.backPasteMainItem.setEnabled(False)
308 self.backDeleteCompletedItem.setEnabled( 306 self.backDeleteCompletedItem.setEnabled(bool(self.tasks))
309 bool(self.tasks))
310 self.__backMenu.popup(coord) 307 self.__backMenu.popup(coord)
311 else: 308 else:
312 self.projectTasksMenuItem.setEnabled(self.projectOpen) 309 self.projectTasksMenuItem.setEnabled(self.projectOpen)
313 if itm.getFilename(): 310 if itm.getFilename():
314 self.gotoItem.setEnabled(True) 311 self.gotoItem.setEnabled(True)
326 self.pasteItem.setEnabled(True) 323 self.pasteItem.setEnabled(True)
327 self.pasteMainItem.setEnabled(True) 324 self.pasteMainItem.setEnabled(True)
328 else: 325 else:
329 self.pasteItem.setEnabled(False) 326 self.pasteItem.setEnabled(False)
330 self.pasteMainItem.setEnabled(False) 327 self.pasteMainItem.setEnabled(False)
331 328
332 self.__menu.popup(coord) 329 self.__menu.popup(coord)
333 330
334 def setProjectOpen(self, o=False): 331 def setProjectOpen(self, o=False):
335 """ 332 """
336 Public slot to set the project status. 333 Public slot to set the project status.
337 334
338 @param o flag indicating the project status 335 @param o flag indicating the project status
339 """ 336 """
340 self.projectOpen = o 337 self.projectOpen = o
341 338
342 def addTask(self, summary, priority=TaskPriority.NORMAL, filename="", 339 def addTask(
343 lineno=0, completed=False, _time=0, isProjectTask=False, 340 self,
344 taskType=TaskType.TODO, description="", uid="", 341 summary,
345 parentTask=None): 342 priority=TaskPriority.NORMAL,
343 filename="",
344 lineno=0,
345 completed=False,
346 _time=0,
347 isProjectTask=False,
348 taskType=TaskType.TODO,
349 description="",
350 uid="",
351 parentTask=None,
352 ):
346 """ 353 """
347 Public slot to add a task. 354 Public slot to add a task.
348 355
349 @param summary summary text of the task 356 @param summary summary text of the task
350 @type str 357 @type str
351 @param priority priority of the task 358 @param priority priority of the task
352 @type TaskPriority 359 @type TaskPriority
353 @param filename filename containing the task 360 @param filename filename containing the task
385 # parent task item 392 # parent task item
386 if parentTask: 393 if parentTask:
387 parentUid = parentTask.getUuid() 394 parentUid = parentTask.getUuid()
388 else: 395 else:
389 parentUid = "" 396 parentUid = ""
390 task = Task(summary, priority, filename, lineno, completed, 397 task = Task(
391 _time, isProjectTask, taskType, 398 summary,
392 self.project, description, uid, parentUid) 399 priority,
400 filename,
401 lineno,
402 completed,
403 _time,
404 isProjectTask,
405 taskType,
406 self.project,
407 description,
408 uid,
409 parentUid,
410 )
393 if not self.containsTask(task): 411 if not self.containsTask(task):
394 self.tasks.append(task) 412 self.tasks.append(task)
395 if parentTask: 413 if parentTask:
396 parentTask.addChild(task) 414 parentTask.addChild(task)
397 parentTask.setExpanded(True) 415 parentTask.setExpanded(True)
398 elif filename: 416 elif filename:
399 self.__extractedItem.addChild(task) 417 self.__extractedItem.addChild(task)
400 else: 418 else:
401 self.__manualItem.addChild(task) 419 self.__manualItem.addChild(task)
402 task.setHidden(not self.taskFilter.showTask(task)) 420 task.setHidden(not self.taskFilter.showTask(task))
403 421
404 self.__checkTopLevelItems() 422 self.__checkTopLevelItems()
405 self.__resort() 423 self.__resort()
406 self.__resizeColumns() 424 self.__resizeColumns()
407 425
408 if isProjectTask: 426 if isProjectTask:
409 self.__projectTasksSaveTimer.changeOccurred() 427 self.__projectTasksSaveTimer.changeOccurred()
410 428
411 return task 429 return task
412 else: 430 else:
413 return None 431 return None
414 432
415 def addFileTask(self, summary, filename, lineno, taskType=TaskType.TODO, 433 def addFileTask(
416 description=""): 434 self, summary, filename, lineno, taskType=TaskType.TODO, description=""
435 ):
417 """ 436 """
418 Public slot to add a file related task. 437 Public slot to add a file related task.
419 438
420 @param summary summary text of the task 439 @param summary summary text of the task
421 @type str 440 @type str
422 @param filename filename containing the task 441 @param filename filename containing the task
423 @type str 442 @type str
424 @param lineno line number containing the task 443 @param lineno line number containing the task
426 @param taskType type of the task 445 @param taskType type of the task
427 @type TaskType 446 @type TaskType
428 @param description explanatory text of the task 447 @param description explanatory text of the task
429 @type str 448 @type str
430 """ 449 """
431 self.addTask(summary, filename=filename, lineno=lineno, 450 self.addTask(
432 isProjectTask=( 451 summary,
433 self.project and 452 filename=filename,
434 self.project.isProjectSource(filename)), 453 lineno=lineno,
435 taskType=TaskType(taskType), description=description) 454 isProjectTask=(self.project and self.project.isProjectSource(filename)),
436 455 taskType=TaskType(taskType),
456 description=description,
457 )
458
437 def getProjectTasks(self): 459 def getProjectTasks(self):
438 """ 460 """
439 Public method to retrieve all project related tasks. 461 Public method to retrieve all project related tasks.
440 462
441 @return copy of tasks (list of Task) 463 @return copy of tasks (list of Task)
442 """ 464 """
443 tasks = [task for task in self.tasks if task.isProjectTask()] 465 tasks = [task for task in self.tasks if task.isProjectTask()]
444 return tasks[:] 466 return tasks[:]
445 467
446 def getGlobalTasks(self): 468 def getGlobalTasks(self):
447 """ 469 """
448 Public method to retrieve all non project related tasks. 470 Public method to retrieve all non project related tasks.
449 471
450 @return copy of tasks (list of Task) 472 @return copy of tasks (list of Task)
451 """ 473 """
452 tasks = [task for task in self.tasks if not task.isProjectTask()] 474 tasks = [task for task in self.tasks if not task.isProjectTask()]
453 return tasks[:] 475 return tasks[:]
454 476
455 def clearTasks(self): 477 def clearTasks(self):
456 """ 478 """
457 Public slot to clear all tasks from display. 479 Public slot to clear all tasks from display.
458 """ 480 """
459 self.tasks = [] 481 self.tasks = []
460 self.clear() 482 self.clear()
461 self.__generateTopLevelItems() 483 self.__generateTopLevelItems()
462 484
463 def clearProjectTasks(self, fileOnly=False): 485 def clearProjectTasks(self, fileOnly=False):
464 """ 486 """
465 Public slot to clear project related tasks. 487 Public slot to clear project related tasks.
466 488
467 @param fileOnly flag indicating to clear only file related 489 @param fileOnly flag indicating to clear only file related
468 project tasks (boolean) 490 project tasks (boolean)
469 """ 491 """
470 for task in reversed(self.tasks[:]): 492 for task in reversed(self.tasks[:]):
471 if ( 493 if (fileOnly and task.isProjectFileTask()) or (
472 (fileOnly and task.isProjectFileTask()) or 494 not fileOnly and task.isProjectTask()
473 (not fileOnly and task.isProjectTask())
474 ): 495 ):
475 if self.copyTask == task: 496 if self.copyTask == task:
476 self.copyTask = None 497 self.copyTask = None
477 parent = task.parent() 498 parent = task.parent()
478 parent.removeChild(task) 499 parent.removeChild(task)
479 self.tasks.remove(task) 500 self.tasks.remove(task)
480 del task 501 del task
481 502
482 self.__checkTopLevelItems() 503 self.__checkTopLevelItems()
483 self.__resort() 504 self.__resort()
484 self.__resizeColumns() 505 self.__resizeColumns()
485 506
486 def clearFileTasks(self, filename, conditionally=False): 507 def clearFileTasks(self, filename, conditionally=False):
487 """ 508 """
488 Public slot to clear all tasks related to a file. 509 Public slot to clear all tasks related to a file.
489 510
490 @param filename name of the file (string) 511 @param filename name of the file (string)
491 @param conditionally flag indicating to clear the tasks of the file 512 @param conditionally flag indicating to clear the tasks of the file
492 checking some conditions (boolean) 513 checking some conditions (boolean)
493 """ 514 """
494 if conditionally: 515 if conditionally:
504 self.__extractedItem.removeChild(task) 525 self.__extractedItem.removeChild(task)
505 self.tasks.remove(task) 526 self.tasks.remove(task)
506 if task.isProjectTask: 527 if task.isProjectTask:
507 self.__projectTasksSaveTimer.changeOccurred() 528 self.__projectTasksSaveTimer.changeOccurred()
508 del task 529 del task
509 530
510 self.__checkTopLevelItems() 531 self.__checkTopLevelItems()
511 self.__resort() 532 self.__resort()
512 self.__resizeColumns() 533 self.__resizeColumns()
513 534
514 def __editTaskProperties(self): 535 def __editTaskProperties(self):
515 """ 536 """
516 Private slot to handle the "Properties" context menu entry. 537 Private slot to handle the "Properties" context menu entry.
517 """ 538 """
518 from .TaskPropertiesDialog import TaskPropertiesDialog 539 from .TaskPropertiesDialog import TaskPropertiesDialog
540
519 task = self.currentItem() 541 task = self.currentItem()
520 dlg = TaskPropertiesDialog(task, parent=self, 542 dlg = TaskPropertiesDialog(task, parent=self, projectOpen=self.projectOpen)
521 projectOpen=self.projectOpen) 543 if dlg.exec() == QDialog.DialogCode.Accepted and dlg.isManualTaskMode():
522 if ( 544 (
523 dlg.exec() == QDialog.DialogCode.Accepted and 545 summary,
524 dlg.isManualTaskMode() 546 priority,
525 ): 547 taskType,
526 (summary, priority, taskType, completed, isProjectTask, 548 completed,
527 description) = dlg.getData() 549 isProjectTask,
550 description,
551 ) = dlg.getData()
528 task.setSummary(summary) 552 task.setSummary(summary)
529 task.setPriority(priority) 553 task.setPriority(priority)
530 task.setTaskType(taskType) 554 task.setTaskType(taskType)
531 task.setCompleted(completed) 555 task.setCompleted(completed)
532 task.setProjectTask(isProjectTask) 556 task.setProjectTask(isProjectTask)
533 task.setDescription(description) 557 task.setDescription(description)
534 self.__projectTasksSaveTimer.changeOccurred() 558 self.__projectTasksSaveTimer.changeOccurred()
535 559
536 def __newTask(self): 560 def __newTask(self):
537 """ 561 """
538 Private slot to handle the "New Task" context menu entry. 562 Private slot to handle the "New Task" context menu entry.
539 """ 563 """
540 from .TaskPropertiesDialog import TaskPropertiesDialog 564 from .TaskPropertiesDialog import TaskPropertiesDialog
541 dlg = TaskPropertiesDialog(None, parent=self, 565
542 projectOpen=self.projectOpen) 566 dlg = TaskPropertiesDialog(None, parent=self, projectOpen=self.projectOpen)
543 if dlg.exec() == QDialog.DialogCode.Accepted: 567 if dlg.exec() == QDialog.DialogCode.Accepted:
544 (summary, priority, taskType, completed, isProjectTask, 568 (
545 description) = dlg.getData() 569 summary,
546 self.addTask(summary, priority, completed=completed, 570 priority,
547 isProjectTask=isProjectTask, taskType=taskType, 571 taskType,
548 description=description) 572 completed,
549 573 isProjectTask,
574 description,
575 ) = dlg.getData()
576 self.addTask(
577 summary,
578 priority,
579 completed=completed,
580 isProjectTask=isProjectTask,
581 taskType=taskType,
582 description=description,
583 )
584
550 def __newSubTask(self): 585 def __newSubTask(self):
551 """ 586 """
552 Private slot to handle the "New Sub-Task" context menu entry. 587 Private slot to handle the "New Sub-Task" context menu entry.
553 """ 588 """
554 parentTask = self.currentItem() 589 parentTask = self.currentItem()
555 projectTask = parentTask.isProjectTask() 590 projectTask = parentTask.isProjectTask()
556 591
557 from .TaskPropertiesDialog import TaskPropertiesDialog 592 from .TaskPropertiesDialog import TaskPropertiesDialog
558 dlg = TaskPropertiesDialog(None, parent=self, 593
559 projectOpen=self.projectOpen) 594 dlg = TaskPropertiesDialog(None, parent=self, projectOpen=self.projectOpen)
560 dlg.setSubTaskMode(projectTask) 595 dlg.setSubTaskMode(projectTask)
561 if dlg.exec() == QDialog.DialogCode.Accepted: 596 if dlg.exec() == QDialog.DialogCode.Accepted:
562 (summary, priority, taskType, completed, isProjectTask, 597 (
563 description) = dlg.getData() 598 summary,
564 self.addTask(summary, priority, completed=completed, 599 priority,
565 isProjectTask=isProjectTask, taskType=taskType, 600 taskType,
566 description=description, parentTask=parentTask) 601 completed,
567 602 isProjectTask,
603 description,
604 ) = dlg.getData()
605 self.addTask(
606 summary,
607 priority,
608 completed=completed,
609 isProjectTask=isProjectTask,
610 taskType=taskType,
611 description=description,
612 parentTask=parentTask,
613 )
614
568 def __markCompleted(self): 615 def __markCompleted(self):
569 """ 616 """
570 Private slot to handle the "Mark Completed" context menu entry. 617 Private slot to handle the "Mark Completed" context menu entry.
571 """ 618 """
572 task = self.currentItem() 619 task = self.currentItem()
573 task.setCompleted(True) 620 task.setCompleted(True)
574 621
575 def __deleteCompleted(self): 622 def __deleteCompleted(self):
576 """ 623 """
577 Private slot to handle the "Delete Completed Tasks" context menu entry. 624 Private slot to handle the "Delete Completed Tasks" context menu entry.
578 """ 625 """
579 for task in reversed(self.tasks[:]): 626 for task in reversed(self.tasks[:]):
584 parent.removeChild(task) 631 parent.removeChild(task)
585 self.tasks.remove(task) 632 self.tasks.remove(task)
586 if task.isProjectTask: 633 if task.isProjectTask:
587 self.__projectTasksSaveTimer.changeOccurred() 634 self.__projectTasksSaveTimer.changeOccurred()
588 del task 635 del task
589 636
590 self.__checkTopLevelItems() 637 self.__checkTopLevelItems()
591 self.__resort() 638 self.__resort()
592 self.__resizeColumns() 639 self.__resizeColumns()
593 640
594 ci = self.currentItem() 641 ci = self.currentItem()
595 if ci: 642 if ci:
596 ind = self.indexFromItem(ci, self.currentColumn()) 643 ind = self.indexFromItem(ci, self.currentColumn())
597 self.scrollTo(ind, QAbstractItemView.ScrollHint.PositionAtCenter) 644 self.scrollTo(ind, QAbstractItemView.ScrollHint.PositionAtCenter)
598 645
599 def __copyTask(self): 646 def __copyTask(self):
600 """ 647 """
601 Private slot to handle the "Copy" context menu entry. 648 Private slot to handle the "Copy" context menu entry.
602 """ 649 """
603 task = self.currentItem() 650 task = self.currentItem()
604 self.copyTask = task 651 self.copyTask = task
605 652
606 def __pasteTask(self): 653 def __pasteTask(self):
607 """ 654 """
608 Private slot to handle the "Paste" context menu entry. 655 Private slot to handle the "Paste" context menu entry.
609 """ 656 """
610 if self.copyTask: 657 if self.copyTask:
611 parent = self.copyTask.parent() 658 parent = self.copyTask.parent()
612 if not isinstance(parent, Task): 659 if not isinstance(parent, Task):
613 parent = None 660 parent = None
614 661
615 self.addTask(self.copyTask.summary, 662 self.addTask(
616 priority=self.copyTask.priority, 663 self.copyTask.summary,
617 completed=self.copyTask.completed, 664 priority=self.copyTask.priority,
618 description=self.copyTask.description, 665 completed=self.copyTask.completed,
619 isProjectTask=self.copyTask._isProjectTask, 666 description=self.copyTask.description,
620 parentTask=parent) 667 isProjectTask=self.copyTask._isProjectTask,
621 668 parentTask=parent,
669 )
670
622 def __pasteMainTask(self): 671 def __pasteMainTask(self):
623 """ 672 """
624 Private slot to handle the "Paste as Main Task" context menu entry. 673 Private slot to handle the "Paste as Main Task" context menu entry.
625 """ 674 """
626 if self.copyTask: 675 if self.copyTask:
627 self.addTask(self.copyTask.summary, 676 self.addTask(
628 priority=self.copyTask.priority, 677 self.copyTask.summary,
629 completed=self.copyTask.completed, 678 priority=self.copyTask.priority,
630 description=self.copyTask.description, 679 completed=self.copyTask.completed,
631 isProjectTask=self.copyTask._isProjectTask) 680 description=self.copyTask.description,
632 681 isProjectTask=self.copyTask._isProjectTask,
682 )
683
633 def __deleteSubTasks(self, task): 684 def __deleteSubTasks(self, task):
634 """ 685 """
635 Private method to delete all sub-tasks. 686 Private method to delete all sub-tasks.
636 687
637 @param task task to delete sub-tasks of (Task) 688 @param task task to delete sub-tasks of (Task)
638 """ 689 """
639 for subtask in task.takeChildren(): 690 for subtask in task.takeChildren():
640 if self.copyTask == subtask: 691 if self.copyTask == subtask:
641 self.copyTask = None 692 self.copyTask = None
642 if subtask.childCount() > 0: 693 if subtask.childCount() > 0:
643 self.__deleteSubTasks(subtask) 694 self.__deleteSubTasks(subtask)
644 self.tasks.remove(subtask) 695 self.tasks.remove(subtask)
645 696
646 def __deleteTask(self, task=None): 697 def __deleteTask(self, task=None):
647 """ 698 """
648 Private slot to delete a task. 699 Private slot to delete a task.
649 700
650 @param task task to be deleted 701 @param task task to be deleted
651 @type Task 702 @type Task
652 """ 703 """
653 if task is None: 704 if task is None:
654 # called via "Delete Task" context menu entry 705 # called via "Delete Task" context menu entry
655 task = self.currentItem() 706 task = self.currentItem()
656 707
657 if self.copyTask is task: 708 if self.copyTask is task:
658 self.copyTask = None 709 self.copyTask = None
659 if task.childCount() > 0: 710 if task.childCount() > 0:
660 self.__deleteSubTasks(task) 711 self.__deleteSubTasks(task)
661 parent = task.parent() 712 parent = task.parent()
662 parent.removeChild(task) 713 parent.removeChild(task)
663 self.tasks.remove(task) 714 self.tasks.remove(task)
664 if task.isProjectTask: 715 if task.isProjectTask:
665 self.__projectTasksSaveTimer.changeOccurred() 716 self.__projectTasksSaveTimer.changeOccurred()
666 del task 717 del task
667 718
668 self.__checkTopLevelItems() 719 self.__checkTopLevelItems()
669 self.__resort() 720 self.__resort()
670 self.__resizeColumns() 721 self.__resizeColumns()
671 722
672 ci = self.currentItem() 723 ci = self.currentItem()
673 if ci: 724 if ci:
674 ind = self.indexFromItem(ci, self.currentColumn()) 725 ind = self.indexFromItem(ci, self.currentColumn())
675 self.scrollTo(ind, QAbstractItemView.ScrollHint.PositionAtCenter) 726 self.scrollTo(ind, QAbstractItemView.ScrollHint.PositionAtCenter)
676 727
677 def __goToTask(self): 728 def __goToTask(self):
678 """ 729 """
679 Private slot to handle the "Go To" context menu entry. 730 Private slot to handle the "Go To" context menu entry.
680 """ 731 """
681 task = self.currentItem() 732 task = self.currentItem()
689 task.colorizeTask() 740 task.colorizeTask()
690 741
691 def __activateFilter(self, on): 742 def __activateFilter(self, on):
692 """ 743 """
693 Private slot to handle the "Filtered display" context menu entry. 744 Private slot to handle the "Filtered display" context menu entry.
694 745
695 @param on flag indicating the filter state (boolean) 746 @param on flag indicating the filter state (boolean)
696 """ 747 """
697 if on and not self.taskFilter.hasActiveFilter(): 748 if on and not self.taskFilter.hasActiveFilter():
698 res = EricMessageBox.yesNo( 749 res = EricMessageBox.yesNo(
699 self, 750 self,
700 self.tr("Activate task filter"), 751 self.tr("Activate task filter"),
701 self.tr( 752 self.tr(
702 """The task filter doesn't have any active filters.""" 753 """The task filter doesn't have any active filters."""
703 """ Do you want to configure the filter settings?"""), 754 """ Do you want to configure the filter settings?"""
704 yesDefault=True) 755 ),
756 yesDefault=True,
757 )
705 if not res: 758 if not res:
706 on = False 759 on = False
707 else: 760 else:
708 self.__configureFilter() 761 self.__configureFilter()
709 on = self.taskFilter.hasActiveFilter() 762 on = self.taskFilter.hasActiveFilter()
710 763
711 self.taskFilter.setActive(on) 764 self.taskFilter.setActive(on)
712 self.__menuFilteredAct.setChecked(on) 765 self.__menuFilteredAct.setChecked(on)
713 self.__backMenuFilteredAct.setChecked(on) 766 self.__backMenuFilteredAct.setChecked(on)
714 self.__refreshDisplay() 767 self.__refreshDisplay()
715 768
716 def __configureFilter(self): 769 def __configureFilter(self):
717 """ 770 """
718 Private slot to handle the "Configure filter" context menu entry. 771 Private slot to handle the "Configure filter" context menu entry.
719 """ 772 """
720 from .TaskFilterConfigDialog import TaskFilterConfigDialog 773 from .TaskFilterConfigDialog import TaskFilterConfigDialog
774
721 dlg = TaskFilterConfigDialog(self.taskFilter) 775 dlg = TaskFilterConfigDialog(self.taskFilter)
722 if dlg.exec() == QDialog.DialogCode.Accepted: 776 if dlg.exec() == QDialog.DialogCode.Accepted:
723 dlg.configureTaskFilter(self.taskFilter) 777 dlg.configureTaskFilter(self.taskFilter)
724 self.__refreshDisplay() 778 self.__refreshDisplay()
725 779
728 Private slot to configure scan options for project tasks. 782 Private slot to configure scan options for project tasks.
729 """ 783 """
730 scanFilter, ok = QInputDialog.getText( 784 scanFilter, ok = QInputDialog.getText(
731 self, 785 self,
732 self.tr("Scan Filter Patterns"), 786 self.tr("Scan Filter Patterns"),
733 self.tr("Enter filename patterns of files" 787 self.tr(
734 " to be excluded separated by a comma:"), 788 "Enter filename patterns of files"
789 " to be excluded separated by a comma:"
790 ),
735 QLineEdit.EchoMode.Normal, 791 QLineEdit.EchoMode.Normal,
736 self.__projectTasksScanFilter) 792 self.__projectTasksScanFilter,
793 )
737 if ok: 794 if ok:
738 self.__projectTasksScanFilter = scanFilter 795 self.__projectTasksScanFilter = scanFilter
739 796
740 def regenerateProjectTasks(self, quiet=False): 797 def regenerateProjectTasks(self, quiet=False):
741 """ 798 """
742 Public slot to regenerate project related tasks. 799 Public slot to regenerate project related tasks.
743 800
744 @param quiet flag indicating quiet operation 801 @param quiet flag indicating quiet operation
745 @type bool 802 @type bool
746 """ 803 """
747 markers = { 804 markers = {
748 taskType: Preferences.getTasks(markersName).split() 805 taskType: Preferences.getTasks(markersName).split()
749 for taskType, markersName in Task.TaskType2MarkersName.items() 806 for taskType, markersName in Task.TaskType2MarkersName.items()
750 } 807 }
751 files = self.project.pdata["SOURCES"] 808 files = self.project.pdata["SOURCES"]
752 809
753 # apply file filter 810 # apply file filter
754 filterList = [f.strip() 811 filterList = [
755 for f in self.__projectTasksScanFilter.split(",") 812 f.strip() for f in self.__projectTasksScanFilter.split(",") if f.strip()
756 if f.strip()] 813 ]
757 if filterList: 814 if filterList:
758 for scanFilter in filterList: 815 for scanFilter in filterList:
759 files = [f for f in files 816 files = [f for f in files if not fnmatch.fnmatch(f, scanFilter)]
760 if not fnmatch.fnmatch(f, scanFilter)] 817
761
762 # remove all project tasks 818 # remove all project tasks
763 self.clearProjectTasks(fileOnly=True) 819 self.clearProjectTasks(fileOnly=True)
764 820
765 # now process them 821 # now process them
766 if quiet: 822 if quiet:
767 ppath = self.project.getProjectPath() 823 ppath = self.project.getProjectPath()
768 self.__projectTaskExtractionThread.scan( 824 self.__projectTaskExtractionThread.scan(
769 markers, [os.path.join(ppath, f) for f in files]) 825 markers, [os.path.join(ppath, f) for f in files]
826 )
770 else: 827 else:
771 progress = EricProgressDialog( 828 progress = EricProgressDialog(
772 self.tr("Extracting project tasks..."), 829 self.tr("Extracting project tasks..."),
773 self.tr("Abort"), 0, len(files), self.tr("%v/%m Files"), self) 830 self.tr("Abort"),
831 0,
832 len(files),
833 self.tr("%v/%m Files"),
834 self,
835 )
774 progress.setMinimumDuration(0) 836 progress.setMinimumDuration(0)
775 progress.setWindowTitle(self.tr("Tasks")) 837 progress.setWindowTitle(self.tr("Tasks"))
776 838
777 ppath = self.project.getProjectPath() 839 ppath = self.project.getProjectPath()
778 840
779 now = time.monotonic() 841 now = time.monotonic()
780 for count, file in enumerate(files): 842 for count, file in enumerate(files):
781 progress.setLabelText( 843 progress.setLabelText(
782 self.tr("Extracting project tasks...\n{0}").format(file)) 844 self.tr("Extracting project tasks...\n{0}").format(file)
845 )
783 progress.setValue(count) 846 progress.setValue(count)
784 if time.monotonic() - now > 0.01: 847 if time.monotonic() - now > 0.01:
785 QApplication.processEvents() 848 QApplication.processEvents()
786 now = time.monotonic() 849 now = time.monotonic()
787 if progress.wasCanceled(): 850 if progress.wasCanceled():
788 break 851 break
789 852
790 fn = os.path.join(ppath, file) 853 fn = os.path.join(ppath, file)
791 # read the file and split it into textlines 854 # read the file and split it into textlines
792 try: 855 try:
793 text, encoding = Utilities.readEncodedFile(fn) 856 text, encoding = Utilities.readEncodedFile(fn)
794 lines = text.splitlines() 857 lines = text.splitlines()
795 except (UnicodeError, OSError): 858 except (UnicodeError, OSError):
796 count += 1 859 count += 1
797 progress.setValue(count) 860 progress.setValue(count)
798 continue 861 continue
799 862
800 # now search tasks and record them 863 # now search tasks and record them
801 for lineIndex, line in enumerate(lines, start=1): 864 for lineIndex, line in enumerate(lines, start=1):
802 shouldBreak = False 865 shouldBreak = False
803 866
804 if line.endswith("__NO-TASK__"): 867 if line.endswith("__NO-TASK__"):
805 # ignore potential task marker 868 # ignore potential task marker
806 continue 869 continue
807 870
808 for taskType, taskMarkers in markers.items(): 871 for taskType, taskMarkers in markers.items():
809 for taskMarker in taskMarkers: 872 for taskMarker in taskMarkers:
810 index = line.find(taskMarker) 873 index = line.find(taskMarker)
811 if index > -1: 874 if index > -1:
812 task = line[index:] 875 task = line[index:]
813 self.addFileTask(task, fn, lineIndex, taskType) 876 self.addFileTask(task, fn, lineIndex, taskType)
814 shouldBreak = True 877 shouldBreak = True
815 break 878 break
816 if shouldBreak: 879 if shouldBreak:
817 break 880 break
818 881
819 progress.setValue(len(files)) 882 progress.setValue(len(files))
820 883
821 def __configure(self): 884 def __configure(self):
822 """ 885 """
823 Private method to open the configuration dialog. 886 Private method to open the configuration dialog.
824 """ 887 """
825 ericApp().getObject("UserInterface").showPreferences("tasksPage") 888 ericApp().getObject("UserInterface").showPreferences("tasksPage")
826 889
827 def saveProjectTasks(self): 890 def saveProjectTasks(self):
828 """ 891 """
829 Public method to write the project tasks. 892 Public method to write the project tasks.
830 """ 893 """
831 if self.projectOpen and Preferences.getProject("TasksProjectAutoSave"): 894 if self.projectOpen and Preferences.getProject("TasksProjectAutoSave"):
832 self.project.writeTasks() 895 self.project.writeTasks()
833 896
834 def stopProjectTaskExtraction(self): 897 def stopProjectTaskExtraction(self):
835 """ 898 """
836 Public method to stop the project task extraction thread. 899 Public method to stop the project task extraction thread.
837 """ 900 """
838 self.__projectTaskExtractionThread.requestInterrupt() 901 self.__projectTaskExtractionThread.requestInterrupt()
839 self.__projectTaskExtractionThread.wait() 902 self.__projectTaskExtractionThread.wait()
840 903
841 def getTasksScanFilter(self) -> str: 904 def getTasksScanFilter(self) -> str:
842 """ 905 """
843 Public method to get the project scan filter. 906 Public method to get the project scan filter.
844 907
845 @return project scan filter 908 @return project scan filter
846 @rtype str 909 @rtype str
847 """ 910 """
848 return self.__projectTasksScanFilter.strip() 911 return self.__projectTasksScanFilter.strip()
849 912
850 def setTasksScanFilter(self, filterStr: str): 913 def setTasksScanFilter(self, filterStr: str):
851 """ 914 """
852 Public method to set the project scan filter. 915 Public method to set the project scan filter.
853 916
854 @param filterStr project scan filter 917 @param filterStr project scan filter
855 @type str 918 @type str
856 """ 919 """
857 self.__projectTasksScanFilter = filterStr 920 self.__projectTasksScanFilter = filterStr
858 921
859 922
860 class ProjectTaskExtractionThread(QThread): 923 class ProjectTaskExtractionThread(QThread):
861 """ 924 """
862 Class implementing a thread to extract tasks related to a project. 925 Class implementing a thread to extract tasks related to a project.
863 926
864 @signal taskFound(str, str, int, TaskType) emitted with the task 927 @signal taskFound(str, str, int, TaskType) emitted with the task
865 description, the file name, the line number and task type to signal 928 description, the file name, the line number and task type to signal
866 the presence of a task 929 the presence of a task
867 """ 930 """
931
868 taskFound = pyqtSignal(str, str, int, TaskType) 932 taskFound = pyqtSignal(str, str, int, TaskType)
869 933
870 def __init__(self, parent=None): 934 def __init__(self, parent=None):
871 """ 935 """
872 Constructor 936 Constructor
873 937
874 @param parent reference to the parent object (QObject) 938 @param parent reference to the parent object (QObject)
875 """ 939 """
876 super().__init__() 940 super().__init__()
877 941
878 self.__lock = threading.Lock() 942 self.__lock = threading.Lock()
879 self.__interrupt = False 943 self.__interrupt = False
880 944
881 def requestInterrupt(self): 945 def requestInterrupt(self):
882 """ 946 """
883 Public method to request iterruption of the thread. 947 Public method to request iterruption of the thread.
884 """ 948 """
885 if self.isRunning(): 949 if self.isRunning():
886 self.__interrupt = True 950 self.__interrupt = True
887 951
888 def scan(self, markers, files): 952 def scan(self, markers, files):
889 """ 953 """
890 Public method to scan the given list of files for tasks. 954 Public method to scan the given list of files for tasks.
891 955
892 @param markers dictionary of defined task markers 956 @param markers dictionary of defined task markers
893 @type dict of lists of str 957 @type dict of lists of str
894 @param files list of file names to be scanned 958 @param files list of file names to be scanned
895 @type list of str 959 @type list of str
896 """ 960 """
898 self.__interrupt = False 962 self.__interrupt = False
899 self.__files = files[:] 963 self.__files = files[:]
900 self.__markers = {} 964 self.__markers = {}
901 for markerType in markers: 965 for markerType in markers:
902 self.__markers[markerType] = markers[markerType][:] 966 self.__markers[markerType] = markers[markerType][:]
903 967
904 if not self.isRunning(): 968 if not self.isRunning():
905 self.start(QThread.Priority.LowPriority) 969 self.start(QThread.Priority.LowPriority)
906 970
907 def run(self): 971 def run(self):
908 """ 972 """
909 Public thread method to scan the given files. 973 Public thread method to scan the given files.
910 """ 974 """
911 with self.__lock: 975 with self.__lock:
912 files = self.__files[:] 976 files = self.__files[:]
913 markers = {} 977 markers = {}
914 for markerType in self.__markers: 978 for markerType in self.__markers:
915 markers[markerType] = self.__markers[markerType][:] 979 markers[markerType] = self.__markers[markerType][:]
916 980
917 for fn in files: 981 for fn in files:
918 if self.__interrupt: 982 if self.__interrupt:
919 break 983 break
920 984
921 # read the file and split it into textlines 985 # read the file and split it into textlines
922 try: 986 try:
923 text, encoding = Utilities.readEncodedFile(fn) 987 text, encoding = Utilities.readEncodedFile(fn)
924 lines = text.splitlines() 988 lines = text.splitlines()
925 except (UnicodeError, OSError): 989 except (UnicodeError, OSError):
926 continue 990 continue
927 991
928 # now search tasks and record them 992 # now search tasks and record them
929 for lineIndex, line in enumerate(lines, start=1): 993 for lineIndex, line in enumerate(lines, start=1):
930 if self.__interrupt: 994 if self.__interrupt:
931 break 995 break
932 996
933 found = False 997 found = False
934 998
935 if line.endswith("__NO-TASK__"): 999 if line.endswith("__NO-TASK__"):
936 # ignore potential task marker 1000 # ignore potential task marker
937 continue 1001 continue
938 1002
939 for taskType, taskMarkers in markers.items(): 1003 for taskType, taskMarkers in markers.items():
940 for taskMarker in taskMarkers: 1004 for taskMarker in taskMarkers:
941 index = line.find(taskMarker) 1005 index = line.find(taskMarker)
942 if index > -1: 1006 if index > -1:
943 task = line[index:] 1007 task = line[index:]
944 with self.__lock: 1008 with self.__lock:
945 self.taskFound.emit(task, fn, lineIndex, 1009 self.taskFound.emit(task, fn, lineIndex, taskType)
946 taskType)
947 found = True 1010 found = True
948 break 1011 break
949 if found: 1012 if found:
950 break 1013 break

eric ide

mercurial