From 1afa8702547d25cb617b7c0d9891eb0c43703cf9 Mon Sep 17 00:00:00 2001 From: Eugenio Parodi Date: Sun, 7 Mar 2021 20:34:02 +0000 Subject: [PATCH] Improved the documentation, initial packaging --- .gitignore | 1 + Makefile | 36 ++++- README.md | 8 +- TermTk/TTkTemplates/__init__.py | 0 TermTk/TTkTemplates/keyevents.py | 56 ++++++++ TermTk/TTkTemplates/mouseevents.py | 112 ++++++++++++++++ TermTk/TTkWidgets/widget.py | 149 ++++++++++++++++----- tests/test.showcase.001.py => demo/demo.py | 0 {tests => demo}/gittk.py | 0 {tests => demo}/showcase/formwidgets.py | 0 {tests => demo}/showcase/graph.py | 0 {tests => demo}/showcase/layout.py | 0 {tests => demo}/showcase/layoutnested.py | 0 {tests => demo}/showcase/list.py | 0 {tests => demo}/showcase/menubar.py | 0 {tests => demo}/showcase/scrollarea.py | 0 {tests => demo}/showcase/splitter.py | 0 {tests => demo}/showcase/tab.py | 0 {tests => demo}/showcase/table.py | 0 {tests => demo}/showcase/tree.py | 0 {tests => demo}/showcase/windows.py | 0 setup.py | 44 ++++++ 22 files changed, 368 insertions(+), 38 deletions(-) create mode 100644 TermTk/TTkTemplates/__init__.py create mode 100644 TermTk/TTkTemplates/keyevents.py create mode 100644 TermTk/TTkTemplates/mouseevents.py rename tests/test.showcase.001.py => demo/demo.py (100%) rename {tests => demo}/gittk.py (100%) rename {tests => demo}/showcase/formwidgets.py (100%) rename {tests => demo}/showcase/graph.py (100%) rename {tests => demo}/showcase/layout.py (100%) rename {tests => demo}/showcase/layoutnested.py (100%) rename {tests => demo}/showcase/list.py (100%) rename {tests => demo}/showcase/menubar.py (100%) rename {tests => demo}/showcase/scrollarea.py (100%) rename {tests => demo}/showcase/splitter.py (100%) rename {tests => demo}/showcase/tab.py (100%) rename {tests => demo}/showcase/table.py (100%) rename {tests => demo}/showcase/tree.py (100%) rename {tests => demo}/showcase/windows.py (100%) diff --git a/.gitignore b/.gitignore index 0a11441d..667b326a 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ tests/test.dummy.py tmp profiler.txt .vscode +docs/html # C extensions *.so diff --git a/Makefile b/Makefile index 93d50e4a..ba95cd4e 100644 --- a/Makefile +++ b/Makefile @@ -1,2 +1,34 @@ -doc: - sphinx-build -b html doc/source doc/build \ No newline at end of file +.PHONY: doc, runGittk, runDemo, build, testdeploy, deploy + +.venv: + python3 -m venv .venv + . .venv/bin/activate + pip3 install --upgrade pdoc3 + pip3 install --upgrade GitPython + pip3 install --upgrade build + pip3 install --upgrade twine + +doc: .venv + . .venv/bin/activate + rm -rf docs/html + pdoc --html TermTk -o docs/html + +runGittk: .venv + . .venv/bin/activate + demo/gittk.py -f + +runDemo: .venv + . .venv/bin/activate + demo/demo.py -f + +build: .venv + . .venv/bin/activate + python3 -m build + +testDeploy: .venv + . .venv/bin/activate + python3 -m twine upload --repository testpypi dist/* --verbose + +deploy: .venv + . .venv/bin/activate + python3 -m twine upload --repository TermTk dist/* diff --git a/README.md b/README.md index 2fee2f67..97faf3f7 100644 --- a/README.md +++ b/README.md @@ -30,11 +30,15 @@ python3 tests/test.input.py ```shell # Press CTRL-C to exit # the logs are written to "session.log" -python3 tests/test.showcase.001.py -f +make runDemo +# or +python3 demo/demo.py -f # Try gittk +make runGittk +# or pip3 install GitPython -tests/gittk.py +demo/gittk.py -f ``` #### Profiling ##### cProfile diff --git a/TermTk/TTkTemplates/__init__.py b/TermTk/TTkTemplates/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/TermTk/TTkTemplates/keyevents.py b/TermTk/TTkTemplates/keyevents.py new file mode 100644 index 00000000..68f23b63 --- /dev/null +++ b/TermTk/TTkTemplates/keyevents.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 + +# MIT License +# +# Copyright (c) 2021 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. + +class TKeyEvents(): + def keyPressEvent(self, evt) -> bool : + ''' + This event handler, can be reimplemented in a subclass to receive key press events for the widget. + .. note:: Reimplement this function to handle this event + Args: + evt ([TermTk.libbpytop.input.MouseEvent]): the mouse event + Returns: + bool: True if the event has been handled + ''' + return False + def keyReleaseEvent(self, evt) -> bool : + ''' + This event handler, can be reimplemented in a subclass to receive key release events for the widget. + .. note:: Reimplement this function to handle this event + Args: + evt ([TermTk.libbpytop.input.MouseEvent]): the mouse event + Returns: + bool: True if the event has been handled + ''' + return False + + def keyEvent(self, evt) -> bool : + ''' + This event handler, can be reimplemented in a subclass to receive key events for the widget. + .. note:: Reimplement this function to handle this event + Args: + evt ([TermTk.libbpytop.input.MouseEvent]): the mouse event + Returns: + bool: True if the event has been handled + ''' + return False diff --git a/TermTk/TTkTemplates/mouseevents.py b/TermTk/TTkTemplates/mouseevents.py new file mode 100644 index 00000000..2e501058 --- /dev/null +++ b/TermTk/TTkTemplates/mouseevents.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python3 + +# MIT License +# +# Copyright (c) 2021 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. + +class TMouseEvents(): + def mouseDoubleClickEvent(self, evt) -> bool : + ''' + This event handler, can be reimplemented in a subclass to receive mouse click events for the widget. + .. note:: Reimplement this function to handle this event + Args: + evt ([TermTk.libbpytop.input.MouseEvent]): the mouse event + Returns: + bool: True if the event has been handled + ''' + return False + + def mouseMoveEvent(self, evt) -> bool : + ''' + This event handler, can be reimplemented in a subclass to receive mouse move events for the widget. + .. note:: Reimplement this function to handle this event + Args: + evt ([TermTk.libbpytop.input.MouseEvent]): the mouse event + Returns: + bool: True if the event has been handled + ''' + return False + + def mouseDragEvent(self, evt) -> bool : + ''' + This event handler, can be reimplemented in a subclass to receive mouse drag events for the widget. + .. note:: Reimplement this function to handle this event + Args: + evt ([TermTk.libbpytop.input.MouseEvent]): the mouse event + Returns: + bool: True if the event has been handled + ''' + return False + + def mousePressEvent(self, evt) -> bool : + ''' + This event handler, can be reimplemented in a subclass to receive mouse press events for the widget. + .. note:: Reimplement this function to handle this event + Args: + evt ([TermTk.libbpytop.input.MouseEvent]): the mouse event + Returns: + bool: True if the event has been handled + ''' + return False + + def mouseReleaseEvent(self, evt) -> bool : + ''' + This event handler, can be reimplemented in a subclass to receive mouse release events for the widget. + .. note:: Reimplement this function to handle this event + Args: + evt ([TermTk.libbpytop.input.MouseEvent]): the mouse event + Returns: + bool: True if the event has been handled + ''' + return False + + def wheelEvent(self, evt) -> bool : + ''' + This event handler, can be reimplemented in a subclass to receive mouse wheel events for the widget. + .. note:: Reimplement this function to handle this event + Args: + evt ([TermTk.libbpytop.input.MouseEvent]): the mouse event + Returns: + bool: True if the event has been handled + ''' + return False + + def enterEvent(self, evt) -> bool : + ''' + This event handler, can be reimplemented in a subclass to receive mouse enter events for the widget. + .. note:: Reimplement this function to handle this event + Args: + evt ([TermTk.libbpytop.input.MouseEvent]): the mouse event + Returns: + bool: True if the event has been handled + ''' + return False + + def leaveEvent(self, evt) -> bool : + ''' + This event handler, can be reimplemented in a subclass to receive mouse leave events for the widget. + .. note:: Reimplement this function to handle this event + Args: + evt ([TermTk.libbpytop.input.MouseEvent]): the mouse event + Returns: + bool: True if the event has been handled + ''' + return False diff --git a/TermTk/TTkWidgets/widget.py b/TermTk/TTkWidgets/widget.py index db4013ad..cdfdab62 100644 --- a/TermTk/TTkWidgets/widget.py +++ b/TermTk/TTkWidgets/widget.py @@ -28,24 +28,27 @@ from TermTk.TTkCore.log import TTkLog from TermTk.TTkCore.helper import TTkHelper from TermTk.TTkCore.canvas import TTkCanvas from TermTk.TTkCore.signal import pyTTkSignal, pyTTkSlot +from TermTk.TTkTemplates.mouseevents import TMouseEvents +from TermTk.TTkTemplates.keyevents import TKeyEvents from TermTk.TTkLayouts.layout import TTkLayout, TTkWidgetItem import TermTk.libbpytop as lbt -class TTkWidget: +class TTkWidget(TMouseEvents,TKeyEvents): ''' - Terminal - ┌─────────────────────────────────────────┐ - │ │ - │ TTkWidget width │ - │ (x,y)┌─────────────────────────┐ │ - │ │ padt │ │ - │ │ ┌───────────────┐ │ height │ - │ │padl│ Layout/childs │padr│ │ - │ │ └───────────────┘ │ │ - │ │ padl │ │ - │ └─────────────────────────┘ │ - └─────────────────────────────────────────┘ + ### Widget Layout sizes: + Terminal window + ┌─────────────────────────────────────────┐ + │ │ + │ TTkWidget width │ + │ (x,y)┌─────────────────────────┐ │ + │ │ padt │ │ + │ │ ┌───────────────┐ │ height │ + │ │padl│ Layout/childs │padr│ │ + │ │ └───────────────┘ │ │ + │ │ padl │ │ + │ └─────────────────────────┘ │ + └─────────────────────────────────────────┘ ''' __slots__ = ( '_name', '_parent', @@ -56,6 +59,36 @@ class TTkWidget: '_layout', '_canvas', '_visible', '_transparent') def __init__(self, *args, **kwargs): + ''' + TTkWidget constructor + + Args: + name (str, optional): the name of the widget + parent ([TermTk.TTkWidgets.widget.TTkWidget], optional): the parent widget + + x (int, optional, default=0): the x position + y (int, optional, default=0): the y position + pos ([int,int], optional, default=[0,0]): the [x,y] position (override the previously defined x, y) + + width (int, optional, default=0): the width of the widget + height (int, optional, default=0): the height of the widget + size ([int,int], optional, default=[0,0]): the size [width, height] of the widget (override the previously defined sizes) + + padding (int, optional, default=0): the padding (top, bottom, left, right) of the widget + paddingTop (int, optional, default=padding): the Top padding, override Top padding if already defined + paddingBottom (int, optional, default=padding): the Bottom padding, override Bottom padding if already defined + paddingLeft (int, optional, default=padding): the Left padding, override Left padding if already defined + paddingRight (int, optional, default=padding): the Right padding, override Right padding if already defined + maxWidth (int, optional, default=0x10000): the maxWidth of the widget + maxHeight (int, optional, default=0x10000): the maxHeight of the widget + maxSize ([int,int], optional): the max [width,height] of the widget + minWidth (int, optional, default=0): the minWidth of the widget + minHeight (int, optional, default=0): the minHeight of the widget + minSize ([int,int], optional): the minSize [width,height] of the widget + + visible (bool, optional, default=True): the visibility + layout ([TermTk.TTkLayouts], optional, default=[TermTk.TTkLayouts.layout.TTkLayout]): the layout of this widget + ''' self._name = kwargs.get('name', 'TTkWidget' ) self._parent = kwargs.get('parent', None ) @@ -100,12 +133,19 @@ class TTkWidget: self.update(repaint=True, updateLayout=True) def __del__(self): + ''' .. caution:: Don't touch this! ''' TTkLog.debug("DESTRUCTOR") if self._parent is not None: self._parent.removeWidget(self) self._parent = None def addWidget(self, widget): + ''' + Add a child widget to the layout + + Args: + widget ([TermTk.TTkWidgets.widget.TTkWidget]): the widget to be added + ''' widget._parent = self if self.layout() is not None: self.layout().addWidget(widget) @@ -113,14 +153,26 @@ class TTkWidget: # widget.show() def removeWidget(self, widget): + ''' + Remove the child widget from the layout + + Args: + widget ([TermTk.TTkWidgets.widget.TTkWidget]): the widget to be removed + ''' if self.layout() is not None: self.layout().removeWidget(widget) self.update(repaint=True, updateLayout=True) - def paintEvent(self): pass + def paintEvent(self): + ''' + Pain Event callback, + ths need to be overridden in the widget. + ''' + pass @staticmethod def _paintChildCanvas(canvas, item, geometry): + ''' .. caution:: Don't touch this! ''' lx,ly,lw,lh = geometry if item.layoutItemType == TTkK.WidgetItem and not item.isEmpty(): child = item.widget() @@ -143,9 +195,11 @@ class TTkWidget: TTkWidget._paintChildCanvas(canvas, child, (bx,by,bw,bh)) def paintChildCanvas(self): + ''' .. caution:: Don't touch this! ''' TTkWidget._paintChildCanvas(self._canvas, self.rootLayout(), self.rootLayout().geometry()) def paintNotifyParent(self): + ''' .. caution:: Don't touch this! ''' parent = self._parent while parent is not None: parent._canvas.clean() @@ -153,17 +207,33 @@ class TTkWidget: parent.paintChildCanvas() parent = parent._parent - def moveEvent(self, x, y): pass - def resizeEvent(self, w, h): pass + def moveEvent(self, x: int, y: int): + ''' Event Callback triggered after a successful move''' + pass + def resizeEvent(self, w: int, h: int): + ''' Event Callback triggered after a successful resize''' + pass - def move(self, x, y): + def move(self, x: int, y: int): + ''' + Move the widget + Args: + x (int): x position + y (int): y position + ''' if x==self._x and y==self._y: return self._x = x self._y = y self.update(repaint=False, updateLayout=False) self.moveEvent(x,y) - def resize(self, w, h): + def resize(self, w: int, h: int): + ''' + Resize the widget + Args: + w (int): the new width + h (int): the new height + ''' # TTkLog.debug(f"resize: {w,h} {self._name}") if w!=self._width or h!=self._height: self._width = w @@ -172,14 +242,35 @@ class TTkWidget: self.update(repaint=True, updateLayout=True) self.resizeEvent(w,h) - def setGeometry(self, x, y, w, h): + def setGeometry(self, x: int, y: int, w: int, h: int): + ''' + Resize and move the widget + Args: + x (int): x position + y (int): y position + w (int): the new width + h (int): the new height + ''' self.resize(w, h) self.move(x, y) - def getPadding(self) -> (int, int, int, int) : + def getPadding(self) -> (int, int, int, int): + ''' + Retrieve the widget padding sizes + Returns: + List[top, bottom, left, right]: the 4 padding sizes + ''' return self._padt, self._padb, self._padl, self._padr def setPadding(self, top, bottom, left, right): + ''' + set the padding of the widget + Args: + top (int): top padding + bottom (int): bottom padding + left (int): left padding + right (int): right padding + ''' if self._padt == top and self._padb == bottom and \ self._padl == left and self._padr == right: return self._padt = top @@ -188,19 +279,9 @@ class TTkWidget: self._padr = right self.update(repaint=True, updateLayout=True) - def mouseDoubleClickEvent(self, evt) -> bool : return False - def mouseMoveEvent(self, evt) -> bool : return False - def mouseDragEvent(self, evt) -> bool : return False - def mousePressEvent(self, evt) -> bool : return False - def mouseReleaseEvent(self, evt) -> bool : return False - def wheelEvent(self, evt) -> bool : return False - def enterEvent(self, evt) -> bool : return False - def leaveEvent(self, evt) -> bool : return False - def keyPressEvent(self, evt) -> bool : return False - def keyReleaseEvent(self, evt) -> bool : return False - @staticmethod def _mouseEventLayoutHandle(evt, layout): + ''' .. caution:: Don't touch this! ''' x, y = evt.x, evt.y lx,ly,lw,lh =layout.geometry() # opt of bounds @@ -243,6 +324,7 @@ class TTkWidget: return False def mouseEvent(self, evt): + ''' .. caution:: Don't touch this! ''' # Mouse Drag has priority because it # should be handled by the focussed widget if evt.evt == TTkK.Drag: @@ -284,9 +366,6 @@ class TTkWidget: # Trigger this event to the childs return False - def keyEvent(self, evt): - pass - #def event(self, evt): # pass # # handle own events @@ -412,6 +491,7 @@ class TTkWidget: @staticmethod def _propagateShowToLayout(layout): + ''' .. caution:: Don't touch this! ''' if layout is None: return for item in layout.zSortedItems: if item.layoutItemType == TTkK.WidgetItem and not item.isEmpty(): @@ -421,6 +501,7 @@ class TTkWidget: TTkWidget._propagateShowToLayout(item) def _propagateShow(self): + ''' .. caution:: Don't touch this! ''' if not self._visible: return self.update(updateLayout=True, updateParent=True) TTkWidget._propagateShowToLayout(self.rootLayout()) diff --git a/tests/test.showcase.001.py b/demo/demo.py similarity index 100% rename from tests/test.showcase.001.py rename to demo/demo.py diff --git a/tests/gittk.py b/demo/gittk.py similarity index 100% rename from tests/gittk.py rename to demo/gittk.py diff --git a/tests/showcase/formwidgets.py b/demo/showcase/formwidgets.py similarity index 100% rename from tests/showcase/formwidgets.py rename to demo/showcase/formwidgets.py diff --git a/tests/showcase/graph.py b/demo/showcase/graph.py similarity index 100% rename from tests/showcase/graph.py rename to demo/showcase/graph.py diff --git a/tests/showcase/layout.py b/demo/showcase/layout.py similarity index 100% rename from tests/showcase/layout.py rename to demo/showcase/layout.py diff --git a/tests/showcase/layoutnested.py b/demo/showcase/layoutnested.py similarity index 100% rename from tests/showcase/layoutnested.py rename to demo/showcase/layoutnested.py diff --git a/tests/showcase/list.py b/demo/showcase/list.py similarity index 100% rename from tests/showcase/list.py rename to demo/showcase/list.py diff --git a/tests/showcase/menubar.py b/demo/showcase/menubar.py similarity index 100% rename from tests/showcase/menubar.py rename to demo/showcase/menubar.py diff --git a/tests/showcase/scrollarea.py b/demo/showcase/scrollarea.py similarity index 100% rename from tests/showcase/scrollarea.py rename to demo/showcase/scrollarea.py diff --git a/tests/showcase/splitter.py b/demo/showcase/splitter.py similarity index 100% rename from tests/showcase/splitter.py rename to demo/showcase/splitter.py diff --git a/tests/showcase/tab.py b/demo/showcase/tab.py similarity index 100% rename from tests/showcase/tab.py rename to demo/showcase/tab.py diff --git a/tests/showcase/table.py b/demo/showcase/table.py similarity index 100% rename from tests/showcase/table.py rename to demo/showcase/table.py diff --git a/tests/showcase/tree.py b/demo/showcase/tree.py similarity index 100% rename from tests/showcase/tree.py rename to demo/showcase/tree.py diff --git a/tests/showcase/windows.py b/demo/showcase/windows.py similarity index 100% rename from tests/showcase/windows.py rename to demo/showcase/windows.py diff --git a/setup.py b/setup.py index e69de29b..3e52e12d 100644 --- a/setup.py +++ b/setup.py @@ -0,0 +1,44 @@ +import setuptools, os, subprocess + +with open("README.md", "r", encoding="utf-8") as fh: + long_description = fh.read() + +# Retrieve the version +out = subprocess.Popen( + ['git','describe'], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) +version, stderr = out.communicate() +version = version.decode("utf-8").strip() + +print(f"Version: {version}") + +setuptools.setup( + name='pyTermTk', + # name='example-pkg-ceccopierangiolieugenio', + version=version, + # version="0.1.0a2", + author='Eugenio Parodi', + author_email='ceccopierangiolieugenio@googlemail.com', + # packages=['TermTk'], + description='Python Terminal Toolkit', + long_description=long_description, + long_description_content_type="text/markdown", + url="https://github.com/ceccopierangiolieugenio/pyTermTk", + classifiers=[ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "License :: OSI Approved :: Apache Software License", + "Operating System :: OS Independent", + "Development Status :: 3 - Alpha", + "Environment :: Console", + "Intended Audience :: Developers", + "Intended Audience :: Information Technology", + "Topic :: Terminals", + "Topic :: Software Development :: User Interfaces"], + # packages=setuptools.find_packages(), + packages = setuptools.find_packages(), + #where = '.', + #include = ['TermTk',]), + python_requires=">=3.6", +) \ No newline at end of file