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/menu.py49
-rw-r--r--src/gui/tasks/table/widget.py284
2 files changed, 0 insertions, 333 deletions
diff --git a/src/gui/tasks/table/menu.py b/src/gui/tasks/table/menu.py
deleted file mode 100644
index 5356be2..0000000
--- a/src/gui/tasks/table/menu.py
+++ /dev/null
@@ -1,49 +0,0 @@
-from PyQt5 import QtWidgets, QtCore
-from typing import List
-
-import db.tasks
-import gui.tasks.dialog
-from model.status import Status
-from model.task import Task, ValidTaskForm
-from model.tag import Tag
-
-def open(table: QtWidgets.QTableWidget, status: Status, 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.icon.dialog_open(menu.style()), "modify")
- else:
- modify_action = QtWidgets.QAction(menu)
-
- delete_action = menu.addAction(gui.icon.trash(menu.style()), "delete")
-
- if status != Status.READY:
- move_to_ready = menu.addAction(gui.icon.task_ready(menu.style()), "move to ready")
- else:
- move_to_ready = QtWidgets.QAction(menu)
-
- if status != Status.WAITING:
- move_to_waiting = menu.addAction(gui.icon.task_waiting(menu.style()), "move to waiting")
- else:
- move_to_waiting = QtWidgets.QAction(menu)
-
- if status != Status.MAYBE:
- move_to_maybe = menu.addAction(gui.icon.task_maybe(menu.style()), "move to maybe")
- else:
- move_to_maybe = QtWidgets.QAction(menu)
-
- action = menu.exec_(table.mapToGlobal(position + QtCore.QPoint(15, 20)))
- if action == modify_action and len(rows) == 1:
- row = list(rows)[0]
- (task, tags) = table.get_at(row)
- gui.tasks.dialog.update(table, update_task_signal, row, task, tags).exec_()
- elif action == delete_action:
- gui.tasks.dialog.confirm_delete(table, rows, lambda: table.delete_rows(rows))
- elif action == move_to_ready:
- gui.tasks.dialog.confirm_move(table, rows, Status.READY, lambda: table.update_status(rows, Status.READY))
- elif action == move_to_waiting:
- gui.tasks.dialog.confirm_move(table, rows, Status.WAITING, lambda: table.update_status(rows, Status.WAITING))
- elif action == move_to_maybe:
- gui.tasks.dialog.confirm_move(table, rows, Status.MAYBE, lambda: table.update_status(rows, Status.MAYBE))
diff --git a/src/gui/tasks/table/widget.py b/src/gui/tasks/table/widget.py
deleted file mode 100644
index aacae2f..0000000
--- a/src/gui/tasks/table/widget.py
+++ /dev/null
@@ -1,284 +0,0 @@
-from PyQt5 import QtWidgets, QtCore, QtGui
-from PyQt5.QtCore import Qt
-from typing import List, Tuple
-import time
-import math
-
-from model import difficulty, priority
-from model.difficulty import Difficulty
-from model.priority import Priority
-from model.tag import Tag
-from model.task import Task
-from model.task_tag import TaskTag
-from model.status import Status
-import database
-import db.tags
-import db.task_tags
-import gui.color
-import gui.signal
-import gui.tasks.dialog
-import gui.tasks.duration
-import gui.tasks.signal
-import gui.tasks.signal
-import gui.tasks.table.menu
-import service.tasks
-import util.array
-import util.range
-
-class Widget(QtWidgets.QTableWidget):
- def __init__(
- self,
- parent,
- on_show: gui.signal.Reload,
- add_task_signal: gui.tasks.signal.AddTask,
- status: Status):
- super().__init__(parent)
-
- self.init_state(status)
- self.sort()
-
- self.setSelectionBehavior(QtWidgets.QTableView.SelectRows)
- self.init_header()
- self.setRowCount(len(self._tasks))
- self.setColumnCount(len(self.header_labels()))
- self.setColumnWidth(1, 500)
-
- self.update_view()
- self.horizontalHeader().setStretchLastSection(True)
-
- # Menu
- self.setContextMenuPolicy(Qt.CustomContextMenu)
- self.customContextMenuRequested.connect(lambda position: gui.tasks.table.menu.open(self, status, self._update_task_signal, position))
-
- self.doubleClicked.connect(lambda index: self.on_double_click(index.row()))
- add_task_signal.connect(lambda task, tags: self.insert(task, tags))
- self._update_task_signal.connect(lambda row, task, tags: self.update_task(row, task, tags))
- on_show.connect(lambda: self.on_show())
-
- def init_state(self, status: Status):
- self._update_task_signal = gui.tasks.signal.UpdateTask()
- cursor = database.cursor()
- self._status = status
- self._tasks = service.tasks.get(cursor, self._status)
- self._task_tags = db.task_tags.get(cursor)
- self._tags = db.tags.get(cursor)
- self._sort_column = 0
- self._sort_is_ascending = True
-
- def init_header(self):
- self._header_view = QtWidgets.QHeaderView(Qt.Horizontal, self)
- self._header_model = QtGui.QStandardItemModel()
- self._header_model.setHorizontalHeaderLabels(self.header_labels())
- self._header_view.setModel(self._header_model)
- self._header_view.setSectionsClickable(True)
- self._header_view.sectionClicked.connect(self.on_header_click)
- self.setHorizontalHeader(self._header_view)
-
- def on_show(self):
- cursor = database.cursor()
- self._tasks = service.tasks.get(cursor, self._status)
- self._task_tags = db.task_tags.get(cursor)
- self._tags = db.tags.get(cursor)
- self.setRowCount(len(self._tasks))
- self.sort()
- self.update_view()
-
- def on_header_click(self, column):
- if self._sort_column == column:
- self._sort_is_ascending = not self._sort_is_ascending
- else:
- self._sort_is_ascending = True
- self._sort_column = column
- self.sort()
- self._header_model.setHorizontalHeaderLabels(self.header_labels())
- self.update_view()
-
- def sort(self):
- is_rev = self.is_reversed()
- self._tasks = sorted(
- self._tasks,
- key = lambda task: self.sort_key(task, is_rev),
- reverse = is_rev)
-
- def update_task(self, row, task: Task, tags: List[int]):
- self._tasks[row] = task
- filtred_task_tags = [tt for tt in self._task_tags if tt.task_id in [t.id for t in self._tasks if t.id != task.id]]
- new_task_tags = [TaskTag(task_id=task.id, tag_id=tag_id) for tag_id in tags]
- self._task_tags = filtred_task_tags + new_task_tags
-
- # Update task in table
- self.sort()
- row_after_sort = [i for i in range(len(self._tasks)) if self._tasks[i].id == task.id][0]
- if row_after_sort == row:
- self.update_row(row)
- else:
- self.removeRow(row)
- self.insertRow(row_after_sort)
- self.update_row(row_after_sort)
- self.selectRow(row_after_sort)
-
- def update_view(self):
- for row in range(len(self._tasks)):
- self.update_row(row)
-
- def update_row(self, row: int):
- task = self._tasks[row]
- self.setItem(row, 0, item(age_since(task.created_at)))
- self.setItem(row, 1, item(task.name))
- self.setCellWidget(row, 2, colored_label(self, gui.tasks.duration.format(task.duration), gui.tasks.duration.color(task.duration)))
- self.setCellWidget(row, 3, colored_label(self, difficulty.format(task.difficulty), difficulty_color(task.difficulty)))
- self.setCellWidget(row, 4, colored_label(self, priority.format(task.priority), priority_color(task.priority)))
- tag_ids = [tt.tag_id for tt in self._task_tags if tt.task_id == task.id]
- res_tags = sorted([tag for tag in self._tags if tag.id in tag_ids], key=lambda t: t.name)
- self.setCellWidget(row, 5, render_tags(self, res_tags))
- self.setRowHeight(row, 45)
-
- def insert(self, task: Task, tags: List[int]) -> int:
- is_rev = self.is_reversed()
- row = util.array.insert_position(
- self.sort_key(task, is_rev),
- [self.sort_key(t, is_rev) for t in self._tasks],
- is_rev)
- self._tasks.insert(row, task)
- self._task_tags += [TaskTag(task_id=task.id, tag_id=tag_id) for tag_id in tags]
- self.insertRow(row)
- self.update_row(row)
- self._task_tags += [TaskTag(task_id=task.id, tag_id=tag_id) for tag_id in tags]
- self.setRowCount(len(self._tasks))
- return row
-
- def is_reversed(self) -> bool:
- if self._sort_column == 0:
- return self._sort_is_ascending
- else:
- return not self._sort_is_ascending
-
- def sort_key(self, task: Task, is_reversed: bool):
- row = self._sort_column
- 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:
- tag_ids = [tt.tag_id for tt in self._task_tags if tt.task_id == task.id]
- tags = sorted([tag.name.lower() for tag in self._tags if tag.id in tag_ids])
- key = "".join(tags)
- if is_reversed:
- return key
- else:
- return (key == "", key)
-
- def keyPressEvent(self, event):
- super().keyPressEvent(event)
- if event.key() in (Qt.Key_Return, Qt.Key_Enter):
- rows = self.get_selected_rows()
- if len(rows) == 1:
- row = rows[0]
- (task, tags) = self.get_at(row)
- gui.tasks.dialog.update(self, self._update_task_signal, row, task, tags).exec_()
- elif event.key() == Qt.Key_Delete:
- rows = self.get_selected_rows()
- gui.tasks.dialog.show_delete(self, rows, lambda: self.delete_rows(rows))
-
- def delete_rows(self, rows: List[int]):
- task_ids = [task.id for i, task in enumerate(self._tasks) if i in rows]
- service.tasks.delete(database.cursor(), task_ids)
- self.remove_rows_from_table(rows, task_ids)
-
- def update_status(self, rows: List[int], status: Status):
- task_ids = [task.id for i, task in enumerate(self._tasks) if i in rows]
- service.tasks.update_status(database.cursor(), task_ids, status)
- self.remove_rows_from_table(rows, task_ids)
-
- def remove_rows_from_table(self, rows: List[int], task_ids: List[int]):
- for row in sorted(rows, reverse=True):
- self.removeRow(row)
- self._tasks = [t for t in self._tasks if t.id not in task_ids]
- self._task_tags = [tt for tt in self._task_tags if tt.task_id in [t.id for t in self._tasks]]
- self.setRowCount(len(self._tasks))
-
- def get_selected_rows(self):
- return list(set([index.row() for index in self.selectedIndexes()]))
-
- def on_double_click(self, row: int):
- (task, tags) = self.get_at(row)
- gui.tasks.dialog.update(self, self._update_task_signal, row, task, tags).exec_()
-
- def get_at(self, row: int) -> Tuple[Task, List[int]]:
- task = self._tasks[row]
- tags = [tt.tag_id for tt in self._task_tags if tt.task_id == task.id]
- return (task, tags)
-
- def header_labels(self):
- labels = ["Age", "Name", "Duration", "Difficulty", "Priority", "Tag"]
- if self._sort_is_ascending:
- sign = "▼"
- else:
- sign = "▲"
- labels[self._sort_column] = labels[self._sort_column] + " " + sign
- return labels
-
-def item(text: str):
- item = QtWidgets.QTableWidgetItem(text)
- item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
- return item
-
-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 colored_label(parent, text: str, color: QtGui.QColor):
- label = QtWidgets.QLabel(text)
- palette = QtGui.QPalette()
- palette.setColor(QtGui.QPalette.Text, color)
- label.setPalette(palette)
- return label
-
-def render_tags(parent, tags: List[Tag]):
- widget = QtWidgets.QWidget(parent)
-
- layout = QtWidgets.QHBoxLayout(widget)
- widget.setLayout(layout)
-
- for tag in tags:
- label = QtWidgets.QLabel(tag.name)
- label.setContentsMargins(3, 3, 3, 3)
- palette = QtGui.QPalette()
- palette.setColor(QtGui.QPalette.Base, QtGui.QColor(tag.color))
- label.setAutoFillBackground(True)
- label.setPalette(palette)
- layout.addWidget(label)
-
- return widget
-
-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