From 9c79d017ffb9ce501ba0ae3218f2cfa612e88fae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Mart=C3=ADnez=20Portela?= Date: Sat, 20 Mar 2021 19:44:01 +0100 Subject: [PATCH 1/9] Initial Kivy test. --- main.py | 60 ++++++++++++++++++++++++++++++++++++++++++++++++ requirements.txt | 1 + 2 files changed, 61 insertions(+) create mode 100644 main.py create mode 100644 requirements.txt diff --git a/main.py b/main.py new file mode 100644 index 0000000..6cdcac7 --- /dev/null +++ b/main.py @@ -0,0 +1,60 @@ +from kivy.app import App +from kivy.core.window import Window +from kivy.modules import inspector +from kivy.uix.boxlayout import BoxLayout +from kivy.uix.button import Button +from kivy.uix.gridlayout import GridLayout +from kivy.uix.scrollview import ScrollView +from kivy.uix.textinput import TextInput + + +class TestApp(App): + def __init__(self): + App.__init__(self) + self.results = [] + + def on_omnibox_change(self, _instance, value): + print("⇒", value) + while self.results: + btn = self.results.pop() + self.layout.remove_widget(btn) + + for i in range(100): + btn = Button(text=value + str(i), height=40, size_hint_y=None) + self.layout.add_widget(btn) + self.results.append(btn) + + def build(self): + self.root = BoxLayout(orientation="vertical", spacing=5) + # Make sure the height is such that there is something to scroll. + self.root.bind(minimum_height=self.root.setter("height")) + + self.omnibox = TextInput( + text="", + hint_text="Search here files and headlines", + multiline=False, + write_tab=False, + size_hint_y=None, + height=40, + focus=False, + ) + self.omnibox.bind(text=self.on_omnibox_change) + self.root.add_widget(self.omnibox) + + self.viewer = ScrollView( + size_hint=(1, 1), size=(Window.width / 2, Window.height / 2) + ) + self.layout = BoxLayout(orientation="vertical", spacing=2, size_hint_y=None) + self.layout.bind(minimum_height=self.layout.setter("height")) + for i in range(100): + btn = Button(text=str(i), height=40, size_hint_y=None) + self.layout.add_widget(btn) + self.results.append(btn) + + self.viewer.add_widget(self.layout) + self.root.add_widget(self.viewer) + inspector.create_inspector(Window, self.root) + return self.root + + +TestApp().run() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..c098fc2 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +kivy[base] From 97a63380c6457535293f554727d256d04e35dae9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Mart=C3=ADnez=20Portela?= Date: Sat, 3 Apr 2021 00:16:06 +0200 Subject: [PATCH 2/9] Initial commit. --- .gitignore | 1 + main.py | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 .gitignore create mode 100755 main.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bee8a64 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__pycache__ diff --git a/main.py b/main.py new file mode 100755 index 0000000..4f12d2c --- /dev/null +++ b/main.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python3 + +import logging +import os +import sys +import time + +from PySide2.QtCore import QObject, QThread, Signal, Slot +from PySide2.QtWidgets import (QApplication, QDialog, QGroupBox, QHBoxLayout, + QLabel, QLineEdit, QProgressBar, QPushButton, + QScrollArea, QTabBar, QVBoxLayout) + +ORG_PATH = os.environ['ORG_PATH'] + + +class LoadDoneSignal(QObject): + sig = Signal(str) + + +class DocumentLoader(QThread): + def __init__(self, parent = None): + QThread.__init__(self, parent) + self.exiting = False + self.signal = LoadDoneSignal() + + def run(self): + end = time.time() + 3 + while self.exiting==False: + sys.stdout.write('*') + sys.stdout.flush() + time.sleep(1) + now = time.time() + if now >= end: + self.exiting = True + self.signal.sig.emit('OK') + + +class Dialog(QDialog): + def __init__(self): + super(Dialog, self).__init__() + + self.loader = None + + layout = QVBoxLayout() + + # Edit box + self.progressBar = QProgressBar() + self.progressBar.setRange(0, 0) # Make undetermined + layout.addWidget(self.progressBar) + + self.edit = QLineEdit("", placeholderText='Search for notes') + layout.addWidget(self.edit) + + layout.setSpacing(0) + + self.results = QScrollArea() + layout.addWidget(self.results) + + # Options + self.tabBar = QTabBar(shape=QTabBar.RoundedSouth) + + self.tabBar.addTab("Agenda") + self.tabBar.addTab("Notes") + self.tabBar.addTab("Tasks") + self.tabBar.currentChanged.connect(self.update_tab) + + layout.addWidget(self.tabBar) + + self.setLayout(layout) + self.startLoad() + + @Slot() + def update_tab(self): + tabIndex = self.tabBar.currentIndex() + if tabIndex == 0: + self.loadAgenda() + elif tabIndex == 1: + self.loadNotes() + elif tabIndex == 2: + self.loadTasks() + + def startLoad(self): + self.edit.setDisabled(True) + self.tabBar.setDisabled(True) + self.progressBar.setVisible(True) + + self.loader = DocumentLoader() + self.loader.signal.sig.connect(self.longoperationcomplete) + self.loader.start() + + def endLoad(self): + self.edit.setDisabled(False) + self.tabBar.setDisabled(False) + self.progressBar.setVisible(False) + + self.update_tab() + + def longoperationcomplete(self, data): + print("Complete with", data) + self.endLoad() + + def loadAgenda(self): + logging.warning("loadAgenda not yet implemented") + + def loadNotes(self): + logging.warning("loadNotes not yet implemented") + + def loadTasks(self): + logging.warning("loadTasks not yet implemented") + +# Create the Qt Application +app = QApplication(sys.argv) + +dialog = Dialog() +sys.exit(dialog.exec_()) From 6a71f342f09ea3b7a4b4a978348e7badcd8a7069 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Mart=C3=ADnez=20Portela?= Date: Sat, 3 Apr 2021 01:13:41 +0200 Subject: [PATCH 3/9] Add base functionality: Show agenda. --- .gitignore | 1 + doc_manager.py | 106 +++++++++++++++++++++++++++++++++++++++++++++++++ main.py | 79 +++++++++++++++++++++++++----------- 3 files changed, 163 insertions(+), 23 deletions(-) create mode 100644 doc_manager.py diff --git a/.gitignore b/.gitignore index bee8a64..083d732 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ __pycache__ +.idea diff --git a/doc_manager.py b/doc_manager.py new file mode 100644 index 0000000..fe1229d --- /dev/null +++ b/doc_manager.py @@ -0,0 +1,106 @@ +import logging +import os +import sys +from datetime import datetime +from typing import List + +import org_rw +from org_rw import OrgTime + + +def is_today(ot: OrgTime): + now = datetime.now() + return ( + (ot.time.year == now.year) + and (ot.time.month == now.month) + and (ot.time.day == now.day) + ) + + +class Agenda: + def __init__(self, /, + with_hour: List[org_rw.Headline], + no_hour: List[org_rw.Headline], + ): + self.with_hour = with_hour + self.no_hour = no_hour + + def print(self): + for item in self.with_hour: + print(item.scheduled.time, item.state, item.title) + + if len(self.with_hour) > 0: + print("--------") + + for item in self.no_hour: + print(item.scheduled.time, item.state, item.title) + + +class DocumentManager: + def __init__(self, basepath): + self.basepath = basepath + + def load(self): + top = os.path.abspath(self.basepath) + + docs = [] + + for root, dirs, files in os.walk(top): + for name in files: + if ".org" not in name: + continue + + path = os.path.join(root, name) + + try: + doc = org_rw.load(open(path), extra_cautious=True) + docs.append(doc) + except Exception as err: + import traceback + + traceback.print_exc() + print(f"== On {path}") + sys.exit(1) + + logging.info("Loaded {} files".format(len(docs))) + + self.docs = docs + + def get_agenda(self) -> Agenda: + + headline_count = 0 + items_in_agenda = [] + now = datetime.now() + + for doc in self.docs: + for hl in doc.getAllHeadlines(): + headline_count += 1 + + if hl.scheduled and isinstance(hl.scheduled, OrgTime): + if is_today(hl.scheduled): + items_in_agenda.append(hl) + elif (hl.scheduled.time.to_datetime() < now) and hl.is_todo: + items_in_agenda.append(hl) + + logging.info("Read {} items".format(headline_count)) + logging.info("{} items in agenda today".format(len(items_in_agenda))) + + items_with_hour = [ + item + for item in items_in_agenda + if item.scheduled and is_today(item.scheduled) and item.scheduled.time.hour + ] + other_items = [ + item + for item in items_in_agenda + if not ( + item.scheduled and is_today(item.scheduled) and item.scheduled.time.hour + ) + ] + + logging.info("{} items today for a specific hour".format(len(items_with_hour))) + + return Agenda( + with_hour=sorted(items_with_hour, key=lambda x: x.scheduled.time), + no_hour=other_items + ) diff --git a/main.py b/main.py index 4f12d2c..de08f09 100755 --- a/main.py +++ b/main.py @@ -8,31 +8,25 @@ import time from PySide2.QtCore import QObject, QThread, Signal, Slot from PySide2.QtWidgets import (QApplication, QDialog, QGroupBox, QHBoxLayout, QLabel, QLineEdit, QProgressBar, QPushButton, - QScrollArea, QTabBar, QVBoxLayout) + QScrollArea, QTabBar, QVBoxLayout, QSplitter, QFrame) -ORG_PATH = os.environ['ORG_PATH'] +import doc_manager + +DOCS_PATH = os.environ['ORG_PATH'] class LoadDoneSignal(QObject): - sig = Signal(str) - + sig = Signal(doc_manager.DocumentManager) class DocumentLoader(QThread): - def __init__(self, parent = None): - QThread.__init__(self, parent) - self.exiting = False + def __init__(self, manager): + QThread.__init__(self, None) + self.manager = manager self.signal = LoadDoneSignal() def run(self): - end = time.time() + 3 - while self.exiting==False: - sys.stdout.write('*') - sys.stdout.flush() - time.sleep(1) - now = time.time() - if now >= end: - self.exiting = True - self.signal.sig.emit('OK') + self.manager.load() + self.signal.sig.emit(self.manager) class Dialog(QDialog): @@ -40,6 +34,7 @@ class Dialog(QDialog): super(Dialog, self).__init__() self.loader = None + self.manager = doc_manager.DocumentManager(DOCS_PATH) layout = QVBoxLayout() @@ -53,7 +48,7 @@ class Dialog(QDialog): layout.setSpacing(0) - self.results = QScrollArea() + self.results = QScrollArea(widgetResizable=True) layout.addWidget(self.results) # Options @@ -81,26 +76,61 @@ class Dialog(QDialog): def startLoad(self): self.edit.setDisabled(True) + self.edit.setVisible(False) self.tabBar.setDisabled(True) self.progressBar.setVisible(True) - self.loader = DocumentLoader() + self.loader = DocumentLoader(self.manager) self.loader.signal.sig.connect(self.longoperationcomplete) + self.loading_start_time = time.time() self.loader.start() def endLoad(self): self.edit.setDisabled(False) + self.edit.setVisible(True) self.tabBar.setDisabled(False) self.progressBar.setVisible(False) self.update_tab() def longoperationcomplete(self, data): - print("Complete with", data) + logging.info("Loading complete in {:.3f}s".format(time.time() - self.loading_start_time)) self.endLoad() def loadAgenda(self): - logging.warning("loadAgenda not yet implemented") + agenda = self.manager.get_agenda() + old = self.results.layout() + + if old: + print("Deleting old") + old.deleteLater() + + layout = QVBoxLayout() + + for item in agenda.with_hour: + text = "{} {} {}".format( + item.scheduled.time, + item.state, + item.title, + ) + label = QLabel(text=text) + layout.addWidget(label) + + # if len(agenda.with_hour) > 0 and len(agenda.no_hour) > 0: + # layout.addWidget(QSplitter()) + + for item in agenda.no_hour: + text = "{} {} {}".format( + item.scheduled.time, + item.state, + item.title, + ) + label = QLabel(text=text) + layout.addWidget(label) + + frame = QFrame(self.results) + frame.setLayout(layout) + self.results.setWidget(frame) def loadNotes(self): logging.warning("loadNotes not yet implemented") @@ -109,7 +139,10 @@ class Dialog(QDialog): logging.warning("loadTasks not yet implemented") # Create the Qt Application -app = QApplication(sys.argv) +if __name__ == '__main__': + logging.basicConfig(level=logging.INFO, format="%(levelname)-8s %(message)s") -dialog = Dialog() -sys.exit(dialog.exec_()) + app = QApplication(sys.argv) + + dialog = Dialog() + sys.exit(dialog.exec_()) From 74cb24ae973a484e1d93a7b520da01e0db449adb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Mart=C3=ADnez=20Portela?= Date: Sat, 3 Apr 2021 01:29:17 +0200 Subject: [PATCH 4/9] Add kinetic scrolling. --- main.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/main.py b/main.py index de08f09..089edd9 100755 --- a/main.py +++ b/main.py @@ -6,9 +6,8 @@ import sys import time from PySide2.QtCore import QObject, QThread, Signal, Slot -from PySide2.QtWidgets import (QApplication, QDialog, QGroupBox, QHBoxLayout, - QLabel, QLineEdit, QProgressBar, QPushButton, - QScrollArea, QTabBar, QVBoxLayout, QSplitter, QFrame) +from PySide2.QtWidgets import (QApplication, QDialog, QLabel, QLineEdit, QProgressBar, QScrollArea, QTabBar, + QVBoxLayout, QFrame, QScroller) import doc_manager @@ -50,6 +49,9 @@ class Dialog(QDialog): self.results = QScrollArea(widgetResizable=True) layout.addWidget(self.results) + QScroller.grabGesture( + self.results.viewport(), QScroller.LeftMouseButtonGesture, + ) # Options self.tabBar = QTabBar(shape=QTabBar.RoundedSouth) From b126e178b119f999bd2e0d286d4c8e0b37e1709d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Mart=C3=ADnez=20Portela?= Date: Sat, 3 Apr 2021 01:35:34 +0200 Subject: [PATCH 5/9] Add window title. --- main.py | 1 + 1 file changed, 1 insertion(+) diff --git a/main.py b/main.py index 089edd9..418546c 100755 --- a/main.py +++ b/main.py @@ -32,6 +32,7 @@ class Dialog(QDialog): def __init__(self): super(Dialog, self).__init__() + self.setWindowTitle("OrgEditor") self.loader = None self.manager = doc_manager.DocumentManager(DOCS_PATH) From f3e1573677d8786426d5cebb116a5040f69d17bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Mart=C3=ADnez=20Portela?= Date: Sat, 3 Apr 2021 01:47:33 +0200 Subject: [PATCH 6/9] Minor cleanup in document loader. --- doc_manager.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/doc_manager.py b/doc_manager.py index fe1229d..8e53025 100644 --- a/doc_manager.py +++ b/doc_manager.py @@ -5,8 +5,9 @@ from datetime import datetime from typing import List import org_rw -from org_rw import OrgTime +from org_rw import OrgTime, OrgDoc +EXTENSIONS = ( ".org", ".org.txt" ) def is_today(ot: OrgTime): now = datetime.now() @@ -37,17 +38,19 @@ class Agenda: class DocumentManager: - def __init__(self, basepath): - self.basepath = basepath + docs: list[OrgDoc] + + def __init__(self, base_path: os.PathLike): + self.base_path = base_path def load(self): - top = os.path.abspath(self.basepath) + top = os.path.abspath(self.base_path) docs = [] for root, dirs, files in os.walk(top): for name in files: - if ".org" not in name: + if all(map(lambda ext: not name.endswith(ext), EXTENSIONS)): continue path = os.path.join(root, name) From 2196c13b147536ea546fb10c92f0dc63fae320e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Mart=C3=ADnez=20Portela?= Date: Sat, 3 Apr 2021 01:47:58 +0200 Subject: [PATCH 7/9] Try to find a reasonable size when starting application. --- main.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/main.py b/main.py index 418546c..7128930 100755 --- a/main.py +++ b/main.py @@ -33,6 +33,9 @@ class Dialog(QDialog): super(Dialog, self).__init__() self.setWindowTitle("OrgEditor") + scrSize = self.screen().size() + self.resize(scrSize.width() / 1.5, + scrSize.height() / 1.5) self.loader = None self.manager = doc_manager.DocumentManager(DOCS_PATH) From df5931aeb7e252a41067459f1007bce7da3391a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Mart=C3=ADnez=20Portela?= Date: Tue, 14 Sep 2021 23:02:40 +0200 Subject: [PATCH 8/9] Save changes. --- doc_manager.py | 11 +++- main.py | 51 ++++++++++++----- org-mode.svg | 149 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 195 insertions(+), 16 deletions(-) create mode 100644 org-mode.svg diff --git a/doc_manager.py b/doc_manager.py index 8e53025..edd4e05 100644 --- a/doc_manager.py +++ b/doc_manager.py @@ -5,7 +5,7 @@ from datetime import datetime from typing import List import org_rw -from org_rw import OrgTime, OrgDoc +from org_rw import OrgDoc, OrgTime EXTENSIONS = ( ".org", ".org.txt" ) @@ -49,6 +49,15 @@ class DocumentManager: docs = [] for root, dirs, files in os.walk(top): + # Prune dirs + i = 0 + while i < len(dirs): + if dirs[i].startswith('.git'): + del dirs[i] + else: + i += 1 + + # Process files for name in files: if all(map(lambda ext: not name.endswith(ext), EXTENSIONS)): continue diff --git a/main.py b/main.py index 7128930..12eaf34 100755 --- a/main.py +++ b/main.py @@ -7,7 +7,7 @@ import time from PySide2.QtCore import QObject, QThread, Signal, Slot from PySide2.QtWidgets import (QApplication, QDialog, QLabel, QLineEdit, QProgressBar, QScrollArea, QTabBar, - QVBoxLayout, QFrame, QScroller) + QVBoxLayout, QFrame, QScroller, QHBoxLayout, QPushButton, QGroupBox) import doc_manager @@ -47,6 +47,7 @@ class Dialog(QDialog): layout.addWidget(self.progressBar) self.edit = QLineEdit("", placeholderText='Search for notes') + self.edit.textEdited.connect(self.on_text_edited) layout.addWidget(self.edit) layout.setSpacing(0) @@ -70,6 +71,10 @@ class Dialog(QDialog): self.setLayout(layout) self.startLoad() + @Slot() + def on_text_edited(self): + self.tabBar.setCurrentIndex(1) + @Slot() def update_tab(self): tabIndex = self.tabBar.currentIndex() @@ -114,30 +119,46 @@ class Dialog(QDialog): layout = QVBoxLayout() for item in agenda.with_hour: - text = "{} {} {}".format( - item.scheduled.time, - item.state, - item.title, - ) - label = QLabel(text=text) - layout.addWidget(label) + layout.addWidget(self.build_agenda_task_widget(item)) # if len(agenda.with_hour) > 0 and len(agenda.no_hour) > 0: # layout.addWidget(QSplitter()) for item in agenda.no_hour: - text = "{} {} {}".format( - item.scheduled.time, - item.state, - item.title, - ) - label = QLabel(text=text) - layout.addWidget(label) + layout.addWidget(self.build_agenda_task_widget(item)) frame = QFrame(self.results) frame.setLayout(layout) self.results.setWidget(frame) + def build_agenda_task_widget(self, item): + box = QHBoxLayout() + frame = QGroupBox() + frame.setLayout(box) + + state_button = QPushButton(text=f"{item.state or '-'}", maximumWidth=60) + if item.is_done: + state_button.setFlat(True) + box.addWidget(state_button) + + box.addWidget( + QLabel(text=f"{item.scheduled.time}", maximumWidth=200) + ) + box.addWidget( + QLabel(text=f"{item.title}") + ) + + def on_clicked(): + state_button.setText('DONE') + # state_button.setFlat(True) + # item.state = 'DONE' + + if not item.is_done: + state_button.clicked.connect(on_clicked) + + + return frame + def loadNotes(self): logging.warning("loadNotes not yet implemented") diff --git a/org-mode.svg b/org-mode.svg new file mode 100644 index 0000000..917cf3a --- /dev/null +++ b/org-mode.svg @@ -0,0 +1,149 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + From a8e667dbce3926a010bce73dbacbea7e0619306b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Mart=C3=ADnez=20Portela?= Date: Wed, 15 Sep 2021 00:04:39 +0200 Subject: [PATCH 9/9] Add base note searching functionality. --- doc_manager.py | 40 ++++++++++++++++++++++------ main.py | 71 +++++++++++++++++++++++++++++++++++++------------- 2 files changed, 85 insertions(+), 26 deletions(-) diff --git a/doc_manager.py b/doc_manager.py index edd4e05..32d5a99 100644 --- a/doc_manager.py +++ b/doc_manager.py @@ -7,7 +7,8 @@ from typing import List import org_rw from org_rw import OrgDoc, OrgTime -EXTENSIONS = ( ".org", ".org.txt" ) +EXTENSIONS = (".org", ".org.txt") + def is_today(ot: OrgTime): now = datetime.now() @@ -19,10 +20,12 @@ def is_today(ot: OrgTime): class Agenda: - def __init__(self, /, - with_hour: List[org_rw.Headline], - no_hour: List[org_rw.Headline], - ): + def __init__( + self, + /, + with_hour: List[org_rw.Headline], + no_hour: List[org_rw.Headline], + ): self.with_hour = with_hour self.no_hour = no_hour @@ -52,7 +55,7 @@ class DocumentManager: # Prune dirs i = 0 while i < len(dirs): - if dirs[i].startswith('.git'): + if dirs[i].startswith(".git"): del dirs[i] else: i += 1 @@ -79,7 +82,6 @@ class DocumentManager: self.docs = docs def get_agenda(self) -> Agenda: - headline_count = 0 items_in_agenda = [] now = datetime.now() @@ -114,5 +116,27 @@ class DocumentManager: return Agenda( with_hour=sorted(items_with_hour, key=lambda x: x.scheduled.time), - no_hour=other_items + no_hour=other_items, ) + + def get_notes(self, query) -> List[org_rw.Headline]: + headline_count = 0 + t0 = datetime.now() + notes = [] + query = [q.lower() for q in query] + + for doc in self.docs: + for hl in doc.getAllHeadlines(): + headline_count += 1 + + data = "\n".join(hl.get_contents("raw")).lower() + if all([q in data for q in query]): + notes.append(hl) + + logging.info( + "Filtered {} to {} items in {:.3f}s".format( + headline_count, len(notes), (datetime.now() - t0).total_seconds() + ) + ) + + return notes diff --git a/main.py b/main.py index 12eaf34..4be07ca 100755 --- a/main.py +++ b/main.py @@ -4,19 +4,23 @@ import logging import os import sys import time +import webbrowser from PySide2.QtCore import QObject, QThread, Signal, Slot -from PySide2.QtWidgets import (QApplication, QDialog, QLabel, QLineEdit, QProgressBar, QScrollArea, QTabBar, - QVBoxLayout, QFrame, QScroller, QHBoxLayout, QPushButton, QGroupBox) +from PySide2.QtWidgets import (QApplication, QDialog, QFrame, QGroupBox, + QHBoxLayout, QLabel, QLineEdit, QProgressBar, + QPushButton, QScrollArea, QScroller, QTabBar, + QVBoxLayout) import doc_manager -DOCS_PATH = os.environ['ORG_PATH'] +DOCS_PATH = os.environ["ORG_PATH"] class LoadDoneSignal(QObject): sig = Signal(doc_manager.DocumentManager) + class DocumentLoader(QThread): def __init__(self, manager): QThread.__init__(self, None) @@ -34,8 +38,7 @@ class Dialog(QDialog): self.setWindowTitle("OrgEditor") scrSize = self.screen().size() - self.resize(scrSize.width() / 1.5, - scrSize.height() / 1.5) + self.resize(scrSize.width() / 1.5, scrSize.height() / 1.5) self.loader = None self.manager = doc_manager.DocumentManager(DOCS_PATH) @@ -46,7 +49,7 @@ class Dialog(QDialog): self.progressBar.setRange(0, 0) # Make undetermined layout.addWidget(self.progressBar) - self.edit = QLineEdit("", placeholderText='Search for notes') + self.edit = QLineEdit("", placeholderText="Search for notes") self.edit.textEdited.connect(self.on_text_edited) layout.addWidget(self.edit) @@ -55,7 +58,8 @@ class Dialog(QDialog): self.results = QScrollArea(widgetResizable=True) layout.addWidget(self.results) QScroller.grabGesture( - self.results.viewport(), QScroller.LeftMouseButtonGesture, + self.results.viewport(), + QScroller.LeftMouseButtonGesture, ) # Options @@ -73,7 +77,10 @@ class Dialog(QDialog): @Slot() def on_text_edited(self): - self.tabBar.setCurrentIndex(1) + if self.tabBar.currentIndex() != 1: + self.tabBar.setCurrentIndex(1) + else: + self.loadNotes() @Slot() def update_tab(self): @@ -105,7 +112,9 @@ class Dialog(QDialog): self.update_tab() def longoperationcomplete(self, data): - logging.info("Loading complete in {:.3f}s".format(time.time() - self.loading_start_time)) + logging.info( + "Loading complete in {:.3f}s".format(time.time() - self.loading_start_time) + ) self.endLoad() def loadAgenda(self): @@ -141,32 +150,58 @@ class Dialog(QDialog): state_button.setFlat(True) box.addWidget(state_button) - box.addWidget( - QLabel(text=f"{item.scheduled.time}", maximumWidth=200) - ) - box.addWidget( - QLabel(text=f"{item.title}") - ) + box.addWidget(QLabel(text=f"{item.scheduled.time}", maximumWidth=200)) + box.addWidget(QLabel(text=f"{item.title}")) def on_clicked(): - state_button.setText('DONE') + state_button.setText("DONE") # state_button.setFlat(True) # item.state = 'DONE' if not item.is_done: state_button.clicked.connect(on_clicked) + return frame + + def build_note_task_widget(self, item): + box = QHBoxLayout() + frame = QGroupBox() + frame.setLayout(box) + + titleButton = QPushButton(text=f"{item.title}") + box.addWidget(titleButton) + + def on_clicked(): + webbrowser.open("org-protocol://org-id?id=" + item.id) + + titleButton.clicked.connect(on_clicked) return frame def loadNotes(self): - logging.warning("loadNotes not yet implemented") + query = self.edit.text() + notes = self.manager.get_notes(query.split()) + old = self.results.layout() + + if old: + print("Deleting old") + old.deleteLater() + + layout = QVBoxLayout() + + for note in notes: + layout.addWidget(self.build_note_task_widget(note)) + + frame = QFrame(self.results) + frame.setLayout(layout) + self.results.setWidget(frame) def loadTasks(self): logging.warning("loadTasks not yet implemented") + # Create the Qt Application -if __name__ == '__main__': +if __name__ == "__main__": logging.basicConfig(level=logging.INFO, format="%(levelname)-8s %(message)s") app = QApplication(sys.argv)