Browse Source

Added transparent canvas support and basic layout support in ttkDesigner

pull/99/head
Eugenio Parodi 3 years ago
parent
commit
bd432c5cc6
  1. 39
      TermTk/TTkCore/canvas.py
  2. 5
      TermTk/TTkWidgets/image.py
  3. 4
      docs/designer/images/internals.001.svg
  4. 114
      tests/timeit/13.canvas.alpha.py
  5. 3
      ttkDesigner/app/__init__.py
  6. 26
      ttkDesigner/app/superobj/__init__.py
  7. 108
      ttkDesigner/app/superobj/supercontrol.py
  8. 146
      ttkDesigner/app/superobj/superlayout.py
  9. 201
      ttkDesigner/app/superobj/superwidget.py
  10. 22
      ttkDesigner/app/treeinspector.py
  11. 10
      ttkDesigner/app/widgetbox.py
  12. 270
      ttkDesigner/app/windoweditor.py

39
TermTk/TTkCore/canvas.py

@ -41,10 +41,11 @@ class TTkCanvas:
'_theme',
'_data', '_colors',
'_bufferedData', '_bufferedColors',
'_visible', '_doubleBuffer')
'_visible', '_transparent', '_doubleBuffer')
def __init__(self, *args, **kwargs):
self._widget = kwargs.get('widget', None)
self._visible = True
self._transparent = False
self._doubleBuffer = False
self._width = 0
self._height = 0
@ -56,6 +57,12 @@ class TTkCanvas:
# self.resize(self._width, self._height)
# TTkLog.debug((self._width, self._height))
def transparent(self):
return self._transparent
def setTransparent(self, tr):
self._transparent = tr
def getWidget(self): return self._widget
def enableDoubleBuffer(self):
@ -92,8 +99,12 @@ class TTkCanvas:
def clean(self):
if not self._visible: return
w,h = self._width, self._height
baseData = [' ']*w
baseColors = [TTkColor.RST]*w
if self._transparent:
baseData = [None]*w
baseColors = baseData
else:
baseData = [' ']*w
baseColors = [TTkColor.RST]*w
self._data = [baseData.copy() for _ in range(h)]
self._colors = [baseColors.copy() for _ in range(h)]
@ -635,18 +646,30 @@ class TTkCanvas:
hslice = min(h if y+h < by+bh else by+bh-y,canvas._height)
a, b = x+xoffset, x+wslice
for iy in range(yoffset,hslice):
self._data[y+iy][a:b] = canvas._data[iy][xoffset:wslice]
self._colors[y+iy][a:b] = canvas._colors[iy][xoffset:wslice]
if canvas._transparent:
for iy in range(yoffset,hslice):
if None in canvas._data[iy][xoffset:wslice]:
self._data[y+iy][a:b] = [cca if cca else ccb for cca,ccb in zip(canvas._data[iy][xoffset:wslice],self._data[y+iy][a:b])]
else:
self._data[y+iy][a:b] = canvas._data[iy][xoffset:wslice]
if None in canvas._colors[iy][xoffset:wslice]:
self._colors[y+iy][a:b] = [cca if cca else ccb for cca,ccb in zip(canvas._colors[iy][xoffset:wslice],self._colors[y+iy][a:b])]
else:
self._colors[y+iy][a:b] = canvas._colors[iy][xoffset:wslice]
else:
for iy in range(yoffset,hslice):
self._data[y+iy][a:b] = canvas._data[iy][xoffset:wslice]
self._colors[y+iy][a:b] = canvas._colors[iy][xoffset:wslice]
# Check the full wide chars on the edge of the two canvasses
if ((0 <= a < cw) and self._data[y+iy][a]==''):
self._data[y+iy][a] = TTkCfg.theme.unicodeWideOverflowCh[0]
self._colors[y+iy][a] = TTkCfg.theme.unicodeWideOverflowColor
if ((0 < b <= cw) and TTkString._isWideCharData(self._data[y+iy][b-1])):
if ((0 < b <= cw) and self._data[y+iy][b-1] and TTkString._isWideCharData(self._data[y+iy][b-1])):
self._data[y+iy][b-1] = TTkCfg.theme.unicodeWideOverflowCh[1]
self._colors[y+iy][b-1] = TTkCfg.theme.unicodeWideOverflowColor
if ((0 < a <= cw) and TTkString._isWideCharData(self._data[y+iy][a-1])):
if ((0 < a <= cw) and self._data[y+iy][a-1] and TTkString._isWideCharData(self._data[y+iy][a-1])):
self._data[y+iy][a-1] = TTkCfg.theme.unicodeWideOverflowCh[1]
self._colors[y+iy][a-1] = TTkCfg.theme.unicodeWideOverflowColor
if ((0 <= b < cw) and self._data[y+iy][b]==''):

5
TermTk/TTkWidgets/image.py

