Browse Source

Improved performances (just a little bit)

pull/1/head
Eugenio Parodi 5 years ago
parent
commit
09ef6bb045
  1. 4
      .gitignore
  2. 13
      README.md
  3. 8
      TermTk/TTkCore/canvas.py
  4. 25
      TermTk/TTkCore/helper.py
  5. 26
      TermTk/TTkCore/ttk.py
  6. 1
      TermTk/TTkWidgets/__init__.py
  7. 1
      TermTk/TTkWidgets/button.py
  8. 6
      TermTk/TTkWidgets/frame.py
  9. 4
      TermTk/TTkWidgets/layout.py
  10. 23
      TermTk/TTkWidgets/logviewer.py
  11. 52
      TermTk/TTkWidgets/splitter.py
  12. 6
      TermTk/TTkWidgets/testwidget.py
  13. 52
      TermTk/TTkWidgets/widget.py
  14. 1
      TermTk/TTkWidgets/window.py
  15. 10
      tests/test.ui.004.py

4
.gitignore vendored

@ -3,8 +3,10 @@ __pycache__/
*.py[cod]
*$py.class
# tmp folder
# other
tmp
profiler.txt
.vscode
# C extensions
*.so

13
README.md

@ -1,7 +1,7 @@
# pyTermTk
#### Python Terminal Toolkit
Text-based user interface library ([TUI](https://en.wikipedia.org/wiki/Text-based_user_interface))
Evolved from the dead project [pyCuT](https://github.com/ceccopierangiolieugenio/pyCuT)
Evolved from the discontinued project [pyCuT](https://github.com/ceccopierangiolieugenio/pyCuT)
and inspired by a mix of [Qt5](https://www.riverbankcomputing.com/static/Docs/PyQt5/),[GTK](https://pygobject.readthedocs.io/en/latest/) and [tkinter](https://docs.python.org/3/library/tkinter.html) api definition with a touch of personal interpretation
## Quick Test/Try
@ -22,4 +22,15 @@ python3 tests/test.input.py
# Press CTRL-C to exit
# the logs are written to "session.log"
python3 tests/test.ui.002.py
python3 tests/test.ui.003.py
python3 tests/test.ui.004.py
```
#### Profiling
```shell
python3 -m cProfile -o profiler.txt tests/test.ui.004.py
# install cprofilev:
# pip3 install cprofilev
cprofilev -f profiler.txt
# open http://127.0.0.1:4000
```

8
TermTk/TTkCore/canvas.py

@ -54,12 +54,6 @@ class TTkCanvas:
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
@ -69,7 +63,6 @@ class TTkCanvas:
self._colors[i] = [TTkColor.RST]*w
self._width = w
self._height = h
TTkHelper.addPaintBuffer(self)
def clean(self, pos=(0, 0), size=None):
x,y = pos
@ -119,7 +112,6 @@ class TTkCanvas:
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

25
TermTk/TTkCore/helper.py

@ -31,7 +31,7 @@ class TTkHelper:
_focusWidget = None
_rootCanvas = None
_updateWidget = []
_paintBuffer = []
_updateBuffer = []
@staticmethod
def addUpdateWidget(widget):
@ -39,31 +39,36 @@ class TTkHelper:
TTkHelper._updateWidget.append(widget)
@staticmethod
def addPaintBuffer(canvas):
def addUpdateBuffer(canvas):
if canvas is not TTkHelper._rootCanvas:
if canvas not in TTkHelper._paintBuffer:
TTkHelper._paintBuffer.append(canvas)
if canvas not in TTkHelper._updateBuffer:
TTkHelper._updateBuffer.append(canvas)
@staticmethod
def registerRootCanvas(canvas):
TTkHelper._rootCanvas = canvas
TTkHelper._paintBuffer = []
TTkHelper._updateBuffer = []
TTkHelper._updateWidget = []
@staticmethod
def paintAll():
if TTkHelper._rootCanvas is None:
return
processed = []
for widget in TTkHelper._updateWidget:
processed.append(widget)
#processed = []
pushToTerminal = False
for widget in TTkHelper._updateBuffer:
widget.paintEvent()
TTkHelper._updateBuffer = []
for widget in TTkHelper._updateWidget:
pushToTerminal = True
#processed.append(widget)
widget.paintChildCanvas()
p = widget.parentWidget()
#p = widget.parentWidget()
#if p in TTkHelper._updateWidget and p not in processed:
widget.paintNotifyParent()
TTkHelper._updateWidget = []
TTkHelper._rootCanvas.pushToTerminal(0, 0, TTkGlbl.term_w, TTkGlbl.term_h)
if pushToTerminal:
TTkHelper._rootCanvas.pushToTerminal(0, 0, TTkGlbl.term_w, TTkGlbl.term_h)
@staticmethod
def absPos(widget) -> (int,int):

26
TermTk/TTkCore/ttk.py

@ -33,6 +33,19 @@ from TermTk.TTkCore.canvas import *
from TermTk.TTkWidgets.layout import *
from TermTk.TTkWidgets.widget import *
class TTkTimer(threading.Thread):
def __init__(self, callback):
threading.Thread.__init__(self)
self.stopped = threading.Event()
self._callback = callback
def quit(self):
self.stopped.set()
def run(self):
while not self.stopped.wait(0.05):
self._callback()
class TTk(TTkWidget):
running: bool = False
events = None
@ -46,6 +59,9 @@ class TTk(TTkWidget):
QUIT_EVENT = 0x08
TIME_EVENT = 0x10
HORIZONTAL = 0x01
VERTICAL = 0x02
def __init__(self, *args, **kwargs):
TTkWidget.__init__(self, *args, **kwargs)
self.events = queue.Queue()
@ -69,7 +85,8 @@ class TTk(TTkWidget):
lbt.Term.registerResizeCb(self._win_resize_cb)
threading.Thread(target=self._input_thread, daemon=True).start()
# threading.Timer(30.0, hello)
self._timer = TTkTimer(self._time_event)
self._timer.start()
self.running = True
lbt.Term.init()
@ -91,6 +108,7 @@ class TTk(TTkWidget):
# TTkLog.info(f"Key Event: {kevt}")
pass
elif evt is TTk.TIME_EVENT:
TTkHelper.paintAll()
pass
elif evt is TTk.SCREEN_EVENT:
self.setGeometry(0,0,TTkGlbl.term_w,TTkGlbl.term_h)
@ -102,11 +120,12 @@ class TTk(TTkWidget):
TTkLog.error(f"Unhandled Event {evt}")
break
TTkHelper.paintAll()
lbt.Term.exit()
pass
def _time_event(self):
self.events.put(TTk.TIME_EVENT)
def _win_resize_cb(self, width, height):
TTkGlbl.term_w = int(width)
TTkGlbl.term_h = int(height)
@ -129,6 +148,7 @@ class TTk(TTkWidget):
def quit(self):
self.events.put(TTk.QUIT_EVENT)
self._timer.quit()
self.running = False
def _SIGSTOP(self, signum, frame):

1
TermTk/TTkWidgets/__init__.py

@ -3,4 +3,5 @@ from .button import *
from .layout import *
from .widget import *
from .window import *
from .logviewer import *
from .testwidget import *

1
TermTk/TTkWidgets/button.py

@ -27,6 +27,7 @@ from TermTk.TTkCore.color import TTkColor
from TermTk.TTkWidgets.widget import *
class TTkButton(TTkWidget):
__slots__ = ('_text', '_border', '_pressed')
def __init__(self, *args, **kwargs):
TTkWidget.__init__(self, *args, **kwargs)
self._text = kwargs.get('text', "" )

6
TermTk/TTkWidgets/frame.py

@ -26,14 +26,12 @@ from TermTk.TTkCore.log import TTkLog
from .widget import *
class TTkFrame(TTkWidget):
__slots__ = ('_border')
def __init__(self, *args, **kwargs):
TTkWidget.__init__(self, *args, **kwargs)
self._border = kwargs.get('border', True )
if self._border:
self._padt = 1
self._padb = 1
self._padl = 1
self._padr = 1
self.setPadding(1,1,1,1)
def paintEvent(self):
if self._border:

4
TermTk/TTkWidgets/layout.py

@ -171,10 +171,10 @@ class TTkHBoxLayout(TTkLayout):
for item in self.children():
if item._sMax:
item.setGeometry(newx, newy, item._sMaxVal, h)
newy += item._sMaxVal
newx += item._sMaxVal
elif item._sMin:
item.setGeometry(newx, newy, item._sMinVal, h)
newy += item._sMinVal
newx += item._sMinVal
else:
sliceSize = freeWidth//leftWidgets
item.setGeometry(newx, newy, sliceSize, h)

23
TermTk/TTkWidgets/logviewer.py

@ -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.

52
TermTk/TTkWidgets/splitter.py

@ -0,0 +1,52 @@
#!/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 TermTk.TTkCore.color import TTkColor
from TermTk.TTkCore.ttk import *
from TermTk.TTkWidgets.widget import *
from TermTk.TTkWidgets.frame import *
class TTkSplitter(TTkFrame):
__slots__ = ('_widgets', '_orientation', '_splitters', '_selected')
def __init__(self, *args, **kwargs):
TTkFrame.__init__(self, *args, **kwargs)
self._widgets = []
self._splitters = []
self._orientation = kwargs.get('orientation' , TTk.HORIZONTAL )
def addWidget(self, widget):
# NOTE: Check with the max/min size if the new widget can fit
self._widgets.append(widget)
self._splitters.append(0x10000)
self._rearrange()
def _rearrange(self):
w, h = self.size()
if self._orientation == TTk.HORIZONTAL:
pass
else:
pass

6
TermTk/TTkWidgets/testwidget.py

@ -49,12 +49,14 @@ class _TestContent(TTkWidget):
class TTkTestWidget(TTkFrame):
ID = 1
__slots__ = ('_name')
def __init__(self, *args, **kwargs):
TTkFrame.__init__(self, *args, **kwargs)
#self.setLayout(TTkHBoxLayout())
self._name = f"TestWidget-{TTkTestWidget.ID}"
TTkButton(parent=self, x=1, y=1, width=15, height=3, text=' Test Button')
_TestContent(parent=self, x=1, y=4, width=50, height=50, name=f"content-{self._name}")
t,_,l,_ = self.getPadding()
TTkButton(parent=self, x=l, y=t, width=15, height=3, text=' Test Button')
_TestContent(parent=self, x=l, y=3+t, width=50, height=50, name=f"content-{self._name}")
TTkTestWidget.ID+=1
def mousePressEvent(self, evt):

52
TermTk/TTkWidgets/widget.py

@ -55,8 +55,15 @@ class TTkWidget:
'''
__slots__ = (
'_name', '_parent',
'_x', '_y', '_width', '_height',
'_padt', '_padb', '_padl', '_padr',
'_maxw', '_maxh', '_minw', '_minh',
'_focus','_focus_policy',
'_layout', '_canvas')
def __init__(self, *args, **kwargs):
self._childs = []
self._name = kwargs.get('name', None )
self._parent = kwargs.get('parent', None )
@ -67,15 +74,18 @@ class TTkWidget:
self._height = kwargs.get('height', 0 )
self._width, self._height = kwargs.get('size', (self._width, self._height))
self._padt = kwargs.get('paddingTop', 0 )
self._padb = kwargs.get('paddingBottom', 0 )
self._padl = kwargs.get('paddingLeft', 0 )
self._padr = kwargs.get('paddingRight', 0 )
padding = kwargs.get('padding', 0 )
self._padt = kwargs.get('paddingTop', padding )
self._padb = kwargs.get('paddingBottom', padding )
self._padl = kwargs.get('paddingLeft', padding )
self._padr = kwargs.get('paddingRight', padding )
self._maxw = 0x10000
self._maxh = 0x10000
self._minw = 0x00000
self._minh = 0x00000
self._maxw = kwargs.get('maxWidth', 0x10000)
self._maxh = kwargs.get('maxHeight', 0x10000)
self._maxw, self._maxh = kwargs.get('maxSize', (self._maxw, self._maxh))
self._minw = kwargs.get('minWidth', 0x00000)
self._minh = kwargs.get('minHeight', 0x00000)
self._minw, self._minh = kwargs.get('minSize', (self._minw, self._minh))
self._focus = False
self._focus_policy = TTkWidget.NoFocus
@ -84,10 +94,14 @@ class TTkWidget:
widget = self,
width = self._width ,
height = self._height )
self.setLayout(TTkLayout())
if self._parent is not None and \
self._parent._layout is not None:
self._parent._layout.addWidget(self)
self._parent._layout.update()
self.update(repaint=True, updateLayout=True)
def addLayout(self, l):
self._layout = l
@ -120,8 +134,7 @@ class TTkWidget:
def move(self, x, y):
self._x = x
self._y = y
self._canvas.move(self._x, self._y)
self.update()
self.update(repaint=False, updateLayout=False)
def resize(self, w, h):
self._width = w
@ -132,12 +145,15 @@ class TTkWidget:
self._padl, self._padt,
self._width - self._padl - self._padr,
self._height - self._padt - self._padb)
self.update()
self.update(repaint=True, updateLayout=True)
def setGeometry(self, x, y, w, h):
self.resize(w, h)
self.move(x, y)
def getPadding(self):
return self._padt, self._padb, self._padl, self._padr
def setPadding(self, top, bottom, left, right):
self._padt = top
self._padb = bottom
@ -165,8 +181,6 @@ class TTkWidget:
x, y = evt.x, evt.y
lx,ly,lw,lh =layout.geometry()
# opt of bounds
#x-=lx
#y-=ly
if x<0 or x>lw or y<0 or y>lh:
return True
for i in range(layout.count()):
@ -264,7 +278,7 @@ class TTkWidget:
self._padl, self._padt,
self._width - self._padl - self._padr,
self._height - self._padt - self._padb)
self._layout.update()
self.update(repaint=True, updateLayout=True)
def layout(self): return self._layout
@ -324,9 +338,11 @@ class TTkWidget:
def setMinimumHeight(self, minh): self._minh = minh
def setMinimumWidth(self, minw): self._minw = minw
def update(self):
def update(self, repaint=True, updateLayout=False):
if repaint:
TTkHelper.addUpdateBuffer(self)
TTkHelper.addUpdateWidget(self)
if self._layout is not None:
if updateLayout and self._layout is not None:
self._layout.update()
def setFocus(self):
@ -334,7 +350,7 @@ class TTkWidget:
if tmp is not None:
tmp.clearFocus()
tmp.focusOutEvent()
tmp.update()
tmp.update(repaint=True, updateLayout=False)
TTkHelper.setFocus(self)
self._focus = True
self.focusInEvent()

1
TermTk/TTkWidgets/window.py

@ -29,6 +29,7 @@ from TermTk.TTkWidgets.widget import TTkWidget
class TTkWindow(TTkWidget):
__slots__ = ('_title', '_mouseDelta', '_draggable', '_resizable')
def __init__(self, *args, **kwargs):
TTkWidget.__init__(self, *args, **kwargs)
self._title = kwargs.get('title' , 0 )

10
tests/test.ui.004.py

@ -40,11 +40,15 @@ ttk.TTkTestWidget(parent=win2, border=False)
win3 = ttk.TTkWindow(parent=root,pos = (20,5), size=(60,20), title="Test Window 3", border=True)
win3.setLayout(ttk.TTkHBoxLayout())
ttk.TTkTestWidget(parent=win3,border=True)
ttk.TTkTestWidget(parent=win3, border=True, maxWidth=30, minWidth=20)
rightFrame = ttk.TTkFrame(parent=win3,border=True)
rightFrame.setLayout(ttk.TTkVBoxLayout())
ttk.TTkTestWidget(parent=rightFrame,border=True)
ttk.TTkFrame(parent=rightFrame,border=True)
ttk.TTkTestWidget(parent=rightFrame,border=True, maxHeight=11, minHeight=6)
bottomrightframe = ttk.TTkFrame(parent=rightFrame,border=True)
win4 = ttk.TTkWindow(parent=bottomrightframe, pos = (3,3), size=(40,20), title="Test Window 4", border=True)
win4.setLayout(ttk.TTkHBoxLayout())
ttk.TTkTestWidget(parent=win4, border=False)
root.mainloop()
Loading…
Cancel
Save