Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8ba9af8971 | ||
|
|
624ce61764 | ||
|
|
546297e763 | ||
|
|
3ce0f0ead9 | ||
|
|
16f6a2e2fd | ||
|
|
5930fd681d | ||
|
|
26a38d7db8 |
@@ -0,0 +1 @@
|
|||||||
|
PySide6~=6.0.0
|
||||||
140
src/QtLearning.py
Normal file
140
src/QtLearning.py
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from PySide6.QtCore import QStandardPaths
|
||||||
|
from PySide6.QtGui import QStandardItemModel, QStandardItem, QIcon
|
||||||
|
from PySide6.QtWidgets import QMainWindow, QApplication, QTreeView, QTextEdit, QSplitter, QSizePolicy, QMenuBar, QMenu
|
||||||
|
|
||||||
|
# TODO: Split this out into its own module for handling the FS operations in an abstract manner. We need to relocate
|
||||||
|
# all of this code up to the class.
|
||||||
|
NYTEWORKS_DIR = QStandardPaths.locate(QStandardPaths.AppDataLocation, 'NyteWoks', QStandardPaths.LocateDirectory)
|
||||||
|
if not NYTEWORKS_DIR:
|
||||||
|
# We don't already have a directory in the roaming area
|
||||||
|
NYTEWORKS_DIR = QStandardPaths.standardLocations(QStandardPaths.AppDataLocation)
|
||||||
|
if not NYTEWORKS_DIR:
|
||||||
|
# There are no suitable areas on the system to write our data to. Throw an error and let the user decide where
|
||||||
|
# to store the data if possible.
|
||||||
|
# TODO: Implement some sort of handling for this unfortunate situation...
|
||||||
|
print('Unable to find a suitable location for data on your system.', file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
else:
|
||||||
|
NYTEWORKS_DIR = Path(NYTEWORKS_DIR[0] + '/Nyteworks')
|
||||||
|
else:
|
||||||
|
NYTEWORKS_DIR = Path(NYTEWORKS_DIR)
|
||||||
|
|
||||||
|
# We have the parent directory, let's get the binder list
|
||||||
|
LIFEFLOW_DIR = NYTEWORKS_DIR / 'LifeFlow'
|
||||||
|
BINDERS_DIR = LIFEFLOW_DIR / 'Binders'
|
||||||
|
|
||||||
|
# Ensure that the binder directory exists
|
||||||
|
BINDERS_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# Some business logic that is currently necessary...
|
||||||
|
# TODO: Find a more elegant way to handle the Inbox folder
|
||||||
|
INBOX_DIR = BINDERS_DIR / 'Inbox'
|
||||||
|
Path(INBOX_DIR).mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
|
class MainWindow(QMainWindow):
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super(MainWindow, self).__init__(parent)
|
||||||
|
self.setWindowTitle('LifeFlow - Notes for the Real World')
|
||||||
|
self._create_ui()
|
||||||
|
|
||||||
|
def _create_ui(self):
|
||||||
|
self._setup_menu_bar()
|
||||||
|
self._create_note_tree_view()
|
||||||
|
self.text_area = QTextEdit()
|
||||||
|
self.setCentralWidget(self.note_tree_view)
|
||||||
|
|
||||||
|
self.note_tree_view.setSizePolicy(QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding))
|
||||||
|
self.text_area.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))
|
||||||
|
|
||||||
|
self.note_tree_view.clicked.connect(self.selected_note_changed)
|
||||||
|
|
||||||
|
self.splitter = QSplitter()
|
||||||
|
self.splitter.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))
|
||||||
|
self.splitter.addWidget(self.note_tree_view)
|
||||||
|
self.splitter.addWidget(self.text_area)
|
||||||
|
|
||||||
|
self.splitter.setSizes([self.note_tree_view.sizeHint().width(), 1000])
|
||||||
|
|
||||||
|
self.setCentralWidget(self.splitter)
|
||||||
|
|
||||||
|
def _setup_menu_bar(self):
|
||||||
|
menuBar = self.menuBar()
|
||||||
|
|
||||||
|
fileMenu = menuBar.addMenu('&File')
|
||||||
|
fileMenu.addAction('Open')
|
||||||
|
fileMenu.addAction('New')
|
||||||
|
fileMenu.addAction('Save')
|
||||||
|
fileMenu.addAction('Save As...')
|
||||||
|
|
||||||
|
editMenu = menuBar.addMenu('&Edit')
|
||||||
|
editMenu.addAction('Undo')
|
||||||
|
editMenu.addAction('Redo')
|
||||||
|
editMenu.addSeparator()
|
||||||
|
editMenu.addAction('Cut')
|
||||||
|
editMenu.addAction('Copy')
|
||||||
|
editMenu.addAction('Paste')
|
||||||
|
|
||||||
|
def _create_note_tree_view(self):
|
||||||
|
self.note_tree_view = QTreeView()
|
||||||
|
self.note_tree_view.setHeaderHidden(True)
|
||||||
|
self.note_tree_view.setModel(self._create_note_model())
|
||||||
|
|
||||||
|
def _create_note_model(self):
|
||||||
|
self.note_tree_model = QStandardItemModel()
|
||||||
|
root = self.note_tree_model.invisibleRootItem()
|
||||||
|
|
||||||
|
# TODO: Investigate what else pathlib might be able to do for us better here.
|
||||||
|
# TODO: Right now the note hierarchy is hard-coded. We need a more generic recursion code and need to consider
|
||||||
|
# how the database will be implemented so that other views fit in the hierarchy.
|
||||||
|
binders = [binder for binder in BINDERS_DIR.iterdir() if binder.is_dir()]
|
||||||
|
for binder in binders:
|
||||||
|
binder_name = binder.parts[-1]
|
||||||
|
binder_item = QStandardItem(binder_name)
|
||||||
|
binder_item.setData(False, 1)
|
||||||
|
notebooks = [notebook for notebook in binder.iterdir() if notebook.is_dir()]
|
||||||
|
for notebook in notebooks:
|
||||||
|
notebook_name = notebook.parts[-1]
|
||||||
|
notebook_item = QStandardItem(notebook_name)
|
||||||
|
notebook_item.setData(False, 1)
|
||||||
|
notes = [note for note in notebook.iterdir() if note.is_file()]
|
||||||
|
for note in notes:
|
||||||
|
note_name = note.parts[-1]
|
||||||
|
note_item = QStandardItem(note_name)
|
||||||
|
note_item.is_note = True
|
||||||
|
note_item.setData(True, 1)
|
||||||
|
notebook_item.appendRow(note_item)
|
||||||
|
binder_item.appendRow(notebook_item)
|
||||||
|
root.appendRow(binder_item)
|
||||||
|
|
||||||
|
return self.note_tree_model
|
||||||
|
|
||||||
|
def selected_note_changed(self, index):
|
||||||
|
item = index.model().itemFromIndex(index)
|
||||||
|
if item.data(1):
|
||||||
|
notebook = item.parent().text()
|
||||||
|
binder = item.parent().parent().text()
|
||||||
|
note = BINDERS_DIR / binder / notebook / item.text()
|
||||||
|
print('I should get:', note)
|
||||||
|
note_text = None
|
||||||
|
with open(note, 'r') as note_file:
|
||||||
|
note_text = note_file.readlines()
|
||||||
|
self.text_area.setText(''.join(note_text))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
app = QApplication()
|
||||||
|
|
||||||
|
window = MainWindow()
|
||||||
|
window.show()
|
||||||
|
|
||||||
|
sys.exit(app.exec_())
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
94
src/checklist_view.py
Normal file
94
src/checklist_view.py
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
"""
|
||||||
|
考えろ、バカ:
|
||||||
|
- Checklists will have a tree structure.
|
||||||
|
- The parent nodes are checklists.
|
||||||
|
- Leaf nodes are checklist items.
|
||||||
|
- If a parent task's status is changed to completed, all of its children will be changed as well.
|
||||||
|
- If all of a parent task's children tasks are completed, it will also be automatically completed.
|
||||||
|
- If one of a completed parent task's child tasks is un-completed, the parent task will be as well.
|
||||||
|
|
||||||
|
- Until it's discussed more, I'll treat the schedule variable as the number of days until which you want the list
|
||||||
|
to reset.
|
||||||
|
|
||||||
|
CURRENT SETBACKS:
|
||||||
|
- There might be unfortunate times whenever you accidentally complete a parent task, subsequently completing
|
||||||
|
all of the subtasks. In which case, it might be impossible to auto 'un-complete' the subtasks.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class ChecklistItem:
|
||||||
|
def __init__(self, task=None):
|
||||||
|
self.task = task
|
||||||
|
self.parent = None
|
||||||
|
self.children = []
|
||||||
|
self.completed = False
|
||||||
|
|
||||||
|
def add_child(self, sub_task):
|
||||||
|
sub_task.set_parent(self)
|
||||||
|
self.children.append(sub_task)
|
||||||
|
|
||||||
|
def mark_task(self):
|
||||||
|
self.completed = True
|
||||||
|
|
||||||
|
def unmark_task(self):
|
||||||
|
self.completed = False
|
||||||
|
|
||||||
|
def set_parent(self, parent):
|
||||||
|
self.parent = parent
|
||||||
|
|
||||||
|
def get_task(self):
|
||||||
|
return self.task
|
||||||
|
|
||||||
|
def get_completion(self):
|
||||||
|
return self.completed
|
||||||
|
|
||||||
|
def get_parent(self):
|
||||||
|
return self.parent
|
||||||
|
|
||||||
|
|
||||||
|
class Checklist:
|
||||||
|
def __init__(self, schedule=0):
|
||||||
|
self.root = ChecklistItem()
|
||||||
|
self.schedule = schedule
|
||||||
|
|
||||||
|
|
||||||
|
def print_checklist(root):
|
||||||
|
for item in root.children:
|
||||||
|
|
||||||
|
temp = root
|
||||||
|
while temp.get_parent() is not None:
|
||||||
|
temp = temp.get_parent()
|
||||||
|
print(" ", end="")
|
||||||
|
|
||||||
|
if item.get_completion():
|
||||||
|
print("x " + item.get_task())
|
||||||
|
else:
|
||||||
|
print("o " + item.get_task())
|
||||||
|
|
||||||
|
print_checklist(item)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
test_checklist = Checklist()
|
||||||
|
|
||||||
|
"""
|
||||||
|
As a proof of concept, I'll make a checklist similar to the following, x is complete, o is incomplete:
|
||||||
|
Backpack Checklist
|
||||||
|
o back pocket
|
||||||
|
x laptop
|
||||||
|
o げんき third edition Japanese textbook and workbook with bonus learning exercises
|
||||||
|
o front pocket
|
||||||
|
o pens
|
||||||
|
o headphones
|
||||||
|
"""
|
||||||
|
|
||||||
|
test_checklist.root.add_child(ChecklistItem("back pocket"))
|
||||||
|
test_checklist.root.children[0].add_child(ChecklistItem("laptop"))
|
||||||
|
test_checklist.root.children[0].children[0].mark_task()
|
||||||
|
test_checklist.root.children[0].add_child(ChecklistItem("げんき third edition Japanese textbook and workbook with "
|
||||||
|
"bonus learning exercises"))
|
||||||
|
test_checklist.root.add_child(ChecklistItem("front pocket"))
|
||||||
|
test_checklist.root.children[1].add_child(ChecklistItem("pens"))
|
||||||
|
test_checklist.root.children[1].add_child(ChecklistItem("headphones"))
|
||||||
|
|
||||||
|
print_checklist(test_checklist.root)
|
||||||
134
src/notes_view.py
Normal file
134
src/notes_view.py
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import os.path
|
||||||
|
import appdirs
|
||||||
|
import shutil
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
from os.path import join
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
BASE_LOCATION = appdirs.user_data_dir("LifeFlow", "NyteWorks", "0.1.0")
|
||||||
|
NOTES_FOLDER = join(BASE_LOCATION, "Notes")
|
||||||
|
|
||||||
|
#TODO: In the future, the user will be able to change which folder is the default for QuickNotes
|
||||||
|
QUICK_NOTES_FOLDER = join(NOTES_FOLDER, "Inbox")
|
||||||
|
|
||||||
|
if not os.path.exists(BASE_LOCATION):
|
||||||
|
os.makedirs(BASE_LOCATION, exist_ok=True)
|
||||||
|
if not os.path.exists(NOTES_FOLDER):
|
||||||
|
os.makedirs(NOTES_FOLDER, exist_ok=True)
|
||||||
|
if not os.path.exists(QUICK_NOTES_FOLDER):
|
||||||
|
os.makedirs(QUICK_NOTES_FOLDER, exist_ok=True)
|
||||||
|
|
||||||
|
current_notebook = "Inbox"
|
||||||
|
all_notebooks = []
|
||||||
|
for notebook in os.listdir(NOTES_FOLDER):
|
||||||
|
all_notebooks.append(notebook)
|
||||||
|
|
||||||
|
def list_notes(args):
|
||||||
|
if len(args) == 0:
|
||||||
|
print("You have the following notebooks:")
|
||||||
|
notebooks = os.listdir(NOTES_FOLDER)
|
||||||
|
for notebook in notebooks:
|
||||||
|
print("*", notebook)
|
||||||
|
else:
|
||||||
|
notebook = args[0]
|
||||||
|
print("These are the notes in", notebook)
|
||||||
|
notes = os.listdir(join(NOTES_FOLDER, notebook))
|
||||||
|
for note in notes:
|
||||||
|
note = ".".join(note.split(".")[:-1])
|
||||||
|
print(note)
|
||||||
|
|
||||||
|
def select_notebook(args):
|
||||||
|
global all_notebooks
|
||||||
|
global current_notebook
|
||||||
|
if args[0] in all_notebooks:
|
||||||
|
current_notebook = args[0]
|
||||||
|
|
||||||
|
def create_notebook(args):
|
||||||
|
global all_notebooks
|
||||||
|
global current_notebook
|
||||||
|
if len(args) < 1:
|
||||||
|
print("You need a name for your new notebook!", file=sys.stderr)
|
||||||
|
return
|
||||||
|
if not os.path.exists(join(NOTES_FOLDER, args[0])):
|
||||||
|
os.makedirs(join(NOTES_FOLDER, args[0]), exist_ok=True)
|
||||||
|
all_notebooks.append(args[0])
|
||||||
|
current_notebook = args[0]
|
||||||
|
|
||||||
|
def delete(args):
|
||||||
|
global current_notebook
|
||||||
|
global all_notebooks
|
||||||
|
if len(args) < 1:
|
||||||
|
print("Unable to delete nothing.", file=sys.stderr)
|
||||||
|
return
|
||||||
|
if args[0] == 'notebook':
|
||||||
|
if len(args) < 2:
|
||||||
|
print("You need to select a notebook to delete.")
|
||||||
|
else:
|
||||||
|
if args[1] == 'Inbox':
|
||||||
|
print("You cannot delete the Inbox.")
|
||||||
|
return
|
||||||
|
elif args[1] == current_notebook:
|
||||||
|
current_notebook = "Inbox"
|
||||||
|
path = join(NOTES_FOLDER, args[1])
|
||||||
|
if not os.path.exists(path):
|
||||||
|
print("That notebook does not exist.")
|
||||||
|
return
|
||||||
|
print("CHECKING:", path)
|
||||||
|
shutil.rmtree(path)
|
||||||
|
print("Deleted notebook", args[1])
|
||||||
|
else:
|
||||||
|
path = join(NOTES_FOLDER, current_notebook, args[0] + ".txt")
|
||||||
|
print("CHECKING:", path)
|
||||||
|
if not os.path.exists(path):
|
||||||
|
print("That note does not exist.")
|
||||||
|
return
|
||||||
|
os.remove(path)
|
||||||
|
print("Deleted", args[0])
|
||||||
|
|
||||||
|
def add_note(arguments):
|
||||||
|
global current_notebook
|
||||||
|
if current_notebook is None:
|
||||||
|
current_notebook = "Inbox"
|
||||||
|
if len(arguments) == 0:
|
||||||
|
filename = '{date:%Y-%m-%d_%H:%M:%S}.txt'.format(date=datetime.datetime.now())
|
||||||
|
else:
|
||||||
|
#TODO: We need to sanitize this for legal filename
|
||||||
|
filename = " ".join(arguments) + ".txt"
|
||||||
|
filename = join(NOTES_FOLDER, current_notebook, filename)
|
||||||
|
if not os.path.exists(filename):
|
||||||
|
filepath = Path(filename)
|
||||||
|
filepath.touch()
|
||||||
|
os.startfile(join(NOTES_FOLDER, current_notebook, filename))
|
||||||
|
|
||||||
|
def run_tui():
|
||||||
|
global current_notebook
|
||||||
|
valid_commands = {
|
||||||
|
"list": list_notes,
|
||||||
|
"select": select_notebook,
|
||||||
|
"create": create_notebook,
|
||||||
|
"delete": delete,
|
||||||
|
"add": add_note,
|
||||||
|
"quit": exit,
|
||||||
|
}
|
||||||
|
print("Welcome to LifeFlow Notes")
|
||||||
|
print("-------------------------")
|
||||||
|
print()
|
||||||
|
command = None
|
||||||
|
while(command != "quit"):
|
||||||
|
if current_notebook is None:
|
||||||
|
current_notebook = "Inbox"
|
||||||
|
print("You are currently in notebook:", current_notebook)
|
||||||
|
command_parts = input("> ").strip().lower().split(" ")
|
||||||
|
arguments = command_parts[1:]
|
||||||
|
command = command_parts[0]
|
||||||
|
if command not in valid_commands:
|
||||||
|
print("Invalid command. Please input one of: ", file=sys.stderr)
|
||||||
|
for key in valid_commands.keys():
|
||||||
|
print("\t", key)
|
||||||
|
else:
|
||||||
|
valid_commands[command](arguments)
|
||||||
|
if __name__ == '__main__':
|
||||||
|
run_tui()
|
||||||
Reference in New Issue
Block a user