From cda2eb45c560f35d2b4ec798600509c6eaea67a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Parodi=2C=20Eugenio=20=F0=9F=8C=B6?= Date: Fri, 4 Apr 2025 19:40:36 +0100 Subject: [PATCH] feat: added plugins and use apptemplate in ttkode --- apps/ttkode/ttkode/__init__.py | 4 ++ apps/ttkode/ttkode/__main__.py | 2 +- apps/ttkode/ttkode/app/main.py | 7 ++ apps/ttkode/ttkode/app/ttkode.py | 24 ++++--- apps/ttkode/ttkode/helper.py | 83 +++++++++++++++++++++++ apps/ttkode/ttkode/plugin.py | 43 ++++++++++++ apps/ttkode/ttkode/plugins/testplugin.py | 33 +++++++++ apps/ttkode/ttkode/plugins/testplugin1.py | 36 ++++++++++ apps/ttkode/ttkode/proxy.py | 48 +++++++++++++ 9 files changed, 268 insertions(+), 12 deletions(-) create mode 100644 apps/ttkode/ttkode/helper.py create mode 100644 apps/ttkode/ttkode/plugin.py create mode 100644 apps/ttkode/ttkode/plugins/testplugin.py create mode 100644 apps/ttkode/ttkode/plugins/testplugin1.py create mode 100644 apps/ttkode/ttkode/proxy.py diff --git a/apps/ttkode/ttkode/__init__.py b/apps/ttkode/ttkode/__init__.py index f0f5e076..691fdfcd 100755 --- a/apps/ttkode/ttkode/__init__.py +++ b/apps/ttkode/ttkode/__init__.py @@ -23,3 +23,7 @@ # SOFTWARE. __version__:str = '0.2.15-a.2' + +from .helper import TTkodeHelper +from .plugin import TTkodePlugin +from .proxy import TTkodeViewerProxy, TTkodeProxy, tloggProxy \ No newline at end of file diff --git a/apps/ttkode/ttkode/__main__.py b/apps/ttkode/ttkode/__main__.py index 22bab16d..eecaa338 100644 --- a/apps/ttkode/ttkode/__main__.py +++ b/apps/ttkode/ttkode/__main__.py @@ -22,7 +22,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -from .app import main +from ttkode.app import main if __name__ == '__main__': main() \ No newline at end of file diff --git a/apps/ttkode/ttkode/app/main.py b/apps/ttkode/ttkode/app/main.py index 07f1f066..354aa3f4 100644 --- a/apps/ttkode/ttkode/app/main.py +++ b/apps/ttkode/ttkode/app/main.py @@ -31,9 +31,12 @@ import appdirs from TermTk import TTk, TTkTerm, TTkTheme from TermTk import TTkLog +from ttkode import TTkodeHelper + from .ttkode import TTKode from .cfg import TTKodeCfg + def main(): TTKodeCfg.pathCfg = appdirs.user_config_dir("ttkode") @@ -57,6 +60,8 @@ def main(): TTkTheme.loadTheme(TTkTheme.NERD) + TTkodeHelper._loadPlugins() + root = TTk( layout=TTKode(files=args.filename), title="TTkode", sigmask=( # TTkTerm.Sigmask.CTRL_C | @@ -64,4 +69,6 @@ def main(): TTkTerm.Sigmask.CTRL_S | TTkTerm.Sigmask.CTRL_Z )) + TTkodeHelper._runPlugins() + root.mainloop() diff --git a/apps/ttkode/ttkode/app/ttkode.py b/apps/ttkode/ttkode/app/ttkode.py index 3f320fa5..c7b509df 100644 --- a/apps/ttkode/ttkode/app/ttkode.py +++ b/apps/ttkode/ttkode/app/ttkode.py @@ -36,8 +36,10 @@ from TermTk import TTkFileDialogPicker from TermTk import TTkFileTree, TTkTextEdit from TermTk import TTkGridLayout -from TermTk import TTkSplitter +from TermTk import TTkSplitter,TTkAppTemplate from TermTk import TextDocumentHighlight +from TermTk import TTkLogViewer +from TermTk import TTkMenuBarLayout from TermTk import TTkAbout from .about import About @@ -56,18 +58,15 @@ class TTKode(TTkGridLayout): super().__init__(**kwargs) - self.addWidget(splitter := TTkSplitter()) - layoutLeft = TTkGridLayout() - splitter.addItem(layoutLeft, 20) - - hSplitter = TTkSplitter(parent=splitter, orientation=TTkK.HORIZONTAL) - menuFrame = TTkFrame(border=False, maxHeight=1) + appTemplate = TTkAppTemplate(border=False) + self.addWidget(appTemplate) - self._kodeTab = TTkKodeTab(parent=hSplitter, border=False, closable=True) + self._kodeTab = TTkKodeTab(border=False, closable=True) - fileMenu = menuFrame.newMenubarTop().addMenu("&File") + appTemplate.setMenuBar(appMenuBar:=TTkMenuBarLayout(), TTkAppTemplate.LEFT) + fileMenu = appMenuBar.addMenu("&File") fileMenu.addMenu("Open").menuButtonClicked.connect(self._showFileDialog) fileMenu.addMenu("Close") # .menuButtonClicked.connect(self._closeFile) fileMenu.addMenu("Exit").menuButtonClicked.connect(lambda _:TTkHelper.quit()) @@ -77,16 +76,19 @@ class TTKode(TTkGridLayout): def _showAboutTTk(btn): TTkHelper.overlay(None, TTkAbout(), 30,10) - helpMenu = menuFrame.newMenubarTop().addMenu("&Help", alignment=TTkK.RIGHT_ALIGN) + helpMenu = appMenuBar.addMenu("&Help", alignment=TTkK.RIGHT_ALIGN) helpMenu.addMenu("About ...").menuButtonClicked.connect(_showAbout) helpMenu.addMenu("About ttk").menuButtonClicked.connect(_showAboutTTk) fileTree = TTkFileTree(path='.') - layoutLeft.addWidget(menuFrame, 0,0) layoutLeft.addWidget(fileTree, 1,0) layoutLeft.addWidget(quitbtn := TTkButton(border=True, text="Quit", maxHeight=3), 2,0) + appTemplate.setWidget(self._kodeTab, TTkAppTemplate.MAIN) + appTemplate.setItem(layoutLeft, TTkAppTemplate.LEFT, size=30) + appTemplate.setWidget(TTkLogViewer(), TTkAppTemplate.BOTTOM, title="Logs", size=3) + quitbtn.clicked.connect(TTkHelper.quit) for file in files: diff --git a/apps/ttkode/ttkode/helper.py b/apps/ttkode/ttkode/helper.py new file mode 100644 index 00000000..5e9b784b --- /dev/null +++ b/apps/ttkode/ttkode/helper.py @@ -0,0 +1,83 @@ +# MIT License +# +# Copyright (c) 2023 Eugenio Parodi +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +__all__ = ["TTkodeHelper"] + +import os +import importlib, pkgutil + +import TermTk as ttk +from .plugin import TTkodePlugin + +class TTkodeHelper(): + @staticmethod + def _loadPlugins(): + # Check for the plugin folder + pluginFolder = os.path.join(os.path.dirname(os.path.abspath(__file__)),'plugins') + if not os.path.exists(pluginFolder): + ttk.TTkLog.error("No 'plugins' folder found in the 'tlogg' main directory") + else: + for fn in os.listdir(pluginFolder): + filePath = os.path.join(pluginFolder,fn) + if not os.path.isfile(filePath): continue + absolute_name = importlib.util.resolve_name(filePath, None) + ttk.TTkLog.debug(absolute_name) + loader = importlib.machinery.SourceFileLoader(fn, filePath) + spec = importlib.util.spec_from_loader(loader.name, loader) + mod = importlib.util.module_from_spec(spec) + loader.exec_module(mod) + + # Check installed plugins + for finder, name, ispkg in pkgutil.iter_modules(): + if name.startswith("tlogg_"): + loader = importlib.find_loader(name) + spec = importlib.util.find_spec(name) + mod = importlib.util.module_from_spec(spec) + # runpy.run_module(name) + loader.exec_module(mod) + + # check the plugin folder + for mod in TTkodePlugin.instances: + if mod.init is not None: + mod.init() + + @staticmethod + def _runPlugins(): + for mod in TTkodePlugin.instances: + if mod.apply is not None: + mod.apply() + + @staticmethod + def _getPlugins(): + return TTkodePlugin.instances + + @staticmethod + def _getPluginPlacements(): + ret = ttk.TTkK.NONE + for mod in TTkodePlugin.instances: + ret |= mod.position + return ret + + @staticmethod + def _getPlacedPlugins(placement): + return [mod for mod in TTkodePlugin.instances if mod.position & placement] + diff --git a/apps/ttkode/ttkode/plugin.py b/apps/ttkode/ttkode/plugin.py new file mode 100644 index 00000000..155bf196 --- /dev/null +++ b/apps/ttkode/ttkode/plugin.py @@ -0,0 +1,43 @@ +# MIT License +# +# Copyright (c) 2023 Eugenio Parodi +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +__all__ = ['TTkodePlugin'] + +from dataclasses import dataclass +from typing import Callable + +import TermTk as ttk + +@dataclass +class TTkodePlugin: + instances = [] + name : str + init : Callable[[],None] = None + apply : Callable[[],None] = None + run : Callable[[],None] = None + position : int = ttk.TTkK.NONE # Accepted Values are ; NONE, LEFT, RIGHT + widget : ttk.TTkWidget = None # Required if a position is defined + menu : bool = False + visible: bool = False + + def __post_init__(self): + TTkodePlugin.instances.append(self) diff --git a/apps/ttkode/ttkode/plugins/testplugin.py b/apps/ttkode/ttkode/plugins/testplugin.py new file mode 100644 index 00000000..3be4becc --- /dev/null +++ b/apps/ttkode/ttkode/plugins/testplugin.py @@ -0,0 +1,33 @@ +# MIT License +# +# Copyright (c) 2023 Eugenio Parodi +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import TermTk as ttk + +import ttkode + +def init(): + ttk.TTkLog.debug("Test Plugin Init") + +def apply(): + ttk.TTkLog.debug("Test Plugin Apply") + +ttkode.TTkodePlugin(name="Test Plugin", init=init, apply=apply) diff --git a/apps/ttkode/ttkode/plugins/testplugin1.py b/apps/ttkode/ttkode/plugins/testplugin1.py new file mode 100644 index 00000000..ffeb350c --- /dev/null +++ b/apps/ttkode/ttkode/plugins/testplugin1.py @@ -0,0 +1,36 @@ +# MIT License +# +# Copyright (c) 2023 Eugenio Parodi +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import TermTk as ttk + +import ttkode + +def init(): + ttk.TTkLog.debug("Test Plugin1 Init") + +def apply(): + ttk.TTkLog.debug("Test Plugin1 Apply") + +def run(): + ttk.TTkLog.debug("Test Plugin1 Run") + +ttkode.TTkodePlugin(name="Test Plugin 1", init=init, apply=apply, run=run) \ No newline at end of file diff --git a/apps/ttkode/ttkode/proxy.py b/apps/ttkode/ttkode/proxy.py new file mode 100644 index 00000000..5aac29f9 --- /dev/null +++ b/apps/ttkode/ttkode/proxy.py @@ -0,0 +1,48 @@ +# MIT License +# +# Copyright (c) 2023 Eugenio Parodi +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +__all__ = ['TTkodeViewerProxy', 'TTkodeProxy', 'tloggProxy'] + +import TermTk as ttk + +class TTkodeViewerProxy(): + __slots__ = ('_fileName') + def __init__(self, fileName) -> None: + self._fileName = fileName + + def fileName(self): + return self._fileName + +class TTkodeProxy(): + __slots__ = ('_openFileCb', + # Signals + ) + def __init__(self) -> None: + self._openFileCb = lambda _ : None + + def setOpenFile(self, cb): + self._openFileCb = cb + + def openFile(self, fileName): + return self._openFileCb(fileName) + +tloggProxy = TTkodeProxy() \ No newline at end of file