develop #1
| @@ -8,6 +8,8 @@ A template project for Python with Gtk applications. | |||||||
| * sqlmodel (SQL databases and is powered by Pydantic and SQLAlchemy) | * sqlmodel (SQL databases and is powered by Pydantic and SQLAlchemy) | ||||||
|  |  | ||||||
| ### Note | ### Note | ||||||
|  | * 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: | There are a "\<change_me\>" strings and files that need to be set according to your app's name located at: | ||||||
| * \_\_builtins\_\_.py | * \_\_builtins\_\_.py | ||||||
| * user_config/bin/app_name | * user_config/bin/app_name | ||||||
| @@ -18,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. | 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. | ||||||
| @@ -6,7 +6,7 @@ import sys | |||||||
| # Lib imports | # Lib imports | ||||||
|  |  | ||||||
| # Application imports | # Application imports | ||||||
| from utils.models import engine | from utils.db import DB | ||||||
| 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 | ||||||
| @@ -18,13 +18,17 @@ from utils.settings_manager.manager import SettingsManager | |||||||
| # 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): | ||||||
|         threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=False).start() |         thread = threading.Thread(target = fn, args = args, kwargs = kwargs, daemon = False) | ||||||
|  |         thread.start() | ||||||
|  |         return thread | ||||||
|     return wrapper |     return wrapper | ||||||
|  |  | ||||||
| # NOTE: Threads WILL die with parent's destruction. | # NOTE: Threads WILL die with parent's destruction. | ||||||
| def daemon_threaded_wrapper(fn): | def daemon_threaded_wrapper(fn): | ||||||
|     def wrapper(*args, **kwargs): |     def wrapper(*args, **kwargs): | ||||||
|         threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=True).start() |         thread = threading.Thread(target = fn, args = args, kwargs = kwargs, daemon = True) | ||||||
|  |         thread.start() | ||||||
|  |         return thread | ||||||
|     return wrapper |     return wrapper | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -32,12 +36,12 @@ 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>" | ||||||
| builtins.db                = engine |  | ||||||
|  |  | ||||||
| builtins.keybindings       = Keybindings() | builtins.keybindings       = Keybindings() | ||||||
| builtins.event_system      = EventSystem() | builtins.event_system      = EventSystem() | ||||||
| builtins.endpoint_registry = EndpointRegistry() | builtins.endpoint_registry = EndpointRegistry() | ||||||
| builtins.settings_manager  = SettingsManager() | builtins.settings_manager  = SettingsManager() | ||||||
|  | builtins.db                = DB() | ||||||
|  |  | ||||||
| settings_manager.load_settings() | settings_manager.load_settings() | ||||||
|  |  | ||||||
| @@ -58,4 +62,4 @@ def custom_except_hook(exc_type, exc_value, exc_traceback): | |||||||
|  |  | ||||||
|     logger.error("Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback)) |     logger.error("Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback)) | ||||||
|  |  | ||||||
| sys.excepthook = custom_except_hook | sys.excepthook = custom_except_hook | ||||||
| @@ -15,6 +15,7 @@ class AppLaunchException(Exception): | |||||||
|     ... |     ... | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class Application(IPCServer): | class Application(IPCServer): | ||||||
|     """ docstring for Application. """ |     """ docstring for Application. """ | ||||||
|  |  | ||||||
| @@ -30,7 +31,7 @@ class Application(IPCServer): | |||||||
|                         message = f"FILE|{arg}" |                         message = f"FILE|{arg}" | ||||||
|                         self.send_ipc_message(message) |                         self.send_ipc_message(message) | ||||||
|  |  | ||||||
|                 raise AppLaunchException(f"{app_name} IPC Server Exists: Will send path(s) to it and close...") |                 raise AppLaunchException(f"{app_name} IPC Server Exists: Have sent path(s) to it and closing...") | ||||||
|  |  | ||||||
|         self.setup_debug_hook() |         self.setup_debug_hook() | ||||||
|         Window(args, unknownargs) |         Window(args, unknownargs) | ||||||
| @@ -56,4 +57,4 @@ class Application(IPCServer): | |||||||
|             ) |             ) | ||||||
|         except ValueError: |         except ValueError: | ||||||
|             # Typically: ValueError: signal only works in main thread |             # Typically: ValueError: signal only works in main thread | ||||||
|             ... |             ... | ||||||
| @@ -6,9 +6,8 @@ gi.require_version('Gtk', '3.0') | |||||||
| from gi.repository import Gtk | from gi.repository import Gtk | ||||||
|  |  | ||||||
| # Application imports | # Application imports | ||||||
| from .left_container import LeftContainer | from .header_container import HeaderContainer | ||||||
| from .center_container import CenterContainer | from .body_container import BodyContainer | ||||||
| from .right_container import RightContainer |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -38,12 +37,11 @@ class BaseContainer(Gtk.Box): | |||||||
|         event_system.subscribe("remove_transparency", self._remove_transparency) |         event_system.subscribe("remove_transparency", self._remove_transparency) | ||||||
|  |  | ||||||
|     def _load_widgets(self): |     def _load_widgets(self): | ||||||
|         self.add(LeftContainer()) |         self.add(HeaderContainer()) | ||||||
|         self.add(CenterContainer()) |         self.add(BodyContainer()) | ||||||
|         self.add(RightContainer()) |  | ||||||
|  |  | ||||||
|     def _update_transparency(self): |     def _update_transparency(self): | ||||||
|         self.ctx.add_class(f"mw_transparency_{settings.theming.transparency}") |         self.ctx.add_class(f"mw_transparency_{settings.theming.transparency}") | ||||||
|  |  | ||||||
|     def _remove_transparency(self): |     def _remove_transparency(self): | ||||||
|         self.ctx.remove_class(f"mw_transparency_{settings.theming.transparency}") |         self.ctx.remove_class(f"mw_transparency_{settings.theming.transparency}") | ||||||
							
								
								
									
										44
									
								
								src/core/containers/body_container.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/core/containers/body_container.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | |||||||
