From 62debf9ece02fb93356ea8d84173a375f7fe5565 Mon Sep 17 00:00:00 2001 From: itdominator <1itdominator@gmail.com> Date: Sat, 29 Jun 2024 21:24:57 -0500 Subject: [PATCH] extending plugins to load pre or post app start --- plugins/README.txt | 31 +++++++++- requirements.txt | 10 +-- src/core/controllers/base_controller.py | 5 +- src/core/controllers/base_controller_data.py | 19 +++--- src/core/window.py | 2 +- src/libs/settings/manager.py | 2 +- src/plugins/manifest.py | 31 +++++----- src/plugins/plugins_controller.py | 57 +++++++++++++++--- ...p_name-64x64.png => -64x64.png} | Bin .../icons/{app_name.png => .png} | Bin 10 files changed, 117 insertions(+), 40 deletions(-) rename user_config/usr/share/app_name/icons/{app_name-64x64.png => -64x64.png} (100%) rename user_config/usr/share/app_name/icons/{app_name.png => .png} (100%) diff --git a/plugins/README.txt b/plugins/README.txt index 4173ddd..dfbb0f9 100644 --- a/plugins/README.txt +++ b/plugins/README.txt @@ -1,2 +1,31 @@ ### Note -Copy the example and rename it to your desired name. The Main class and passed in arguments are required. You don't necessarily need to use the passed in socket_id or event_system. +Copy the example and rename it to your desired name. Plugins define a ui target slot with the 'ui_target' requests data but don't have to if not directly interacted with. +Plugins must have a run method defined; though, you do not need to necessarily do anything within it. The run method implies that the passed in event system or other data is ready for the plugin to use. + + +### Manifest Example (All are required even if empty.) +``` +class Manifest: + name: str = "Example Plugin" + author: str = "John Doe" + version: str = "0.0.1" + support: str = "" + requests: {} = { + 'pass_ui_objects': ["plugin_control_list"], + 'pass_events': "true", + 'bind_keys': [] + } + pre_launch: bool = False +``` + + +### Requests +``` +requests: {} = { + 'pass_events': "true", # If empty or not present will be ignored. + "pass_ui_objects": [""], # Request reference to a UI component. Will be passed back as array to plugin. + 'bind_keys': [f"{name}||send_message:f"], + f"{name}||do_save:s"] # Bind keys with method and key pare using list. Must pass "name" like shown with delimiter to its right. + +} +``` diff --git a/requirements.txt b/requirements.txt index feb761b..bfb1be3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,7 @@ -PyGObject +PyGObject==3.40.1 pygobject-stubs --no-cache-dir --config-settings=config=Gtk3,Gdk3,Soup2 -pyxdg -setproctitle -sqlmodel \ No newline at end of file +setproctitle==1.2.2 +pyxdg==0.27 +psutil==5.8.0 +pycryptodome==3.20.0 +sqlmodel==0.0.19 \ No newline at end of file diff --git a/src/core/controllers/base_controller.py b/src/core/controllers/base_controller.py index 8ccd90c..9da61d9 100644 --- a/src/core/controllers/base_controller.py +++ b/src/core/controllers/base_controller.py @@ -28,7 +28,10 @@ class BaseController(IPCSignalsMixin, KeyboardSignalsMixin, BaseControllerData): self._load_controllers() if args.no_plugins == "false": - self.plugins.launch_plugins() + self.plugins_controller.pre_launch_plugins() + + if args.no_plugins == "false": + self.plugins_controller.post_launch_plugins() for file in settings_manager.get_starting_files(): event_system.emit("post-file-to-ipc", file) diff --git a/src/core/controllers/base_controller_data.py b/src/core/controllers/base_controller_data.py index 23f7461..4581209 100644 --- a/src/core/controllers/base_controller_data.py +++ b/src/core/controllers/base_controller_data.py @@ -15,17 +15,18 @@ class BaseControllerData: ''' BaseControllerData 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.window = settings_manager.get_main_window() - self.builder = BuilderWrapper() + self.window = settings_manager.get_main_window() + self.builder = BuilderWrapper() + self.plugins_controller = PluginsController() - self.base_container = None - self.was_midified_key = False - self.ctrl_down = False - self.shift_down = False - self.alt_down = False + self.base_container = None + self.was_midified_key = False + self.ctrl_down = False + self.shift_down = False + self.alt_down = False self._load_glade_file() - self.plugins = PluginsController() + def collect_files_dirs(self, args, unknownargs): files = [] @@ -96,4 +97,4 @@ class BaseControllerData: proc = subprocess.Popen(command, stdin = subprocess.PIPE) proc.stdin.write(data.encode(encoding)) proc.stdin.close() - retcode = proc.wait() \ No newline at end of file + retcode = proc.wait() diff --git a/src/core/window.py b/src/core/window.py index f1f8997..1b09fb4 100644 --- a/src/core/window.py +++ b/src/core/window.py @@ -135,4 +135,4 @@ class Window(Gtk.ApplicationWindow): Gtk.main_quit() def main(self): - Gtk.main() \ No newline at end of file + Gtk.main() diff --git a/src/libs/settings/manager.py b/src/libs/settings/manager.py index cb465bc..ab449da 100644 --- a/src/libs/settings/manager.py +++ b/src/libs/settings/manager.py @@ -193,4 +193,4 @@ class SettingsManager(StartCheckMixin, Singleton): def save_settings(self): with open(self._CONFIG_FILE, 'w') as outfile: - json.dump(self.settings.as_dict(), outfile, separators=(',', ':'), indent=4) \ No newline at end of file + json.dump(self.settings.as_dict(), outfile, separators=(',', ':'), indent=4) diff --git a/src/plugins/manifest.py b/src/plugins/manifest.py index 1b93f34..b2320f5 100644 --- a/src/plugins/manifest.py +++ b/src/plugins/manifest.py @@ -15,13 +15,14 @@ class ManifestProcessor(Exception): class Plugin: - path: str = None - name: str = None - author: str = None - version: str = None - support: str = None - requests:{} = None - reference: type = None + path: str = None + name: str = None + author: str = None + version: str = None + support: str = None + requests:{} = None + reference: type = None + pre_launch: bool = False class ManifestProcessor: @@ -46,23 +47,25 @@ class ManifestProcessor: plugin.support = self._manifest["support"] plugin.requests = self._manifest["requests"] + if "pre_launch" in self._manifest.keys(): + plugin.pre_launch = True if self._manifest["pre_launch"] == "true" else False + return plugin def get_loading_data(self): loading_data = {} requests = self._plugin.requests - keys = requests.keys() - if "pass_events" in keys: + if "pass_events" in requests: if requests["pass_events"] in ["true"]: loading_data["pass_events"] = True - if "bind_keys" in keys: - if isinstance(requests["bind_keys"], list): - loading_data["bind_keys"] = requests["bind_keys"] - - if "pass_ui_objects" in keys: + if "pass_ui_objects" in requests: if isinstance(requests["pass_ui_objects"], list): loading_data["pass_ui_objects"] = [ self._builder.get_object(obj) for obj in requests["pass_ui_objects"] ] + if "bind_keys" in requests: + if isinstance(requests["bind_keys"], list): + loading_data["bind_keys"] = requests["bind_keys"] + return self._plugin, loading_data diff --git a/src/plugins/plugins_controller.py b/src/plugins/plugins_controller.py index a77186c..10d5dc2 100644 --- a/src/plugins/plugins_controller.py +++ b/src/plugins/plugins_controller.py @@ -10,6 +10,7 @@ from os.path import isdir import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk +from gi.repository import GLib from gi.repository import Gio # Application imports @@ -35,11 +36,23 @@ class PluginsController: self._plugins_dir_watcher = None self._plugin_collection = [] + self._plugin_manifests = {} + + self._load_manifests() - def launch_plugins(self) -> None: + def _load_manifests(self): + logger.info(f"Loading manifests...") + + for path, folder in [[join(self._plugins_path, item), item] if os.path.isdir(join(self._plugins_path, item)) else None for item in os.listdir(self._plugins_path)]: + manifest = ManifestProcessor(path, self._builder) + self._plugin_manifests[path] = { + "path": path, + "folder": folder, + "manifest": manifest + } + self._set_plugins_watcher() - self.load_plugins() def _set_plugins_watcher(self) -> None: self._plugins_dir_watcher = Gio.File.new_for_path(self._plugins_path) \ @@ -52,21 +65,47 @@ class PluginsController: Gio.FileMonitorEvent.MOVED_OUT]: self.reload_plugins(file) - def load_plugins(self, file: str = None) -> None: - logger.info(f"Loading plugins...") + def pre_launch_plugins(self) -> None: + logger.info(f"Loading pre-launch plugins...") + plugin_manifests: {} = {} + + for key in self._plugin_manifests: + target_manifest = self._plugin_manifests[key]["manifest"] + if target_manifest.is_pre_launch(): + plugin_manifests[key] = self._plugin_manifests[key] + + self._load_plugins(plugin_manifests, is_pre_launch = True) + + def post_launch_plugins(self) -> None: + logger.info(f"Loading post-launch plugins...") + plugin_manifests: {} = {} + + for key in self._plugin_manifests: + target_manifest = self._plugin_manifests[key]["manifest"] + if not target_manifest.is_pre_launch(): + plugin_manifests[key] = self._plugin_manifests[key] + + self._load_plugins(plugin_manifests) + + def _load_plugins(self, plugin_manifests: {} = {}, is_pre_launch: bool = False) -> None: parent_path = os.getcwd() - for path, folder in [[join(self._plugins_path, item), item] if os.path.isdir(join(self._plugins_path, item)) else None for item in os.listdir(self._plugins_path)]: - try: - target = join(path, "plugin.py") - manifest = ManifestProcessor(path, self._builder) + for key in plugin_manifests: + target_manifest = plugin_manifests[key] + path, folder, manifest = target_manifest["path"], target_manifest["folder"], target_manifest["manifest"] + try: + target = join(path, "plugin.py") if not os.path.exists(target): raise InvalidPluginException("Invalid Plugin Structure: Plugin doesn't have 'plugin.py'. Aboarting load...") plugin, loading_data = manifest.get_loading_data() module = self.load_plugin_module(path, folder, target) - self.execute_plugin(module, plugin, loading_data) + + if is_pre_launch: + self.execute_plugin(module, plugin, loading_data) + else: + GLib.idle_add(self.execute_plugin, *(module, plugin, loading_data)) except Exception as e: logger.info(f"Malformed Plugin: Not loading -->: '{folder}' !") logger.debug("Trace: ", traceback.print_exc()) diff --git a/user_config/usr/share/app_name/icons/app_name-64x64.png b/user_config/usr/share/app_name/icons/-64x64.png similarity index 100% rename from user_config/usr/share/app_name/icons/app_name-64x64.png rename to user_config/usr/share/app_name/icons/-64x64.png diff --git a/user_config/usr/share/app_name/icons/app_name.png b/user_config/usr/share/app_name/icons/.png similarity index 100% rename from user_config/usr/share/app_name/icons/app_name.png rename to user_config/usr/share/app_name/icons/.png