Browse Source

Merge remote-tracking branch 'origin' into ttkDesigner

pull/115/head
Eugenio Parodi 3 years ago
parent
commit
5831c7b3fa
  1. 2
      TermTk/TTkCore/TTkTerm/input.py
  2. 12
      TermTk/TTkCore/TTkTerm/term_base.py
  3. 27
      TermTk/TTkCore/helper.py
  4. 76
      TermTk/TTkCore/timer.py
  5. 18
      TermTk/TTkCore/ttk.py
  6. 3
      TermTk/TTkGui/__init__.py
  7. 3
      TermTk/TTkGui/drag.py
  8. 78
      TermTk/TTkGui/tooltip.py
  9. 5
      TermTk/TTkTheme/theme.py
  10. 24
      TermTk/TTkWidgets/button.py
  11. 66
      TermTk/TTkWidgets/widget.py
  12. 4
      demo/demo.py
  13. 4
      docs/MDNotes/TODO.md
  14. 2
      tests/pytest/mock_term.py
  15. 10
      tests/sandbox/Makefile
  16. 133
      tests/sandbox/sandbox.html
  17. 0
      tests/test.generic.001.py
  18. 0
      tests/test.generic.002.py
  19. 0
      tests/test.generic.003.py
  20. 0
      tests/test.generic.004.footprint.py
  21. 0
      tests/test.generic.006.weakref.01.py
  22. 0
      tests/test.generic.006.weakref.02.py
  23. 144
      tests/test.generic.007.timer.py
  24. 46
      tests/test.ui.025.toolTip.py

2
TermTk/TTkCore/TTkTerm/input.py

@ -96,7 +96,7 @@ class TTkInput:
y = int(m.group(3))-1
state = m.group(4)
key = TTkMouseEvent.NoButton
evt = TTkMouseEvent.NoEvent
evt = TTkMouseEvent.Move
tap = 0
def _checkTap(lastTime, tap):

12
TermTk/TTkCore/TTkTerm/term_base.py

@ -82,19 +82,23 @@ class TTkTermBase():
CTRL_Q = 0x0008
title: str = "TermTk"
mouse: bool = True
width: int = 0
height: int = 0
mouse: bool = True
directMouse: bool = False
_sigWinChCb = None
@staticmethod
def init(mouse: bool = True, title: str = "TermTk", sigmask=0):
def init(mouse: bool = True, directMouse: bool = False, title: str = "TermTk", sigmask=0):
TTkTermBase.title = title
TTkTermBase.mouse = mouse
TTkTermBase.mouse = mouse | directMouse
TTkTermBase.directMouse = directMouse
TTkTermBase.push(TTkTermBase.ALT_SCREEN + TTkTermBase.CLEAR + TTkTermBase.Cursor.HIDE + TTkTermBase.escTitle(TTkTermBase.title))
if TTkTermBase.mouse:
TTkTermBase.push(TTkTermBase.Mouse.ON)
if TTkTermBase.directMouse:
TTkTermBase.push(TTkTermBase.Mouse.DIRECT_ON)
TTkTermBase.setEcho(False)
TTkTermBase.CRNL(False)
TTkTermBase.setSigmask(sigmask, False)
@ -118,6 +122,8 @@ class TTkTermBase():
TTkTermBase.push(TTkTermBase.ALT_SCREEN + TTkTermBase.CLEAR + TTkTermBase.Cursor.HIDE + TTkTermBase.escTitle(TTkTermBase.title))
if TTkTermBase.mouse:
TTkTermBase.push(TTkTermBase.Mouse.ON)
if TTkTermBase.directMouse:
TTkTermBase.push(TTkTermBase.Mouse.DIRECT_ON)
TTkTermBase.setEcho(False)
TTkTermBase.CRNL(False)

27
TermTk/TTkCore/helper.py

