Refactoring, settings cleanup, glade load fixes

This commit is contained in:
itdominator 2023-02-23 18:48:37 -06:00
parent b8306a9c4d
commit 596608642f
15 changed files with 313 additions and 182 deletions

View File

@ -8,11 +8,11 @@ import threading
from utils.event_system import EventSystem from utils.event_system import EventSystem
from utils.endpoint_registry import EndpointRegistry from utils.endpoint_registry import EndpointRegistry
from utils.keybindings import Keybindings from utils.keybindings import Keybindings
from utils.logger import Logger
from utils.settings import Settings from utils.settings import Settings
# NOTE: Threads WILL NOT die with parent's destruction. # NOTE: Threads WILL NOT die with parent's destruction.
def threaded_wrapper(fn): def threaded_wrapper(fn):
def wrapper(*args, **kwargs): 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... # NOTE: Just reminding myself we can add to builtins two different ways...
# __builtins__.update({"event_system": Builtins()}) # __builtins__.update({"event_system": Builtins()})
builtins.app_name = "<change_me>" builtins.app_name = "<change_me>"
@ -35,7 +34,9 @@ builtins.keybindings = Keybindings()
builtins.event_system = EventSystem() builtins.event_system = EventSystem()
builtins.endpoint_registry = EndpointRegistry() builtins.endpoint_registry = EndpointRegistry()
builtins.settings = Settings() 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.threaded = threaded_wrapper
builtins.daemon_threaded = daemon_threaded_wrapper builtins.daemon_threaded = daemon_threaded_wrapper

View File

@ -1,6 +1,5 @@
# Python imports # Python imports
import os import os
import time
# Lib imports # Lib imports
@ -9,13 +8,9 @@ from utils.ipc_server import IPCServer
from core.window import Window from core.window import Window
class AppLaunchException(Exception): class AppLaunchException(Exception):
... ...
class ControllerStartExceptiom(Exception):
...
class Application(IPCServer): class Application(IPCServer):

View File

@ -1,5 +1,4 @@
# Python imports # Python imports
import subprocess
# Lib imports # Lib imports
import gi import gi
@ -26,6 +25,8 @@ class Controller(DummyMixin, ControllerData):
self.setup_controller_data() self.setup_controller_data()
self.print_hello_world() # A mixin method from the DummyMixin file self.print_hello_world() # A mixin method from the DummyMixin file
logger.info(f"Made it past {self.__class__} loading...")
def _setup_styling(self): def _setup_styling(self):
... ...
@ -43,6 +44,8 @@ class Controller(DummyMixin, ControllerData):
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())
self.builder.expose_object("main_window", self.window)
settings.set_builder(self.builder) settings.set_builder(self.builder)
self.core_widget = CoreWidget() self.core_widget = CoreWidget()
@ -71,16 +74,3 @@ class Controller(DummyMixin, ControllerData):
else: else:
print(f"on_global_key_release_controller > key > {keyname}") print(f"on_global_key_release_controller > key > {keyname}")
print(f"Add logic or remove this from: {self.__class__}") 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()

View File

@ -1,5 +1,6 @@
# Python imports # Python imports
import os import os
import subprocess
# Lib imports # 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. ''' ''' 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: def setup_controller_data(self) -> None:
self.logger = settings.get_logger() self.window = settings.get_main_window()
self.builder = None self.builder = None
self.core_widget = None self.core_widget = None
@ -50,3 +51,15 @@ class ControllerData:
''' Clear children of a gtk widget. ''' ''' Clear children of a gtk widget. '''
for child in widget.get_children(): for child in widget.get_children():
widget.remove(child) 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()

View File

@ -14,6 +14,8 @@ class CoreWidget(Gtk.Box):
def __init__(self): def __init__(self):
super(CoreWidget, self).__init__() super(CoreWidget, self).__init__()
self._builder = settings.get_builder()
self._setup_styling() self._setup_styling()
self._setup_signals() self._setup_signals()
self._load_widgets() self._load_widgets()
@ -28,10 +30,15 @@ class CoreWidget(Gtk.Box):
... ...
def _load_widgets(self): 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) button.connect("clicked", self._hello_world)
self.add(button) self.add(button)
self.add(glade_box)
def _hello_world(self, widget=None, eve=None): def _hello_world(self, widget=None, eve=None):
print("Hello, World!") print("Hello, World!")

