aboutsummaryrefslogtreecommitdiff
path: root/src/gui/tasks/table
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/tasks/table')
-rw-r--r--src/gui/tasks/table/main.py46
-rw-r--r--src/gui/tasks/table/menu.py43
-rw-r--r--src/gui/tasks/table/model.py116
3 files changed, 205 insertions, 0 deletions
diff --git a/src/gui/tasks/table/main.py b/src/gui/tasks/table/main.py
new file mode 100644
index 0000000..a990c0e
--- /dev/null
+++ b/src/gui/tasks/table/main.py
@@ -0,0 +1,46 @@
+from PyQt5 import QtWidgets
+from PyQt5.QtCore import Qt
+
+import db.tasks
+import gui.tasks.signal
+import gui.tasks.table.menu
+import gui.tasks.table.model
+
+def widget(database, parent, add_task_signal):
+ table = QtWidgets.QTableView(parent)
+
+ tasks = db.tasks.get(database.cursor())
+ table_model = gui.tasks.table.model.TableModel(tasks)
+
+ table.setModel(table_model)
+ table.sortByColumn(
+ gui.tasks.table.model.default_sort[0],
+ gui.tasks.table.model.default_sort[1])
+ table.setSortingEnabled(True)
+ table.setSelectionBehavior(QtWidgets.QTableView.SelectRows)
+ table.horizontalHeader().setStretchLastSection(True)
+ resizeColumns(table)
+
+ update_task_signal = gui.tasks.signal.UpdateTask()
+
+ # Menu
+ table.setContextMenuPolicy(Qt.CustomContextMenu)
+ table.customContextMenuRequested.connect(lambda position: gui.tasks.table.menu.open(database, table, update_task_signal, position))
+
+ add_task_signal.get().connect(lambda task: insert(table, task))
+ update_task_signal.get().connect(lambda row, task: update(table, row, task))
+
+ return table
+
+def insert(table, task):
+ table.model().insert_task(table.horizontalHeader(), task)
+ resizeColumns(table)
+
+def update(table, row, task):
+ row = table.model().update_task(table.horizontalHeader(), row, task)
+ table.selectRow(row)
+ resizeColumns(table)
+
+def resizeColumns(table):
+ for column in range(gui.tasks.table.model.columns):
+ table.resizeColumnToContents(column)
diff --git a/src/gui/tasks/table/menu.py b/src/gui/tasks/table/menu.py
new file mode 100644
index 0000000..4366c25
--- /dev/null
+++ b/src/gui/tasks/table/menu.py
@@ -0,0 +1,43 @@
+from PyQt5 import QtWidgets
+
+import db.tasks
+import gui.tasks.modal
+from model.task import Task, TaskForm
+
+def open(database, table, update_task_signal, position):
+ rows = set([index.row() for index in table.selectedIndexes()])
+
+ menu = QtWidgets.QMenu(table)
+
+ if len(rows) == 1:
+ modify_action = menu.addAction(gui.icons.dialog_open(menu.style()), 'modify')
+ else:
+ modify_action = QtWidgets.QAction(menu)
+
+ delete_action = menu.addAction(gui.icons.trash(menu.style()), 'Delete')
+
+ action = menu.exec_(table.mapToGlobal(position))
+ if action == modify_action and len(rows) == 1:
+ row = list(rows)[0]
+ task = table.model().get_at(row)
+ show_update_dialog(database, table, update_task_signal, row, task)
+ elif action == delete_action:
+ confirm = QtWidgets.QMessageBox.question(table, 'Task deletion', 'Do you really want to delete the selected tasks ?', QtWidgets.QMessageBox.No | QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.Yes)
+ if confirm == QtWidgets.QMessageBox.Yes:
+ db.tasks.delete(database.cursor(), table.model().row_ids(rows))
+ database.commit()
+ table.model().delete_tasks(rows)
+
+def show_update_dialog(database, parent_widget, update_task_signal, row, task):
+ dialog = gui.tasks.modal.dialog(
+ parent_widget,
+ 'Modify a task',
+ 'modify',
+ task,
+ lambda taskForm: on_update(database, update_task_signal, row, task, taskForm))
+ dialog.exec_()
+
+def on_update(database, update_task_signal, row, task: Task, taskForm: TaskForm):
+ task = db.tasks.update(database.cursor(), task, taskForm)
+ update_task_signal.emit(row, task)
+ database.commit()
diff --git a/src/gui/tasks/table/model.py b/src/gui/tasks/table/model.py
new file mode 100644
index 0000000..90bcc4c
--- /dev/null
+++ b/src/gui/tasks/table/model.py
@@ -0,0 +1,116 @@
+from PyQt5 import QtCore, QtWidgets
+from PyQt5.QtCore import Qt
+
+from model.task import Task
+import time
+import math
+import util.array
+import util.range
+
+columns = 3
+
+headers = ['Age', 'Name', '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):
+ if role == Qt.DisplayRole:
+ task = self._tasks[index.row()]
+ if index.column() == 0:
+ return age_since(task.created_at)
+ if index.column() == 1:
+ return task.name
+ if index.column() == 2:
+ return task.tag
+ 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()
+ return util.array.insert_position(
+ sort_key(task, row),
+ [sort_key(t, row) for t in self._tasks],
+ is_reversed(row, order))
+
+ 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()
+ self._tasks = sorted(
+ self._tasks,
+ key = lambda task: sort_key(task, row),
+ reverse = is_reversed(row, order))
+ 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):
+ if row == 0:
+ return task.created_at
+ elif row == 1:
+ return task.name.lower()
+ elif row == 2:
+ return task.tag.lower()
+
+def is_reversed(row: int, order: Qt.SortOrder) -> bool:
+ if row == 0:
+ return order == Qt.AscendingOrder
+ else:
+ return order == Qt.DescendingOrder