@ -106,10 +106,7 @@ class TTkImage(TTkWidget):
for row in self._data:
for i,pixel in enumerate(row):
h,s,l = TTkColor.rgb2hsl(pixel)
h += deg
#TTkLog.debug(f"{h=}")
if h >= 360: h-=360
row[i] = TTkColor.hsl2rgb((h,s,l))
row[i] = TTkColor.hsl2rgb(((h+deg)%360,s,l))
def paintEvent(self):
img = self._data

4
docs/designer/images/internals.001.svg

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 17 KiB

114
tests/timeit/13.canvas.alpha.py

@ -0,0 +1,114 @@
#!/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 timeit
import random
sys.path.append(os.path.join(sys.path[0],'../..'))
import TermTk as ttk
w=0x1000
h=0x1000
c1 = [[0x1234]*w for _ in range(h)]
c2 = [[0x4321,None]*(w//2) for _ in range(h//2)]+[[0x5678]*w for _ in range(h//2)]
c3 = [[0x5678]*((w)-1)+[None] for _ in range(h)]
def paintCanvasNew1():
d1,d2 = [c.copy() for c in c1], [c.copy() for c in c2]
a,b = 0x10,w-0x10
for iy in range(0x10,h-0x10):
d1[iy][a:b] = [x if x else y for x,y in zip(d2[iy][a:b],d1[iy][a:b])]
def paintCanvasNew2():
d1,d2 = [c.copy() for c in c1], [c.copy() for c in c2]
a,b = 0x10,w-0x10
for iy in range(0x10,h-0x10):
if None in d2[iy]:
d1[iy][a:b] = [x if x else y for x,y in zip(d2[iy][a:b],d1[iy][a:b])]
else:
d1[iy][a:b] = d2[iy][a:b]
def paintCanvasNew3():
d1,d2 = [c.copy() for c in c1], [c.copy() for c in c1]
a,b = 0x10,w-0x10
for iy in range(0x10,h-0x10):
if None in d2[iy]:
d1[iy][a:b] = [x if x else y for x,y in zip(d2[iy][a:b],d1[iy][a:b])]
else:
d1[iy][a:b] = d2[iy][a:b]
def paintCanvasNew4():
d1,d2 = [c.copy() for c in c1], [c.copy() for c in c3]
a,b = 0x10,w-0x10
for iy in range(0x10,h-0x10):
if None in d2[iy]:
d1[iy][a:b] = [x if x else y for x,y in zip(d2[iy][a:b],d1[iy][a:b])]
else:
d1[iy][a:b] = d2[iy][a:b]
def paintCanvasOld():
d1,d2 = [c.copy() for c in c1], [c.copy() for c in c2]
a,b = 0x10,w-0x10
for iy in range(0x10,h-0x10):
d1[iy][a:b] = d2[iy][a:b]
def _re(s):
paintCanvasOld()
def _old(s):
paintCanvasNew1()
def test1(v): return paintCanvasOld()
def test2(v): return paintCanvasNew1()
def test3(v): return paintCanvasNew2()
def test4(v): return paintCanvasNew3()
def test5(v): return paintCanvasNew4()
def test6(v): return _old(v)
def test7(v): return _re(v)
def test8(v): return _old(v)
loop=10
a=1
result = timeit.timeit('test1(a)', globals=globals(), number=loop)
print(f"1a s {result / loop:.10f} - {result / loop} {test1(a)}")
result = timeit.timeit('test2(a)', globals=globals(), number=loop)
print(f"2a {result / loop:.10f} - {result / loop} {test2(a)}")
result = timeit.timeit('test3(a)', globals=globals(), number=loop)
print(f"3b s {result / loop:.10f} - {result / loop} {test3(a)}")
result = timeit.timeit('test4(a)', globals=globals(), number=loop)
print(f"4b {result / loop:.10f} - {result / loop} {test4(a)}")
result = timeit.timeit('test5(a)', globals=globals(), number=loop)
print(f"5c s {result / loop:.10f} - {result / loop} {test5(a)}")
result = timeit.timeit('test6(a)', globals=globals(), number=loop)
print(f"6c {result / loop:.10f} - {result / loop} {test6(a)}")
result = timeit.timeit('test7(a)', globals=globals(), number=loop)
print(f"7d s {result / loop:.10f} - {result / loop} {test7(a)}")
result = timeit.timeit('test8(a)', globals=globals(), number=loop)
print(f"8d {result / loop:.10f} - {result / loop} {test8(a)}")

3
ttkDesigner/app/__init__.py

@ -27,5 +27,6 @@ from .main import main
from .designer import TTkDesigner
from .treeinspector import TreeInspector
from .widgetbox import DragDesignItem, WidgetBox
from .windoweditor import WindowEditor, SuperWidget
from .windoweditor import WindowEditor
from .propertyeditor import PropertyEditor
from .superobj import *

26
ttkDesigner/app/superobj/__init__.py

@ -0,0 +1,26 @@
# 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 .supercontrol import SuperControlWidget
from .superwidget import SuperWidget
from .superlayout import SuperLayout

108
ttkDesigner/app/superobj/supercontrol.py

@ -0,0 +1,108 @@
# 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 TermTk as ttk
class SuperControlWidget(ttk.TTkResizableFrame):
def __init__(self, wid, *args, **kwargs):
self._wid = wid
self._widPos = self._wid.pos()
self._draggable = False
self._mouseDelta = (0,0)
kwargs['maxSize'] = [v+2 for v in wid.maximumSize()]
kwargs['minSize'] = [v+2 for v in wid.minimumSize()]
kwargs['size'] = [v+2 for v in wid.size() ]
super().__init__(*args, **kwargs)
self.getCanvas().setTransparent(True)
def _alignWidToPos(self, pos):
x,y = self.pos()
ox,oy = pos
wx,wy = self._wid.pos()
self._wid.move(wx+x-ox, wy+y-oy)
self.update()
return super().move(x,y)
def resizeEvent(self, w, h):
self._wid.resize(w-2,h-2)
self._wid._canvas.updateSize()
return super().resizeEvent(w, h)
def mouseReleaseEvent(self, evt) -> bool:
self._draggable = False
return super().mouseReleaseEvent(evt)
def mousePressEvent(self, evt):
self._draggable = False
self._mouseDelta = (evt.x, evt.y)
w,h = self.size()
x,y = evt.x, evt.y
if x==0 or x==w-1 or y==0 or y==h-1:
return super().mousePressEvent(evt)
self._draggable = True
return True
def mouseDragEvent(self, evt):
bkPos = self.pos()
if self._draggable:
x,y = self.pos()
dx = evt.x-self._mouseDelta[0]
dy = evt.y-self._mouseDelta[1]
self.move(x+dx, y+dy)
self._alignWidToPos(bkPos)
return True
ret = super().mouseDragEvent(evt)
self._alignWidToPos(bkPos)
return ret
def keyEvent(self, evt):
if evt.type == ttk.TTkK.SpecialKey:
if evt.key in (ttk.TTkK.Key_Delete, ttk.TTkK.Key_Backspace) :
self._wid.close()
self.close()
self._wid.weModified.emit()
return True
bkPos = self.pos()
x,y = 0,0
if evt.key == ttk.TTkK.Key_Up: y=-1
elif evt.key == ttk.TTkK.Key_Down: y=1
elif evt.key == ttk.TTkK.Key_Left: x=-1
elif evt.key == ttk.TTkK.Key_Right: x=1
if any((x,y)):
self.move(bkPos[0]+x, bkPos[1]+y)
self._alignWidToPos(bkPos)
return True
def paintEvent(self):
w,h = self.size()
self._wid.paintEvent()
self._wid.paintChildCanvas()
self._canvas.paintCanvas(
self._wid.getCanvas(),
( 1, 1, w, h), # geometry
( 0, 0, w, h), # slice
( 0, 0, w, h)) # bound
self._canvas.drawBox(pos=(0,0),size=self.size())
self._canvas.drawChar(pos=( 0, 0), char='')
self._canvas.drawChar(pos=(w-1, 0), char='')
self._canvas.drawChar(pos=( 0, h-1), char='')
self._canvas.drawChar(pos=(w-1, h-1), char='')

146
ttkDesigner/app/superobj/superlayout.py

@ -0,0 +1,146 @@
# 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 random import randint
import TermTk as ttk
import ttkDesigner.app.superobj as so
class SuperLayout(ttk.TTkWidget):
def __init__(self, lay, weModified, widgetSelected, *args, **kwargs):
self.weModified = weModified
self.widgetSelected = widgetSelected
self._lay = lay
self._superRootWidget = kwargs.get('superRootWidget',False)
self._selectable = kwargs.get('selectable', False)
# kwargs['pos'] = (x,y) = lay.pos()
x,y = kwargs.get('pos',lay.pos())
kwargs['size'] = (w,h) = lay.size()
self._lay.setGeometry(x,y,w,h)
super().__init__(*args, **kwargs)
self.getCanvas().setTransparent(True)
# r,g,b = randint(0,0xFF),randint(0,0xFF),randint(0,0xFF)
# self._layoutColor = ttk.TTkColor.bg(f"#{r:02X}{g:02X}{b:02X}")
self.setFocusPolicy(ttk.TTkK.ClickFocus)
so.SuperWidget.toggleHighlightLayout.connect(self._toggleHighlightLayout)
@ttk.pyTTkSlot(bool)
def _toggleHighlightLayout(self, state):
so.SuperWidget._showLayout = state
self.update()
def dumpDict(self):
ret = {}
return ret
def updateAll(self):
self.resize(*(self._lay.size()))
self.move(*(self._lay.pos()))
# self.setPadding(*(self._lay.getPadding()))
self.setMaximumSize(*(self._lay.maximumSize()))
self.setMinimumSize(*(self._lay.minimumSize()))
self.update()
def mousePressEvent(self, evt) -> bool:
return self._selectable and not self._superRootWidget
def pushSuperControlWidget(self):
if self._superRootWidget or not self._selectable: return False
scw = so.SuperControlWidget(self)
ttk.TTkHelper.removeOverlay()
ttk.TTkHelper.overlay(self, scw, -1,-1, forceBoundaries=False)
def mouseReleaseEvent(self, evt) -> bool:
if self._superRootWidget or not self._selectable: return False
self.pushSuperControlWidget()
# self.widgetSelected.emit(self._lay,self)
return True
def mouseDragEvent(self, evt) -> bool:
if self._superRootWidget or not self._selectable: return False
drag = ttk.TTkDrag()
data = self
canvas = self.getCanvas()
canvas.clean()
ttk.TTkWidget._paintChildCanvas(canvas, self._lay, self._lay.geometry(), self._lay.offset())
drag.setHotSpot(evt.x, evt.y)
drag.setPixmap(canvas)
drag.setData(data)
drag.exec()
self.parentWidget().layout().removeWidget(self)
self.parentWidget().update()
return True
def dropEvent(self, evt) -> bool:
data = evt.data()
hsx,hsy = evt.hotSpot()
ttk.TTkLog.debug(f"Drop ({data.__class__.__name__}) -> pos={evt.pos()}")
if issubclass(type(data),ttk.TTkLayout):
self.layout().addWidget(sw := so.SuperLayout(lay=data, weModified=self.weModified, widgetSelected=self.widgetSelected, pos=(evt.x-hsx, evt.y-hsy), selectable=True))
self._lay.addItem(data)
elif issubclass(type(data), so.SuperLayout):
sw = data
self.layout().addWidget(sw)
data = data._lay
self._lay.addItem(data)
sw.show()
sw.move(evt.x-hsx, evt.y-hsy)
elif issubclass(type(data), so.SuperWidget):
sw = data
self.layout().addWidget(sw)
data = data._wid
sw.move(evt.x-hsx, evt.y-hsy)
sw.show()
self._lay.addWidget(data)
data.move(evt.x-hsx, evt.y-hsy)
elif issubclass(type(data),ttk.TTkWidget):
self.layout().addWidget(sw := so.SuperWidget(wid=data, weModified=self.weModified, widgetSelected=self.widgetSelected, pos=(evt.x-hsx, evt.y-hsy)))
self._lay.addWidget(data)
data.move(evt.x-hsx, evt.y-hsy)
else:
return False
self.update()
self.weModified.emit()
return True
def move(self, x: int, y: int):
w,h = self._lay.size()
self._lay.setGeometry(x,y,w,h)
# self.update()
return super().move(x, y)
def resizeEvent(self, w, h):
x,y = self._lay.pos()
self._lay.setGeometry(x,y,w,h)
return super().resizeEvent(w, h)
def paintEvent(self):
if self._selectable:
if so.SuperWidget._showLayout:
w,h = self.size()
self._canvas.drawBox(pos=(0,0),size=(w,h), color=ttk.TTkColor.fg('#88DD88', modifier=ttk.TTkColorGradient(increment=+1)))
else:
w,h = self.size()
self._canvas.drawBox(pos=(0,0),size=(w,h), color=ttk.TTkColor.fg('#223322', modifier=ttk.TTkColorGradient(increment=+1)))

201
ttkDesigner/app/superobj/superwidget.py

@ -0,0 +1,201 @@
# 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 random import randint
import TermTk as ttk
import ttkDesigner.app.superobj as so
class SuperWidget(ttk.TTkWidget):
def __init__(self, wid, weModified, widgetSelected, *args, **kwargs):
self.weModified = weModified
self.widgetSelected = widgetSelected
self._wid = wid
self._wid.move(*kwargs['pos'])
self._wid._canvas.show()
self._superLayout = so.SuperLayout(lay=self._wid.layout(), weModified=self.weModified, widgetSelected=self.widgetSelected,)
self._superRootWidget = kwargs.get('superRootWidget',False)
kwargs['layout'] = ttk.TTkGridLayout()
kwargs['layout'].addWidget(self._superLayout)
kwargs['maxSize'] = wid.maximumSize()
kwargs['minSize'] = wid.minimumSize()
kwargs['size'] = wid.size()
padt, padb, padl, padr = wid.getPadding()
kwargs['paddingTop'] = padt
kwargs['paddingBottom'] = padb
kwargs['paddingLeft'] = padl
kwargs['paddingRight'] = padr
super().__init__(*args, **kwargs)
#self.resize(*self._wid.size())
h,s,l = randint(0,359),100,80
r,g,b = ttk.TTkColor.hsl2rgb(((h+5)%360,s,l))
self._layoutColor = ttk.TTkColor.bg(f"#{r:02X}{g:02X}{b:02X}", modifier=ttk.TTkColorGradient(increment=+2))
r,g,b = ttk.TTkColor.hsl2rgb(((h+5)%360,s,l))
self._layoutPadColor = ttk.TTkColor.bg(f"#{r:02X}{g:02X}{b:02X}", modifier=ttk.TTkColorGradient(increment=-2))
self.setFocusPolicy(ttk.TTkK.ClickFocus)
SuperWidget.toggleHighlightLayout.connect(self._toggleHighlightLayout)
_showLayout = False
toggleHighlightLayout = ttk.pyTTkSignal(bool)
@ttk.pyTTkSlot(bool)
def _toggleHighlightLayout(self, state):
SuperWidget._showLayout = state
self.update()
def dumpDict(self):
wid = self._wid
def _dumpPrimitive(val):
return val
def _dumpTTkString(val):
return val.toAnsi()
def _dumpTTkColor(val):
return str(val)
def _dumpTTkLayout(val):
return type(val).__name__
def _dumpFlag(val):
return val
def _dumpList(val, propType):
ret = []
for i,t in enumerate(propType):
if t['type'] in (int,str,float,bool):
ret.append(_dumpPrimitive(val[i]))
elif type(t['type']) in (list,tuple):
ttk.TTkLog.warn("Feature not Implemented yet")
elif t['type'] is ttk.TTkLayout:
ret.append(_dumpTTkLayout(val[i]))
elif t['type'] in (ttk.TTkString,'singleLineTTkString'):
ret.append(_dumpTTkString(val[i]))
elif t['type'] is ttk.TTkColor:
ret.append(_dumpTTkColor(val[i]))
elif t['type'] in ('singleFlag','multiFlag'):
ret.append(_dumpFlag(val[i]))
else:
ttk.TTkLog.warn("Type not Recognised")
return ret
children = []
for w in self.layout().children():
children.append(w.widget().dumpDict())
params = {}
for cc in reversed(type(wid).__mro__):
# if hasattr(cc,'_ttkProperties'):
if issubclass(cc, ttk.TTkWidget):
ccName = cc.__name__
if ccName in ttk.TTkUiProperties:
for p in ttk.TTkUiProperties[ccName]:
prop = ttk.TTkUiProperties[ccName][p]
propType = prop['get']['type']
propCb = prop['get']['cb']
# ttk.TTkLog.debug(ccName)
if propType in (int,str,float,bool):
params |= {p: _dumpPrimitive(propCb(wid))}
elif type(propType) in (list,tuple):
params |= {p: _dumpList(propCb(wid), propType)}
elif propType is ttk.TTkLayout:
params |= {p: _dumpTTkLayout(propCb(wid))}
elif propType in (ttk.TTkString,'singleLineTTkString'):
params |= {p: _dumpTTkString(propCb(wid))}
elif propType is ttk.TTkColor:
params |= {p: _dumpTTkColor(propCb(wid))}
elif propType in ('singleflag','multiflags'):
params |= {p: _dumpFlag(propCb(wid))}
else:
ttk.TTkLog.warn("Type not Recognised")
ret = {
'class' : wid.__class__.__name__,
'params' : params,
'children': children
}
return ret
def updateAll(self):
self.resize(*(self._wid.size()))
self.move(*(self._wid.pos()))
self.setPadding(*(self._wid.getPadding()))
self.setMaximumSize(*(self._wid.maximumSize()))
self.setMinimumSize(*(self._wid.minimumSize()))
self.update()
def mousePressEvent(self, evt) -> bool:
return True
def pushSuperControlWidget(self):
if self._superRootWidget: return False
scw = so.SuperControlWidget(self)
ttk.TTkHelper.removeOverlay()
ttk.TTkHelper.overlay(self, scw, -1,-1, forceBoundaries=False)
def mouseReleaseEvent(self, evt) -> bool:
self.pushSuperControlWidget()
self.widgetSelected.emit(self._wid,self)
return True
def mouseDragEvent(self, evt) -> bool:
if self._superRootWidget: return False
drag = ttk.TTkDrag()
data = self
data.paintChildCanvas()
drag.setHotSpot(evt.x, evt.y)
drag.setPixmap(data.getCanvas())
drag.setData(data)
drag.exec()
self.parentWidget().layout().removeWidget(self)
self.parentWidget().update()
return True
def dropEvent(self, evt) -> bool:
padt, padb, padl, padr = self._wid.getPadding()
# evt = evt.copy()
evt.x-=padl
evt.y-=padt
return self._superLayout.dropEvent(evt)
def move(self, x: int, y: int):
self._wid.move(x,y)
self.update()
return super().move(x, y)
def resizeEvent(self, w, h):
self._wid.resize(w,h)
self._wid._canvas.updateSize()
return super().resizeEvent(w, h)
def paintEvent(self):
w,h = self.size()
if SuperWidget._showLayout:
t,b,l,r = self._wid.getPadding()
for y in range(h):
self._canvas.drawText(pos=(0,y),text='',width=w,color=self._layoutColor)
for y in range(t,h-b):
self._canvas.drawText(pos=(l,y),text='',width=w-r-l,color=self._layoutPadColor)
# self._canvas.fill(color=self._layoutColor)
# self._canvas.fill(pos=(l,t), size=(w-r-l,h-b-t), color=self._layoutPadColor)
else:
self._wid.paintEvent()
self._canvas.paintCanvas(
self._wid.getCanvas(),
( 0, 0, w, h), # geometry
( 0, 0, w, h), # slice
( 0, 0, w, h)) # bound

22
ttkDesigner/app/treeinspector.py

@ -102,15 +102,19 @@ class TreeInspector(ttk.TTkGridLayout):
tomWidget=widget,
tomSuperWidget=superWidget,
expanded=expanded)
for c in superWidget.layout().children():
top.addChild(TreeInspector._getTomTreeItem(c,widSelected))
for c in widget.rootLayout().children():
if c == widget.layout(): continue
if c.layoutItemType == ttk.TTkK.LayoutItem:
top.addChild(tc:=_TTkTomTreeWidgetItem(["layout (Other)", c.__class__.__name__, ""]))
for cc in c.children():
tc.addChild(TreeInspector._getTomTreeItem(cc,widSelected))
if issubclass(type(superWidget), SuperWidget):
for c in superWidget._superLayout.layout().children():
top.addChild(TreeInspector._getTomTreeItem(c,widSelected))
else:
for c in superWidget.layout().children():
top.addChild(TreeInspector._getTomTreeItem(c,widSelected))
# for c in widget.rootLayout().children():
# if c == widget.layout(): continue
# if c.layoutItemType == ttk.TTkK.LayoutItem:
# top.addChild(tc:=_TTkTomTreeWidgetItem(["layout (Other)", c.__class__.__name__, ""]))
# for cc in c.children():
# tc.addChild(TreeInspector._getTomTreeItem(cc,widSelected))
return top
if layoutItem.layoutItemType == ttk.TTkK.LayoutItem:

10
ttkDesigner/app/widgetbox.py

@ -27,7 +27,7 @@ from .about import *
dWidgets = {
'Layouts':{
"Layout" : { "class":ttk.TTkLayout , "params":{}},
"Layout" : { "class":ttk.TTkLayout , "params":{'size':(30,10)}},
"H Box Layout" : { "class":ttk.TTkHBoxLayout, "params":{}},
"V Box Layout" : { "class":ttk.TTkVBoxLayout, "params":{}},
"Grid Layout" : { "class":ttk.TTkGridLayout, "params":{}},
@ -87,7 +87,13 @@ class DragDesignItem(ttk.TTkWidget):
name = f"{name}-{DragDesignItem._objNames[name]}"
drag = ttk.TTkDrag()
data = wc['class'](**(wc['params']|{'name':name}))
drag.setPixmap(data)
if issubclass(wc['class'], ttk.TTkWidget):
drag.setPixmap(data)
else:
w,h = wc['params']['size']
pm = ttk.TTkCanvas(width=w, height=h)
pm.drawBox(pos=(0,0),size=(w,h), color=ttk.TTkColor.fg('#888888'))
drag.setPixmap(pm)
drag.setData(data)
drag.exec()
return True

270
ttkDesigner/app/windoweditor.py

@ -23,273 +23,11 @@
# Yaml is not included by default
# import yaml
import json
from random import randint
import TermTk as ttk
class SuperControlWidget(ttk.TTkResizableFrame):
def __init__(self, wid, *args, **kwargs):
self._wid = wid
self._widPos = self._wid.pos()
self._draggable = False
self._mouseDelta = (0,0)
kwargs['maxSize'] = [v+2 for v in wid.maximumSize()]
kwargs['minSize'] = [v+2 for v in wid.minimumSize()]
kwargs['size'] = [v+2 for v in wid.size() ]
super().__init__(*args, **kwargs)
def _alignWidToPos(self, pos):
x,y = self.pos()
ox,oy = pos
wx,wy = self._wid.pos()
self._wid.move(wx+x-ox, wy+y-oy)
self.update()
return super().move(x,y)
def resizeEvent(self, w, h):
self._wid.resize(w-2,h-2)
self._wid._canvas.updateSize()
return super().resizeEvent(w, h)
def mouseReleaseEvent(self, evt) -> bool:
self._draggable = False
return super().mouseReleaseEvent(evt)
def mousePressEvent(self, evt):
self._draggable = False
self._mouseDelta = (evt.x, evt.y)
w,h = self.size()
x,y = evt.x, evt.y
if x==0 or x==w-1 or y==0 or y==h-1:
return super().mousePressEvent(evt)
self._draggable = True
return True
def mouseDragEvent(self, evt):
bkPos = self.pos()
if self._draggable:
x,y = self.pos()
dx = evt.x-self._mouseDelta[0]
dy = evt.y-self._mouseDelta[1]
self.move(x+dx, y+dy)
self._alignWidToPos(bkPos)
return True
ret = super().mouseDragEvent(evt)
self._alignWidToPos(bkPos)
return ret
def keyEvent(self, evt):
if evt.type == ttk.TTkK.SpecialKey:
if evt.key in (ttk.TTkK.Key_Delete, ttk.TTkK.Key_Backspace) :
self._wid.close()
self.close()
self._wid.weModified.emit()
return True
bkPos = self.pos()
x,y = 0,0
if evt.key == ttk.TTkK.Key_Up: y=-1
elif evt.key == ttk.TTkK.Key_Down: y=1
elif evt.key == ttk.TTkK.Key_Left: x=-1
elif evt.key == ttk.TTkK.Key_Right: x=1
if any((x,y)):
self.move(bkPos[0]+x, bkPos[1]+y)
self._alignWidToPos(bkPos)
return True
def paintEvent(self):
w,h = self.size()
self._wid.paintEvent()
self._wid.paintChildCanvas()
self._canvas.paintCanvas(
self._wid.getCanvas(),
( 1, 1, w, h), # geometry
( 0, 0, w, h), # slice
( 0, 0, w, h)) # bound
self._canvas.drawBox(pos=(0,0),size=self.size())
self._canvas.drawChar(pos=( 0, 0), char='')
self._canvas.drawChar(pos=(w-1, 0), char='')
self._canvas.drawChar(pos=( 0, h-1), char='')
self._canvas.drawChar(pos=(w-1, h-1), char='')
class SuperWidget(ttk.TTkWidget):
def __init__(self, wid, *args, **kwargs):
self._wid = wid
self._wid.move(*kwargs['pos'])
self._wid._canvas.show()
self._superRootWidget = kwargs.get('superRootWidget',False)
kwargs['maxSize'] = wid.maximumSize()
kwargs['minSize'] = wid.minimumSize()
kwargs['size'] = wid.size()
padt, padb, padl, padr = wid.getPadding()
kwargs['paddingTop'] = padt
kwargs['paddingBottom'] = padb
kwargs['paddingLeft'] = padl
kwargs['paddingRight'] = padr
super().__init__(*args, **kwargs)
#self.resize(*self._wid.size())
r,g,b = randint(0,0xFF),randint(0,0xFF),randint(0,0xFF)
self._layoutColor = ttk.TTkColor.bg(f"#{r*9//10:02X}{g*9//10:02X}{b*9//10:02X}")
self._layoutPadColor = ttk.TTkColor.bg(f"#{r:02X}{g:02X}{b:02X}")
self.setFocusPolicy(ttk.TTkK.ClickFocus)
SuperWidget.toggleHighlightLayout.connect(self._toggleHighlightLayout)
from .superobj import SuperWidget
_showLayout = False
toggleHighlightLayout = ttk.pyTTkSignal(bool)
@ttk.pyTTkSlot(bool)
def _toggleHighlightLayout(self, state):
SuperWidget._showLayout = state
self.update()
def dumpDict(self):
wid = self._wid
def _dumpPrimitive(val):
return val
def _dumpTTkString(val):
return val.toAnsi()
def _dumpTTkColor(val):
return str(val)
def _dumpTTkLayout(val):
return type(val).__name__
def _dumpFlag(val):
return val
def _dumpList(val, propType):
ret = []
for i,t in enumerate(propType):
if t['type'] in (int,str,float,bool):
ret.append(_dumpPrimitive(val[i]))
elif type(t['type']) in (list,tuple):
ttk.TTkLog.warn("Feature not Implemented yet")
elif t['type'] is ttk.TTkLayout:
ret.append(_dumpTTkLayout(val[i]))
elif t['type'] in (ttk.TTkString,'singleLineTTkString'):
ret.append(_dumpTTkString(val[i]))
elif t['type'] is ttk.TTkColor:
ret.append(_dumpTTkColor(val[i]))
elif t['type'] in ('singleFlag','multiFlag'):
ret.append(_dumpFlag(val[i]))
else:
ttk.TTkLog.warn("Type not Recognised")
return ret
children = []
for w in self.layout().children():
children.append(w.widget().dumpDict())
params = {}
for cc in reversed(type(wid).__mro__):
# if hasattr(cc,'_ttkProperties'):
if issubclass(cc, ttk.TTkWidget):
ccName = cc.__name__
if ccName in ttk.TTkUiProperties:
for p in ttk.TTkUiProperties[ccName]:
prop = ttk.TTkUiProperties[ccName][p]
propType = prop['get']['type']
propCb = prop['get']['cb']
# ttk.TTkLog.debug(ccName)
if propType in (int,str,float,bool):
params |= {p: _dumpPrimitive(propCb(wid))}
elif type(propType) in (list,tuple):
params |= {p: _dumpList(propCb(wid), propType)}
elif propType is ttk.TTkLayout:
params |= {p: _dumpTTkLayout(propCb(wid))}
elif propType in (ttk.TTkString,'singleLineTTkString'):
params |= {p: _dumpTTkString(propCb(wid))}
elif propType is ttk.TTkColor:
params |= {p: _dumpTTkColor(propCb(wid))}
elif propType in ('singleflag','multiflags'):
params |= {p: _dumpFlag(propCb(wid))}
else:
ttk.TTkLog.warn("Type not Recognised")
ret = {
'class' : wid.__class__.__name__,
'params' : params,
'children': children
}
return ret
def updateAll(self):
self.resize(*(self._wid.size()))
self.move(*(self._wid.pos()))
self.setPadding(*(self._wid.getPadding()))
self.setMaximumSize(*(self._wid.maximumSize()))
self.setMinimumSize(*(self._wid.minimumSize()))
self.update()
def mousePressEvent(self, evt) -> bool:
return True
def pushSuperControlWidget(self):
if self._superRootWidget: return False
scw = SuperControlWidget(self)
ttk.TTkHelper.removeOverlay()
ttk.TTkHelper.overlay(self, scw, -1,-1, forceBoundaries=False)
def mouseReleaseEvent(self, evt) -> bool:
self.pushSuperControlWidget()
self.widgetSelected.emit(self._wid,self)
return True
def mouseDragEvent(self, evt) -> bool:
if self._superRootWidget: return False
drag = ttk.TTkDrag()
data = self
data.paintChildCanvas()
drag.setHotSpot(evt.x, evt.y)
drag.setPixmap(data.getCanvas())
drag.setData(data)
drag.exec()
self.parentWidget().layout().removeWidget(self)
self.parentWidget().update()
return True
def dropEvent(self, evt) -> bool:
data = evt.data()
hsx,hsy = evt.hotSpot()
padt, padb, padl, padr = self._wid.getPadding()
ttk.TTkLog.debug(f"Drop ({data.__class__.__name__}) -> pos={evt.pos()}")
if issubclass(type(data),ttk.TTkWidget):
if issubclass(type(data), SuperWidget):
sw = data
self.layout().addWidget(sw)
data = data._wid
sw.move(evt.x-hsx-padl, evt.y-hsy-padt)
sw.show()
else:
self.layout().addWidget(sw := SuperWidget(wid=data, pos=(evt.x-hsx-padl, evt.y-hsy-padt)))
self._wid.addWidget(data)
data.move(evt.x-hsx-padl, evt.y-hsy-padt)
sw.weModified = self.weModified
sw.widgetSelected = self.widgetSelected
self.update()
self.weModified.emit()
return True
return False
def move(self, x: int, y: int):
self._wid.move(x,y)
self.update()
return super().move(x, y)
def resizeEvent(self, w, h):
self._wid.resize(w,h)
self._wid._canvas.updateSize()
return super().resizeEvent(w, h)
import TermTk as ttk
def paintEvent(self):
w,h = self.size()
if SuperWidget._showLayout:
t,b,l,r = self._wid.getPadding()
w,h = self.size()
self._canvas.fill(color=self._layoutColor)
self._canvas.fill(pos=(l,t), size=(w-r-l,h-b-t), color=self._layoutPadColor)
else:
self._wid.paintEvent()
self._canvas.paintCanvas(
self._wid.getCanvas(),
( 0, 0, w, h), # geometry
( 0, 0, w, h), # slice
( 0, 0, w, h)) # bound
class WindowEditorView(ttk.TTkAbstractScrollView):
def __init__(self, *args, **kwargs):
@ -297,9 +35,7 @@ class WindowEditorView(ttk.TTkAbstractScrollView):
self.widgetSelected = ttk.pyTTkSignal(ttk.TTkWidget, ttk.TTkWidget)
super().__init__(*args, **kwargs)
self.viewChanged.connect(self._viewChangedHandler)
self._ttk = SuperWidget(wid=ttk.TTkWidget(name = 'TTk'), pos=(4,2), size=(self.width()-8,self.height()-4), superRootWidget=True)
self._ttk.weModified = self.weModified
self._ttk.widgetSelected = self.widgetSelected
self._ttk = SuperWidget(wid=ttk.TTkWidget(name = 'TTk'), weModified=self.weModified, widgetSelected=self.widgetSelected, pos=(4,2), size=(self.width()-8,self.height()-4), superRootWidget=True)
self.layout().addWidget(self._ttk)
def getTTk(self):

Loading…
Cancel
Save