30 changed files with 1566 additions and 39 deletions
@ -1,2 +1,22 @@ |
|||||||
# pyTermTk |
# pyTermTk |
||||||
Python Terminal Toolkit |
Python Terminal Toolkit |
||||||
|
|
||||||
|
## Quick Test/Try |
||||||
|
|
||||||
|
#### Clone |
||||||
|
```shell |
||||||
|
clone git@github.com:ceccopierangiolieugenio/pyTermTk.git |
||||||
|
pyTermTk |
||||||
|
``` |
||||||
|
|
||||||
|
#### Run Basic input test |
||||||
|
```shell |
||||||
|
python3 tests/test.input.py |
||||||
|
``` |
||||||
|
|
||||||
|
#### Run Terminal resize test |
||||||
|
```shell |
||||||
|
# Press CTRL-C to exit |
||||||
|
# the logs are written to "session.log" |
||||||
|
python3 tests/test.ui.002.py |
||||||
|
``` |
||||||
|
|||||||
@ -0,0 +1,3 @@ |
|||||||
|
from .log import * |
||||||
|
from .cfg import * |
||||||
|
from .ttk import * |
||||||
@ -0,0 +1,120 @@ |
|||||||
|
#!/usr/bin/env python3 |
||||||
|
|
||||||
|
# MIT License |
||||||
|
# |
||||||
|
# Copyright (c) 2021 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com> |
||||||
|
# |
||||||
|
# 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.libbpytop as lbt |
||||||
|
from TermTk.TTkCore.log import TTkLog |
||||||
|
from TermTk.TTkCore.cfg import * |
||||||
|
from TermTk.TTkCore.helper import * |
||||||
|
|
||||||
|
class TTkCanvas: |
||||||
|
''' |
||||||
|
TTkCanvas |
||||||
|
canvas window primitives |
||||||
|
... |
||||||
|
Attributes |
||||||
|
---------- |
||||||
|
Methods |
||||||
|
------- |
||||||
|
__init__({}) |
||||||
|
input obj{ width, height} |
||||||
|
|
||||||
|
resize(w, h) |
||||||
|
- resize the canvas keeping or cutting the current one |
||||||
|
in w = the width of the new canvas |
||||||
|
in h = the height of the new canvas |
||||||
|
''' |
||||||
|
def __init__(self, *args, **kwargs): |
||||||
|
self._widget = kwargs.get('widget', None) |
||||||
|
self._width = kwargs.get('width', 0 ) |
||||||
|
self._height = kwargs.get('height', 0 ) |
||||||
|
self.resize(self._width, self._height) |
||||||
|
TTkLog.debug((self._width, self._height)) |
||||||
|
|
||||||
|
def getWidget(self): return self._widget |
||||||
|
|
||||||
|
def move(self, x, y): |
||||||
|
npos = TTkHelper.absParentPos(self._widget) |
||||||
|
# CuTCore.cuDebug("Move: x:"+str(nx+x)+" y:"+str(ny+y)) |
||||||
|
# self._bufPaint['move']={'x':npos.x()+x, 'y':npos.y()+y} |
||||||
|
TTkHelper.addPaintBuffer(self) |
||||||
|
|
||||||
|
def resize(self, w, h): |
||||||
|
# TTkLog.debug(f"CanvasResize:{(w,h)}") |
||||||
|
self._data = [[]]*h |
||||||
|
self._colors = [[]]*h |
||||||
|
for i in range(0,h): |
||||||
|
self._data[i] = [' ']*w |
||||||
|
self._colors[i] = [None]*w |
||||||
|
self._width = w |
||||||
|
self._height = h |
||||||
|
TTkHelper.addPaintBuffer(self) |
||||||
|
|
||||||
|
def zTop(self): |
||||||
|
# TODO: Figure out how to use this |
||||||
|
pass |
||||||
|
|
||||||
|
def drawBox(self, x, y, w, h): |
||||||
|
def _set(_y,_x,_c): |
||||||
|
if _y<self._height and _x < self._width: |
||||||
|
self._data[_y][_x] = _c |
||||||
|
# 4 corners |
||||||
|
_set(y, x, "╔") |
||||||
|
_set(y, x+w-1, "╗") |
||||||
|
_set(y+h-1, x, "╚") |
||||||
|
_set(y+h-1, x+w-1, "╝") |
||||||
|
if w > 2: |
||||||
|
for i in range(x+1,x+w-1): |
||||||
|
_set(y, i, "═") |
||||||
|
_set(y+h-1, i, "═") |
||||||
|
if h > 2: |
||||||
|
for i in range(y+1,y+h-1): |
||||||
|
_set(i, x, "║") |
||||||
|
_set(i, x+w-1, "║") |
||||||
|
TTkHelper.addPaintBuffer(self) |
||||||
|
|
||||||
|
def execPaint(self, winw, winh): |
||||||
|
pass |
||||||
|
|
||||||
|
def paintCanvas(self, canvas, x, y, w, h): |
||||||
|
# TTkLog.debug(f"PaintCanvas:{(x,y,w,h)}") |
||||||
|
x = x if x<self._width else self._width-1 |
||||||
|
y = y if y<self._height else self._height-1 |
||||||
|
w = w if x+w<self._width else self._width-x |
||||||
|
h = h if y+h<self._height else self._height-y |
||||||
|
for iy in range(0,h): |
||||||
|
for ix in range(0,w): |
||||||
|
self._data[y+iy][x+ix] = canvas._data[iy][ix] |
||||||
|
self._colors[y+iy][x+ix] = canvas._colors[iy][ix] |
||||||
|
|
||||||
|
def pushToTerminal(self, x, y, w, h): |
||||||
|
TTkLog.debug("pushToTerminal") |
||||||
|
ansi = "" |
||||||
|
for y in range(0, self._height): |
||||||
|
s = lbt.Mv.t(y+1,1) |
||||||
|
for x in range(0, self._width): |
||||||
|
c = self._data[y][x] |
||||||
|
s+=c |
||||||
|
ansi += s |
||||||
|
|
||||||
|
lbt.Term.push(ansi) |
||||||
@ -0,0 +1,37 @@ |
|||||||
|
#!/usr/bin/env python3 |
||||||
|
|
||||||
|
# MIT License |
||||||
|
# |
||||||
|
# Copyright (c) 2021 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com> |
||||||
|
# |
||||||
|
# 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 TTkCfg: |
||||||
|
DEP_2: int = 0x02 |
||||||
|
DEP_4: int = 0x04 |
||||||
|
DEP_8: int = 0x08 |
||||||
|
DEP_24: int = 0x18 |
||||||
|
|
||||||
|
color_depth: int = DEP_24 |
||||||
|
|
||||||
|
class TTkGlbl: |
||||||
|
term_w: int = 0 |
||||||
|
term_h: int = 0 |
||||||
|
|
||||||
|
|
||||||
@ -0,0 +1,24 @@ |
|||||||
|
#!/usr/bin/env python3 |
||||||
|
|
||||||
|
# MIT License |
||||||
|
# |
||||||
|
# Copyright (c) 2021 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com> |
||||||
|
# |
||||||
|
# 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. |
||||||
|
|
||||||
@ -0,0 +1,87 @@ |
|||||||
|
#!/usr/bin/env python3 |
||||||
|
|
||||||
|
# MIT License |
||||||
|
# |
||||||
|
# Copyright (c) 2021 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com> |
||||||
|
# |
||||||
|
# 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.libbpytop as lbt |
||||||
|
from TermTk.TTkCore.log import TTkLog |
||||||
|
from TermTk.TTkCore.cfg import * |
||||||
|
|
||||||
|
class TTkHelper: |
||||||
|
# TODO: Add Setter/Getter |
||||||
|
_rootCanvas = None |
||||||
|
_updateWidget = [] |
||||||
|
_paintBuffer = [] |
||||||
|
|
||||||
|
@staticmethod |
||||||
|
def addUpdateWidget(widget): |
||||||
|
if widget not in TTkHelper._updateWidget: |
||||||
|
TTkHelper._updateWidget.append(widget) |
||||||
|
|
||||||
|
@staticmethod |
||||||
|
def addPaintBuffer(canvas): |
||||||
|
if canvas is not TTkHelper._rootCanvas: |
||||||
|
if canvas not in TTkHelper._paintBuffer: |
||||||
|
TTkHelper._paintBuffer.append(canvas) |
||||||
|
|
||||||
|
@staticmethod |
||||||
|
def registerRootCanvas(canvas): |
||||||
|
TTkHelper._rootCanvas = canvas |
||||||
|
TTkHelper._paintBuffer = [] |
||||||
|
TTkHelper._updateWidget = [] |
||||||
|
|
||||||
|
@staticmethod |
||||||
|
def execPaint(cw, ch): |
||||||
|
if TTkHelper._rootCanvas is None : |
||||||
|
return |
||||||
|
for canvas in TTkHelper._paintBuffer: |
||||||
|
widget = canvas.getWidget() |
||||||
|
x = widget.getX() |
||||||
|
y = widget.getY() |
||||||
|
w = widget.getWidth() |
||||||
|
h = widget.getHeight() |
||||||
|
TTkHelper._rootCanvas.paintCanvas(canvas, x, y, w, h) |
||||||
|
TTkHelper._paintBuffer = [] |
||||||
|
|
||||||
|
@staticmethod |
||||||
|
def paintAll(): |
||||||
|
if TTkHelper._rootCanvas is None: |
||||||
|
return |
||||||
|
for widget in TTkHelper._updateWidget: |
||||||
|
widget.paintEvent() |
||||||
|
TTkHelper._updateWidget = [] |
||||||
|
TTkHelper.execPaint(TTkGlbl.term_w,TTkGlbl.term_h) |
||||||
|
TTkHelper._rootCanvas.pushToTerminal(0, 0, TTkGlbl.term_w, TTkGlbl.term_h) |
||||||
|
|
||||||
|
# curses.panel.update_panels() |
||||||
|
# TTkGlbl.GLBL['screen'].refresh() |
||||||
|
|
||||||
|
@staticmethod |
||||||
|
def absPos(widget) -> (int,int): |
||||||
|
pos = TTkHelper.absParentPos(widget) |
||||||
|
return widget.pos() + pos |
||||||
|
|
||||||
|
@staticmethod |
||||||
|
def absParentPos(widget) -> (int,int): |
||||||
|
if widget is None or widget.parentWidget() is None: |
||||||
|
return (0, 0) |
||||||
|
return TTkHelper.absPos(widget.parentWidget()) |
||||||
@ -0,0 +1,143 @@ |
|||||||
|
#!/usr/bin/env python3 |
||||||
|
|
||||||
|
# MIT License |
||||||
|
# |
||||||
|
# Copyright (c) 2021 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com> |
||||||
|
# |
||||||
|
# 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 os |
||||||
|
import signal |
||||||
|
import time |
||||||
|
import threading, queue |
||||||
|
|
||||||
|
import TermTk.libbpytop as lbt |
||||||
|
from TermTk.TTkCore.log import TTkLog |
||||||
|
from TermTk.TTkCore.cfg import * |
||||||
|
from TermTk.TTkCore.canvas import * |
||||||
|
from TermTk.TTkWidgets.layout import * |
||||||
|
from TermTk.TTkWidgets.widget import * |
||||||
|
|
||||||
|
class TTk(TTkWidget): |
||||||
|
running: bool = False |
||||||
|
events = None |
||||||
|
key_events = None |
||||||
|
mouse_events = None |
||||||
|
screen_events = None |
||||||
|
|
||||||
|
MOUSE_EVENT = 0x01 |
||||||
|
KEY_EVENT = 0x02 |
||||||
|
SCREEN_EVENT = 0x04 |
||||||
|
QUIT_EVENT = 0x08 |
||||||
|
TIME_EVENT = 0x10 |
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs): |
||||||
|
TTkWidget.__init__(self, *args, **kwargs) |
||||||
|
self.events = queue.Queue() |
||||||
|
self.key_events = queue.Queue() |
||||||
|
self.mouse_events = queue.Queue() |
||||||
|
self.screen_events = queue.Queue() |
||||||
|
TTkHelper.registerRootCanvas(self._canvas) |
||||||
|
|
||||||
|
def mainloop(self): |
||||||
|
TTkLog.debug("Starting Main Loop...") |
||||||
|
# Register events |
||||||
|
try: |
||||||
|
signal.signal(signal.SIGTSTP, self._SIGSTOP) # Ctrl-Z |
||||||
|
signal.signal(signal.SIGCONT, self._SIGCONT) # Resume |
||||||
|
signal.signal(signal.SIGINT, self._SIGINT) # Ctrl-C |
||||||
|
except Exception as e: |
||||||
|
TTkLog.error(f"{e}") |
||||||
|
exit(1) |
||||||
|
else: |
||||||
|
TTkLog.debug("Signal Event Registered") |
||||||
|
|
||||||
|
lbt.Term.registerResizeCb(self._win_resize_cb) |
||||||
|
threading.Thread(target=self._input_thread, daemon=True).start() |
||||||
|
# threading.Timer(30.0, hello) |
||||||
|
|
||||||
|
self.running = True |
||||||
|
lbt.Term.init() |
||||||
|
while self.running: |
||||||
|
# Main Loop |
||||||
|
evt = self.events.get() |
||||||
|
if evt is TTk.MOUSE_EVENT: |
||||||
|
mevt = self.mouse_events.get() |
||||||
|
TTkLog.info(f"Mouse Event: {mevt}") |
||||||
|
elif evt is TTk.KEY_EVENT: |
||||||
|
kevt = self.key_events.get() |
||||||
|
TTkLog.info(f"Key Event: {kevt}") |
||||||
|
pass |
||||||
|
elif evt is TTk.TIME_EVENT: |
||||||
|
pass |
||||||
|
elif evt is TTk.SCREEN_EVENT: |
||||||
|
self.setGeometry(0,0,TTkGlbl.term_w,TTkGlbl.term_h) |
||||||
|
TTkLog.info(f"Resize: w:{TTkGlbl.term_w}, h:{TTkGlbl.term_h}") |
||||||
|
elif evt is TTk.QUIT_EVENT: |
||||||
|
TTkLog.debug(f"Quit.") |
||||||
|
break |
||||||
|
else: |
||||||
|
TTkLog.error(f"Unhandled Event {evt}") |
||||||
|
break |
||||||
|
|
||||||
|
TTkHelper.paintAll() |
||||||
|
|
||||||
|
lbt.Term.exit() |
||||||
|
pass |
||||||
|
|
||||||
|
def _win_resize_cb(self, width, height): |
||||||
|
TTkGlbl.term_w = int(width) |
||||||
|
TTkGlbl.term_h = int(height) |
||||||
|
self.events.put(TTk.SCREEN_EVENT) |
||||||
|
|
||||||
|
def _input_thread(self): |
||||||
|
def _inputCallback(kevt=None, mevt=None): |
||||||
|
if kevt is not None: |
||||||
|
self.key_events.put(kevt) |
||||||
|
self.events.put(TTk.KEY_EVENT) |
||||||
|
if mevt is not None: |
||||||
|
self.mouse_events.put(mevt) |
||||||
|
self.events.put(TTk.MOUSE_EVENT) |
||||||
|
return self.running |
||||||
|
# Start input key loop |
||||||
|
lbt.Input.get_key(_inputCallback) |
||||||
|
|
||||||
|
def _canvas_thread(self): |
||||||
|
pass |
||||||
|
|
||||||
|
def quit(self): |
||||||
|
self.events.put(TTk.QUIT_EVENT) |
||||||
|
self.running = False |
||||||
|
|
||||||
|
def _SIGSTOP(self, signum, frame): |
||||||
|
"""Reset terminal settings and stop background input read before putting to sleep""" |
||||||
|
TTkLog.debug("Captured SIGSTOP <CTRL-z>") |
||||||
|
lbt.Term.stop() |
||||||
|
# TODO: stop the threads |
||||||
|
os.kill(os.getpid(), signal.SIGSTOP) |
||||||
|
|
||||||
|
def _SIGCONT(self, signum, frame): |
||||||
|
"""Set terminal settings and restart background input read""" |
||||||
|
TTkLog.debug("Captured SIGCONT 'fg/bg'") |
||||||
|
lbt.Term.cont() |
||||||
|
# TODO: Restart threads |
||||||
|
# TODO: Redraw the screen |
||||||
|
|
||||||
|
def _SIGINT(self, signum, frame): |
||||||
|
TTkLog.debug("Captured SIGINT <CTRL-C>") |
||||||
|
self.quit() |
||||||
@ -0,0 +1,23 @@ |
|||||||
|
#!/usr/bin/env python3 |
||||||
|
|
||||||
|
# MIT License |
||||||
|
# |
||||||
|
# Copyright (c) 2021 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com> |
||||||
|
# |
||||||
|
# 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. |
||||||
@ -0,0 +1,39 @@ |
|||||||
|
#!/usr/bin/env python3 |
||||||
|
|
||||||
|
# MIT License |
||||||
|
# |
||||||
|
# Copyright (c) 2021 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com> |
||||||
|
# |
||||||
|
# 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. |
||||||
|
|
||||||
|
from TermTk.TTkCore.log import TTkLog |
||||||
|
from .widget import * |
||||||
|
|
||||||
|
class TTkFrame(TTkWidget): |
||||||
|
def __init__(self, *args, **kwargs): |
||||||
|
TTkWidget.__init__(self, *args, **kwargs) |
||||||
|
self._border = kwargs.get('border', True ) |
||||||
|
self.update() |
||||||
|
|
||||||
|
def paintEvent(self): |
||||||
|
if self._border: |
||||||
|
self._canvas.drawBox(0,0,self._width,self._height) |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -0,0 +1,277 @@ |
|||||||
|
''' |
||||||
|
Layout System |
||||||
|
''' |
||||||
|
|
||||||
|
class TTkLayoutItem: |
||||||
|
__slots__ = ('_x', '_y', '_w', '_h', '_sMax', '_sMaxVal', '_sMin', '_sMinVal') |
||||||
|
def __init__(self): |
||||||
|
self._x, self._y = 0, 0 |
||||||
|
self._w, self._h = 0, 0 |
||||||
|
self._sMax, self._sMin = False, False |
||||||
|
self._sMaxVal, self._sMinVal = 0, 0 |
||||||
|
pass |
||||||
|
def minimumSize(self): |
||||||
|
return self.minimumWidth(), self.minimumHeight() |
||||||
|
def minimumHeight(self): return 0 |
||||||
|
def minimumWidth(self): return 0 |
||||||
|
|
||||||
|
def maximumSize(self): |
||||||
|
return self.maximumWidth(), self.maximumHeight() |
||||||
|
def maximumHeight(self): return 0x80000000 |
||||||
|
def maximumWidth(self): return 0x80000000 |
||||||
|
|
||||||
|
def geometry(self): |
||||||
|
return self._x, self._y, self._w, self._h |
||||||
|
|
||||||
|
def setGeometry(self, x, y, w, h): |
||||||
|
self._x = x |
||||||
|
self._y = y |
||||||
|
self._w = w |
||||||
|
self._h = h |
||||||
|
|
||||||
|
|
||||||
|
class TTkLayout(TTkLayoutItem): |
||||||
|
def __init__(self): |
||||||
|
TTkLayoutItem.__init__(self) |
||||||
|
self._items = [] |
||||||
|
self._parent = None |
||||||
|
pass |
||||||
|
|
||||||
|
def children(self): |
||||||
|
return self._items |
||||||
|
|
||||||
|
def count(self): |
||||||
|
return len(self._items) |
||||||
|
|
||||||
|
def itemAt(self, index): |
||||||
|
if index < len(self._items): |
||||||
|
return self._items[index] |
||||||
|
return 0 |
||||||
|
|
||||||
|
def setParent(self, parent): |
||||||
|
self._parent = parent |
||||||
|
|
||||||
|
def parentWidget(self): |
||||||
|
return self._parent |
||||||
|
|
||||||
|
def addItem(self, item): |
||||||
|
self._items.append(item) |
||||||
|
|
||||||
|
def addWidget(self, widget): |
||||||
|
self.addItem(TTkWidgetItem(widget)) |
||||||
|
|
||||||
|
def removeWidget(self, widget): |
||||||
|
for i in self._items: |
||||||
|
if i.widget() == widget: |
||||||
|
self._items.remove(i) |
||||||
|
return |
||||||
|
|
||||||
|
def update(self): |
||||||
|
for i in self.children(): |
||||||
|
if isinstance(i, TTkWidgetItem) and not i.isEmpty(): |
||||||
|
i.widget().update() |
||||||
|
# TODO: Have a look at this: |
||||||
|
# i.getCanvas().top() |
||||||
|
elif isinstance(i, TTkLayout): |
||||||
|
i.update() |
||||||
|
|
||||||
|
class TTkWidgetItem(TTkLayoutItem): |
||||||
|
def __init__(self, widget): |
||||||
|
TTkLayoutItem.__init__(self) |
||||||
|
self._widget = widget |
||||||
|
|
||||||
|
def widget(self): |
||||||
|
return self._widget |
||||||
|
|
||||||
|
def isEmpty(self): return self._widget is None |
||||||
|
|
||||||
|
def minimumSize(self): return self._widget.minimumSize() |
||||||
|
def minimumHeight(self): return self._widget.minimumHeight() |
||||||
|
def minimumWidth(self): return self._widget.minimumWidth() |
||||||
|
def maximumSize(self): return self._widget.maximumSize() |
||||||
|
def maximumHeight(self): return self._widget.maximumHeight() |
||||||
|
def maximumWidth(self): return self._widget.maximumWidth() |
||||||
|
|
||||||
|
def geometry(self): return self._widget.geometry() |
||||||
|
|
||||||
|
def setGeometry(self, x, y, w, h): |
||||||
|
self._widget.setGeometry(x, y, w, h) |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class TTkHBoxLayout(TTkLayout): |
||||||
|
def __init__(self): |
||||||
|
TTkLayout.__init__(self) |
||||||
|
|
||||||
|
def minimumWidth(self): |
||||||
|
''' process the widgets and get the min size ''' |
||||||
|
minw = 0 |
||||||
|
for item in self.children(): |
||||||
|
w1 = item.minimumWidth() |
||||||
|
minw += w1 |
||||||
|
return minw |
||||||
|
|
||||||
|
def minimumHeight(self): |
||||||
|
''' process the widgets and get the min size ''' |
||||||
|
minh = TTkLayout.minimumHeight(self) |
||||||
|
for item in self.children(): |
||||||
|
h1 = item.minimumHeight() |
||||||
|
if h1 > minh : minh = h1 |
||||||
|
return minh |
||||||
|
|
||||||
|
def maximumWidth(self): |
||||||
|
''' process the widgets and get the min size ''' |
||||||
|
maxw = 0 |
||||||
|
for item in self.children(): |
||||||
|
w1 = item.maximumWidth() |
||||||
|
maxw += w1 |
||||||
|
return maxw |
||||||
|
|
||||||
|
def maximumHeight(self): |
||||||
|
''' process the widgets and get the min size ''' |
||||||
|
maxh = TTkLayout.maximumHeight(self) |
||||||
|
for item in self.children(): |
||||||
|
h1 = item.maximumHeight() |
||||||
|
if h1 < maxh : maxh = h1 |
||||||
|
return maxh |
||||||
|
|
||||||
|
def update(self): |
||||||
|
x, y, w, h = self.geometry() |
||||||
|
numWidgets = self.count() |
||||||
|
leftWidgets = numWidgets |
||||||
|
freeWidth = w |
||||||
|
newx, newy = x, y |
||||||
|
# Loop to check the resizable space |
||||||
|
for item in self.children(): |
||||||
|
item._sMax = False |
||||||
|
item._sMin = False |
||||||
|
iterate = True |
||||||
|
while iterate and leftWidgets > 0: |
||||||
|
iterate = False |
||||||
|
sliceSize = freeWidth//leftWidgets |
||||||
|
for item in self.children(): |
||||||
|
if item._sMax or item._sMin: continue |
||||||
|
maxs = item.maximumWidth() |
||||||
|
mins = item.minimumWidth() |
||||||
|
if sliceSize > maxs: |
||||||
|
freeWidth -= maxs |
||||||
|
iterate = True |
||||||
|
item._sMax = True |
||||||
|
item._sMaxVal = maxs |
||||||
|
leftWidgets -= 1 |
||||||
|
elif sliceSize < mins: |
||||||
|
freeWidth -= mins |
||||||
|
leftWidgets -= 1 |
||||||
|
# slicesize = freeWidth//leftWidgets |
||||||
|
iterate = True |
||||||
|
item._sMin = True |
||||||
|
item._sMinVal = mins |
||||||
|
|
||||||
|
# loop and set the geometry of any item |
||||||
|
for item in self.children(): |
||||||
|
if item._sMax: |
||||||
|
item.setGeometry(newx, newy, item._sMaxVal, h) |
||||||
|
newy += item._sMaxVal |
||||||
|
elif item._sMin: |
||||||
|
item.setGeometry(newx, newy, item._sMinVal, h) |
||||||
|
newy += item._sMinVal |
||||||
|
else: |
||||||
|
sliceSize = freeWidth//leftWidgets |
||||||
|
item.setGeometry(newx, newy, sliceSize, h) |
||||||
|
newx += sliceSize |
||||||
|
freeWidth -= sliceSize |
||||||
|
leftWidgets -= 1 |
||||||
|
if isinstance(item, TTkWidgetItem) and not item.isEmpty(): |
||||||
|
item.widget().update() |
||||||
|
item.widget().getCanvas().zTop() |
||||||
|
elif isinstance(item, TTkLayout): |
||||||
|
item.update() |
||||||
|
|
||||||
|
|
||||||
|
class TTkVBoxLayout(TTkLayout): |
||||||
|
def __init__(self): |
||||||
|
TTkLayout.__init__(self) |
||||||
|
|
||||||
|
def minimumWidth(self): |
||||||
|
''' process the widgets and get the min size ''' |
||||||
|
minw = TTkLayout.minimumWidth(self) |
||||||
|
for item in self.children(): |
||||||
|
w1 = item.minimumWidth() |
||||||
|
if w1 > minw : minw = w1 |
||||||
|
return minw |
||||||
|
|
||||||
|
def minimumHeight(self): |
||||||
|
''' process the widgets and get the min size ''' |
||||||
|
minh = 0 |
||||||
|
for item in self.children(): |
||||||
|
h1 = item.minimumHeight() |
||||||
|
minh += h1 |
||||||
|
return minh |
||||||
|
|
||||||
|
def maximumWidth(self): |
||||||
|
''' process the widgets and get the min size ''' |
||||||
|
maxw = TTkLayout.maximumWidth(self) |
||||||
|
for item in self.children(): |
||||||
|
w1 = item.maximumWidth() |
||||||
|
if w1 < maxw : maxw = w1 |
||||||
|
return maxw |
||||||
|
|
||||||
|
def maximumHeight(self): |
||||||
|
''' process the widgets and get the min size ''' |
||||||
|
maxh = 0 |
||||||
|
for item in self.children(): |
||||||
|
h1 = item.maximumHeight() |
||||||
|
maxh += h1 |
||||||
|
return maxh |
||||||
|
|
||||||
|
def update(self): |
||||||
|
x, y, w, h = self.geometry() |
||||||
|
numWidgets = self.count() |
||||||
|
leftWidgets = numWidgets |
||||||
|
freeHeight = h |
||||||
|
newx, newy = x, y |
||||||
|
# Loop to check the resizable space |
||||||
|
for item in self.children(): |
||||||
|
item._sMax = False |
||||||
|
item._sMin = False |
||||||
|
iterate = True |
||||||
|
while iterate and leftWidgets > 0: |
||||||
|
iterate = False |
||||||
|
sliceSize = freeHeight//leftWidgets |
||||||
|
for item in self.children(): |
||||||
|
if item._sMax or item._sMin: continue |
||||||
|
maxs = item.maximumHeight() |
||||||
|
mins = item.minimumHeight() |
||||||
|
if sliceSize > maxs: |
||||||
|
freeHeight -= maxs |
||||||
|
iterate = True |
||||||
|
item._sMax = True |
||||||
|
item._sMaxVal = maxs |
||||||
|
leftWidgets -= 1 |
||||||
|
elif sliceSize < mins: |
||||||
|
freeHeight -= mins |
||||||
|
leftWidgets -= 1 |
||||||
|
# slicesize = freeHeight//leftWidgets |
||||||
|
iterate = True |
||||||
|
item._sMin = True |
||||||
|
item._sMinVal = mins |
||||||
|
|
||||||
|
# loop and set the geometry of any item |
||||||
|
for item in self.children(): |
||||||
|
if item._sMax: |
||||||
|
item.setGeometry(newx, newy, w, item._sMaxVal) |
||||||
|
newy += item._sMaxVal |
||||||
|
elif item._sMin: |
||||||
|
item.setGeometry(newx, newy, w, item._sMinVal) |
||||||
|
newy += item._sMinVal |
||||||
|
else: |
||||||
|
sliceSize = freeHeight//leftWidgets |
||||||
|
item.setGeometry(newx, newy, w, sliceSize) |
||||||
|
newy += sliceSize |
||||||
|
freeHeight -= sliceSize |
||||||
|
leftWidgets -= 1 |
||||||
|
if isinstance(item, TTkWidgetItem) and not item.isEmpty(): |
||||||
|
item.widget().update() |
||||||
|
item.widget().getCanvas().zTop() |
||||||
|
elif isinstance(item, TTkLayout): |
||||||
|
item.update() |
||||||
@ -0,0 +1,172 @@ |
|||||||
|
#!/usr/bin/env python3 |
||||||
|
|
||||||
|
# MIT License |
||||||
|
# |
||||||
|
# Copyright (c) 2021 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com> |
||||||
|
# |
||||||
|
# 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. |
||||||
|
|
||||||
|
from TermTk.TTkCore.canvas import * |
||||||
|
from TermTk.TTkCore.cfg import * |
||||||
|
from .layout import * |
||||||
|
|
||||||
|
class TTkWidget: |
||||||
|
def __init__(self, *args, **kwargs): |
||||||
|
self._childs = [] |
||||||
|
self._parent = kwargs.get('parent', None ) |
||||||
|
self._x = kwargs.get('x', 0 ) |
||||||
|
self._y = kwargs.get('y', 0 ) |
||||||
|
self._width = kwargs.get('width' , 0 ) |
||||||
|
self._height = kwargs.get('height', 0 ) |
||||||
|
self._maxw = 0x80000000 |
||||||
|
self._maxh = 0x80000000 |
||||||
|
self._minw = 0x00000000 |
||||||
|
self._minh = 0x00000000 |
||||||
|
self._layout = TTkLayout() |
||||||
|
self._canvas = TTkCanvas( |
||||||
|
widget = self, |
||||||
|
width = self._width , |
||||||
|
height = self._height ) |
||||||
|
if self._parent is not None and \ |
||||||
|
self._parent._layout is not None: |
||||||
|
self._parent._layout.addWidget(self) |
||||||
|
|
||||||
|
def getX(self): return self._x |
||||||
|
def getY(self): return self._y |
||||||
|
def getWidth(self): return self._width |
||||||
|
def getHeight(self): return self._height |
||||||
|
|
||||||
|
def pos(self): |
||||||
|
return (self._x, self._y) |
||||||
|
|
||||||
|
def addLayout(self, l): |
||||||
|
self._layout = l |
||||||
|
|
||||||
|
def paintEvent(self): pass |
||||||
|
|
||||||
|
def move(self, x, y): |
||||||
|
self._x = x |
||||||
|
self._y = y |
||||||
|
self._canvas.move(self._x, self._y) |
||||||
|
if self._layout is not None: |
||||||
|
self._layout.setGeometry(self._x, self._y, self._width, self._height) |
||||||
|
self.update() |
||||||
|
|
||||||
|
def resize(self, w, h): |
||||||
|
self._width = w |
||||||
|
self._height = h |
||||||
|
self._canvas.resize(self._width, self._height) |
||||||
|
if self._layout is not None: |
||||||
|
self._layout.setGeometry(self._x, self._y, self._width, self._height) |
||||||
|
self.update() |
||||||
|
|
||||||
|
def setGeometry(self, x, y, w, h): |
||||||
|
self.resize(w, h) |
||||||
|
self.move(x, y) |
||||||
|
|
||||||
|
def mouseDoubleClickEvent(self, evt): pass |
||||||
|
def mouseMoveEvent(self, evt): pass |
||||||
|
def mousePressEvent(self, evt): pass |
||||||
|
def mouseReleaseEvent(self, evt): pass |
||||||
|
def wheelEvent(self, evt): pass |
||||||
|
def enterEvent(self, evt): pass |
||||||
|
def leaveEvent(self, evt): pass |
||||||
|
def keyPressEvent(self, evt): pass |
||||||
|
def keyReleaseEvent(self, evt): pass |
||||||
|
|
||||||
|
def event(self, evt): |
||||||
|
pass |
||||||
|
# # handle own events |
||||||
|
# if evt.type() == CuEvent.MouseMove: |
||||||
|
# if evt.button() == CuT.NoButton: |
||||||
|
# self.mouseMoveEvent(evt) |
||||||
|
# elif evt.type() == CuEvent.MouseButtonRelease: |
||||||
|
# self.mouseReleaseEvent(evt) |
||||||
|
# elif evt.type() == CuEvent.MouseButtonPress: |
||||||
|
# self.mousePressEvent(evt) |
||||||
|
# if self.focusPolicy() & CuT.ClickFocus == CuT.ClickFocus: |
||||||
|
# self.setFocus() |
||||||
|
# elif evt.type() == CuEvent.Wheel: |
||||||
|
# self.wheelEvent(evt) |
||||||
|
# if self.focusPolicy() & CuT.WheelFocus == CuT.WheelFocus: |
||||||
|
# self.setFocus() |
||||||
|
# elif evt.type() == CuEvent.KeyPress: |
||||||
|
# self.keyPressEvent(evt) |
||||||
|
# elif evt.type() == CuEvent.KeyRelease: |
||||||
|
# self.keyReleaseEvent(evt) |
||||||
|
# # Trigger this event to the childs |
||||||
|
# if self._layout is not None: |
||||||
|
# return CuWidget._eventLayoutHandle(evt, self._layout) |
||||||
|
def maximumSize(self): |
||||||
|
return self.maximumWidth(), self.maximumHeight() |
||||||
|
def maximumHeight(self): |
||||||
|
wMaxH = self._maxh |
||||||
|
if self._layout is not None: |
||||||
|
lMaxH = self._layout.maximumHeight() |
||||||
|
if lMaxH < wMaxH: |
||||||
|
return lMaxH |
||||||
|
return wMaxH |
||||||
|
def maximumWidth(self): |
||||||
|
wMaxW = self._maxw |
||||||
|
if self._layout is not None: |
||||||
|
lMaxW = self._layout.maximumWidth() |
||||||
|
if lMaxW < wMaxW: |
||||||
|
return lMaxW |
||||||
|
return wMaxW |
||||||
|
|
||||||
|
def minimumSize(self): |
||||||
|
return self.minimumWidth(), self.minimumHeight() |
||||||
|
def minimumHeight(self): |
||||||
|
wMinH = self._minh |
||||||
|
if self._layout is not None: |
||||||
|
lMinH = self._layout.minimumHeight() |
||||||
|
if lMinH > wMinH: |
||||||
|
return lMinH |
||||||
|
return wMinH |
||||||
|
def minimumWidth(self): |
||||||
|
wMinW = self._minw |
||||||
|
if self._layout is not None: |
||||||
|
lMinW = self._layout.minimumWidth() |
||||||
|
if lMinW > wMinW: |
||||||
|
return lMinW |
||||||
|
return wMinW |
||||||
|
|
||||||
|
def setMaximumSize(self, maxw, maxh): self._maxw = maxw; self._maxh = maxh |
||||||
|
def setMaximumHeight(self, maxh): self._maxh = maxh |
||||||
|
def setMaximumWidth(self, maxw): self._maxw = maxw |
||||||
|
|
||||||
|
def setMinimumSize(self, minw, minh): self._minw = minw; self._minh = minh |
||||||
|
def setMinimumHeight(self, minh): self._minh = minh |
||||||
|
def setMinimumWidth(self, minw): self._minw = minw |
||||||
|
|
||||||
|
def update(self): |
||||||
|
TTkHelper.addUpdateWidget(self) |
||||||
|
if self._layout is not None: |
||||||
|
self._layout.update() |
||||||
|
|
||||||
|
def getCanvas(self): |
||||||
|
return self._canvas |
||||||
|
|
||||||
|
def layout(self): |
||||||
|
return self._layout |
||||||
|
|
||||||
|
def setParent(self, parent): |
||||||
|
self._parent = parent |
||||||
|
def parentWidget(self): |
||||||
|
return self._parent |
||||||
@ -0,0 +1,2 @@ |
|||||||
|
from .TTkCore import * |
||||||
|
from .TTkWidgets import * |
||||||
@ -1,2 +1,3 @@ |
|||||||
from .input import * |
from .input import * |
||||||
from .term import * |
from .term import * |
||||||
|
from .colors import * |
||||||
@ -1,16 +0,0 @@ |
|||||||
#!/usr/bin/env python3 |
|
||||||
|
|
||||||
# Copyright 2021 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com> |
|
||||||
# Copyright 2020 Aristocratos (https://github.com/aristocratos/bpytop) |
|
||||||
# |
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
# you may not use this file except in compliance with the License. |
|
||||||
# You may obtain a copy of the License at |
|
||||||
# |
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0 |
|
||||||
# |
|
||||||
# Unless required by applicable law or agreed to in writing, software |
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
# See the License for the specific language governing permissions and |
|
||||||
# limitations under the License. |
|
||||||
@ -0,0 +1,171 @@ |
|||||||
|
#!/usr/bin/env python3 |
||||||
|
|
||||||
|
# Copyright 2021 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com> |
||||||
|
# Copyright 2020 Aristocratos (https://github.com/aristocratos/bpytop) |
||||||
|
# |
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
# you may not use this file except in compliance with the License. |
||||||
|
# You may obtain a copy of the License at |
||||||
|
# |
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
# |
||||||
|
# Unless required by applicable law or agreed to in writing, software |
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
# See the License for the specific language governing permissions and |
||||||
|
# limitations under the License. |
||||||
|
|
||||||
|
import os, sys, io, threading, signal, re, subprocess, logging, logging.handlers, argparse |
||||||
|
import queue |
||||||
|
from select import select |
||||||
|
from time import time, sleep, strftime, localtime |
||||||
|
from typing import List, Set, Dict, Tuple, Optional, Union, Any, Callable, ContextManager, Iterable, Type, NamedTuple |
||||||
|
|
||||||
|
try: import fcntl, termios, tty, pwd |
||||||
|
except Exception as e: |
||||||
|
print(f'ERROR: {e}') |
||||||
|
exit(1) |
||||||
|
|
||||||
|
from . import Term |
||||||
|
import TermTk as ttk |
||||||
|
|
||||||
|
|
||||||
|
# Ansi Escape Codes: |
||||||
|
# https://conemu.github.io/en/AnsiEscapeCodes.html |
||||||
|
|
||||||
|
class Color: |
||||||
|
'''Holds representations for a 24-bit color value |
||||||
|
__init__(color, depth="fg", default=False) |
||||||
|
-- color accepts 6 digit hexadecimal: string "#RRGGBB", 2 digit hexadecimal: string "#FF" or decimal RGB "255 255 255" as a string. |
||||||
|
-- depth accepts "fg" or "bg" |
||||||
|
__call__(*args) joins str arguments to a string and apply color |
||||||
|
__str__ returns escape sequence to set color |
||||||
|
__iter__ returns iteration over red, green and blue in integer values of 0-255. |
||||||
|
* Values: .hexa: str | .dec: Tuple[int, int, int] | .red: int | .green: int | .blue: int | .depth: str | .escape: str |
||||||
|
''' |
||||||
|
hexa: str; dec: Tuple[int, int, int]; red: int; green: int; blue: int; depth: str; escape: str; default: bool |
||||||
|
|
||||||
|
def __init__(self, color: str, depth: str = "fg", default: bool = False): |
||||||
|
self.depth = depth |
||||||
|
self.default = default |
||||||
|
try: |
||||||
|
if not color: |
||||||
|
self.dec = (-1, -1, -1) |
||||||
|
self.hexa = "" |
||||||
|
self.red = self.green = self.blue = -1 |
||||||
|
self.escape = "\033[49m" if depth == "bg" and default else "" |
||||||
|
return |
||||||
|
|
||||||
|
elif color.startswith("#"): |
||||||
|
self.hexa = color |
||||||
|
if len(self.hexa) == 3: |
||||||
|
self.hexa += self.hexa[1:3] + self.hexa[1:3] |
||||||
|
c = int(self.hexa[1:3], base=16) |
||||||
|
self.dec = (c, c, c) |
||||||
|
elif len(self.hexa) == 7: |
||||||
|
self.dec = (int(self.hexa[1:3], base=16), int(self.hexa[3:5], base=16), int(self.hexa[5:7], base=16)) |
||||||
|
else: |
||||||
|
raise ValueError(f'Incorrectly formatted hexadecimal rgb string: {self.hexa}') |
||||||
|
|
||||||
|
else: |
||||||
|
c_t = tuple(map(int, color.split(" "))) |
||||||
|
if len(c_t) == 3: |
||||||
|
self.dec = c_t #type: ignore |
||||||
|
else: |
||||||
|
raise ValueError(f'RGB dec should be "0-255 0-255 0-255"') |
||||||
|
|
||||||
|
ct = self.dec[0] + self.dec[1] + self.dec[2] |
||||||
|
if ct > 255*3 or ct < 0: |
||||||
|
raise ValueError(f'RGB values out of range: {color}') |
||||||
|
except Exception as e: |
||||||
|
ttk.TTkLog.error(str(e)) |
||||||
|
self.escape = "" |
||||||
|
return |
||||||
|
|
||||||
|
if self.dec and not self.hexa: self.hexa = f'{hex(self.dec[0]).lstrip("0x").zfill(2)}{hex(self.dec[1]).lstrip("0x").zfill(2)}{hex(self.dec[2]).lstrip("0x").zfill(2)}' |
||||||
|
|
||||||
|
if self.dec and self.hexa: |
||||||
|
self.red, self.green, self.blue = self.dec |
||||||
|
self.escape = f'\033[{38 if self.depth == "fg" else 48};2;{";".join(str(c) for c in self.dec)}m' |
||||||
|
|
||||||
|
if ttk.TTkCfg.color_depth is not ttk.TTkCfg.DEP_24: |
||||||
|
self.escape = f'{self.truecolor_to_256(rgb=self.dec, depth=self.depth)}' |
||||||
|
|
||||||
|
def __str__(self) -> str: |
||||||
|
return self.escape |
||||||
|
|
||||||
|
def __repr__(self) -> str: |
||||||
|
return repr(self.escape) |
||||||
|
|
||||||
|
def __iter__(self) -> Iterable: |
||||||
|
for c in self.dec: yield c |
||||||
|
|
||||||
|
def __call__(self, *args: str) -> str: |
||||||
|
if len(args) < 1: return "" |
||||||
|
return f'{self.escape}{"".join(args)}{getattr(Term, self.depth)}' |
||||||
|
|
||||||
|
@staticmethod |
||||||
|
def truecolor_to_256(rgb: Tuple[int, int, int], depth: str="fg") -> str: |
||||||
|
out: str = "" |
||||||
|
pre: str = f'\033[{"38" if depth == "fg" else "48"};5;' |
||||||
|
|
||||||
|
greyscale: Tuple[int, int, int] = ( rgb[0] // 11, rgb[1] // 11, rgb[2] // 11 ) |
||||||
|
if greyscale[0] == greyscale[1] == greyscale[2]: |
||||||
|
out = f'{pre}{232 + greyscale[0]}m' |
||||||
|
else: |
||||||
|
out = f'{pre}{round(rgb[0] / 51) * 36 + round(rgb[1] / 51) * 6 + round(rgb[2] / 51) + 16}m' |
||||||
|
|
||||||
|
return out |
||||||
|
|
||||||
|
@staticmethod |
||||||
|
def escape_color(hexa: str = "", r: int = 0, g: int = 0, b: int = 0, depth: str = "fg") -> str: |
||||||
|
"""Returns escape sequence to set color |
||||||
|
* accepts either 6 digit hexadecimal hexa="#RRGGBB", 2 digit hexadecimal: hexa="#FF" |
||||||
|
* or decimal RGB: r=0-255, g=0-255, b=0-255 |
||||||
|
* depth="fg" or "bg" |
||||||
|
""" |
||||||
|
dint: int = 38 if depth == "fg" else 48 |
||||||
|
color: str = "" |
||||||
|
if hexa: |
||||||
|
try: |
||||||
|
if len(hexa) == 3: |
||||||
|
c = int(hexa[1:], base=16) |
||||||
|
if ttk.TTkCfg.color_depth is ttk.TTkCfg.DEP_24: |
||||||
|
color = f'\033[{dint};2;{c};{c};{c}m' |
||||||
|
else: |
||||||
|
color = f'{Color.truecolor_to_256(rgb=(c, c, c), depth=depth)}' |
||||||
|
elif len(hexa) == 7: |
||||||
|
if ttk.TTkCfg.color_depth is ttk.TTkCfg.DEP_24: |
||||||
|
color = f'\033[{dint};2;{int(hexa[1:3], base=16)};{int(hexa[3:5], base=16)};{int(hexa[5:7], base=16)}m' |
||||||
|
else: |
||||||
|
color = f'{Color.truecolor_to_256(rgb=(int(hexa[1:3], base=16), int(hexa[3:5], base=16), int(hexa[5:7], base=16)), depth=depth)}' |
||||||
|
except ValueError as e: |
||||||
|
ttk.TTkLog.error(f'{e}') |
||||||
|
|
||||||
|
else: |
||||||
|
if ttk.TTkCfg.color_depth is ttk.TTkCfg.DEP_24: |
||||||
|
color = f'\033[{dint};2;{r};{g};{b}m' |
||||||
|
else: |
||||||
|
color = f'{Color.truecolor_to_256(rgb=(r, g, b), depth=depth)}' |
||||||
|
return color |
||||||
|
|
||||||
|
@classmethod |
||||||
|
def fg(cls, *args) -> str: |
||||||
|
if len(args) > 2: return cls.escape_color(r=args[0], g=args[1], b=args[2], depth="fg") |
||||||
|
else: return cls.escape_color(hexa=args[0], depth="fg") |
||||||
|
|
||||||
|
@classmethod |
||||||
|
def bg(cls, *args) -> str: |
||||||
|
if len(args) > 2: return cls.escape_color(r=args[0], g=args[1], b=args[2], depth="bg") |
||||||
|
else: return cls.escape_color(hexa=args[0], depth="bg") |
||||||
|
|
||||||
|
#class Colors: |
||||||
|
# '''Standard colors for menus and dialogs''' |
||||||
|
# default = Color("#cc") |
||||||
|
# white = Color("#ff") |
||||||
|
# red = Color("#bf3636") |
||||||
|
# green = Color("#68bf36") |
||||||
|
# blue = Color("#0fd7ff") |
||||||
|
# yellow = Color("#db8b00") |
||||||
|
# black_bg = Color("#00", depth="bg") |
||||||
|
# null = Color("") |
||||||
@ -0,0 +1,33 @@ |
|||||||
|
#!/usr/bin/env bash |
||||||
|
|
||||||
|
esc=$(printf '\033') |
||||||
|
|
||||||
|
_RST_=${esc}'[0m' # resets color and format |
||||||
|
|
||||||
|
# Regular Colors |
||||||
|
Black=${esc}'[38;5;0m' |
||||||
|
Red=${esc}'[38;5;1m' |
||||||
|
Green=${esc}'[38;5;2m' |
||||||
|
Yellow=${esc}'[38;5;3m' |
||||||
|
Blue=${esc}'[38;5;4m' |
||||||
|
Magenta=${esc}'[38;5;5m' |
||||||
|
Cyan=${esc}'[38;5;6m' |
||||||
|
White=${esc}'[38;5;7m' |
||||||
|
|
||||||
|
# Background |
||||||
|
On_Black=${esc}'[48;5;0m' |
||||||
|
On_Red=${esc}'[48;5;1m' |
||||||
|
On_Green=${esc}'[48;5;2m' |
||||||
|
On_Yellow=${esc}'[48;5;3m' |
||||||
|
On_Blue=${esc}'[48;5;4m' |
||||||
|
On_Magenta=${esc}'[48;5;5m' |
||||||
|
On_Cyan=${esc}'[48;5;6m' |
||||||
|
On_White=${esc}'[48;5;7m' |
||||||
|
|
||||||
|
while read -r line; do |
||||||
|
echo "$line" | |
||||||
|
sed "s,/home.*/TermTk/,TermTk/," | |
||||||
|
sed "s,^\(INFO:\),${Green}\1${_RST_}," | |
||||||
|
sed "s,^\(ERROR:\),${Red}\1${_RST_}," | |
||||||
|
sed "s,^\(DEBUG:\),${Blue}\1${_RST_}," |
||||||
|
done < <(tail -F session.log) |
||||||
@ -0,0 +1,65 @@ |
|||||||
|
#!/usr/bin/env python3 |
||||||
|
|
||||||
|
# MIT License |
||||||
|
# |
||||||
|
# Copyright (c) 2021 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com> |
||||||
|
# |
||||||
|
# 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 sys, os |
||||||
|
import logging |
||||||
|
import time |
||||||
|
|
||||||
|
sys.path.append(os.path.join(sys.path[0],'..')) |
||||||
|
from TermTk.libbpytop import Term, Mv |
||||||
|
from TermTk import TTkLog |
||||||
|
|
||||||
|
def message_handler(mode, context, message): |
||||||
|
log = logging.debug |
||||||
|
if mode == TTkLog.InfoMsg: log = logging.info |
||||||
|
elif mode == TTkLog.WarningMsg: log = logging.warning |
||||||
|
elif mode == TTkLog.CriticalMsg: log = logging.critical |
||||||
|
elif mode == TTkLog.FatalMsg: log = logging.fatal |
||||||
|
elif mode == TTkLog.ErrorMsg: log = logging.error |
||||||
|
log(f"{context.file} {message}") |
||||||
|
|
||||||
|
logging.basicConfig(level=logging.DEBUG, |
||||||
|
filename='session.log', |
||||||
|
format='%(levelname)s:(%(threadName)-9s) %(message)s',) |
||||||
|
TTkLog.installMessageHandler(message_handler) |
||||||
|
|
||||||
|
Term.init(mouse=False) |
||||||
|
|
||||||
|
Term.push( |
||||||
|
Mv.t(2,4) + |
||||||
|
"Test Text 3" |
||||||
|
) |
||||||
|
time.sleep(1) |
||||||
|
Term.push( |
||||||
|
Mv.d(1) + Mv.l(3) + |
||||||
|
"Test Text 2" |
||||||
|
) |
||||||
|
time.sleep(1) |
||||||
|
Term.push( |
||||||
|
Mv.d(1) + Mv.l(3) + |
||||||
|
"Test Text 1" |
||||||
|
) |
||||||
|
time.sleep(1) |
||||||
|
|
||||||
|
Term.exit() |
||||||
@ -0,0 +1,74 @@ |
|||||||
|
#!/usr/bin/env python3 |
||||||
|
|
||||||
|
# MIT License |
||||||
|
# |
||||||
|
# Copyright (c) 2021 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com> |
||||||
|
# |
||||||
|
# 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 sys, os |
||||||
|
import logging |
||||||
|
import time |
||||||
|
|
||||||
|
sys.path.append(os.path.join(sys.path[0],'..')) |
||||||
|
from TermTk.libbpytop import Term, Mv, Color |
||||||
|
from TermTk import TTkLog |
||||||
|
|
||||||
|
def message_handler(mode, context, message): |
||||||
|
log = logging.debug |
||||||
|
if mode == TTkLog.InfoMsg: log = logging.info |
||||||
|
elif mode == TTkLog.WarningMsg: log = logging.warning |
||||||
|
elif mode == TTkLog.CriticalMsg: log = logging.critical |
||||||
|
elif mode == TTkLog.FatalMsg: log = logging.fatal |
||||||
|
elif mode == TTkLog.ErrorMsg: log = logging.error |
||||||
|
log(f"{context.file} {message}") |
||||||
|
|
||||||
|
logging.basicConfig(level=logging.DEBUG, |
||||||
|
filename='session.log', |
||||||
|
format='%(levelname)s:(%(threadName)-9s) %(message)s',) |
||||||
|
TTkLog.installMessageHandler(message_handler) |
||||||
|
|
||||||
|
Term.init(mouse=False) |
||||||
|
TTkLog.info("Starting") |
||||||
|
Term.push( |
||||||
|
Mv.t(2,4) + # Cursor x:2, y:4 |
||||||
|
Color.fg("#ff0000") + |
||||||
|
"Test Text 3" |
||||||
|
) |
||||||
|
time.sleep(1) |
||||||
|
TTkLog.info("next : 2") |
||||||
|
|
||||||
|
Term.push( |
||||||
|
Mv.d(1) + Mv.l(3) + # Cursor 1 Down, 3 Left |
||||||
|
Color.bg("#550088") + |
||||||
|
"Test Text 2" |
||||||
|
) |
||||||
|
time.sleep(1) |
||||||
|
TTkLog.info("next : 1") |
||||||
|
|
||||||
|
Term.push( |
||||||
|
Mv.d(1) + Mv.l(3) + # Cursor 1 Down, 3 Left |
||||||
|
Color.fg("#00ff00") + |
||||||
|
Color.bg("#555500") + |
||||||
|
"Test Text 1" |
||||||
|
) |
||||||
|
time.sleep(1) |
||||||
|
TTkLog.info("Ending") |
||||||
|
|
||||||
|
Term.exit() |
||||||
@ -0,0 +1,35 @@ |
|||||||
|
#!/usr/bin/env python3 |
||||||
|
|
||||||
|
# MIT License |
||||||
|
# |
||||||
|
# Copyright (c) 2021 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com> |
||||||
|
# |
||||||
|
# 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 sys, os |
||||||
|
|
||||||
|
sys.path.append(os.path.join(sys.path[0],'..')) |
||||||
|
import TermTk as ttk |
||||||
|
|
||||||
|
ttk.TTkLog.use_default_file_logging() |
||||||
|
|
||||||
|
root = ttk.TTk() |
||||||
|
ttk.TTkFrame(parent=root, x=5, y=3, width=20, height=15, border=True) |
||||||
|
# ttk.Button(root, text="Hello World").grid() |
||||||
|
root.mainloop() |
||||||
@ -0,0 +1,47 @@ |
|||||||
|
#!/usr/bin/env python3 |
||||||
|
|
||||||
|
# MIT License |
||||||
|
# |
||||||
|
# Copyright (c) 2021 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com> |
||||||
|
# |
||||||
|
# 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 sys, os |
||||||
|
|
||||||
|
sys.path.append(os.path.join(sys.path[0],'..')) |
||||||
|
import TermTk as ttk |
||||||
|
|
||||||
|
ttk.TTkLog.use_default_file_logging() |
||||||
|
|
||||||
|
root = ttk.TTk() |
||||||
|
root._layout = ttk.TTkHBoxLayout() |
||||||
|
ttk.TTkFrame(parent=root,border=True) |
||||||
|
rightframe = ttk.TTkFrame(parent=root) |
||||||
|
rightframe._layout = ttk.TTkVBoxLayout() |
||||||
|
|
||||||
|
ttk.TTkFrame(parent=rightframe, border=True) |
||||||
|
centerrightframe=ttk.TTkFrame(parent=rightframe) |
||||||
|
ttk.TTkFrame(parent=rightframe, border=True) |
||||||
|
|
||||||
|
centerrightframe._layout = ttk.TTkHBoxLayout() |
||||||
|
|
||||||
|
ttk.TTkFrame(parent=centerrightframe, border=True) |
||||||
|
ttk.TTkFrame(parent=centerrightframe, border=True) |
||||||
|
ttk.TTkFrame(parent=centerrightframe, border=True) |
||||||
|
root.mainloop() |
||||||
@ -0,0 +1,63 @@ |
|||||||
|
#!/usr/bin/env python3 |
||||||
|
|
||||||
|
# MIT License |
||||||
|
# |
||||||
|
# Copyright (c) 2021 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com> |
||||||
|
# |
||||||
|
# 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 sys, os |
||||||
|
import logging |
||||||
|
import time |
||||||
|
|
||||||
|
sys.path.append(os.path.join(sys.path[0],'..')) |
||||||
|
from TermTk import TTkLog |
||||||
|
|
||||||
|
from TermTk import TTk |
||||||
|
|
||||||
|
class Demo1: |
||||||
|
def __init__(self, master): |
||||||
|
self.master = master |
||||||
|
self.frame = ttk.Frame(self.master) |
||||||
|
self.button1 = ttk.Button(self.frame, text = 'New Window', width = 25, command = self.new_window) |
||||||
|
self.button1.pack() |
||||||
|
self.frame.pack() |
||||||
|
|
||||||
|
def new_window(self): |
||||||
|
self.newWindow = ttk.Toplevel(self.master) |
||||||
|
self.app = Demo2(self.newWindow) |
||||||
|
|
||||||
|
class Demo2: |
||||||
|
def __init__(self, master): |
||||||
|
self.master = master |
||||||
|
self.frame = ttk.Frame(self.master) |
||||||
|
self.quitButton = ttk.Button(self.frame, text = 'Quit', width = 25, command = self.close_windows) |
||||||
|
self.quitButton.pack() |
||||||
|
self.frame.pack() |
||||||
|
|
||||||
|
def close_windows(self): |
||||||
|
self.master.destroy() |
||||||
|
|
||||||
|
def main(): |
||||||
|
root = ttk.TTk() |
||||||
|
app = Demo1(root) |
||||||
|
root.mainloop() |
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
main() |
||||||
Loading…
Reference in new issue