diff --git a/TermTk/TTkAbstract/abstractscrollarea.py b/TermTk/TTkAbstract/abstractscrollarea.py index cbff51f5..3cf60790 100644 --- a/TermTk/TTkAbstract/abstractscrollarea.py +++ b/TermTk/TTkAbstract/abstractscrollarea.py @@ -109,4 +109,5 @@ class TTkAbstractScrollArea(TTkWidget): def setHorizontalScrollBarPolicy(self, policy): self._horizontalScrollBarPolicy = policy - + def viewport(self): + return self._viewport diff --git a/TermTk/TTkAbstract/abstractscrollview.py b/TermTk/TTkAbstract/abstractscrollview.py index 1e081705..22e01537 100644 --- a/TermTk/TTkAbstract/abstractscrollview.py +++ b/TermTk/TTkAbstract/abstractscrollview.py @@ -42,7 +42,6 @@ class TTkAbstractScrollView(TTkWidget): super().__init__(self, *args, **kwargs) self._name = kwargs.get('name' , 'TTkAbstractScrollView') - self._viewOffsetX = 0 self._viewOffsetY = 0 @@ -61,10 +60,8 @@ class TTkAbstractScrollView(TTkWidget): rangex = fw - dw rangey = fh - dh # TTkLog.debug(f"x:{x},y:{y}, full:{fw,fh}, display:{dw,dh}, range:{rangex,rangey}") - if x>rangex: x = rangex - if y>rangey: y = rangey - if x<0 : x = 0 - if y<0 : y = 0 + x = max(0,min(rangex,x)) + y = max(0,min(rangey,y)) # TTkLog.debug(f"x:{x},y:{y}, wo:{self._viewOffsetX,self._viewOffsetY}") if self._viewOffsetX == x and \ self._viewOffsetY == y: # Nothong to do diff --git a/TermTk/TTkCore/canvas.py b/TermTk/TTkCore/canvas.py index 37fc95ae..78fd0295 100644 --- a/TermTk/TTkCore/canvas.py +++ b/TermTk/TTkCore/canvas.py @@ -299,7 +299,7 @@ class TTkCanvas: tt = TTkCfg.theme.tab # phase 0 - Draw the Bottom bar if slim: - bottomBar = tt[16]+tt[12]*(w-2)+tt[17] + bottomBar = tt[18]+tt[19]*(w-2)+tt[20] bottomPos = y+1 else: bottomBar = tt[11]+tt[12]*(w-2)+tt[15] @@ -329,22 +329,22 @@ class TTkCanvas: list(reversed(range(offset+1, len(labels)) )): text = labels[i] posx = labelsPos[i] - _drawTab(x+posx,y,tt[0],tt[1],tt[3],tt[9],tt[9],tt[12],tt[12],tt[12],tt[9],tt[9],tt[13],tt[12],tt[13], text, color, borderColor, slim) + _drawTab(x+posx,y,tt[0],tt[1],tt[3],tt[9],tt[9],tt[12],tt[12],tt[12],tt[9],tt[9],tt[23],tt[19],tt[24], text, color, borderColor, slim) # phase 3 - Draw 'Selected' if selected != -1: i = selected text = labels[i] posx = labelsPos[i] - _drawTab(x+posx,y,tt[4],tt[5],tt[6],tt[10],tt[10],tt[14],tt[12],tt[14],tt[10],tt[10],tt[14],tt[12],tt[14], text, selectColor, borderColor, slim) + _drawTab(x+posx,y,tt[4],tt[5],tt[6],tt[10],tt[10],tt[14],tt[12],tt[14],tt[10],tt[10],tt[21],tt[12],tt[22], text, selectColor, borderColor, slim) if selected != offset: i = offset text = labels[i] posx = labelsPos[i] - _drawTab(x+posx,y,tt[0],tt[1],tt[3],tt[9],tt[9],tt[13],tt[12],tt[13],tt[9],tt[9],tt[13],tt[12],tt[13], text, color, borderColor, slim) + _drawTab(x+posx,y,tt[0],tt[1],tt[3],tt[9],tt[9],tt[13],tt[12],tt[13],tt[9],tt[9],tt[18],tt[19],tt[20], text, color, borderColor, slim) # phase 4 - Draw left right tilt if leftScroller: top = tt[7]+tt[1] - center = tt[9]+tt[18] + center = tt[9]+tt[31] if slim: self.drawText(pos=(x,y),text=center, color=borderColor) else: @@ -352,7 +352,7 @@ class TTkCanvas: self.drawText(pos=(x,y+1),text=center, color=borderColor) if rightScroller: top = tt[1]+tt[8] - center = tt[19]+tt[9] + center = tt[32]+tt[9] if slim: self.drawText(pos=(x+w-2,y),text=center, color=borderColor) else: diff --git a/TermTk/TTkCore/color.py b/TermTk/TTkCore/color.py index a539b9b8..a4dcafa0 100644 --- a/TermTk/TTkCore/color.py +++ b/TermTk/TTkCore/color.py @@ -154,12 +154,9 @@ class TTkColorGradient(_TTkColorModifier): 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 + r = max(min(255,r),0) + g = max(min(255,g),0) + b = max(min(255,b),0) return f"{cc[0]};{cc[1]};{r};{g};{b}m" bname = str(color) diff --git a/TermTk/TTkCore/constant.py b/TermTk/TTkCore/constant.py index 48377e7b..bce7ac1e 100644 --- a/TermTk/TTkCore/constant.py +++ b/TermTk/TTkCore/constant.py @@ -43,6 +43,13 @@ class TTkConstant: RIGHT = 0x0008 CENTER = 0x0010 + # SelectionMode + NoSelection = 0x00 + SingleSelection = 0x01 + MultiSelection = 0x02 + ExtendedSelection = 0x03 + ContiguousSelection = 0x04 + # Graph types FILLED = 0x0001 LINE = 0x0002 diff --git a/TermTk/TTkGui/theme.py b/TermTk/TTkGui/theme.py index 81232ea1..ccf083db 100644 --- a/TermTk/TTkGui/theme.py +++ b/TermTk/TTkGui/theme.py @@ -136,9 +136,11 @@ class TTkTheme(): '┌','─','┬','┐','╔','═','╗','╭','╮', #9 10 '│','║', - #11 12 13 14 15 16 17 - '╞','═','╧','╩','╡','╘','╛', - #18 19 + #11 12 13 14 15 16 17 18 19 20 + '╞','═','╧','╩','╡','╘','╛','└','─','┘', + #21 22 23 24 25 26 27 28 29 30 + '╚','╝','╰','╯','X','X','X','X','X','X', + #31 32 '◀','▶' ) ''' @@ -195,6 +197,8 @@ class TTkTheme(): buttonTextColorFocus = buttonTextColor + TTkColor.BOLD buttonBorderColorFocus = buttonBorderColor + TTkColor.BOLD + listColor = TTkColor.RST + listColorSelected = TTkColor.fg("#ffffdd")+TTkColor.bg("#000044") + TTkColor.BOLD lineEditTextColor = TTkColor.fg("#dddddd")+TTkColor.bg("#222222") lineEditTextColorFocus = TTkColor.fg("#dddddd")+TTkColor.bg("#000044") diff --git a/TermTk/TTkLayouts/layout.py b/TermTk/TTkLayouts/layout.py index 3d7ba0e9..e674aa9c 100644 --- a/TermTk/TTkLayouts/layout.py +++ b/TermTk/TTkLayouts/layout.py @@ -135,6 +135,25 @@ class TTkLayout(TTkLayoutItem): TTkLayoutItem.setGeometry(self, x, y, w, h) self.update() + def groupMoveTo(self, x, y): + ox,oy,_,_ = self.fullWidgetAreaGeometry() + dx = x-ox + dy = y-oy + for item in self._items: + x,y,w,h = item.geometry() + item.setGeometry(x+dx,y+dy,w,h) + + def fullWidgetAreaGeometry(self): + if not self._items: return 0,0,0,0 + minx,miny,maxx,maxy = 0x10000,0x10000,-0x10000,-0x10000 + for item in self._items: + x,y,w,h = item.geometry() + minx = min(minx,x) + miny = min(miny,y) + maxx = max(maxx,x+w) + maxy = max(maxy,y+h) + return minx, miny, maxx-minx, maxy-miny + def update(self): ret = False for i in self.children(): diff --git a/TermTk/TTkTemplates/color.py b/TermTk/TTkTemplates/color.py index 70bd1795..3e843628 100644 --- a/TermTk/TTkTemplates/color.py +++ b/TermTk/TTkTemplates/color.py @@ -27,8 +27,7 @@ from TermTk.TTkCore.color import TTkColor class TColor(): #__slots__ = ('_color') def __init__(self, *args, **kwargs): - self._color = TTkColor.RST - self.color = kwargs.get('color', TTkColor.RST ) + self._color = kwargs.get('color', TTkColor.RST ) def colorUpdated(self, color): pass diff --git a/TermTk/TTkTemplates/text.py b/TermTk/TTkTemplates/text.py index cdfd1dec..f1299c10 100644 --- a/TermTk/TTkTemplates/text.py +++ b/TermTk/TTkTemplates/text.py @@ -25,8 +25,7 @@ class TText(): #__slots__ = ('_text') def __init__(self, *args, **kwargs): - self._text = "" - self.text = kwargs.get('text', "" ) + self._text = kwargs.get('text', "" ) def textUpdated(self, text): pass diff --git a/TermTk/TTkTestWidgets/__init__.py b/TermTk/TTkTestWidgets/__init__.py index 89d7e830..cd13ba68 100644 --- a/TermTk/TTkTestWidgets/__init__.py +++ b/TermTk/TTkTestWidgets/__init__.py @@ -1,2 +1,3 @@ +from .logviewer import * from .testwidget import * from .testwidgetsizes import * diff --git a/TermTk/TTkTestWidgets/logviewer.py b/TermTk/TTkTestWidgets/logviewer.py new file mode 100644 index 00000000..536685d9 --- /dev/null +++ b/TermTk/TTkTestWidgets/logviewer.py @@ -0,0 +1,98 @@ +#!/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. + +import os +from TermTk.TTkCore.constant import TTkK +from TermTk.TTkCore.log import TTkLog +from TermTk.TTkCore.color import TTkColor +from TermTk.TTkWidgets.frame import TTkFrame +from TermTk.TTkTemplates.color import TColor +from TermTk.TTkTemplates.text import TText +from TermTk.TTkCore.signal import pyTTkSlot, pyTTkSignal +from TermTk.TTkAbstract.abstractscrollarea import TTkAbstractScrollArea +from TermTk.TTkAbstract.abstractscrollview import TTkAbstractScrollView + +class _TTkLogViewer(TTkAbstractScrollView): + __slots__ = ('_color', '_text', '_messages', '_cwd') + def __init__(self, *args, **kwargs): + TTkAbstractScrollView.__init__(self, *args, **kwargs) + self._name = kwargs.get('name' , '_TTkLogViewer' ) + self._messages = [""] + self._cwd = os.getcwd() + TTkLog.installMessageHandler(self.loggingCallback) + self.viewChanged.connect(self._viewChangedHandler) + + @pyTTkSlot() + def _viewChangedHandler(self): + self.update() + + def viewFullAreaSize(self) -> (int, int): + w = max([ len(m) for m in self._messages]) + h = len(self._messages) + return w , h + + def viewDisplayedSize(self) -> (int, int): + return self.size() + + def loggingCallback(self, mode, context, message): + logType = "NONE" + if mode == TTkLog.InfoMsg: logType = "INFO " + elif mode == TTkLog.DebugMsg: logType = "DEBUG" + elif mode == TTkLog.ErrorMsg: logType = "ERROR" + elif mode == TTkLog.FatalMsg: logType = "FATAL" + elif mode == TTkLog.WarningMsg: logType = "WARNING " + elif mode == TTkLog.CriticalMsg: logType = "CRITICAL" + self._messages.append(f"{logType}: {context.file}:{context.line} {message}".replace(self._cwd,"_")) + offx, offy = self.getViewOffsets() + _,h = self.size() + if offy == len(self._messages)-h-1: + offy = len(self._messages)-h + self.viewMoveTo(offx, offy) + self.viewChanged.emit() + self.update() + + def paintEvent(self): + ox,oy = self.getViewOffsets() + y = 0 + _,h = self.size() + offset = max(0,ox) + for message in self._messages[oy:]: + self._canvas.drawText(pos=(0,y),text=message[ox:]) + c = TTkColor.RST + if message.startswith("INFO ") : c = TTkColor.fg("#00ff00") + elif message.startswith("DEBUG") : c = TTkColor.fg("#00ffff") + elif message.startswith("ERROR") : c = TTkColor.fg("#ff0000") + elif message.startswith("FATAL") : c = TTkColor.fg("#ff0000") + self._canvas.drawText(pos=(-ox,y),text=message[:5], color=c) + y+=1 + +class TTkLogViewer(TTkAbstractScrollArea): + __slots__ = ('_logView') + def __init__(self, *args, **kwargs): + TTkAbstractScrollArea.__init__(self, *args, **kwargs) + self._name = kwargs.get('name' , 'TTkLogViewer' ) + if 'parent' in kwargs: kwargs.pop('parent') + self._logView = _TTkLogViewer(*args, **kwargs) + self.setFocusPolicy(TTkK.ClickFocus) + self.setViewport(self._logView) diff --git a/TermTk/TTkWidgets/__init__.py b/TermTk/TTkWidgets/__init__.py index 555502eb..661972e8 100644 --- a/TermTk/TTkWidgets/__init__.py +++ b/TermTk/TTkWidgets/__init__.py @@ -11,12 +11,14 @@ from .combobox import * from .lineedit import * from .texedit import * from .scrollbar import * +from .scrollarea import * from .window import * from .tabwidget import * +from .list import * +from .listwidget import * from .table import * from .tableview import * from .tree import * from .treeview import * from .treewidget import * -from .logviewer import * from .graph import * diff --git a/TermTk/TTkWidgets/label.py b/TermTk/TTkWidgets/label.py index dac5d8b3..5b672c73 100644 --- a/TermTk/TTkWidgets/label.py +++ b/TermTk/TTkWidgets/label.py @@ -30,10 +30,12 @@ from TermTk.TTkTemplates.text import TText class TTkLabel(TTkWidget, TColor, TText): __slots__ = ('_color', '_text') def __init__(self, *args, **kwargs): - TTkWidget.__init__(self, *args, **kwargs) - self._name = kwargs.get('name' , 'TTkLabel' ) TColor.__init__(self, *args, **kwargs) TText.__init__(self, *args, **kwargs) + TTkWidget.__init__(self, *args, **kwargs) + self._name = kwargs.get('name' , 'TTkLabel' ) + # self.setMinimumSize(len(self.text), 1) + self.textUpdated(self.text) def paintEvent(self): self._canvas.drawText(pos=(0,0), text=' '*self.width(), color=self.color) diff --git a/TermTk/TTkWidgets/list.py b/TermTk/TTkWidgets/list.py new file mode 100644 index 00000000..e84ea845 --- /dev/null +++ b/TermTk/TTkWidgets/list.py @@ -0,0 +1,50 @@ +#!/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. + +from TermTk.TTkCore.cfg import TTkCfg +from TermTk.TTkCore.constant import TTkK +from TermTk.TTkCore.log import TTkLog +from TermTk.TTkCore.signal import pyTTkSlot, pyTTkSignal +from TermTk.TTkWidgets.listwidget import TTkListWidget +from TermTk.TTkAbstract.abstractscrollarea import TTkAbstractScrollArea + +class TTkList(TTkAbstractScrollArea): + __slots__ = ('_listView', 'itemClicked', 'textClicked') + def __init__(self, *args, **kwargs): + TTkAbstractScrollArea.__init__(self, *args, **kwargs) + self._name = kwargs.get('name' , 'TTkList' ) + if 'parent' in kwargs: kwargs.pop('parent') + self._listView = TTkListWidget(*args, **kwargs) + self.setFocusPolicy(TTkK.ClickFocus) + self.setViewport(self._listView) + self.itemClicked = self._listView.itemClicked + self.textClicked = self._listView.textClicked + + def addItem(self, *args, **kwargs): + return self._listView.addItem(*args, **kwargs) + def setSelectionMode(self, *args, **kwargs): + return self._listView.setSelectionMode(*args, **kwargs) + def selectedLabels(self, *args, **kwargs): + return self._listView.selectedLabels(*args, **kwargs) + diff --git a/TermTk/TTkWidgets/listwidget.py b/TermTk/TTkWidgets/listwidget.py new file mode 100644 index 00000000..464bbbfd --- /dev/null +++ b/TermTk/TTkWidgets/listwidget.py @@ -0,0 +1,126 @@ +#!/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. + +from TermTk.TTkCore.cfg import TTkCfg +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.TTkWidgets.widget import TTkWidget +from TermTk.TTkWidgets.label import TTkLabel +from TermTk.TTkTestWidgets.testwidgetsizes import TTkTestWidgetSizes +from TermTk.TTkAbstract.abstractscrollview import TTkAbstractScrollView + +class _TTkListWidgetText(TTkLabel): + __slots__ = ('selected', '_selected') + def __init__(self, *args, **kwargs): + TTkLabel.__init__(self, *args, **kwargs) + self._name = kwargs.get('name' , '_TTkListWidgetText' ) + # Define Signals + self.selected = pyTTkSignal(_TTkListWidgetText) + self._selected = False + + def mouseReleaseEvent(self, evt): + self.selected.emit(self) + return True + +class TTkListWidget(TTkAbstractScrollView): + __slots__ = ('itemClicked', 'textClicked', '_color', '_selectedColor', '_selectedItems', '_selectionMode') + def __init__(self, *args, **kwargs): + # Default Class Specific Values + self._selectionMode = kwargs.get("selectionMode", TTkK.SingleSelection) + self._selectedItems = [] + self._color = TTkCfg.theme.listColor + self._selectedColor = TTkCfg.theme.listColorSelected + # Signals + self.itemClicked = pyTTkSignal(TTkWidget) + self.textClicked = pyTTkSignal(str) + # Init Super + TTkAbstractScrollView.__init__(self, *args, **kwargs) + self._name = kwargs.get('name' , 'TTkListWidget' ) + self.viewChanged.connect(self._viewChangedHandler) + + @pyTTkSlot() + def _viewChangedHandler(self): + x,y = self.getViewOffsets() + self.layout().groupMoveTo(-x,-y) + + @pyTTkSlot(_TTkListWidgetText) + def _labelSelectedHandler(self, label): + if self._selectionMode == TTkK.SingleSelection: + for i in self._selectedItems: + i._selected = False + i.color = TTkCfg.theme.listColor + label._selected = True + elif self._selectionMode == TTkK.MultiSelection: + label._selected = not label._selected + if label._selected: + self._selectedItems.append(label) + else: + self._selectedItems.remove(label) + if label._selected: + label.color = TTkCfg.theme.listColorSelected + else: + label.color = TTkCfg.theme.listColor + + self.textClicked.emit(label.text) + + def setSelectionMode(self, mode): + self._selectionMode = mode + + def selectedLabels(self): + return [i.text for i in self._selectedItems] + + def resizeEvent(self, w, h): + maxw = 0 + for item in self.layout().children(): + maxw = max(maxw,item.minimumWidth()) + maxw = max(self.width(),maxw) + for item in self.layout().children(): + x,y,_,h = item.geometry() + item.setGeometry(x,y,maxw,h) + self.viewChanged.emit() + + def viewFullAreaSize(self) -> (int, int): + _,_,w,h = self.layout().fullWidgetAreaGeometry() + return w , h + + def viewDisplayedSize(self) -> (int, int): + return self.size() + + def addItem(self, item): + if isinstance(item, str): + label = _TTkListWidgetText(text=item, width=max(len(item),self.width())) + label.selected.connect(self._labelSelectedHandler) + return self.addItem(label) + _,y,_,h = self.layout().fullWidgetAreaGeometry() + self.addWidget(item) + item.move(0,y+h) + _,_,fw,_ = self.layout().fullWidgetAreaGeometry() + w = self.width() + for item in self.layout().children(): + x,y,_,h = item.geometry() + minw = item.minimumWidth() + item.setGeometry(x,y,max(w-1,fw),h) + self.viewChanged.emit() \ No newline at end of file diff --git a/TermTk/TTkWidgets/logviewer.py b/TermTk/TTkWidgets/logviewer.py deleted file mode 100644 index bb047dda..00000000 --- a/TermTk/TTkWidgets/logviewer.py +++ /dev/null @@ -1,62 +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. - -import os -from TermTk.TTkCore.log import TTkLog -from TermTk.TTkWidgets.widget import * -from TermTk.TTkTemplates.color import TColor -from TermTk.TTkTemplates.text import TText - -class TTkLogViewer(TTkWidget): - __slots__ = ('_color', '_text', '_messages', '_cwd') - def __init__(self, *args, **kwargs): - TTkWidget.__init__(self, *args, **kwargs) - self._name = kwargs.get('name' , 'TTkLabel' ) - self._messages = [] - self._cwd = os.getcwd() - TTkLog.installMessageHandler(self.loggingCallback) - - - def loggingCallback(self, mode, context, message): - logType = "NONE" - if mode == TTkLog.InfoMsg: logType = "INFO" - elif mode == TTkLog.DebugMsg: logType = "DEBUG" - elif mode == TTkLog.WarningMsg: logType = "WARNING" - elif mode == TTkLog.CriticalMsg: logType = "CRITICAL" - elif mode == TTkLog.FatalMsg: logType = "FATAL" - elif mode == TTkLog.ErrorMsg: logType = "ERROR" - self._messages.append(f"{logType}: {context.file}:{context.line} {message}".replace(self._cwd,"_")) - self.update() - - def paintEvent(self): - y = 0 - _,h = self.size() - offset = len(self._messages)-h - if offset<0: offset = 0 - for message in self._messages[offset:]: - self._canvas.drawText(pos=(0,y),text=message) - y+=1 - - - diff --git a/TermTk/TTkWidgets/scrollarea.py b/TermTk/TTkWidgets/scrollarea.py index 7394a89c..a011b30c 100644 --- a/TermTk/TTkWidgets/scrollarea.py +++ b/TermTk/TTkWidgets/scrollarea.py @@ -23,38 +23,36 @@ # SOFTWARE. from TermTk.TTkCore.log import TTkLog +from TermTk.TTkCore.constant import TTkConstant, TTkK from TermTk.TTkCore.signal import pyTTkSlot, pyTTkSignal -from TermTk.TTkCore.color import TTkColor -from TermTk.TTkWidgets.widget import TTkWidget, TTkScrollBar, TTkLayout +from TermTk.TTkAbstract.abstractscrollarea import TTkAbstractScrollArea +from TermTk.TTkAbstract.abstractscrollview import TTkAbstractScrollView -''' - - -''' -class TTkScrollArea(TTkWidget): - __slots__ = ('_border', '_hszroll', '_vscroll', '_widgetScroller') +class _TTkAreaWidget(TTkAbstractScrollView): + __slots__ = () def __init__(self, *args, **kwargs): - TTkWidget.__init__(self, *args, **kwargs) - self._name = kwargs.get('name' , 'TTkScrollArea' ) - # self._border = kwargs.get('border', True ) - # if self._border: - # self.setPadding(1,2,1,2) - # else: - # self.setPadding(0,1,0,1) - # self._hscroll = TTkScrollBar(parent=self) - # self._vscroll = TTkScrollBar(parent=self) + TTkAbstractScrollView.__init__(self, *args, **kwargs) + self._name = kwargs.get('name' , '_TTkAreaWidget' ) + self.viewChanged.connect(self._viewChangedHandler) - #def setWidget(self, widget): + @pyTTkSlot() + def _viewChangedHandler(self): + x,y = self.getViewOffsets() + self.layout().groupMoveTo(-x,-y) - #def setLayout(self, layout): - # self._layout = layout - # self._layout.setParent(self) - # self._layout.setGeometry( - # self._padl, self._padt, - # self._width - self._padl - self._padr, - # self._height - self._padt - self._padb) - # self.update(repaint=True, updateLayout=True) + def viewFullAreaSize(self) -> (int, int): + _,_,w,h = self.layout().fullWidgetAreaGeometry() + return w , h - #def paintEvent(self): - # if self._border: - # self._canvas.drawBox(pos=(0,0),size=(self._width,self._height)) \ No newline at end of file + def viewDisplayedSize(self) -> (int, int): + return self.size() + +class TTkScrollArea(TTkAbstractScrollArea): + __slots__ = ('_areaView') + def __init__(self, *args, **kwargs): + TTkAbstractScrollArea.__init__(self, *args, **kwargs) + self._name = kwargs.get('name' , 'TTkScrollArea' ) + if 'parent' in kwargs: kwargs.pop('parent') + self._areaView = _TTkAreaWidget(*args, **kwargs) + self.setFocusPolicy(TTkK.ClickFocus) + self.setViewport(self._areaView) \ No newline at end of file diff --git a/TermTk/TTkWidgets/splitter.py b/TermTk/TTkWidgets/splitter.py index 5b8a4423..ddd1842f 100644 --- a/TermTk/TTkWidgets/splitter.py +++ b/TermTk/TTkWidgets/splitter.py @@ -64,7 +64,7 @@ class TTkSplitter(TTkFrame): return 0, 0x1000 # this is because there is a hidden splitter at position -1 minsize = -1 - maxsize = 0x0 + maxsize = -1 for i in range(self._separatorSelected): item = self.layout().itemAt(i) minsize += item.minDimension(self._orientation)+1 diff --git a/TermTk/TTkWidgets/tabwidget.py b/TermTk/TTkWidgets/tabwidget.py index 257245ad..1e4d9b98 100644 --- a/TermTk/TTkWidgets/tabwidget.py +++ b/TermTk/TTkWidgets/tabwidget.py @@ -62,7 +62,7 @@ class TTkTabWidget(TTkFrame): self._tabColor = TTkCfg.theme.tabColor self._tabBorderColor = TTkCfg.theme.tabBorderColor self._tabSelectColor = TTkCfg.theme.tabSelectColor - super().__init__(self, *args, **kwargs) + TTkFrame.__init__(self, *args, **kwargs) self._name = kwargs.get('name' , 'TTkTabWidget') self.setLayout(TTkGridLayout()) self._viewport = TTkWidget(layout=TTkGridLayout()) diff --git a/TermTk/TTkWidgets/widget.py b/TermTk/TTkWidgets/widget.py index b8d8bcd8..3ecaf37c 100644 --- a/TermTk/TTkWidgets/widget.py +++ b/TermTk/TTkWidgets/widget.py @@ -145,11 +145,11 @@ class TTkWidget: self.moveEvent(x,y) def resize(self, w, h): - if w==self._width and h==self._height: return - self._width = w - self._height = h - self._canvas.resize(self._width, self._height) - self.update(repaint=True, updateLayout=True) + if w!=self._width or h!=self._height: + self._width = w + self._height = h + self._canvas.resize(self._width, self._height) + self.update(repaint=True, updateLayout=True) self.resizeEvent(w,h) def setGeometry(self, x, y, w, h): @@ -360,9 +360,11 @@ class TTkWidget: self.setMaximumWidth(maxw) self.setMaximumHeight(maxh) def setMaximumHeight(self, maxh): + if self._maxh == maxh: return self._maxh = maxh self.update(updateLayout=True, updateParent=True) def setMaximumWidth(self, maxw): + if self._maxw == maxw: return self._maxw = maxw self.update(updateLayout=True, updateParent=True) @@ -370,9 +372,11 @@ class TTkWidget: self.setMinimumWidth(minw) self.setMinimumHeight(minh) def setMinimumHeight(self, minh): + if self._minh == minh: return self._minh = minh self.update(updateLayout=True, updateParent=True) def setMinimumWidth(self, minw): + if self._minw == minw: return self._minw = minw self.update(updateLayout=True, updateParent=True) diff --git a/docs/TODO.md b/docs/TODO.md index f0c51ef5..703d8f7d 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -20,6 +20,8 @@ - [ ] Investigate the middle mouse button paste *note: It works only in "INSERT" mode on Vim* - [x] Handle Special Keys (UP, Down, . . .) +- [ ] Handle CTRL-Mouse +- [ ] Handle CTRL,ALT,SHIFT + Key (Tab, UP, Down, . . .) - [ ] Events https://tkinterexamples.com/events/events.html https://www.pythontutorial.net/tkinter/tkinter-event-binding/ diff --git a/tests/gittk.py b/tests/gittk.py index b43273eb..089f6897 100755 --- a/tests/gittk.py +++ b/tests/gittk.py @@ -80,7 +80,7 @@ for commit in commitResults: @ttk.pyTTkSlot(int) def _tableCallback(val): commit = allCommits[val] - diff = repo.git.diff(f"{commit.hexsha}",f"{commit.hexsha}~") + diff = repo.git.diff(f"{commit.hexsha}~",f"{commit.hexsha}") # ttk.TTkLog.debug(diff) lines = [] for line in diff.split('\n'): diff --git a/tests/showcase/list.py b/tests/showcase/list.py new file mode 100755 index 00000000..5ff733a6 --- /dev/null +++ b/tests/showcase/list.py @@ -0,0 +1,92 @@ +#!/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. + +import sys, os, argparse, math, random + +sys.path.append(os.path.join(sys.path[0],'../..')) +import TermTk as ttk + +words = ["Lorem", "ipsum", "dolor", "sit", "amet,", "consectetur", "adipiscing", "elit,", "sed", "do", "eiusmod", "tempor", "incididunt", "ut", "labore", "et", "dolore", "magna", "aliqua.", "Ut", "enim", "ad", "minim", "veniam,", "quis", "nostrud", "exercitation", "ullamco", "laboris", "nisi", "ut", "aliquip", "ex", "ea", "commodo", "consequat.", "Duis", "aute", "irure", "dolor", "in", "reprehenderit", "in", "voluptate", "velit", "esse", "cillum", "dolore", "eu", "fugiat", "nulla", "pariatur.", "Excepteur", "sint", "occaecat", "cupidatat", "non", "proident,", "sunt", "in", "culpa", "qui", "officia", "deserunt", "mollit", "anim", "id", "est", "laborum."] +def getWord(): + return random.choice(words) + +def demoList(root= None): + # Define the main Layout + splitter = ttk.TTkSplitter(parent=root, orientation=ttk.TTkK.HORIZONTAL) + frame2 = ttk.TTkFrame(parent=splitter, border=0, layout=ttk.TTkVBoxLayout()) + frame1 = ttk.TTkFrame(parent=splitter, border=0, layout=ttk.TTkVBoxLayout()) + frame3 = ttk.TTkFrame(parent=splitter, border=0, layout=ttk.TTkVBoxLayout()) + + # Multi Selection List + ttk.TTkLabel(parent=frame1, text="[ MultiSelect ]",maxHeight=2) + listWidgetMulti = ttk.TTkList(parent=frame1, maxWidth=40, minWidth=10, selectionMode=ttk.TTkK.MultiSelection) + + # Single Selection List + ttk.TTkLabel(parent=frame2, text="[ SingleSelect ]",maxHeight=2) + listWidgetSingle = ttk.TTkList(parent=frame2, maxWidth=40, minWidth=10) + + # Log Viewer + label1 = ttk.TTkLabel(parent=frame3, text="[ list1 ]",maxHeight=2) + label2 = ttk.TTkLabel(parent=frame3, text="[ list2 ]",maxHeight=2) + ttk.TTkLogViewer(parent=frame3)#, border=True) + + @ttk.pyTTkSlot(str) + def _listCallback1(label): + ttk.TTkLog.info(f"Clicked label1: {label}") + label1.text = f"[ list1 ] clicked {label}" + + @ttk.pyTTkSlot(str) + def _listCallback2(label): + ttk.TTkLog.info(f"Clicked label2: {label} - selected: {listWidgetMulti.selectedLabels()}") + label2.text = f"[ list2 ] {listWidgetMulti.selectedLabels()}" + + # Connect the signals to the 2 slots defines + listWidgetSingle.textClicked.connect(_listCallback1) + listWidgetMulti.textClicked.connect(_listCallback2) + + # populate the lists with random entries + for i in range(100): + listWidgetSingle.addItem(f"{i}) {getWord()} {getWord()}") + listWidgetMulti.addItem(f"{getWord()} {getWord()}") + + return splitter + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('-f', help='Full Screen', action='store_true') + args = parser.parse_args() + + ttk.TTkLog.use_default_file_logging() + + root = ttk.TTk() + if args.f: + rootGraph = root + root.setLayout(ttk.TTkGridLayout()) + else: + rootGraph = ttk.TTkWindow(parent=root,pos=(1,1), size=(100,40), title="Test List", border=True, layout=ttk.TTkGridLayout()) + demoList(rootGraph) + root.mainloop() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/tests/showcase/scrollarea.py b/tests/showcase/scrollarea.py new file mode 100755 index 00000000..dfff0f00 --- /dev/null +++ b/tests/showcase/scrollarea.py @@ -0,0 +1,77 @@ +#!/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. + +import sys, os, argparse, math, random + +sys.path.append(os.path.join(sys.path[0],'../..')) +import TermTk as ttk + +class graphTimerEvent(): + def __init__(self, w, delay): + self.timer = ttk.TTkTimer() + self.val = 10 + self.delay = delay + self.w = w + self.timer.timeout.connect(self.timerEvent) + self.timer.start(1) + @ttk.pyTTkSlot() + def timerEvent(self): + plot = [ math.sin( self.val *math.pi/40)*4*10 , + math.sin((self.val+15)*math.pi/40)*4*7, + math.sin((self.val+20)*math.pi/30)*4*5,] + self.val+=1 + self.w.addValue(plot) + self.timer.start(self.delay) + +def demoScrollArea(root= None): + scrollArea = ttk.TTkScrollArea(parent=root) + ttk.TTkTestWidgetSizes(pos=(0,0) , size=(50,25), parent=scrollArea.viewport(), border=True) + ttk.TTkTestWidgetSizes(pos=(10,25) , size=(40,20), parent=scrollArea.viewport(), border=True) + ttk.TTkTestWidgetSizes(pos=(20,50) , size=(60,10), parent=scrollArea.viewport(), border=True) + ttk.TTkTestWidgetSizes(pos=(50,0) , size=(40,10), parent=scrollArea.viewport(), border=True) + ttk.TTkTestWidgetSizes(pos=(100,0) , size=(40,10), parent=scrollArea.viewport(), border=True) + ttk.TTkTestWidgetSizes(pos=(150,0) , size=(40,10), parent=scrollArea.viewport(), border=True) + ttk.TTkTestWidgetSizes(pos=(50,31) , size=(60,10), parent=scrollArea.viewport(), border=True) + graph = ttk.TTkGraph( pos=(50,11) , size=(150,20), parent=scrollArea.viewport(), color=ttk.TTkColor.fg('#ff8800', modifier=ttk.TTkColorGradient(increment= 40))) + graphTimerEvent(graph, 0.1) + return scrollArea + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('-f', help='Full Screen', action='store_true') + args = parser.parse_args() + + ttk.TTkLog.use_default_file_logging() + + root = ttk.TTk() + if args.f: + rootGraph = root + root.setLayout(ttk.TTkGridLayout()) + else: + rootGraph = ttk.TTkWindow(parent=root,pos=(1,1), size=(100,40), title="Test Graph", border=True, layout=ttk.TTkGridLayout()) + demoScrollArea(rootGraph) + root.mainloop() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/tests/test.showcase.001.py b/tests/test.showcase.001.py index 9a019189..36bd0101 100755 --- a/tests/test.showcase.001.py +++ b/tests/test.showcase.001.py @@ -32,10 +32,12 @@ from showcase.layout import demoLayout from showcase.table import demoTable from showcase.tab import demoTab from showcase.tree import demoTree -from showcase.graph import demoGraph +from showcase.graph import demoGraph from showcase.splitter import demoSplitter from showcase.windows import demoWindows from showcase.formwidgets import demoFormWidgets +from showcase.scrollarea import demoScrollArea +from showcase.list import demoList def demoShowcase(root= None, border=True): tabWidget1 = ttk.TTkTabWidget(parent=root, border=border) @@ -43,12 +45,14 @@ def demoShowcase(root= None, border=True): tabWidget1.addTab(ttk.TTkTestWidget(border=True, title="Frame1.2"), " Label Test 1.2 ") tabWidget1.addTab(demoLayout(), " Layout Test ") tabWidget1.addTab(demoFormWidgets(), " Form Test ") + tabWidget1.addTab(demoList(), " List Test ") tabWidget1.addTab(demoGraph(), " Graph Test ") tabWidget1.addTab(demoTable(), " Table Test ") tabWidget1.addTab(demoTree(), " Tree Test ") tabWidget1.addTab(demoSplitter(), " Splitter Test ") tabWidget1.addTab(demoWindows(), " Windows Test ") tabWidget1.addTab(demoTab(), " Tab Test ") + tabWidget1.addTab(demoScrollArea(), " Scroll Area ") return tabWidget1 def main(): diff --git a/tests/test.ui.014.list.py b/tests/test.ui.014.list.py new file mode 100755 index 00000000..ce04c3bc --- /dev/null +++ b/tests/test.ui.014.list.py @@ -0,0 +1,92 @@ +#!/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. + +import sys, os, argparse, math, random + +sys.path.append(os.path.join(sys.path[0],'..')) +import TermTk as ttk + +words = ["Lorem", "ipsum", "dolor", "sit", "amet,", "consectetur", "adipiscing", "elit,", "sed", "do", "eiusmod", "tempor", "incididunt", "ut", "labore", "et", "dolore", "magna", "aliqua.", "Ut", "enim", "ad", "minim", "veniam,", "quis", "nostrud", "exercitation", "ullamco", "laboris", "nisi", "ut", "aliquip", "ex", "ea", "commodo", "consequat.", "Duis", "aute", "irure", "dolor", "in", "reprehenderit", "in", "voluptate", "velit", "esse", "cillum", "dolore", "eu", "fugiat", "nulla", "pariatur.", "Excepteur", "sint", "occaecat", "cupidatat", "non", "proident,", "sunt", "in", "culpa", "qui", "officia", "deserunt", "mollit", "anim", "id", "est", "laborum."] +def getWord(): + return random.choice(words) + +def demoList(root= None): + # Define the main Layout + splitter = ttk.TTkSplitter(parent=root, orientation=ttk.TTkK.HORIZONTAL) + frame2 = ttk.TTkFrame(parent=splitter, border=0, layout=ttk.TTkVBoxLayout()) + frame1 = ttk.TTkFrame(parent=splitter, border=0, layout=ttk.TTkVBoxLayout()) + frame3 = ttk.TTkFrame(parent=splitter, border=0, layout=ttk.TTkVBoxLayout()) + + # Multi Selection List + ttk.TTkLabel(parent=frame1, text="[ MultiSelect ]",maxHeight=2) + listWidgetMulti = ttk.TTkList(parent=frame1, maxWidth=40, minWidth=10, selectionMode=ttk.TTkK.MultiSelection) + + # Single Selection List + ttk.TTkLabel(parent=frame2, text="[ SingleSelect ]",maxHeight=2) + listWidgetSingle = ttk.TTkList(parent=frame2, maxWidth=40, minWidth=10) + + # Log Viewer + label1 = ttk.TTkLabel(parent=frame3, text="[ list1 ]",maxHeight=2) + label2 = ttk.TTkLabel(parent=frame3, text="[ list2 ]",maxHeight=2) + ttk.TTkLogViewer(parent=frame3)#, border=True) + + @ttk.pyTTkSlot(str) + def _listCallback1(label): + ttk.TTkLog.info(f"Clicked label1: {label}") + label1.text = f"[ list1 ] clicked {label}" + + @ttk.pyTTkSlot(str) + def _listCallback2(label): + ttk.TTkLog.info(f"Clicked label2: {label} - selected: {listWidgetMulti.selectedLabels()}") + label2.text = f"[ list2 ] {listWidgetMulti.selectedLabels()}" + + # Connect the signals to the 2 slots defines + listWidgetSingle.textClicked.connect(_listCallback1) + listWidgetMulti.textClicked.connect(_listCallback2) + + # populate the lists with random entries + for i in range(100): + listWidgetSingle.addItem(f"{i}) {getWord()} {getWord()}") + listWidgetMulti.addItem(f"{getWord()} {getWord()}") + + return splitter + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('-f', help='Full Screen', action='store_true') + args = parser.parse_args() + + ttk.TTkLog.use_default_file_logging() + + root = ttk.TTk() + if args.f: + rootGraph = root + root.setLayout(ttk.TTkGridLayout()) + else: + rootGraph = ttk.TTkWindow(parent=root,pos=(1,1), size=(100,40), title="Test List", border=True, layout=ttk.TTkGridLayout()) + demoList(rootGraph) + root.mainloop() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/tests/test.ui.015.scrollArea.py b/tests/test.ui.015.scrollArea.py new file mode 100755 index 00000000..e54f4c2c --- /dev/null +++ b/tests/test.ui.015.scrollArea.py @@ -0,0 +1,58 @@ +#!/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. + +import sys, os, argparse, math, random + +sys.path.append(os.path.join(sys.path[0],'..')) +import TermTk as ttk + +def demoScrollArea(root= None): + scrollArea = ttk.TTkScrollArea(parent=root) + ttk.TTkTestWidgetSizes(pos=(0,0) , size=(40,20), parent=scrollArea.viewport(), border=True) + ttk.TTkTestWidgetSizes(pos=(10,25) , size=(40,20), parent=scrollArea.viewport(), border=True) + ttk.TTkTestWidgetSizes(pos=(20,50) , size=(60,10), parent=scrollArea.viewport(), border=True) + ttk.TTkTestWidgetSizes(pos=(50,0) , size=(40,10), parent=scrollArea.viewport(), border=True) + ttk.TTkTestWidgetSizes(pos=(100,0) , size=(40,10), parent=scrollArea.viewport(), border=True) + ttk.TTkTestWidgetSizes(pos=(150,0) , size=(40,10), parent=scrollArea.viewport(), border=True) + ttk.TTkTestWidgetSizes(pos=(60,30) , size=(60,10), parent=scrollArea.viewport(), border=True) + return scrollArea + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('-f', help='Full Screen', action='store_true') + args = parser.parse_args() + + ttk.TTkLog.use_default_file_logging() + + root = ttk.TTk() + if args.f: + rootGraph = root + root.setLayout(ttk.TTkGridLayout()) + else: + rootGraph = ttk.TTkWindow(parent=root,pos=(1,1), size=(100,40), title="Test Graph", border=True, layout=ttk.TTkGridLayout()) + demoScrollArea(rootGraph) + root.mainloop() + +if __name__ == "__main__": + main() \ No newline at end of file