From f3b222ec1bb27c97720c1e694cbd958455db2c54 Mon Sep 17 00:00:00 2001 From: itdominator <1itdominator@gmail.com> Date: Fri, 25 Feb 2022 17:53:58 -0600 Subject: [PATCH] import refactoring --- src/__builtins__.py | 22 +++--- src/__init__.py | 53 +------------ src/__main__.py | 5 +- src/context/__init__.py | 3 + .../Controller.py => context/controller.py} | 19 +---- .../controller_data.py} | 46 ++++++++--- src/context/mixins/__init__.py | 3 + src/context/mixins/dummy_mixin.py | 4 + src/controller/__init__.py | 7 -- src/controller/mixins/DummyMixin.py | 4 - src/controller/mixins/__init__.py | 1 - .../IPCServerMixin.py => ipc_server.py} | 9 ++- src/main.py | 52 +++++++++++++ src/plugins/__init__.py | 3 + src/plugins/plugins.py | 78 +++++++++++++++++++ src/utils/__init__.py | 3 - src/utils/{Logger.py => logger.py} | 0 src/utils/{Settings.py => settings.py} | 10 ++- 18 files changed, 214 insertions(+), 108 deletions(-) create mode 100644 src/context/__init__.py rename src/{controller/Controller.py => context/controller.py} (71%) rename src/{controller/Controller_Data.py => context/controller_data.py} (56%) create mode 100644 src/context/mixins/__init__.py create mode 100644 src/context/mixins/dummy_mixin.py delete mode 100644 src/controller/__init__.py delete mode 100644 src/controller/mixins/DummyMixin.py delete mode 100644 src/controller/mixins/__init__.py rename src/{controller/IPCServerMixin.py => ipc_server.py} (84%) create mode 100644 src/main.py create mode 100644 src/plugins/__init__.py create mode 100644 src/plugins/plugins.py rename src/utils/{Logger.py => logger.py} (100%) rename src/utils/{Settings.py => settings.py} (93%) diff --git a/src/__builtins__.py b/src/__builtins__.py index 34de298..bb681c8 100644 --- a/src/__builtins__.py +++ b/src/__builtins__.py @@ -6,26 +6,24 @@ import builtins # Lib imports # Application imports -from controller import IPCServerMixin +from ipc_server import IPCServer -class Builtins(IPCServerMixin): - """Docstring for __builtins__ extender""" +class EventSystem(IPCServer): + """ Inheret IPCServerMixin. Create an pub/sub systems. """ def __init__(self): - # NOTE: The format used is list of [type, target, data] Where: + super(EventSystem, self).__init__() + + # NOTE: The format used is list of [type, target, (data,)] Where: # type is useful context for control flow, # target is the method to call, # data is the method parameters to give # Where data may be any kind of data self._gui_events = [] self._module_events = [] - self.is_ipc_alive = False - self.ipc_authkey = b'app-ipc' - self.ipc_address = '127.0.0.1' - self.ipc_port = 8888 - self.ipc_timeout = 15.0 + # Makeshift fake "events" type system FIFO def _pop_gui_event(self): @@ -44,14 +42,14 @@ class Builtins(IPCServerMixin): self._gui_events.append(event) return None - raise Exception("Invald event format! Please do: [type, target, data]") + raise Exception("Invald event format! Please do: [type, target, (data,)]") def push_module_event(self, event): if len(event) == 3: self._module_events.append(event) return None - raise Exception("Invald event format! Please do: [type, target, data]") + raise Exception("Invald event format! Please do: [type, target, (data,)]") def read_gui_event(self): return self._gui_events[0] @@ -70,7 +68,7 @@ class Builtins(IPCServerMixin): # NOTE: Just reminding myself we can add to builtins two different ways... # __builtins__.update({"event_system": Builtins()}) builtins.app_name = "" -builtins.event_system = Builtins() +builtins.event_system = EventSystem() builtins.event_sleep_time = 0.2 builtins.debug = False builtins.trace_debug = False diff --git a/src/__init__.py b/src/__init__.py index 297bded..90dc8da 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -1,50 +1,3 @@ -# Python imports -import os, inspect, time - -# Lib imports - -# Application imports -from utils import Settings -from controller import Controller -from __builtins__ import Builtins - - - - -class Main(Builtins): - def __init__(self, args, unknownargs): - if not debug: - event_system.create_ipc_server() - - time.sleep(0.2) - if not trace_debug: - if not event_system.is_ipc_alive: - if unknownargs: - for arg in unknownargs: - if os.path.isdir(arg): - message = f"FILE|{arg}" - event_system.send_ipc_message(message) - - raise Exception("IPC Server Exists: Will send data to it and close...") - - - settings = Settings() - settings.create_window() - - controller = Controller(settings, args, unknownargs) - if not controller: - raise Exception("Controller exited and doesn't exist...") - - # Gets the methods from the classes and sets to handler. - # Then, builder from settings will connect to any signals it needs. - classes = [controller] - 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)) - - settings.get_builder().connect_signals(handlers) +""" + Start of package. +""" diff --git a/src/__main__.py b/src/__main__.py index 366ef2a..8b547b0 100644 --- a/src/__main__.py +++ b/src/__main__.py @@ -1,6 +1,5 @@ #!/usr/bin/python3 - # Python imports import argparse, faulthandler, traceback from setproctitle import setproctitle @@ -15,10 +14,12 @@ gi.require_version('Gtk', '3.0') from gi.repository import Gtk # Application imports -from __init__ import Main +from main import Main if __name__ == "__main__": + ''' Set process title, get arguments, and create GTK main thread. ''' + try: # import web_pdb # web_pdb.set_trace() diff --git a/src/context/__init__.py b/src/context/__init__.py new file mode 100644 index 0000000..90cfadc --- /dev/null +++ b/src/context/__init__.py @@ -0,0 +1,3 @@ +""" + Gtk Bound Signal Module +""" diff --git a/src/controller/Controller.py b/src/context/controller.py similarity index 71% rename from src/controller/Controller.py rename to src/context/controller.py index 40ddc5b..e274d48 100644 --- a/src/controller/Controller.py +++ b/src/context/controller.py @@ -8,8 +8,8 @@ gi.require_version('Gtk', '3.0') from gi.repository import Gtk, GLib # Application imports -from .mixins import * -from . import Controller_Data +from .mixins.dummy_mixin import DummyMixin +from .controller_data import Controller_Data @@ -42,22 +42,11 @@ class Controller(DummyMixin, Controller_Data): if event: try: type, target, data = event - if not type: - method = getattr(self.__class__, target) - GLib.idle_add(method, *(self, *data,)) - else: - method = getattr(self.__class__, "hadle_gui_event_and_call_back") - GLib.idle_add(method, *(self, type, target, data)) + method = getattr(self.__class__, target) + GLib.idle_add(method, *(self, *data,)) except Exception as e: print(repr(e)) - def hadle_gui_event_and_call_back(self, type, target, parameters): - method = getattr(self.__class__, target) - data = method(*(self, *parameters)) - event_system.push_module_event([type, None, (data,)]) - - - def handle_file_from_ipc(self, path): print(f"Path From IPC: {path}") diff --git a/src/controller/Controller_Data.py b/src/context/controller_data.py similarity index 56% rename from src/controller/Controller_Data.py rename to src/context/controller_data.py index c54df69..f4604e2 100644 --- a/src/controller/Controller_Data.py +++ b/src/context/controller_data.py @@ -5,22 +5,15 @@ import os, signal from gi.repository import GLib # Application imports - +from plugins.plugins import Plugins class Controller_Data: - def clear_console(self): - os.system('cls' if os.name == 'nt' else 'clear') - - def call_method(self, _method_name, data = None): - method_name = str(_method_name) - method = getattr(self, method_name, lambda data: f"No valid key passed...\nkey={method_name}\nargs={data}") - return method(data) if data else method() - - def has_method(self, obj, name): - return callable(getattr(obj, name, None)) + ''' Controller_Data contains most of the state of the app at ay given time. It also has some support methods. ''' def setup_controller_data(self, _settings): + self.plugins = Plugins(_settings) + self.settings = _settings self.builder = self.settings.get_builder() self.window = self.settings.get_main_window() @@ -33,3 +26,34 @@ class Controller_Data: self.window.connect("delete-event", self.tear_down) GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, self.tear_down) + + + def clear_console(self): + ''' Clears the terminal screen. ''' + os.system('cls' if os.name == 'nt' else 'clear') + + def call_method(self, _method_name, data = None): + ''' + Calls a method from scope of class. + + Parameters: + a (obj): self + b (str): method name to be called + c (*): Data (if any) to be passed to the method. + Note: It must be structured according to the given methods requirements. + + Returns: + Return data is that which the calling method gives. + ''' + method_name = str(_method_name) + method = getattr(self, method_name, lambda data: f"No valid key passed...\nkey={method_name}\nargs={data}") + return method(data) if data else method() + + def has_method(self, obj, name): + ''' Checks if a given method exists. ''' + return callable(getattr(obj, name, None)) + + def clear_children(self, widget): + ''' Clear children of a gtk widget. ''' + for child in widget.get_children(): + widget.remove(child) diff --git a/src/context/mixins/__init__.py b/src/context/mixins/__init__.py new file mode 100644 index 0000000..4589fc7 --- /dev/null +++ b/src/context/mixins/__init__.py @@ -0,0 +1,3 @@ +""" + Generic Mixins Module +""" diff --git a/src/context/mixins/dummy_mixin.py b/src/context/mixins/dummy_mixin.py new file mode 100644 index 0000000..72f3db0 --- /dev/null +++ b/src/context/mixins/dummy_mixin.py @@ -0,0 +1,4 @@ +class DummyMixin: + """ DummyMixin is an example of how mixins are used and structured in a project. """ + def print_hello_world(self): + print("Hello, World!") diff --git a/src/controller/__init__.py b/src/controller/__init__.py deleted file mode 100644 index 52c4098..0000000 --- a/src/controller/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -""" - Gtk Bound Signal Module -""" -from .mixins import * -from .IPCServerMixin import IPCServerMixin -from .Controller_Data import Controller_Data -from .Controller import Controller diff --git a/src/controller/mixins/DummyMixin.py b/src/controller/mixins/DummyMixin.py deleted file mode 100644 index 6dc5461..0000000 --- a/src/controller/mixins/DummyMixin.py +++ /dev/null @@ -1,4 +0,0 @@ -class DummyMixin: - """docstring for DummyMixin""" - def print_hello_world(self): - print("Hello, World!") diff --git a/src/controller/mixins/__init__.py b/src/controller/mixins/__init__.py deleted file mode 100644 index e6b3866..0000000 --- a/src/controller/mixins/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .DummyMixin import DummyMixin diff --git a/src/controller/IPCServerMixin.py b/src/ipc_server.py similarity index 84% rename from src/controller/IPCServerMixin.py rename to src/ipc_server.py index 7410fb8..03cd2d9 100644 --- a/src/controller/IPCServerMixin.py +++ b/src/ipc_server.py @@ -15,7 +15,14 @@ def threaded(fn): -class IPCServerMixin: +class IPCServer: + ''' Create a listener so that other instances send requests back to existing instance. ''' + def __init__(self): + self.is_ipc_alive = False + self.ipc_authkey = b'app-ipc' + self.ipc_address = '127.0.0.1' + self.ipc_port = 8888 + self.ipc_timeout = 15.0 @threaded def create_ipc_server(self): diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..628614d --- /dev/null +++ b/src/main.py @@ -0,0 +1,52 @@ +# Python imports +import os, inspect, time + +# Lib imports + +# Application imports +from utils.settings import Settings +from context.controller import Controller +from __builtins__ import EventSystem + + + + +class Main(EventSystem): + ''' Create Settings and Controller classes. Bind signal to Builder. Inherit from Builtins to bind global methods and classes.''' + + def __init__(self, args, unknownargs): + if not debug: + event_system.create_ipc_server() + + time.sleep(0.2) + if not trace_debug: + if not event_system.is_ipc_alive: + if unknownargs: + for arg in unknownargs: + if os.path.isdir(arg): + message = f"FILE|{arg}" + event_system.send_ipc_message(message) + + raise Exception("IPC Server Exists: Will send data to it and close...") + + + settings = Settings() + settings.create_window() + + controller = Controller(settings, args, unknownargs) + if not controller: + raise Exception("Controller exited and doesn't exist...") + + # Gets the methods from the classes and sets to handler. + # Then, builder from settings will connect to any signals it needs. + classes = [controller] + 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)) + + settings.get_builder().connect_signals(handlers) diff --git a/src/plugins/__init__.py b/src/plugins/__init__.py new file mode 100644 index 0000000..5624b32 --- /dev/null +++ b/src/plugins/__init__.py @@ -0,0 +1,3 @@ +""" + Gtk Bound Plugins Module +""" diff --git a/src/plugins/plugins.py b/src/plugins/plugins.py new file mode 100644 index 0000000..b77ab32 --- /dev/null +++ b/src/plugins/plugins.py @@ -0,0 +1,78 @@ +# Python imports +import os, sys, importlib, traceback +from os.path import join, isdir + +# Lib imports +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, Gio + +# Application imports + + +class Plugin: + name = None + module = None + reference = None + + +class Plugins: + """Plugins controller""" + + def __init__(self, settings): + self._settings = settings + self._builder = self._settings.get_builder() + self._plugins_path = self._settings.get_plugins_path() + self._plugins_dir_watcher = None + self._plugin_collection = [] + + + def launch_plugins(self): + self._set_plugins_watcher() + self.load_plugins() + + def _set_plugins_watcher(self): + self._plugins_dir_watcher = Gio.File.new_for_path(self._plugins_path) \ + .monitor_directory(Gio.FileMonitorFlags.WATCH_MOVES, Gio.Cancellable()) + self._plugins_dir_watcher.connect("changed", self._on_plugins_changed, ()) + + def _on_plugins_changed(self, file_monitor, file, other_file=None, eve_type=None, data=None): + if eve_type in [Gio.FileMonitorEvent.CREATED, Gio.FileMonitorEvent.DELETED, + Gio.FileMonitorEvent.RENAMED, Gio.FileMonitorEvent.MOVED_IN, + Gio.FileMonitorEvent.MOVED_OUT]: + self.reload_plugins(file) + + def load_plugins(self, file=None): + print(f"Loading plugins...") + parent_path = os.getcwd() + + for file in os.listdir(self._plugins_path): + try: + path = join(self._plugins_path, file) + if isdir(path): + os.chdir(path) + + sys.path.insert(0, path) + spec = importlib.util.spec_from_file_location(file, join(path, "__main__.py")) + app = importlib.util.module_from_spec(spec) + spec.loader.exec_module(app) + + plugin_reference = app.Plugin(self._builder, event_system) + plugin = Plugin() + plugin.name = plugin_reference.get_plugin_name() + plugin.module = path + plugin.reference = plugin_reference + + self._plugin_collection.append(plugin) + except Exception as e: + print("Malformed plugin! Not loading!") + traceback.print_exc() + + os.chdir(parent_path) + + + def reload_plugins(self, file=None): + print(f"Reloading plugins...") + + def set_message_on_plugin(self, type, data): + print("Trying to send message to plugin...") diff --git a/src/utils/__init__.py b/src/utils/__init__.py index 415301e..a8e5edd 100644 --- a/src/utils/__init__.py +++ b/src/utils/__init__.py @@ -1,6 +1,3 @@ """ Utils module """ - -from .Logger import Logger -from .Settings import Settings diff --git a/src/utils/Logger.py b/src/utils/logger.py similarity index 100% rename from src/utils/Logger.py rename to src/utils/logger.py diff --git a/src/utils/Settings.py b/src/utils/settings.py similarity index 93% rename from src/utils/Settings.py rename to src/utils/settings.py index 364bc5b..a7cab15 100644 --- a/src/utils/Settings.py +++ b/src/utils/settings.py @@ -11,7 +11,8 @@ from gi.repository import Gdk # Application imports -from . import Logger +from .logger import Logger + @@ -20,6 +21,7 @@ class Settings: 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._CSS_FILE = f"{self._CONFIG_PATH}/stylesheet.css" self._DEFAULT_ICONS = f"{self._CONFIG_PATH}/icons" @@ -28,6 +30,9 @@ class Settings: 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._CSS_FILE): @@ -42,7 +47,7 @@ class Settings: 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') + self._images_filter = ('.png', '.jpg', '.jpeg', '.gif', '.ico', '.tga') self._pdf_filter = ('.pdf') self._success_color = "#88cc27" @@ -102,6 +107,7 @@ class Settings: def get_logger(self): return self._logger def get_main_window(self): return self._main_window def get_home_path(self): return self._USER_HOME + def get_plugins_path(self): return self._PLUGINS_PATH # Filter returns def get_office_filter(self): return self._office_filter