From 75d145d8fbbab3cc3c95284ad759b4ea5fb53831 Mon Sep 17 00:00:00 2001 From: itdominator <1itdominator@gmail.com> Date: Sun, 23 Jan 2022 16:56:27 -0600 Subject: [PATCH] Big refactor --- src/__builtins__.py | 76 ++++++++++++++-- src/__init__.py | 44 ++++++--- src/__main__.py | 27 +++--- src/signal_classes/Controller.py | 62 +++++++++++++ src/signal_classes/Controller_Data.py | 26 ++++++ src/signal_classes/IPCServerMixin.py | 64 +++++++++++++ src/signal_classes/Signals.py | 35 -------- src/signal_classes/__init__.py | 4 +- src/signal_classes/mixins/DummyMixin.py | 4 +- src/utils/Settings.py | 115 +++++++++++++++--------- 10 files changed, 345 insertions(+), 112 deletions(-) create mode 100644 src/signal_classes/Controller.py create mode 100644 src/signal_classes/Controller_Data.py create mode 100644 src/signal_classes/IPCServerMixin.py delete mode 100644 src/signal_classes/Signals.py diff --git a/src/__builtins__.py b/src/__builtins__.py index 261d55d..5087108 100644 --- a/src/__builtins__.py +++ b/src/__builtins__.py @@ -1,13 +1,73 @@ import builtins -class Builtins: - def hello_world(): - print("Hello, world!") +# Python imports +import builtins - def hello_user(): - print("Hello, user!") +# Lib imports + +# Application imports +from signal_classes import IPCServerMixin - # NOTE: There are two ways to add global items. - builtins.hello_user = hello_user - __builtins__.update({"hello_world": hello_world}) + +class Builtins(IPCServerMixin): + """Docstring for __builtins__ extender""" + + def __init__(self): + # NOTE: The format used is list of [type, target, data] + # Where data may be any kind of data + self._gui_events = [] + self._fm_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): + if len(self._gui_events) > 0: + return self._gui_events.pop(0) + return None + + def _pop_fm_event(self): + if len(self._fm_events) > 0: + return self._fm_events.pop(0) + return None + + + def push_gui_event(self, event): + if len(event) == 3: + self._gui_events.append(event) + return None + + raise Exception("Invald event format! Please do: [type, target, data]") + + def push_fm_event(self, event): + if len(event) == 3: + self._fm_events.append(event) + return None + + raise Exception("Invald event format! Please do: [type, target, data]") + + def read_gui_event(self): + return self._gui_events[0] + + def read_fm_event(self): + return self._fm_events[0] + + def consume_gui_event(self): + return self._pop_gui_event() + + def consume_fm_event(self): + return self._pop_fm_event() + + + +# 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_sleep_time = 0.2 +builtins.debug = False +builtins.trace_debug = False diff --git a/src/__init__.py b/src/__init__.py index eb1236b..0863a01 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -1,25 +1,43 @@ # Python imports -import inspect - - -# Gtk imports +import os, inspect, time +# Lib imports # Application imports from utils import Settings -from signal_classes import Signals +from signal_classes import Controller from __builtins__ import Builtins + + class Main(Builtins): - def __init__(self, args): + 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() - builder = settings.returnBuilder() + settings.create_window() + + controller = Controller(args, unknownargs, settings) + if not controller: + raise Exception("Controller exited and doesn't exist...") # Gets the methods from the classes and sets to handler. - # Then, builder connects to any signals it needs. - classes = [Signals(settings)] - + # Then, builder from settings will connect to any signals it needs. + classes = [controller] handlers = {} for c in classes: methods = None @@ -27,8 +45,6 @@ class Main(Builtins): methods = inspect.getmembers(c, predicate=inspect.ismethod) handlers.update(methods) except Exception as e: - pass + print(repr(e)) - builder.connect_signals(handlers) - window = settings.createWindow() - window.show() + settings.get_builder().connect_signals(handlers) diff --git a/src/__main__.py b/src/__main__.py index 9873357..c3eb784 100644 --- a/src/__main__.py +++ b/src/__main__.py @@ -2,14 +2,17 @@ # Python imports -import argparse +import argparse, faulthandler, traceback from setproctitle import setproctitle -# Gtk imports -import gi, faulthandler, signal +import tracemalloc +tracemalloc.start() + + +# Lib imports +import gi gi.require_version('Gtk', '3.0') -from gi.repository import Gtk as gtk -from gi.repository import GLib +from gi.repository import Gtk # Application imports from __init__ import Main @@ -17,16 +20,20 @@ from __init__ import Main if __name__ == "__main__": try: + # import web_pdb + # web_pdb.set_trace() + setproctitle('') - GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, gtk.main_quit) faulthandler.enable() # For better debug info parser = argparse.ArgumentParser() # Add long and short arguments parser.add_argument("--file", "-f", default="default", help="JUST SOME FILE ARG.") # Read arguments (If any...) - args = parser.parse_args() - main = Main(args) - gtk.main() + args, unknownargs = parser.parse_known_args() + + Main(args, unknownargs) + Gtk.main() except Exception as e: - print( repr(e) ) + traceback.print_exc() + quit() diff --git a/src/signal_classes/Controller.py b/src/signal_classes/Controller.py new file mode 100644 index 0000000..15ffc87 --- /dev/null +++ b/src/signal_classes/Controller.py @@ -0,0 +1,62 @@ +# Python imports +import threading, signal, inspect, os, time + + +# Gtk imports +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, GLib + +# Application imports +from .mixins import * +from . import Controller_Data + + + +def threaded(fn): + def wrapper(*args, **kwargs): + threading.Thread(target=fn, args=args, kwargs=kwargs).start() + + return wrapper + + +class Controller(DummyMixin, Controller_Data): + def __init__(self, _settings, args, unknownargs): + self.setup_controller_data(_settings) + self.window.show() + self.print_hello_world() # A mixin method from the DummyMixin file + + + def tear_down(self, widget=None, eve=None): + event_system.send_ipc_message("close server") + + time.sleep(event_sleep_time) + Gtk.main_quit() + + + @threaded + def gui_event_observer(self): + while True: + time.sleep(event_sleep_time) + event = event_system.consume_gui_event() + if event: + try: + type, target, data = event + method = getattr(self.__class__, type) + GLib.idle_add(method, (self, data,)) + except Exception as e: + print(repr(e)) + + + + def get_clipboard_data(self): + 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): + proc = subprocess.Popen(['xclip','-selection','clipboard'], stdin=subprocess.PIPE) + proc.stdin.write(data) + proc.stdin.close() + retcode = proc.wait() diff --git a/src/signal_classes/Controller_Data.py b/src/signal_classes/Controller_Data.py new file mode 100644 index 0000000..bf5181e --- /dev/null +++ b/src/signal_classes/Controller_Data.py @@ -0,0 +1,26 @@ +# Python imports + +# Lib imports +from gi.repository import GLib + +# Application imports + + + +class Controller_Data: + def has_method(self, obj, name): + return callable(getattr(obj, name, None)) + + def setup_controller_data(self, _settings): + self.settings = _settings + self.builder = self.settings.get_builder() + self.window = self.settings.get_main_window() + self.logger = self.settings.get_logger() + + self.home_path = self.settings.get_home_path() + self.success_color = self.settings.get_success_color() + self.warning_color = self.settings.get_warning_color() + self.error_color = self.settings.get_error_color() + + self.window.connect("delete-event", self.tear_down) + GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, self.tear_down) diff --git a/src/signal_classes/IPCServerMixin.py b/src/signal_classes/IPCServerMixin.py new file mode 100644 index 0000000..be92ace --- /dev/null +++ b/src/signal_classes/IPCServerMixin.py @@ -0,0 +1,64 @@ +# Python imports +import threading, socket, time +from multiprocessing.connection import Listener, Client + +# Lib imports + +# Application imports + + +def threaded(fn): + def wrapper(*args, **kwargs): + threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=True).start() + return wrapper + + + + +class IPCServerMixin: + + @threaded + def create_ipc_server(self): + listener = Listener((self.ipc_address, self.ipc_port), authkey=self.ipc_authkey) + self.is_ipc_alive = True + while True: + conn = listener.accept() + start_time = time.time() + + print(f"New Connection: {listener.last_accepted}") + while True: + msg = conn.recv() + if debug: + print(msg) + + if "FILE|" in msg: + file = msg.split("FILE|")[1].strip() + if file: + event_system.push_gui_event(["create_tab_from_ipc", None, file]) + + conn.close() + break + + + if msg == 'close connection': + conn.close() + break + if msg == 'close server': + conn.close() + break + + # NOTE: Not perfect but insures we don't lockup the connection for too long. + end_time = time.time() + if (end - start) > self.ipc_timeout: + conn.close() + + listener.close() + + + def send_ipc_message(self, message="Empty Data..."): + try: + conn = Client((self.ipc_address, self.ipc_port), authkey=self.ipc_authkey) + conn.send(message) + conn.send('close connection') + except Exception as e: + print(repr(e)) diff --git a/src/signal_classes/Signals.py b/src/signal_classes/Signals.py deleted file mode 100644 index 16e33db..0000000 --- a/src/signal_classes/Signals.py +++ /dev/null @@ -1,35 +0,0 @@ -# Python imports -import threading, subprocess, os - -# Gtk imports - -# Application imports -from .mixins import * - - -def threaded(fn): - def wrapper(*args, **kwargs): - threading.Thread(target=fn, args=args, kwargs=kwargs).start() - - return wrapper - - -class Signals(DummyMixin): - def __init__(self, settings): - self.settings = settings - self.builder = self.settings.returnBuilder() - - hello_world() # A global method from the __builtins__ file that added it - - - def getClipboardData(self): - proc = subprocess.Popen(['xclip','-selection', 'clipboard', '-o'], stdout=subprocess.PIPE) - retcode = proc.wait() - data = proc.stdout.read() - return data.decode("utf-8").strip() - - def setClipboardData(self, data): - proc = subprocess.Popen(['xclip','-selection','clipboard'], stdin=subprocess.PIPE) - proc.stdin.write(data) - proc.stdin.close() - retcode = proc.wait() diff --git a/src/signal_classes/__init__.py b/src/signal_classes/__init__.py index 159e1e6..52c4098 100644 --- a/src/signal_classes/__init__.py +++ b/src/signal_classes/__init__.py @@ -2,4 +2,6 @@ Gtk Bound Signal Module """ from .mixins import * -from .Signals import Signals +from .IPCServerMixin import IPCServerMixin +from .Controller_Data import Controller_Data +from .Controller import Controller diff --git a/src/signal_classes/mixins/DummyMixin.py b/src/signal_classes/mixins/DummyMixin.py index 61d44f7..6dc5461 100644 --- a/src/signal_classes/mixins/DummyMixin.py +++ b/src/signal_classes/mixins/DummyMixin.py @@ -1,4 +1,4 @@ class DummyMixin: """docstring for DummyMixin""" - def printHelloWorld(self): - print("Hello World!") + def print_hello_world(self): + print("Hello, World!") diff --git a/src/utils/Settings.py b/src/utils/Settings.py index 05b7b0f..7e12833 100644 --- a/src/utils/Settings.py +++ b/src/utils/Settings.py @@ -6,79 +6,110 @@ import gi, cairo gi.require_version('Gtk', '3.0') gi.require_version('Gdk', '3.0') -from gi.repository import Gtk as gtk -from gi.repository import Gdk as gdk +from gi.repository import Gtk +from gi.repository import Gdk # Application imports +from . import Logger + class Settings: def __init__(self): - self.SCRIPT_PTH = os.path.dirname(os.path.realpath(__file__)) + "/" - self.builder = gtk.Builder() - self.builder.add_from_file(self.SCRIPT_PTH + "../resources/Main_Window.glade") + self._SCRIPT_PTH = os.path.dirname(os.path.realpath(__file__)) + self._USR_HOME = os.path.expanduser('~') + self._CONFIG_PATH = f"{self._USER_HOME}/.config/{app_name}" + 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" + self._WINDOW_ICON = f"{self.DEFAULT_ICONS}/{app_name}.png" + self._USR_PATH = "/usr/share/{app_name}" - # 'Filters' - self.office = ('.doc', '.docx', '.xls', '.xlsx', '.xlt', '.xltx', '.xlm', - '.ppt', 'pptx', '.pps', '.ppsx', '.odt', '.rtf') - self.vids = ('.mkv', '.avi', '.flv', '.mov', '.m4v', '.mpg', '.wmv', - '.mpeg', '.mp4', '.webm') - self.txt = ('.txt', '.text', '.sh', '.cfg', '.conf') - self.music = ('.psf', '.mp3', '.ogg' , '.flac') - self.images = ('.png', '.jpg', '.jpeg', '.gif') - self.pdf = ('.pdf') + self._logger = Logger().get_logger() + self._builder = Gtk.Builder() + self._main_window = None + + # '_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') + self._pdf_filter = ('.pdf') + + self._success_color = "#88cc27" + self._warning_color = "#ffa800" + self._error_color = "#ff0000" + + 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): + 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}.png" + if not os.path.exists(self.DEFAULT_ICONS): + self.DEFAULT_ICONS = f"{self._USR_PATH}/icons" + + self._builder.add_from_file(self._GLADE_FILE) - def createWindow(self): + + def create_window(self): # Get window and connect signals - window = self.builder.get_object("Main_Window") - window.connect("delete-event", gtk.main_quit) - self.setWindowData(window, False) - return window + self._main_window = self._builder.get_object("Main_Window") + self.set_window_data() - def setWindowData(self, window, paintable): - screen = window.get_screen() + def set_window_data(self): + self._main_window.set_icon_from_file(self._WINDOW_ICON) + screen = self._main_window.get_screen() visual = screen.get_rgba_visual() if visual != None and screen.is_composited(): - window.set_visual(visual) + self._main_window.set_visual(visual) + self._main_window.set_app_paintable(True) + self._main_window.connect("draw", self.draw_area) # bind css file - cssProvider = gtk.CssProvider() - cssProvider.load_from_path(self.SCRIPT_PTH + '../resources/stylesheet.css') - screen = gdk.Screen.get_default() - styleContext = gtk.StyleContext() - styleContext.add_provider_for_screen(screen, cssProvider, gtk.STYLE_PROVIDER_PRIORITY_USER) + cssProvider = Gtk.CssProvider() + cssProvider.load_from_path(self._CSS_FILE) + screen = Gdk.Screen.get_default() + styleContext = Gtk.StyleContext() + styleContext.add_provider_for_screen(screen, cssProvider, Gtk.STYLE_PROVIDER_PRIORITY_USER) - window.set_app_paintable(paintable) - if paintable: - window.connect("draw", self.area_draw) - - def getMonitorData(self): - screen = self.builder.get_object("Main_Window").get_screen() + def get_monitor_data(self): + screen = self._builder.get_object("Main_Window").get_screen() monitors = [] for m in range(screen.get_n_monitors()): monitors.append(screen.get_monitor_geometry(m)) for monitor in monitors: - print("{}x{}+{}+{}".format(monitor.width, monitor.height, monitor.x, monitor.y)) + print("{}x{}|{}+{}".format(monitor.width, monitor.height, monitor.x, monitor.y)) return monitors - def area_draw(self, widget, cr): + def draw_area(self, widget, cr): cr.set_source_rgba(0, 0, 0, 0.54) cr.set_operator(cairo.OPERATOR_SOURCE) cr.paint() cr.set_operator(cairo.OPERATOR_OVER) - def returnBuilder(self): return self.builder + + + def get_builder(self): return self._builder + def get_logger(self): return self._logger + def get_main_window(self): return self._main_window + def get_home_path(self): return self._USR_HOME # Filter returns - def returnOfficeFilter(self): return self.office - def returnVidsFilter(self): return self.vids - def returnTextFilter(self): return self.txt - def returnMusicFilter(self): return self.music - def returnImagesFilter(self): return self.images - def returnPdfFilter(self): return self.pdf + def get_office_filter(self): return self.office_filter + def get_vids_filter(self): return self.vids_filter + def get_text_filter(self): return self.txt_filter + def get_music_filter(self): return self.music_filter + def get_images_filter(self): return self.images_filter + def get_pdf_filter(self): return self.pdf_filter + + def get_success_color(self): return self._success_color + def get_warning_color(self): return self._warning_color + def get_error_color(self): return self._error_color