|  | # Python imports | ||||||
|  |  | ||||||
|  | # Lib imports | ||||||
|  | import gi | ||||||
|  | gi.require_version('Gtk', '3.0') | ||||||
|  | from gi.repository import Gtk | ||||||
|  |  | ||||||
|  | # Application imports | ||||||
|  | from .left_container import LeftContainer | ||||||
|  | from .center_container import CenterContainer | ||||||
|  | from .right_container import RightContainer | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class BodyContainer(Gtk.Box): | ||||||
|  |     def __init__(self): | ||||||
|  |         super(BodyContainer, self).__init__() | ||||||
|  |  | ||||||
|  |         self.ctx = self.get_style_context() | ||||||
|  |  | ||||||
|  |         self._setup_styling() | ||||||
|  |         self._setup_signals() | ||||||
|  |         self._subscribe_to_events() | ||||||
|  |         self._load_widgets() | ||||||
|  |  | ||||||
|  |         self.show_all() | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     def _setup_styling(self): | ||||||
|  |         self.set_orientation(Gtk.Orientation.HORIZONTAL) | ||||||
|  |         self.ctx.add_class("body-container") | ||||||
|  |         self.set_homogeneous(True) | ||||||
|  |  | ||||||
|  |     def _setup_signals(self): | ||||||
|  |         ... | ||||||
|  |  | ||||||
|  |     def _subscribe_to_events(self): | ||||||
|  |         ... | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     def _load_widgets(self): | ||||||
|  |         self.add(LeftContainer()) | ||||||
|  |         self.add(CenterContainer()) | ||||||
|  |         self.add(RightContainer()) | ||||||
| @@ -6,7 +6,7 @@ gi.require_version('Gtk', '3.0') | |||||||
| from gi.repository import Gtk | from gi.repository import Gtk | ||||||
|  |  | ||||||
| # Application imports | # Application imports | ||||||
| from ..widgets.transparency_scale import TransparencyScale |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class CenterContainer(Gtk.Box): | class CenterContainer(Gtk.Box): | ||||||
| @@ -35,21 +35,12 @@ class CenterContainer(Gtk.Box): | |||||||
|  |  | ||||||
|     def _load_widgets(self): |     def _load_widgets(self): | ||||||
|         glade_box = self._builder.get_object("glade_box") |         glade_box = self._builder.get_object("glade_box") | ||||||
|         glade_box = self._builder.get_object("glade_box") |         button   = Gtk.Button(label = "Click Me!") | ||||||
|         button    = Gtk.Button(label = "Interactive Debug") |  | ||||||
|         button2   = Gtk.Button(label = "Click Me!") |  | ||||||
|  |  | ||||||
|         button.connect("clicked", self._interactive_debug) |         button.connect("clicked", self._hello_world) | ||||||
|         button2.connect("clicked", self._hello_world) |  | ||||||
|  |  | ||||||
|         self.add(TransparencyScale()) |  | ||||||
|         self.add(button) |         self.add(button) | ||||||
|         self.add(button2) |  | ||||||
|         self.add(glade_box) |         self.add(glade_box) | ||||||
|  |  | ||||||
|  |  | ||||||
|     def _interactive_debug(self, widget = None, eve = None): |  | ||||||
|         event_system.emit("load_interactive_debug") |  | ||||||
|  |  | ||||||
|     def _hello_world(self, widget = None, eve = None): |     def _hello_world(self, widget = None, eve = None): | ||||||
|         logger.debug("Hello, World!") |         logger.debug("Hello, World!") | ||||||
							
								
								
									
										46
									
								
								src/core/containers/header_container.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/core/containers/header_container.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | |||||||
