From 65d8cbafe34b946d450ce46703e6449eb9962741 Mon Sep 17 00:00:00 2001 From: Louie Shprung Date: Thu, 21 Dec 2023 13:17:39 -0800 Subject: Rename src directory to unique name to avoid install issues --- src/__init__.py | 0 src/add_entry_form.py | 51 ----- src/add_entry_form.ui | 185 ----------------- src/add_group_form.py | 47 ----- src/add_group_form.ui | 131 ------------ src/config.py | 95 --------- src/db_sqlite.py | 493 ---------------------------------------------- src/edit_entry_form.py | 83 -------- src/edit_group_form.py | 63 ------ src/entry.py | 100 ---------- src/globals.py | 4 - src/group.py | 25 --- src/main.py | 273 ------------------------- src/main.ui | 207 ------------------- src/preferences_dialog.py | 70 ------- src/preferences_dialog.ui | 128 ------------ src/rule.py | 44 ----- src/rules_dialog.py | 128 ------------ 18 files changed, 2127 deletions(-) delete mode 100644 src/__init__.py delete mode 100644 src/add_entry_form.py delete mode 100644 src/add_entry_form.ui delete mode 100644 src/add_group_form.py delete mode 100644 src/add_group_form.ui delete mode 100644 src/config.py delete mode 100644 src/db_sqlite.py delete mode 100644 src/edit_entry_form.py delete mode 100644 src/edit_group_form.py delete mode 100644 src/entry.py delete mode 100644 src/globals.py delete mode 100644 src/group.py delete mode 100644 src/main.py delete mode 100644 src/main.ui delete mode 100644 src/preferences_dialog.py delete mode 100644 src/preferences_dialog.ui delete mode 100644 src/rule.py delete mode 100644 src/rules_dialog.py (limited to 'src') diff --git a/src/__init__.py b/src/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/add_entry_form.py b/src/add_entry_form.py deleted file mode 100644 index 9a2d260..0000000 --- a/src/add_entry_form.py +++ /dev/null @@ -1,51 +0,0 @@ -import os -import sys -from PyQt5 import uic -from PyQt5.QtWidgets import QApplication, QDialog, QMessageBox -from PyQt5.QtCore import QDate -from src.entry import Entry -import src.globals as Globals -import src.db_sqlite as DB - -class addEntryForm(QDialog): - def __init__(self, parent): - super().__init__() - uic.loadUi(os.path.join(os.path.dirname(os.path.abspath(__file__)), - "add_entry_form.ui"), self) - self.initializeUI(parent) - - def initializeUI(self, parent): - self.displayWidgets(parent) - self.exec() - - def displayWidgets(self, parent): - self.new_entry_due.setDate(QDate.currentDate()) - self.buttonBox.rejected.connect(self.close) - self.buttonBox.accepted.connect(lambda: self.handleSubmit(parent)) - - def handleSubmit(self, parent): - # Check that the new entry is not blank - desc_text = self.new_entry_desc.text() - due_text = "" - if self.new_entry_due_checkbox.isChecked(): - due_text = self.new_entry_due.date() # due_text is a QDate - due_alt_text = self.new_entry_due_alt.text() - link_text = self.new_entry_link.text() - color_text = self.new_entry_color.text() - highlight_text = self.new_entry_highlight.text() - - if not desc_text: - QMessageBox.warning(self, "Error Message", - "Description cannot be blank", - QMessageBox.Close, - QMessageBox.Close) - return - - new_id = DB.insertEntry(Entry(0, parent, desc_text, due_text, due_alt_text, link_text, color_text, highlight_text)) - Globals.entries.append(Entry(new_id, parent, desc_text, due_text, due_alt_text, link_text, color_text, highlight_text)) - self.close() - -if __name__ == "__main__": - app = QApplication(sys.argv) - window = addEntryForm() - sys.exit(app.exec_()) diff --git a/src/add_entry_form.ui b/src/add_entry_form.ui deleted file mode 100644 index 3f8f9e7..0000000 --- a/src/add_entry_form.ui +++ /dev/null @@ -1,185 +0,0 @@ - - - Dialog - - - - 0 - 0 - 400 - 266 - - - - Add Entry - - - - - - - - Description: - - - - - - - - - - - Arial - 18 - - - - Add Entry - - - Qt::AlignCenter - - - - - - - Due Date: - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - MM/dd/yyyy - - - - - - - true - - - - - - true - - - - - - - - - - Due Date (Alt): - - - - - - - - - - Link: - - - - - - - - - - Color: - - - - - - - - - - Highlight: - - - - - - - - - - - - Qt::RightToLeft - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - true - - - - - - - - - buttonBox - accepted() - Dialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - Dialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/src/add_group_form.py b/src/add_group_form.py deleted file mode 100644 index 4a543e4..0000000 --- a/src/add_group_form.py +++ /dev/null @@ -1,47 +0,0 @@ -import os -import sys -from PyQt5 import uic -from PyQt5.QtWidgets import QApplication, QDialog, QMessageBox - -import src.globals as Globals -from src.group import Group -import src.db_sqlite as DB - -class addGroupForm(QDialog): - """ - Implemented so that it can be used for adding and editing groups - """ - def __init__(self): - super().__init__() - uic.loadUi(os.path.join(os.path.dirname(os.path.abspath(__file__)), - "add_group_form.ui"), self) - self.initializeUI() - - def initializeUI(self): - self.displayWidgets() - self.exec() - - def displayWidgets(self): - self.buttonBox.rejected.connect(self.close) - self.buttonBox.accepted.connect(self.handleSubmit) - - def handleSubmit(self): - name_text = self.new_group_name.text() - column_text = self.new_group_column.currentText() - link_text = self.new_group_link.text() - - if not name_text: - QMessageBox.warning(self, "Error Message", - "Name cannot be blank", - QMessageBox.Close, - QMessageBox.Close) - return - - new_id = DB.insertGroup(Group(0, name_text, column_text, link_text)) - Globals.groups.append(Group(new_id, name_text, column_text, link_text)) - self.close() - -if __name__ == "__main__": - app = QApplication(sys.argv) - window = addGroupForm() - sys.exit(app.exec_()) diff --git a/src/add_group_form.ui b/src/add_group_form.ui deleted file mode 100644 index c3c5c80..0000000 --- a/src/add_group_form.ui +++ /dev/null @@ -1,131 +0,0 @@ - - - Dialog - - - - 0 - 0 - 400 - 172 - - - - Add Entry - - - - - - - - - Arial - 18 - - - - Add Group - - - Qt::AlignCenter - - - - - - - Name: - - - - - - - - - - Column: - - - - - - - - Left - - - - - Right - - - - - - - - Link: - - - - - - - - - - - - Qt::RightToLeft - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - true - - - - - - - - - buttonBox - accepted() - Dialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - Dialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/src/config.py b/src/config.py deleted file mode 100644 index 0fa034e..0000000 --- a/src/config.py +++ /dev/null @@ -1,95 +0,0 @@ -""" -Handle reading of the config file, which on POSIX-compliant systems will be -created in ~/.config/assignment-list-pyqt5/config.py -""" - -import configparser -import os -import sys - -import src.globals as Globals - -class Config(): - def __init__(self): - self.config_path = self.getConfigPath() - - if not os.path.exists(self.config_path): - self.createConfig() - - self.loadConfig() - - def getConfigPath(self): - # Windows config path is "$LOCALAPPDATA/assignment-list-pyqt5/config" - if sys.platform.startswith("win32"): - return os.path.join(os.path.expandvars("$LOCALAPPDATA"), - "assignment-list-pyqt5", - "config") - # Unix config path is "$HOME/.config/assignment-list-pyqt5/config" - else: - return os.path.join( - os.path.expanduser("~"), - ".config", - "assignment-list-pyqt5", - "config") - - def loadConfig(self): - self.config = configparser.ConfigParser() - - try: - self.config.read(self.config_path) - except: - print("Could not parse config file '{}'".format(self.config_path)) - - if "paths" in self.config: - if self.config["paths"]["db_path"]: - Globals.db_path = self.config["paths"]["db_path"] - - def createConfig(self): - self.config = configparser.ConfigParser() - if sys.platform.startswith("win32"): - self.config["paths"] = { - # Windows default DB path is "$APPDATA/assignment-list-pyqt5/data.db" - "db_path": os.path.join( - os.path.expandvars("$APPDATA"), - "assignment-list-pyqt5", - "data.db" - ) - } - else: - self.config["paths"] = { - # Unix default DB path is "$HOME/.local/share/assignment-list-pyqt5/data.db" - "db_path": os.path.join( - os.path.expanduser("~"), - ".local", - "share", - "assignment-list-pyqt5", - "data.db" - ) - } - - self.updateConfig() - - def updateConfig(self): - """ - Update the configuration file with values from self.config - """ - # Attempt to create directory if necessary - if not os.path.exists(os.path.dirname(self.config_path)): - try: - os.mkdir(os.path.dirname(self.config_path)) - except: - print("Error: Could not create config directory '{}'".format(os.path.dirname(self.config_path))) - sys.exit(1) - - # Attempt to write to file - try: - with open(self.config_path, 'w') as configfile: - self.config.write(configfile) - except: - print("Error: Could not open config file '{}'".format(self.config_path)) - sys.exit(1) - - print("Successfully created config at {}".format(self.config_path)) - -if __name__ == "__main__": - Config() diff --git a/src/db_sqlite.py b/src/db_sqlite.py deleted file mode 100644 index 00f4edb..0000000 --- a/src/db_sqlite.py +++ /dev/null @@ -1,493 +0,0 @@ -import os -import sys -from time import strptime -from PyQt5.QtCore import QDate -from PyQt5.QtSql import QSqlDatabase, QSqlQuery -import src.globals as Globals -from src.group import Group -from src.entry import Entry -from src.rule import Rule - -def initDB(): - """ - Check for existing database. If it doesn't exist, build it - """ - if not os.path.exists(Globals.db_path) or not os.stat(Globals.db_path).st_size: - createTables() - - loadFromTables() - -def createTables(): - """ - Create database at a specified Globals.db_path - """ - print(Globals.db_path) - database = QSqlDatabase.addDatabase("QSQLITE") # SQlite version 3 - database.setDatabaseName(Globals.db_path) - - # Create database parent directory if necessary - if not os.path.exists(os.path.dirname(Globals.db_path)): - try: - os.mkdir(os.path.dirname(Globals.db_path)) - except: - print("Unable to open data source file.") - sys.exit(1) - - if not database.open(): - print("Unable to open data source file.") - sys.exit(1) # Error out. TODO consider throwing a dialog instead - - query = QSqlQuery() - # Erase database contents so that we don't have duplicates - query.exec_("DROP TABLE groups") - query.exec_("DROP TABLE entries") - query.exec_("DROP TABLE rules") - - query.exec_(""" - CREATE TABLE groups ( - id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE NOT NULL, - name VARCHAR(255) NOT NULL, - column TINYINT(1) DEFAULT FALSE, - link VARCHAR(255) NOT NULL, - hidden TINYINT(1) DEFAULT FALSE - ) - """) - - query.exec_(""" - CREATE TABLE entries ( - id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE NOT NULL, - parent_id REFERENCES groups (id), - description VARCHAR(255) NOT NULL, - due_date TEXT DEFAULT NULL, - alt_due_date VARCHAR(255) DEFAULT NULL, - link VARCHAR(255) DEFAULT NULL, - color VARCHAR(255) DEFAULT NULL, - highlight VARCHAR(255) DEFAULT NULL, - done TINYINT(1) DEFAULT FALSE, - hidden TINYINT(1) DEFAULT FALSE - ) - """) - - query.exec_(""" - CREATE TABLE rules ( - id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE NOT NULL, - entry_id REFERENCES entries (id), - before_after TINYINT(1) DEFAULT TRUE, - date TEXT NOT NULL, - color VARCHAR(255) DEFAULT NULL, - highlight VARCHAR(255) DEFAULT NULL - ) - """) - - print(database.lastError().text()) - - database.close() - -def loadFromTables(): - """ - Load groups and entries into global variables - """ - database = QSqlDatabase.addDatabase("QSQLITE") # SQlite version 3 - database.setDatabaseName(Globals.db_path) - - if not database.open(): - print("Unable to open data source file.") - sys.exit(1) # Error out. TODO consider throwing a dialog instead - - query = QSqlQuery() - - # Load groups - Globals.groups = [] # Reset local groups array - query.exec_("SELECT * FROM groups") - while query.next(): - record = query.record() - Globals.groups.append( - Group( - record.field("id").value(), - record.field("name").value(), - record.field("column").value(), - record.field("link").value(), - record.field("hidden").value())) - - # Load entries - Globals.entries = [] # Reset local entries array - query.exec_("SELECT * FROM entries") - while query.next(): - record = query.record() - # create a QDate if the due date is set - if record.field("due_date").value(): - due_date_struct = strptime(record.field("due_date").value(), "%Y-%m-%d") - due_date = QDate(due_date_struct.tm_year, due_date_struct.tm_mon, due_date_struct.tm_mday) - else: - due_date = "" - Globals.entries.append( - Entry( - record.field("id").value(), - record.field("parent_id").value(), - record.field("description").value(), - due_date, - record.field("alt_due_date").value(), - record.field("link").value(), - record.field("color").value(), - record.field("highlight").value(), - record.field("done").value(), - record.field("hidden").value())) - - # Load rules - Globals.rules = [] # Reset local rules array - query.exec_("SELECT * FROM rules") - while query.next(): - record = query.record() - date_struct = strptime(record.field("date").value(), "%Y-%m-%d") - date = QDate(date_struct.tm_year, date_struct.tm_mon, date_struct.tm_mday) - Globals.rules.append( - Rule( - record.field("id").value(), - record.field("entry_id").value(), - "before" if record.field("before_after").value() == 0 else "after", - date, - record.field("color").value(), - record.field("highlight").value())) - - database.close() - -def insertGroup(new_group): - """ - Insert group to the database at Globals.db_path - """ - output = -1 - - database = QSqlDatabase.addDatabase("QSQLITE") # SQlite version 3 - database.setDatabaseName(Globals.db_path) - - if not database.open(): - print("Unable to open data source file.") - sys.exit(1) # Error out. TODO consider throwing a dialog instead - - query = QSqlQuery() - - query.prepare(""" - INSERT INTO groups (name, column, link) VALUES (?, ?, ?) - """) - query.addBindValue(new_group.name) - query.addBindValue(new_group.column) - query.addBindValue(new_group.link) - query.exec_() - - output = query.lastInsertId() - - database.close() - - return output - -def insertEntry(new_entry): - """ - Insert entry to the database at Globals.db_path - """ - output = -1 - - database = QSqlDatabase.addDatabase("QSQLITE") # SQlite version 3 - database.setDatabaseName(Globals.db_path) - - if not database.open(): - print("Unable to open data source file.") - sys.exit(1) # Error out. TODO consider throwing a dialog instead - - query = QSqlQuery() - - query.prepare(""" - INSERT INTO entries (parent_id, description, due_date, alt_due_date, link, color, highlight) VALUES (:p_id, :desc, :due, :alt_due, :link, :color, :highlight) - """) - query.bindValue(":p_id", new_entry.parent_id) - query.bindValue(":desc", new_entry.desc) - if new_entry.due: - query.bindValue(":due", "{0}-{1}-{2}".format( - new_entry.due.year(), - new_entry.due.month(), - new_entry.due.day())) - else: - query.bindValue(":due", "") - query.bindValue(":alt_due", new_entry.due_alt) - query.bindValue(":link", new_entry.link) - query.bindValue(":color", new_entry.color) - query.bindValue(":highlight", new_entry.highlight) - success = query.exec_() - # DEBUG - #print(query.lastError().text()) - #print(query.boundValues()) - #if success: - # print("Query succeeded") - #else: - # print("Query failed") - - output = query.lastInsertId() - - database.close() - - return output - -def insertRule(new_rule): - """ - Insert rule to the database at Globals.db_path - """ - output = -1 - - database = QSqlDatabase.addDatabase("QSQLITE") # SQlite version 3 - database.setDatabaseName(Globals.db_path) - - if not database.open(): - print("Unable to open data source file.") - sys.exit(1) # Error out. TODO consider throwing a dialog instead - - query = QSqlQuery() - - query.prepare(""" - INSERT INTO rules (entry_id, before_after, date, color, highlight) VALUES (:e_id, :when, :date, :color, :highlight) - """) - query.bindValue(":e_id", new_rule.entry_id) - query.bindValue(":when", 0 if new_rule.when.lower() == "before" else 1) - query.bindValue(":date", "{0}-{1}-{2}".format( - new_rule.date.year(), - new_rule.date.month(), - new_rule.date.day())) - query.bindValue(":color", new_rule.color) - query.bindValue(":highlight", new_rule.highlight) - success = query.exec_() - # DEBUG - #print(query.lastError().text()) - #print(query.boundValues()) - #if success: - # print("Query succeeded") - #else: - # print("Query failed") - - output = query.lastInsertId() - - database.close() - - return output - -def updateGroup(group): - """ - Update group by its id - """ - database = QSqlDatabase.addDatabase("QSQLITE") # SQlite version 3 - database.setDatabaseName(Globals.db_path) - - if not database.open(): - print("Unable to open data source file.") - sys.exit(1) # Error out. TODO consider throwing a dialog instead - - query = QSqlQuery() - - query.prepare(""" - UPDATE groups SET name = ?, column = ?, link = ?, hidden = ? WHERE id = ? - """) - query.addBindValue(group.name) - query.addBindValue(group.column) - query.addBindValue(group.link) - query.addBindValue(group.hidden) - query.addBindValue(group.id) - query.exec_() - - database.close() - -def updateEntry(entry): - """ - Update entry by its id - """ - database = QSqlDatabase.addDatabase("QSQLITE") # SQlite version 3 - database.setDatabaseName(Globals.db_path) - - if not database.open(): - print("Unable to open data source file.") - sys.exit(1) # Error out. TODO consider throwing a dialog instead - - query = QSqlQuery() - - query.prepare(""" - UPDATE entries SET - description = :desc, - due_date = :due, - alt_due_date = :alt_due, - link = :link, - color = :color, - highlight = :highlight, - done = :done, - hidden = :hidden - WHERE id = :id - """) - query.bindValue(":desc", entry.desc) - if entry.due: - query.bindValue(":due", "{0}-{1}-{2}".format( - entry.due.year(), - entry.due.month(), - entry.due.day())) - else: - query.bindValue(":due", "") - query.bindValue(":alt_due", entry.due_alt) - query.bindValue(":link", entry.link) - query.bindValue(":color", entry.color) - query.bindValue(":highlight", entry.highlight) - query.bindValue(":done", entry.done) - query.bindValue(":hidden", entry.hidden) - query.bindValue(":id", entry.id) - query.exec_() - - database.close() - -def updateRule(rule): - """ - Update rule by its id - """ - database = QSqlDatabase.addDatabase("QSQLITE") # SQlite version 3 - database.setDatabaseName(Globals.db_path) - - if not database.open(): - print("Unable to open data source file.") - sys.exit(1) # Error out. TODO consider throwing a dialog instead - - query = QSqlQuery() - - query.prepare(""" - UPDATE rules SET - before_after = :when, - date = :date, - color = :color, - highlight = :highlight - WHERE id = :id - """) - query.bindValue(":when", 0 if rule.when.lower() == "before" else 1) - query.bindValue(":date", "{0}-{1}-{2}".format( - rule.date.year(), - rule.date.month(), - rule.date.day())) - query.bindValue(":color", rule.color) - query.bindValue(":highlight", rule.highlight) - query.bindValue(":id", rule.id) - success = query.exec_() - # DEBUG - #print(query.lastError().text()) - #print(query.boundValues()) - #if success: - # print("Query succeeded") - #else: - # print("Query failed") - - database.close() - -def removeGroup(group_id): - """ - Remove a group by id from the database - (actually set hidden to true, don't permanently delete it) - """ - - database = QSqlDatabase.addDatabase("QSQLITE") # SQlite version 3 - database.setDatabaseName(Globals.db_path) - - if not database.open(): - print("Unable to open data source file.") - sys.exit(1) # Error out. TODO consider throwing a dialog instead - - query = QSqlQuery() - - # First, set entries to hidden - query.prepare(""" - UPDATE entries SET hidden = 1 WHERE parent_id = ? - """) - query.addBindValue(group_id) - query.exec_() - - # Now, set the group to hidden - query.prepare(""" - UPDATE groups SET hidden = 1 WHERE id = ? - """) - query.addBindValue(group_id) - query.exec_() - - output = query.numRowsAffected() - database.close() - return output - -def removeEntry(entry_id): - """ - Remove a group by id from the database - (actually set hidden to true, don't permanently delete it) - """ - database = QSqlDatabase.addDatabase("QSQLITE") # SQlite version 3 - database.setDatabaseName(Globals.db_path) - - if not database.open(): - print("Unable to open data source file.") - sys.exit(1) # Error out. TODO consider throwing a dialog instead - - query = QSqlQuery() - - # Set entry to hidden - query.prepare(""" - UPDATE entries SET hidden = 1 WHERE id = ? - """) - query.addBindValue(entry_id) - query.exec_() - - output = query.numRowsAffected() - database.close() - return output - -def removeRule(rule_id): - """ - Remove a rule by id from the database - (we do not preserve rules, unlike groups and entries) - """ - database = QSqlDatabase.addDatabase("QSQLITE") # SQlite version 3 - database.setDatabaseName(Globals.db_path) - - if not database.open(): - print("Unable to open data source file.") - sys.exit(1) # Error out. TODO consider throwing a dialog instead - - query = QSqlQuery() - - # Set entry to hidden - query.prepare(""" - DELETE FROM rules WHERE id = ? - """) - query.addBindValue(rule_id) - query.exec_() - - output = query.numRowsAffected() - database.close() - return output - -def cleanHidden(): - """ - Permanently delete removed/hidden groups and entries - """ - database = QSqlDatabase.addDatabase("QSQLITE") # SQlite version 3 - database.setDatabaseName(Globals.db_path) - - if not database.open(): - print("Unable to open data source file.") - sys.exit(1) # Error out. TODO consider throwing a dialog instead - - query = QSqlQuery() - - # Remove rules associated with hidden entries - query.exec_(""" - DELETE FROM rules WHERE id IN ( - SELECT rules.id FROM rules - INNER JOIN entries ON rules.entry_id = entries.id - WHERE entries.hidden = 1 - )""") - - # Remove hidden entries - query.exec_(""" - DELETE FROM entries WHERE hidden = 1 - """) - - # Remove hidden groups - query.exec_(""" - DELETE FROM groups WHERE hidden = 1 - """) - - database.close() diff --git a/src/edit_entry_form.py b/src/edit_entry_form.py deleted file mode 100644 index 3c7f8c2..0000000 --- a/src/edit_entry_form.py +++ /dev/null @@ -1,83 +0,0 @@ -import os -import sys -from PyQt5 import uic -from PyQt5.QtWidgets import QApplication, QDialog, QMessageBox -from PyQt5.QtCore import QDate - -import src.globals as Globals -from src.entry import Entry -import src.db_sqlite as DB - -# Reuses the add_entry_form UI file -class editEntryForm(QDialog): - """ - Form to edit/update an entry - """ - def __init__(self, id): - self.id = id - super().__init__() - uic.loadUi(os.path.join(os.path.dirname(os.path.abspath(__file__)), - "add_entry_form.ui"), self) - self.initializeUI() - - def initializeUI(self): - self.setWindowTitle("Edit Entry") - self.displayWidgets() - self.exec() - - def displayWidgets(self): - entry = list(filter(lambda e: e.id == self.id, Globals.entries))[0] - - self.title.setText("Edit Entry") - self.new_entry_desc.setText(entry.desc) - self.new_entry_due.setDate(QDate.currentDate()) - if entry.due: - self.new_entry_due.setDate(entry.due) - self.new_entry_due_checkbox.setChecked(True) - else: - self.new_entry_due_checkbox.setChecked(False) - self.new_entry_due_alt.setText(entry.due_alt) - self.new_entry_link.setText(entry.link) - self.new_entry_color.setText(entry.color) - self.new_entry_highlight.setText(entry.highlight) - self.buttonBox.rejected.connect(self.close) - self.buttonBox.accepted.connect(self.handleSubmit) - - def handleSubmit(self): - desc_text = self.new_entry_desc.text() - if self.new_entry_due_checkbox.isChecked(): - due_text = self.new_entry_due.date() # due_text is a QDate - else: - due_text = "" # due is unchecked - due_alt_text = self.new_entry_due_alt.text() - link_text = self.new_entry_link.text() - color_text = self.new_entry_color.text() - highlight_text = self.new_entry_highlight.text() - - if not desc_text: - QMessageBox.warning(self, "Error Message", - "Name cannot be blank", - QMessageBox.Close, - QMessageBox.Close) - return - - # Update DB - entry = list(filter(lambda e: e.id == self.id, Globals.entries))[0] - entry.desc = desc_text - entry.due = due_text - entry.due_alt = due_alt_text - entry.link = link_text - entry.color = color_text - entry.highlight = highlight_text - DB.updateEntry(entry) - - # Update global variables - Globals.entries = list(filter(lambda e: e.id != self.id, Globals.entries)) - Globals.entries.append(Entry(self.id, entry.parent_id, desc_text, due_text, due_alt_text, link_text, color_text, highlight_text, entry.done, entry.hidden)) - self.close() - -if __name__ == "__main__": - app = QApplication(sys.argv) - window = editEntryForm() - sys.exit(app.exec_()) - diff --git a/src/edit_group_form.py b/src/edit_group_form.py deleted file mode 100644 index cd01162..0000000 --- a/src/edit_group_form.py +++ /dev/null @@ -1,63 +0,0 @@ -import os -import sys -from PyQt5 import uic -from PyQt5.QtWidgets import QApplication, QDialog, QMessageBox - -import src.globals as Globals -from src.group import Group -import src.db_sqlite as DB - -class editGroupForm(QDialog): - """ - Form to edit/update a group - """ - def __init__(self, id): - self.id = id - super().__init__() - uic.loadUi(os.path.join(os.path.dirname(os.path.abspath(__file__)), - "add_group_form.ui"), self) - self.initializeUI() - - def initializeUI(self): - self.setWindowTitle("Edit Group") - self.displayWidgets() - self.exec() - - def displayWidgets(self): - group = list(filter(lambda g: g.id == self.id, Globals.groups))[0] - - self.title.setText("Edit Group") - self.new_group_name.setText(group.name) - self.new_group_column.setCurrentIndex(0 if group.column.lower() == "left" else 1) - self.new_group_link.setText(group.link) - self.buttonBox.rejected.connect(self.close) - self.buttonBox.accepted.connect(self.handleSubmit) - - def handleSubmit(self): - name_text = self.new_group_name.text() - column_text = self.new_group_column.currentText() - link_text = self.new_group_link.text() - - if not name_text: - QMessageBox.warning(self, "Error Message", - "Name cannot be blank", - QMessageBox.Close, - QMessageBox.Close) - return - - # Update DB - group = list(filter(lambda g: g.id == self.id, Globals.groups))[0] - group.name = name_text - group.column = column_text - group.link = link_text - DB.updateGroup(group) - - # Update global variables - Globals.groups = list(filter(lambda g: g.id != self.id, Globals.groups)) - Globals.groups.append(Group(self.id, name_text, column_text, link_text, group.hidden)) - self.close() - -if __name__ == "__main__": - app = QApplication(sys.argv) - window = editGroupForm() - sys.exit(app.exec_()) diff --git a/src/entry.py b/src/entry.py deleted file mode 100644 index 3b639ab..0000000 --- a/src/entry.py +++ /dev/null @@ -1,100 +0,0 @@ -from datetime import date -from PyQt5.QtCore import QDate, Qt -from PyQt5.QtGui import QFont -from PyQt5.QtWidgets import QHBoxLayout, QLabel -import src.globals as Globals - -class Entry: - def __init__(self, id, parent_id, desc, due = "", due_alt = "", link = "", color = "", highlight = "", done = False, hidden = False): - self.id = id - self.parent_id = parent_id - self.desc = desc - self.due = due - self.due_alt = due_alt - self.link = link - self.color = color - self.highlight = highlight - self.done = done - self.hidden = hidden - - def buildLayout(self): - output = QHBoxLayout() - bullet = QLabel() - body = QLabel() - - output.setContentsMargins(2,2,2,2) - - bullet.setFont(QFont("Arial", 11)) - bullet.setMaximumWidth(15) - - body.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.LinksAccessibleByMouse) - body.setFont(QFont("Arial", 11)) - body.setWordWrap(True) - body.setToolTip("Right-Click for actions") - - # Check rules - relevant_rules = list(filter(lambda r: r.entry_id == self.id, Globals.rules)) - for r in relevant_rules: - if (r.when.lower() == "before" and r.date > QDate.currentDate()) or (r.when.lower() == "after" and r.date <= QDate.currentDate()): - if r.color: - self.color = r.color - if r.highlight: - self.highlight = r.highlight - - if self.done: - bullet.setText("\u2713 ") - bullet.setStyleSheet(""" - QLabel{ - color: green; - } - """) - self.color = "green" - else: - bullet.setText("- ") - output.addWidget(bullet) - - if self.due: - body.setText("{0}/{1}/{2}: ".format( - self.due.month(), - self.due.day(), - self.due.year() - )) - elif self.due_alt: - body.setText("{0}: ".format( - self.due_alt - )) - if self.link: - body.setOpenExternalLinks(True) - body.setText(body.text() + "".format( - self.link, - self.color if self.color else "default" - )) - body.setText(body.text() + self.desc) - if self.link: - body.setText(body.text() + "") - body.setToolTip("{}".format(self.link)) - output.addWidget(body) - - if self.done: - font = body.font() - font.setStrikeOut(True) - body.setFont(font) - body.setStyleSheet(""" - QLabel{ - color: green; - } - """) - - else: - body.setStyleSheet(""" - QLabel{{ - color: {0}; - background-color: {1}; - font-weight: {2}; - }}""".format( - self.color if self.color else "default", - self.highlight if self.highlight else "none", - "bold" if self.due and self.due <= date.today() else "normal" - )) - - return output diff --git a/src/globals.py b/src/globals.py deleted file mode 100644 index bb6f717..0000000 --- a/src/globals.py +++ /dev/null @@ -1,4 +0,0 @@ -groups = [] -entries = [] -rules = [] -db_path = "" diff --git a/src/group.py b/src/group.py deleted file mode 100644 index 18b4836..0000000 --- a/src/group.py +++ /dev/null @@ -1,25 +0,0 @@ -from PyQt5.QtCore import Qt -from PyQt5.QtGui import QFont -from PyQt5.QtWidgets import QLabel, QVBoxLayout -import src.globals as Globals - -class Group: - def __init__(self, id, name, column = "left", link = "", hidden = False): - self.id = id - self.name = name - self.column = column - self.link = link - self.hidden = hidden - - def buildLayout(self): - output = QVBoxLayout() - output.setContentsMargins(0, 10, 0, 10) - - name = QLabel(self.name) - name.setTextInteractionFlags(Qt.TextSelectableByMouse) - name_font = QFont("Arial", 13) - name_font.setUnderline(True) - name.setFont(name_font) - output.addWidget(name) - - return output diff --git a/src/main.py b/src/main.py deleted file mode 100644 index 88e0cda..0000000 --- a/src/main.py +++ /dev/null @@ -1,273 +0,0 @@ -#!/usr/bin/python3 -import os -import sys -import time -from PyQt5 import uic -from PyQt5.QtWidgets import QAction, QApplication, QMainWindow, QMenu, QMessageBox, QVBoxLayout -from PyQt5.QtGui import QCursor -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 -from src.rules_dialog import RulesDialog - -class AssignmentList(QMainWindow): - def __init__(self): - super().__init__() - uic.loadUi(os.path.join(os.path.dirname(os.path.abspath(__file__)), - "main.ui"), self) - - self.initializeUI() - - def initializeUI(self): - self.createMenu() - self.createToolbar() - Config() - self.setupDB() - self.displayWidgets() - self.show() - - def createMenu(self): - self.actionPreferences.triggered.connect(self.preferences) - self.actionReload.triggered.connect(self.reload) - self.actionExit.triggered.connect(self.close) - - self.actionAdd_Group.triggered.connect(self.addGroup) - self.actionClean_Hidden.triggered.connect(self.cleanHidden) - - self.actionAbout.triggered.connect(self.aboutDialog) - - def createToolbar(self): - self.toolBar.addAction(self.actionAdd_Group) - - def setupDB(self): - DB.initDB() - - def displayWidgets(self): - self.title.setText(time.strftime("%A, %b %d %Y")) - self.drawGroups() - - 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 editRules(self, id): - pass - need_reload = RulesDialog(id) - if need_reload: - self.reload() - - 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) - - rules_act = QAction("Rules") - rules_act.triggered.connect((lambda id: lambda: self.editRules(id))(entry_id)) - menu.addAction(rules_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 preferences(self): - # TODO not sure if this is working exactly how I think it does, but it works - need_reload = PreferencesDialog() - if need_reload: - self.reload() - - 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)) - - # Sort the rules - Globals.rules = sorted(Globals.rules, key=lambda r: (r.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 reload(self): - Config() - self.setupDB() - self.drawGroups() - - 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_()) - diff --git a/src/main.ui b/src/main.ui deleted file mode 100644 index bc3c0fb..0000000 --- a/src/main.ui +++ /dev/null @@ -1,207 +0,0 @@ - - - MainWindow - - - - 0 - 0 - 640 - 480 - - - - Assignment List - - - - - - - true - - - - - 0 - 0 - 620 - 421 - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - Arial - 17 - - - - [DATE] - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - 20 - - - 5 - - - 20 - - - 5 - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - - - - 0 - 0 - 640 - 22 - - - - - File - - - - - - - - - Edit - - - - - - - - Help - - - - - - - - - - toolBar - - - TopToolBarArea - - - false - - - - - Preferences - - - Alt+Return - - - - - Reload - - - F5 - - - - - Exit - - - Ctrl+Q - - - - - Add Group - - - - - Permanently Delete Removed Groups and Entries - - - - - About - - - - - - diff --git a/src/preferences_dialog.py b/src/preferences_dialog.py deleted file mode 100644 index e6d4187..0000000 --- a/src/preferences_dialog.py +++ /dev/null @@ -1,70 +0,0 @@ -import os -import sys -from PyQt5 import uic -from PyQt5.QtWidgets import QApplication, QDialog, QFileDialog -from src.config import Config - -class PreferencesDialog(QDialog): - """ - Implemented to set configuration options in the program - """ - def __init__(self): - super().__init__() - uic.loadUi(os.path.join("src", "preferences_dialog.ui"), self) - - # class globals - self.config = Config() - - self.initializeUI() - - def initializeUI(self): - self.displayWidgets() - self.exec() - - def displayWidgets(self): - # TODO make this a scrollable window - # FIXME could use some work on sizing - self.pathsTabLayout() - - self.close_button.clicked.connect(self.close) - self.apply_button.clicked.connect(self.apply) - self.reload_button.clicked.connect(self.reload) - - def pathsTabLayout(self): - if "paths" in self.config.config: - self.db_path_edit.setText(self.config.config["paths"]["db_path"]) - self.db_path_button.clicked.connect(self.dbPathDialog) - - def dbPathDialog(self): - file_dialog = QFileDialog(self) - # TODO create filter to only allow selecting .db files - new_path = file_dialog.getOpenFileName(self, "Open File") - - if new_path[0]: - self.db_path_edit.setText(new_path[0]) - - def apply(self): - """ - Update the configuration in the config file - """ - # Save paths - if "paths" in self.config.config: - try: - with open(self.db_path_edit.text(), 'a'): - self.config.config["paths"]["db_path"] = self.db_path_edit.text() - except: - print("Warning: db_path '{}' does not exist; skipping".format(self.db_path_edit.text())) - - self.config.updateConfig() - - def reload(self): - """ - Update, reload, and close the window - """ - self.apply() - self.done(1) - -if __name__ == "__main__": - app = QApplication(sys.argv) - window = PreferencesDialog() - sys.exit(app.exec_()) diff --git a/src/preferences_dialog.ui b/src/preferences_dialog.ui deleted file mode 100644 index 244eb26..0000000 --- a/src/preferences_dialog.ui +++ /dev/null @@ -1,128 +0,0 @@ - - - Dialog - - - - 0 - 0 - 500 - 320 - - - - Preferences - - - - - - - - 0 - - - - Paths - - - - - - - - - - - Database File: - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - - 25 - 16777215 - - - - ... - - - - - - - - - - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Close - - - - - - - Apply - - - - - - - Reload - - - - - - - - - - - - diff --git a/src/rule.py b/src/rule.py deleted file mode 100644 index 414ccf0..0000000 --- a/src/rule.py +++ /dev/null @@ -1,44 +0,0 @@ - -from PyQt5.QtCore import QDate -from PyQt5.QtWidgets import QComboBox, QDateTimeEdit, QHBoxLayout, QLineEdit - - -class Rule: - def __init__(self, id, entry_id, when, date, color = "", highlight = ""): - self.id = id - self.entry_id = entry_id - self.when = when - self.date = date - self.color = color - self.highlight = highlight - - def buildLayout(self): - output = QHBoxLayout() - - when_widget = QComboBox() - when_widget.addItems(["Before", "After"]) - when_widget.setCurrentIndex(0 if self.when.lower() == "before" else 1) - output.addWidget(when_widget) - - date_widget = QDateTimeEdit(QDate.currentDate()) - date_widget.setDisplayFormat("MM/dd/yyyy") - date_widget.setDate(self.date) - output.addWidget(date_widget) - - output.addStretch() - - # TODO Consider making this a color selector widget - color_widget = QLineEdit() - color_widget.setPlaceholderText("Color") - if self.color: - color_widget.setText(self.color) - output.addWidget(color_widget) - - # TODO Consider making this a color selector widget - highlight_widget = QLineEdit() - highlight_widget.setPlaceholderText("Highlight") - if self.highlight: - highlight_widget.setText(self.highlight) - output.addWidget(highlight_widget) - - return output diff --git a/src/rules_dialog.py b/src/rules_dialog.py deleted file mode 100644 index 0980fdd..0000000 --- a/src/rules_dialog.py +++ /dev/null @@ -1,128 +0,0 @@ -import sys -from PyQt5.QtCore import QDate -from PyQt5.QtWidgets import QApplication, QDialog, QHBoxLayout, QPushButton, QScrollArea, QVBoxLayout -from src.config import Config -from src.rule import Rule -import src.db_sqlite as DB -import src.globals as Globals - -class RulesDialog(QDialog): - """ - Show the list of rules associated with an entry - """ - def __init__(self, entry_id): - super().__init__() - - self.entry_id = entry_id - # class globals - self.config = Config() - self.relevant_rules = self.getRelevantRules() - - self.initializeUI() - - def initializeUI(self): - self.resize(500, 320) - self.setWindowTitle("Rules") - self.displayWidgets() - self.exec() - - def displayWidgets(self): - main_layout = QVBoxLayout() - main_layout_scroll_area = QScrollArea() - main_layout_scroll_area.setWidgetResizable(True) - main_layout_scroll_area.setLayout(main_layout) - - self.rules_layout = QVBoxLayout() - self.drawRules() - main_layout.addLayout(self.rules_layout) - - main_layout.addStretch() - # Create Close and Save buttons - buttons_hbox = QHBoxLayout() - buttons_hbox.addStretch() - - close_button = QPushButton("Close", self) - close_button.clicked.connect(self.close) - buttons_hbox.addWidget(close_button) - - save_button = QPushButton("Save", self) - save_button.clicked.connect(self.save) - buttons_hbox.addWidget(save_button) - - main_layout.addLayout(buttons_hbox) - self.setLayout(main_layout) - - def drawRules(self): - # 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.rules_layout) - - # Draw each rule - self.r_layouts_dict = {} # Use to help update things in the save() function - for r in self.relevant_rules: - r_layout = r.buildLayout() - self.r_layouts_dict[r.id] = r_layout - del_button = QPushButton("Delete", self) - del_button.clicked.connect((lambda id: lambda: self.deleteRule(id))(r.id)) - r_layout.addWidget(del_button) - self.rules_layout.addLayout(r_layout) - - # Draw a button to add rules - rules_buttons_hbox = QHBoxLayout() - add_rule_button = QPushButton("Add Rule", self) - add_rule_button.clicked.connect(self.addRule) - rules_buttons_hbox.addWidget(add_rule_button) - rules_buttons_hbox.addStretch() - self.rules_layout.addLayout(rules_buttons_hbox) - - def addRule(self): - self.apply() - - new_rule = Rule(0, self.entry_id, "before", QDate.currentDate()) - new_rule_id = DB.insertRule(new_rule) - new_rule.id = new_rule_id - Globals.rules.append(new_rule) - self.relevant_rules = self.getRelevantRules() - self.drawRules() - - def deleteRule(self, rule_id): - DB.removeRule(rule_id) - Globals.rules = list(filter(lambda r: r.id != rule_id, Globals.rules)) - self.relevant_rules = self.getRelevantRules() - self.drawRules() - - def getRelevantRules(self): - return list(filter(lambda r: r.entry_id == self.entry_id, Globals.rules)) - - def apply(self): - for id, layout in self.r_layouts_dict.items(): - updated_rule = Rule( - id, - self.entry_id, - layout.itemAt(0).widget().currentText(), - layout.itemAt(1).widget().date(), - layout.itemAt(3).widget().text(), - layout.itemAt(4).widget().text()) - DB.updateRule(updated_rule) - Globals.rules = list(filter(lambda r: r.id != id, Globals.rules)) - Globals.rules.append(updated_rule) - - def save(self): - """ - Save any existing rules. Added rules are automatically saved, - but changing rules is not, hence the need for a manual save - """ - self.apply() - self.done(1) - -if __name__ == "__main__": - app = QApplication(sys.argv) - window = RulesDialog() - sys.exit(app.exec_()) -- cgit