26 changed files with 2834 additions and 408 deletions
@ -0,0 +1,159 @@ |
|||||||
|
# MIT License |
||||||
|
# |
||||||
|
# Copyright (c) 2024 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. |
||||||
|
|
||||||
|
__all__ = ['ttkCrossOpen', 'ttkCrossSave', 'ttkCrossSaveAs', 'TTkEncoding', 'ttkConnectDragOpen', 'ttkEmitDragOpen', 'ttkEmitFileOpen'] |
||||||
|
|
||||||
|
import os |
||||||
|
import importlib.util |
||||||
|
import json |
||||||
|
|
||||||
|
from TermTk import pyTTkSlot, pyTTkSignal |
||||||
|
from TermTk import TTkLog |
||||||
|
from TermTk import TTkMessageBox, TTkFileDialogPicker, TTkHelper, TTkString, TTkK, TTkColor |
||||||
|
|
||||||
|
ttkCrossOpen = None |
||||||
|
ttkCrossSave = None |
||||||
|
ttkCrossSaveAs = None |
||||||
|
ttkEmitDragOpen = None |
||||||
|
ttkEmitFileOpen = None |
||||||
|
ttkConnectDragOpen = None |
||||||
|
|
||||||
|
class TTkEncoding(str): |
||||||
|
TEXT = "text" |
||||||
|
TEXT_PLAIN = "text/plain" |
||||||
|
TEXT_PLAIN_UTF8 = "text/plain;charset=utf-8" |
||||||
|
APPLICATION = 'application' |
||||||
|
APPLICATION_JSON = 'application/json' |
||||||
|
IMAGE = 'image' |
||||||
|
IMAGE_PNG = 'image/png' |
||||||
|
IMAGE_SVG = 'image/svg+xml' |
||||||
|
IMAGE_JPG = 'image/jpeg' |
||||||
|
|
||||||
|
if importlib.util.find_spec('pyodideProxy'): |
||||||
|
TTkLog.info("Using 'pyodideProxy' as clipboard manager") |
||||||
|
import pyodideProxy |
||||||
|
ttkDragOpen = {} |
||||||
|
ttkFileOpen = pyTTkSignal(dict) |
||||||
|
|
||||||
|
def _open(path, encoding, filter, cb=None): |
||||||
|
if not cb: return |
||||||
|
ttkFileOpen.connect(cb) |
||||||
|
pyodideProxy.openFile(encoding) |
||||||
|
|
||||||
|
def _save(filePath, content, encoding, filter=None): |
||||||
|
pyodideProxy.saveFile(os.path.basename(filePath), content, encoding) |
||||||
|
|
||||||
|
def _connectDragOpen(encoding, cb): |
||||||
|
if not encoding in ttkDragOpen: |
||||||
|
ttkDragOpen[encoding] = pyTTkSignal(dict) |
||||||
|
return ttkDragOpen[encoding].connect(cb) |
||||||
|
|
||||||
|
def _emitDragOpen(encoding, data): |
||||||
|
for do in [ttkDragOpen[e] for e in ttkDragOpen if encoding.startswith(e)]: |
||||||
|
do.emit(data) |
||||||
|
|
||||||
|
def _emitFileOpen(encoding, data): |
||||||
|
ttkFileOpen.emit(data) |
||||||
|
ttkFileOpen.clear() |
||||||
|
|
||||||
|
ttkCrossOpen = _open |
||||||
|
ttkCrossSave = _save |
||||||
|
ttkCrossSaveAs = _save |
||||||
|
ttkEmitDragOpen = _emitDragOpen |
||||||
|
ttkEmitFileOpen = _emitFileOpen |
||||||
|
ttkConnectDragOpen = _connectDragOpen |
||||||
|
|
||||||
|
else: |
||||||
|
def _crossDecoder_text(fileName) : |
||||||
|
with open(fileName) as fp: |
||||||
|
return fp.read() |
||||||
|
def _crossDecoder_json(fileName) : |
||||||
|
with open(fileName) as fp: |
||||||
|
# return json.load(fp) |
||||||
|
return fp.read() |
||||||
|
def _crossDecoder_image(fileName): |
||||||
|
return None |
||||||
|
|
||||||
|
_crossDecoder = { |
||||||
|
TTkEncoding.TEXT : _crossDecoder_text , |
||||||
|
TTkEncoding.TEXT_PLAIN : _crossDecoder_text , |
||||||
|
TTkEncoding.TEXT_PLAIN_UTF8 : _crossDecoder_text , |
||||||
|
TTkEncoding.APPLICATION : _crossDecoder_json , |
||||||
|
TTkEncoding.APPLICATION_JSON : _crossDecoder_json , |
||||||
|
TTkEncoding.IMAGE : _crossDecoder_image , |
||||||
|
TTkEncoding.IMAGE_PNG : _crossDecoder_image , |
||||||
|
TTkEncoding.IMAGE_SVG : _crossDecoder_image , |
||||||
|
TTkEncoding.IMAGE_JPG : _crossDecoder_image , |
||||||
|
} |
||||||
|
|
||||||
|
def _open(path, encoding, filter, cb=None): |
||||||
|
if not cb: return |
||||||
|
def __openFile(fileName): |
||||||
|
_decoder = _crossDecoder.get(encoding,lambda _:None) |
||||||
|
content = _decoder(fileName) |
||||||
|
cb({'name':fileName, 'data':content}) |
||||||
|
filePicker = TTkFileDialogPicker(pos = (3,3), size=(100,30), caption="Open", path=path, fileMode=TTkK.FileMode.ExistingFile ,filter=filter) |
||||||
|
filePicker.pathPicked.connect(__openFile) |
||||||
|
TTkHelper.overlay(None, filePicker, 5, 5, True) |
||||||
|
|
||||||
|
def _save(filePath, content, encoding): |
||||||
|
TTkLog.info(f"Saving to: {filePath}") |
||||||
|
with open(filePath,'w') as fp: |
||||||
|
fp.write(content) |
||||||
|
|
||||||
|
def _saveAs(filePath, content, encoding, filter, cb=None): |
||||||
|
if not cb: return |
||||||
|
def _approveFile(fileName): |
||||||
|
if os.path.exists(fileName): |
||||||
|
@pyTTkSlot(TTkMessageBox.StandardButton) |
||||||
|
def _cb(btn): |
||||||
|
if btn == TTkMessageBox.StandardButton.Save: |
||||||
|
ttkCrossSave(fileName,content,encoding) |
||||||
|
elif btn == TTkMessageBox.StandardButton.Cancel: |
||||||
|
return |
||||||
|
if cb: |
||||||
|
cb() |
||||||
|
messageBox = TTkMessageBox( |
||||||
|
text= ( |
||||||
|
TTkString( f'A file named "{os.path.basename(fileName)}" already exists.\nDo you want to replace it?', TTkColor.BOLD) + |
||||||
|
TTkString( f'\n\nReplacing it will overwrite its contents.') ), |
||||||
|
icon=TTkMessageBox.Icon.Warning, |
||||||
|
standardButtons=TTkMessageBox.StandardButton.Discard|TTkMessageBox.StandardButton.Save|TTkMessageBox.StandardButton.Cancel) |
||||||
|
messageBox.buttonSelected.connect(_cb) |
||||||
|
TTkHelper.overlay(None, messageBox, 5, 5, True) |
||||||
|
else: |
||||||
|
ttkCrossSave(fileName,content,encoding) |
||||||
|
filePicker = TTkFileDialogPicker( |
||||||
|
size=(100,30), path=filePath, |
||||||
|
acceptMode=TTkK.AcceptMode.AcceptSave, |
||||||
|
caption="Save As...", |
||||||
|
fileMode=TTkK.FileMode.AnyFile , |
||||||
|
filter=filter) |
||||||
|
filePicker.pathPicked.connect(_approveFile) |
||||||
|
TTkHelper.overlay(None, filePicker, 5, 5, True) |
||||||
|
|
||||||
|
ttkCrossOpen = _open |
||||||
|
ttkCrossSave = _save |
||||||
|
ttkCrossSaveAs = _saveAs |
||||||
|
ttkEmitDragOpen = lambda a:None |
||||||
|
ttkEmitFileOpen = lambda a:None |
||||||
|
ttkConnectDragOpen = lambda a,b:None |
||||||
@ -1,11 +1,12 @@ |
|||||||
from .TTkCore import * |
from .TTkCore import * |
||||||
from .TTkTheme import * |
from .TTkTheme import * |
||||||
from .TTkGui import * |
from .TTkGui import * |
||||||
from .TTkWidgets import * |
from .TTkWidgets import * |
||||||
from .TTkTypes import * |
from .TTkTypes import * |
||||||
from .TTkLayouts import * |
from .TTkLayouts import * |
||||||
from .TTkTestWidgets import * |
from .TTkTestWidgets import * |
||||||
from .TTkAbstract import * |
from .TTkAbstract import * |
||||||
from .TTkUiTools import * |
from .TTkUiTools import * |
||||||
|
from .TTkCrossTools import * |
||||||
|
|
||||||
TTkCfg.theme = TTkTheme() |
TTkCfg.theme = TTkTheme() |
||||||
|
|||||||
@ -0,0 +1,2 @@ |
|||||||
|
https://dev.to/shubhamtiwari909/drag-and-drop-file-using-javascript-2h99 |
||||||
|
https://www.smashingmagazine.com/2018/01/drag-drop-file-uploader-vanilla-js/ |
||||||
@ -1,4 +1,8 @@ |
|||||||
from .maintemplate import * |
from .maintemplate import * |
||||||
from .paintarea import * |
from .paintarea import * |
||||||
from .textarea import * |
from .painttoolkit import * |
||||||
from .palette import * |
from .textarea import * |
||||||
|
from .palette import * |
||||||
|
from .layers import * |
||||||
|
from .canvaslayer import * |
||||||
|
from .about import * |
||||||
@ -0,0 +1,33 @@ |
|||||||
|
# MIT License |
||||||
|
# |
||||||
|
# Copyright (c) 2024 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. |
||||||
|
|
||||||
|
__all__ = ['About'] |
||||||
|
|
||||||
|
from TermTk.TTkCore.log import TTkLog |
||||||
|
from TermTk.TTkCore.color import TTkColor |
||||||
|
from TermTk.TTkCore.string import TTkString |
||||||
|
from TermTk import TTkAbout, TTkWindow |
||||||
|
# from .cfg import TTkDesignerCfg |
||||||
|
|
||||||
|
class About(TTkAbout): |
||||||
|
def paintEvent(self, canvas): |
||||||
|
super().paintEvent(canvas) |
||||||
@ -0,0 +1,374 @@ |
|||||||
|
# MIT License |
||||||
|
# |
||||||
|
# Copyright (c) 2024 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. |
||||||
|
|
||||||
|
__all__ = ['CanvasLayer'] |
||||||
|
|
||||||
|
import sys, os |
||||||
|
|
||||||
|
sys.path.append(os.path.join(sys.path[0],'../..')) |
||||||
|
import TermTk as ttk |
||||||
|
|
||||||
|
# Canvas Layer structure |
||||||
|
# The data may include more areas than the visible one |
||||||
|
# This is helpful in case of resize to not lose the drawn areas |
||||||
|
# |
||||||
|
# |---| OffsetX |
||||||
|
# x |
||||||
|
# ╭────────────────╮ - - |
||||||
|
# │ │ | OffsetY | |
||||||
|
# y │ ┌───────┐ │ \ - | Data |
||||||
|
# │ │Visible│ │ | h | |
||||||
|
# │ └───────┘ │ / | |
||||||
|
# │ │ | |
||||||
|
# └────────────────┘ - |
||||||
|
# \---w--/ |
||||||
|
|
||||||
|
class CanvasLayer(): |
||||||
|
class Tool(int): |
||||||
|
MOVE = 0x01 |
||||||
|
RESIZE = 0x02 |
||||||
|
BRUSH = 0x04 |
||||||
|
RECTFILL = 0x08 |
||||||
|
RECTEMPTY = 0x10 |
||||||
|
CLONE = 0x20 |
||||||
|
|
||||||
|
__slot__ = ('_pos','_name','_visible','_size','_data','_colors','_preview','_offset') |
||||||
|
def __init__(self) -> None: |
||||||
|
self._pos = (0,0) |
||||||
|
self._size = (0,0) |
||||||
|
self._offset = (0,0) |
||||||
|
self._name = "" |
||||||
|
self._visible = True |
||||||
|
self._preview = None |
||||||
|
self._data: list[list[str ]] = [] |
||||||
|
self._colors:list[list[ttk.TTkColor]] = [] |
||||||
|
|
||||||
|
def pos(self): |
||||||
|
return self._pos |
||||||
|
def size(self): |
||||||
|
return self._size |
||||||
|
|
||||||
|
def visible(self): |
||||||
|
return self._visible |
||||||
|
@ttk.pyTTkSlot(bool) |
||||||
|
def setVisible(self, visible): |
||||||
|
self._visible = visible |
||||||
|
|
||||||
|
def name(self): |
||||||
|
return self._name |
||||||
|
@ttk.pyTTkSlot(str) |
||||||
|
def setName(self, name): |
||||||
|
self._name = name |
||||||
|
|
||||||
|
def isOpaque(self,x,y): |
||||||
|
if not self._visible: return False |
||||||
|
ox,oy = self._offset |
||||||
|
w,h = self._size |
||||||
|
data = self._data |
||||||
|
colors = self._colors |
||||||
|
if 0<=x<w and 0<=y<h: |
||||||
|
return data[oy+y][ox+x] != ' ' or colors[oy+y][ox+x].background() |
||||||
|
return False |
||||||
|
|
||||||
|
def move(self,x,y): |
||||||
|
self._pos=(x,y) |
||||||
|
|
||||||
|
def resize(self,w,h): |
||||||
|
self._size = (w,h) |
||||||
|
self._data = (self._data + [[] for _ in range(h)])[:h] |
||||||
|
self._colors = (self._colors + [[] for _ in range(h)])[:h] |
||||||
|
for i in range(h): |
||||||
|
self._data[i] = (self._data[i] + [' ' for _ in range(w)])[:w] |
||||||
|
self._colors[i] = (self._colors[i] + [ttk.TTkColor.RST for _ in range(w)])[:w] |
||||||
|
|
||||||
|
def superResize(self,dx,dy,dw,dh): |
||||||
|
ox,oy = self._offset |
||||||
|
x,y = self.pos() |
||||||
|
w,h = self.size() |
||||||
|
daw = len(self._data[0]) |
||||||
|
dah = len(self._data) |
||||||
|
diffx = dx-x |
||||||
|
diffy = dy-y |
||||||
|
self._preview = None |
||||||
|
if ox < x-dx: # we need to resize and move ox |
||||||
|
_nw = x-dx-ox |
||||||
|
ox = x-dx |
||||||
|
self._data = [([' ']*_nw ) + _r for _r in self._data] |
||||||
|
self._colors = [([ttk.TTkColor.RST]*_nw) + _r for _r in self._colors] |
||||||
|
if dw+ox > daw: |
||||||
|
_nw = dw+ox-daw |
||||||
|
self._data = [_r + ([' ']*_nw ) for _r in self._data] |
||||||
|
self._colors = [_r + ([ttk.TTkColor.RST]*_nw) for _r in self._colors] |
||||||
|
daw = len(self._data[0]) |
||||||
|
if oy < y-dy: # we need to resize and move ox |
||||||
|
_nh = y-dy-oy |
||||||
|
oy = y-dy |
||||||
|
self._data = [[' ']*daw for _ in range(_nh)] + self._data |
||||||
|
self._colors = [[ttk.TTkColor.RST]*daw for _ in range(_nh)] + self._colors |
||||||
|
if dh+oy > dah: |
||||||
|
_nh = dh+oy-dah |
||||||
|
self._data = self._data + [[' ']*daw for _ in range(_nh)] |
||||||
|
self._colors = self._colors + [[ttk.TTkColor.RST]*daw for _ in range(_nh)] |
||||||
|
self._offset = (ox+diffx,oy+diffy) |
||||||
|
self._pos = (dx,dy) |
||||||
|
self._size = (dw,dh) |
||||||
|
|
||||||
|
|
||||||
|
def clean(self): |
||||||
|
w,h = self._size |
||||||
|
self._offset = (0,0) |
||||||
|
self._preview = None |
||||||
|
for i in range(h): |
||||||
|
self._data[i] = [' ']*w |
||||||
|
self._colors[i] = [ttk.TTkColor.RST]*w |
||||||
|
|
||||||
|
|
||||||
|
def exportLayer(self, full=False, palette=True, crop=True): |
||||||
|
# xa|----------| xb |
||||||
|
# px |-----------| = max(px,px+xa-ox) |
||||||
|
# Offset |------| pw |
||||||
|
# Data |----------------------------| |
||||||
|
# daw |
||||||
|
|
||||||
|
# Don't try this at home |
||||||
|
ox,oy = self._offset |
||||||
|
px,py = self.pos() |
||||||
|
pw,ph = self.size() |
||||||
|
|
||||||
|
if full: |
||||||
|
data = self._data |
||||||
|
colors = self._colors |
||||||
|
else: |
||||||
|
data = [row[ox:ox+pw] for row in self._data[ oy:oy+ph] ] |
||||||
|
colors = [row[ox:ox+pw] for row in self._colors[oy:oy+ph] ] |
||||||
|
ox=oy=0 |
||||||
|
|
||||||
|
daw = len(data[0]) |
||||||
|
dah = len(data) |
||||||
|
|
||||||
|
# get the bounding box |
||||||
|
if crop: |
||||||
|
xa,xb,ya,yb = daw,0,dah,0 |
||||||
|
for y,(drow,crow) in enumerate(zip(data,colors)): |
||||||
|
for x,(d,c) in enumerate(zip(drow,crow)): |
||||||
|
if d != ' ' or c.background(): |
||||||
|
xa = min(x,xa) |
||||||
|
xb = max(x,xb) |
||||||
|
ya = min(y,ya) |
||||||
|
yb = max(y,yb) |
||||||
|
if (xa,xb,ya,yb) == (daw,0,dah,0): |
||||||
|
xa=xb=ya=yb=0 |
||||||
|
else: |
||||||
|
xa,xb,ya,yb = 0,daw,0,dah |
||||||
|
|
||||||
|
# Visble Area intersecting the bounding box |
||||||
|
vxa,vya = max(px,px+xa-ox), max(py,py+ya-oy) |
||||||
|
vxb,vyb = min(px+pw,vxa+xb-xa),min(py+ph,vya+yb-ya) |
||||||
|
vw,vh = vxb-vxa+1, vyb-vya+1 |
||||||
|
|
||||||
|
outData = { |
||||||
|
'version':'1.1.0', |
||||||
|
'size':[vw,vh], |
||||||
|
'pos': (vxa,vya), |
||||||
|
'name':str(self.name()), |
||||||
|
'data':[], 'colors':[]} |
||||||
|
|
||||||
|
if palette: |
||||||
|
palette = outData['palette'] = [] |
||||||
|
for row in colors: |
||||||
|
for c in row: |
||||||
|
fg = f"{c.getHex(ttk.TTkK.Foreground)}" if c.foreground() else None |
||||||
|
bg = f"{c.getHex(ttk.TTkK.Background)}" if c.background() else None |
||||||
|
if (pc:=(fg,bg)) not in palette: |
||||||
|
palette.append(pc) |
||||||
|
|
||||||
|
if full: |
||||||
|
wslice = slice(xa,xb+1) |
||||||
|
hslice = slice(ya,yb+1) |
||||||
|
outData['offset'] = (max(0,ox-xa),max(0,oy-ya)) |
||||||
|
else: |
||||||
|
wslice = slice(ox+vxa-px,ox+vxa-px+vw) |
||||||
|
hslice = slice(oy+vya-py,oy+vya-py+vh) |
||||||
|
|
||||||
|
for row in data[hslice]: |
||||||
|
outData['data'].append(row[wslice]) |
||||||
|
for row in colors[hslice]: |
||||||
|
outData['colors'].append([]) |
||||||
|
for c in row[wslice]: |
||||||
|
fg = f"{c.getHex(ttk.TTkK.Foreground)}" if c.foreground() else None |
||||||
|
bg = f"{c.getHex(ttk.TTkK.Background)}" if c.background() else None |
||||||
|
if palette: |
||||||
|
outData['colors'][-1].append(palette.index((fg,bg))) |
||||||
|
else: |
||||||
|
outData['colors'][-1].append((fg,bg)) |
||||||
|
|
||||||
|
return outData |
||||||
|
|
||||||
|
def _import_v1_1_0(self, dd): |
||||||
|
self._import_v1_0_0(dd) |
||||||
|
self._offset = dd.get('offset',(0,0)) |
||||||
|
|
||||||
|
def _import_v1_0_0(self, dd): |
||||||
|
self._pos = dd['pos'] |
||||||
|
self._size = dd['size'] |
||||||
|
self._name = dd['name'] |
||||||
|
self._data = dd['data'] |
||||||
|
def _getColor(cd): |
||||||
|
fg,bg = cd |
||||||
|
if fg and bg: return ttk.TTkColor.fg(fg)+ttk.TTkColor.bg(bg) |
||||||
|
elif fg: return ttk.TTkColor.fg(fg) |
||||||
|
elif bg: return ttk.TTkColor.bg(bg) |
||||||
|
else: return ttk.TTkColor.RST |
||||||
|
if 'palette' in dd: |
||||||
|
palette = [_getColor(c) for c in dd['palette']] |
||||||
|
self._colors = [[palette[c] for c in row] for row in dd['colors']] |
||||||
|
else: |
||||||
|
self._colors = [[_getColor(c) for c in row] for row in dd['colors']] |
||||||
|
|
||||||
|
def _import_v0_0_0(self, dd): |
||||||
|
# Legacy old import |
||||||
|
w = len(dd['data'][0]) + 10 |
||||||
|
h = len(dd['data']) + 4 |
||||||
|
x,y=5,2 |
||||||
|
self.resize(w,h) |
||||||
|
self._pos = (0,0) |
||||||
|
for i,rd in enumerate(dd['data']): |
||||||
|
for ii,cd in enumerate(rd): |
||||||
|
self._data[i+y][ii+x] = cd |
||||||
|
for i,rd in enumerate(dd['colors']): |
||||||
|
for ii,cd in enumerate(rd): |
||||||
|
fg,bg = cd |
||||||
|
if fg and bg: |
||||||
|
self._colors[i+y][ii+x] = ttk.TTkColor.fg(fg)+ttk.TTkColor.bg(bg) |
||||||
|
elif fg: |
||||||
|
self._colors[i+y][ii+x] = ttk.TTkColor.fg(fg) |
||||||
|
elif bg: |
||||||
|
self._colors[i+y][ii+x] = ttk.TTkColor.bg(bg) |
||||||
|
else: |
||||||
|
self._colors[i+y][ii+x] = ttk.TTkColor.RST |
||||||
|
|
||||||
|
def importLayer(self, dd): |
||||||
|
self.clean() |
||||||
|
|
||||||
|
if 'version' in dd: |
||||||
|
ver = dd['version'] |
||||||
|
if ver == ('1.0.0'): |
||||||
|
self._import_v1_0_0(dd) |
||||||
|
elif ver == ('1.1.0'): |
||||||
|
self._import_v1_1_0(dd) |
||||||
|
else: |
||||||
|
self._import_v0_0_0(dd) |
||||||
|
|
||||||
|
def placeFill(self,geometry,tool,glyph:str,color:ttk.TTkColor,preview=False): |
||||||
|
ox,oy = self._offset |
||||||
|
w,h = self._size |
||||||
|
ax,ay,bx,by = geometry |
||||||
|
ax = max(0,min(w-1,ax)) |
||||||
|
ay = max(0,min(h-1,ay)) |
||||||
|
bx = max(0,min(w-1,bx)) |
||||||
|
by = max(0,min(h-1,by)) |
||||||
|
fax,fay = ox+min(ax,bx), oy+min(ay,by) |
||||||
|
fbx,fby = ox+max(ax,bx), oy+max(ay,by) |
||||||
|
|
||||||
|
color = color if glyph != ' ' else color.background() |
||||||
|
color = color if color else ttk.TTkColor.RST |
||||||
|
if preview: |
||||||
|
data = [_r.copy() for _r in self._data] |
||||||
|
colors = [_r.copy() for _r in self._colors] |
||||||
|
self._preview = {'data':data,'colors':colors} |
||||||
|
else: |
||||||
|
self._preview = None |
||||||
|
data = self._data |
||||||
|
colors = self._colors |
||||||
|
|
||||||
|
if tool == CanvasLayer.Tool.RECTFILL: |
||||||
|
for row in data[fay:fby+1]: |
||||||
|
row[fax:fbx+1] = [glyph]*(fbx-fax+1) |
||||||
|
for row in colors[fay:fby+1]: |
||||||
|
row[fax:fbx+1] = [color]*(fbx-fax+1) |
||||||
|
if tool == CanvasLayer.Tool.RECTEMPTY: |
||||||
|
data[fay][fax:fbx+1] = [glyph]*(fbx-fax+1) |
||||||
|
data[fby][fax:fbx+1] = [glyph]*(fbx-fax+1) |
||||||
|
colors[fay][fax:fbx+1] = [color]*(fbx-fax+1) |
||||||
|
colors[fby][fax:fbx+1] = [color]*(fbx-fax+1) |
||||||
|
for row in data[fay:fby]: |
||||||
|
row[fax]=row[fbx]=glyph |
||||||
|
for row in colors[fay:fby]: |
||||||
|
row[fax]=row[fbx]=color |
||||||
|
return True |
||||||
|
|
||||||
|
def placeGlyph(self,x,y,glyph:str,color:ttk.TTkColor,preview=False): |
||||||
|
ox,oy = self._offset |
||||||
|
w,h = self._size |
||||||
|
color = color if glyph != ' ' else color.background() |
||||||
|
color = color if color else ttk.TTkColor.RST |
||||||
|
if preview: |
||||||
|
data = [_r.copy() for _r in self._data] |
||||||
|
colors = [_r.copy() for _r in self._colors] |
||||||
|
self._preview = {'data':data,'colors':colors} |
||||||
|
else: |
||||||
|
self._preview = None |
||||||
|
data = self._data |
||||||
|
colors = self._colors |
||||||
|
if 0<=x<w and 0<=y<h: |
||||||
|
data[ oy+y][ox+x] = glyph |
||||||
|
colors[oy+y][ox+x] = color |
||||||
|
return True |
||||||
|
return False |
||||||
|
|
||||||
|
def drawInCanvas(self, pos, canvas:ttk.TTkCanvas): |
||||||
|
if not self._visible: return |
||||||
|
px,py = pos |
||||||
|
pw,ph = self._size |
||||||
|
cw,ch = canvas.size() |
||||||
|
if px+pw<0 or py+ph<0:return |
||||||
|
if px>=cw or py>=ch:return |
||||||
|
# Data Offset |
||||||
|
ox,oy = self._offset |
||||||
|
# x,y position in the Canvas |
||||||
|
cx = max(0,px) |
||||||
|
cy = max(0,py) |
||||||
|
# x,y position in the Layer |
||||||
|
lx,ly = (cx-px),(cy-py) |
||||||
|
# Area to be copyed |
||||||
|
dw = min(cw-cx,pw-lx) |
||||||
|
dh = min(ch-cy,ph-ly) |
||||||
|
|
||||||
|
if _p := self._preview: |
||||||
|
data = _p['data'] |
||||||
|
colors = _p['colors'] |
||||||
|
else: |
||||||
|
data = self._data |
||||||
|
colors = self._colors |
||||||
|
for y in range(cy,cy+dh): |
||||||
|
for x in range(cx,cx+dw): |
||||||
|
gl = data[ oy+y+ly-cy][ox+x+lx-cx] |
||||||
|
c = colors[oy+y+ly-cy][ox+x+lx-cx] |
||||||
|
if gl==' ' and c._bg: |
||||||
|
canvas._data[y][x] = gl |
||||||
|
canvas._colors[y][x] = c |
||||||
|
elif gl!=' ': |
||||||
|
canvas._data[y][x] = gl |
||||||
|
cc = canvas._colors[y][x] |
||||||
|
newC = c.copy() |
||||||
|
newC._bg = c._bg if c._bg else cc._bg |
||||||
|
canvas._colors[y][x] = newC |
||||||
@ -0,0 +1,324 @@ |
|||||||
|
# MIT License |
||||||
|
# |
||||||
|
# Copyright (c) 2024 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. |
||||||
|
|
||||||
|
__all__ = ['Layers','LayerData'] |
||||||
|
|
||||||
|
import sys, os |
||||||
|
|
||||||
|
sys.path.append(os.path.join(sys.path[0],'../..')) |
||||||
|
import TermTk as ttk |
||||||
|
|
||||||
|
class LayerData(): |
||||||
|
__slots__ = ('_name','_data', |
||||||
|
#signals |
||||||
|
'nameChanged','visibilityToggled') |
||||||
|
def __init__(self,name:ttk.TTkString=ttk.TTkString('New'),data=None) -> None: |
||||||
|
self._name:ttk.TTkString = ttk.TTkString(name) if type(name)==str else name |
||||||
|
self.visibilityToggled = ttk.pyTTkSignal(bool) |
||||||
|
self._data = data |
||||||
|
self.nameChanged = ttk.pyTTkSignal(str) |
||||||
|
def name(self): |
||||||
|
return self._name |
||||||
|
def setName(self,name): |
||||||
|
self.nameChanged.emit(name) |
||||||
|
self._name = name |
||||||
|
def data(self): |
||||||
|
return self._data |
||||||
|
def setData(self,data): |
||||||
|
self._data = data |
||||||
|
|
||||||
|
class _layerButton(ttk.TTkContainer): |
||||||
|
classStyle = { |
||||||
|
'default': {'color': ttk.TTkColor.fg("#dddd88")+ttk.TTkColor.bg("#000044"), |
||||||
|
'borderColor': ttk.TTkColor.fg('#CCDDDD'), |
||||||
|
'grid':1}, |
||||||
|
'disabled': {'color': ttk.TTkColor.fg('#888888'), |
||||||
|
'borderColor':ttk.TTkColor.fg('#888888'), |
||||||
|
'grid':0}, |
||||||
|
'hover': {'color': ttk.TTkColor.fg("#dddd88")+ttk.TTkColor.bg("#000050")+ttk.TTkColor.BOLD, |
||||||
|
'borderColor': ttk.TTkColor.fg("#FFFFCC")+ttk.TTkColor.BOLD, |
||||||
|
'grid':1}, |
||||||
|
'selected': {'color': ttk.TTkColor.fg("#dddd88")+ttk.TTkColor.bg("#004488"), |
||||||
|
'borderColor': ttk.TTkColor.fg("#FFFF00"), |
||||||
|
'grid':0}, |
||||||
|
'unchecked': {'color': ttk.TTkColor.fg("#dddd88")+ttk.TTkColor.bg("#000044"), |
||||||
|
'borderColor': ttk.TTkColor.RST, |
||||||
|
'grid':3}, |
||||||
|
'clicked': {'color': ttk.TTkColor.fg("#FFFFDD")+ttk.TTkColor.BOLD, |
||||||
|
'borderColor': ttk.TTkColor.fg("#DDDDDD")+ttk.TTkColor.BOLD, |
||||||
|
'grid':0}, |
||||||
|
'focus': {'color': ttk.TTkColor.fg("#dddd88")+ttk.TTkColor.bg("#000044")+ttk.TTkColor.BOLD, |
||||||
|
'borderColor': ttk.TTkColor.fg("#ffff00") + ttk.TTkColor.BOLD, |
||||||
|
'grid':1}, |
||||||
|
} |
||||||
|
|
||||||
|
__slots__ = ('_layerData','_first', '_isSelected', '_layerVisible', |
||||||
|
'_ledit', |
||||||
|
# signals |
||||||
|
'clicked', 'visibilityToggled', |
||||||
|
) |
||||||
|
def __init__(self, layer:LayerData, **kwargs): |
||||||
|
self.clicked = ttk.pyTTkSignal(_layerButton) |
||||||
|
self._layerData:LayerData = layer |
||||||
|
self._isSelected = False |
||||||
|
self._first = True |
||||||
|
self._layerVisible = True |
||||||
|
self.visibilityToggled = layer.visibilityToggled |
||||||
|
|
||||||
|
super().__init__(**kwargs|{'layout':ttk.TTkGridLayout()}) |
||||||
|
self.setPadding(1,1,7,2) |
||||||
|
self._ledit = ttk.TTkLineEdit(parent=self, text=layer.name(),visible=False) |
||||||
|
self._ledit.focusChanged.connect(self._ledit.setVisible) |
||||||
|
self._ledit.textEdited.connect(self._textEdited) |
||||||
|
# self.setFocusPolicy(ttk.TTkK.ClickFocus) |
||||||
|
|
||||||
|
@ttk.pyTTkSlot(str) |
||||||
|
def _textEdited(self, text): |
||||||
|
self._layerData.setName(text) |
||||||
|
|
||||||
|
def mousePressEvent(self, evt) -> bool: |
||||||
|
if evt.x <= 3: |
||||||
|
self._layerVisible = not self._layerVisible |
||||||
|
self.visibilityToggled.emit(self._layerVisible) |
||||||
|
self.setFocus() |
||||||
|
self.update() |
||||||
|
return True |
||||||
|
|
||||||
|
def mouseReleaseEvent(self, evt) -> bool: |
||||||
|
self.clicked.emit(self) |
||||||
|
return True |
||||||
|
|
||||||
|
def mouseDoubleClickEvent(self, evt) -> bool: |
||||||
|
self._ledit.setVisible(True) |
||||||
|
self._ledit.setFocus() |
||||||
|
return True |
||||||
|
|
||||||
|
def mouseDragEvent(self, evt) -> bool: |
||||||
|
drag = ttk.TTkDrag() |
||||||
|
drag.setData(self) |
||||||
|
name = self._layerData.name() |
||||||
|
pm = ttk.TTkCanvas(width=len(name)+4,height=3) |
||||||
|
pm.drawBox(pos=(0,0),size=pm.size()) |
||||||
|
pm.drawText(pos=(2,1), text=name) |
||||||
|
drag.setHotSpot(5, 1) |
||||||
|
drag.setPixmap(pm) |
||||||
|
drag.exec() |
||||||
|
return True |
||||||
|
|
||||||
|
def paintEvent(self, canvas: ttk.TTkCanvas): |
||||||
|
if self._isSelected: |
||||||
|
style = self.style()['selected'] |
||||||
|
else: |
||||||
|
style = self.currentStyle() |
||||||
|
borderColor = style['borderColor'] |
||||||
|
textColor = style['color'] |
||||||
|
btnVisible = '▣' if self._layerVisible else '□' |
||||||
|
w,h = self.size() |
||||||
|
canvas.drawText( pos=(0,0),text=f" ┏{'━'*(w-7)}┓",color=borderColor) |
||||||
|
canvas.drawText( pos=(0,2),text=f" ┗{'━'*(w-7)}┛",color=borderColor) |
||||||
|
if self._first: |
||||||
|
canvas.drawText(pos=(0,1),text=f" {btnVisible} - ┃{' '*(w-7)}┃",color=borderColor) |
||||||
|
else: |
||||||
|
canvas.drawText(pos=(0,1),text=f" {btnVisible} - ╽{' '*(w-7)}╽",color=borderColor) |
||||||
|
canvas.drawTTkString(pos=(7,1),text=self._layerData.name(), width=w-9, color=textColor) |
||||||
|
|
||||||
|
class LayerScrollWidget(ttk.TTkAbstractScrollView): |
||||||
|
__slots__ = ('_layers','_selected', '_dropTo', |
||||||
|
# Signals |
||||||
|
'layerSelected','layerAdded','layerDeleted','layersOrderChanged') |
||||||
|
def __init__(self, **kwargs): |
||||||
|
self.layerSelected = ttk.pyTTkSignal(LayerData) |
||||||
|
self.layerAdded = ttk.pyTTkSignal(LayerData) |
||||||
|
self.layerDeleted = ttk.pyTTkSignal(LayerData) |
||||||
|
self.layersOrderChanged = ttk.pyTTkSignal(list[LayerData]) |
||||||
|
|
||||||
|
self._selected = None |
||||||
|
self._dropTo = None |
||||||
|
self._layers:list[_layerButton] = [] |
||||||
|
super().__init__(**kwargs) |
||||||
|
self.viewChanged.connect(self._placeTheButtons) |
||||||
|
self.viewChanged.connect(self._viewChangedHandler) |
||||||
|
|
||||||
|
@ttk.pyTTkSlot() |
||||||
|
def _viewChangedHandler(self): |
||||||
|
x,y = self.getViewOffsets() |
||||||
|
self.layout().setOffset(-x,-y) |
||||||
|
|
||||||
|
def viewFullAreaSize(self) -> tuple: |
||||||
|
_,_,w,h = self.layout().fullWidgetAreaGeometry() |
||||||
|
return w,h |
||||||
|
|
||||||
|
def viewDisplayedSize(self) -> tuple: |
||||||
|
return self.size() |
||||||
|
|
||||||
|
def maximumWidth(self): return 0x10000 |
||||||
|
def maximumHeight(self): return 0x10000 |
||||||
|
def minimumWidth(self): return 0 |
||||||
|
def minimumHeight(self): return 0 |
||||||
|
|
||||||
|
@ttk.pyTTkSlot(_layerButton) |
||||||
|
def _clickedLayer(self, layerButton:_layerButton): |
||||||
|
if sel:=self._selected: |
||||||
|
sel._isSelected = False |
||||||
|
sel.update() |
||||||
|
self._selected = layerButton |
||||||
|
layerButton._isSelected = True |
||||||
|
self.layerSelected.emit(layerButton._layerData) |
||||||
|
self.update() |
||||||
|
|
||||||
|
def clear(self): |
||||||
|
for layBtn in self._layers: |
||||||
|
self.layout().removeWidget(layBtn) |
||||||
|
layBtn.clicked.clear() |
||||||
|
layBtn.visibilityToggled.clear() |
||||||
|
layBtn._layerData.nameChanged.clear() |
||||||
|
self._layers.clear() |
||||||
|
self.update() |
||||||
|
|
||||||
|
@ttk.pyTTkSlot() |
||||||
|
def moveUp(self): |
||||||
|
return self._moveButton(-1) |
||||||
|
|
||||||
|
@ttk.pyTTkSlot() |
||||||
|
def moveDown(self): |
||||||
|
return self._moveButton(+1) |
||||||
|
|
||||||
|
def _moveButton(self,direction): |
||||||
|
if not self._selected: return |
||||||
|
index = self._layers.index(self._selected) |
||||||
|
if index+direction < 0: return |
||||||
|
l = self._layers.pop(index) |
||||||
|
self._layers.insert(index+direction,l) |
||||||
|
self._placeTheButtons() |
||||||
|
self.layersOrderChanged.emit([_l._layerData for _l in self._layers]) |
||||||
|
|
||||||
|
@ttk.pyTTkSlot() |
||||||
|
def addLayer(self,name=None, data=None): |
||||||
|
name = name if name else f"Layer #{len(self._layers)}" |
||||||
|
_l=LayerData(name=name,data=data) |
||||||
|
newLayerBtn:_layerButton = _layerButton(parent=self,layer=_l) |
||||||
|
self._layers.insert(0,newLayerBtn) |
||||||
|
if sel:=self._selected: sel._isSelected = False |
||||||
|
self._selected = newLayerBtn |
||||||
|
newLayerBtn._isSelected = True |
||||||
|
newLayerBtn.clicked.connect(self._clickedLayer) |
||||||
|
self.viewChanged.emit() |
||||||
|
self._placeTheButtons() |
||||||
|
self.layerAdded.emit(newLayerBtn._layerData) |
||||||
|
return _l |
||||||
|
|
||||||
|
def _placeTheButtons(self): |
||||||
|
w,h = self.size() |
||||||
|
for i,l in enumerate(self._layers): |
||||||
|
l._first = i==0 |
||||||
|
l.setGeometry(0,i*2,w,3) |
||||||
|
l.lowerWidget() |
||||||
|
self.update() |
||||||
|
|
||||||
|
@ttk.pyTTkSlot() |
||||||
|
def delLayer(self): |
||||||
|
self._layers.remove() |
||||||
|
|
||||||
|
def dragEnterEvent(self, evt) -> bool: |
||||||
|
if type(evt.data())!=_layerButton: return False |
||||||
|
x,y = self.getViewOffsets() |
||||||
|
self._dropTo = max(0,min(len(self._layers),(evt.y-1+y)//2)) |
||||||
|
self.update() |
||||||
|
return True |
||||||
|
def dragLeaveEvent(self, evt) -> bool: |
||||||
|
if type(evt.data())!=_layerButton: return False |
||||||
|
self._dropTo = None |
||||||
|
self.update() |
||||||
|
return True |
||||||
|
def dragMoveEvent(self, evt) -> bool: |
||||||
|
if type(evt.data())!=_layerButton: return False |
||||||
|
x,y = self.getViewOffsets() |
||||||
|
self._dropTo = max(0,min(len(self._layers),(evt.y-1+y)//2)) |
||||||
|
self.update() |
||||||
|
ttk.TTkLog.debug(f"{evt.x},{evt.y-y} - {len(self._layers)} - {self._dropTo}") |
||||||
|
return True |
||||||
|
def dropEvent(self, evt) -> bool: |
||||||
|
if type(evt.data())!=_layerButton: return False |
||||||
|
x,y = self.getViewOffsets() |
||||||
|
self._dropTo = None |
||||||
|
data = evt.data() |
||||||
|
# dropPos = len(self._layers)-(evt.y-1)//2 |
||||||
|
dropPos = max(0,min(len(self._layers),(evt.y-1+y)//2)) |
||||||
|
ttk.TTkLog.debug(f"{evt.x},{evt.y-y} - {len(self._layers)} - {self._dropTo} {dropPos}") |
||||||
|
if dropPos > self._layers.index(data): |
||||||
|
dropPos -= 1 |
||||||
|
self._layers.remove(data) |
||||||
|
self._layers.insert(dropPos,data) |
||||||
|
self._placeTheButtons() |
||||||
|
self.layersOrderChanged.emit([_l._layerData for _l in self._layers]) |
||||||
|
return True |
||||||
|
|
||||||
|
# Stupid hack to paint on top of the child widgets |
||||||
|
def paintChildCanvas(self): |
||||||
|
super().paintChildCanvas() |
||||||
|
offx, offy = self.getViewOffsets() |
||||||
|
if self._dropTo == None: return |
||||||
|
canvas = self.getCanvas() |
||||||
|
w,h = canvas.size() |
||||||
|
color = ttk.TTkColor.YELLOW |
||||||
|
canvas.drawText(pos=(0,(self._dropTo)*2-offy),text=f"╞{'═'*(w-2)}╍",color=color) |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Layers(ttk.TTkGridLayout): |
||||||
|
__slots__ = ('_scrollWidget', |
||||||
|
# Forward Methods |
||||||
|
'addLayer','clear', |
||||||
|
# Forward Signals |
||||||
|
'layerSelected','layerAdded','layerDeleted','layersOrderChanged') |
||||||
|
def __init__(self, **kwargs): |
||||||
|
super().__init__(**kwargs) |
||||||
|
self._scrollWidget = _lsw = LayerScrollWidget() |
||||||
|
_sa = ttk.TTkAbstractScrollArea(scrollWidget=self._scrollWidget,minWidth=16) |
||||||
|
_sa.setViewport(_lsw) |
||||||
|
self.addWidget(_sa,0,0,1,5) |
||||||
|
self.addWidget(btnAdd :=ttk.TTkButton(text='add') ,1,0) |
||||||
|
self.addWidget(btnUp :=ttk.TTkButton(text='▲',maxWidth=3) ,1,1) |
||||||
|
self.addWidget(btnDown:=ttk.TTkButton(text='▼',maxWidth=3) ,1,2) |
||||||
|
# self.addItem(ttk.TTkLayout(),1,3) |
||||||
|
self.addWidget(btnDel :=ttk.TTkButton(text=ttk.TTkString('del',ttk.TTkColor.RED),maxWidth=5),1,4) |
||||||
|
|
||||||
|
btnAdd.setToolTip( "Create a new Layer\nand add it to the image") |
||||||
|
btnDel.setToolTip( "Delete the selected Layer") |
||||||
|
btnUp.setToolTip( "Raise the selected Layer one step") |
||||||
|
btnDown.setToolTip("Lower the selected Layer one step") |
||||||
|
|
||||||
|
btnAdd.clicked.connect( _lsw.addLayer) |
||||||
|
btnDel.clicked.connect( _lsw.delLayer) |
||||||
|
btnUp.clicked.connect( _lsw.moveUp) |
||||||
|
btnDown.clicked.connect(_lsw.moveDown) |
||||||
|
|
||||||
|
# forward signals |
||||||
|
self.layerSelected = _lsw.layerSelected |
||||||
|
self.layerSelected = _lsw.layerSelected |
||||||
|
self.layerAdded = _lsw.layerAdded |
||||||
|
self.layerDeleted = _lsw.layerDeleted |
||||||
|
self.layersOrderChanged = _lsw.layersOrderChanged |
||||||
|
|
||||||
|
# forward methods |
||||||
|
self.addLayer = _lsw.addLayer |
||||||
|
self.clear = _lsw.clear |
||||||
@ -0,0 +1,123 @@ |
|||||||
|
# MIT License |
||||||
|
# |
||||||
|
# Copyright (c) 2024 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. |
||||||
|
|
||||||
|
__all__ = ['PaintToolKit'] |
||||||
|
|
||||||
|
import sys, os |
||||||
|
|
||||||
|
sys.path.append(os.path.join(sys.path[0],'../..')) |
||||||
|
import TermTk as ttk |
||||||
|
|
||||||
|
from .paintarea import * |
||||||
|
|
||||||
|
class PaintToolKit(ttk.TTkContainer): |
||||||
|
__slots__ = ('_rSelect', '_rPaint', '_lgliph', |
||||||
|
'_cbFg', '_cbBg', |
||||||
|
'_bpFg', '_bpBg', '_bpDef', |
||||||
|
'_sbDx','_sbDy','_sbDw','_sbDh', |
||||||
|
'_sbLx','_sbLy','_sbLw','_sbLh', |
||||||
|
'_glyph', |
||||||
|
#Signals |
||||||
|
'updatedColor', 'updatedTrans') |
||||||
|
def __init__(self, *args, **kwargs): |
||||||
|
ttk.TTkUiLoader.loadFile(os.path.join(os.path.dirname(os.path.abspath(__file__)),"tui/paintToolKit.tui.json"),self) |
||||||
|
self._glyph = 'X' |
||||||
|
self.updatedColor = ttk.pyTTkSignal(ttk.TTkColor) |
||||||
|
self.updatedTrans = ttk.pyTTkSignal(ttk.TTkColor) |
||||||
|
self._lgliph = self.getWidgetByName("lglyph") |
||||||
|
self._cbFg = self.getWidgetByName("cbFg") |
||||||
|
self._cbBg = self.getWidgetByName("cbBg") |
||||||
|
self._bpFg = self.getWidgetByName("bpFg") |
||||||
|
self._bpBg = self.getWidgetByName("bpBg") |
||||||
|
self._bpDef = self.getWidgetByName("bpDef") |
||||||
|
|
||||||
|
self._sbDx = self.getWidgetByName("sbDx") |
||||||
|
self._sbDy = self.getWidgetByName("sbDy") |
||||||
|
self._sbDw = self.getWidgetByName("sbDw") |
||||||
|
self._sbDh = self.getWidgetByName("sbDh") |
||||||
|
self._sbLx = self.getWidgetByName("sbLx") |
||||||
|
self._sbLy = self.getWidgetByName("sbLy") |
||||||
|
self._sbLw = self.getWidgetByName("sbLw") |
||||||
|
self._sbLh = self.getWidgetByName("sbLh") |
||||||
|
|
||||||
|
self._bpDef.setColor(ttk.TTkColor.bg('#FF00FF')) |
||||||
|
self._cbFg.toggled.connect(self._refreshColor) |
||||||
|
self._cbBg.toggled.connect(self._refreshColor) |
||||||
|
|
||||||
|
self._bpFg.colorSelected.connect(self._refreshColor) |
||||||
|
self._bpBg.colorSelected.connect(self._refreshColor) |
||||||
|
self._bpDef.colorSelected.connect(self.updatedTrans.emit) |
||||||
|
|
||||||
|
self._refreshColor(emit=False) |
||||||
|
|
||||||
|
@ttk.pyTTkSlot(CanvasLayer) |
||||||
|
def updateLayer(self, layer:CanvasLayer): |
||||||
|
lx,ly = layer.pos() |
||||||
|
lw,lh = layer.size() |
||||||
|
self._sbLx.setValue(lx) |
||||||
|
self._sbLy.setValue(ly) |
||||||
|
self._sbLw.setValue(lw) |
||||||
|
self._sbLh.setValue(lh) |
||||||
|
|
||||||
|
@ttk.pyTTkSlot() |
||||||
|
def _refreshColor(self, emit=True): |
||||||
|
color =self.color() |
||||||
|
self._lgliph.setText( |
||||||
|
ttk.TTkString("Glyph: '") + |
||||||
|
ttk.TTkString(self._glyph,color) + |
||||||
|
ttk.TTkString("'")) |
||||||
|
if emit: |
||||||
|
self.updatedColor.emit(color) |
||||||
|
|
||||||
|
@ttk.pyTTkSlot(ttk.TTkString) |
||||||
|
def glyphFromString(self, ch:ttk.TTkString): |
||||||
|
if len(ch)<=0: return |
||||||
|
self._glyph = ch.charAt(0) |
||||||
|
self._refreshColor() |
||||||
|
# self.setColor(ch.colorAt(0)) |
||||||
|
|
||||||
|
def color(self): |
||||||
|
color = ttk.TTkColor() |
||||||
|
if self._cbFg.checkState() == ttk.TTkK.Checked: |
||||||
|
color += self._bpFg.color().invertFgBg() |
||||||
|
if self._cbBg.checkState() == ttk.TTkK.Checked: |
||||||
|
color += self._bpBg.color() |
||||||
|
return color |
||||||
|
|
||||||
|
@ttk.pyTTkSlot(ttk.TTkColor) |
||||||
|
def setColor(self, color:ttk.TTkColor): |
||||||
|
if fg := color.foreground(): |
||||||
|
self._cbFg.setCheckState(ttk.TTkK.Checked) |
||||||
|
self._bpFg.setEnabled() |
||||||
|
self._bpFg.setColor(fg.invertFgBg()) |
||||||
|
else: |
||||||
|
self._cbFg.setCheckState(ttk.TTkK.Unchecked) |
||||||
|
self._bpFg.setDisabled() |
||||||
|
|
||||||
|
if bg := color.background(): |
||||||
|
self._cbBg.setCheckState(ttk.TTkK.Checked) |
||||||
|
self._bpBg.setEnabled() |
||||||
|
self._bpBg.setColor(bg) |
||||||
|
else: |
||||||
|
self._cbBg.setCheckState(ttk.TTkK.Unchecked) |
||||||
|
self._bpBg.setDisabled() |
||||||
|
self._refreshColor(emit=False) |
||||||
@ -0,0 +1,918 @@ |
|||||||
|
{ |
||||||
|
"version": "2.0.0", |
||||||
|
"tui": { |
||||||
|
"class": "TTkContainer", |
||||||
|
"params": { |
||||||
|
"Name": "MainWindow", |
||||||
|
"Position": [ |
||||||
|
4, |
||||||
|
2 |
||||||
|
], |
||||||
|
"Size": [ |
||||||
|
78, |
||||||
|
2 |
||||||
|
], |
||||||
|
"Min Width": 50, |
||||||
|
"Min Height": 2, |
||||||
|
"Max Width": 65536, |
||||||
|
"Max Height": 2, |
||||||
|
"Visible": true, |
||||||
|
"Enabled": true, |
||||||
|
"ToolTip": "", |
||||||
|
"Padding": [ |
||||||
|
0, |
||||||
|
0, |
||||||
|
0, |
||||||
|
0 |
||||||
|
], |
||||||
|
"Layout": "TTkLayout" |
||||||
|
}, |
||||||
|
"layout": { |
||||||
|
"class": "TTkLayout", |
||||||
|
"params": { |
||||||
|
"Geometry": [ |
||||||
|
0, |
||||||
|
0, |
||||||
|
78, |
||||||
|
2 |
||||||
|
] |
||||||
|
}, |
||||||
|
"children": [ |
||||||
|
{ |
||||||
|
"class": "TTkCheckbox", |
||||||
|
"params": { |
||||||
|
"Name": "cbFg", |
||||||
|
"Position": [ |
||||||
|
12, |
||||||
|
0 |
||||||
|
], |
||||||
|
"Size": [ |
||||||
|
5, |
||||||
|
1 |
||||||
|
], |
||||||
|
"Min Width": 5, |
||||||
|
"Min Height": 1, |
||||||
|
"Max Width": 65536, |
||||||
|
"Max Height": 1, |
||||||
|
"Visible": true, |
||||||
|
"Enabled": true, |
||||||
|
"ToolTip": "", |
||||||
|
"Text": "Fg", |
||||||
|
"Tristate": false, |
||||||
|
"Checked": false, |
||||||
|
"Check State": 0 |
||||||
|
}, |
||||||
|
"row": 0, |
||||||
|
"col": 0, |
||||||
|
"rowspan": 1, |
||||||
|
"colspan": 1 |
||||||
|
}, |
||||||
|
{ |
||||||
|
"class": "TTkCheckbox", |
||||||
|
"params": { |
||||||
|
"Name": "cbBg", |
||||||
|
"Position": [ |
||||||
|
12, |
||||||
|
1 |
||||||
|
], |
||||||
|
"Size": [ |
||||||
|
5, |
||||||
|
1 |
||||||
|
], |
||||||
|
"Min Width": 5, |
||||||
|
"Min Height": 1, |
||||||
|
"Max Width": 65536, |
||||||
|
"Max Height": 1, |
||||||
|
"Visible": true, |
||||||
|
"Enabled": true, |
||||||
|
"ToolTip": "", |
||||||
|
"Text": "Bg", |
||||||
|
"Tristate": false, |
||||||
|
"Checked": false, |
||||||
|
"Check State": 0 |
||||||
|
}, |
||||||
|
"row": 0, |
||||||
|
"col": 0, |
||||||
|
"rowspan": 1, |
||||||
|
"colspan": 1 |
||||||
|
}, |
||||||
|
{ |
||||||
|
"class": "TTkLabel", |
||||||
|
"params": { |
||||||
|
"Name": "TTkLabel-1", |
||||||
|
"Position": [ |
||||||
|
0, |
||||||
|
1 |
||||||
|
], |
||||||
|
"Size": [ |
||||||
|
5, |
||||||
|
1 |
||||||
|
], |
||||||
|
"Min Width": 5, |
||||||
|
"Min Height": 1, |
||||||
|
"Max Width": 65536, |
||||||
|
"Max Height": 65536, |
||||||
|
"Visible": true, |
||||||
|
"Enabled": true, |
||||||
|
"ToolTip": "", |
||||||
|
"Text": "Trans", |
||||||
|
"Color": "\u001b[0m", |
||||||
|
"Alignment": 0 |
||||||
|
}, |
||||||
|
"row": 0, |
||||||
|
"col": 0, |
||||||
|
"rowspan": 1, |
||||||
|
"colspan": 1 |
||||||
|
}, |
||||||
|
{ |
||||||
|
"class": "TTkLabel", |
||||||
|
"params": { |
||||||
|
"Name": "lglyph", |
||||||
|
"Position": [ |
||||||
|
0, |
||||||
|
0 |
||||||
|
], |
||||||
|
"Size": [ |
||||||
|
12, |
||||||
|
1 |
||||||
|
], |
||||||
|
"Min Width": 6, |
||||||
|
"Min Height": 1, |
||||||
|
"Max Width": 65536, |
||||||
|
"Max Height": 65536, |
||||||
|
"Visible": true, |
||||||
|
"Enabled": true, |
||||||
|
"ToolTip": "", |
||||||
|
"Text": "Glyph:", |
||||||
|
"Color": "\u001b[0m", |
||||||
|
"Alignment": 0 |
||||||
|
}, |
||||||
|
"row": 0, |
||||||
|
"col": 0, |
||||||
|
"rowspan": 1, |
||||||
|
"colspan": 1 |
||||||
|
}, |
||||||
|
{ |
||||||
|
"class": "TTkColorButtonPicker", |
||||||
|
"params": { |
||||||
|
"Name": "bpDef", |
||||||
|
"Position": [ |
||||||
|
5, |
||||||
|
1 |
||||||
|
], |
||||||
|
"Size": [ |
||||||
|
6, |
||||||
|
1 |
||||||
|
], |
||||||
|
"Min Width": 1, |
||||||
|
"Min Height": 1, |
||||||
|
"Max Width": 65536, |
||||||
|
"Max Height": 1, |
||||||
|
"Visible": true, |
||||||
|
"Enabled": true, |
||||||
|
"ToolTip": "", |
||||||
|
"Text": "", |
||||||
|
"Border": false, |
||||||
|
"Checkable": false, |
||||||
|
"Checked": false, |
||||||
|
"Color": "\u001b[48;2;0;0;0m" |
||||||
|
}, |
||||||
|
"row": 0, |
||||||
|
"col": 0, |
||||||
|
"rowspan": 1, |
||||||
|
"colspan": 1 |
||||||
|
}, |
||||||
|
{ |
||||||
|
"class": "TTkLabel", |
||||||
|
"params": { |
||||||
|
"Name": "TTkLabel-2", |
||||||
|
"Position": [ |
||||||
|
25, |
||||||
|
0 |
||||||
|
], |
||||||
|
"Size": [ |
||||||
|
3, |
||||||
|
1 |
||||||
|
], |
||||||
|
"Min Width": 3, |
||||||
|
"Min Height": 1, |
||||||
|
"Max Width": 65536, |
||||||
|
"Max Height": 65536, |
||||||
|
"Visible": true, |
||||||
|
"Enabled": true, |
||||||
|
"ToolTip": "", |
||||||
|
"Text": "Doc", |
||||||
|
"Color": "\u001b[0m", |
||||||
|
"Alignment": 0 |
||||||
|
}, |
||||||
|
"row": 0, |
||||||
|
"col": 0, |
||||||
|
"rowspan": 1, |
||||||
|
"colspan": 1 |
||||||
|
}, |
||||||
|
{ |
||||||
|
"class": "TTkColorButtonPicker", |
||||||
|
"params": { |
||||||
|
"Name": "bpBg", |
||||||
|
"Position": [ |
||||||
|
17, |
||||||
|
1 |
||||||
|
], |
||||||
|
"Size": [ |
||||||
|
6, |
||||||
|
1 |
||||||
|
], |
||||||
|
"Min Width": 2, |
||||||
|
"Min Height": 1, |
||||||
|
"Max Width": 65536, |
||||||
|
"Max Height": 1, |
||||||
|
"Visible": true, |
||||||
|
"Enabled": false, |
||||||
|
"ToolTip": "", |
||||||
|
"Text": "", |
||||||
|
"Border": false, |
||||||
|
"Checkable": false, |
||||||
|
"Checked": false, |
||||||
|
"Color": "\u001b[48;2;0;0;0m" |
||||||
|
}, |
||||||
|
"row": 0, |
||||||
|
"col": 0, |
||||||
|
"rowspan": 1, |
||||||
|
"colspan": 1 |
||||||
|
}, |
||||||
|
{ |
||||||
|
"class": "TTkColorButtonPicker", |
||||||
|
"params": { |
||||||
|
"Name": "bpFg", |
||||||
|
"Position": [ |
||||||
|
17, |
||||||
|
0 |
||||||
|
], |
||||||
|
"Size": [ |
||||||
|
6, |
||||||
|
1 |
||||||
|
], |
||||||
|
"Min Width": 2, |
||||||
|
"Min Height": 1, |
||||||
|
"Max Width": 65536, |
||||||
|
"Max Height": 1, |
||||||
|
"Visible": true, |
||||||
|
"Enabled": false, |
||||||
|
"ToolTip": "", |
||||||
|
"Text": "", |
||||||
|
"Border": false, |
||||||
|
"Checkable": false, |
||||||
|
"Checked": false, |
||||||
|
"Color": "\u001b[48;2;0;0;0m" |
||||||
|
}, |
||||||
|
"row": 0, |
||||||
|
"col": 0, |
||||||
|
"rowspan": 1, |
||||||
|
"colspan": 1 |
||||||
|
}, |
||||||
|
{ |
||||||
|
"class": "TTkLabel", |
||||||
|
"params": { |
||||||
|
"Name": "TTkLabel-4", |
||||||
|
"Position": [ |
||||||
|
29, |
||||||
|
0 |
||||||
|
], |
||||||
|
"Size": [ |
||||||
|
1, |
||||||
|
1 |
||||||
|
], |
||||||
|
"Min Width": 1, |
||||||
|
"Min Height": 1, |
||||||
|
"Max Width": 65536, |
||||||
|
"Max Height": 65536, |
||||||
|
"Visible": true, |
||||||
|
"Enabled": true, |
||||||
|
"ToolTip": "", |
||||||
|
"Text": "x", |
||||||
|
"Color": "\u001b[0m", |
||||||
|
"Alignment": 0 |
||||||
|
}, |
||||||
|
"row": 0, |
||||||
|
"col": 0, |
||||||
|
"rowspan": 1, |
||||||
|
"colspan": 1 |
||||||
|
}, |
||||||
|
{ |
||||||
|
"class": "TTkLabel", |
||||||
|
"params": { |
||||||
|
"Name": "TTkLabel-5", |
||||||
|
"Position": [ |
||||||
|
29, |
||||||
|
1 |
||||||
|
], |
||||||
|
"Size": [ |
||||||
|
1, |
||||||
|
1 |
||||||
|
], |
||||||
|
"Min Width": 1, |
||||||
|
"Min Height": 1, |
||||||
|
"Max Width": 65536, |
||||||
|
"Max Height": 65536, |
||||||
|
"Visible": true, |
||||||
|
"Enabled": true, |
||||||
|
"ToolTip": "", |
||||||
|
"Text": "y", |
||||||
|
"Color": "\u001b[0m", |
||||||
|
"Alignment": 0 |
||||||
|
}, |
||||||
|
"row": 0, |
||||||
|
"col": 0, |
||||||
|
"rowspan": 1, |
||||||
|
"colspan": 1 |
||||||
|
}, |
||||||
|
{ |
||||||
|
"class": "TTkSpinBox", |
||||||
|
"params": { |
||||||
|
"Name": "sbDx", |
||||||
|
"Position": [ |
||||||
|
31, |
||||||
|
0 |
||||||
|
], |
||||||
|
"Size": [ |
||||||
|
6, |
||||||
|
1 |
||||||
|
], |
||||||
|
"Min Width": 4, |
||||||
|
"Min Height": 1, |
||||||
|
"Max Width": 65536, |
||||||
|
"Max Height": 65536, |
||||||
|
"Visible": true, |
||||||
|
"Enabled": true, |
||||||
|
"ToolTip": "", |
||||||
|
"Padding": [ |
||||||
|
0, |
||||||
|
0, |
||||||
|
0, |
||||||
|
2 |
||||||
|
], |
||||||
|
"Layout": "TTkGridLayout", |
||||||
|
"Value": 0, |
||||||
|
"Minimum": -100, |
||||||
|
"Maximum": 100 |
||||||
|
}, |
||||||
|
"layout": { |
||||||
|
"class": "TTkGridLayout", |
||||||
|
"params": { |
||||||
|
"Geometry": [ |
||||||
|
0, |
||||||
|
0, |
||||||
|
4, |
||||||
|
1 |
||||||
|
] |
||||||
|
}, |
||||||
|
"children": [] |
||||||
|
}, |
||||||
|
"row": 0, |
||||||
|
"col": 0, |
||||||
|
"rowspan": 1, |
||||||
|
"colspan": 1 |
||||||
|
}, |
||||||
|
{ |
||||||
|
"class": "TTkSpinBox", |
||||||
|
"params": { |
||||||
|
"Name": "sbDy", |
||||||
|
"Position": [ |
||||||
|
31, |
||||||
|
1 |
||||||
|
], |
||||||
|
"Size": [ |
||||||
|
6, |
||||||
|
1 |
||||||
|
], |
||||||
|
"Min Width": 4, |
||||||
|
"Min Height": 1, |
||||||
|
"Max Width": 65536, |
||||||
|
"Max Height": 65536, |
||||||
|
"Visible": true, |
||||||
|
"Enabled": true, |
||||||
|
"ToolTip": "", |
||||||
|
"Padding": [ |
||||||
|
0, |
||||||
|
0, |
||||||
|
0, |
||||||
|
2 |
||||||
|
], |
||||||
|
"Layout": "TTkGridLayout", |
||||||
|
"Value": 0, |
||||||
|
"Minimum": -100, |
||||||
|
"Maximum": 100 |
||||||
|
}, |
||||||
|
"layout": { |
||||||
|
"class": "TTkGridLayout", |
||||||
|
"params": { |
||||||
|
"Geometry": [ |
||||||
|
0, |
||||||
|
0, |
||||||
|
4, |
||||||
|
1 |
||||||
|
] |
||||||
|
}, |
||||||
|
"children": [] |
||||||
|
}, |
||||||
|
"row": 0, |
||||||
|
"col": 0, |
||||||
|
"rowspan": 1, |
||||||
|
"colspan": 1 |
||||||
|
}, |
||||||
|
{ |
||||||
|
"class": "TTkLabel", |
||||||
|
"params": { |
||||||
|
"Name": "TTkLabel-6", |
||||||
|
"Position": [ |
||||||
|
38, |
||||||
|
0 |
||||||
|
], |
||||||
|
"Size": [ |
||||||
|
1, |
||||||
|
1 |
||||||
|
], |
||||||
|
"Min Width": 1, |
||||||
|
"Min Height": 1, |
||||||
|
"Max Width": 65536, |
||||||
|
"Max Height": 65536, |
||||||
|
"Visible": true, |
||||||
|
"Enabled": true, |
||||||
|
"ToolTip": "", |
||||||
|
"Text": "w", |
||||||
|
"Color": "\u001b[0m", |
||||||
|
"Alignment": 0 |
||||||
|
}, |
||||||
|
"row": 0, |
||||||
|
"col": 0, |
||||||
|
"rowspan": 1, |
||||||
|
"colspan": 1 |
||||||
|
}, |
||||||
|
{ |
||||||
|
"class": "TTkLabel", |
||||||
|
"params": { |
||||||
|
"Name": "TTkLabel-7", |
||||||
|
"Position": [ |
||||||
|
38, |
||||||
|
1 |
||||||
|
], |
||||||
|
"Size": [ |
||||||
|
1, |
||||||
|
1 |
||||||
|
], |
||||||
|
"Min Width": 1, |
||||||
|
"Min Height": 1, |
||||||
|
"Max Width": 65536, |
||||||
|
"Max Height": 65536, |
||||||
|
"Visible": true, |
||||||
|
"Enabled": true, |
||||||
|
"ToolTip": "", |
||||||
|
"Text": "h", |
||||||
|
"Color": "\u001b[0m", |
||||||
|
"Alignment": 0 |
||||||
|
}, |
||||||
|
"row": 0, |
||||||
|
"col": 0, |
||||||
|
"rowspan": 1, |
||||||
|
"colspan": 1 |
||||||
|
}, |
||||||
|
{ |
||||||
|
"class": "TTkSpinBox", |
||||||
|
"params": { |
||||||
|
"Name": "sbDw", |
||||||
|
"Position": [ |
||||||
|
40, |
||||||
|
0 |
||||||
|
], |
||||||
|
"Size": [ |
||||||
|
6, |
||||||
|
1 |
||||||
|
], |
||||||
|
"Min Width": 4, |
||||||
|
"Min Height": 1, |
||||||
|
"Max Width": 65536, |
||||||
|
"Max Height": 65536, |
||||||
|
"Visible": true, |
||||||
|
"Enabled": true, |
||||||
|
"ToolTip": "", |
||||||
|
"Padding": [ |
||||||
|
0, |
||||||
|
0, |
||||||
|
0, |
||||||
|
2 |
||||||
|
], |
||||||
|
"Layout": "TTkGridLayout", |
||||||
|
"Value": 0, |
||||||
|
"Minimum": 0, |
||||||
|
"Maximum": 99 |
||||||
|
}, |
||||||
|
"layout": { |
||||||
|
"class": "TTkGridLayout", |
||||||
|
"params": { |
||||||
|
"Geometry": [ |
||||||
|
0, |
||||||
|
0, |
||||||
|
4, |
||||||
|
1 |
||||||
|
] |
||||||
|
}, |
||||||
|
"children": [] |
||||||
|
}, |
||||||
|
"row": 0, |
||||||
|
"col": 0, |
||||||
|
"rowspan": 1, |
||||||
|
"colspan": 1 |
||||||
|
}, |
||||||
|
{ |
||||||
|
"class": "TTkSpinBox", |
||||||
|
"params": { |
||||||
|
"Name": "sbDh", |
||||||
|
"Position": [ |
||||||
|
40, |
||||||
|
1 |
||||||
|
], |
||||||
|
"Size": [ |
||||||
|
6, |
||||||
|
1 |
||||||
|
], |
||||||
|
"Min Width": 4, |
||||||
|
"Min Height": 1, |
||||||
|
"Max Width": 65536, |
||||||
|
"Max Height": 65536, |
||||||
|
"Visible": true, |
||||||
|
"Enabled": true, |
||||||
|
"ToolTip": "", |
||||||
|
"Padding": [ |
||||||
|
0, |
||||||
|
0, |
||||||
|
0, |
||||||
|
2 |
||||||
|
], |
||||||
|
"Layout": "TTkGridLayout", |
||||||
|
"Value": 0, |
||||||
|
"Minimum": 0, |
||||||
|
"Maximum": 99 |
||||||
|
}, |
||||||
|
"layout": { |
||||||
|
"class": "TTkGridLayout", |
||||||
|
"params": { |
||||||
|
"Geometry": [ |
||||||
|
0, |
||||||
|
0, |
||||||
|
4, |
||||||
|
1 |
||||||
|
] |
||||||
|
}, |
||||||
|
"children": [] |
||||||
|
}, |
||||||
|
"row": 0, |
||||||
|
"col": 0, |
||||||
|
"rowspan": 1, |
||||||
|
"colspan": 1 |
||||||
|
}, |
||||||
|
{ |
||||||
|
"class": "TTkLabel", |
||||||
|
"params": { |
||||||
|
"Name": "TTkLabel-3", |
||||||
|
"Position": [ |
||||||
|
48, |
||||||
|
0 |
||||||
|
], |
||||||
|
"Size": [ |
||||||
|
5, |
||||||
|
1 |
||||||
|
], |
||||||
|
"Min Width": 5, |
||||||
|
"Min Height": 1, |
||||||
|
"Max Width": 65536, |
||||||
|
"Max Height": 65536, |
||||||
|
"Visible": true, |
||||||
|
"Enabled": true, |
||||||
|
"ToolTip": "", |
||||||
|
"Text": "Layer", |
||||||
|
"Color": "\u001b[0m", |
||||||
|
"Alignment": 0 |
||||||
|
}, |
||||||
|
"row": 0, |
||||||
|
"col": 0, |
||||||
|
"rowspan": 1, |
||||||
|
"colspan": 1 |
||||||
|
}, |
||||||
|
{ |
||||||
|
"class": "TTkLabel", |
||||||
|
"params": { |
||||||
|
"Name": "TTkLabel-8", |
||||||
|
"Position": [ |
||||||
|
54, |
||||||
|
0 |
||||||
|
], |
||||||
|
"Size": [ |
||||||
|
1, |
||||||
|
1 |
||||||
|
], |
||||||
|
"Min Width": 1, |
||||||
|
"Min Height": 1, |
||||||
|
"Max Width": 65536, |
||||||
|
"Max Height": 65536, |
||||||
|
"Visible": true, |
||||||
|
"Enabled": true, |
||||||
|
"ToolTip": "", |
||||||
|
"Text": "x", |
||||||
|
"Color": "\u001b[0m", |
||||||
|
"Alignment": 0 |
||||||
|
}, |
||||||
|
"row": 0, |
||||||
|
"col": 0, |
||||||
|
"rowspan": 1, |
||||||
|
"colspan": 1 |
||||||
|
}, |
||||||
|
{ |
||||||
|
"class": "TTkLabel", |
||||||
|
"params": { |
||||||
|
"Name": "TTkLabel-9", |
||||||
|
"Position": [ |
||||||
|
54, |
||||||
|
1 |
||||||
|
], |
||||||
|
"Size": [ |
||||||
|
1, |
||||||
|
1 |
||||||
|
], |
||||||
|
"Min Width": 1, |
||||||
|
"Min Height": 1, |
||||||
|
"Max Width": 65536, |
||||||
|
"Max Height": 65536, |
||||||
|
"Visible": true, |
||||||
|
"Enabled": true, |
||||||
|
"ToolTip": "", |
||||||
|
"Text": "y", |
||||||
|
"Color": "\u001b[0m", |
||||||
|
"Alignment": 0 |
||||||
|
}, |
||||||
|
"row": 0, |
||||||
|
"col": 0, |
||||||
|
"rowspan": 1, |
||||||
|
"colspan": 1 |
||||||
|
}, |
||||||
|
{ |
||||||
|
"class": "TTkSpinBox", |
||||||
|
"params": { |
||||||
|
"Name": "sbLx", |
||||||
|
"Position": [ |
||||||
|
56, |
||||||
|
0 |
||||||
|
], |
||||||
|
"Size": [ |
||||||
|
6, |
||||||
|
1 |
||||||
|
], |
||||||
|
"Min Width": 4, |
||||||
|
"Min Height": 1, |
||||||
|
"Max Width": 65536, |
||||||
|
"Max Height": 65536, |
||||||
|
"Visible": true, |
||||||
|
"Enabled": true, |
||||||
|
"ToolTip": "", |
||||||
|
"Padding": [ |
||||||
|
0, |
||||||
|
0, |
||||||
|
0, |
||||||
|
2 |
||||||
|
], |
||||||
|
"Layout": "TTkGridLayout", |
||||||
|
"Value": 0, |
||||||
|
"Minimum": -100, |
||||||
|
"Maximum": 100 |
||||||
|
}, |
||||||
|
"layout": { |
||||||
|
"class": "TTkGridLayout", |
||||||
|
"params": { |
||||||
|
"Geometry": [ |
||||||
|
0, |
||||||
|
0, |
||||||
|
4, |
||||||
|
1 |
||||||
|
] |
||||||
|
}, |
||||||
|
"children": [] |
||||||
|
}, |
||||||
|
"row": 0, |
||||||
|
"col": 0, |
||||||
|
"rowspan": 1, |
||||||
|
"colspan": 1 |
||||||
|
}, |
||||||
|
{ |
||||||
|
"class": "TTkSpinBox", |
||||||
|
"params": { |
||||||
|
"Name": "sbLy", |
||||||
|
"Position": [ |
||||||
|
56, |
||||||
|
1 |
||||||
|
], |
||||||
|
"Size": [ |
||||||
|
6, |
||||||
|
1 |
||||||
|
], |
||||||
|
"Min Width": 4, |
||||||
|
"Min Height": 1, |
||||||
|
"Max Width": 65536, |
||||||
|
"Max Height": 65536, |
||||||
|
"Visible": true, |
||||||
|
"Enabled": true, |
||||||
|
"ToolTip": "", |
||||||
|
"Padding": [ |
||||||
|
0, |
||||||
|
0, |
||||||
|
0, |
||||||
|
2 |
||||||
|
], |
||||||
|
"Layout": "TTkGridLayout", |
||||||
|
"Value": 0, |
||||||
|
"Minimum": -100, |
||||||
|
"Maximum": 100 |
||||||
|
}, |
||||||
|
"layout": { |
||||||
|
"class": "TTkGridLayout", |
||||||
|
"params": { |
||||||
|
"Geometry": [ |
||||||
|
0, |
||||||
|
0, |
||||||
|
4, |
||||||
|
1 |
||||||
|
] |
||||||
|
}, |
||||||
|
"children": [] |
||||||
|
}, |
||||||
|
"row": 0, |
||||||
|
"col": 0, |
||||||
|
"rowspan": 1, |
||||||
|
"colspan": 1 |
||||||
|
}, |
||||||
|
{ |
||||||
|
"class": "TTkLabel", |
||||||
|
"params": { |
||||||
|
"Name": "TTkLabel-10", |
||||||
|
"Position": [ |
||||||
|
63, |
||||||
|
0 |
||||||
|
], |
||||||
|
"Size": [ |
||||||
|
1, |
||||||
|
1 |
||||||
|
], |
||||||
|
"Min Width": 1, |
||||||
|
"Min Height": 1, |
||||||
|
"Max Width": 65536, |
||||||
|
"Max Height": 65536, |
||||||
|
"Visible": true, |
||||||
|
"Enabled": true, |
||||||
|
"ToolTip": "", |
||||||
|
"Text": "w", |
||||||
|
"Color": "\u001b[0m", |
||||||
|
"Alignment": 0 |
||||||
|
}, |
||||||
|
"row": 0, |
||||||
|
"col": 0, |
||||||
|
"rowspan": 1, |
||||||
|
"colspan": 1 |
||||||
|
}, |
||||||
|
{ |
||||||
|
"class": "TTkLabel", |
||||||
|
"params": { |
||||||
|
"Name": "TTkLabel-11", |
||||||
|
"Position": [ |
||||||
|
63, |
||||||
|
1 |
||||||
|
], |
||||||
|
"Size": [ |
||||||
|
1, |
||||||
|
1 |
||||||
|
], |
||||||
|
"Min Width": 1, |
||||||
|
"Min Height": 1, |
||||||
|
"Max Width": 65536, |
||||||
|
"Max Height": 65536, |
||||||
|
"Visible": true, |
||||||
|
"Enabled": true, |
||||||
|
"ToolTip": "", |
||||||
|
"Text": "h", |
||||||
|
"Color": "\u001b[0m", |
||||||
|
"Alignment": 0 |
||||||
|
}, |
||||||
|
"row": 0, |
||||||
|
"col": 0, |
||||||
|
"rowspan": 1, |
||||||
|
"colspan": 1 |
||||||
|
}, |
||||||
|
{ |
||||||
|
"class": "TTkSpinBox", |
||||||
|
"params": { |
||||||
|
"Name": "sbLw", |
||||||
|
"Position": [ |
||||||
|
65, |
||||||
|
0 |
||||||
|
], |
||||||
|
"Size": [ |
||||||
|
6, |
||||||
|
1 |
||||||
|
], |
||||||
|
"Min Width": 4, |
||||||
|
"Min Height": 1, |
||||||
|
"Max Width": 65536, |
||||||
|
"Max Height": 65536, |
||||||
|
"Visible": true, |
||||||
|
"Enabled": true, |
||||||
|
"ToolTip": "", |
||||||
|
"Padding": [ |
||||||
|
0, |
||||||
|
0, |
||||||
|
0, |
||||||
|
2 |
||||||
|
], |
||||||
|
"Layout": "TTkGridLayout", |
||||||
|
"Value": 0, |
||||||
|
"Minimum": 0, |
||||||
|
"Maximum": 99 |
||||||
|
}, |
||||||
|
"layout": { |
||||||
|
"class": "TTkGridLayout", |
||||||
|
"params": { |
||||||
|
"Geometry": [ |
||||||
|
0, |
||||||
|
0, |
||||||
|
4, |
||||||
|
1 |
||||||
|
] |
||||||
|
}, |
||||||
|
"children": [] |
||||||
|
}, |
||||||
|
"row": 0, |
||||||
|
"col": 0, |
||||||
|
"rowspan": 1, |
||||||
|
"colspan": 1 |
||||||
|
}, |
||||||
|
{ |
||||||
|
"class": "TTkSpinBox", |
||||||
|
"params": { |
||||||
|
"Name": "sbLh", |
||||||
|
"Position": [ |
||||||
|
65, |
||||||
|
1 |
||||||
|
], |
||||||
|
"Size": [ |
||||||
|
6, |
||||||
|
1 |
||||||
|
], |
||||||
|
"Min Width": 4, |
||||||
|
"Min Height": 1, |
||||||
|
"Max Width": 65536, |
||||||
|
"Max Height": 65536, |
||||||
|
"Visible": true, |
||||||
|
"Enabled": true, |
||||||
|
"ToolTip": "", |
||||||
|
"Padding": [ |
||||||
|
0, |
||||||
|
0, |
||||||
|
0, |
||||||
|
2 |
||||||
|
], |
||||||
|
"Layout": "TTkGridLayout", |
||||||
|
"Value": 0, |
||||||
|
"Minimum": 0, |
||||||
|
"Maximum": 99 |
||||||
|
}, |
||||||
|
"layout": { |
||||||
|
"class": "TTkGridLayout", |
||||||
|
"params": { |
||||||
|
"Geometry": [ |
||||||
|
0, |
||||||
|
0, |
||||||
|
4, |
||||||
|
1 |
||||||
|
] |
||||||
|
}, |
||||||
|
"children": [] |
||||||
|
}, |
||||||
|
"row": 0, |
||||||
|
"col": 0, |
||||||
|
"rowspan": 1, |
||||||
|
"colspan": 1 |
||||||
|
} |
||||||
|
] |
||||||
|
} |
||||||
|
}, |
||||||
|
"connections": [ |
||||||
|
{ |
||||||
|
"sender": "cbFg", |
||||||
|
"receiver": "bpFg", |
||||||
|
"signal": "toggled(bool)", |
||||||
|
"slot": "setEnabled(bool)" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"sender": "cbBg", |
||||||
|
"receiver": "bpBg", |
||||||
|
"signal": "toggled(bool)", |
||||||
|
"slot": "setEnabled(bool)" |
||||||
|
} |
||||||
|
] |
||||||
|
} |
||||||
Loading…
Reference in new issue