diff options
Diffstat (limited to 'src/main.py')
-rw-r--r-- | src/main.py | 295 |
1 files changed, 295 insertions, 0 deletions
diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..dfdf8a7 --- /dev/null +++ b/src/main.py @@ -0,0 +1,295 @@ +#!/usr/bin/python3 +import sys +import time +from PyQt5.QtWidgets import QAction, QApplication, QGridLayout, QHBoxLayout, QLabel, QMainWindow, QMenu, QMessageBox, QScrollArea, QToolBar, QVBoxLayout, QWidget +from PyQt5.QtGui import QCursor, QFont +from PyQt5.QtCore import QDate, Qt +from src.config import Config +from src.preferences_dialog import PreferencesDialog +from src.add_group_form import addGroupForm +from src.edit_group_form import editGroupForm +from src.add_entry_form import addEntryForm +from src.edit_entry_form import editEntryForm +import src.globals as Globals +import src.db_sqlite as DB + +class AssignmentList(QMainWindow): + def __init__(self): + super().__init__() + + self.initializeUI() + + def initializeUI(self): + self.resize(640, 480) + self.setWindowTitle("Assignment List") + self.createMenu() + self.createToolbar() + Config() + self.setupDB() + self.displayWidgets() + self.show() + + def createMenu(self): + menu_bar = self.menuBar() + file_menu = menu_bar.addMenu("File") + edit_menu = menu_bar.addMenu("Edit") + help_menu = menu_bar.addMenu("Help") + + self.preferences_act = QAction("Preferences", self) + self.preferences_act.setShortcut("Alt+Return") + self.preferences_act.triggered.connect(PreferencesDialog) + file_menu.addAction(self.preferences_act) + # TODO implement reload of DB that works + self.reload_act = QAction("Reload [WIP]", self) + self.reload_act.setShortcut("F5") + #self.reload_act.triggered.connect(self.reload) + file_menu.addAction(self.reload_act) + file_menu.addSeparator() + exit_act = QAction("Exit", self) + exit_act.setShortcut("Ctrl+Q") + exit_act.triggered.connect(self.close) + file_menu.addAction(exit_act) + + self.add_group_act = QAction("Add Group", self) + self.add_group_act.triggered.connect(self.addGroup) + edit_menu.addAction(self.add_group_act) + edit_menu.addSeparator() + self.clean_hidden_act = QAction("Permanently Delete Removed Groups and Entries", self) + self.clean_hidden_act.triggered.connect(self.cleanHidden) + edit_menu.addAction(self.clean_hidden_act) + + about_act = QAction("About", self) + about_act.triggered.connect(self.aboutDialog) + help_menu.addAction(about_act) + + def createToolbar(self): + tool_bar = QToolBar("Toolbar") + self.addToolBar(tool_bar) + + tool_bar.addAction(self.add_group_act) + + def setupDB(self): + DB.initDB() + + def displayWidgets(self): + main_widget_scroll_area = QScrollArea(self) + main_widget_scroll_area.setWidgetResizable(True) + main_widget = QWidget() + self.setCentralWidget(main_widget_scroll_area) + + title = QLabel(time.strftime("%A, %b %d %Y")) + title.setFont(QFont("Arial", 17)) + title.setTextInteractionFlags(Qt.TextSelectableByMouse) + + title_h_box = QHBoxLayout() + title_h_box.addStretch() + title_h_box.addWidget(title) + title_h_box.addStretch() + + self.groups_layout = QGridLayout() + self.groups_layout.setContentsMargins(20, 5, 20, 5) + self.drawGroups() + + v_box = QVBoxLayout() + v_box.addLayout(title_h_box) + v_box.addLayout(self.groups_layout) + v_box.addStretch() + + main_widget.setLayout(v_box) + main_widget_scroll_area.setWidget(main_widget) + + def addGroup(self): + """ + Open the 'addGroup' form + """ + old_count = len(Globals.groups) + self.create_new_group_dialog = addGroupForm() + if old_count != len(Globals.groups): + self.drawGroups() + + def editGroup(self, id): + """ + Open the 'editGroup' form + """ + self.create_edit_group_dialog = editGroupForm(id) + self.drawGroups() + + def removeGroup(self, id): + """ + Delete a group with a given id + """ + # TODO might want to add a warning + # TODO might want to make part of the a destructor in the Group class + removed = DB.removeGroup(id) + if removed > 0: + Globals.entries = list(filter(lambda e: e.parent_id != id, Globals.entries)) + Globals.groups = list(filter(lambda g: g.id != id, Globals.groups)) + self.drawGroups() + + def groupContextMenu(self, group_id): + menu = QMenu() + + add_entry_act = QAction("Add Entry") + add_entry_act.triggered.connect((lambda id: lambda: self.addEntry(id))(group_id)) + menu.addAction(add_entry_act) + + edit_group_act = QAction("Edit Group") + edit_group_act.triggered.connect((lambda id: lambda: self.editGroup(id))(group_id)) + menu.addAction(edit_group_act) + + del_group_act = QAction("Remove Group") + del_group_act.triggered.connect((lambda id: lambda: self.removeGroup(id))(group_id)) + menu.addAction(del_group_act) + + menu.exec_(QCursor.pos()) + + def addEntry(self, parent): + """ + Open the 'addEntry' form + """ + old_count = len(Globals.entries) + self.create_new_entry_dialog = addEntryForm(parent) + if old_count != len(Globals.entries): + self.drawGroups() # TODO see if we can do this with only redrawing a single group + + def editEntry(self, id): + """ + Open the 'editEntry' form + """ + self.create_edit_entry_dialog = editEntryForm(id) + self.drawGroups() + + def toggleDoneEntry(self, id): + """ + Toggle the 'done' flag on the entry with the given id + """ + entry = list(filter(lambda e: e.id == id, Globals.entries))[0] + if entry.done: + entry.done = False + else: + entry.done = True + DB.updateEntry(entry) + Globals.entries = list(filter(lambda e: e.id != id, Globals.entries)) + Globals.entries.append(entry) + self.drawGroups() + + def removeEntry(self, id): + """ + Delete an entry with a given id + """ + # TODO might want to add a warning + # TODO might want to make part of the a destructor in the Entry class + removed = DB.removeEntry(id) + if removed > 0: + Globals.entries = list(filter(lambda e: e.id != id, Globals.entries)) + self.drawGroups() + + def entryContextMenu(self, entry_id): + menu = QMenu() + + edit_entry_act = QAction("Edit Entry") + edit_entry_act.triggered.connect((lambda id: lambda: self.editEntry(id))(entry_id)) + menu.addAction(edit_entry_act) + + mark_done_act = QAction("Done", checkable=True) + if list(filter(lambda e: e.id == entry_id, Globals.entries))[0].done: + mark_done_act.setChecked(True) + mark_done_act.triggered.connect((lambda id: lambda: self.toggleDoneEntry(id))(entry_id)) + menu.addAction(mark_done_act) + + del_entry_act = QAction("Remove Entry") + del_entry_act.triggered.connect((lambda id: lambda: self.removeEntry(id))(entry_id)) + menu.addAction(del_entry_act) + + menu.exec_(QCursor.pos()) + + def cleanHidden(self): + """ + Permanently delete removed groups and entries from db + """ + # TODO consider creating a warning dialogue for this + DB.cleanHidden() + + def drawGroups(self): + """ + Redraw the groups_layout + """ + # Remove all children from layout + def recursiveClear(layout): + while layout.count(): + child = layout.takeAt(0) + if child.widget(): + child.widget().deleteLater() + elif child.layout(): + recursiveClear(child) + + recursiveClear(self.groups_layout) + + # Sort the groups + Globals.groups = sorted(Globals.groups, key=lambda g: g.id) + + # Sort the entries + Globals.entries = sorted(Globals.entries, key=lambda e: (e.parent_id, (e.due if e.due else QDate.currentDate()), e.done, e.id)) + + # Create columns as vertical boxes + column_left = QVBoxLayout() + column_right = QVBoxLayout() + + for g in Globals.groups: + # skip if this group is set to hidden + if g.hidden: + continue + + g_layout = g.buildLayout() + + # Create custom context menu + g_layout.itemAt(0).widget().setToolTip("Right-Click for actions") + g_layout.itemAt(0).widget().setContextMenuPolicy(Qt.CustomContextMenu) + g_layout.itemAt(0).widget().customContextMenuRequested.connect((lambda id: lambda: self.groupContextMenu(id))(g.id)) + + # Draw entries belonging to this group + g_layout.addLayout(self.drawEntries(g.id)) + + if g.column.lower() == "left": + column_left.addLayout(g_layout) + else: + column_right.addLayout(g_layout) + + column_left.addStretch() + column_right.addStretch() + + self.groups_layout.addLayout(column_left, 0, 0) + self.groups_layout.addLayout(column_right, 0, 1) + + def drawEntries(self, group_id): + """ + Redraw the entries of a specific group + """ + # TODO consider having code to remove existing widgets to make this function more modular + entries = list(filter(lambda e: e.parent_id == group_id, Globals.entries)) + entries_vbox = QVBoxLayout() + entries_vbox.setContentsMargins(5, 0, 0, 0) + + for e in entries: + # skip if this entry is set to hidden + if e.hidden: + continue + + e_layout = e.buildLayout() + entries_vbox.addLayout(e_layout) + + # Create custom context menu + e_layout.itemAt(1).widget().setContextMenuPolicy(Qt.CustomContextMenu) + e_layout.itemAt(1).widget().customContextMenuRequested.connect((lambda id: lambda: self.entryContextMenu(id))(e.id)) + + return entries_vbox + + def aboutDialog(self): + QMessageBox.about(self, "About Assignment List", + "Created by Louie S. - 2023") + +if __name__ == "__main__": + app = QApplication(sys.argv) + window = AssignmentList() + sys.exit(app.exec_()) + |