|  | # Python imports | ||||||
|  |  | ||||||
|  | # Lib imports | ||||||
|  | import gi | ||||||
|  | gi.require_version('Gtk', '3.0') | ||||||
|  | from gi.repository import Gtk | ||||||
|  |  | ||||||
|  | # Application imports | ||||||
|  | from ..widgets.transparency_scale import TransparencyScale | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class HeaderContainer(Gtk.Box): | ||||||
|  |     def __init__(self): | ||||||
|  |         super(HeaderContainer, self).__init__() | ||||||
|  |  | ||||||
|  |         self.ctx = self.get_style_context() | ||||||
|  |  | ||||||
|  |         self._setup_styling() | ||||||
|  |         self._setup_signals() | ||||||
|  |         self._subscribe_to_events() | ||||||
|  |         self._load_widgets() | ||||||
|  |  | ||||||
|  |         self.show_all() | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     def _setup_styling(self): | ||||||
|  |         self.set_orientation(Gtk.Orientation.HORIZONTAL) | ||||||
|  |         self.ctx.add_class("header-container") | ||||||
|  |  | ||||||
|  |     def _setup_signals(self): | ||||||
|  |         ... | ||||||
|  |  | ||||||
|  |     def _subscribe_to_events(self): | ||||||
|  |         ... | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     def _load_widgets(self): | ||||||
|  |         button    = Gtk.Button(label = "Interactive Debug") | ||||||
|  |         button.connect("clicked", self._interactive_debug) | ||||||
|  |  | ||||||
|  |         self.add(TransparencyScale()) | ||||||
|  |         self.add(button) | ||||||
|  |  | ||||||
|  |     def _interactive_debug(self, widget = None, eve = None): | ||||||
|  |         event_system.emit("load_interactive_debug") | ||||||
| @@ -26,7 +26,7 @@ class TransparencyScale(Gtk.Scale): | |||||||
|         self.set_digits(0) |         self.set_digits(0) | ||||||
|         self.set_value_pos(Gtk.PositionType.RIGHT) |         self.set_value_pos(Gtk.PositionType.RIGHT) | ||||||
|         self.add_mark(50.0, Gtk.PositionType.TOP, "50%") |         self.add_mark(50.0, Gtk.PositionType.TOP, "50%") | ||||||
|  |         self.set_hexpand(True) | ||||||
|  |  | ||||||
|     def _setup_signals(self): |     def _setup_signals(self): | ||||||
|         self.connect("value-changed", self._update_transparency) |         self.connect("value-changed", self._update_transparency) | ||||||
| @@ -45,4 +45,4 @@ class TransparencyScale(Gtk.Scale): | |||||||
|         event_system.emit("remove_transparency") |         event_system.emit("remove_transparency") | ||||||
|         tp = int(range.get_value()) |         tp = int(range.get_value()) | ||||||
|         settings.theming.transparency = tp |         settings.theming.transparency = tp | ||||||
|         event_system.emit("update_transparency") |         event_system.emit("update_transparency") | ||||||
							
								
								
									
										38
									
								
								src/utils/db.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/utils/db.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | |||||||
|  | # Python imports | ||||||
|  | from typing import Optional | ||||||
|  | from os import path | ||||||
|  |  | ||||||
|  | # Lib imports | ||||||
|  | from sqlmodel import Session, create_engine | ||||||
|  |  | ||||||
|  | # Application imports | ||||||
|  | from .models import SQLModel, User | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class DB: | ||||||
|  |     def __init__(self): | ||||||
|  |         super(DB, self).__init__() | ||||||
|  |          | ||||||
|  |         self.create_engine() | ||||||
|  |  | ||||||
|  |         # NOTE: for sake of example we create an admin user with no password set. | ||||||
|  |         self.add_user_entry(name = "Admin", password = "", email = "admin@domain.com") | ||||||
|  |  | ||||||
|  |     def create_engine(self): | ||||||
|  |         db_path     = f"sqlite:///{settings_manager.get_home_config_path()}/database.db" | ||||||
|  |         self.engine = create_engine(db_path) | ||||||
|  |  | ||||||
|  |         SQLModel.metadata.create_all(self.engine) | ||||||
|  |  | ||||||
|  |     def _add_entry(self, entry): | ||||||
|  |         with Session(self.engine) as session: | ||||||
|  |             session.add(entry) | ||||||
|  |             session.commit() | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     def add_user_entry(self, name = None, password = None, email = None): | ||||||
|  |         if not name or not password or not email: return | ||||||
|  |  | ||||||
|  |         user   = User(name, password, email) | ||||||
|  |         self._add_entry(user) | ||||||
| @@ -2,7 +2,7 @@ | |||||||
| from typing import Optional | from typing import Optional | ||||||
|  |  | ||||||
| # Lib imports | # Lib imports | ||||||
| from sqlmodel import Field, Session, SQLModel, create_engine | from sqlmodel import SQLModel, Field | ||||||
|  |  | ||||||
| # Application imports | # Application imports | ||||||
|  |  | ||||||
| @@ -13,13 +13,3 @@ class User(SQLModel, table = True): | |||||||
|     name: str |     name: str | ||||||
|     password: str |     password: str | ||||||
|     email: Optional[str] = None |     email: Optional[str] = None | ||||||
|  |  | ||||||
|  |  | ||||||
| # NOTE: for sake of example we create an admin user with no password set. |  | ||||||
| user   = User(name = "Admin", password = "", email = "admin@domain.com") |  | ||||||
| engine = create_engine("sqlite:///database.db") |  | ||||||
| SQLModel.metadata.create_all(engine) |  | ||||||
|  |  | ||||||
| with Session(engine) as session: |  | ||||||
|     session.add(user) |  | ||||||
|     session.commit() |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user