Browse Source

Extend property animation support for function pointer and type hints

pull/99/head
Eugenio Parodi 3 years ago
parent
commit
cb068f0785
  1. 6
      TermTk/TTkAbstract/abstractscrollview.py
  2. 37
      TermTk/TTkCore/propertyanimation.py
  3. 48
      TermTk/TTkTestWidgets/keypressview.py
  4. 12
      TermTk/TTkWidgets/Fancy/tableview.py
  5. 26
      TermTk/TTkWidgets/widget.py
  6. 97
      demo/showcase/animation.01.py
  7. 38
      tests/test.argspec.001.py

6
TermTk/TTkAbstract/abstractscrollview.py

@ -38,7 +38,7 @@ class TTkAbstractScrollViewInterface():
raise NotImplementedError()
@pyTTkSlot(int, int)
def viewMoveTo(self, x, y):
def viewMoveTo(self, x: int, y: int):
raise NotImplementedError()
def getViewOffsets(self):
@ -60,7 +60,7 @@ class TTkAbstractScrollView(TTkWidget, TTkAbstractScrollViewInterface):
self._viewOffsetY = 0
@pyTTkSlot(int, int)
def viewMoveTo(self, x, y):
def viewMoveTo(self, x: int, y: int):
fw, fh = self.viewFullAreaSize()
dw, dh = self.viewDisplayedSize()
rangex = fw - dw
@ -113,7 +113,7 @@ class TTkAbstractScrollViewGridLayout(TTkGridLayout, TTkAbstractScrollViewInterf
self._excludeEvent = False
@pyTTkSlot(int, int)
def viewMoveTo(self, x, y):
def viewMoveTo(self, x: int, y: int):
fw, fh = self.viewFullAreaSize()
dw, dh = self.viewDisplayedSize()
rangex = fw - dw

37
TermTk/TTkCore/propertyanimation.py

@ -21,7 +21,10 @@
# SOFTWARE.
import time, math
from inspect import getfullargspec
from types import LambdaType
from TermTk.TTkCore.log import TTkLog
from TermTk.TTkCore.signal import pyTTkSignal, pyTTkSlot
from TermTk.TTkCore.helper import TTkHelper
@ -127,7 +130,7 @@ class TTkEasingCurve():
return n1 * x * x + 0.984375
class TTkPropertyAnimation():
__slots__ = ('_target', '_propertyName', '_parent',
__slots__ = ('_target', '_propertyName', '_parent', '_cb', '_cast',
'_duration', '_startValue', '_endValue',
'_easingCurve', '_baseTime')
def __init__(self, target, propertyName, parent=None):
@ -140,6 +143,25 @@ class TTkPropertyAnimation():
self._endValue = None
self._easingCurve = TTkEasingCurve(TTkEasingCurve.Linear)
if type(propertyName) == str:
self._cb = getattr(self._target,self._propertyName)
else:
self._cb = propertyName
def _cast():
_spec = getfullargspec(self._cb)
if isinstance(self._cb, LambdaType) and self._cb.__name__ == "<lambda>":
_args = _spec.args
else:
_args = _spec.args[1:] if hasattr(self._cb, '__self__') else _spec.args
_castList = [
(lambda x:_spec.annotations[a](x)) if a in _spec.annotations else (lambda x:x) for a in _args]
def _ret(*args):
return [c(x) for (c,x) in zip(_castList, args)]
return _ret
self._cast = _cast()
def setDuration(self, duration):
self._duration = duration
@ -155,20 +177,21 @@ class TTkPropertyAnimation():
@pyTTkSlot()
def _refreshAnimation(self):
diff = time.time() - self._baseTime
# TTkLog.info(f"diff: {diff}")
if diff >= self._duration:
TTkHelper._rootWidget.paintExecuted.disconnect(self._refreshAnimation)
if type(self._endValue) in (list,tuple):
getattr(self._target,self._propertyName)(*self._endValue)
self._cb(*self._cast(*self._endValue))
else:
getattr(self._target,self._propertyName)(self._endValue)
self._cb(*self._cast(self._endValue))
else:
v = diff/self._duration
if type(self._startValue) in (list,tuple):
newVal = [int(self._easingCurve.process(s,e,v)) for (s,e) in zip(self._startValue,self._endValue)]
getattr(self._target,self._propertyName)(*newVal)
newVal = [self._easingCurve.process(s,e,v) for (s,e) in zip(self._startValue,self._endValue)]
self._cb(*self._cast(*newVal))
else:
newVal = int(self._easingCurve.process(self._startValue,self._endValue,v))
getattr(self._target,self._propertyName)(newVal)
newVal = self._easingCurve.process(self._startValue,self._endValue,v)
self._cb(*self._cast(newVal))
@pyTTkSlot()
def start(self):

48
TermTk/TTkTestWidgets/keypressview.py

@ -27,24 +27,21 @@ from TermTk.TTkCore.TTkTerm.inputmouse import TTkMouseEvent
from TermTk.TTkCore.helper import TTkHelper
from TermTk.TTkCore.signal import pyTTkSlot
from TermTk.TTkCore.constant import TTkK
from TermTk.TTkCore.timer import TTkTimer
from TermTk.TTkCore.propertyanimation import TTkPropertyAnimation, TTkEasingCurve
from TermTk.TTkCore.color import TTkColor
from TermTk.TTkWidgets.widget import TTkWidget
from TermTk.TTkTestWidgets.keypressviewfont import TTkKeyPressViewFont
class TTkKeyPressView(TTkWidget):
__slots__ = ('_timer','_keys','_fade','_period')
__slots__ = ('_fadeDuration','_keys','_anim')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
TTkHelper._rootWidget._input.inputEvent.connect(self._processInput)
self._keys = []
self._period = 0.1
self._fade = 10
self._timer = TTkTimer()
self._timer.timeout.connect(self._timerEvent)
self._timer.start(self._period)
self._fadeDuration = 2.5
self._anim = TTkPropertyAnimation(self, '_pushFade')
@pyTTkSlot(TTkKeyEvent, TTkMouseEvent)
def _processInput(self, kevt, mevt):
@ -62,10 +59,10 @@ class TTkKeyPressView(TTkWidget):
text = f"{m} {text}"
if self._keys and evt.type == self._keys[-1][2] == TTkK.Character:
self._keys[-1][1]+=evt.key
self._keys[-1][0]=0
self._keys[-1][0]=1
else:
self._keys.append([0,text,evt.type])
self.update()
self._keys.append([1,text,evt.type])
self._startFade()
@pyTTkSlot(TTkMouseEvent)
def _addMouse(self, evt):
@ -77,17 +74,22 @@ class TTkKeyPressView(TTkWidget):
if evt.tap>3: tap=f" {evt.tap} Clicks "
text = f"M:{(evt.x,evt.y)} {evt.key2str().replace('Button','')}{tap}{evt.mod2str().replace('NoModifier','')}"
self._keys.append([0,text,0x100])
self.update()
self._keys.append([1,text,0x100])
self._startFade()
def _timerEvent(self):
def _startFade(self):
self._anim.setDuration(self._fadeDuration)
self._anim.setStartValue(0)
self._anim.setEndValue(1)
self._anim.setEasingCurve(TTkEasingCurve.OutExpo)
self._anim.start()
def _pushFade(self, fade: float):
for i,k in enumerate(self._keys):
if k[0] > self._fade:
k[0] -= fade
if k[0] <= 0:
self._keys.pop(i)
else:
k[0] += 1
self.update()
self._timer.start(self._period)
self.update()
def txt2map(self, txt):
ret = ["","",""]
@ -99,12 +101,10 @@ class TTkKeyPressView(TTkWidget):
return ret
def paintEvent(self):
for k in self._keys:
text = k[1]
gr = 0x1000 - 0x1000 * k[0] // self._fade
r = 0xbb*gr//0x1000
g = 0xff*gr//0x1000
b = 0xff*gr//0x1000
for alpha,text,_ in self._keys:
r = int(0xbb*alpha)
g = int(0xff*alpha)
b = int(0xff*alpha)
color = TTkColor.fg(f"#{r<<16|g<<8|b:06x}")
#self._canvas.drawText(pos=((self.width()-len(text))//2,0),text=text,color=color)
m = self.txt2map(text)

12
TermTk/TTkWidgets/Fancy/tableview.py

@ -221,7 +221,7 @@ class _TTkFancyTableView(TTkAbstractScrollView):
self.viewChanged.emit()
self.update()
def insertItem(self, index, item, id=None):
def insertItem(self, index: int, item, id=None):
if len(item) != len(self._columns):
return#
textItem = [TTkString(i) if isinstance(i,str) else i if issubclass(type(i), TTkString) else TTkString() for i in item]
@ -243,7 +243,7 @@ class _TTkFancyTableView(TTkAbstractScrollView):
self.viewChanged.emit()
self.update()
def removeItemAt(self, index):
def removeItemAt(self, index: int):
if self._selected == index:
self._selected = -1
del self._tableDataId[index]
@ -253,7 +253,7 @@ class _TTkFancyTableView(TTkAbstractScrollView):
self.viewChanged.emit()
self.update()
def removeItemsFrom(self, index):
def removeItemsFrom(self, index: int):
if self._selected >= index:
self._selected = -1
self._tableDataId = self._tableDataId[:index]
@ -263,7 +263,7 @@ class _TTkFancyTableView(TTkAbstractScrollView):
self.viewChanged.emit()
self.update()
def itemAt(self, index):
def itemAt(self, index: int):
if 0 <= index < len(self._tableDataId):
if item:=self._tableDataWidget[index]:
return item
@ -271,7 +271,7 @@ class _TTkFancyTableView(TTkAbstractScrollView):
return self._tableDataText[index]
return None
def dataAt(self, index):
def dataAt(self, index: int):
if 0 <= index < len(self._tableDataId):
return self._tableDataId[index]
return None
@ -392,7 +392,7 @@ class TTkFancyTableView(TTkAbstractScrollView):
self.removeItemsFrom = self._tableView.removeItemsFrom
@pyTTkSlot(int, int)
def viewMoveTo(self, x, y):
def viewMoveTo(self, x: int, y: int):
self._tableView.viewMoveTo(x, y)
def getViewOffsets(self):

26
TermTk/TTkWidgets/widget.py

@ -274,7 +274,7 @@ class TTkWidget(TMouseEvents,TKeyEvents, TDragEvents):
''' Event Callback triggered after a successful resize'''
pass
def setDefaultSize(self, arg, width, height):
def setDefaultSize(self, arg, width: int, height: int):
if ( 'size' in arg or
'width' in arg or
'height' in arg ):
@ -327,7 +327,7 @@ class TTkWidget(TMouseEvents,TKeyEvents, TDragEvents):
'''
return self._padt, self._padb, self._padl, self._padr
def setPadding(self, top, bottom, left, right):
def setPadding(self, top: int, bottom: int, left: int, right: int):
''' Set the padding of the widget
:param int top: top padding
@ -544,26 +544,26 @@ class TTkWidget(TMouseEvents,TKeyEvents, TDragEvents):
return lMinW
return wMinW
def setMaximumSize(self, maxw, maxh):
def setMaximumSize(self, maxw: int, maxh: int):
self.setMaximumWidth(maxw)
self.setMaximumHeight(maxh)
def setMaximumHeight(self, maxh):
def setMaximumHeight(self, maxh: int):
if self._maxh == maxh: return
self._maxh = maxh
self.update(updateLayout=True, updateParent=True)
def setMaximumWidth(self, maxw):
def setMaximumWidth(self, maxw: int):
if self._maxw == maxw: return
self._maxw = maxw
self.update(updateLayout=True, updateParent=True)
def setMinimumSize(self, minw, minh):
def setMinimumSize(self, minw: int, minh: int):
self.setMinimumWidth(minw)
self.setMinimumHeight(minh)
def setMinimumHeight(self, minh):
def setMinimumHeight(self, minh: int):
if self._minh == minh: return
self._minh = minh
self.update(updateLayout=True, updateParent=True)
def setMinimumWidth(self, minw):
def setMinimumWidth(self, minw: int):
if self._minw == minw: return
self._minw = minw
self.update(updateLayout=True, updateParent=True)
@ -609,7 +609,7 @@ class TTkWidget(TMouseEvents,TKeyEvents, TDragEvents):
self.hide()
@pyTTkSlot(bool)
def setVisible(self, visible):
def setVisible(self, visible: bool):
if visible: self.show()
else: self.hide()
@ -625,7 +625,7 @@ class TTkWidget(TMouseEvents,TKeyEvents, TDragEvents):
# TODO: Remove This
def layoutUpdated(self): pass
def update(self, repaint=True, updateLayout=False, updateParent=False):
def update(self, repaint: bool =True, updateLayout: bool =False, updateParent: bool =False):
if repaint:
TTkHelper.addUpdateBuffer(self)
TTkHelper.addUpdateWidget(self)
@ -686,7 +686,7 @@ class TTkWidget(TMouseEvents,TKeyEvents, TDragEvents):
return self._enabled
@pyTTkSlot(bool)
def setEnabled(self, enabled=True):
def setEnabled(self, enabled: bool=True):
if self._enabled == enabled: return
self._enabled = enabled
self.update()
@ -709,10 +709,10 @@ class TTkWidget(TMouseEvents,TKeyEvents, TDragEvents):
def toolTip(self):
return self._toolTip
def setToolTip(self, toolTip):
def setToolTip(self, toolTip: TTkString):
self._toolTip = toolTip
def getWidgetByName(self, name):
def getWidgetByName(self, name: str):
for w in self.rootLayout().iterWidgets(onlyVisible=False, recurse=True):
if w._name == name:
return w

97
demo/showcase/animation.01.py

@ -40,22 +40,97 @@ class superSimpleHorizontalLine(ttk.TTkWidget):
def demoTextEditRO(root=None):
easingList = (
(ttk.TTkEasingCurve.Linear , 'Linear'),
(ttk.TTkEasingCurve.InQuad , 'InQuad'),
(ttk.TTkEasingCurve.OutQuad , 'OutQuad'),
(ttk.TTkEasingCurve.InOutQuad , 'InOutQuad'),
(ttk.TTkEasingCurve.OutInQuad , 'OutInQuad'),
(ttk.TTkEasingCurve.InCubic , 'InCubic'),
(ttk.TTkEasingCurve.OutCubic , 'OutCubic'),
(ttk.TTkEasingCurve.InOutCubic , 'InOutCubic'),
(ttk.TTkEasingCurve.OutInCubic , 'OutInCubic'),
(ttk.TTkEasingCurve.InQuart , 'InQuart'),
(ttk.TTkEasingCurve.OutQuart , 'OutQuart'),
(ttk.TTkEasingCurve.InOutQuart , 'InOutQuart'),
(ttk.TTkEasingCurve.OutInQuart , 'OutInQuart'),
(ttk.TTkEasingCurve.InQuint , 'InQuint'),
(ttk.TTkEasingCurve.OutQuint , 'OutQuint'),
(ttk.TTkEasingCurve.InOutQuint , 'InOutQuint'),
(ttk.TTkEasingCurve.OutInQuint , 'OutInQuint'),
(ttk.TTkEasingCurve.InSine , 'InSine'),
(ttk.TTkEasingCurve.OutSine , 'OutSine'),
(ttk.TTkEasingCurve.InOutSine , 'InOutSine'),
(ttk.TTkEasingCurve.OutInSine , 'OutInSine'),
(ttk.TTkEasingCurve.InExpo , 'InExpo'),
(ttk.TTkEasingCurve.OutExpo , 'OutExpo'),
(ttk.TTkEasingCurve.InOutExpo , 'InOutExpo'),
(ttk.TTkEasingCurve.OutInExpo , 'OutInExpo'),
(ttk.TTkEasingCurve.InCirc , 'InCirc'),
(ttk.TTkEasingCurve.OutCirc , 'OutCirc'),
(ttk.TTkEasingCurve.InOutCirc , 'InOutCirc'),
(ttk.TTkEasingCurve.OutInCirc , 'OutInCirc'),
(ttk.TTkEasingCurve.InElastic , 'InElastic'),
(ttk.TTkEasingCurve.OutElastic , 'OutElastic'),
(ttk.TTkEasingCurve.InOutElastic , 'InOutElastic'),
(ttk.TTkEasingCurve.OutInElastic , 'OutInElastic'),
(ttk.TTkEasingCurve.InBack , 'InBack'),
(ttk.TTkEasingCurve.OutBack , 'OutBack'),
(ttk.TTkEasingCurve.InOutBack , 'InOutBack'),
(ttk.TTkEasingCurve.OutInBack , 'OutInBack'),
(ttk.TTkEasingCurve.InBounce , 'InBounce'),
(ttk.TTkEasingCurve.OutBounce , 'OutBounce'),
(ttk.TTkEasingCurve.InOutBounce , 'InOutBounce'),
(ttk.TTkEasingCurve.OutInBounce , 'OutInBounce'))
frame = ttk.TTkFrame(parent=root, border=False)
winTe = ttk.TTkWindow(parent=frame, title="Text Edit", pos=(20,3), size=(50,30), layout=ttk.TTkGridLayout())
te = ttk.TTkTextEdit(parent=winTe, lineNumber=True)
winAc = ttk.TTkWindow(parent=frame, title="Animation Controls", pos=(0,0), size=(50,30))
animBtn = ttk.TTkButton(parent=winAc, text="Animate",border=True,pos=(0,0))
anim = ttk.TTkPropertyAnimation(te.viewport(),'viewMoveTo')
anim.setDuration(1)
anim.setStartValue((0, 0))
anim.setEndValue(( 00, 70))
# anim.setEasingCurve(ttk.TTkEasingCurve.OutQuad)
anim.setEasingCurve(ttk.TTkEasingCurve.OutBounce)
animBtn.clicked.connect(anim.start)
animBtnScroll = ttk.TTkButton(parent=winAc, text="Anim Scroll",border=True,pos=(0,0))
animBtnWinPos = ttk.TTkButton(parent=winAc, text="Anim Pos",border=True,pos=(15,0))
animBoth = ttk.TTkButton(parent=winAc, text="Anim Both",border=True,pos=(25,0))
class PosControls(ttk.TTkFrame):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs|{'border':True})
ttk.TTkLabel( parent=self,pos=(0,0),text='Starting Position (x,y)')
self.axspb = ttk.TTkSpinBox(parent=self,maximum=200, minimum=-100, pos=(0,1),size=(9,1),value=0)
self.ayspb = ttk.TTkSpinBox(parent=self,maximum=200, minimum=-100, pos=(8,1),size=(9,1),value=100)
ttk.TTkLabel( parent=self,pos=(0,2),text='Ending Position (x,y)')
self.bxspb = ttk.TTkSpinBox(parent=self,maximum=200, minimum=-100, pos=(0,3),size=(9,1),value=0)
self.byspb = ttk.TTkSpinBox(parent=self,maximum=200, minimum=-100, pos=(8,3),size=(9,1),value=0)
ttk.TTkLabel( parent=self,pos=(0,4),text='Duration (sec.)')
self.dursb = ttk.TTkSpinBox(parent=self,maximum=200, minimum=0, pos=(0,5),size=(12,1),value=2)
ttk.TTkLabel( parent=self,pos=(0,6),text='Easing Curve')
self.ecb = ttk.TTkComboBox(parent=self,pos=(0,7),size=(20,1),list=[v for (_,v) in easingList],index=0)
pcScroll = PosControls(parent=winAc, pos=(0,3), size=(25,10), title="Text Scroll")
pcWinPos = PosControls(parent=winAc, pos=(0,13), size=(25,10), title="Window Position")
animScroll = ttk.TTkPropertyAnimation(te.viewport(),'viewMoveTo')
animWinPos = ttk.TTkPropertyAnimation(None, winTe.move)
def _startAnimScroll():
animScroll.setDuration(pcScroll.dursb.value())
animScroll.setStartValue((pcScroll.axspb.value(), pcScroll.ayspb.value()))
animScroll.setEndValue( (pcScroll.bxspb.value(), pcScroll.byspb.value()))
animScroll.setEasingCurve({t:v for (v,t) in easingList}.get(pcScroll.ecb.currentText(),easingList[0][0]))
animScroll.start()
def _startAnimWinPos():
animWinPos.setDuration(pcWinPos.dursb.value())
animWinPos.setStartValue((pcWinPos.axspb.value(), pcWinPos.ayspb.value()))
animWinPos.setEndValue( (pcWinPos.bxspb.value(), pcWinPos.byspb.value()))
animWinPos.setEasingCurve({t:v for (v,t) in easingList}.get(pcWinPos.ecb.currentText(),easingList[0][0]))
animWinPos.start()
animBtnScroll.clicked.connect(_startAnimScroll)
animBtnWinPos.clicked.connect(_startAnimWinPos)
animBoth.clicked.connect(_startAnimScroll)
animBoth.clicked.connect(_startAnimWinPos)
# Initialize the textedit with come text
@ -102,7 +177,7 @@ def demoTextEditRO(root=None):
te.append("-------tab\ttab\ttab\ttab\ttab\ttab\ttab\ttab\ttab\ttab\ttab\ttab\n")
te.append(ttk.TTkString("Random TTkString Input Test\n",ttk.TTkColor.UNDERLINE+ttk.TTkColor.BOLD))
te.append(ttk.TTkString('\n').join([ getUtfColoredSentence(3,10) for _ in range(100)]))
te.append(ttk.TTkString('\n').join([ getUtfColoredSentence(10,15) for _ in range(100)]))
te.append(ttk.TTkString("-- The Very END --",ttk.TTkColor.UNDERLINE+ttk.TTkColor.BOLD))

38
tests/test.argspec.001.py

@ -0,0 +1,38 @@
#!/usr/bin/env python3
# MIT License
#
# Copyright (c) 2023 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 inspect import getfullargspec
def f1(a:int, b:float =123.123, c:str = "pippo", d=1234) -> str:
return f"{a=} {b=} {c=}"
spec = getfullargspec(f1)
print(f"{spec=}")
x = spec.annotations['a'](1.001)
y = spec.annotations['b'](1.001)
z = spec.annotations['c'](1.001)
print(f"{x=} {y=} {z=}")
Loading…
Cancel
Save