View File

@ -15,6 +15,10 @@ from gi.repository import GLib
from core.controller import Controller from core.controller import Controller
class ControllerStartExceptiom(Exception):
...
class Window(Gtk.ApplicationWindow): class Window(Gtk.ApplicationWindow):
@ -23,12 +27,17 @@ class Window(Gtk.ApplicationWindow):
def __init__(self, args, unknownargs): def __init__(self, args, unknownargs):
super(Window, self).__init__() super(Window, self).__init__()
self._controller = None
self._set_window_data() self._set_window_data()
self._setup_styling() self._setup_styling()
self._setup_signals() self._setup_signals()
self._subscribe_to_events()
settings.set_main_window(self)
self._load_widgets(args, unknownargs) self._load_widgets(args, unknownargs)
self.show_all() self.show()
def _setup_styling(self): def _setup_styling(self):
@ -42,9 +51,16 @@ class Window(Gtk.ApplicationWindow):
self.connect("delete-event", self._tear_down) self.connect("delete-event", self._tear_down)
GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, 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): def _load_widgets(self, args, unknownargs):
controller = Controller(args, unknownargs) self._controller = Controller(args, unknownargs)
self.add( controller.get_core_widget() )
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: def _set_window_data(self) -> None:
screen = self.get_screen() screen = self.get_screen()

View File

@ -1,7 +1,7 @@
# Python imports # Python imports
import os import os
import sys import sys
import portlib import importlib
import traceback import traceback
from os.path import join from os.path import join
from os.path import isdir from os.path import isdir
@ -13,7 +13,8 @@ from gi.repository import Gtk
from gi.repository import Gio from gi.repository import Gio
# Application imports # Application imports
from .manifest import Plugin, ManifestProcessor from .manifest import Plugin
from .manifest import ManifestProcessor

View File

@ -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

View File

@ -0,0 +1,4 @@
"""
Settings module
"""
from .settings import Settings

View File

@ -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)

View File

@ -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}")

29
user_config/bin/<change_me> Executable file
View File

@ -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 "<change_me>: Path given not a directory..."
exit 1
fi
cd "/opt/"
python /opt/<change_me>.zip "$@"
}
main "$@";

View File

@ -0,0 +1,11 @@
[Desktop Entry]
Name=<change_me>
GenericName=<change_me>
Comment=<change_me>
Exec=/bin/<change_me> %F
Icon=/usr/share/<change_me>/icons/<change_me>.png
Type=Application
StartupNotify=true
Categories=System;FileTools;Utility;Core;GTK;FileManager;
MimeType=
Terminal=false

View File

@ -1,10 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 --> <!-- Generated with glade 3.40.0 -->
<interface> <interface>
<requires lib="gtk+" version="3.20"/> <requires lib="gtk+" version="3.20"/>
<object class="GtkApplicationWindow" id="Main_Window"> <object class="GtkBox" id="glade_box">
<property name="visible">True</property>
<property name="can-focus">False</property> <property name="can-focus">False</property>
<signal name="key-release-event" handler="on_global_key_release_controller" swapped="no"/> <property name="orientation">vertical</property>
<child>
<object class="GtkLabel" id="glade_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Loaded Me From Glade!</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child> <child>
<placeholder/> <placeholder/>
</child> </child>

View File

@ -1,5 +1,5 @@
{ {
"settings": { "config": {
"base_of_home": "", "base_of_home": "",
"hide_hidden_files": "true", "hide_hidden_files": "true",
"thumbnailer_path": "ffmpegthumbnailer", "thumbnailer_path": "ffmpegthumbnailer",
@ -27,5 +27,14 @@
"music": [".psf", ".mp3", ".ogg", ".flac", ".m4a"], "music": [".psf", ".mp3", ".ogg", ".flac", ".m4a"],
"pdf": [".pdf"] "pdf": [".pdf"]
},
"theming":{
"success_color":"#88cc27",
"warning_color":"#ffa800",
"error_color":"#ff0000"
},
"debugging": {
"ch_log_lvl": 10,
"fh_log_lvl": 20
} }
} }