Tasks/TaskViewer.py

changeset 1819
cfcfd617216a
parent 1653
fed1920ff53b
child 1965
96f5a76e1845
equal deleted inserted replaced
1818:a5e4cb4bed43 1819:cfcfd617216a
10 generated tasks are derived from a comment with a special 10 generated tasks are derived from a comment with a special
11 introductory text. This text is configurable. 11 introductory text. This text is configurable.
12 """ 12 """
13 13
14 import os 14 import os
15 import time
16 import fnmatch 15 import fnmatch
17 16
18 from PyQt4.QtCore import pyqtSignal, QRegExp, Qt 17 from PyQt4.QtCore import pyqtSignal, Qt
19 from PyQt4.QtGui import QHeaderView, QLineEdit, QTreeWidget, QDialog, QInputDialog, \ 18 from PyQt4.QtGui import QHeaderView, QLineEdit, QTreeWidget, QDialog, QInputDialog, \
20 QApplication, QMenu, QAbstractItemView, QProgressDialog, QTreeWidgetItem 19 QApplication, QMenu, QAbstractItemView, QProgressDialog, QTreeWidgetItem
21 20
22 from E5Gui.E5Application import e5App 21 from E5Gui.E5Application import e5App
23 from E5Gui import E5MessageBox 22 from E5Gui import E5MessageBox
24 23
24 from .Task import Task
25 from .TaskPropertiesDialog import TaskPropertiesDialog 25 from .TaskPropertiesDialog import TaskPropertiesDialog
26 from .TaskFilter import TaskFilter
26 from .TaskFilterConfigDialog import TaskFilterConfigDialog 27 from .TaskFilterConfigDialog import TaskFilterConfigDialog
27 28
28 import UI.PixmapCache 29 import UI.PixmapCache
29 30
30 import Preferences 31 import Preferences
31 import Utilities 32 import Utilities
32 33
33 from Utilities.AutoSaver import AutoSaver 34 from Utilities.AutoSaver import AutoSaver
34
35
36 class Task(QTreeWidgetItem):
37 """
38 Class implementing the task data structure.
39 """
40 def __init__(self, description, priority=1, filename="", lineno=0,
41 completed=False, _time=0, isProjectTask=False,
42 isBugfixTask=False, project=None, longtext=""):
43 """
44 Constructor
45
46 @param parent parent widget of the task (QWidget)
47 @param description descriptive text of the task (string)
48 @param priority priority of the task (0=high, 1=normal, 2=low)
49 @param filename filename containing the task (string)
50 @param lineno line number containing the task (integer)
51 @param completed flag indicating completion status (boolean)
52 @param _time creation time of the task (float, if 0 use current time)
53 @param isProjectTask flag indicating a task related to the current project
54 (boolean)
55 @param isBugfixTask flag indicating a bugfix task (boolean)
56 @param project reference to the project object (Project)
57 @param longtext explanatory text of the task (string)
58 """
59 super().__init__()
60
61 self.description = description
62 self.longtext = longtext
63 if priority in [0, 1, 2]:
64 self.priority = priority
65 else:
66 self.priority = 1
67 self.filename = filename
68 self.lineno = lineno
69 self.completed = completed
70 self.created = _time and _time or time.time()
71 self._isProjectTask = isProjectTask
72 self.isBugfixTask = isBugfixTask
73 self.project = project
74
75 if isProjectTask:
76 self.filename = self.project.getRelativePath(self.filename)
77
78 self.setData(0, Qt.DisplayRole, "")
79 self.setData(1, Qt.DisplayRole, "")
80 self.setData(2, Qt.DisplayRole, self.description)
81 self.setData(3, Qt.DisplayRole, self.filename)
82 self.setData(4, Qt.DisplayRole, self.lineno or "")
83
84 if self.completed:
85 self.setIcon(0, UI.PixmapCache.getIcon("taskCompleted.png"))
86 strikeOut = True
87 else:
88 self.setIcon(0, UI.PixmapCache.getIcon("empty.png"))
89 strikeOut = False
90 for column in range(2, 5):
91 f = self.font(column)
92 f.setStrikeOut(strikeOut)
93 self.setFont(column, f)
94
95 if self.priority == 1:
96 self.setIcon(1, UI.PixmapCache.getIcon("empty.png"))
97 elif self.priority == 0:
98 self.setIcon(1, UI.PixmapCache.getIcon("taskPrioHigh.png"))
99 elif self.priority == 2:
100 self.setIcon(1, UI.PixmapCache.getIcon("taskPrioLow.png"))
101 else:
102 self.setIcon(1, UI.PixmapCache.getIcon("empty.png"))
103
104 self.colorizeTask()
105 self.setTextAlignment(4, Qt.AlignRight)
106
107 def colorizeTask(self):
108 """
109 Public slot to set the colors of the task item.
110 """
111 for col in range(5):
112 if self.isBugfixTask:
113 self.setTextColor(col, Preferences.getTasks("TasksBugfixColour"))
114 else:
115 self.setTextColor(col, Preferences.getTasks("TasksColour"))
116 if self._isProjectTask:
117 self.setBackgroundColor(col, Preferences.getTasks("TasksProjectBgColour"))
118 else:
119 self.setBackgroundColor(col, Preferences.getTasks("TasksBgColour"))
120
121 def setDescription(self, description):
122 """
123 Public slot to update the description.
124
125 @param longtext explanatory text of the task (string)
126 """
127 self.description = description
128 self.setText(2, self.description)
129
130 def setLongText(self, longtext):
131 """
132 Public slot to update the longtext field.
133
134 @param longtext descriptive text of the task (string)
135 """
136 self.longtext = longtext
137
138 def setPriority(self, priority):
139 """
140 Public slot to update the priority.
141
142 @param priority priority of the task (0=high, 1=normal, 2=low)
143 """
144 if priority in [0, 1, 2]:
145 self.priority = priority
146 else:
147 self.priority = 1
148
149 if self.priority == 1:
150 self.setIcon(1, UI.PixmapCache.getIcon("empty.png"))
151 elif self.priority == 0:
152 self.setIcon(1, UI.PixmapCache.getIcon("taskPrioHigh.png"))
153 elif self.priority == 2:
154 self.setIcon(1, UI.PixmapCache.getIcon("taskPrioLow.png"))
155 else:
156 self.setIcon(1, UI.PixmapCache.getIcon("empty.png"))
157
158 def setCompleted(self, completed):
159 """
160 Public slot to update the completed flag.
161
162 @param completed flag indicating completion status (boolean)
163 """
164 self.completed = completed
165 if self.completed:
166 self.setIcon(0, UI.PixmapCache.getIcon("taskCompleted.png"))
167 strikeOut = True
168 else:
169 self.setIcon(0, UI.PixmapCache.getIcon("empty.png"))
170 strikeOut = False
171 for column in range(2, 5):
172 f = self.font(column)
173 f.setStrikeOut(strikeOut)
174 self.setFont(column, f)
175
176 def isCompleted(self):
177 """
178 Public slot to return the completion status.
179
180 @return flag indicating the completion status (boolean)
181 """
182 return self.completed
183
184 def getFilename(self):
185 """
186 Public method to retrieve the tasks filename.
187
188 @return filename (string)
189 """
190 if self._isProjectTask and self.filename:
191 return os.path.join(self.project.getProjectPath(), self.filename)
192 else:
193 return self.filename
194
195 def getLineno(self):
196 """
197 Public method to retrieve the tasks linenumber.
198
199 @return linenumber (integer)
200 """
201 return self.lineno
202
203 def setProjectTask(self, pt):
204 """
205 Public method to set the project relation flag.
206
207 @param pt flag indicating a project task (boolean)
208 """
209 self._isProjectTask = pt
210 self.colorizeTask()
211
212 def isProjectTask(self):
213 """
214 Public slot to return the project relation status.
215
216 @return flag indicating the project relation status (boolean)
217 """
218 return self._isProjectTask
219
220
221 class TaskFilter(object):
222 """
223 Class implementing a filter for tasks.
224 """
225 def __init__(self):
226 """
227 Constructor
228 """
229 self.active = False
230
231 self.descriptionFilter = None
232 self.filenameFilter = None
233 self.typeFilter = None # standard (False) or bugfix (True)
234 self.scopeFilter = None # global (False) or project (True)
235 self.statusFilter = None # uncompleted (False) or completed (True)
236 self.prioritiesFilter = None # list of priorities [0 (high), 1 (normal), 2 (low)]
237
238 def setActive(self, enabled):
239 """
240 Public method to activate the filter.
241
242 @param enabled flag indicating the activation state (boolean)
243 """
244 self.active = enabled
245
246 def setDescriptionFilter(self, filter):
247 """
248 Public method to set the description filter.
249
250 @param filter a regular expression for the description filter
251 to set (string) or None
252 """
253 if not filter:
254 self.descriptionFilter = None
255 else:
256 self.descriptionFilter = QRegExp(filter)
257
258 def setFileNameFilter(self, filter):
259 """
260 Public method to set the filename filter.
261
262 @param filter a wildcard expression for the filename filter
263 to set (string) or None
264 """
265 if not filter:
266 self.filenameFilter = None
267 else:
268 self.filenameFilter = QRegExp(filter)
269 self.filenameFilter.setPatternSyntax(QRegExp.Wildcard)
270
271 def setTypeFilter(self, type_):
272 """
273 Public method to set the type filter.
274
275 @param type_ flag indicating a bugfix task (boolean) or None
276 """
277 self.typeFilter = type_
278
279 def setScopeFilter(self, scope):
280 """
281 Public method to set the scope filter.
282
283 @param scope flag indicating a project task (boolean) or None
284 """
285 self.scopeFilter = scope
286
287 def setStatusFilter(self, status):
288 """
289 Public method to set the status filter.
290
291 @param status flag indicating a completed task (boolean) or None
292 """
293 self.statusFilter = status
294
295 def setPrioritiesFilter(self, priorities):
296 """
297 Public method to set the priorities filter.
298
299 @param priorities list of task priorities (list of integer) or None
300 """
301 self.prioritiesFilter = priorities
302
303 def hasActiveFilter(self):
304 """
305 Public method to check for active filters.
306
307 @return flag indicating an active filter was found (boolean)
308 """
309 return self.descriptionFilter is not None or \
310 self.filenameFilter is not None or \
311 self.typeFilter is not None or \
312 self.scopeFilter is not None or \
313 self.statusFilter is not None or \
314 self.prioritiesFilter is not None
315
316 def showTask(self, task):
317 """
318 Public method to check, if a task should be shown.
319
320 @param task reference to the task object to check (Task)
321 @return flag indicating whether the task should be shown (boolean)
322 """
323 if not self.active:
324 return True
325
326 if self.descriptionFilter and \
327 self.descriptionFilter.indexIn(task.description) == -1:
328 return False
329
330 if self.filenameFilter and \
331 not self.filenameFilter.exactMatch(task.filename):
332 return False
333
334 if self.typeFilter is not None and \
335 self.typeFilter != task.isBugfixTask:
336 return False
337
338 if self.scopeFilter is not None and \
339 self.scopeFilter != task._isProjectTask:
340 return False
341
342 if self.statusFilter is not None and \
343 self.statusFilter != task.completed:
344 return False
345
346 if self.prioritiesFilter is not None and \
347 not task.priority in self.prioritiesFilter:
348 return False
349
350 return True
351 35
352 36
353 class TaskViewer(QTreeWidget): 37 class TaskViewer(QTreeWidget):
354 """ 38 """
355 Class implementing the task viewer. 39 Class implementing the task viewer.
544 """ 228 """
545 self.projectOpen = o 229 self.projectOpen = o
546 230
547 def addTask(self, description, priority=1, filename="", lineno=0, 231 def addTask(self, description, priority=1, filename="", lineno=0,
548 completed=False, _time=0, isProjectTask=False, 232 completed=False, _time=0, isProjectTask=False,
549 isBugfixTask=False, longtext=""): 233 taskType=Task.TypeTodo, longtext=""):
550 """ 234 """
551 Public slot to add a task. 235 Public slot to add a task.
552 236
553 @param description descriptive text of the task (string) 237 @param description descriptive text of the task (string)
554 @param priority priority of the task (0=high, 1=normal, 2=low) 238 @param priority priority of the task (0=high, 1=normal, 2=low)
556 @param lineno line number containing the task (integer) 240 @param lineno line number containing the task (integer)
557 @param completed flag indicating completion status (boolean) 241 @param completed flag indicating completion status (boolean)
558 @param _time creation time of the task (float, if 0 use current time) 242 @param _time creation time of the task (float, if 0 use current time)
559 @param isProjectTask flag indicating a task related to the current 243 @param isProjectTask flag indicating a task related to the current
560 project (boolean) 244 project (boolean)
561 @param isBugfixTask flag indicating a bugfix task (boolean) 245 @param taskType type of the task (one of Task.TypeFixme, Task.TypeTodo,
246 Task.TypeWarning, Task.TypeNote)
562 @param longtext explanatory text of the task (string) 247 @param longtext explanatory text of the task (string)
563 """ 248 """
564 task = Task(description, priority, filename, lineno, completed, 249 task = Task(description, priority, filename, lineno, completed,
565 _time, isProjectTask, isBugfixTask, 250 _time, isProjectTask, taskType,
566 self.project, longtext) 251 self.project, longtext)
567 self.tasks.append(task) 252 self.tasks.append(task)
568 if self.taskFilter.showTask(task): 253 if self.taskFilter.showTask(task):
569 self.addTopLevelItem(task) 254 self.addTopLevelItem(task)
570 self.__resort() 255 self.__resort()
571 self.__resizeColumns() 256 self.__resizeColumns()
572 257
573 if isProjectTask: 258 if isProjectTask:
574 self.__projectTasksSaveTimer.changeOccurred() 259 self.__projectTasksSaveTimer.changeOccurred()
575 260
576 def addFileTask(self, description, filename, lineno, isBugfixTask=False, 261 def addFileTask(self, description, filename, lineno, taskType=Task.TypeTodo,
577 longtext=""): 262 longtext=""):
578 """ 263 """
579 Public slot to add a file related task. 264 Public slot to add a file related task.
580 265
581 @param description descriptive text of the task (string) 266 @param description descriptive text of the task (string)
582 @param filename filename containing the task (string) 267 @param filename filename containing the task (string)
583 @param lineno line number containing the task (integer) 268 @param lineno line number containing the task (integer)
584 @param isBugfixTask flag indicating a bugfix task (boolean) 269 @param taskType type of the task (one of Task.TypeFixme, Task.TypeTodo,
270 Task.TypeWarning, Task.TypeNote)
585 @param longtext explanatory text of the task (string) 271 @param longtext explanatory text of the task (string)
586 """ 272 """
587 self.addTask(description, filename=filename, lineno=lineno, 273 self.addTask(description, filename=filename, lineno=lineno,
588 isProjectTask=( 274 isProjectTask=(
589 self.project and self.project.isProjectSource(filename)), 275 self.project and self.project.isProjectSource(filename)),
590 isBugfixTask=isBugfixTask, longtext=longtext) 276 taskType=taskType, longtext=longtext)
591 277
592 def getProjectTasks(self): 278 def getProjectTasks(self):
593 """ 279 """
594 Public method to retrieve all project related tasks. 280 Public method to retrieve all project related tasks.
595 281
804 490
805 def __regenerateProjectTasks(self): 491 def __regenerateProjectTasks(self):
806 """ 492 """
807 Private slot to handle the "Regenerated project tasks" context menu entry. 493 Private slot to handle the "Regenerated project tasks" context menu entry.
808 """ 494 """
809 todoMarkers = Preferences.getTasks("TasksMarkers").split() 495 markers = {
810 bugfixMarkers = Preferences.getTasks("TasksMarkersBugfix").split() 496 Task.TypeWarning: Preferences.getTasks("TasksWarningMarkers").split(),
497 Task.TypeNote: Preferences.getTasks("TasksNoteMarkers").split(),
498 Task.TypeTodo: Preferences.getTasks("TasksTodoMarkers").split(),
499 Task.TypeFixme: Preferences.getTasks("TasksFixmeMarkers").split(),
500 }
811 files = self.project.pdata["SOURCES"] 501 files = self.project.pdata["SOURCES"]
812 502
813 # apply file filter 503 # apply file filter
814 filterList = [f.strip() for f in self.projectTasksScanFilter.split(",") 504 filterList = [f.strip() for f in self.projectTasksScanFilter.split(",")
815 if f.strip()] 505 if f.strip()]
846 536
847 # now search tasks and record them 537 # now search tasks and record them
848 lineIndex = 0 538 lineIndex = 0
849 for line in lines: 539 for line in lines:
850 lineIndex += 1 540 lineIndex += 1
851 shouldContinue = False 541 shouldBreak = False
852 # normal tasks first
853 for tasksMarker in todoMarkers:
854 index = line.find(tasksMarker)
855 if index > -1:
856 task = line[index:]
857 self.addFileTask(task, fn, lineIndex, False)
858 shouldContinue = True
859 break
860 if shouldContinue:
861 continue
862 542
863 # bugfix tasks second 543 for taskType, taskMarkers in markers.items():
864 for tasksMarker in bugfixMarkers: 544 for taskMarker in taskMarkers:
865 index = line.find(tasksMarker) 545 index = line.find(taskMarker)
866 if index > -1: 546 if index > -1:
867 task = line[index:] 547 task = line[index:]
868 self.addFileTask(task, fn, lineIndex, True) 548 self.addFileTask(task, fn, lineIndex, taskType)
869 shouldContinue = True 549 shouldBreak = True
550 break
551 if shouldBreak:
870 break 552 break
871 553
872 count += 1 554 count += 1
873 555
874 progress.setValue(len(files)) 556 progress.setValue(len(files))

eric ide

mercurial