diff --git a/TermTk/TTkCore/canvas.py b/TermTk/TTkCore/canvas.py index abba88ef..a09cd71a 100644 --- a/TermTk/TTkCore/canvas.py +++ b/TermTk/TTkCore/canvas.py @@ -99,7 +99,7 @@ class TTkCanvas: _x < self._width and \ _x >= 0 and _y >=0 : self._data[_y][_x] = _ch - self._colors[_y][_x] = _col + self._colors[_y][_x] = _col.mod(_x,_y) def drawText(self, pos, text, color=TTkColor.RST): if not self._visible: return @@ -208,7 +208,7 @@ class TTkCanvas: def pushToTerminal(self, x, y, w, h): # TTkLog.debug("pushToTerminal") - lastcolor = None + lastcolor = TTkColor.RST for y in range(0, self._height): ansi = lbt.Mv.t(y+1,1) for x in range(0, self._width): diff --git a/TermTk/TTkCore/color.py b/TermTk/TTkCore/color.py index a9de6f0a..a539b9b8 100644 --- a/TermTk/TTkCore/color.py +++ b/TermTk/TTkCore/color.py @@ -62,12 +62,13 @@ from TermTk.TTkCore.helper import * # [49m 2.53 set background color to default (black) class _TTkColor: - __slots__ = ('_fg','_bg','_mod') + __slots__ = ('_fg','_bg','_mod', '_colorMod') _fg: str; _bg: str; _mod: str - def __init__(self, fg:str="", bg:str="", mod:str=""): + def __init__(self, fg:str="", bg:str="", mod:str="", colorMod=None): self._fg = fg self._bg = bg self._mod = mod + self._colorMod = colorMod def __str__(self): return self._fg+self._bg+self._mod @@ -86,8 +87,9 @@ class _TTkColor: else: fg: str = self._fg if other._fg == "" else other._fg bg: str = self._bg if other._bg == "" else other._bg - mod: str = self._mod if other._mod == "" else other._mod - return TTkColor(fg,bg,mod) + mod: str = self._mod + other._mod + colorMod = self._colorMod if other._colorMod == None else other._colorMod + return TTkColor(fg,bg,mod,colorMod) def __radd__(self, other): # TTkLog.debug("__radd__") @@ -96,23 +98,116 @@ class _TTkColor: else: fg: str = self._fg if other._fg == "" else other._fg bg: str = self._bg if other._bg == "" else other._bg - mod: str = self._mod if other._mod == "" else other._mod - return TTkColor(fg,bg,mod) + mod: self._mod + other._mod + colorMod = self._colorMod if other._colorMod == None else other._colorMod + return TTkColor(fg,bg,mod,colorMod) def __sub__(self, other): # TTkLog.debug("__sub__") - if other is None: return str(self) + # if other is None: return str(self) if "" == self._bg != other._bg or \ + "" == self._fg != other._fg or \ "" == self._mod != other._mod : return '\033[0m'+self return str(self) + def modParam(self, *args, **kwargs): + if self._colorMod is None: return self + ret = self.copy() + ret._colorMod.setParam(*args, **kwargs) + return ret + + def mod(self, x , y): + if self._colorMod is None: return self + return self._colorMod.exec(x,y,self) + + def copy(self, modifier=True): + ret = _TTkColor() + ret._fg = self._fg + ret._bg = self._bg + ret._mod = self._mod + if modifier: + ret._colorMod = self._colorMod.copy() + return ret + +class _TTkColorModifier(): + def __init__(self, *args, **kwargs): pass + def setParam(self, *args, **kwargs): pass + def copy(self): return self + +class TTkColorGradient(_TTkColorModifier): + __slots__ = ('_increment', '_val', '_buffer') + _increment: int; _val: int + def __init__(self, *args, **kwargs): + _TTkColorModifier.__init__(self, *args, **kwargs) + self._increment = kwargs.get("increment",0) + self._val = 0 + self._buffer = {} + def setParam(self, *args, **kwargs): + self._val = kwargs.get("val",0) + def exec(self, x, y, color): + def _applyGradient(c): + if c == "": return c + multiplier = abs(self._val + y) + cc = c.split(';') + #TTkLog.debug("Eugenio "+c.replace('\033','')) + r = int(cc[2]) + self._increment * multiplier + g = int(cc[3]) + self._increment * multiplier + b = int(cc[4][:-1])+ self._increment * multiplier + if r>255: r=255 + if g>255: g=255 + if b>255: b=255 + if r<0: r=0 + if g<0: g=0 + if b<0: b=0 + return f"{cc[0]};{cc[1]};{r};{g};{b}m" + + bname = str(color) + # I made a buffer to keep all the gradient values to speed up the paint process + if bname not in self._buffer: + self._buffer[bname] = [None]*(256*2) + id = self._val + y - 256 + if self._buffer[bname][id] is not None: + return self._buffer[bname][id] + copy = color.copy(modifier=False) + copy._fg = _applyGradient(color._fg) + copy._bg = _applyGradient(color._bg) + self._buffer[bname][id] = copy + return self._buffer[bname][id] + + def copy(self): + return self + #ret = TTkColorGradient() + #ret._increment = self._increment + #ret._val = self._val + #return ret + + + class TTkColor(_TTkColor): - RST = _TTkColor('\033[0m') + RST = _TTkColor(fg='\033[0m') + + # Modifiers: + BOLD = _TTkColor(mod='\033[1m') + ITALIC = _TTkColor(mod='\033[3m') + UNDERLINE = _TTkColor(mod='\033[4m') + STRIKETROUGH = _TTkColor(mod='\033[9m') @staticmethod def fg(*args, **kwargs): - return _TTkColor(fg=TTkHelper.Color.fg(*args, **kwargs)) + mod = kwargs.get('modifier', None ) + if len(args) > 0: + color = args[0] + else: + color = kwargs.get('color', "" ) + return _TTkColor(fg=TTkHelper.Color.fg(color), colorMod=mod) + @staticmethod def bg(*args, **kwargs): - return _TTkColor(bg=TTkHelper.Color.bg(*args, **kwargs)) \ No newline at end of file + mod = kwargs.get('modifier', None ) + if len(args) > 0: + color = args[0] + else: + color = kwargs.get('color', "" ) + return _TTkColor(bg=TTkHelper.Color.bg(color), colorMod=mod) + diff --git a/TermTk/TTkCore/constant.py b/TermTk/TTkCore/constant.py index 9a97d8ba..9c842fab 100644 --- a/TermTk/TTkCore/constant.py +++ b/TermTk/TTkCore/constant.py @@ -50,5 +50,13 @@ class TTkConstant: WHEEL_Up = 0x00100000 # Wheel Up WHEEL_Down = 0x00200000 # Wheel Down + # Alignment + NONE = 0x0000 + LEFT_ALIGN = 0x0001 + RIGHT_ALIGN = 0x0002 + CENTER_ALIGN = 0x0003 + JUSTIFY = 0x0004 + + # Alias to TTkConstant class TTkK(TTkConstant): pass \ No newline at end of file diff --git a/TermTk/TTkCore/ttk.py b/TermTk/TTkCore/ttk.py index 43f5606d..b07502de 100644 --- a/TermTk/TTkCore/ttk.py +++ b/TermTk/TTkCore/ttk.py @@ -31,7 +31,7 @@ from TermTk.TTkCore.constant import TTkConstant, TTkK from TermTk.TTkCore.log import TTkLog from TermTk.TTkCore.cfg import * from TermTk.TTkCore.canvas import * -from TermTk.TTkWidgets.layout import * +from TermTk.TTkLayouts.layout import TTkLayout from TermTk.TTkWidgets.widget import * class TTkTimer(threading.Thread): @@ -59,21 +59,13 @@ class TTk(TTkWidget): mouse_events = None screen_events = None - MOUSE_EVENT = TTkK.MOUSE_EVENT - KEY_EVENT = TTkK.KEY_EVENT - SCREEN_EVENT = TTkK.SCREEN_EVENT - QUIT_EVENT = TTkK.QUIT_EVENT - TIME_EVENT = TTkK.TIME_EVENT - - HORIZONTAL = TTkConstant.HORIZONTAL - VERTICAL = TTkConstant.VERTICAL - def __init__(self, *args, **kwargs): TTkWidget.__init__(self, *args, **kwargs) self.events = queue.Queue() self.key_events = queue.Queue() self.mouse_events = queue.Queue() self.screen_events = queue.Queue() + self.setFocusPolicy(TTkWidget.ClickFocus) TTkHelper.registerRootCanvas(self._canvas) def mainloop(self): @@ -101,7 +93,7 @@ class TTk(TTkWidget): while self.running: # Main Loop evt = self.events.get() - if evt is TTk.MOUSE_EVENT: + if evt is TTkK.MOUSE_EVENT: mevt = self.mouse_events.get() focusWidget = TTkHelper.getFocus() if focusWidget is not None and mevt.evt != TTkK.Press: @@ -110,19 +102,19 @@ class TTk(TTkWidget): focusWidget.mouseEvent(nmevt) else: self.mouseEvent(mevt) - elif evt is TTk.KEY_EVENT: + elif evt is TTkK.KEY_EVENT: kevt = self.key_events.get() self.keyEvent(kevt) # TTkLog.info(f"Key Event: {kevt}") pass - elif evt is TTk.TIME_EVENT: + elif evt is TTkK.TIME_EVENT: TTkHelper.paintAll() self._timerEvent.set() pass - elif evt is TTk.SCREEN_EVENT: + elif evt is TTkK.SCREEN_EVENT: self.setGeometry(0,0,TTkGlbl.term_w,TTkGlbl.term_h) TTkLog.info(f"Resize: w:{TTkGlbl.term_w}, h:{TTkGlbl.term_h}") - elif evt is TTk.QUIT_EVENT: + elif evt is TTkK.QUIT_EVENT: TTkLog.debug(f"Quit.") break else: @@ -133,21 +125,21 @@ class TTk(TTkWidget): pass def _time_event(self): - self.events.put(TTk.TIME_EVENT) + self.events.put(TTkK.TIME_EVENT) def _win_resize_cb(self, width, height): TTkGlbl.term_w = int(width) TTkGlbl.term_h = int(height) - self.events.put(TTk.SCREEN_EVENT) + self.events.put(TTkK.SCREEN_EVENT) def _input_thread(self): def _inputCallback(kevt=None, mevt=None): if kevt is not None: self.key_events.put(kevt) - self.events.put(TTk.KEY_EVENT) + self.events.put(TTkK.KEY_EVENT) if mevt is not None: self.mouse_events.put(mevt) - self.events.put(TTk.MOUSE_EVENT) + self.events.put(TTkK.MOUSE_EVENT) return self.running # Start input key loop lbt.Input.get_key(_inputCallback) @@ -156,7 +148,7 @@ class TTk(TTkWidget): pass def quit(self): - self.events.put(TTk.QUIT_EVENT) + self.events.put(TTkK.QUIT_EVENT) self._timer.quit() self.running = False diff --git a/TermTk/TTkLayouts/__init__.py b/TermTk/TTkLayouts/__init__.py new file mode 100644 index 00000000..fead494d --- /dev/null +++ b/TermTk/TTkLayouts/__init__.py @@ -0,0 +1,3 @@ +from .layout import * +from .gridlayout import * +from .boxlayout import * \ No newline at end of file diff --git a/TermTk/TTkLayouts/boxlayout.py b/TermTk/TTkLayouts/boxlayout.py new file mode 100644 index 00000000..2945b3e2 --- /dev/null +++ b/TermTk/TTkLayouts/boxlayout.py @@ -0,0 +1,214 @@ +#!/usr/bin/env python3 + +# MIT License +# +# Copyright (c) 2021 Eugenio Parodi +# +# 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. + +''' + Layout System +''' + +from TermTk.TTkCore.log import TTkLog +from TermTk.TTkLayouts.layout import TTkLayout, TTkWidgetItem +from TermTk.TTkLayouts.gridlayout import TTkGridLayout + +class TTkHBoxLayout(TTkGridLayout): pass + +# class TTkHBoxLayout(TTkLayout): +# def __init__(self): +# TTkLayout.__init__(self) +# +# def minimumWidth(self) -> int: +# ''' process the widgets and get the min size ''' +# minw = 0 +# for item in self.children(): +# w1 = item.minimumWidth() +# minw += w1 +# return minw +# +# def minimumHeight(self) -> int: +# ''' process the widgets and get the min size ''' +# minh = TTkLayout.minimumHeight(self) +# for item in self.children(): +# h1 = item.minimumHeight() +# if h1 > minh : minh = h1 +# return minh +# +# def maximumWidth(self) -> int: +# ''' process the widgets and get the min size ''' +# maxw = 0 +# for item in self.children(): +# w1 = item.maximumWidth() +# maxw += w1 +# return maxw +# +# def maximumHeight(self) -> int: +# ''' process the widgets and get the min size ''' +# maxh = TTkLayout.maximumHeight(self) +# for item in self.children(): +# h1 = item.maximumHeight() +# if h1 < maxh : maxh = h1 +# return maxh +# +# def update(self): +# x, y, w, h = self.geometry() +# numWidgets = self.count() +# leftWidgets = numWidgets +# freeWidth = w +# newx, newy = x, y +# # Loop to check the resizable space +# for item in self.children(): +# item._sMax = False +# item._sMin = False +# iterate = True +# +# # Copy and Sort list of items based on the minsize +# sortedItems = sorted(self.children(), key=lambda item: item.minimumWidth()) +# +# while iterate and leftWidgets > 0: +# iterate = False +# for item in sortedItems: +# if item._sMax or item._sMin: continue +# sliceSize = freeWidth//leftWidgets +# maxs = item.maximumWidth() +# mins = item.minimumWidth() +# if sliceSize >= maxs: +# freeWidth -= maxs +# iterate = True +# item._sMax = True +# item._sMaxVal = maxs +# leftWidgets -= 1 +# elif sliceSize < mins: +# freeWidth -= mins +# leftWidgets -= 1 +# iterate = True +# item._sMin = True +# item._sMinVal = mins +# +# # loop and set the geometry of any item +# for item in self.children(): +# if item._sMax: +# item.setGeometry(newx, newy, item._sMaxVal, h) +# newx += item._sMaxVal +# elif item._sMin: +# item.setGeometry(newx, newy, item._sMinVal, h) +# newx += item._sMinVal +# else: +# sliceSize = freeWidth//leftWidgets +# item.setGeometry(newx, newy, sliceSize, h) +# newx += sliceSize +# freeWidth -= sliceSize +# leftWidgets -= 1 +# if isinstance(item, TTkWidgetItem) and not item.isEmpty(): +# item.widget().update() +# elif isinstance(item, TTkLayout): +# item.update() + + +class TTkVBoxLayout(TTkLayout): + def __init__(self): + TTkLayout.__init__(self) + + def minimumWidth(self) -> int: + ''' process the widgets and get the min size ''' + minw = TTkLayout.minimumWidth(self) + for item in self.children(): + w1 = item.minimumWidth() + if w1 > minw : minw = w1 + return minw + + def minimumHeight(self) -> int: + ''' process the widgets and get the min size ''' + minh = 0 + for item in self.children(): + h1 = item.minimumHeight() + minh += h1 + return minh + + def maximumWidth(self) -> int: + ''' process the widgets and get the min size ''' + maxw = TTkLayout.maximumWidth(self) + for item in self.children(): + w1 = item.maximumWidth() + if w1 < maxw : maxw = w1 + return maxw + + def maximumHeight(self) -> int: + ''' process the widgets and get the min size ''' + maxh = 0 + for item in self.children(): + h1 = item.maximumHeight() + maxh += h1 + return maxh + + def update(self): + x, y, w, h = self.geometry() + numWidgets = self.count() + leftWidgets = numWidgets + freeHeight = h + newx, newy = x, y + # Loop to check the resizable space + for item in self.children(): + item._sMax = False + item._sMin = False + iterate = True + + # Copy and Sort list of items based on the minsize + sortedItems = sorted(self.children(), key=lambda item: item.minimumHeight()) + + while iterate and leftWidgets > 0: + iterate = False + for item in sortedItems: + if item._sMax or item._sMin: continue + sliceSize = freeHeight//leftWidgets + maxs = item.maximumHeight() + mins = item.minimumHeight() + if sliceSize >= maxs: + freeHeight -= maxs + iterate = True + item._sMax = True + item._sMaxVal = maxs + leftWidgets -= 1 + elif sliceSize < mins: + freeHeight -= mins + leftWidgets -= 1 + iterate = True + item._sMin = True + item._sMinVal = mins + + # loop and set the geometry of any item + for item in self.children(): + if item._sMax: + item.setGeometry(newx, newy, w, item._sMaxVal) + newy += item._sMaxVal + elif item._sMin: + item.setGeometry(newx, newy, w, item._sMinVal) + newy += item._sMinVal + else: + sliceSize = freeHeight//leftWidgets + item.setGeometry(newx, newy, w, sliceSize) + newy += sliceSize + freeHeight -= sliceSize + leftWidgets -= 1 + if isinstance(item, TTkWidgetItem) and not item.isEmpty(): + item.widget().update() + elif isinstance(item, TTkLayout): + item.update() diff --git a/TermTk/TTkLayouts/gridlayout.py b/TermTk/TTkLayouts/gridlayout.py new file mode 100644 index 00000000..617f768d --- /dev/null +++ b/TermTk/TTkLayouts/gridlayout.py @@ -0,0 +1,283 @@ +#!/usr/bin/env python3 + +# MIT License +# +# Copyright (c) 2021 Eugenio Parodi +# +# 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. + +''' + Layout System +''' + +from TermTk.TTkCore.log import TTkLog +from TermTk.TTkLayouts.layout import TTkLayout, TTkWidgetItem + +class TTkGridWidgetItem(TTkWidgetItem): + __slots__ = ('_row','_col') + def __init__(self, *args, **kwargs): + TTkWidgetItem.__init__(self, args[0]) + self._row = kwargs.get('row') + self._col = kwargs.get('col') + +class TTkGridLayout(TTkLayout): + __slots__ = ('_gridItems','_columnMinWidth','_columnMinHeight') + def __init__(self, *args, **kwargs): + TTkLayout.__init__(self, args, kwargs) + self._gridItems = [[]] + self._columnMinWidth = kwargs.get('columnMinWidth',0) + self._columnMinHeight = kwargs.get('columnMinHeight',0) + + def _gridUsedsize(self): + rows = 1 + cols = 0 + for gridRow in range(len(self._gridItems)): + if rows < gridRow: + rows = gridRow + for gridCol in range(len(self._gridItems[0])): + if self._gridItems[gridRow][gridCol] is not None: + if cols < gridCol: + cols = gridCol + return (rows+1, cols+1) + + def _reshapeGrid(self, size): + rows = size[0] + cols = size[1] + + # remove extra rows + if rows < len(self._gridItems): + self._gridItems = self._gridItems[:rows] + elif rows > len(self._gridItems): + self._gridItems += [None]*(rows-len(self._gridItems)) + # remove extra cols + for gridRow in range(len(self._gridItems)): + if self._gridItems[gridRow] is None: + self._gridItems[gridRow] = [None]*(cols+1) + continue + sizeRow = len(self._gridItems[gridRow]) + if cols < sizeRow: + self._gridItems[gridRow] = self._gridItems[gridRow][:cols] + elif cols > sizeRow: + self._gridItems[gridRow] += [None]*(cols-sizeRow) + + # addWidget(self, widget, row, col) + def addWidget(self, *args, **kwargs): + widget = args[0] + if len(args) == 3: + row = args[1] + col = args[2] + else: + # Append The widget at the end + row = 0 + col = len(self._gridItems[0]) + + #retrieve the max col/rows to reshape the grid + maxrow = row + maxcol = col + for item in self.children(): + if maxrow < item._row: maxrow = item._row + if maxcol < item._col: maxcol = item._col + # reshape the gridItems + maxrow += 1 + maxcol += 1 + self._reshapeGrid(size=(maxrow,maxcol)) + + if self._gridItems[row][col] is not None: + # TODO: Handle the LayoutItem + self.removeWidget(self._gridItems[row][col]) + + item = TTkGridWidgetItem(widget, row=row, col=col) + self._gridItems[row][col] = item + self.addItem(item) + + def removeWidget(self, widget): + TTkLayout.removeWidget(self, widget) + for gridRow in range(len(self._gridItems)): + for gridCol in range(len(self._gridItems[0])): + if self._gridItems[gridRow][gridCol] is not None and \ + self._gridItems[gridRow][gridCol].widget() == widget: + self._gridItems[gridRow][gridCol] = None + self._reshapeGrid(self._gridUsedsize()) + + def itemAtPosition(self, row: int, col: int): + if row >= len(self._gridItems) or \ + col >= len(self._gridItems[0]): + return None + return self._gridItems[row][col] + + def minimumColWidth(self, gridCol: int) -> int: + colw = 0 + anyItem = False + for gridRow in range(len(self._gridItems)): + item = self._gridItems[gridRow][gridCol] + if item is not None: + anyItem = True + w = item.minimumWidth() + if colw < w: + colw = w + if not anyItem: + return self._columnMinWidth + return colw + + def minimumRowHeight(self, gridRow: int): + rowh = 0 + anyItem = False + for item in self._gridItems[gridRow]: + if item is not None: + anyItem = True + h = item.minimumHeight() + if rowh < h: + rowh = h + if not anyItem: + return self._columnMinHeight + return rowh + + def maximumColWidth(self, gridCol: int) -> int: + colw = 0x10000 + anyItem = False + for gridRow in range(len(self._gridItems)): + item = self._gridItems[gridRow][gridCol] + if item is not None: + anyItem = True + w = item.maximumWidth() + if colw > w: + colw = w + if not anyItem: + return self._columnMinWidth + return colw + + def maximumRowHeight(self, gridRow: int): + rowh = 0x10000 + anyItem = False + for item in self._gridItems[gridRow]: + if item is not None: + anyItem = True + h = item.maximumHeight() + if rowh > h: + rowh = h + if not anyItem: + return self._columnMinHeight + return rowh + + def minimumWidth(self) -> int: + ''' process the widgets and get the min size ''' + minw = 0 + for gridCol in range(len(self._gridItems[0])): + minw += self.minimumColWidth(gridCol) + return minw + + def minimumHeight(self) -> int: + ''' process the widgets and get the min size ''' + minh = 0 + for gridRow in range(len(self._gridItems)): + minh += self.minimumRowHeight(gridRow) + return minh + + def maximumWidth(self) -> int: + ''' process the widgets and get the min size ''' + maxw = 0 + for gridCol in range(len(self._gridItems[0])): + maxw += self.maximumColWidth(gridCol) + return maxw + + def maximumHeight(self) -> int: + ''' process the widgets and get the min size ''' + maxh = 0 + for gridRow in range(len(self._gridItems)): + maxh += self.maximumRowHeight(gridRow) + return maxh + + + def update(self): + x, y, w, h = self.geometry() + newx, newy = x, y + + # Sorted List of minimum heights + # min max val + # content IDs 0 1 2 3 + sortedHeights = [ [i, self.minimumRowHeight(i), self.maximumRowHeight(i), -1] for i in range(len(self._gridItems)) ] + sortedWidths = [ [i, self.minimumColWidth(i), self.maximumColWidth(i), -1] for i in range(len(self._gridItems[0])) ] + sortedHeights = sorted(sortedHeights, key=lambda h: h[1]) + sortedWidths = sorted(sortedWidths, key=lambda w: w[1]) + + minWidth = 0 + minHeight = 0 + for i in sortedWidths: minWidth += i[1] + for i in sortedHeights: minHeight += i[1] + + if h < minHeight: h = minHeight + if w < minWidth: w = minWidth + + # TTkLog.debug(f"w,h:({w,h}) mh:{minHeight} sh:{sortedHeights}") + # TTkLog.debug(f"w,h:({w,h}) mw:{minWidth} sw:{sortedWidths}") + + def parseSizes(sizes, space, out): + iterate = True + freeSpace = space + leftSlots = len(sizes) + while iterate and leftSlots > 0: + iterate = False + for item in sizes: + if item[3] != -1: continue + if freeSpace < 0: freeSpace=0 + sliceSize = freeSpace//leftSlots + mins = item[1] + maxs = item[2] + if sliceSize >= maxs: + iterate = True + freeSpace -= maxs + leftSlots -= 1 + item[3] = maxs + elif sliceSize < mins: + iterate = True + freeSpace -= mins + leftSlots -= 1 + item[3] = mins + # Push the sizes + for item in sizes: + out[item[0]] = [0,item[3]] + if item[3] == -1: + sliceSize = freeSpace//leftSlots + out[item[0]] = [0,sliceSize] + freeSpace -= sliceSize + leftSlots -= 1 + + vertSizes = [None]*len(sortedHeights) + horSizes = [None]*len(sortedWidths) + parseSizes(sortedHeights,h, vertSizes) + parseSizes(sortedWidths, w, horSizes) + + for i in horSizes: + i[0] = newx + newx += i[1] + for i in vertSizes: + i[0] = newy + newy += i[1] + + # loop and set the geometry of any item + for item in self.children(): + col = item._col + row = item._row + item.setGeometry( + horSizes[col][0], vertSizes[row][0] , + horSizes[col][1], vertSizes[row][1] ) + if isinstance(item, TTkWidgetItem) and not item.isEmpty(): + item.widget().update() + elif isinstance(item, TTkLayout): + item.update() diff --git a/TermTk/TTkLayouts/layout.py b/TermTk/TTkLayouts/layout.py new file mode 100644 index 00000000..6fb03fe9 --- /dev/null +++ b/TermTk/TTkLayouts/layout.py @@ -0,0 +1,544 @@ +#!/usr/bin/env python3 + +# MIT License +# +# Copyright (c) 2021 Eugenio Parodi +# +# 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. + +''' + Layout System +''' + +from TermTk.TTkCore.log import TTkLog + +class TTkLayoutItem: + __slots__ = ('_x', '_y', '_w', '_h', '_sMax', '_sMaxVal', '_sMin', '_sMinVal') + def __init__(self, *args, **kwargs): + self._x, self._y = 0, 0 + self._w, self._h = 0, 0 + self._sMax, self._sMin = False, False + self._sMaxVal, self._sMinVal = 0, 0 + pass + def minimumSize(self): + return self.minimumWidth(), self.minimumHeight() + def minimumHeight(self) -> int: return 0 + def minimumWidth(self) -> int: return 0 + + def maximumSize(self): + return self.maximumWidth(), self.maximumHeight() + def maximumHeight(self) -> int: return 0x80000000 + def maximumWidth(self) -> int: return 0x80000000 + + def geometry(self): + return self._x, self._y, self._w, self._h + + def setGeometry(self, x, y, w, h): + self._x = x + self._y = y + self._w = w + self._h = h + + +class TTkLayout(TTkLayoutItem): + __slots__ = ('_items', '_zSortedItems', '_parent') + def __init__(self, *args, **kwargs): + TTkLayoutItem.__init__(self, args, kwargs) + self._items = [] + self._zSortedItems = [] + self._parent = None + pass + + def children(self): + return self._items + + def count(self): + return len(self._items) + + def itemAt(self, index): + if index < len(self._items): + return self._items[index] + return 0 + + def setParent(self, parent): + self._parent = parent + + def parentWidget(self): + return self._parent + + def _zSortItems(self): + self._zSortedItems = sorted(self._items, key=lambda item: item.z) + + @property + def zSortedItems(self): return self._zSortedItems + + def addItem(self, item): + self._items.append(item) + self._zSortItems() + + def addWidget(self, widget): + if widget.parentWidget() is not None: + widget.parentWidget().removeWidget(self) + self.addItem(TTkWidgetItem(widget)) + + def removeWidget(self, widget): + for i in self._items: + if i.widget() == widget: + self._items.remove(i) + return + self._zSortItems() + + def raiseWidget(self, widget): + maxz = 0 + item = None + for i in self._items: + if i.widget() == widget: + item = i + elif i.z >= maxz: + maxz=i.z+1 + item.z = maxz + self._zSortItems() + + + def lowerWidget(self, widget): + minz = 0 + item = None + for i in self._items: + if i.widget() == widget: + item = i + elif i.z <= minz: + minz=i.z-1 + item.z = minz + self._zSortItems() + + def update(self): + for i in self.children(): + if isinstance(i, TTkWidgetItem) and not i.isEmpty(): + i.widget().update() + # TODO: Have a look at this: + # i.getCanvas().top() + elif isinstance(i, TTkLayout): + i.update() + +class TTkWidgetItem(TTkLayoutItem): + slots = ('_widget','_z') + def __init__(self, widget, z=0): + TTkLayoutItem.__init__(self) + self._widget = widget + self.z = z + + def widget(self): + return self._widget + + def isEmpty(self): return self._widget is None + + def minimumSize(self) -> int: return self._widget.minimumSize() + def minimumHeight(self)-> int: return self._widget.minimumHeight() + def minimumWidth(self) -> int: return self._widget.minimumWidth() + def maximumSize(self) -> int: return self._widget.maximumSize() + def maximumHeight(self)-> int: return self._widget.maximumHeight() + def maximumWidth(self) -> int: return self._widget.maximumWidth() + + def geometry(self): return self._widget.geometry() + + def setGeometry(self, x, y, w, h): + self._widget.setGeometry(x, y, w, h) + + @property + def z(self): return self._z + @z.setter + def z(self, z): self._z = z + + + +#class TTkHBoxLayout(TTkLayout): +# def __init__(self): +# TTkLayout.__init__(self) +# +# def minimumWidth(self) -> int: +# ''' process the widgets and get the min size ''' +# minw = 0 +# for item in self.children(): +# w1 = item.minimumWidth() +# minw += w1 +# return minw +# +# def minimumHeight(self) -> int: +# ''' process the widgets and get the min size ''' +# minh = TTkLayout.minimumHeight(self) +# for item in self.children(): +# h1 = item.minimumHeight() +# if h1 > minh : minh = h1 +# return minh +# +# def maximumWidth(self) -> int: +# ''' process the widgets and get the min size ''' +# maxw = 0 +# for item in self.children(): +# w1 = item.maximumWidth() +# maxw += w1 +# return maxw +# +# def maximumHeight(self) -> int: +# ''' process the widgets and get the min size ''' +# maxh = TTkLayout.maximumHeight(self) +# for item in self.children(): +# h1 = item.maximumHeight() +# if h1 < maxh : maxh = h1 +# return maxh +# +# def update(self): +# x, y, w, h = self.geometry() +# numWidgets = self.count() +# leftWidgets = numWidgets +# freeWidth = w +# newx, newy = x, y +# # Loop to check the resizable space +# for item in self.children(): +# item._sMax = False +# item._sMin = False +# iterate = True +# +# # Copy and Sort list of items based on the minsize +# sortedItems = sorted(self.children(), key=lambda item: item.minimumWidth()) +# +# while iterate and leftWidgets > 0: +# iterate = False +# for item in sortedItems: +# if item._sMax or item._sMin: continue +# sliceSize = freeWidth//leftWidgets +# maxs = item.maximumWidth() +# mins = item.minimumWidth() +# if sliceSize >= maxs: +# freeWidth -= maxs +# iterate = True +# item._sMax = True +# item._sMaxVal = maxs +# leftWidgets -= 1 +# elif sliceSize < mins: +# freeWidth -= mins +# leftWidgets -= 1 +# iterate = True +# item._sMin = True +# item._sMinVal = mins +# +# # loop and set the geometry of any item +# for item in self.children(): +# if item._sMax: +# item.setGeometry(newx, newy, item._sMaxVal, h) +# newx += item._sMaxVal +# elif item._sMin: +# item.setGeometry(newx, newy, item._sMinVal, h) +# newx += item._sMinVal +# else: +# sliceSize = freeWidth//leftWidgets +# item.setGeometry(newx, newy, sliceSize, h) +# newx += sliceSize +# freeWidth -= sliceSize +# leftWidgets -= 1 +# if isinstance(item, TTkWidgetItem) and not item.isEmpty(): +# item.widget().update() +# elif isinstance(item, TTkLayout): +# item.update() +# +# +#class TTkVBoxLayout(TTkLayout): +# def __init__(self): +# TTkLayout.__init__(self) +# +# def minimumWidth(self) -> int: +# ''' process the widgets and get the min size ''' +# minw = TTkLayout.minimumWidth(self) +# for item in self.children(): +# w1 = item.minimumWidth() +# if w1 > minw : minw = w1 +# return minw +# +# def minimumHeight(self) -> int: +# ''' process the widgets and get the min size ''' +# minh = 0 +# for item in self.children(): +# h1 = item.minimumHeight() +# minh += h1 +# return minh +# +# def maximumWidth(self) -> int: +# ''' process the widgets and get the min size ''' +# maxw = TTkLayout.maximumWidth(self) +# for item in self.children(): +# w1 = item.maximumWidth() +# if w1 < maxw : maxw = w1 +# return maxw +# +# def maximumHeight(self) -> int: +# ''' process the widgets and get the min size ''' +# maxh = 0 +# for item in self.children(): +# h1 = item.maximumHeight() +# maxh += h1 +# return maxh +# +# def update(self): +# x, y, w, h = self.geometry() +# numWidgets = self.count() +# leftWidgets = numWidgets +# freeHeight = h +# newx, newy = x, y +# # Loop to check the resizable space +# for item in self.children(): +# item._sMax = False +# item._sMin = False +# iterate = True +# +# # Copy and Sort list of items based on the minsize +# sortedItems = sorted(self.children(), key=lambda item: item.minimumHeight()) +# +# while iterate and leftWidgets > 0: +# iterate = False +# for item in sortedItems: +# if item._sMax or item._sMin: continue +# sliceSize = freeHeight//leftWidgets +# maxs = item.maximumHeight() +# mins = item.minimumHeight() +# if sliceSize >= maxs: +# freeHeight -= maxs +# iterate = True +# item._sMax = True +# item._sMaxVal = maxs +# leftWidgets -= 1 +# elif sliceSize < mins: +# freeHeight -= mins +# leftWidgets -= 1 +# iterate = True +# item._sMin = True +# item._sMinVal = mins +# +# # loop and set the geometry of any item +# for item in self.children(): +# if item._sMax: +# item.setGeometry(newx, newy, w, item._sMaxVal) +# newy += item._sMaxVal +# elif item._sMin: +# item.setGeometry(newx, newy, w, item._sMinVal) +# newy += item._sMinVal +# else: +# sliceSize = freeHeight//leftWidgets +# item.setGeometry(newx, newy, w, sliceSize) +# newy += sliceSize +# freeHeight -= sliceSize +# leftWidgets -= 1 +# if isinstance(item, TTkWidgetItem) and not item.isEmpty(): +# item.widget().update() +# elif isinstance(item, TTkLayout): +# item.update() +# +#class TTkGridWidgetItem(TTkWidgetItem): +# __slots__ = ('_row','_col') +# def __init__(self, *args, **kwargs): +# TTkWidgetItem.__init__(self, args[0]) +# self._row = kwargs.get('row') +# self._col = kwargs.get('col') +# +#class TTkGridLayout(TTkLayout): +# __slots__ = ('_gridItems') +# def __init__(self): +# TTkLayout.__init__(self) +# self._gridItems = [[]] +# +# # addWidget(self, widget, row, col) +# def addWidget(self, *args, **kwargs): +# if len(args) == 3: +# row = args[1] +# col = args[2] +# else: +# # Append The widget at the end +# row = 0 +# col = len(self._gridItems[0]) +# #retrieve the max col/rows to reshape the grid +# maxrow = row + 1 +# maxcol = col + 1 +# for item in self.children(): +# if maxrow < item._row: maxrow = item._row + 1 +# if maxcol < item._col: maxcol = item._col + 1 +# # reshape the gridItems +# if len(self._gridItems) > maxrow: +# self._gridItems = self._gridItems[:maxrow] +# if len(self._gridItems) < maxrow: +# self._gridItems += [[]]*(maxrow-len(self._gridItems)) +# for gridRow in range(len(self._gridItems)): +# if len(self._gridItems[gridRow]) > maxcol: +# self._gridItems[gridRow] = self._gridItems[gridRow][:maxcol] +# if len(self._gridItems[gridRow]) < maxcol: +# self._gridItems[gridRow] += [None]*(maxcol-len(self._gridItems[gridRow])) +# if self._gridItems[row][col] is not None: +# # TODO: Handle the LayoutItem +# self.removeWidget(self._gridItems[row][col]) +# +# item = TTkGridWidgetItem(args[0], row=row, col=col) +# self._gridItems[row][col] = item +# self.addItem(item) +# +# def itemAtPosition(self, row: int, col: int): +# if row >= len(self._gridItems) or \ +# col >= len(self._gridItems[0]): +# return None +# return self._gridItems[row][col] +# +# def minimumColWidth(self, gridCol: int) -> int: +# colw = 0 +# for gridRow in range(len(self._gridItems)): +# item = self._gridItems[gridRow][gridCol] +# if item is not None: +# w = item.minimumWidth() +# if colw < w: +# colw = w +# return colw +# +# def minimumRowHeight(self, gridRow: int): +# rowh = 0 +# for item in self._gridItems[gridRow]: +# if item is not None: +# h = item.minimumHeight() +# if rowh < h: +# rowh = h +# return rowh +# +# def maximumColWidth(self, gridCol: int) -> int: +# colw = 0x10000 +# for gridRow in range(len(self._gridItems)): +# item = self._gridItems[gridRow][gridCol] +# if item is not None: +# w = item.maximumWidth() +# if colw > w: +# colw = w +# return colw +# +# def maximumRowHeight(self, gridRow: int): +# rowh = 0x10000 +# for item in self._gridItems[gridRow]: +# if item is not None: +# h = item.maximumHeight() +# if rowh > h: +# rowh = h +# return rowh +# +# def minimumWidth(self) -> int: +# ''' process the widgets and get the min size ''' +# minw = 0 +# for gridCol in range(len(self._gridItems[0])): +# minw += self.minimumColWidth(gridCol) +# return minw +# +# def minimumHeight(self) -> int: +# ''' process the widgets and get the min size ''' +# minh = 0 +# for gridRow in range(len(self._gridItems)): +# minh += self.minimumRowHeight(gridRow) +# return minh +# +# def maximumWidth(self) -> int: +# ''' process the widgets and get the min size ''' +# maxw = 0 +# for gridCol in range(len(self._gridItems[0])): +# maxw += self.maximumColWidth(gridCol) +# return maxw +# +# def maximumHeight(self) -> int: +# ''' process the widgets and get the min size ''' +# maxh = 0 +# for gridRow in range(len(self._gridItems)): +# maxh += self.maximumRowHeight(gridRow) +# return maxh +# +# +# def update(self): +# x, y, w, h = self.geometry() +# newx, newy = x, y +# TTkLog.debug(f"1) x:{x} y:{y} w:{w} h:{h}") +# +# # Sorted List of minimum heights +# # 0 1 2 3 +# sortedHeights = [ [i,self.minimumRowHeight(i),self.maximumRowHeight(i),-1] for i in range(len(self._gridItems)) ] +# sortedWidths = [ [i,self.minimumColWidth(i), self.maximumColWidth(i), -1] for i in range(len(self._gridItems[0])) ] +# sortedHeights = sorted(sortedHeights, key=lambda h: h[1]) +# sortedWidths = sorted(sortedWidths, key=lambda w: w[1]) +# +# minWidth = 0 +# minHeight = 0 +# for i in sortedWidths: minWidth += i[1] +# for i in sortedHeights: minHeight += i[1] +# +# if h < minHeight: h = minHeight +# if w < minWidth: w = minWidth +# TTkLog.debug(f"2) x:{x} y:{y} w:{w} h:{h} - {self._gridItems}") +# +# def parseSizes(sizes, space, out): +# iterate = True +# freeSpace = space +# leftSlots = len(sizes)+1 +# while iterate and leftSlots > 0: +# iterate = False +# for item in sizes: +# if item[3] != -1: continue +# if freeSpace < 0: freeSpace=0 +# sliceSize = freeSpace//leftSlots +# mins = item[1] +# maxs = item[2] +# if sliceSize >= maxs: +# iterate = True +# freeSpace -= maxs +# leftSlots -= 1 +# item[3] = maxs +# elif sliceSize < mins: +# iterate = True +# freeSpace -= mins +# leftSlots -= 1 +# item[3] = mins +# # Push the sizes +# TTkLog.debug(f"2) sizes:{sizes}, space:{space}, fs:{freeSpace}, ls:{leftSlots}") +# for item in sizes: +# out[item[0]] = [0,item[3]] +# if item[3] != -1: +# sliceSize = freeSpace//leftSlots +# out[item[0]] = [0,sliceSize] +# freeSpace -= sliceSize +# leftSlots -= 1 +# +# vertSizes = [None]*len(sortedHeights) +# horSizes = [None]*len(sortedWidths) +# parseSizes(sortedHeights,h, vertSizes) +# parseSizes(sortedWidths, w, horSizes) +# +# for i in horSizes: +# i[0] = newx +# newx += i[1] +# for i in vertSizes: +# i[0] = newy +# newy += i[1] +# +# # loop and set the geometry of any item +# for item in self.children(): +# col = item._col +# row = item._row +# item.setGeometry(horSizes[col][0], vertSizes[row][0], 10, 3) +# if isinstance(item, TTkWidgetItem) and not item.isEmpty(): +# item.widget().update() +# elif isinstance(item, TTkLayout): +# item.update() +# \ No newline at end of file diff --git a/TermTk/TTkWidgets/__init__.py b/TermTk/TTkWidgets/__init__.py index 50103619..7bed29fc 100644 --- a/TermTk/TTkWidgets/__init__.py +++ b/TermTk/TTkWidgets/__init__.py @@ -1,5 +1,4 @@ from .widget import * -from .layout import * from .spacer import * from .frame import * from .button import * diff --git a/TermTk/TTkWidgets/button.py b/TermTk/TTkWidgets/button.py index a8b44b39..bcc9ee4e 100644 --- a/TermTk/TTkWidgets/button.py +++ b/TermTk/TTkWidgets/button.py @@ -46,10 +46,13 @@ class TTkButton(TTkWidget): def paintEvent(self): if self._pressed: - borderColor = TTkColor.fg("#00ffff") - textColor = TTkColor.fg("#0000ff") + # borderColor = TTkColor.fg("#00ffff") + # textColor = TTkColor.fg("#0000ff") + borderColor = TTkColor.fg("#ffff88")+TTkColor.BOLD + textColor = TTkColor.fg("#00ff00")+TTkColor.BOLD else: - borderColor = TTkColor.fg("#ffff00") + # borderColor = TTkColor.fg("#ffff00") + borderColor = TTkColor.RST textColor = TTkColor.fg("#00ff00") self._canvas.drawText(pos=(1,1), color=textColor ,text=self.text) if self._border: diff --git a/TermTk/TTkWidgets/layout.py b/TermTk/TTkWidgets/layout.py deleted file mode 100644 index 1de57305..00000000 --- a/TermTk/TTkWidgets/layout.py +++ /dev/null @@ -1,347 +0,0 @@ -#!/usr/bin/env python3 - -# MIT License -# -# Copyright (c) 2021 Eugenio Parodi -# -# 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. - -''' - Layout System -''' - -from TermTk.TTkCore.log import TTkLog - -class TTkLayoutItem: - __slots__ = ('_x', '_y', '_w', '_h', '_sMax', '_sMaxVal', '_sMin', '_sMinVal') - def __init__(self): - self._x, self._y = 0, 0 - self._w, self._h = 0, 0 - self._sMax, self._sMin = False, False - self._sMaxVal, self._sMinVal = 0, 0 - pass - def minimumSize(self): - return self.minimumWidth(), self.minimumHeight() - def minimumHeight(self): return 0 - def minimumWidth(self): return 0 - - def maximumSize(self): - return self.maximumWidth(), self.maximumHeight() - def maximumHeight(self): return 0x80000000 - def maximumWidth(self): return 0x80000000 - - def geometry(self): - return self._x, self._y, self._w, self._h - - def setGeometry(self, x, y, w, h): - self._x = x - self._y = y - self._w = w - self._h = h - - -class TTkLayout(TTkLayoutItem): - __slots__ = ('_items', '_zSortedItems', '_parent') - def __init__(self): - TTkLayoutItem.__init__(self) - self._items = [] - self._zSortedItems = [] - self._parent = None - pass - - def children(self): - return self._items - - def count(self): - return len(self._items) - - def itemAt(self, index): - if index < len(self._items): - return self._items[index] - return 0 - - def setParent(self, parent): - self._parent = parent - - def parentWidget(self): - return self._parent - - def _zSortItems(self): - self._zSortedItems = sorted(self._items, key=lambda item: item.z) - - @property - def zSortedItems(self): return self._zSortedItems - - def addItem(self, item): - self._items.append(item) - self._zSortItems() - - def addWidget(self, widget): - self.addItem(TTkWidgetItem(widget)) - - def removeWidget(self, widget): - for i in self._items: - if i.widget() == widget: - self._items.remove(i) - return - self._zSortItems() - - def raiseWidget(self, widget): - maxz = 0 - item = None - for i in self._items: - if i.widget() == widget: - item = i - elif i.z >= maxz: - maxz=i.z+1 - item.z = maxz - self._zSortItems() - - - def lowerWidget(self, widget): - minz = 0 - item = None - for i in self._items: - if i.widget() == widget: - item = i - elif i.z <= minz: - minz=i.z-1 - item.z = minz - self._zSortItems() - - def update(self): - for i in self.children(): - if isinstance(i, TTkWidgetItem) and not i.isEmpty(): - i.widget().update() - # TODO: Have a look at this: - # i.getCanvas().top() - elif isinstance(i, TTkLayout): - i.update() - -class TTkWidgetItem(TTkLayoutItem): - slots = ('_widget','_z') - def __init__(self, widget, z=0): - TTkLayoutItem.__init__(self) - self._widget = widget - self.z = z - - def widget(self): - return self._widget - - def isEmpty(self): return self._widget is None - - def minimumSize(self): return self._widget.minimumSize() - def minimumHeight(self): return self._widget.minimumHeight() - def minimumWidth(self): return self._widget.minimumWidth() - def maximumSize(self): return self._widget.maximumSize() - def maximumHeight(self): return self._widget.maximumHeight() - def maximumWidth(self): return self._widget.maximumWidth() - - def geometry(self): return self._widget.geometry() - - def setGeometry(self, x, y, w, h): - self._widget.setGeometry(x, y, w, h) - - @property - def z(self): return self._z - @z.setter - def z(self, z): self._z = z - - - -class TTkHBoxLayout(TTkLayout): - def __init__(self): - TTkLayout.__init__(self) - - def minimumWidth(self): - ''' process the widgets and get the min size ''' - minw = 0 - for item in self.children(): - w1 = item.minimumWidth() - minw += w1 - return minw - - def minimumHeight(self): - ''' process the widgets and get the min size ''' - minh = TTkLayout.minimumHeight(self) - for item in self.children(): - h1 = item.minimumHeight() - if h1 > minh : minh = h1 - return minh - - def maximumWidth(self): - ''' process the widgets and get the min size ''' - maxw = 0 - for item in self.children(): - w1 = item.maximumWidth() - maxw += w1 - return maxw - - def maximumHeight(self): - ''' process the widgets and get the min size ''' - maxh = TTkLayout.maximumHeight(self) - for item in self.children(): - h1 = item.maximumHeight() - if h1 < maxh : maxh = h1 - return maxh - - def update(self): - x, y, w, h = self.geometry() - numWidgets = self.count() - leftWidgets = numWidgets - freeWidth = w - newx, newy = x, y - # Loop to check the resizable space - for item in self.children(): - item._sMax = False - item._sMin = False - iterate = True - - # Copy and Sort list of items based on the minsize - sortedItems = sorted(self.children(), key=lambda item: item.minimumWidth()) - - while iterate and leftWidgets > 0: - iterate = False - for item in sortedItems: - if item._sMax or item._sMin: continue - sliceSize = freeWidth//leftWidgets - maxs = item.maximumWidth() - mins = item.minimumWidth() - if sliceSize >= maxs: - freeWidth -= maxs - iterate = True - item._sMax = True - item._sMaxVal = maxs - leftWidgets -= 1 - elif sliceSize < mins: - freeWidth -= mins - leftWidgets -= 1 - iterate = True - item._sMin = True - item._sMinVal = mins - - # loop and set the geometry of any item - for item in self.children(): - if item._sMax: - item.setGeometry(newx, newy, item._sMaxVal, h) - newx += item._sMaxVal - elif item._sMin: - item.setGeometry(newx, newy, item._sMinVal, h) - newx += item._sMinVal - else: - sliceSize = freeWidth//leftWidgets - item.setGeometry(newx, newy, sliceSize, h) - newx += sliceSize - freeWidth -= sliceSize - leftWidgets -= 1 - if isinstance(item, TTkWidgetItem) and not item.isEmpty(): - item.widget().update() - elif isinstance(item, TTkLayout): - item.update() - - -class TTkVBoxLayout(TTkLayout): - def __init__(self): - TTkLayout.__init__(self) - - def minimumWidth(self): - ''' process the widgets and get the min size ''' - minw = TTkLayout.minimumWidth(self) - for item in self.children(): - w1 = item.minimumWidth() - if w1 > minw : minw = w1 - return minw - - def minimumHeight(self): - ''' process the widgets and get the min size ''' - minh = 0 - for item in self.children(): - h1 = item.minimumHeight() - minh += h1 - return minh - - def maximumWidth(self): - ''' process the widgets and get the min size ''' - maxw = TTkLayout.maximumWidth(self) - for item in self.children(): - w1 = item.maximumWidth() - if w1 < maxw : maxw = w1 - return maxw - - def maximumHeight(self): - ''' process the widgets and get the min size ''' - maxh = 0 - for item in self.children(): - h1 = item.maximumHeight() - maxh += h1 - return maxh - - def update(self): - x, y, w, h = self.geometry() - numWidgets = self.count() - leftWidgets = numWidgets - freeHeight = h - newx, newy = x, y - # Loop to check the resizable space - for item in self.children(): - item._sMax = False - item._sMin = False - iterate = True - - # Copy and Sort list of items based on the minsize - sortedItems = sorted(self.children(), key=lambda item: item.minimumHeight()) - - while iterate and leftWidgets > 0: - iterate = False - for item in sortedItems: - if item._sMax or item._sMin: continue - sliceSize = freeHeight//leftWidgets - maxs = item.maximumHeight() - mins = item.minimumHeight() - if sliceSize >= maxs: - freeHeight -= maxs - iterate = True - item._sMax = True - item._sMaxVal = maxs - leftWidgets -= 1 - elif sliceSize < mins: - freeHeight -= mins - leftWidgets -= 1 - iterate = True - item._sMin = True - item._sMinVal = mins - - # loop and set the geometry of any item - for item in self.children(): - if item._sMax: - item.setGeometry(newx, newy, w, item._sMaxVal) - newy += item._sMaxVal - elif item._sMin: - item.setGeometry(newx, newy, w, item._sMinVal) - newy += item._sMinVal - else: - sliceSize = freeHeight//leftWidgets - item.setGeometry(newx, newy, w, sliceSize) - newy += sliceSize - freeHeight -= sliceSize - leftWidgets -= 1 - if isinstance(item, TTkWidgetItem) and not item.isEmpty(): - item.widget().update() - elif isinstance(item, TTkLayout): - item.update() diff --git a/TermTk/TTkWidgets/scrollbar.py b/TermTk/TTkWidgets/scrollbar.py index ec0665e8..d3dc6c1c 100644 --- a/TermTk/TTkWidgets/scrollbar.py +++ b/TermTk/TTkWidgets/scrollbar.py @@ -229,6 +229,7 @@ class TTkScrollBar(TTkWidget): def value(self, v): if v > self._maximum: v = self._maximum if v < self._minimum: v = self._minimum + if self._value == v: return self._value = v self.valueChanged.emit(v) self.update() diff --git a/TermTk/TTkWidgets/splitter.py b/TermTk/TTkWidgets/splitter.py index 87c2902a..64c470c2 100644 --- a/TermTk/TTkWidgets/splitter.py +++ b/TermTk/TTkWidgets/splitter.py @@ -36,7 +36,7 @@ class TTkSplitter(TTkFrame): self._name = kwargs.get('name' , 'TTkSplitter' ) self._widgets = [] self._splitters = [] - self._orientation = kwargs.get('orientation' , TTk.HORIZONTAL ) + self._orientation = kwargs.get('orientation' , TTkK.HORIZONTAL ) def addWidget(self, widget): # NOTE: Check with the max/min size if the new widget can fit @@ -46,7 +46,7 @@ class TTkSplitter(TTkFrame): def _rearrange(self): w, h = self.size() - if self._orientation == TTk.HORIZONTAL: + if self._orientation == TTkK.HORIZONTAL: pass else: pass diff --git a/TermTk/TTkWidgets/table.py b/TermTk/TTkWidgets/table.py index 156227fa..4a73d783 100644 --- a/TermTk/TTkWidgets/table.py +++ b/TermTk/TTkWidgets/table.py @@ -26,8 +26,8 @@ from TermTk.TTkCore.constant import TTkK from TermTk.TTkCore.log import TTkLog from TermTk.TTkCore.signal import pyTTkSlot, pyTTkSignal from TermTk.TTkCore.color import TTkColor +from TermTk.TTkLayouts.boxlayout import TTkHBoxLayout from TermTk.TTkWidgets.widget import TTkWidget -from TermTk.TTkWidgets.layout import * from TermTk.TTkWidgets.spacer import TTkSpacer from TermTk.TTkWidgets.scrollbar import TTkScrollBar @@ -36,30 +36,59 @@ from TermTk.TTkWidgets.scrollbar import TTkScrollBar ''' class TTkTable(TTkWidget): - __slots__ = ('_hlayout','_vscroller','_columns','_tableData', '_moveTo') + __slots__ = ('_hlayout','_vscroller', '_header', '_alignments', '_headerColor', '_columns', '_columnColors', '_selectColor', '_tableData', '_moveTo', '_selected') def __init__(self, *args, **kwargs): self._vscroller = None # This is required to avoid crash int he vScroller Tuning self._moveTo = 0 self._tableData = [] TTkWidget.__init__(self, *args, **kwargs) self._name = kwargs.get('name' , 'TTkTable' ) - self._columns = kwargs.get('columns' , 'TTkTable' ) + self._columns = kwargs.get('columns' , [] ) + self._header = [""]*len(self._columns) + self._alignments = [TTkK.NONE]*len(self._columns) + self._columnColors = kwargs.get('columnColors' , [TTkColor.RST]*len(self._columns) ) + self._selectColor = kwargs.get('selectColor' , TTkColor.BOLD ) + self._headerColor = kwargs.get('headerColor' , TTkColor.BOLD ) self._hlayout = TTkHBoxLayout() self.setLayout(self._hlayout) + self._selected = -1 TTkSpacer(parent=self) self._vscroller = TTkScrollBar(parent=self) self._vscroller.valueChanged.connect(self.scrollTo) + self.setFocusPolicy(TTkWidget.ClickFocus) + + def setAlignment(self, alignments): + if len(alignments) != len(self._columns): + return + self._alignments = alignments + + def setHeader(self, header): + if len(header) != len(self._columns): + return + self._header = header def setColumnSize(self, columns): self._columns = columns + self._columnColors = [TTkColor.RST]*len(self._columns) + self._header = [""]*len(self._columns) + self._alignments = [TTkK.NONE]*len(self._columns) + + def setColumnColors(self, colors): + if len(colors) != len(self._columns): + return + self._columnColors = colors def appendItem(self, item): + if len(item) != len(self._columns): + return self._tableData.append(item) self._tuneTheScroller() def resizeEvent(self, w, h): - if self._moveTo > len(self._tableData)-h: - self._moveTo = len(self._tableData)-h + if self._moveTo > len(self._tableData)-h-1: + self._moveTo = len(self._tableData)-h-1 + if self._moveTo < 0: + self._moveTo = 0 self._tuneTheScroller() def _tuneTheScroller(self): @@ -68,6 +97,15 @@ class TTkTable(TTkWidget): self._vscroller.setRange(0, scrollTo) self._vscroller.pagestep = self.height() + def mousePressEvent(self, evt): + x,y = evt.x, evt.y + if x == self.width() - 1: + return False + if y > 0: + self._selected = self._moveTo + y - 1 + self.update() + return True + def wheelEvent(self, evt): # delta = self.height() delta = 5 @@ -86,7 +124,6 @@ class TTkTable(TTkWidget): self.update() def paintEvent(self): - y = 0 w,h = self.size() total = 0 variableCols = 0 @@ -98,34 +135,64 @@ class TTkTable(TTkWidget): variableCols += 1 if variableCols > 0: slicesize = int((w-total)/variableCols) - TTkLog.debug(f"ss:{slicesize}, w:{w}") + # TTkLog.debug(f"ss:{slicesize}, w:{w}") maxItems = len(self._tableData) - itemFrom = self._moveTo + itemFrom = self._moveTo -1 + if itemFrom > maxItems-h: itemFrom = maxItems-h + if itemFrom < 0 : itemFrom = 0 itemTo = itemFrom + h - if itemFrom > maxItems: itemFrom = maxItems if itemTo > maxItems: itemTo = maxItems - for i in range(itemFrom, itemTo): - item= self._tableData[i] - line = "" - for i in range(0,len(item)): - txt = item[i] - width = self._columns[i] - if width < 0: - width = slicesize - if width > 0: - lentxt = len(txt) - if lentxt > width: - line += txt[0:width] + # TTkLog.debug(f"moveto:{self._moveTo}, maxItems:{maxItems}, f:{itemFrom}, t{itemTo}, h:{h}, sel:{self._selected}") + + def _lineDraw(_y, _val, _item, _inColor=None): + _x = 0 + for i in range(0,len(_item)): + _txt = _item[i] + _width = self._columns[i] + _color = self._columnColors[i] + _align = self._alignments[i] + if _inColor is not None: + _color = _inColor + if _width < 0: + _width = slicesize + if _width > 0: + _line = "" + _lentxt = len(_txt) + if _lentxt > _width: + _line += _txt[0:_width] else: - line += txt + " "*(width-lentxt) - line += " " - lentxt = len(line) - if lentxt > w-2: - line = line[0:w-2] + _pad = _width-_lentxt + if _align == TTkK.NONE or _align == TTkK.LEFT_ALIGN: + _line += _txt + " "*_pad + elif _align == TTkK.RIGHT_ALIGN: + _line += " "*_pad + _txt + elif _align == TTkK.CENTER_ALIGN: + _p1 = _pad//2 + _p2 = _pad-_p1 + _line += " "*_p1 + _txt+" "*_p2 + elif _align == TTkK.JUSTIFY: + # TODO: Text Justification + _line += _txt + " "*_pad + self._canvas.drawText(pos=(_x,_y), text=_line, color=_color.modParam(val=-_val)) + _line += " " + _x += _width + 1 + + _lineDraw(0,0,self._header,self._headerColor) + + y = 1 + for it in range(itemFrom, itemTo): + item = self._tableData[it] + if self._selected > 0: + val = self._selected - itemFrom + else: + val = h//2 + if val < 0 : val = 0 + if val > h : val = h + if it == self._selected: + _lineDraw(y,val,item,self._selectColor) else: - line = line + " "*(w-2-lentxt) - self._canvas.drawText(pos=(0,y), text=line) + _lineDraw(y,val,item) y+=1 diff --git a/TermTk/TTkWidgets/widget.py b/TermTk/TTkWidgets/widget.py index b795cb4e..f747151a 100644 --- a/TermTk/TTkWidgets/widget.py +++ b/TermTk/TTkWidgets/widget.py @@ -26,7 +26,7 @@ import TermTk.libbpytop as lbt from TermTk.TTkCore.canvas import * from TermTk.TTkCore.cfg import * from TermTk.TTkCore.signal import * -from TermTk.TTkWidgets.layout import * +from TermTk.TTkLayouts.layout import TTkLayout, TTkWidgetItem class TTkWidget: @@ -110,6 +110,11 @@ class TTkWidget: self._layout.addWidget(widget) self.update(repaint=True, updateLayout=True) + def removeWidget(self, widget): + if self._layout is not None: + self._layout.removeWidget(widget) + self.update(repaint=True, updateLayout=True) + def paintEvent(self): pass def paintChildCanvas(self): @@ -190,7 +195,8 @@ class TTkWidget: # opt of bounds if xlx+lw or ylh+ly: return True - for item in layout.zSortedItems:# reversed(layout.zSortedItems): + for item in reversed(layout.zSortedItems): + # for item in layout.zSortedItems: if isinstance(item, TTkWidgetItem) and not item.isEmpty(): widget = item.widget() if not widget._visible: continue @@ -229,24 +235,25 @@ class TTkWidget: # handle own events if evt.evt == lbt.MouseEvent.Move: if self.mouseMoveEvent(evt): - return + return True if evt.evt == lbt.MouseEvent.Drag: if self.mouseDragEvent(evt): - return + return True elif evt.evt == lbt.MouseEvent.Release: #if self.hasFocus(): # self.clearFocus() if self.mouseReleaseEvent(evt): - return + return True elif evt.evt == lbt.MouseEvent.Press: if self.focusPolicy() & TTkWidget.ClickFocus == TTkWidget.ClickFocus: self.setFocus() self.raiseWidget() if self.mousePressEvent(evt): - return + TTkLog.debug(f"Click {self._name}") + return True elif evt.key == lbt.MouseEvent.Wheel: if self.wheelEvent(evt): - return + return True #if self.focusPolicy() & CuT.WheelFocus == CuT.WheelFocus: # self.setFocus() #elif evt.type() == CuEvent.KeyPress: diff --git a/TermTk/__init__.py b/TermTk/__init__.py index 8300a7cb..8c7597c6 100644 --- a/TermTk/__init__.py +++ b/TermTk/__init__.py @@ -1,3 +1,4 @@ from .TTkCore import * from .TTkGui import * +from .TTkLayouts import * from .TTkWidgets import * \ No newline at end of file diff --git a/docs/BUGS.md b/docs/BUGS.md new file mode 100644 index 00000000..78e2c62c --- /dev/null +++ b/docs/BUGS.md @@ -0,0 +1,5 @@ +# BUGS 😈 + +## Events +- [Minor] ~~Overlapping widget click (below window receive the click event)~~ +- [Minor] Background color still propagate between widgets \ No newline at end of file diff --git a/docs/TODO.md b/docs/TODO.md index 7553946b..2342bb8a 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -1,7 +1,10 @@ # TODO - [ ] Follow [PEP 8](https://www.python.org/dev/peps/pep-0008/) coding style -- [ ] Use @property/@setter when possible - [ ] Move the Global Constants outside TTk object +- [ ] Add Typing (good luck) https://docs.python.org/3/library/typing.html +- [ ] Remove Duplicate functionalities (i.e. Widget) + - [ ] Use @property/@setter when possible + - [ ] Uniform the setter/getter/signal/slots ## Terminal Helper - [ ] Events @@ -21,12 +24,12 @@ ## Colors - [ ] Allow dynamic depth change -- [ ] Define a gradient feature +- [x] Define a gradient feature ## Canvas Class - [ ] Have a look to the Unicode chartable: https://www.utf8-chartable.de/unicode-utf8-table.pl ## Signal/Slots -- [ ] Implement Signal/Slots +- [x] Implement Signal/Slots ## Logs - [x] Log Class @@ -39,6 +42,8 @@ ## Widgets - [ ] Add Size Policy (fixed minimum maximum expanding) -- [ ] Add Show/Hide +- [x] Add Show/Hide ### Layout -- [ ] Add Weight in V and H Layout \ No newline at end of file +- [ ] Add Weight in V and H Layout +- [ ] Add addLayout method +- [x] Add Grid Layout \ No newline at end of file diff --git a/tests/test.showcase.001.py b/tests/test.showcase.001.py index e997fa81..564b8d0b 100755 --- a/tests/test.showcase.001.py +++ b/tests/test.showcase.001.py @@ -84,26 +84,26 @@ btn3.clicked.connect(win_scroller.show) btn4.clicked.connect(win_scroller.hide) win_scroller.setLayout(ttk.TTkVBoxLayout()) top = ttk.TTkFrame(parent=win_scroller, layout=ttk.TTkHBoxLayout()) -ttk.TTkScrollBar(parent=win_scroller, orientation=ttk.TTk.HORIZONTAL, value=0, color=ttk.TTkColor.bg('#990044')+ttk.TTkColor.fg('#ffff00')) -ttk.TTkScrollBar(parent=win_scroller, orientation=ttk.TTk.HORIZONTAL, value=10, color=ttk.TTkColor.bg('#770044')+ttk.TTkColor.fg('#ccff00')) -ttk.TTkScrollBar(parent=win_scroller, orientation=ttk.TTk.HORIZONTAL, value=50, color=ttk.TTkColor.bg('#660044')+ttk.TTkColor.fg('#88ff00')) -ttk.TTkScrollBar(parent=win_scroller, orientation=ttk.TTk.HORIZONTAL, value=80, color=ttk.TTkColor.bg('#550044')+ttk.TTkColor.fg('#55ff00')) -ttk.TTkScrollBar(parent=win_scroller, orientation=ttk.TTk.HORIZONTAL, value=99, color=ttk.TTkColor.bg('#330044')+ttk.TTkColor.fg('#33ff00')) +ttk.TTkScrollBar(parent=win_scroller, orientation=ttk.TTkK.HORIZONTAL, value=0, color=ttk.TTkColor.bg('#990044')+ttk.TTkColor.fg('#ffff00')) +ttk.TTkScrollBar(parent=win_scroller, orientation=ttk.TTkK.HORIZONTAL, value=10, color=ttk.TTkColor.bg('#770044')+ttk.TTkColor.fg('#ccff00')) +ttk.TTkScrollBar(parent=win_scroller, orientation=ttk.TTkK.HORIZONTAL, value=50, color=ttk.TTkColor.bg('#660044')+ttk.TTkColor.fg('#88ff00')) +ttk.TTkScrollBar(parent=win_scroller, orientation=ttk.TTkK.HORIZONTAL, value=80, color=ttk.TTkColor.bg('#550044')+ttk.TTkColor.fg('#55ff00')) +ttk.TTkScrollBar(parent=win_scroller, orientation=ttk.TTkK.HORIZONTAL, value=99, color=ttk.TTkColor.bg('#330044')+ttk.TTkColor.fg('#33ff00')) -ttk.TTkScrollBar(parent=top, orientation=ttk.TTk.VERTICAL, value=0) -ttk.TTkScrollBar(parent=top, orientation=ttk.TTk.VERTICAL, value=10) -ttk.TTkScrollBar(parent=top, orientation=ttk.TTk.VERTICAL, value=40) +ttk.TTkScrollBar(parent=top, orientation=ttk.TTkK.VERTICAL, value=0) +ttk.TTkScrollBar(parent=top, orientation=ttk.TTkK.VERTICAL, value=10) +ttk.TTkScrollBar(parent=top, orientation=ttk.TTkK.VERTICAL, value=40) ttk.TTkSpacer(parent=top) -ttk.TTkScrollBar(parent=top, orientation=ttk.TTk.VERTICAL, value=40, pagestep=3) -ttk.TTkScrollBar(parent=top, orientation=ttk.TTk.VERTICAL, value=50, pagestep=5) -ttk.TTkScrollBar(parent=top, orientation=ttk.TTk.VERTICAL, value=60, pagestep=20) -ttk.TTkScrollBar(parent=top, orientation=ttk.TTk.VERTICAL, value=70, pagestep=30) -ttk.TTkScrollBar(parent=top, orientation=ttk.TTk.VERTICAL, value=80, pagestep=60) +ttk.TTkScrollBar(parent=top, orientation=ttk.TTkK.VERTICAL, value=40, pagestep=3) +ttk.TTkScrollBar(parent=top, orientation=ttk.TTkK.VERTICAL, value=50, pagestep=5) +ttk.TTkScrollBar(parent=top, orientation=ttk.TTkK.VERTICAL, value=60, pagestep=20) +ttk.TTkScrollBar(parent=top, orientation=ttk.TTkK.VERTICAL, value=70, pagestep=30) +ttk.TTkScrollBar(parent=top, orientation=ttk.TTkK.VERTICAL, value=80, pagestep=60) ttk.TTkSpacer(parent=top) -ttk.TTkScrollBar(parent=top, orientation=ttk.TTk.VERTICAL, value=80) -ttk.TTkScrollBar(parent=top, orientation=ttk.TTk.VERTICAL, value=90) -ttk.TTkScrollBar(parent=top, orientation=ttk.TTk.VERTICAL, value=99) +ttk.TTkScrollBar(parent=top, orientation=ttk.TTkK.VERTICAL, value=80) +ttk.TTkScrollBar(parent=top, orientation=ttk.TTkK.VERTICAL, value=90) +ttk.TTkScrollBar(parent=top, orientation=ttk.TTkK.VERTICAL, value=99) # Table window from test.ui.008.table.py diff --git a/tests/test.ui.003.layout.py b/tests/test.ui.003.layout.py index d0211cfc..a73f0ffc 100755 --- a/tests/test.ui.003.layout.py +++ b/tests/test.ui.003.layout.py @@ -32,11 +32,26 @@ ttk.TTkLog.use_default_file_logging() root = ttk.TTk() root.setLayout(ttk.TTkHBoxLayout()) -ttk.TTkTestWidget(parent=root,border=True) +ttk.TTkTestWidget(parent=root,border=True, maxWidth=52) rightframe = ttk.TTkFrame(parent=root) rightframe.setLayout(ttk.TTkVBoxLayout()) -ttk.TTkFrame(parent=rightframe, border=True) +gridFrame = ttk.TTkFrame(parent=rightframe, border=False) +gridFrame.setLayout(ttk.TTkGridLayout()) +ttk.TTkButton(parent=gridFrame, text="Button1") +ttk.TTkButton(parent=gridFrame, text="Button2") +gridFrame.layout().addWidget(ttk.TTkButton(text="Button (1,0)"),1,0) +ttk.TTkButton(parent=gridFrame, text="Button4") +gridFrame.layout().addWidget(ttk.TTkButton(text="Button (1,2)"),1,2) +# Test (add a widget to the same parent with a different layout params than the default) +gridFrame.layout().addWidget(ttk.TTkButton(parent=gridFrame, text="Button (2,1)"),2,1) +gridFrame.layout().addWidget(ttk.TTkButton(text="Button (5,5)"),5,5) + +gridFrame.layout().addWidget(ttk.TTkFrame(border=True),0,5) +gridFrame.layout().addWidget(ttk.TTkFrame(border=True),2,3) +gridFrame.layout().addWidget(ttk.TTkFrame(border=True),5,3) +gridFrame.layout().addWidget(ttk.TTkFrame(border=True),5,1) + centerrightframe=ttk.TTkFrame(parent=rightframe, border=True) centerrightframe.setLayout(ttk.TTkHBoxLayout()) ttk.TTkTestWidget(parent=rightframe, border=True) diff --git a/tests/test.ui.005.labels.py b/tests/test.ui.005.labels.py index 13e36610..96dbe754 100755 --- a/tests/test.ui.005.labels.py +++ b/tests/test.ui.005.labels.py @@ -35,17 +35,18 @@ win1 = ttk.TTkWindow(parent=root,pos = (1,1), size=(100,50), title="Test Window win1.setLayout(ttk.TTkVBoxLayout()) ttk.TTkButton(parent=win1, text="BUTTON") ttk.TTkLabel(parent=win1, text="Test Label 1") -ttk.TTkLabel(parent=win1, text="Test Label 2") -ttk.TTkLabel(parent=win1, text="Test Label 3") -ttk.TTkLabel(parent=win1, text="Test Label 4") -ttk.TTkLabel(parent=win1, text="Test Label 5") -ttk.TTkLabel(parent=win1, text="Test Label 6") -ttk.TTkLabel(parent=win1, text="Test Very Long Label 7 - abcdefghihjlmno") -ttk.TTkLabel(parent=win1, text="Test Label 8") +ttk.TTkLabel(parent=win1, text="Test Label 2 Bold", color=ttk.TTkColor.BOLD) +ttk.TTkLabel(parent=win1, text="Test Label 3 Italic", color=ttk.TTkColor.ITALIC) +ttk.TTkLabel(parent=win1, text="Test Label 4 Underline", color=ttk.TTkColor.UNDERLINE) +ttk.TTkLabel(parent=win1, text="Test Label 5 StrikeTrough", color=ttk.TTkColor.STRIKETROUGH) +ttk.TTkLabel(parent=win1, text="Test Label 6 Mix", color=ttk.TTkColor.BOLD+ttk.TTkColor.ITALIC+ttk.TTkColor.UNDERLINE) +ttk.TTkLabel(parent=win1, text="Test Label 7") +ttk.TTkLabel(parent=win1, text="Test Very Long Label 8 - abcdefghihjlmno") ttk.TTkLabel(parent=win1, text="Test Label 9") ttk.TTkLabel(parent=win1, text="Test Label 10") ttk.TTkLabel(parent=win1, text="Test Label 11") ttk.TTkLabel(parent=win1, text="Test Label 12") +ttk.TTkLabel(parent=win1, text="Test Label 13") root.mainloop() \ No newline at end of file diff --git a/tests/test.ui.006.scroll.py b/tests/test.ui.006.scroll.py index 94cb281a..f21b9b6d 100755 --- a/tests/test.ui.006.scroll.py +++ b/tests/test.ui.006.scroll.py @@ -34,26 +34,26 @@ root = ttk.TTk() win1 = ttk.TTkWindow(parent=root,pos = (1,1), size=(30,40), title="Test Window 1", border=True) win1.setLayout(ttk.TTkVBoxLayout()) top = ttk.TTkFrame(parent=win1, layout=ttk.TTkHBoxLayout()) -ttk.TTkScrollBar(parent=win1, orientation=ttk.TTk.HORIZONTAL, value=0, color=ttk.TTkColor.bg('#990044')+ttk.TTkColor.fg('#ffff00')) -ttk.TTkScrollBar(parent=win1, orientation=ttk.TTk.HORIZONTAL, value=10, color=ttk.TTkColor.bg('#770044')+ttk.TTkColor.fg('#ccff00')) -ttk.TTkScrollBar(parent=win1, orientation=ttk.TTk.HORIZONTAL, value=50, color=ttk.TTkColor.bg('#660044')+ttk.TTkColor.fg('#88ff00')) -ttk.TTkScrollBar(parent=win1, orientation=ttk.TTk.HORIZONTAL, value=80, color=ttk.TTkColor.bg('#550044')+ttk.TTkColor.fg('#55ff00')) -ttk.TTkScrollBar(parent=win1, orientation=ttk.TTk.HORIZONTAL, value=99, color=ttk.TTkColor.bg('#330044')+ttk.TTkColor.fg('#33ff00')) +ttk.TTkScrollBar(parent=win1, orientation=ttk.TTkK.HORIZONTAL, value=0, color=ttk.TTkColor.bg('#990044')+ttk.TTkColor.fg('#ffff00')) +ttk.TTkScrollBar(parent=win1, orientation=ttk.TTkK.HORIZONTAL, value=10, color=ttk.TTkColor.bg('#770044')+ttk.TTkColor.fg('#ccff00')) +ttk.TTkScrollBar(parent=win1, orientation=ttk.TTkK.HORIZONTAL, value=50, color=ttk.TTkColor.bg('#660044')+ttk.TTkColor.fg('#88ff00')) +ttk.TTkScrollBar(parent=win1, orientation=ttk.TTkK.HORIZONTAL, value=80, color=ttk.TTkColor.bg('#550044')+ttk.TTkColor.fg('#55ff00')) +ttk.TTkScrollBar(parent=win1, orientation=ttk.TTkK.HORIZONTAL, value=99, color=ttk.TTkColor.bg('#330044')+ttk.TTkColor.fg('#33ff00')) -ttk.TTkScrollBar(parent=top, orientation=ttk.TTk.VERTICAL, value=0) -ttk.TTkScrollBar(parent=top, orientation=ttk.TTk.VERTICAL, value=10) -ttk.TTkScrollBar(parent=top, orientation=ttk.TTk.VERTICAL, value=40) +ttk.TTkScrollBar(parent=top, orientation=ttk.TTkK.VERTICAL, value=0) +ttk.TTkScrollBar(parent=top, orientation=ttk.TTkK.VERTICAL, value=10) +ttk.TTkScrollBar(parent=top, orientation=ttk.TTkK.VERTICAL, value=40) ttk.TTkSpacer(parent=top) -ttk.TTkScrollBar(parent=top, orientation=ttk.TTk.VERTICAL, value=40, pagestep=3) -ttk.TTkScrollBar(parent=top, orientation=ttk.TTk.VERTICAL, value=50, pagestep=5) -ttk.TTkScrollBar(parent=top, orientation=ttk.TTk.VERTICAL, value=60, pagestep=20) -ttk.TTkScrollBar(parent=top, orientation=ttk.TTk.VERTICAL, value=70, pagestep=30) -ttk.TTkScrollBar(parent=top, orientation=ttk.TTk.VERTICAL, value=80, pagestep=60) +ttk.TTkScrollBar(parent=top, orientation=ttk.TTkK.VERTICAL, value=40, pagestep=3) +ttk.TTkScrollBar(parent=top, orientation=ttk.TTkK.VERTICAL, value=50, pagestep=5) +ttk.TTkScrollBar(parent=top, orientation=ttk.TTkK.VERTICAL, value=60, pagestep=20) +ttk.TTkScrollBar(parent=top, orientation=ttk.TTkK.VERTICAL, value=70, pagestep=30) +ttk.TTkScrollBar(parent=top, orientation=ttk.TTkK.VERTICAL, value=80, pagestep=60) ttk.TTkSpacer(parent=top) -ttk.TTkScrollBar(parent=top, orientation=ttk.TTk.VERTICAL, value=80) -ttk.TTkScrollBar(parent=top, orientation=ttk.TTk.VERTICAL, value=90) -ttk.TTkScrollBar(parent=top, orientation=ttk.TTk.VERTICAL, value=99) +ttk.TTkScrollBar(parent=top, orientation=ttk.TTkK.VERTICAL, value=80) +ttk.TTkScrollBar(parent=top, orientation=ttk.TTkK.VERTICAL, value=90) +ttk.TTkScrollBar(parent=top, orientation=ttk.TTkK.VERTICAL, value=99) root.mainloop() \ No newline at end of file diff --git a/tests/test.ui.008.table.py b/tests/test.ui.008.table.py index 2ae4a0a5..9305eece 100755 --- a/tests/test.ui.008.table.py +++ b/tests/test.ui.008.table.py @@ -38,16 +38,83 @@ def getSentence(): ttk.TTkLog.use_default_file_logging() root = ttk.TTk() -win1 = ttk.TTkWindow(parent=root,pos = (3,3), size=(40,20), title="Test Table 1", layout=ttk.TTkHBoxLayout(), border=True) -table = ttk.TTkTable(parent=win1) +btn1 = ttk.TTkButton(parent=root, pos=(0,0), size=(5,3), text='Add') +btn2 = ttk.TTkButton(parent=root, pos=(5,0), size=(10,3), text='Add Many') -table.setColumnSize((20,-1,10,15)) -table.appendItem(("","You see it's all clear, You were meant to be here, From the beginning","","")) -for i in range(0, 100): - table.appendItem((str(i)+" - "+getWord(), getSentence(), getWord(), getWord())) -table.appendItem(("This is the end", "Beautiful friend, This is the end My only friend", "the end", "...")) +win_table1 = ttk.TTkWindow(parent=root,pos = (3,3), size=(150,40), title="Test Table 1", layout=ttk.TTkHBoxLayout(), border=True) +table1 = ttk.TTkTable(parent=win_table1, selectColor=ttk.TTkColor.bg('#882200')) +win_table2 = ttk.TTkWindow(parent=root,pos = (15,5), size=(100,30), title="Test Table 2 Default", layout=ttk.TTkHBoxLayout(), border=True) +table2 = ttk.TTkTable(parent=win_table2) +win_table3 = ttk.TTkWindow(parent=root,pos = (15,5), size=(130,40), title="Test Table 2 Default", layout=ttk.TTkHBoxLayout(), border=True) +table3 = ttk.TTkTable(parent=win_table3) + +table1.setColumnSize((5,10,-1,10,20)) +table1.setAlignment(( + ttk.TTkK.LEFT_ALIGN, + ttk.TTkK.RIGHT_ALIGN, + ttk.TTkK.LEFT_ALIGN, + ttk.TTkK.LEFT_ALIGN, + ttk.TTkK.CENTER_ALIGN + )) +table1.setHeader(("id","Name","Sentence","Word","center")) +table1.setColumnColors(( + ttk.TTkColor.fg('#888800', modifier=ttk.TTkColorGradient(increment=6)), + ttk.TTkColor.RST, + ttk.TTkColor.fg('#00dddd', modifier=ttk.TTkColorGradient(increment=-4)), + ttk.TTkColor.RST, + ttk.TTkColor.fg('#cccccc', modifier=ttk.TTkColorGradient(increment=-2)) + )) + +table2.setColumnSize((5,10,-1,10,20)) +table2.setHeader(("id","Name","Sentence","Word","")) + +table3.setColumnSize((5,10,-1,10,20)) +table3.setAlignment(( + ttk.TTkK.LEFT_ALIGN, + ttk.TTkK.RIGHT_ALIGN, + ttk.TTkK.LEFT_ALIGN, + ttk.TTkK.LEFT_ALIGN, + ttk.TTkK.CENTER_ALIGN + )) +table3.setHeader(("id","Name","Sentence","Word","center")) +table3.setColumnColors(( + ttk.TTkColor.fg('#ffff00', modifier=ttk.TTkColorGradient(increment=6)), + ttk.TTkColor.fg('#ff0000', modifier=ttk.TTkColorGradient(increment=6)), + ttk.TTkColor.fg('#00ffff', modifier=ttk.TTkColorGradient(increment=-8)), + ttk.TTkColor.fg('#00ff00', modifier=ttk.TTkColorGradient(increment=-4)), + ttk.TTkColor.fg('#cccccc', modifier=ttk.TTkColorGradient(increment=-2)) + )) + + +table3.appendItem((" - ","","You see it's all clear, You were meant to be here, From the beginning","","")) +for i in range(0, 5): + table1.appendItem((str(i), getWord(), getSentence(), getWord(), getWord())) +for i in range(0, 5): + table2.appendItem((str(i), getWord(), getSentence(), getWord(), getWord())) +for i in range(0, 35): + table3.appendItem((str(i), getWord(), getSentence(), getWord(), getWord())) + +table3.appendItem((" - ","This is the end", "Beautiful friend, This is the end My only friend", "the end", "...")) + + + +# Attach the add Event +ii = 1000 +def add(): + global ii + ii+=1 + table1.appendItem((str(ii), getWord(), getSentence(), getWord(), getWord())) + table2.appendItem((str(ii), getWord(), getSentence(), getWord(), getWord())) +btn1.clicked.connect(add) + +def addMany(): + global ii + for i in range(0, 500): + ii+=1 + table1.appendItem((str(ii), getWord(), getSentence(), getWord(), getWord())) +btn2.clicked.connect(addMany) root.mainloop() \ No newline at end of file