From 596608642f5813b2ad4c40c7f29eab4fab3c3c59 Mon Sep 17 00:00:00 2001 From: itdominator <1itdominator@gmail.com> Date: Thu, 23 Feb 2023 18:48:37 -0600 Subject: [PATCH] Refactoring, settings cleanup, glade load fixes --- src/__builtins__.py | 7 +- src/app.py | 5 - src/core/controller.py | 18 +-- src/core/controller_data.py | 15 +- src/core/core_widget.py | 9 +- src/core/window.py | 22 ++- src/plugins/plugins_controller.py | 5 +- src/utils/settings.py | 149 ------------------ src/utils/settings/__init__.py | 4 + src/utils/settings/settings.py | 138 ++++++++++++++++ src/utils/settings/start_check_mixin.py | 50 ++++++ user_config/bin/ | 29 ++++ .../usr/applications/.desktop | 11 ++ .../usr/share/app_name/Main_Window.glade | 22 ++- user_config/usr/share/app_name/settings.json | 11 +- 15 files changed, 313 insertions(+), 182 deletions(-) delete mode 100644 src/utils/settings.py create mode 100644 src/utils/settings/__init__.py create mode 100644 src/utils/settings/settings.py create mode 100644 src/utils/settings/start_check_mixin.py create mode 100755 user_config/bin/ create mode 100755 user_config/usr/applications/.desktop diff --git a/src/__builtins__.py b/src/__builtins__.py index c73cb6e..c7b3d32 100644 --- a/src/__builtins__.py +++ b/src/__builtins__.py @@ -8,11 +8,11 @@ import threading from utils.event_system import EventSystem from utils.endpoint_registry import EndpointRegistry from utils.keybindings import Keybindings +from utils.logger import Logger from utils.settings import Settings - # NOTE: Threads WILL NOT die with parent's destruction. def threaded_wrapper(fn): def wrapper(*args, **kwargs): @@ -27,7 +27,6 @@ def daemon_threaded_wrapper(fn): - # NOTE: Just reminding myself we can add to builtins two different ways... # __builtins__.update({"event_system": Builtins()}) builtins.app_name = "" @@ -35,7 +34,9 @@ builtins.keybindings = Keybindings() builtins.event_system = EventSystem() builtins.endpoint_registry = EndpointRegistry() builtins.settings = Settings() -builtins.logger = settings.get_logger() +builtins.logger = Logger(settings.get_home_config_path(), \ + _ch_log_lvl=settings.get_ch_log_lvl(), \ + _fh_log_lvl=settings.get_fh_log_lvl()).get_logger() builtins.threaded = threaded_wrapper builtins.daemon_threaded = daemon_threaded_wrapper diff --git a/src/app.py b/src/app.py index 6880502..1ea9b36 100644 --- a/src/app.py +++ b/src/app.py @@ -1,6 +1,5 @@ # Python imports import os -import time # Lib imports @@ -9,13 +8,9 @@ from utils.ipc_server import IPCServer from core.window import Window - - class AppLaunchException(Exception): ... -class ControllerStartExceptiom(Exception): - ... class Application(IPCServer): diff --git a/src/core/controller.py b/src/core/controller.py index 0f8475f..a632026 100644 --- a/src/core/controller.py +++ b/src/core/controller.py @@ -1,5 +1,4 @@ # Python imports -import subprocess # Lib imports import gi @@ -26,6 +25,8 @@ class Controller(DummyMixin, ControllerData): self.setup_controller_data() self.print_hello_world() # A mixin method from the DummyMixin file + logger.info(f"Made it past {self.__class__} loading...") + def _setup_styling(self): ... @@ -43,6 +44,8 @@ class Controller(DummyMixin, ControllerData): def load_glade_file(self): self.builder = Gtk.Builder() self.builder.add_from_file(settings.get_glade_file()) + self.builder.expose_object("main_window", self.window) + settings.set_builder(self.builder) self.core_widget = CoreWidget() @@ -71,16 +74,3 @@ class Controller(DummyMixin, ControllerData): else: print(f"on_global_key_release_controller > key > {keyname}") print(f"Add logic or remove this from: {self.__class__}") - - - def get_clipboard_data(self) -> str: - proc = subprocess.Popen(['xclip','-selection', 'clipboard', '-o'], stdout=subprocess.PIPE) - retcode = proc.wait() - data = proc.stdout.read() - return data.decode("utf-8").strip() - - def set_clipboard_data(self, data: type) -> None: - proc = subprocess.Popen(['xclip','-selection','clipboard'], stdin=subprocess.PIPE) - proc.stdin.write(data.encode("utf-8")) - proc.stdin.close() - retcode = proc.wait() diff --git a/src/core/controller_data.py b/src/core/controller_data.py index bc2a941..d37cbe5 100644 --- a/src/core/controller_data.py +++ b/src/core/controller_data.py @@ -1,5 +1,6 @@ # Python imports import os +import subprocess # Lib imports @@ -13,7 +14,7 @@ class ControllerData: ''' ControllerData contains most of the state of the app at ay given time. It also has some support methods. ''' def setup_controller_data(self) -> None: - self.logger = settings.get_logger() + self.window = settings.get_main_window() self.builder = None self.core_widget = None @@ -50,3 +51,15 @@ class ControllerData: ''' Clear children of a gtk widget. ''' for child in widget.get_children(): widget.remove(child) + + def get_clipboard_data(self) -> str: + proc = subprocess.Popen(['xclip','-selection', 'clipboard', '-o'], stdout=subprocess.PIPE) + retcode = proc.wait() + data = proc.stdout.read() + return data.decode("utf-8").strip() + + def set_clipboard_data(self, data: type) -> None: + proc = subprocess.Popen(['xclip','-selection','clipboard'], stdin=subprocess.PIPE) + proc.stdin.write(data.encode("utf-8")) + proc.stdin.close() + retcode = proc.wait() diff --git a/src/core/core_widget.py b/src/core/core_widget.py index ee1da7c..2775c0e 100644 --- a/src/core/core_widget.py +++ b/src/core/core_widget.py @@ -14,6 +14,8 @@ class CoreWidget(Gtk.Box): def __init__(self): super(CoreWidget, self).__init__() + self._builder = settings.get_builder() + self._setup_styling() self._setup_signals() self._load_widgets() @@ -28,10 +30,15 @@ class CoreWidget(Gtk.Box): ... def _load_widgets(self): - button = Gtk.Button(label="Click Me!") + glade_box = self._builder.get_object("glade_box") + button = Gtk.Button(label="Click Me!") + button.connect("clicked", self._hello_world) self.add(button) + self.add(glade_box) + + def _hello_world(self, widget=None, eve=None): print("Hello, World!") diff --git a/src/core/window.py b/src/core/window.py index 6520da7..72430b9 100644 --- a/src/core/window.py +++ b/src/core/window.py @@ -15,6 +15,10 @@ from gi.repository import GLib from core.controller import Controller +class ControllerStartExceptiom(Exception): + ... + + class Window(Gtk.ApplicationWindow): @@ -23,12 +27,17 @@ class Window(Gtk.ApplicationWindow): def __init__(self, args, unknownargs): super(Window, self).__init__() + self._controller = None + self._set_window_data() self._setup_styling() self._setup_signals() + self._subscribe_to_events() + + settings.set_main_window(self) self._load_widgets(args, unknownargs) - self.show_all() + self.show() def _setup_styling(self): @@ -42,9 +51,16 @@ class Window(Gtk.ApplicationWindow): self.connect("delete-event", self._tear_down) GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, self._tear_down) + def _subscribe_to_events(self): + event_system.subscribe("tear_down", self._tear_down) + def _load_widgets(self, args, unknownargs): - controller = Controller(args, unknownargs) - self.add( controller.get_core_widget() ) + self._controller = Controller(args, unknownargs) + + if not self._controller: + raise ControllerStartException("Controller exited and doesn't exist...") + + self.add( self._controller.get_core_widget() ) def _set_window_data(self) -> None: screen = self.get_screen() diff --git a/src/plugins/plugins_controller.py b/src/plugins/plugins_controller.py index 04c2d22..f0561f7 100644 --- a/src/plugins/plugins_controller.py +++ b/src/plugins/plugins_controller.py @@ -1,7 +1,7 @@ # Python imports import os import sys -import portlib +import importlib import traceback from os.path import join from os.path import isdir @@ -13,7 +13,8 @@ from gi.repository import Gtk from gi.repository import Gio # Application imports -from .manifest import Plugin, ManifestProcessor +from .manifest import Plugin +from .manifest import ManifestProcessor diff --git a/src/utils/settings.py b/src/utils/settings.py deleted file mode 100644 index 54650d7..0000000 --- a/src/utils/settings.py +++ /dev/null @@ -1,149 +0,0 @@ -# Python imports -import os -import json -import inspect - -# Lib imports - -# Application imports -from .logger import Logger - - - - -class Settings: - def __init__(self): - self._SCRIPT_PTH = os.path.dirname(os.path.realpath(__file__)) - self._USER_HOME = os.path.expanduser('~') - self._CONFIG_PATH = f"{self._USER_HOME}/.config/{app_name.lower()}" - self._PLUGINS_PATH = f"{self._CONFIG_PATH}/plugins" - self._GLADE_FILE = f"{self._CONFIG_PATH}/Main_Window.glade" - self._KEY_BINDINGS_FILE = f"{self._CONFIG_PATH}/key-bindings.json" - self._CSS_FILE = f"{self._CONFIG_PATH}/stylesheet.css" - self._DEFAULT_ICONS = f"{self._CONFIG_PATH}/icons" - self._PID_FILE = f"{self._CONFIG_PATH}/{app_name.lower()}.pid" - self._WINDOW_ICON = f"{self._DEFAULT_ICONS}/{app_name.lower()}.png" - self._USR_PATH = f"/usr/share/{app_name.lower()}" - - if not os.path.exists(self._CONFIG_PATH): - os.mkdir(self._CONFIG_PATH) - if not os.path.exists(self._PLUGINS_PATH): - os.mkdir(self._PLUGINS_PATH) - - if not os.path.exists(self._GLADE_FILE): - self._GLADE_FILE = f"{self._USR_PATH}/Main_Window.glade" - if not os.path.exists(self._KEY_BINDINGS_FILE): - self._KEY_BINDINGS_FILE = f"{self._USR_PATH}/key-bindings.json" - if not os.path.exists(self._CSS_FILE): - self._CSS_FILE = f"{self._USR_PATH}/stylesheet.css" - if not os.path.exists(self._WINDOW_ICON): - self._WINDOW_ICON = f"{self._USR_PATH}/icons/{app_name.lower()}.png" - if not os.path.exists(self._DEFAULT_ICONS): - self.DEFAULT_ICONS = f"{self._USR_PATH}/icons" - - # '_filters' - self._office_filter = ('.doc', '.docx', '.xls', '.xlsx', '.xlt', '.xltx', '.xlm', '.ppt', 'pptx', '.pps', '.ppsx', '.odt', '.rtf') - self._vids_filter = ('.mkv', '.avi', '.flv', '.mov', '.m4v', '.mpg', '.wmv', '.mpeg', '.mp4', '.webm') - self._txt_filter = ('.txt', '.text', '.sh', '.cfg', '.conf') - self._music_filter = ('.psf', '.mp3', '.ogg' , '.flac') - self._images_filter = ('.png', '.jpg', '.jpeg', '.gif', '.ico', '.tga') - self._pdf_filter = ('.pdf') - - self._success_color = "#88cc27" - self._warning_color = "#ffa800" - self._error_color = "#ff0000" - - with open(self._KEY_BINDINGS_FILE) as file: - bindings = json.load(file)["keybindings"] - keybindings.configure(bindings) - - self._builder = None - self._logger = Logger(self._CONFIG_PATH).get_logger() - - self._trace_debug = False - self._debug = False - self._dirty_start = False - - - def do_dirty_start_check(self): - if not os.path.exists(self._PID_FILE): - self._write_new_pid() - else: - with open(self._PID_FILE, "r") as _pid: - pid = _pid.readline().strip() - if pid not in ("", None): - self._check_alive_status(int(pid)) - else: - self._write_new_pid() - - """ Check For the existence of a unix pid. """ - def _check_alive_status(self, pid): - print(f"PID Found: {pid}") - try: - os.kill(pid, 0) - except OSError: - print(f"{app_name} is starting dirty...") - self._dirty_start = True - self._write_new_pid() - return - - print("PID is alive... Let downstream errors (sans debug args) handle app closure propigation.") - - def _write_new_pid(self): - pid = os.getpid() - self._write_pid(pid) - - def _clean_pid(self): - os.unlink(self._PID_FILE) - - def _write_pid(self, pid): - with open(self._PID_FILE, "w") as _pid: - _pid.write(f"{pid}") - - def register_signals_to_builder(self, classes=None): - handlers = {} - - for c in classes: - methods = None - try: - methods = inspect.getmembers(c, predicate=inspect.ismethod) - handlers.update(methods) - except Exception as e: - print(repr(e)) - - self._builder.connect_signals(handlers) - - - def set_builder(self, builder) -> any: self._builder = builder - def get_builder(self) -> any: return self._builder - def get_glade_file(self) -> str: return self._GLADE_FILE - - def get_logger(self) -> Logger: return self._logger - def get_plugins_path(self) -> str: return self._PLUGINS_PATH - def get_icon_theme(self) -> str: return self._ICON_THEME - def get_css_file(self) -> str: return self._CSS_FILE - def get_window_icon(self) -> str: return self._WINDOW_ICON - def get_home_path(self) -> str: return self._USER_HOME - - # Filter returns - def get_office_filter(self) -> tuple: return self._office_filter - def get_vids_filter(self) -> tuple: return self._vids_filter - def get_text_filter(self) -> tuple: return self._txt_filter - def get_music_filter(self) -> tuple: return self._music_filter - def get_images_filter(self) -> tuple: return self._images_filter - def get_pdf_filter(self) -> tuple: return self._pdf_filter - - def get_success_color(self) -> str: return self._success_color - def get_warning_color(self) -> str: return self._warning_color - def get_error_color(self) -> str: return self._error_color - - def is_trace_debug(self) -> str: return self._trace_debug - def is_debug(self) -> str: return self._debug - def is_dirty_start(self) -> bool: return self._dirty_start - def clear_pid(self): self._clean_pid() - - def set_trace_debug(self, trace_debug): - self._trace_debug = trace_debug - - def set_debug(self, debug): - self._debug = debug diff --git a/src/utils/settings/__init__.py b/src/utils/settings/__init__.py new file mode 100644 index 0000000..e07c5a0 --- /dev/null +++ b/src/utils/settings/__init__.py @@ -0,0 +1,4 @@ +""" + Settings module +""" +from .settings import Settings diff --git a/src/utils/settings/settings.py b/src/utils/settings/settings.py new file mode 100644 index 0000000..00347de --- /dev/null +++ b/src/utils/settings/settings.py @@ -0,0 +1,138 @@ +# Python imports +import os +import json +import inspect + +# Lib imports + +# Application imports +from .start_check_mixin import StartCheckMixin + + + +class Settings(StartCheckMixin): + def __init__(self): + self._SCRIPT_PTH = os.path.dirname(os.path.realpath(__file__)) + self._USER_HOME = os.path.expanduser('~') + self._USR_PATH = f"/usr/share/{app_name.lower()}" + + self._USR_CONFIG_FILE = f"{self._USR_PATH}/settings.json" + self._HOME_CONFIG_PATH = f"{self._USER_HOME}/.config/{app_name.lower()}" + self._PLUGINS_PATH = f"{self._HOME_CONFIG_PATH}/plugins" + self._DEFAULT_ICONS = f"{self._HOME_CONFIG_PATH}/icons" + self._CONFIG_FILE = f"{self._HOME_CONFIG_PATH}/settings.json" + self._GLADE_FILE = f"{self._HOME_CONFIG_PATH}/Main_Window.glade" + self._CSS_FILE = f"{self._HOME_CONFIG_PATH}/stylesheet.css" + self._KEY_BINDINGS_FILE = f"{self._HOME_CONFIG_PATH}/key-bindings.json" + self._PID_FILE = f"{self._HOME_CONFIG_PATH}/{app_name.lower()}.pid" + self._WINDOW_ICON = f"{self._DEFAULT_ICONS}/icons/{app_name.lower()}.png" + + if not os.path.exists(self._HOME_CONFIG_PATH): + os.mkdir(self._HOME_CONFIG_PATH) + if not os.path.exists(self._PLUGINS_PATH): + os.mkdir(self._PLUGINS_PATH) + + if not os.path.exists(self._CONFIG_FILE): + import shutil + try: + shutil.copyfile(self._USR_CONFIG_FILE, self._CONFIG_FILE) + except Exception as e: + raise + + if not os.path.exists(self._DEFAULT_ICONS): + self.DEFAULT_ICONS = f"{self._USR_PATH}/icons" + if not os.path.exists(self._GLADE_FILE): + self._GLADE_FILE = f"{self._USR_PATH}/Main_Window.glade" + if not os.path.exists(self._KEY_BINDINGS_FILE): + self._KEY_BINDINGS_FILE = f"{self._USR_PATH}/key-bindings.json" + if not os.path.exists(self._CSS_FILE): + self._CSS_FILE = f"{self._USR_PATH}/stylesheet.css" + if not os.path.exists(self._WINDOW_ICON): + self._WINDOW_ICON = f"{self._USR_PATH}/icons/{app_name.lower()}.png" + + + with open(self._KEY_BINDINGS_FILE) as file: + bindings = json.load(file)["keybindings"] + keybindings.configure(bindings) + + self._main_window = None + self._builder = None + + self._trace_debug = False + self._debug = False + self._dirty_start = False + + self.load_settings() + + + def register_signals_to_builder(self, classes=None): + handlers = {} + + for c in classes: + methods = None + try: + methods = inspect.getmembers(c, predicate=inspect.ismethod) + handlers.update(methods) + except Exception as e: + ... + + self._builder.connect_signals(handlers) + + def set_main_window(self, window): self._main_window = window + def set_builder(self, builder) -> any: self._builder = builder + + + def get_monitor_data(self) -> list: + screen = self._main_window.get_screen() + monitors = [] + for m in range(screen.get_n_monitors()): + monitors.append(screen.get_monitor_geometry(m)) + print("{}x{}+{}+{}".format(monitor.width, monitor.height, monitor.x, monitor.y)) + + return monitors + + def get_main_window(self) -> any: return self._main_window + def get_builder(self) -> any: return self._builder + def get_glade_file(self) -> str: return self._GLADE_FILE + + def get_plugins_path(self) -> str: return self._PLUGINS_PATH + def get_icon_theme(self) -> str: return self._ICON_THEME + def get_css_file(self) -> str: return self._CSS_FILE + def get_home_config_path(self) -> str: return self._HOME_CONFIG_PATH + def get_window_icon(self) -> str: return self._WINDOW_ICON + def get_home_path(self) -> str: return self._USER_HOME + + # Filter returns + def get_office_filter(self) -> tuple: return tuple(self._settings["filters"]["office"]) + def get_vids_filter(self) -> tuple: return tuple(self._settings["filters"]["videos"]) + def get_text_filter(self) -> tuple: return tuple(self._settings["filters"]["text"]) + def get_music_filter(self) -> tuple: return tuple(self._settings["filters"]["music"]) + def get_images_filter(self) -> tuple: return tuple(self._settings["filters"]["images"]) + def get_pdf_filter(self) -> tuple: return tuple(self._settings["filters"]["pdf"]) + + def get_success_color(self) -> str: return self._theming["success_color"] + def get_warning_color(self) -> str: return self._theming["warning_color"] + def get_error_color(self) -> str: return self._theming["error_color"] + + def is_trace_debug(self) -> str: return self._trace_debug + def is_debug(self) -> str: return self._debug + + def get_ch_log_lvl(self) -> str: return self._settings["debugging"]["ch_log_lvl"] + def get_fh_log_lvl(self) -> str: return self._settings["debugging"]["fh_log_lvl"] + + def set_trace_debug(self, trace_debug): + self._trace_debug = trace_debug + + def set_debug(self, debug): + self._debug = debug + + + def load_settings(self): + with open(self._CONFIG_FILE) as f: + self._settings = json.load(f) + self._config = self._settings["config"] + self._theming = self._settings["theming"] + + def save_settings(self): + with open(self._CONFIG_FILE, 'w') as outfile: + json.dump(self._settings, outfile, separators=(',', ':'), indent=4) diff --git a/src/utils/settings/start_check_mixin.py b/src/utils/settings/start_check_mixin.py new file mode 100644 index 0000000..7fba503 --- /dev/null +++ b/src/utils/settings/start_check_mixin.py @@ -0,0 +1,50 @@ +# Python imports +import os +import json +import inspect + +# Lib imports + +# Application imports + + + + +class StartCheckMixin: + def is_dirty_start(self) -> bool: return self._dirty_start + def clear_pid(self): self._clean_pid() + + def do_dirty_start_check(self): + if not os.path.exists(self._PID_FILE): + self._write_new_pid() + else: + with open(self._PID_FILE, "r") as _pid: + pid = _pid.readline().strip() + if pid not in ("", None): + self._check_alive_status(int(pid)) + else: + self._write_new_pid() + + """ Check For the existence of a unix pid. """ + def _check_alive_status(self, pid): + print(f"PID Found: {pid}") + try: + os.kill(pid, 0) + except OSError: + print(f"{app_name} is starting dirty...") + self._dirty_start = True + self._write_new_pid() + return + + print("PID is alive... Let downstream errors (sans debug args) handle app closure propigation.") + + def _write_new_pid(self): + pid = os.getpid() + self._write_pid(pid) + + def _clean_pid(self): + os.unlink(self._PID_FILE) + + def _write_pid(self, pid): + with open(self._PID_FILE, "w") as _pid: + _pid.write(f"{pid}") diff --git a/user_config/bin/ b/user_config/bin/ new file mode 100755 index 0000000..7a3e523 --- /dev/null +++ b/user_config/bin/ @@ -0,0 +1,29 @@ +#!/bin/bash + +# . CONFIG.sh + +# set -o xtrace ## To debug scripts +# set -o errexit ## To exit on error +# set -o errunset ## To exit if a variable is referenced but not set + + +function main() { + call_path=`pwd` + path="" + + if [[ ! "${1::1}" == /* ]]; then + path="${call_path}/${1}" + else + path="${1}" + fi + + # NOTE: Remove if you want to pass file(s) besides directories... + if [ ! -d "${path}" ]; then + echo ": Path given not a directory..." + exit 1 + fi + + cd "/opt/" + python /opt/.zip "$@" +} +main "$@"; diff --git a/user_config/usr/applications/.desktop b/user_config/usr/applications/.desktop new file mode 100755 index 0000000..d459bfb --- /dev/null +++ b/user_config/usr/applications/.desktop @@ -0,0 +1,11 @@ +[Desktop Entry] +Name= +GenericName= +Comment= +Exec=/bin/ %F +Icon=/usr/share//icons/.png +Type=Application +StartupNotify=true +Categories=System;FileTools;Utility;Core;GTK;FileManager; +MimeType= +Terminal=false diff --git a/user_config/usr/share/app_name/Main_Window.glade b/user_config/usr/share/app_name/Main_Window.glade index 381c685..c1a1964 100644 --- a/user_config/usr/share/app_name/Main_Window.glade +++ b/user_config/usr/share/app_name/Main_Window.glade @@ -1,10 +1,26 @@ - + - + + True False - + vertical + + + True + False + Loaded Me From Glade! + + + False + True + 0 + + + + + diff --git a/user_config/usr/share/app_name/settings.json b/user_config/usr/share/app_name/settings.json index d46cc45..f75af4f 100644 --- a/user_config/usr/share/app_name/settings.json +++ b/user_config/usr/share/app_name/settings.json @@ -1,5 +1,5 @@ { - "settings": { + "config": { "base_of_home": "", "hide_hidden_files": "true", "thumbnailer_path": "ffmpegthumbnailer", @@ -27,5 +27,14 @@ "music": [".psf", ".mp3", ".ogg", ".flac", ".m4a"], "pdf": [".pdf"] + }, + "theming":{ + "success_color":"#88cc27", + "warning_color":"#ffa800", + "error_color":"#ff0000" + }, + "debugging": { + "ch_log_lvl": 10, + "fh_log_lvl": 20 } }