Compare commits

..

1 Commits

Author SHA1 Message Date
itdominator 619fae8a20 Merge pull request 'develop' (#1) from develop into master
Reviewed-on: #1
2024-02-11 01:53:00 +00:00
50 changed files with 258 additions and 495 deletions

View File

@ -3,13 +3,11 @@ A template project for Python with Gtk applications.
### Requirements
* PyGObject (Gtk introspection library)
* pygobject-stubs (For actually getting pylsp or python-language-server to auto complete in LSPs. Do if GTK3 --no-cache-dir --config-settings=config=Gtk3,Gdk3,Soup2)
* pyxdg (Desktop ".desktop" file parser)
* setproctitle (Define process title to search and kill more easily)
* sqlmodel (SQL databases and is powered by Pydantic and SQLAlchemy)
### Note
* pyrightconfig.json can prompt IDEs that use pyright lsp on where imports are located- look at venvPath and venv. "venvPath" is parent path of "venv" where "venv" is just the name of the folder under the parent path that is the python created venv.
* Move respetive sub folder content under user_config to the same places in Linux. Though, user/share/<app name> can go to ~/.config folder if prefered.
* In additiion, place the plugins folder in the same app folder you moved to /usr/share/<app name> or ~/.config/<app name> .
There are a "\<change_me\>" strings and files that need to be set according to your app's name located at:
@ -22,4 +20,4 @@ There are a "\<change_me\>" strings and files that need to be set according to y
For the user_config, after changing names and files, copy all content to their respective destinations.
The logic follows Debian Dpkg packaging and its placement logic.
The logic follows Debian Dpkg packaging and its placement logic.

View File

@ -1,31 +1,2 @@
### Note
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:<Control>f"],
f"{name}||do_save:<Control>s"] # Bind keys with method and key pare using list. Must pass "name" like shown with delimiter to its right.
}
```
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.

View File

@ -1,13 +0,0 @@
{
"reportUndefinedVariable": false,
"reportUnusedVariable": false,
"reportUnusedImport": true,
"reportDuplicateImport": true,
"executionEnvironments": [
{
"root": "./src/versions/solarfm-0.0.1/solarfm"
}
],
"venvPath": ".",
"venv": ".venv"
}

View File

@ -1,7 +1,4 @@
PyGObject==3.40.1
pygobject-stubs --no-cache-dir --config-settings=config=Gtk3,Gdk3,Soup2
setproctitle==1.2.2
pyxdg==0.27
psutil==5.8.0
pycryptodome==3.20.0
sqlmodel==0.0.19
PyGObject
pyxdg
setproctitle
sqlmodel

View File

@ -11,7 +11,7 @@ from libs.event_system import EventSystem
from libs.endpoint_registry import EndpointRegistry
from libs.keybindings import Keybindings
from libs.logger import Logger
from libs.settings.manager import SettingsManager
from libs.settings_manager.manager import SettingsManager
@ -35,7 +35,7 @@ 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 = "<change_me>"
builtins.app_name = "<change_me>"
builtins.keybindings = Keybindings()
builtins.event_system = EventSystem()

View File

@ -18,7 +18,7 @@ from app import Application
def main(args, unknownargs):
setproctitle(f'{APP_NAME}')
setproctitle(f'{app_name}')
if args.debug == "true":
settings_manager.set_debug(True)

View File

@ -39,7 +39,7 @@ class Application:
message = f"FILE|{arg}"
ipc_server.send_ipc_message(message)
raise AppLaunchException(f"{APP_NAME} IPC Server Exists: Have sent path(s) to it and closing...")
raise AppLaunchException(f"{app_name} IPC Server Exists: Have sent path(s) to it and closing...")
def ipc_realization_check(self, ipc_server):
try:
@ -56,7 +56,7 @@ class Application:
try:
# kill -SIGUSR2 <pid> from Linux/Unix or SIGBREAK signal from Windows
signal.signal(
vars(signal).get("SIGBREAK") or vars(signal).get("SIGUSR2"),
vars(signal).get("SIGBREAK") or vars(signal).get("SIGUSR1"),
debug_signal_handler
)
except ValueError:

View File

@ -1,33 +0,0 @@
# Python imports
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
class BuilderWrapper(Gtk.Builder):
"""docstring for BuilderWrapper."""
def __init__(self):
super(BuilderWrapper, self).__init__()
self.objects = {}
def get_object(self, id: str, use_gtk: bool = True) -> any:
if not use_gtk:
return self.objects[id]
return super(BuilderWrapper, self).get_object(id)
def expose_object(self, id: str, object: any, use_gtk: bool = True) -> None:
if not use_gtk:
self.objects[id] = object
else:
super(BuilderWrapper, self).expose_object(id, object)
def dereference_object(self, id: str) -> None:
del self.objects[id]

View File

@ -22,7 +22,7 @@ class BaseContainer(Gtk.Box):
self._subscribe_to_events()
self._load_widgets()
self.show()
self.show_all()
def _setup_styling(self):
@ -33,8 +33,8 @@ class BaseContainer(Gtk.Box):
...
def _subscribe_to_events(self):
event_system.subscribe("update-transparency", self._update_transparency)
event_system.subscribe("remove-transparency", self._remove_transparency)
event_system.subscribe("update_transparency", self._update_transparency)
event_system.subscribe("remove_transparency", self._remove_transparency)
def _load_widgets(self):
self.add(HeaderContainer())

View File

@ -23,7 +23,7 @@ class BodyContainer(Gtk.Box):
self._subscribe_to_events()
self._load_widgets()
self.show()
self.show_all()
def _setup_styling(self):

View File

@ -21,8 +21,6 @@ class CenterContainer(Gtk.Box):
self._subscribe_to_events()
self._load_widgets()
self.show()
def _setup_styling(self):
self.set_orientation(Gtk.Orientation.VERTICAL)
@ -33,6 +31,7 @@ class CenterContainer(Gtk.Box):
...
def _subscribe_to_events(self):
# event_system.subscribe("handle_file_from_ipc", self.handle_file_from_ipc)
...
def _load_widgets(self):
@ -41,9 +40,6 @@ class CenterContainer(Gtk.Box):
button.connect("clicked", self._hello_world)
button.show()
glade_box.show()
self.add(button)
self.add(glade_box)
self.add( WebkitUI() )

View File

@ -6,8 +6,7 @@ gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
from ..widgets.controls.open_files_button import OpenFilesButton
from ..widgets.controls.transparency_scale import TransparencyScale
from ..widgets.transparency_scale import TransparencyScale
@ -33,19 +32,15 @@ class HeaderContainer(Gtk.Box):
...
def _subscribe_to_events(self):
event_system.subscribe("tggl-top-main-menubar", self.tggl_top_main_menubar)
...
def _load_widgets(self):
button = Gtk.Button(label = "Interactive Debug")
button = Gtk.Button(label = "Interactive Debug")
button.connect("clicked", self._interactive_debug)
self.add( OpenFilesButton() )
self.add( TransparencyScale() )
self.add(TransparencyScale())
self.add(button)
def _interactive_debug(self, widget = None, eve = None):
event_system.emit("load-interactive-debug")
def tggl_top_main_menubar(self):
self.hide() if self.is_visible() else self.show_all()
event_system.emit("load_interactive_debug")

View File

@ -18,8 +18,6 @@ class LeftContainer(Gtk.Box):
self._subscribe_to_events()
self._load_widgets()
self.show()
def _setup_styling(self):
self.set_orientation(Gtk.Orientation.VERTICAL)
@ -30,7 +28,8 @@ class LeftContainer(Gtk.Box):
...
def _subscribe_to_events(self):
# event_system.subscribe("handle_file_from_ipc", self.handle_file_from_ipc)
...
def _load_widgets(self):
...
...

View File

@ -18,8 +18,6 @@ class RightContainer(Gtk.Box):
self._subscribe_to_events()
self._load_widgets()
self.show()
def _setup_styling(self):
self.set_orientation(Gtk.Orientation.VERTICAL)
@ -30,7 +28,8 @@ class RightContainer(Gtk.Box):
...
def _subscribe_to_events(self):
# event_system.subscribe("handle_file_from_ipc", self.handle_file_from_ipc)
...
def _load_widgets(self):
...
...

View File

@ -1,4 +1,5 @@
# Python imports
import os
# Lib imports
import gi
@ -18,8 +19,6 @@ from .bridge_controller import BridgeController
class BaseController(IPCSignalsMixin, KeyboardSignalsMixin, BaseControllerData):
def __init__(self, args, unknownargs):
self.collect_files_dirs(args, unknownargs)
self.setup_controller_data()
self._setup_styling()
@ -28,13 +27,16 @@ class BaseController(IPCSignalsMixin, KeyboardSignalsMixin, BaseControllerData):
self._load_controllers()
if args.no_plugins == "false":
self.plugins_controller.pre_launch_plugins()
self.plugins.launch_plugins()
if args.no_plugins == "false":
self.plugins_controller.post_launch_plugins()
for arg in unknownargs + [args.new_tab,]:
if os.path.isfile(arg):
message = f"FILE|{arg}"
event_system.emit("post_file_to_ipc", message)
for file in settings_manager.get_starting_files():
event_system.emit("post-file-to-ipc", file)
if os.path.isdir(arg):
message = f"DIR|{arg}"
event_system.emit("post_file_to_ipc", message)
logger.info(f"Made it past {self.__class__} loading...")
@ -48,10 +50,10 @@ class BaseController(IPCSignalsMixin, KeyboardSignalsMixin, BaseControllerData):
self.window.connect("key-release-event", self.on_global_key_release_controller)
def _subscribe_to_events(self):
event_system.subscribe("shutting-down", lambda: print("Shutting down..."))
event_system.subscribe("handle-file-from-ipc", self.handle_file_from_ipc)
event_system.subscribe("handle-dir-from-ipc", self.handle_dir_from_ipc)
event_system.subscribe("tggl-top-main-menubar", self._tggl_top_main_menubar)
event_system.subscribe("shutting_down", lambda: print("Shutting down..."))
event_system.subscribe("handle_file_from_ipc", self.handle_file_from_ipc)
event_system.subscribe("handle_dir_from_ipc", self.handle_dir_from_ipc)
event_system.subscribe("tggl_top_main_menubar", self._tggl_top_main_menubar)
def _load_controllers(self):
BridgeController()
@ -59,12 +61,12 @@ class BaseController(IPCSignalsMixin, KeyboardSignalsMixin, BaseControllerData):
def _tggl_top_main_menubar(self):
logger.debug("_tggl_top_main_menubar > stub...")
def _load_glade_file(self):
def setup_builder_and_container(self):
self.builder = Gtk.Builder()
self.builder.add_from_file(settings_manager.get_glade_file())
self.builder.expose_object("main_window", self.window)
settings_manager.set_builder(self.builder)
self.base_container = BaseContainer()
settings_manager.register_signals_to_builder([self, self.base_container])
settings_manager.register_signals_to_builder([self, self.base_container])

View File

@ -7,7 +7,6 @@ from shutil import which
# Application imports
from plugins.plugins_controller import PluginsController
from ..builder_wrapper import BuilderWrapper
@ -15,32 +14,16 @@ 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.plugins_controller = PluginsController()
self.window = settings_manager.get_main_window()
self.builder = None
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()
def collect_files_dirs(self, args, unknownargs):
files = []
for arg in unknownargs + [args.new_tab,]:
if os.path.isdir( arg.replace("file://", "") ):
files.append( f"DIR|{arg.replace('file://', '')}" )
# NOTE: If passing line number with file split against :
if os.path.isfile( arg.replace("file://", "").split(":")[0] ):
files.append( f"FILE|{arg.replace('file://', '')}" )
if len(files) > 0:
settings_manager.set_is_starting_with_file(True)
settings_manager.set_starting_files(files)
self.setup_builder_and_container()
self.plugins = PluginsController()
def get_base_container(self):
return self.base_container
@ -97,4 +80,4 @@ class BaseControllerData:
proc = subprocess.Popen(command, stdin = subprocess.PIPE)
proc.stdin.write(data.encode(encoding))
proc.stdin.close()
retcode = proc.wait()
retcode = proc.wait()

View File

@ -20,19 +20,19 @@ class BridgeController:
...
def _subscribe_to_events(self):
event_system.subscribe("handle-bridge-event", self.handle_bridge_event)
event_system.subscribe("handle_bridge_event", self.handle_bridge_event)
def handle_bridge_event(self, event):
match event.topic:
case "save":
event_system.emit(f"handle-file-event-{event.originator}", (event,))
event_system.emit(f"handle_file_event_{event.originator}", (event,))
case "close":
event_system.emit(f"handle-file-event-{event.originator}", (event,))
event_system.emit(f"handle_file_event_{event.originator}", (event,))
case "load_buffer":
event_system.emit(f"handle-file-event-{event.originator}", (event,))
event_system.emit(f"handle_file_event_{event.originator}", (event,))
case "load_file":
event_system.emit(f"handle-file-event-{event.originator}", (event,))
event_system.emit(f"handle_file_event_{event.originator}", (event,))
case "alert":
content = base64.b64decode( event.content.encode() ).decode("utf-8")
logger.info(f"\nMessage Topic: {event.topic}\nMessage Content: {content}")

View File

@ -1,3 +0,0 @@
"""
Widgets.Controls Module
"""

View File

@ -1,86 +0,0 @@
# Python imports
import os
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('Gdk', '3.0')
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import Gio
# Application imports
class OpenFilesButton(Gtk.Button):
"""docstring for OpenFilesButton."""
def __init__(self):
super(OpenFilesButton, self).__init__()
self._setup_styling()
self._setup_signals()
self._subscribe_to_events()
self._load_widgets()
def _setup_styling(self):
self.set_label("Open File(s)...")
self.set_image( Gtk.Image.new_from_icon_name("gtk-open", 4) )
self.set_always_show_image(True)
self.set_image_position(1) # Left - 0, Right = 1
self.set_hexpand(False)
def _setup_signals(self):
self.connect("button-release-event", self._open_files)
def _subscribe_to_events(self):
event_system.subscribe("open_files", self._open_files)
def _load_widgets(self):
...
def _open_files(self, widget = None, eve = None, gfile = None):
start_dir = None
_gfiles = []
if gfile and gfile.query_exists():
start_dir = gfile.get_parent()
chooser = Gtk.FileChooserDialog("Open File(s)...", None,
Gtk.FileChooserAction.OPEN,
(
Gtk.STOCK_CANCEL,
Gtk.ResponseType.CANCEL,
Gtk.STOCK_OPEN,
Gtk.ResponseType.OK
)
)
chooser.set_select_multiple(True)
try:
folder = widget.get_current_file().get_parent() if not start_dir else start_dir
chooser.set_current_folder( folder.get_path() )
except Exception as e:
...
response = chooser.run()
if not response == Gtk.ResponseType.OK:
chooser.destroy()
return _gfiles
filenames = chooser.get_filenames()
if not filenames:
chooser.destroy()
return _gfiles
for file in filenames:
path = file if os.path.isabs(file) else os.path.abspath(file)
_gfiles.append( Gio.File.new_for_path(path) )
chooser.destroy()
logger.debug(_gfiles)
return _gfiles

View File

@ -37,12 +37,12 @@ class TransparencyScale(Gtk.Scale):
def _load_widgets(self):
adjust = self.get_adjustment()
adjust.set_lower(0)
adjust.set_upper(100)
adjust.set_upper(99)
adjust.set_value(settings.theming.transparency)
adjust.set_step_increment(1.0)
def _update_transparency(self, range):
event_system.emit("remove-transparency")
event_system.emit("remove_transparency")
tp = int(range.get_value())
settings.theming.transparency = tp
event_system.emit("update-transparency")
event_system.emit("update_transparency")

View File

@ -9,14 +9,17 @@ from gi.repository import Gdk
from gi.repository import WebKit2
# Application imports
from libs.settings.other.webkit_ui_settings import WebkitUISettings
from libs.dto.event import Event
from libs.data_types import Event
class WebkitUI(WebKit2.WebView):
def __init__(self):
super(WebkitUI, self).__init__()
# self.get_context().set_sandbox_enabled(False)
self._load_settings()
self._setup_styling()
self._subscribe_to_events()
self._load_view()
@ -24,6 +27,10 @@ class WebkitUI(WebKit2.WebView):
self.show_all()
if settings_manager.is_debug():
inspector = self.get_inspector()
inspector.show()
def _setup_styling(self):
self.set_vexpand(True)
@ -31,8 +38,8 @@ class WebkitUI(WebKit2.WebView):
self.set_background_color( Gdk.RGBA(0, 0, 0, 0.0) )
def _subscribe_to_events(self):
event_system.subscribe(f"ui-message", self.ui_message)
event_system.subscribe(f"ui_message", self.ui_message)
def _load_settings(self):
self.set_settings( WebkitUISettings() )
@ -47,6 +54,7 @@ class WebkitUI(WebKit2.WebView):
def _setup_content_manager(self):
content_manager = self.get_user_content_manager()
content_manager.connect("script-message-received", self._process_js_message)
content_manager.register_script_message_handler("backend")
@ -56,14 +64,45 @@ class WebkitUI(WebKit2.WebView):
try:
event = Event( **json.loads(message) )
event_system.emit("handle-bridge-event", (event,))
event_system.emit("handle_bridge_event", (event,))
except Exception as e:
logger.info(e)
def ui_message(self, message, mtype):
def ui_message(self, mtype, message):
command = f"displayMessage('{message}', '{mtype}', '3')"
self.run_javascript(command, None, None)
def run_javascript(self, script, cancellable, callback):
logger.debug(script)
super().run_javascript(script, cancellable, callback)
class WebkitUISettings(WebKit2.Settings):
def __init__(self):
super(WebkitUISettings, self).__init__()
self._set_default_settings()
# Note: Highly insecure setup but most "app" like setup I could think of.
# Audit heavily any scripts/links ran/clicked under this setup!
def _set_default_settings(self):
# self.set_enable_xss_auditor(True)
# self.set_enable_hyperlink_auditing(True)
self.set_enable_xss_auditor(False)
self.set_enable_hyperlink_auditing(False)
self.set_enable_dns_prefetching(False)
self.set_allow_file_access_from_file_urls(True)
self.set_allow_universal_access_from_file_urls(True)
# self.set_enable_java(True)
self.set_enable_page_cache(False)
self.set_enable_offline_web_application_cache(False)
self.set_enable_html5_local_storage(False)
self.set_enable_html5_database(False)
self.set_print_backgrounds(False)
self.set_enable_tabs_to_links(False)
self.set_enable_fullscreen(True)
self.set_enable_developer_extras(True)
self.set_enable_webrtc(True)
self.set_enable_webaudio(True)
self.set_enable_accelerated_2d_canvas(True)
self.set_user_agent(f"Mozilla/5.0 {app_name}")

View File

@ -0,0 +1,64 @@
# Python imports
# Lib imports
import gi
gi.require_version('Gdk', '3.0')
gi.require_version('WebKit2', '4.0')
from gi.repository import Gdk
from gi.repository import WebKit2
# Application imports
from libs.settings_manager.other.webkit_ui_settings import WebkitUISettings
class WebkitUI(WebKit2.WebView):
def __init__(self):
super(WebkitUI, self).__init__()
self._setup_styling()
self._subscribe_to_events()
self._load_view()
self._setup_content_manager()
self.show_all()
def _setup_styling(self):
self.set_vexpand(True)
self.set_hexpand(True)
self.set_background_color( Gdk.RGBA(0, 0, 0, 0.0) )
def _subscribe_to_events(self):
event_system.subscribe(f"ui_message", self.ui_message)
def _load_settings(self):
self.set_settings( WebkitUISettings() )
def _load_view(self):
path = settings_manager.get_context_path()
data = None
with open(f"{path}/index.html", "r") as f:
data = f.read()
self.load_html(content = data, base_uri = f"file://{path}/")
def _setup_content_manager(self):
content_manager = self.get_user_content_manager()
content_manager.connect("script-message-received", self._process_js_message)
content_manager.register_script_message_handler("backend")
def _process_js_message(self, user_content_manager, js_result):
js_value = js_result.get_js_value()
message = js_value.to_string()
try:
event = Event( **json.loads(message) )
event_system.emit("handle_bridge_event", (event,))
except Exception as e:
logger.info(e)
def ui_message(self, message, mtype):
command = f"displayMessage('{message}', '{mtype}', '3')"
self.run_javascript(command, None, None)

View File

@ -11,7 +11,6 @@ from gi.repository import Gdk
from gi.repository import GLib
# Application imports
from libs.status_icon import StatusIcon
from core.controllers.base_controller import BaseController
@ -28,8 +27,7 @@ class Window(Gtk.ApplicationWindow):
super(Window, self).__init__()
settings_manager.set_main_window(self)
self._status_icon = None
self._controller = None
self._controller = None
self._setup_styling()
self._setup_signals()
@ -43,7 +41,7 @@ class Window(Gtk.ApplicationWindow):
def _setup_styling(self):
self.set_title(f"{APP_NAME}")
self.set_title(f"{app_name}")
self.set_icon_from_file( settings_manager.get_window_icon() )
self.set_gravity(5) # 5 = CENTER
self.set_position(1) # 1 = CENTER, 4 = CENTER_ALWAYS
@ -60,15 +58,14 @@ class Window(Gtk.ApplicationWindow):
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)
event_system.subscribe("load-interactive-debug", self._load_interactive_debug)
event_system.subscribe("tear_down", self._tear_down)
event_system.subscribe("load_interactive_debug", self._load_interactive_debug)
def _load_widgets(self, args, unknownargs):
if settings_manager.is_debug():
self.set_interactive_debugging(True)
self._controller = BaseController(args, unknownargs)
self._status_icon = StatusIcon()
self._controller = BaseController(args, unknownargs)
if not self._controller:
raise ControllerStartException("BaseController exited and doesn't exist...")
@ -93,7 +90,7 @@ class Window(Gtk.ApplicationWindow):
if visual and screen.is_composited() and settings.config.make_transparent == 0:
self.set_visual(visual)
self.set_app_paintable(True)
# self.connect("draw", self._area_draw)
self.connect("draw", self._area_draw)
# bind css file
cssProvider = Gtk.CssProvider()
@ -110,17 +107,17 @@ class Window(Gtk.ApplicationWindow):
def _on_focus_in_event(self, widget, event):
event_system.emit("pause-dnd-signals")
event_system.emit("pause_dnd_signals")
def _on_focus_out_event(self, widget, event):
event_system.emit("listen-dnd-signals")
event_system.emit("listen_dnd_signals")
def _load_interactive_debug(self):
self.set_interactive_debugging(True)
def _tear_down(self, widget = None, eve = None):
event_system.emit("shutting-down")
event_system.emit("shutting_down")
size = self.get_size()
pos = self.get_position()
@ -135,4 +132,4 @@ class Window(Gtk.ApplicationWindow):
Gtk.main_quit()
def main(self):
Gtk.main()
Gtk.main()

View File

@ -1,6 +0,0 @@
"""
DB module
"""
from .models import User
from .db import DB

View File

@ -18,7 +18,7 @@ def debug_signal_handler(signal, frame):
rpdb2.start_embedded_debugger("foobar", True, True)
rpdb2.setbreak(depth=1)
return
except Exception:
except StandardError:
...
try:
@ -26,7 +26,7 @@ def debug_signal_handler(signal, frame):
logger.debug("\n\nStarting embedded rconsole debugger...\n\n")
rconsole.spawn_server()
return
except Exception as ex:
except StandardError as ex:
...
try:
@ -34,15 +34,7 @@ def debug_signal_handler(signal, frame):
logger.debug("\n\nStarting PuDB debugger...\n\n")
set_trace(paused = True)
return
except Exception as ex:
...
try:
import ipdb
logger.debug("\n\nStarting IPDB debugger...\n\n")
ipdb.set_trace()
return
except Exception as ex:
except StandardError as ex:
...
try:
@ -50,11 +42,11 @@ def debug_signal_handler(signal, frame):
logger.debug("\n\nStarting embedded PDB debugger...\n\n")
pdb.Pdb(skip=['gi.*']).set_trace()
return
except Exception as ex:
except StandardError as ex:
...
try:
import code
code.interact()
except Exception as ex:
logger.debug(f"{ex}, returning to normal program flow...")
except StandardError as ex:
logger.debug(f"{ex}, returning to normal program flow...")

View File

@ -13,17 +13,17 @@ from .singleton import Singleton
class IPCServer(Singleton):
""" Create a listener so that other {APP_NAME} instances send requests back to existing instance. """
""" Create a listener so that other {app_name} instances send requests back to existing instance. """
def __init__(self, ipc_address: str = '127.0.0.1', conn_type: str = "socket"):
self.is_ipc_alive = False
self._ipc_port = 4848
self._ipc_address = ipc_address
self._conn_type = conn_type
self._ipc_authkey = b'' + bytes(f'{APP_NAME}-ipc', 'utf-8')
self._ipc_authkey = b'' + bytes(f'{app_name}-ipc', 'utf-8')
self._ipc_timeout = 15.0
if conn_type == "socket":
self._ipc_address = f'/tmp/{APP_NAME}-ipc.sock'
self._ipc_address = f'/tmp/{app_name}-ipc.sock'
elif conn_type == "full_network":
self._ipc_address = '0.0.0.0'
elif conn_type == "full_network_unsecured":
@ -35,7 +35,7 @@ class IPCServer(Singleton):
self._subscribe_to_events()
def _subscribe_to_events(self):
event_system.subscribe("post-file-to-ipc", self.send_ipc_message)
event_system.subscribe("post_file_to_ipc", self.send_ipc_message)
def create_ipc_listener(self) -> None:
@ -74,12 +74,12 @@ class IPCServer(Singleton):
if "FILE|" in msg:
file = msg.split("FILE|")[1].strip()
if file:
event_system.emit("handle-file-from-ipc", file)
event_system.emit("handle_file_from_ipc", file)
if "DIR|" in msg:
file = msg.split("DIR|")[1].strip()
if file:
event_system.emit("handle-dir-from-ipc", file)
event_system.emit("handle_dir_from_ipc", file)
conn.close()
break

View File

@ -67,4 +67,4 @@ class DnDMixin:
files.append(gfile)
event_system.emit('set-pre-drop-dnd', (files,))
event_system.emit('set_pre_drop_dnd', (files,))

View File

@ -68,11 +68,11 @@ class KeyboardSignalsMixin:
if mapping:
self.handle_mapped_key_event(mapping)
else:
self.handle_as_key_event_scope(keyname)
self.handle_as_key_event_scope(mapping)
def handle_mapped_key_event(self, mapping):
try:
self.handle_as_controller_scope(mapping)
self.handle_as_controller_scope()
except Exception:
self.handle_as_plugin_scope(mapping)
@ -86,11 +86,13 @@ class KeyboardSignalsMixin:
sender = ""
eve_type = mapping
self.handle_key_event_system(sender, eve_type)
self.handle_as_key_event_system(sender, eve_type)
def handle_as_key_event_scope(self, mapping):
logger.debug(f"on_global_key_release_controller > key > {keyname}")
def handle_as_key_event_scope(self, keyname):
if self.ctrl_down and not keyname in ["1", "kp_1", "2", "kp_2", "3", "kp_3", "4", "kp_4"]:
self.handle_key_event_system(None, keyname)
self.handle_key_event_system(None, mapping)
def handle_key_event_system(self, sender, eve_type):
event_system.emit(eve_type)

View File

@ -24,8 +24,8 @@ class SettingsManager(StartCheckMixin, Singleton):
def __init__(self):
self._SCRIPT_PTH = path.dirname(path.realpath(__file__))
self._USER_HOME = path.expanduser('~')
self._HOME_CONFIG_PATH = f"{self._USER_HOME}/.config/{APP_NAME.lower()}"
self._USR_PATH = f"/usr/share/{APP_NAME.lower()}"
self._HOME_CONFIG_PATH = f"{self._USER_HOME}/.config/{app_name.lower()}"
self._USR_PATH = f"/usr/share/{app_name.lower()}"
self._USR_CONFIG_FILE = f"{self._USR_PATH}/settings.json"
self._CONTEXT_PATH = f"{self._HOME_CONFIG_PATH}/context_path"
@ -35,10 +35,10 @@ class SettingsManager(StartCheckMixin, Singleton):
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._PID_FILE = f"{self._HOME_CONFIG_PATH}/{app_name.lower()}.pid"
self._UI_WIDEGTS_PATH = f"{self._HOME_CONFIG_PATH}/ui_widgets"
self._CONTEXT_MENU = f"{self._HOME_CONFIG_PATH}/contexct_menu.json"
self._WINDOW_ICON = f"{self._DEFAULT_ICONS}/{APP_NAME.lower()}.png"
self._WINDOW_ICON = f"{self._DEFAULT_ICONS}/{app_name.lower()}.png"
# self._USR_CONFIG_FILE = f"{self._USR_PATH}/settings.json"
# self._PLUGINS_PATH = f"plugins"
@ -46,8 +46,8 @@ class SettingsManager(StartCheckMixin, Singleton):
# self._GLADE_FILE = f"Main_Window.glade"
# self._CSS_FILE = f"stylesheet.css"
# self._KEY_BINDINGS_FILE = f"key-bindings.json"
# self._PID_FILE = f"{APP_NAME.lower()}.pid"
# self._WINDOW_ICON = f"{APP_NAME.lower()}.png"
# self._PID_FILE = f"{app_name.lower()}.pid"
# self._WINDOW_ICON = f"{app_name.lower()}.png"
# self._UI_WIDEGTS_PATH = f"ui_widgets"
# self._CONTEXT_MENU = f"contexct_menu.json"
# self._DEFAULT_ICONS = f"icons"
@ -79,7 +79,7 @@ class SettingsManager(StartCheckMixin, Singleton):
if not path.exists(self._CSS_FILE):
raise MissingConfigError("Unable to find the application Stylesheet file.")
if not path.exists(self._WINDOW_ICON):
self._WINDOW_ICON = f"{self._USR_PATH}/icons/{APP_NAME.lower()}.png"
self._WINDOW_ICON = f"{self._USR_PATH}/icons/{app_name.lower()}.png"
if not path.exists(self._WINDOW_ICON):
raise MissingConfigError("Unable to find the application icon.")
if not path.exists(self._UI_WIDEGTS_PATH):
@ -110,8 +110,6 @@ class SettingsManager(StartCheckMixin, Singleton):
self._trace_debug = False
self._debug = False
self._dirty_start = False
self._passed_in_file = False
self._starting_files = []
def register_signals_to_builder(self, classes=None):
@ -130,6 +128,7 @@ class SettingsManager(StartCheckMixin, Singleton):
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 = []
@ -153,24 +152,21 @@ class SettingsManager(StartCheckMixin, Singleton):
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
def get_starting_files(self) -> []: return self._starting_files
def is_trace_debug(self) -> str: return self._trace_debug
def is_debug(self) -> str: return self._debug
def is_starting_with_file(self) -> bool: return self._passed_in_file
def call_method(self, target_class = None, _method_name = None, data = None):
method_name = str(_method_name)
method = getattr(target_class, method_name, lambda data: f"No valid key passed...\nkey={method_name}\nargs={data}")
return method(data) if data else method()
def set_main_window_x(self, x = 0): self.settings.config.main_window_x = x
def set_main_window_y(self, y = 0): self.settings.config.main_window_y = y
def set_main_window_width(self, width = 800): self.settings.config.main_window_width = width
def set_main_window_height(self, height = 600): self.settings.config.main_window_height = height
def set_main_window_x(self, x = 0): self.settings.config.main_window_x = x
def set_main_window_y(self, y = 0): self.settings.config.main_window_y = y
def set_main_window_width(self, width = 800): self.settings.config.main_window_width = width
def set_main_window_height(self, height = 600): self.settings.config.main_window_height = height
def set_main_window_min_width(self, width = 720): self.settings.config.main_window_min_width = width
def set_main_window_min_height(self, height = 480): self.settings.config.main_window_min_height = height
def set_starting_files(self, files: []) -> None: self._starting_files = files
def set_trace_debug(self, trace_debug):
self._trace_debug = trace_debug
@ -178,8 +174,6 @@ class SettingsManager(StartCheckMixin, Singleton):
def set_debug(self, debug):
self._debug = debug
def set_is_starting_with_file(self, is_passed_in_file: False):
self._passed_in_file = is_passed_in_file
def load_settings(self):
if not path.exists(self._CONFIG_FILE):
@ -193,4 +187,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)
json.dump(self.settings.as_dict(), outfile, separators=(',', ':'), indent=4)

View File

@ -39,4 +39,4 @@ class WebkitUISettings(WebKit2.Settings):
self.set_enable_webaudio(True)
self.set_enable_accelerated_2d_canvas(True)
self.set_user_agent(f"{APP_NAME}")
self.set_user_agent(f"{app_name}")

View File

@ -41,7 +41,7 @@ class StartCheckMixin:
try:
os.kill(pid, 0)
except OSError:
print(f"{APP_NAME} PID file exists but PID is irrelevant; starting dirty...")
print(f"{app_name} PID file exists but PID is irrelevant; starting dirty...")
self._dirty_start = True
return False
@ -53,7 +53,7 @@ class StartCheckMixin:
self._print_pid(pid)
def _print_pid(self, pid):
print(f"{APP_NAME} PID: {pid}")
print(f"{app_name} PID: {pid}")
def _clean_pid(self):
os.unlink(self._PID_FILE)

View File

@ -1,67 +0,0 @@
# Python imports
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('AppIndicator3', '0.1')
from gi.repository import Gtk
from gi.repository import GLib
from gi.repository import AppIndicator3
# Application imports
class StatusIcon():
""" StatusIcon for Application to go to Status Tray. """
def __init__(self):
self._setup_styling()
self._setup_signals()
self._subscribe_to_events()
self._load_widgets()
def _setup_styling(self):
...
def _setup_signals(self):
...
def _subscribe_to_events(self):
...
def _load_widgets(self):
status_menu = Gtk.Menu()
icon_theme = Gtk.IconTheme.get_default()
check_menu_item = Gtk.CheckMenuItem.new_with_label("Update icon")
quit_menu_item = Gtk.MenuItem.new_with_label("Quit")
# Create StatusNotifierItem
self.indicator = AppIndicator3.Indicator.new(
f"{APP_NAME}-statusicon",
"gtk-info",
AppIndicator3.IndicatorCategory.APPLICATION_STATUS)
self.indicator.set_status(AppIndicator3.IndicatorStatus.ACTIVE)
check_menu_item.connect("activate", self.check_menu_item_cb)
quit_menu_item.connect("activate", self.quit_menu_item_cb)
icon_theme.connect('changed', self.icon_theme_changed_cb)
self.indicator.set_menu(status_menu)
status_menu.append(check_menu_item)
status_menu.append(quit_menu_item)
status_menu.show_all()
def update_icon(self, icon_name):
self.indicator.set_icon(icon_name)
def check_menu_item_cb(self, widget, data = None):
icon_name = "parole" if widget.get_active() else "gtk-info"
self.update_icon(icon_name)
def icon_theme_changed_cb(self, theme):
self.update_icon("gtk-info")
def quit_menu_item_cb(self, widget, data = None):
event_system.emit("tear-down")

View File

@ -15,14 +15,13 @@ 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
pre_launch: bool = False
path: str = None
name: str = None
author: str = None
version: str = None
support: str = None
requests:{} = None
reference: type = None
class ManifestProcessor:
@ -47,25 +46,23 @@ 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 requests:
if "pass_events" in keys:
if requests["pass_events"] in ["true"]:
loading_data["pass_events"] = True
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 "bind_keys" in keys:
if isinstance(requests["bind_keys"], list):
loading_data["bind_keys"] = requests["bind_keys"]
if "pass_ui_objects" in keys:
if isinstance(requests["pass_ui_objects"], list):
loading_data["pass_ui_objects"] = [ self._builder.get_object(obj) for obj in requests["pass_ui_objects"] ]
return self._plugin, loading_data

View File

@ -10,7 +10,6 @@ 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
@ -36,23 +35,11 @@ class PluginsController:
self._plugins_dir_watcher = None
self._plugin_collection = []
self._plugin_manifests = {}
self._load_manifests()
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
}
def launch_plugins(self) -> None:
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) \
@ -65,47 +52,21 @@ class PluginsController:
Gio.FileMonitorEvent.MOVED_OUT]:
self.reload_plugins(file)
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:
def load_plugins(self, file: str = None) -> None:
logger.info(f"Loading plugins...")
parent_path = os.getcwd()
for key in plugin_manifests:
target_manifest = plugin_manifests[key]
path, folder, manifest = target_manifest["path"], target_manifest["folder"], target_manifest["manifest"]
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")
target = join(path, "plugin.py")
manifest = ManifestProcessor(path, self._builder)
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)
if is_pre_launch:
self.execute_plugin(module, plugin, loading_data)
else:
GLib.idle_add(self.execute_plugin, *(module, plugin, loading_data))
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())

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -1,24 +1,23 @@
{
"keybindings": {
"help" : "F1",
"rename-files" : ["F2", "<Control>e"],
"open-terminal" : "F4",
"refresh-tab" : ["F5", "<Control>r"],
"delete-files" : "Delete",
"tggl-top-main-menubar" : "Alt_L",
"tggl-top-main-menubar" : "<Control>0",
"trash-files" : "<Shift><Control>t",
"tear-down" : "<Control>q",
"go-up" : "<Control>Up",
"go-home" : "<Control>slash",
"grab-focus-path-entry" : "<Control>l",
"open-files" : "<Control>o",
"show-hide-hidden-files" : "<Control>h",
"keyboard-create-tab" : "<Control>t",
"keyboard-close-tab" : "<Control>w",
"keyboard-copy-files" : "<Control>c",
"keyboard-cut-files" : "<Control>x",
"paste-files" : "<Control>v",
"show-new-file-menu" : "<Control>n"
"rename_files" : ["F2", "<Control>e"],
"open_terminal" : "F4",
"refresh_tab" : ["F5", "<Control>r"],
"delete_files" : "Delete",
"tggl_top_main_menubar" : "Alt_L",
"trash_files" : "<Shift><Control>t",
"tear_down" : "<Control>q",
"go_up" : "<Control>Up",
"go_home" : "<Control>slash",
"grab_focus_path_entry" : "<Control>l",
"open_files" : "<Control>o",
"show_hide_hidden_files" : "<Control>h",
"keyboard_create_tab" : "<Control>t",
"keyboard_close_tab" : "<Control>w",
"keyboard_copy_files" : "<Control>c",
"keyboard_cut_files" : "<Control>x",
"paste_files" : "<Control>v",
"show_new_file_menu" : "<Control>n"
}
}

View File

@ -1,16 +1,3 @@
/* ---- Make most desired things base transparent ---- */
popover,
popover > *,
scrolledwindow > *,
textview > *,
.main-window > .base-container > .body-container,
.main-window > .base-container > .body-container > .left-container,
.main-window > .base-container > .body-container > .center-container,
.main-window > .base-container > .body-container > .right-container {
background: rgba(0, 0, 0, 0.0);
color: rgba(255, 255, 255, 1);
}
.base-container {
margin: 10px;
}
@ -141,4 +128,3 @@ XfdesktopIconView.view:active {
.mw_transparency_97 { background: rgba(39, 43, 52, 0.97); }
.mw_transparency_98 { background: rgba(39, 43, 52, 0.98); }
.mw_transparency_99 { background: rgba(39, 43, 52, 0.99); }
.mw_transparency_100 { background: rgba(39, 43, 52, 1.00); }