generated from itdominator/Python-With-Gtk-Template
Adding notebook save and loading structure
This commit is contained in:
parent
c5d80b6c25
commit
fb3564a3aa
16
README.md
16
README.md
|
@ -1,5 +1,5 @@
|
||||||
# Python-With-Gtk-Template
|
# Coherence
|
||||||
A template project for Python with Gtk applications.
|
Coherence is like a OneNote sibling that lets you store notes, images, links, etc.
|
||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
* PyGObject
|
* PyGObject
|
||||||
|
@ -7,14 +7,4 @@ A template project for Python with Gtk applications.
|
||||||
* pyxdg
|
* pyxdg
|
||||||
|
|
||||||
### Note
|
### Note
|
||||||
There are a "\<change_me\>" strings and files that need to be set according to your app's name located at:
|
* n/a
|
||||||
* \_\_builtins\_\_.py
|
|
||||||
* user_config/bin/app_name
|
|
||||||
* user_config/usr/share/app_name
|
|
||||||
* user_config/usr/share/app_name/icons/app_name.png
|
|
||||||
* user_config/usr/share/app_name/icons/app_name-64x64.png
|
|
||||||
* user_config/usr/share/applications/app_name.desktop
|
|
||||||
|
|
||||||
|
|
||||||
For the user_config, after changing names and files, copy all content to their respective destinations.
|
|
||||||
The logic follows Debian Dpkg packaging and its placement logic.
|
|
||||||
|
|
|
@ -32,6 +32,7 @@ if __name__ == "__main__":
|
||||||
# Add long and short arguments
|
# Add long and short arguments
|
||||||
parser.add_argument("--debug", "-d", default="false", help="Do extra console messaging.")
|
parser.add_argument("--debug", "-d", default="false", help="Do extra console messaging.")
|
||||||
parser.add_argument("--trace-debug", "-td", default="false", help="Disable saves, ignore IPC lock, do extra console messaging.")
|
parser.add_argument("--trace-debug", "-td", default="false", help="Disable saves, ignore IPC lock, do extra console messaging.")
|
||||||
|
parser.add_argument("--no-plugins", "-np", default="false", help="Do not load plugins.")
|
||||||
parser.add_argument("--new-tab", "-nt", default="false", help="Opens a 'New Tab' if a handler is set for it.")
|
parser.add_argument("--new-tab", "-nt", default="false", help="Opens a 'New Tab' if a handler is set for it.")
|
||||||
parser.add_argument("--file", "-f", default="default", help="JUST SOME FILE ARG.")
|
parser.add_argument("--file", "-f", default="default", help="JUST SOME FILE ARG.")
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ class Application(IPCServer):
|
||||||
|
|
||||||
if not self.is_ipc_alive:
|
if not self.is_ipc_alive:
|
||||||
for arg in unknownargs + [args.new_tab,]:
|
for arg in unknownargs + [args.new_tab,]:
|
||||||
if os.path.isdir(arg):
|
if os.path.isfile(arg):
|
||||||
message = f"FILE|{arg}"
|
message = f"FILE|{arg}"
|
||||||
self.send_ipc_message(message)
|
self.send_ipc_message(message)
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# Python imports
|
# Python imports
|
||||||
|
import os
|
||||||
|
|
||||||
# Lib imports
|
# Lib imports
|
||||||
import gi
|
import gi
|
||||||
|
@ -11,6 +12,7 @@ from gi.repository import GLib
|
||||||
# Application imports
|
# Application imports
|
||||||
from .mixins.signals_mixins import SignalsMixins
|
from .mixins.signals_mixins import SignalsMixins
|
||||||
from .controller_data import ControllerData
|
from .controller_data import ControllerData
|
||||||
|
from .widgets.create_notebook_widget import CreateNotebookWidget
|
||||||
from .widgets.sections.sections_widget import Sections
|
from .widgets.sections.sections_widget import Sections
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,6 +24,23 @@ class Controller(SignalsMixins, ControllerData):
|
||||||
self._setup_styling()
|
self._setup_styling()
|
||||||
self._setup_signals()
|
self._setup_signals()
|
||||||
self._subscribe_to_events()
|
self._subscribe_to_events()
|
||||||
|
self._load_widgets()
|
||||||
|
|
||||||
|
if args.no_plugins == "false":
|
||||||
|
self.plugins.launch_plugins()
|
||||||
|
|
||||||
|
for arg in unknownargs + [args.new_tab,]:
|
||||||
|
if os.path.isfile(arg):
|
||||||
|
message = f"FILE|{arg}"
|
||||||
|
event_system.emit("post_file_to_ipc", message)
|
||||||
|
|
||||||
|
if os.path.isdir(arg):
|
||||||
|
message = f"DIR|{arg}"
|
||||||
|
event_system.emit("post_file_to_ipc", message)
|
||||||
|
|
||||||
|
event_system.emit_and_await("load_notebook")
|
||||||
|
if settings.get_active_notebook():
|
||||||
|
event_system.emit("load_notebook_data")
|
||||||
|
|
||||||
logger.info(f"Made it past {self.__class__} loading...")
|
logger.info(f"Made it past {self.__class__} loading...")
|
||||||
|
|
||||||
|
@ -38,6 +57,9 @@ class Controller(SignalsMixins, ControllerData):
|
||||||
event_system.subscribe("handle_file_from_ipc", self.handle_file_from_ipc)
|
event_system.subscribe("handle_file_from_ipc", self.handle_file_from_ipc)
|
||||||
event_system.subscribe("tggl_top_main_menubar", self._tggl_top_main_menubar)
|
event_system.subscribe("tggl_top_main_menubar", self._tggl_top_main_menubar)
|
||||||
|
|
||||||
|
def _load_widgets(self):
|
||||||
|
CreateNotebookWidget()
|
||||||
|
|
||||||
def load_glade_file(self):
|
def load_glade_file(self):
|
||||||
self.builder = Gtk.Builder()
|
self.builder = Gtk.Builder()
|
||||||
self.builder.add_from_file(settings.get_glade_file())
|
self.builder.add_from_file(settings.get_glade_file())
|
||||||
|
|
|
@ -15,3 +15,6 @@ class IPCSignalsMixin:
|
||||||
|
|
||||||
def handle_file_from_ipc(self, path: str) -> None:
|
def handle_file_from_ipc(self, path: str) -> None:
|
||||||
print(f"Path From IPC: {path}")
|
print(f"Path From IPC: {path}")
|
||||||
|
|
||||||
|
def handle_dir_from_ipc(self, path: str) -> None:
|
||||||
|
print(f"Dir From IPC: {path}")
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
# Python imports
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Lib imports
|
||||||
|
import gi
|
||||||
|
gi.require_version('Gtk', '3.0')
|
||||||
|
from gi.repository import Gtk
|
||||||
|
|
||||||
|
# Application imports
|
||||||
|
|
||||||
|
|
||||||
|
class CreateNotebookWidget(Gtk.Dialog):
|
||||||
|
def __init__(self):
|
||||||
|
super(CreateNotebookWidget, self).__init__()
|
||||||
|
|
||||||
|
self._nb_path = settings.get_notebooks_path()
|
||||||
|
|
||||||
|
self._setup_styling()
|
||||||
|
self._setup_signals()
|
||||||
|
self._subscribe_to_events()
|
||||||
|
self._load_widgets()
|
||||||
|
|
||||||
|
|
||||||
|
def _setup_styling(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
def _setup_signals(self):
|
||||||
|
self.set_default_geometry(280, 80)
|
||||||
|
self.set_size_request(280, 80)
|
||||||
|
self.set_modal(True)
|
||||||
|
self.set_transient_for( settings.get_main_window() )
|
||||||
|
|
||||||
|
def _subscribe_to_events(self):
|
||||||
|
event_system.subscribe("load_notebook", self._load_notebook)
|
||||||
|
event_system.subscribe("create_notebook", self._create_notebook)
|
||||||
|
|
||||||
|
def _load_widgets(self):
|
||||||
|
area = self.get_content_area()
|
||||||
|
label = Gtk.Label(label="Create A New Notebook")
|
||||||
|
nb_entry = Gtk.Entry()
|
||||||
|
create_btn = Gtk.Button(label="Create")
|
||||||
|
cancel_btn = Gtk.Button(label="Cancel")
|
||||||
|
|
||||||
|
create_btn.set_image( Gtk.Image.new_from_icon_name("gtk-add", 4) )
|
||||||
|
cancel_btn.set_image( Gtk.Image.new_from_icon_name("gtk-cancel", 4) )
|
||||||
|
nb_entry.set_max_length(15)
|
||||||
|
nb_entry.set_placeholder_text("Notebook Name")
|
||||||
|
|
||||||
|
area.add(label)
|
||||||
|
area.add(nb_entry)
|
||||||
|
|
||||||
|
self.add_action_widget(cancel_btn, 1)
|
||||||
|
self.add_action_widget(create_btn, 0)
|
||||||
|
|
||||||
|
area.show_all()
|
||||||
|
|
||||||
|
|
||||||
|
def _load_notebook(self):
|
||||||
|
notebooks = os.listdir(self._nb_path)
|
||||||
|
if len(notebooks) == 0:
|
||||||
|
if self._create_notebook(True) in (-1, 1):
|
||||||
|
return
|
||||||
|
|
||||||
|
notebooks = os.listdir(self._nb_path)
|
||||||
|
for notebook in notebooks:
|
||||||
|
path = os.path.join(self._nb_path, notebook)
|
||||||
|
if os.path.isdir(path):
|
||||||
|
# TODO: Add to a selection window then load choice
|
||||||
|
settings.set_active_notebook(path) # NOTE: Temporary
|
||||||
|
...
|
||||||
|
|
||||||
|
# settings.set_active_notebook(path)
|
||||||
|
|
||||||
|
|
||||||
|
def _create_notebook(self, is_first_notebook = False):
|
||||||
|
response = self.run()
|
||||||
|
self.hide()
|
||||||
|
|
||||||
|
if response == 0:
|
||||||
|
nb_entry = self.get_content_area().get_children()[1]
|
||||||
|
notebook = nb_entry.get_text().strip()
|
||||||
|
|
||||||
|
if not notebook in ("", None):
|
||||||
|
path = os.path.join(self._nb_path, notebook)
|
||||||
|
try:
|
||||||
|
logger.info(f"Creating Notebook at: {path}")
|
||||||
|
os.mkdir(path)
|
||||||
|
settings.set_active_notebook(path)
|
||||||
|
logger.info(f"Created and loaded Notebook: {notebook}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.info(f"Notebook creation failed! ")
|
||||||
|
response = -1
|
||||||
|
else:
|
||||||
|
response = 1
|
||||||
|
|
||||||
|
if response == 1:
|
||||||
|
logger.info(f"Canceled notebook creation...")
|
||||||
|
|
||||||
|
return response
|
|
@ -30,10 +30,37 @@ class Pages(Gtk.Notebook):
|
||||||
|
|
||||||
|
|
||||||
def _setup_signals(self):
|
def _setup_signals(self):
|
||||||
...
|
self.set_scrollable(True)
|
||||||
|
|
||||||
def _load_widgets(self):
|
def _load_widgets(self):
|
||||||
page = Page(self.close_tab)
|
start_box = Gtk.Box()
|
||||||
|
end_box = Gtk.Box()
|
||||||
|
|
||||||
|
search = Gtk.SearchEntry()
|
||||||
|
search.set_placeholder_text("Search...")
|
||||||
|
search.connect("changed", self._text_search)
|
||||||
|
|
||||||
|
add_btn = Gtk.Button()
|
||||||
|
add_btn.set_image( Gtk.Image.new_from_icon_name("add", 4) )
|
||||||
|
add_btn.set_always_show_image(True)
|
||||||
|
add_btn.connect("released", self.create_page_view)
|
||||||
|
|
||||||
|
start_box.add(search)
|
||||||
|
start_box.add(add_btn)
|
||||||
|
|
||||||
|
start_box.show_all()
|
||||||
|
end_box.show_all()
|
||||||
|
|
||||||
|
# PACKTYPE: 0 Start, 1 = End
|
||||||
|
self.set_action_widget(start_box, 0)
|
||||||
|
self.set_action_widget(end_box, 1)
|
||||||
|
|
||||||
|
def _close_tab(self, button, page, eve = None):
|
||||||
|
page_num = self.page_num(page)
|
||||||
|
self.remove_page(page_num)
|
||||||
|
|
||||||
|
def create_page_view(self, widget = None, eve = None):
|
||||||
|
page = Page(self._close_tab)
|
||||||
page_num = self.append_page(page, page.get_tab_widget())
|
page_num = self.append_page(page, page.get_tab_widget())
|
||||||
|
|
||||||
self.set_tab_detachable(page, False)
|
self.set_tab_detachable(page, False)
|
||||||
|
@ -46,6 +73,5 @@ class Pages(Gtk.Notebook):
|
||||||
def get_tab_widget(self):
|
def get_tab_widget(self):
|
||||||
return self._tab_widget
|
return self._tab_widget
|
||||||
|
|
||||||
def close_tab(self, button, page, eve = None):
|
def _text_search(self, widget = None, eve = None):
|
||||||
page_num = self.page_num(page)
|
...
|
||||||
self.remove_page(page_num)
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# Python imports
|
# Python imports
|
||||||
|
import os
|
||||||
|
|
||||||
# Lib imports
|
# Lib imports
|
||||||
import gi
|
import gi
|
||||||
|
@ -16,23 +17,45 @@ class Sections(Gtk.Notebook):
|
||||||
|
|
||||||
self._setup_styling()
|
self._setup_styling()
|
||||||
self._setup_signals()
|
self._setup_signals()
|
||||||
|
self._subscribe_to_events()
|
||||||
self._load_widgets()
|
self._load_widgets()
|
||||||
|
|
||||||
self.show_all()
|
self.show_all()
|
||||||
|
|
||||||
|
|
||||||
def _setup_styling(self):
|
def _setup_styling(self):
|
||||||
...
|
self.set_scrollable(True)
|
||||||
|
|
||||||
def _setup_signals(self):
|
def _setup_signals(self):
|
||||||
...
|
...
|
||||||
|
|
||||||
def _load_widgets(self):
|
def _subscribe_to_events(self):
|
||||||
self.create_view()
|
event_system.subscribe("load_notebook_data", self._load_notebook_data)
|
||||||
...
|
|
||||||
|
|
||||||
def create_view(self, widget = None, eve = None, gfile = None):
|
def _load_widgets(self):
|
||||||
pages = Pages(self.close_tab)
|
start_box = Gtk.Box()
|
||||||
|
end_box = Gtk.Box()
|
||||||
|
|
||||||
|
add_btn = Gtk.Button()
|
||||||
|
add_btn.set_image( Gtk.Image.new_from_icon_name("add", 4) )
|
||||||
|
add_btn.set_always_show_image(True)
|
||||||
|
add_btn.connect("released", self.create_pages_view)
|
||||||
|
|
||||||
|
end_box.add(add_btn)
|
||||||
|
|
||||||
|
start_box.show_all()
|
||||||
|
end_box.show_all()
|
||||||
|
|
||||||
|
# PACKTYPE: 0 Start, 1 = End
|
||||||
|
self.set_action_widget(start_box, 0)
|
||||||
|
self.set_action_widget(end_box, 1)
|
||||||
|
|
||||||
|
def _close_tab(self, button, pages, eve = None):
|
||||||
|
page_num = self.page_num(pages)
|
||||||
|
self.remove_page(page_num)
|
||||||
|
|
||||||
|
def create_pages_view(self, widget = None, eve = None):
|
||||||
|
pages = Pages(self._close_tab)
|
||||||
page_num = self.append_page(pages, pages.get_tab_widget())
|
page_num = self.append_page(pages, pages.get_tab_widget())
|
||||||
|
|
||||||
self.set_tab_detachable(pages, True)
|
self.set_tab_detachable(pages, True)
|
||||||
|
@ -41,6 +64,10 @@ class Sections(Gtk.Notebook):
|
||||||
self.show_all()
|
self.show_all()
|
||||||
self.set_current_page(page_num)
|
self.set_current_page(page_num)
|
||||||
|
|
||||||
def close_tab(self, button, pages, eve = None):
|
def _load_notebook_data(self):
|
||||||
page_num = self.page_num(pages)
|
path = settings.get_active_notebook()
|
||||||
self.remove_page(page_num)
|
sections = os.listdir(path)
|
||||||
|
logger.info(f"Loading Notebook sections from: {path}")
|
||||||
|
if len(sections) == 0:
|
||||||
|
section = os.path.join(path, "New Section")
|
||||||
|
os.mkdir(section)
|
||||||
|
|
|
@ -73,6 +73,11 @@ class IPCServer:
|
||||||
if file:
|
if file:
|
||||||
event_system.emit("handle_file_from_ipc", file)
|
event_system.emit("handle_file_from_ipc", file)
|
||||||
|
|
||||||
|
if "DIR|" in msg:
|
||||||
|
file = msg.split("DIR|")[1].strip()
|
||||||
|
if file:
|
||||||
|
event_system.emit("handle_dir_from_ipc", file)
|
||||||
|
|
||||||
conn.close()
|
conn.close()
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ class Settings(StartCheckMixin):
|
||||||
self._USR_CONFIG_FILE = f"{self._USR_PATH}/settings.json"
|
self._USR_CONFIG_FILE = f"{self._USR_PATH}/settings.json"
|
||||||
self._HOME_CONFIG_PATH = f"{self._USER_HOME}/.config/{app_name.lower()}"
|
self._HOME_CONFIG_PATH = f"{self._USER_HOME}/.config/{app_name.lower()}"
|
||||||
self._PLUGINS_PATH = f"{self._HOME_CONFIG_PATH}/plugins"
|
self._PLUGINS_PATH = f"{self._HOME_CONFIG_PATH}/plugins"
|
||||||
|
self._NOTEBOOKS_PATH = f"{self._HOME_CONFIG_PATH}/notebooks"
|
||||||
self._DEFAULT_ICONS = f"{self._HOME_CONFIG_PATH}/icons"
|
self._DEFAULT_ICONS = f"{self._HOME_CONFIG_PATH}/icons"
|
||||||
self._CONFIG_FILE = f"{self._HOME_CONFIG_PATH}/settings.json"
|
self._CONFIG_FILE = f"{self._HOME_CONFIG_PATH}/settings.json"
|
||||||
self._GLADE_FILE = f"{self._HOME_CONFIG_PATH}/Main_Window.glade"
|
self._GLADE_FILE = f"{self._HOME_CONFIG_PATH}/Main_Window.glade"
|
||||||
|
@ -35,6 +36,8 @@ class Settings(StartCheckMixin):
|
||||||
os.mkdir(self._HOME_CONFIG_PATH)
|
os.mkdir(self._HOME_CONFIG_PATH)
|
||||||
if not os.path.exists(self._PLUGINS_PATH):
|
if not os.path.exists(self._PLUGINS_PATH):
|
||||||
os.mkdir(self._PLUGINS_PATH)
|
os.mkdir(self._PLUGINS_PATH)
|
||||||
|
if not os.path.exists(self._NOTEBOOKS_PATH):
|
||||||
|
os.mkdir(self._NOTEBOOKS_PATH)
|
||||||
|
|
||||||
if not os.path.exists(self._CONFIG_FILE):
|
if not os.path.exists(self._CONFIG_FILE):
|
||||||
import shutil
|
import shutil
|
||||||
|
@ -78,6 +81,7 @@ class Settings(StartCheckMixin):
|
||||||
self._trace_debug = False
|
self._trace_debug = False
|
||||||
self._debug = False
|
self._debug = False
|
||||||
self._dirty_start = False
|
self._dirty_start = False
|
||||||
|
self._active_notebook = None
|
||||||
|
|
||||||
self.load_settings()
|
self.load_settings()
|
||||||
|
|
||||||
|
@ -97,7 +101,8 @@ class Settings(StartCheckMixin):
|
||||||
|
|
||||||
def set_main_window(self, window): self._main_window = window
|
def set_main_window(self, window): self._main_window = window
|
||||||
def set_builder(self, builder) -> any: self._builder = builder
|
def set_builder(self, builder) -> any: self._builder = builder
|
||||||
|
def set_active_notebook(self, path: str): self._active_notebook = path
|
||||||
|
def get_active_notebook(self) -> str: return self._active_notebook
|
||||||
|
|
||||||
def get_monitor_data(self) -> list:
|
def get_monitor_data(self) -> list:
|
||||||
screen = self._main_window.get_screen()
|
screen = self._main_window.get_screen()
|
||||||
|
@ -121,6 +126,7 @@ class Settings(StartCheckMixin):
|
||||||
def get_home_config_path(self) -> str: return self._HOME_CONFIG_PATH
|
def get_home_config_path(self) -> str: return self._HOME_CONFIG_PATH
|
||||||
def get_window_icon(self) -> str: return self._WINDOW_ICON
|
def get_window_icon(self) -> str: return self._WINDOW_ICON
|
||||||
def get_home_path(self) -> str: return self._USER_HOME
|
def get_home_path(self) -> str: return self._USER_HOME
|
||||||
|
def get_notebooks_path(self) -> str: return self._NOTEBOOKS_PATH
|
||||||
|
|
||||||
# Filter returns
|
# Filter returns
|
||||||
def get_office_filter(self) -> tuple: return tuple(self._settings["filters"]["office"])
|
def get_office_filter(self) -> tuple: return tuple(self._settings["filters"]["office"])
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[Desktop Entry]
|
[Desktop Entry]
|
||||||
Name=Coherence
|
Name=Coherence
|
||||||
GenericName=Coherence is like a OneNote sibling thatlets you store notes, images, links, etc.
|
GenericName=Coherence is like a OneNote sibling that lets you store notes, images, links, etc.
|
||||||
Comment=Coherence
|
Comment=Coherence
|
||||||
Exec=/bin/coherence %F
|
Exec=/bin/coherence %F
|
||||||
Icon=/usr/share/Coherence/icons/coherence.png
|
Icon=/usr/share/Coherence/icons/coherence.png
|
||||||
|
|
Loading…
Reference in New Issue