@ -446,3 +446,30 @@ class TTkHelper:
TTkHelper._rootWidget.rootLayout().removeWidget(TTkHelper._dnd['d'].pixmap())
TTkHelper._dnd = None
TTkHelper._rootWidget.update()
# ToolTip Helper Methods
toolTipWidget = None
toolTipTrigger = lambda _: True
toolTipReset = lambda : True
@staticmethod
def toolTipShow(tt):
TTkHelper.toolTipClose()
if not TTkHelper._rootWidget: return
TTkHelper.toolTipWidget = tt
rw,rh = TTkHelper._rootWidget.size()
tw,th = tt.size()
mx,my = TTkHelper._mousePos
x = max(0, min(mx-(tw//2),rw-tw))
if my <= th: # Draw below the Mouse
y = my+1
else: # Draw above the Mouse
y = max(0,my-th)
tt.move(x,y)
TTkHelper._rootWidget.rootLayout().addWidget(tt)
tt.raiseWidget()
def toolTipClose():
TTkHelper.toolTipReset()
if TTkHelper.toolTipWidget:
TTkHelper.toolTipWidget.close()

76
TermTk/TTkCore/timer.py

@ -1,5 +1,3 @@
#!/usr/bin/env python3
# MIT License
#
# Copyright (c) 2021 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com>
@ -35,7 +33,7 @@ if importlib.util.find_spec('pyodideProxy'):
_uid = 0
__slots__ = (
'_id', '_running',
'_id', '_running', '_timer',
'timeout', '_timerEvent',
'_delay', '_delayLock', '_quit',
'_stopTime')
@ -44,6 +42,7 @@ if importlib.util.find_spec('pyodideProxy'):
# Define Signals
self.timeout = pyTTkSignal()
self._running = True
self._timer = None
self._id = TTkTimer._uid
TTkTimer._uid +=1
@ -69,36 +68,33 @@ if importlib.util.find_spec('pyodideProxy'):
def quit(self):
pass
def run(self):
pass
@pyTTkSlot(int)
def start(self, sec=0):
@pyTTkSlot(float)
def start(self, sec=0.0):
self.stop()
if self._running:
pyodideProxy.setTimeout(int(sec*1000), self._id)
self._timer = pyodideProxy.setTimeout(int(sec*1000), self._id)
# pyodideProxy.consoleLog(f"Timer {self._timer}")
@pyTTkSlot()
def stop(self):
pass
# pyodideProxy.consoleLog(f"Timer {self._timer}")
if self._timer:
pyodideProxy.stopTimeout(self._timer)
self._timer = None
else:
class TTkTimer(threading.Thread):
_timers = []
__slots__ = (
'timeout', '_timerEvent',
'_delay', '_delayLock', '_quit',
'_stopTime')
'timeout', '_delay',
'_timer', '_quit', '_start')
def __init__(self):
# Define Signals
self.timeout = pyTTkSignal()
self._timerEvent = threading.Event()
self._quit = threading.Event()
self._stopTime = 0
self._delay=0
self._delayLock = threading.Lock()
threading.Thread.__init__(self)
self._delay = 0
self._quit = threading.Event()
self._start = threading.Event()
self._timer = threading.Event()
super().__init__()
TTkTimer._timers.append(self)
threading.Thread.start(self)
@staticmethod
def quitAll():
@ -107,28 +103,26 @@ else:
def quit(self):
self._quit.set()
self._delay=1
self._timerEvent.set()
self._timer.set()
self._start.set()
def run(self):
while self._timerEvent.wait():
self._timerEvent.clear()
while self._delay > 0:
# self._delayLock.acquire()
delay = self._delay
self._delay = 0
# self._delayLock.release()
if self._quit.wait(delay):
return
self.timeout.emit()
@pyTTkSlot(int)
def start(self, sec=0):
self._lastTime = time.time()
while not self._quit.is_set():
self._start.wait()
self._start.clear()
if not self._timer.wait(self._delay):
self.timeout.emit()
@pyTTkSlot(float)
def start(self, sec=0.0):
self._delay = sec
self._timerEvent.set()
self._timer.set()
self._timer.clear()
self._start.set()
if not self.native_id:
super().start()
@pyTTkSlot()
def stop(self):
# TODO: Timer.stop()
self._stopTime = time.time()
self._timer.set()

18
TermTk/TTkCore/ttk.py

@ -54,7 +54,7 @@ class TTk(TTkWidget):
self.resize(1,1)
input.inputEvent.connect(self._mouseInput)
@pyTTkSlot(TTkKeyEvent, TTkMouseEvent)
def _mouseInput(self, kevt, mevt):
def _mouseInput(self, _, mevt):
if mevt is not None:
self._cursor = ''
self._color = TTkColor.RST
@ -77,7 +77,7 @@ class TTk(TTkWidget):
#self._canvas.drawChar((0,0),'✜')
__slots__ = (
'_input',
'_input', '_termMouse', '_termDirectMouse',
'_title',
'_showMouseCursor',
'_sigmask',
@ -86,6 +86,8 @@ class TTk(TTkWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._termMouse = True
self._termDirectMouse = kwargs.get('mouseTrack',False)
self._input = TTkInput()
self._input.inputEvent.connect(self._processInput)
self._title = kwargs.get('title','TermTk')
@ -142,7 +144,11 @@ class TTk(TTkWidget):
# Keep track of the multiTap to avoid the extra key release
self._lastMultiTap = False
TTkTerm.init(title=self._title, sigmask=self._sigmask)
TTkTerm.init(
title=self._title,
sigmask=self._sigmask,
mouse=self._termMouse,
directMouse=self._termDirectMouse )
if self._showMouseCursor:
TTkTerm.push(TTkTerm.Mouse.DIRECT_ON)
@ -173,6 +179,7 @@ class TTk(TTkWidget):
# Upload the global mouse position
# Mainly used by the drag pixmap display
TTkHelper.setMousePos((mevt.x,mevt.y))
TTkWidget._mouseOverProcessed = False
# Avoid to broadcast a key release after a multitap event
if mevt.evt == TTkK.Release and self._lastMultiTap: return
@ -187,12 +194,11 @@ class TTk(TTkWidget):
# Mouse Events forwarded straight to the Focus widget:
# - Drag
# - Move
# - Release
focusWidget = TTkHelper.getFocus()
if ( focusWidget is not None and
mevt.evt != TTkK.Press and
mevt.key != TTkK.Wheel and
( mevt.evt == TTkK.Drag or
mevt.evt == TTkK.Release ) and
not TTkHelper.isDnD() ) :
x,y = TTkHelper.absPos(focusWidget)
nmevt = mevt.clone(pos=(mevt.x-x, mevt.y-y))

3
TermTk/TTkGui/__init__.py

@ -2,4 +2,5 @@ from .drag import TTkDrag, TTkDropEvent
from .textwrap1 import TTkTextWrap
from .textcursor import TTkTextCursor
from .textdocument import TTkTextDocument
from .clipboard import TTkClipboard
from .clipboard import TTkClipboard
from .tooltip import TTkToolTip

3
TermTk/TTkGui/drag.py

@ -28,9 +28,6 @@ from TermTk.TTkWidgets.widget import TTkWidget
class _TTkDragDisplayWidget(TTkWidget):
__slots__ = ('_pixmap')
def __init__(self, *args, **kwargs):
TTkWidget.__init__(self, *args, **kwargs)
self._name = kwargs.get('name' , '_TTkDragDisplayWidget' )
def setPixmap(self, pixmap, hotSpot):
w,h = pixmap.size()

78
TermTk/TTkGui/tooltip.py

@ -0,0 +1,78 @@
# 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 TermTk.TTkCore.helper import TTkHelper
from TermTk.TTkCore.log import TTkLog
from TermTk.TTkCore.canvas import TTkCanvas
from TermTk.TTkCore.color import TTkColor
from TermTk.TTkCore.timer import TTkTimer
from TermTk.TTkCore.helper import TTkHelper
from TermTk.TTkCore.string import TTkString
from TermTk.TTkWidgets.widget import TTkWidget
from TermTk.TTkCore.signal import pyTTkSlot
class _TTkToolTipDisplayWidget(TTkWidget):
__slots__ = ('_toolTip', '_x', '_y')
def __init__(self, *args, **kwargs):
TTkWidget.__init__(self, *args, **kwargs)
self._toolTip = kwargs.get('toolTip',TTkString()).split('\n')
w = 2+max([s.termWidth() for s in self._toolTip])
h = 2+len(self._toolTip)
self.resize(w,h)
def mouseEvent(self, evt): return False
def paintEvent(self):
w,h = self.size()
borderColor = TTkColor.fg("#888888")
canvas = self.getCanvas()
canvas.drawBox(pos=(0,0),size=(w,h), color=borderColor)
canvas.drawChar(pos=(0, 0), char='', color=borderColor)
canvas.drawChar(pos=(w-1,0), char='', color=borderColor)
canvas.drawChar(pos=(w-1,h-1),char='', color=borderColor)
canvas.drawChar(pos=(0, h-1),char='', color=borderColor)
for i,s in enumerate(self._toolTip,1):
canvas.drawTTkString(pos=(1,i), text=s)
class TTkToolTip():
toolTipTimer = TTkTimer()
toolTip = TTkString()
@pyTTkSlot()
@staticmethod
def _toolTipShow():
# TTkLog.debug(f"TT:{TTkToolTip.toolTip}")
TTkHelper.toolTipShow(_TTkToolTipDisplayWidget(toolTip=TTkToolTip.toolTip))
@staticmethod
def trigger(toolTip):
# TTkToolTip.toolTipTimer.stop()
TTkToolTip.toolTip = toolTip
TTkToolTip.toolTipTimer.start(1)
@staticmethod
def reset():
TTkToolTip.toolTipTimer.stop()
TTkToolTip.toolTipTimer.timeout.connect(TTkToolTip._toolTipShow)
TTkHelper.toolTipTrigger = TTkToolTip.trigger
TTkHelper.toolTipReset = TTkToolTip.reset

5
TermTk/TTkTheme/theme.py

@ -133,6 +133,11 @@ class TTkTheme():
buttonBorderColorFocus = TTkColor.fg("#ffff00") + TTkColor.BOLD
'''Default to **TTkColor.fg("#ffff00") + **:class:`~TermTk.TTkCore.color.TTkColor.BOLD`'''
buttonTextColorHover = TTkColor.fg("#dddd88")+TTkColor.bg("#000050")+TTkColor.BOLD
'''Default to **TTkColor.fg("#dddd88")+TTkColor.bg("#000066")+** :class:`~TermTk.TTkCore.color.TTkColor.BOLD`'''
buttonBorderColorHover = TTkColor.fg("#ffffcc") + TTkColor.BOLD
'''Default to **TTkColor.fg("#ffff88") + **:class:`~TermTk.TTkCore.color.TTkColor.BOLD`'''
buttonTextColorDisabled = textColorDisabled
'''Default to :class:`textColorDisabled`'''
buttonBorderColorDisabled= borderColorDisabled

24
TermTk/TTkWidgets/button.py

@ -23,9 +23,10 @@
# SOFTWARE.
from TermTk.TTkCore.cfg import TTkCfg
from TermTk.TTkCore.constant import TTkK
from TermTk.TTkCore.string import TTkString
from TermTk.TTkCore.signal import pyTTkSignal
from TermTk.TTkWidgets.widget import *
from TermTk.TTkWidgets.widget import TTkWidget
class TTkButton(TTkWidget):
''' TTkButton:
@ -216,6 +217,16 @@ class TTkButton(TTkWidget):
return True
return False
def enterEvent(self, evt) -> bool:
self.update()
def leaveEvent(self, evt) -> bool:
self.update()
def mouseMoveEvent(self, evt) -> bool:
self.update()
return super().mouseMoveEvent(evt)
def paintEvent(self):
if not self.isEnabled():
borderColor = self._borderColorDisabled
@ -235,15 +246,20 @@ class TTkButton(TTkWidget):
grid = TTkCfg.theme.buttonBoxGridUnchecked
borderColor = TTkCfg.theme.buttonBorderColorUnchecked
textColor = TTkCfg.theme.buttonTextColorUnchecked
if self.hasFocus():
borderColor = self._borderColorFocus
else:
grid = TTkCfg.theme.buttonBoxGrid
borderColor = self._borderColor
if self.hasFocus():
textColor = self._textColorFocus
borderColor = self._borderColorFocus
elif self.isEntered():
textColor = TTkCfg.theme.buttonTextColorHover
borderColor = TTkCfg.theme.buttonBorderColorHover
else:
textColor = self._textColor
if self.hasFocus():
borderColor = self._borderColorFocus
borderColor = self._borderColor
text = self._text
w = self.width()-2
h = self.height()

66
TermTk/TTkWidgets/widget.py

@ -26,6 +26,7 @@ from TermTk.TTkCore.cfg import TTkCfg, TTkGlbl
from TermTk.TTkCore.constant import TTkK
from TermTk.TTkCore.log import TTkLog
from TermTk.TTkCore.helper import TTkHelper
from TermTk.TTkCore.string import TTkString
from TermTk.TTkCore.canvas import TTkCanvas
from TermTk.TTkCore.signal import pyTTkSignal, pyTTkSlot
from TermTk.TTkTemplates.lookandfeel import TTkLookAndFeel
@ -80,6 +81,9 @@ class TTkWidget(TMouseEvents,TKeyEvents, TDragEvents):
:param int minHeight: the minHeight of the widget, defaults to 0
:param [int,int] minSize: the minSize [width,height] of the widget, optional
:param toolTip: This property holds the widget's tooltip
:type toolTip: :class:`~TermTk.TTkCore.string.TTkString`
:param lookAndFeel: the style helper to be used for any customization
:type lookAndFeel: :class:`~TermTk.TTkTemplates.lookandfeel.TTkTTkLookAndFeel`
@ -100,6 +104,7 @@ class TTkWidget(TMouseEvents,TKeyEvents, TDragEvents):
'_pendingMouseRelease',
'_enabled',
'_lookAndFeel',
'_toolTip',
#Signals
'focusChanged')
@ -138,6 +143,8 @@ class TTkWidget(TMouseEvents,TKeyEvents, TDragEvents):
self._visible = kwargs.get('visible', True)
self._enabled = kwargs.get('enabled', True)
self._toolTip = TTkString(kwargs.get('toolTip',''))
self._focus = False
self._focus_policy = TTkK.NoFocus
@ -349,30 +356,30 @@ class TTkWidget(TMouseEvents,TKeyEvents, TDragEvents):
if item.layoutItemType == TTkK.WidgetItem and not item.isEmpty():
widget = item.widget()
if not widget._visible: continue
wevt = None
mouseEvent = False
if isinstance(evt, TTkMouseEvent):
mouseEvent = True
wx,wy,ww,wh = widget.geometry()
# Skip the mouse event if outside this widget
if wx <= x < wx+ww and wy <= y < wy+wh:
wevt = evt.clone(pos=(x-wx, y-wy))
if mouseEvent:
if wevt is not None:
if widget.mouseEvent(wevt):
return True
continue
wx,wy,ww,wh = widget.geometry()
# Skip the mouse event if outside this widget
if not (wx <= x < wx+ww and wy <= y < wy+wh): continue
wevt = evt.clone(pos=(x-wx, y-wy))
if widget.mouseEvent(wevt):
return True
elif item.layoutItemType == TTkK.LayoutItem:
levt = evt.clone(pos=(x, y))
if TTkWidget._mouseEventLayoutHandle(levt, item):
return True
return False
_mouseOver = None
_mouseOverTmp = None
_mouseOverProcessed = False
def mouseEvent(self, evt):
''' .. caution:: Don't touch this! '''
if not self._enabled: return True
# Saving self in this global variable
# So that after the "_mouseEventLayoutHandle"
# this tmp value will hold the last widget below the mouse
TTkWidget._mouseOverTmp = self
# Mouse Drag has priority because it
# should be handled by the focused widget and
# not pushed to the unfocused childs
@ -386,7 +393,7 @@ class TTkWidget(TMouseEvents,TKeyEvents, TDragEvents):
return True
# If there is an overlay and it is modal,
# return False if this widget id not part of any
# return False if this widget is not part of any
# of the widgets above the modal
if not TTkHelper.checkModalOverlay(self):
return False
@ -410,10 +417,27 @@ class TTkWidget(TMouseEvents,TKeyEvents, TDragEvents):
return True
return ret
# handle own events
# handle Enter/Leave Events
# _mouseOverTmp hold the top widget under the mouse
# if different than self it means that it is a child
if evt.evt == TTkK.Move:
if not TTkWidget._mouseOverProcessed:
if TTkWidget._mouseOver != TTkWidget._mouseOverTmp == self:
if TTkWidget._mouseOver:
# TTkLog.debug(f"Leave: {TTkWidget._mouseOver._name}")
TTkWidget._mouseOver.leaveEvent(evt)
TTkWidget._mouseOver = self
# TTkLog.debug(f"Enter: {TTkWidget._mouseOver._name}")
TTkHelper.toolTipClose()
if self._toolTip and self._toolTip != '':
TTkHelper.toolTipTrigger(self._toolTip)
# TTkHelper.triggerToolTip(self._name)
TTkWidget._mouseOver.enterEvent(evt)
TTkWidget._mouseOverProcessed = True
if self.mouseMoveEvent(evt):
return True
else:
TTkHelper.toolTipClose()
if evt.evt == TTkK.Release:
self._pendingMouseRelease = False
@ -649,6 +673,9 @@ class TTkWidget(TMouseEvents,TKeyEvents, TDragEvents):
def focusInEvent(self): pass
def focusOutEvent(self): pass
def isEntered(self):
return self._mouseOver == self
def isEnabled(self):
return self._enabled
@ -672,3 +699,10 @@ class TTkWidget(TMouseEvents,TKeyEvents, TDragEvents):
laf = TTkLookAndFeel()
self._lookAndFeel = laf
self._lookAndFeel.modified.connect(self.update)
def toolTip(self):
return self._toolTip
def setToolTip(self, toolTip):
self._toolTip = toolTip

4
demo/demo.py

@ -255,12 +255,14 @@ def main():
parser = argparse.ArgumentParser()
parser.add_argument('-f', help='Full Screen (default)', action='store_true')
parser.add_argument('-w', help='Windowed', action='store_true')
parser.add_argument('-t', help='Track Mouse', action='store_true')
args = parser.parse_args()
windowed = args.w
mouseTrack = args.t
ttk.TTkLog.use_default_file_logging()
root = ttk.TTk(title="pyTermTk Demo")
root = ttk.TTk(title="pyTermTk Demo", mouseTrack=mouseTrack)
if windowed:
winTabbed1 = ttk.TTkWindow(parent=root,pos=(0,0), size=(120,40), title="pyTermTk Showcase", border=True, layout=ttk.TTkGridLayout(), flags=ttk.TTkK.NONE)
border = True

4
docs/MDNotes/TODO.md

@ -55,6 +55,10 @@
## Signal/Slots
- [x] Implement Signal/Slots
## WeakRef
- [ ] TTkTimer._timers
- [ ] Signals/Slots
## Logs
- [x] Log Class
- [ ] Run Logger on a separate thread (push string to a queue)

2
tests/pytest/mock_term.py

@ -79,7 +79,7 @@ class Mock_TTkTerm():
@staticmethod
def exit(): pass
@staticmethod
def init(title,sigmask): pass
def init(title,sigmask,mouse,directMouse): pass
@staticmethod
def getTerminalSize():
return 250,70

10
tests/sandbox/Makefile

@ -39,4 +39,14 @@ buildSandbox: www
find ../../tmp/TermTk/ -name "*.py" | sed 's,.*tmp/,,' | sort | xargs tar cvzf bin/TermTk.tgz -C ../../tmp
find ../../tutorial -name "*.py" | sort | xargs tar cvzf bin/tutorial.tgz
find ../../demo/paint.py ../../demo/ttkode.py ../../demo/demo.py ../../demo/showcase/*.* | sort | xargs tar cvzf bin/demo.tgz
buildTestSandbox: www
rm -rf bin
mkdir -p bin
$( cd ../../ ; tools/prepareBuild.sh release ; )
find ../../TermTk/ -name "*.py" | sort | xargs tar cvzf bin/TermTk.tgz
find ../../tutorial -name "*.py" | sort | xargs tar cvzf bin/tutorial.tgz
find ../../demo/paint.py ../../demo/ttkode.py ../../demo/demo.py ../../demo/showcase/*.* | sort | xargs tar cvzf bin/demo.tgz

133
tests/sandbox/sandbox.html

@ -144,8 +144,12 @@
return [term.cols, term.rows]
},
setTimeout: function(t, i) {
// console.log("TIME",i,t)
setTimeout(() => ttk_timer(i), t)
// console.log("TIME (Start)",i,t)
return setTimeout(() => ttk_timer(i), t)
},
stopTimeout: function(t) {
// console.log("TIME (Stop)",t)
clearTimeout(t)
},
clearTimeout: function(){
let highestTimeoutId = setTimeout(";");
@ -179,6 +183,59 @@
term.write('Tutorials - Loaded\n\r')
/* Sidebar
Fetch all the files in the pyodide.FS
And push them in the sidebar
*/
let getAllFiles = function(p){
let ls = pyodide.FS.readdir(p)
let ret = []
for(let i=0 ; i<ls.length; i++){
let n = p+"/"+ls[i]
if(ls[i]=='.' || ls[i]=='..' || n==(pyodide.FS.currentPath+"/TermTk")) continue;
if( pyodide.FS.isDir(pyodide.FS.lstat(n).mode)){
ret.push({id:n, text:ls[i], expanded: true, nodes:getAllFiles(n)})
}else{
if(n.endsWith('.py')){
ret.push({id:n, text:ls[i]})
}
}
}
return ret
}
let files = getAllFiles(pyodide.FS.currentPath)
new w2sidebar({
box: '#sidebar',
name: 'sidebar',
nodes: files })
w2ui.sidebar.on('click', function (event) {
console.log('Last Event: ' + event.type + ' Target: ' + event.target);
loadFile(event.target)
});
var loadFile = function(f){
let content = pyodide.FS.readFile(f, {encoding:'utf8'})
setCode(content)
document.getElementById("codeUri").value = f
}
/* check the "fileUri" field in the address
and load it if defined */
const queryString = window.location.search;
console.log(queryString);
const urlParams = new URLSearchParams(queryString);
fileUri = urlParams.get("fileUri")
if (fileUri != null){
pyodide.FS.writeFile(pyodide.FS.currentPath+"/test_file.py", await (await fetch(fileUri)).text());
loadFile(pyodide.FS.currentPath+"/test_file.py")
}else{
loadFile(pyodide.FS.currentPath+"/demo/demo.py")
}
//loadFile("/home/pyodide/tutorial/calculator/calculator.005.py")
w2ui.sidebar.select(pyodide.FS.currentPath+"/demo/demo.py")
term.write('Starting Demo...\n\r')
namespace = pyodide.globals.get("dict")();
@ -196,9 +253,7 @@
if ttk.TTkHelper._rootWidget:
ttk.TTkHelper._rootWidget._win_resize_cb(w,h)
ttk.TTkHelper.rePaintAll()
# TODO: I need to get rid of this:
ttk.TTkTerm.push(ttk.TTkTerm.ALT_SCREEN + ttk.TTkTerm.CLEAR + ttk.TTkTerm.Cursor.HIDE + ttk.TTkTerm.escTitle(ttk.TTkTerm.title))
ttk.TTkTerm.push(ttk.TTkTerm.Mouse.ON)
ttk.TTkTerm.cont()
def ttk_timer(tid):
ttk.TTkTimer.triggerTimerId(tid)
@ -221,6 +276,10 @@
ttk.TTkHelper._shortcut = []
ttk.TTkLog._messageHandler = [message_handler]
def ttk_init():
ttk.TTkToolTip.toolTipTimer = ttk.TTkTimer()
ttk.TTkToolTip.toolTipTimer.timeout.connect(ttk.TTkToolTip._toolTipShow)
def message_handler(mode, context, message):
msgType = "DEBUG"
if mode == ttk.TTkLog.InfoMsg: msgType = "[INFO]"
@ -239,6 +298,7 @@
let ttk_timer = namespace.get("ttk_timer");
let ttk_resize = namespace.get("ttk_resize");
let ttk_clean = namespace.get("ttk_clean");
let ttk_init = namespace.get("ttk_init");
term.onResize( (obj) => {
term.reset()
@ -253,59 +313,6 @@
`,{ globals: namespace }
);
/* Sidebar
Fetch all the files in the pyodide.FS
And push them in the sidebar
*/
let getAllFiles = function(p){
let ls = pyodide.FS.readdir(p)
let ret = []
for(let i=0 ; i<ls.length; i++){
let n = p+"/"+ls[i]
if(ls[i]=='.' || ls[i]=='..' || n==(pyodide.FS.currentPath+"/TermTk")) continue;
if( pyodide.FS.isDir(pyodide.FS.lstat(n).mode)){
ret.push({id:n, text:ls[i], expanded: true, nodes:getAllFiles(n)})
}else{
if(n.endsWith('.py')){
ret.push({id:n, text:ls[i]})
}
}
}
return ret
}
let files = getAllFiles(pyodide.FS.currentPath)
new w2sidebar({
box: '#sidebar',
name: 'sidebar',
nodes: files })
w2ui.sidebar.on('click', function (event) {
console.log('Last Event: ' + event.type + ' Target: ' + event.target);
loadFile(event.target)
});
var loadFile = function(f){
let content = pyodide.FS.readFile(f, {encoding:'utf8'})
setCode(content)
document.getElementById("codeUri").value = f
}
/* check the "fileUri" field in the address
and load it if defined */
const queryString = window.location.search;
console.log(queryString);
const urlParams = new URLSearchParams(queryString);
fileUri = urlParams.get("fileUri")
if (fileUri != null){
pyodide.FS.writeFile(pyodide.FS.currentPath+"/test_file.py", await (await fetch(fileUri)).text());
loadFile(pyodide.FS.currentPath+"/test_file.py")
}else{
loadFile(pyodide.FS.currentPath+"/demo/demo.py")
}
//loadFile("/home/pyodide/tutorial/calculator/calculator.005.py")
w2ui.sidebar.select(pyodide.FS.currentPath+"/demo/demo.py")
run = function(){
ttk_clean()
console.log("Run App")
@ -317,22 +324,16 @@
__file__='`+filename+`'
os.chdir('`+pwd+`')`,{ globals: namespace })
ttk_init()
let content = getCode()
pyodide.runPython(content,{ globals: namespace });
// TODO: I need to get rid of this:
pyodide.runPython(`
# TODO: I need to get rid of this:
ttk.TTkTerm.push(ttk.TTkTerm.ALT_SCREEN + ttk.TTkTerm.CLEAR + ttk.TTkTerm.Cursor.HIDE + ttk.TTkTerm.escTitle(ttk.TTkTerm.title))
ttk.TTkTerm.push(ttk.TTkTerm.Mouse.ON)
`,{ globals: namespace }
);
ttk_log(filename + " - LOADED")
};
run()
}
main()
console.log(pyodide)
</script>
</body>

0
tests/test.generic.001.py

0
tests/test.generic.002.py

0
tests/test.generic.003.py

0
tests/test.generic.004.footprint.py

0
tests/test.generic.006.weakref.01.py

0
tests/test.generic.006.weakref.02.py

144
tests/test.generic.007.timer.py

@ -0,0 +1,144 @@
#!/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.
import sys, os
import threading
sys.path.append(os.path.join(sys.path[0],'..'))
import TermTk as ttk
root = ttk.TTk(title="pyTermTk Timer Test")
class TimerV2():
__slots__ = ('timeout', '_timer')
def __init__(self):
# Define Signals
self.timeout = ttk.pyTTkSignal()
self._timer = None
def quit(self):
if self._timer:
self._timer.cancel()
@ttk.pyTTkSlot(float)
def start(self, sec=0.0):
if self._timer:
self._timer.cancel()
self._timer = threading.Timer(sec, self.timeout.emit)
self._timer.start()
@ttk.pyTTkSlot()
def stop(self):
if self._timer:
self._timer.cancel()
class TimerV3(threading.Thread):
__slots__ = ('timeout', '_timer', '_quit', '_delay')
def __init__(self):
self.timeout = ttk.pyTTkSignal()
self._delay = 0
self._quit = threading.Event()
self._start = threading.Event()
self._timer = threading.Event()
super().__init__()
super().start()
def quit(self):
self._quit.set()
self._timer.set()
self._start.set()
def run(self):
while not self._quit.is_set():
# ttk.TTkLog.info(f"t3-2 _start.wait")
self._start.wait()
self._start.clear()
# ttk.TTkLog.info(f"t3-3 _timer.wait {self._delay=}")
if not self._timer.wait(self._delay):
# ttk.TTkLog.info(f"t3-5 (EMIT)")
self.timeout.emit()
#ttk.TTkLog.info(f"t3-6")
self._quit.set()
#ttk.TTkLog.info(f"t3-7")
# def run(self):
# self.finished.wait(self.interval)
# if not self.finished.is_set():
# self.function(*self.args, **self.kwargs)
# self.finished.set()
@ttk.pyTTkSlot(float)
def start(self, sec=0.0):
self._delay = sec
self._timer.set()
self._timer.clear()
self._start.set()
@ttk.pyTTkSlot()
def stop(self):
self._timer.set()
sb = ttk.TTkSpinBox(parent=root, pos=(0,1), size=(10,1), value=2)
bStart = ttk.TTkButton(parent=root, text="Start", pos=(0,2), size=(10,3), border=True)
bStop = ttk.TTkButton(parent=root, text="Stop", pos=(0,5), size=(10,3), border=True)
# ttk.TTkButton(parent=root, text="Button 2", pos=(0,2), size=(10,3), toolTip="TT Button 2", border=True)
# ttk.TTkButton(parent=root, text="Button 2", pos=(0,2), size=(10,3), toolTip="TT Button 2", border=True)
w1 = ttk.TTkWindow(parent=root, title="LOG", pos=(0,10), size=(90,20), layout=ttk.TTkGridLayout(), toolTip="TT Log Window\n With\nLogDump")
ttk.TTkLogViewer(parent=w1)
t2 = TimerV2()
t2.timeout.connect(lambda : ttk.TTkLog.debug(f"timeout (t2)"))
t3 = TimerV3()
t3.timeout.connect(lambda : ttk.TTkLog.debug(f"timeout (t3)"))
t3_loop = TimerV3()
@ttk.pyTTkSlot()
def _loop():
ttk.TTkLog.debug(f"timeout (t3) LOOP")
t3_loop.start(1)
t3_loop.timeout.connect(_loop)
t3_loop.start(1)
@ttk.pyTTkSlot()
def _start():
v = sb.value()
ttk.TTkLog.debug(f"start : {v=}")
t2.start(v)
t3.start(v)
@ttk.pyTTkSlot()
def _stop():
t2.stop()
t3.stop()
bStart.clicked.connect(_start)
bStop.clicked.connect(_stop)
root.mainloop()

46
tests/test.ui.025.toolTip.py

@ -0,0 +1,46 @@
#!/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.
import sys, os, argparse, math, random
sys.path.append(os.path.join(sys.path[0],'..'))
import TermTk as ttk
root = ttk.TTk(title="pyTermTk Demo", mouseTrack=True)
ttk.TTkLabel( parent=root, text="Label 1", pos=(0,0), size=(10,1), toolTip="TT Label 1")
ttk.TTkButton(parent=root, text="Button 1", pos=(0,1), size=(10,1), toolTip="TT Button 1")
ttk.TTkButton(parent=root, text="Button 2", pos=(0,2), size=(10,3), toolTip="TT Button 2", border=True)
ttk.TTkButton(parent=root, text="Button 3", pos=(0,5), size=(20,3), toolTip="TT Button 3\n\nNewline", border=True)
ttk.TTkButton(parent=root, text="Button 3", pos=(21,0), size=(20,10), border=True,
toolTip=
ttk.TTkString(color=ttk.TTkColor.fg("#ff0000") ,text=" L😎rem ipsum\n")+
ttk.TTkString(color=ttk.TTkColor.fg("#00ff00") ,text="dolor sit amet,\n ⌚ ❤ 💙 🙋'\nYepp!!!"))
w1 = ttk.TTkWindow(parent=root, title="LOG", pos=(0,10), size=(90,20), layout=ttk.TTkGridLayout(), toolTip="TT Log Window\n With\nLogDump")
ttk.TTkLogViewer(parent=w1)
w2 = ttk.TTkWindow(parent=root, title="KeyLogger", pos=(0,30), size=(60,7), layout=ttk.TTkGridLayout())
ttk.TTkKeyPressView(parent=w2, toolTip="This is a\nKey Logger Widget")
root.mainloop()
Loading…
Cancel
Save