1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
|
from PyQt5 import QtCore, QtWidgets, QtGui
from PyQt5.QtCore import Qt
from model.task import Task
from model.difficulty import Difficulty
from model.priority import Priority
from model import difficulty, priority
import time
import math
import util.array
import util.range
import gui.tasks.duration
import gui.color
columns = 6
headers = ['Age', 'Name', 'Duration', 'Difficulty', 'Priority', 'Tag']
default_sort = (0, Qt.AscendingOrder)
class TableModel(QtCore.QAbstractTableModel):
def __init__(self, tasks):
super(TableModel, self).__init__()
self._tasks = tasks
def headerData(self, section, orientation, role):
if role == Qt.DisplayRole and orientation == Qt.Horizontal:
return headers[section]
elif role == Qt.DisplayRole and orientation == Qt.Vertical:
return section + 1
else:
return QtCore.QVariant()
def data(self, index, role):
task = self._tasks[index.row()]
if role == Qt.DisplayRole:
if index.column() == 0:
return age_since(task.created_at)
elif index.column() == 1:
return task.name
elif index.column() == 2:
return gui.tasks.duration.format(task.duration)
elif index.column() == 3:
return difficulty.format(task.difficulty)
elif index.column() == 4:
return priority.format(task.priority)
elif index.column() == 5:
return task.tag
elif role == Qt.ForegroundRole:
if index.column() == 2:
return QtGui.QBrush(gui.tasks.duration.color(task.duration))
elif index.column() == 3:
return QtGui.QBrush(difficulty_color(task.difficulty))
elif index.column() == 4:
return QtGui.QBrush(priority_color(task.priority))
else:
return QtCore.QVariant()
def rowCount(self, index):
return len(self._tasks)
def columnCount(self, index):
return columns
def get_at(self, row):
if row >= 0 and row < len(self._tasks):
return self._tasks[row]
def insert_task(self, header: QtWidgets.QHeaderView, task: Task) -> int:
at = self.insert_position(header, task)
self.beginInsertRows(QtCore.QModelIndex(), at, at)
self._tasks.insert(at, task)
self.endInsertRows()
return at
def insert_position(self, header: QtWidgets.QHeaderView, task: Task) -> int:
row = header.sortIndicatorSection()
order = header.sortIndicatorOrder()
is_rev = is_reversed(row, order)
return util.array.insert_position(
sort_key(task, row, is_rev),
[sort_key(t, row, is_rev) for t in self._tasks],
is_rev)
def update_task(self, header: QtWidgets.QHeaderView, row, task: Task) -> int:
self.delete_task_range(row, 1)
return self.insert_task(header, task)
def delete_tasks(self, indexes):
for range in reversed(util.range.from_indexes(indexes)):
self.delete_task_range(range.start, range.length)
return True
def delete_task_range(self, row, rows):
self.beginRemoveRows(QtCore.QModelIndex(), row, row + rows - 1)
self._tasks = self._tasks[:row] + self._tasks[row + rows:]
self.endRemoveRows()
return True
def row_ids(self, rows):
return [task.id for i, task in enumerate(self._tasks) if i in rows]
def sort(self, row: int, order: Qt.SortOrder):
self.layoutAboutToBeChanged.emit()
is_rev = is_reversed(row, order)
self._tasks = sorted(
self._tasks,
key = lambda task: sort_key(task, row, is_rev),
reverse = is_rev)
self.layoutChanged.emit()
def age_since(timestamp):
diff = int(time.time()) - timestamp
if diff >= 60 * 60 * 24:
return '' + str(math.floor(diff / 60 / 60 / 24)) + 'd'
elif diff >= 60 * 60:
return '' + str(math.floor(diff / 60 / 60)) + 'h'
elif diff >= 60:
return '' + str(math.floor(diff / 60)) + 'm'
else:
return '1m'
def sort_key(task: Task, row: int, is_reversed: bool):
if row == 0:
return task.created_at
elif row == 1:
return task.name.lower()
elif row == 2:
if is_reversed:
return task.duration
else:
return (task.duration == 0, task.duration)
elif row == 3:
return task.difficulty
elif row == 4:
return task.priority
elif row == 5:
if is_reversed:
return task.tag.lower()
else:
return (task.tag == '', task.tag.lower())
def is_reversed(row: int, order: Qt.SortOrder) -> bool:
if row == 0:
return order == Qt.AscendingOrder
else:
return order == Qt.DescendingOrder
def difficulty_color(d: Difficulty) -> QtGui.QColor:
if d == Difficulty.EASY:
return gui.color.easy_difficulty
elif d == Difficulty.NORMAL:
return gui.color.normal_difficulty
elif d == Difficulty.HARD:
return gui.color.hard_difficulty
def priority_color(p: Priority) -> QtGui.QColor:
if p == Priority.LOW:
return gui.color.low_priority
elif p == Priority.MIDDLE:
return gui.color.middle_priority
elif p == Priority.HIGH:
return gui.color.high_priority
|