Browse Source

Merge pull request #68 from ceccopierangiolieugenio/Variable_Width_Char_Support

Fix #67 : Variable width char support
pull/71/head 0.13.0-a
Ceccopierangiolieugenio 3 years ago committed by GitHub
parent
commit
b2d42668f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 137
      TermTk/TTkCore/canvas.py
  2. 172
      TermTk/TTkCore/string.py
  3. 15
      TermTk/TTkGui/textcursor.py
  4. 6
      TermTk/TTkGui/textwrap.py
  5. 13
      TermTk/TTkTemplates/text.py
  6. 29
      TermTk/TTkTestWidgets/logviewer.py
  7. 16
      TermTk/TTkTestWidgets/testwidget.py
  8. 2
      TermTk/TTkTheme/draw_ascii.py
  9. 2
      TermTk/TTkTheme/draw_utf8.py
  10. 7
      TermTk/TTkTheme/theme.py
  11. 12
      TermTk/TTkWidgets/Fancy/tableview.py
  12. 3
      TermTk/TTkWidgets/Fancy/treewidget.py
  13. 5
      TermTk/TTkWidgets/TTkModelView/treewidget.py
  14. 4
      TermTk/TTkWidgets/TTkModelView/treewidgetitem.py
  15. 4
      TermTk/TTkWidgets/combobox.py
  16. 64
      TermTk/TTkWidgets/lineedit.py
  17. 4
      TermTk/TTkWidgets/menubar.py
  18. 5
      TermTk/TTkWidgets/texedit.py
  19. 6
      demo/demo.py
  20. 75
      demo/showcase/_showcasehelper.py
  21. 13
      demo/showcase/fancytable.py
  22. 9
      demo/showcase/fancytree.py
  23. 17
      demo/showcase/formwidgets.py
  24. 10
      demo/showcase/list.py
  25. 42
      demo/showcase/textedit.py
  26. 9
      demo/showcase/tree.py
  27. 6
      docs/MDNotes/TODO.md
  28. 98
      tests/test.draw.006.py
  29. 66
      tests/test.draw.007.py
  30. 7
      tests/test.metaclass.001.py
  31. 50
      tests/test.metaclass.002.py
  32. 1
      tests/timeit/.gitignore
  33. 88
      tests/timeit/00.template.py
  34. 158
      tests/timeit/04.wcwidth.bisearch.py
  35. 249
      tests/timeit/05.wcwidth.bisearch.py
  36. 143
      tests/timeit/06.functionPointer.py
  37. 5
      tools/check.import.sh
  38. 56
      tools/gen.utf8.sizeMap.py

137
TermTk/TTkCore/canvas.py

@ -22,6 +22,8 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import unicodedata
from TermTk.TTkCore.TTkTerm.term import TTkTerm
from TermTk.TTkCore.constant import TTkK
from TermTk.TTkCore.log import TTkLog
@ -29,7 +31,6 @@ from TermTk.TTkCore.cfg import TTkCfg
from TermTk.TTkCore.color import TTkColor
from TermTk.TTkCore.string import TTkString
class TTkCanvas:
''' Init the Canvas object
@ -153,24 +154,7 @@ class TTkCanvas:
color = colors[i]
align = alignments[i]
if w > 0:
line = ""
lentxt = len(txt)
if lentxt > w:
line += txt[0:w]
else:
pad = w-lentxt
if align in [TTkK.NONE,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.drawText(pos=(x,y), text=line, color=color)
self.drawTTkString(pos=(x,y), text=txt, width=w, color=color, alignment=align)
x += w + 1
def drawChar(self, pos, char, color=TTkColor.RST):
@ -178,6 +162,46 @@ class TTkCanvas:
x,y = pos
self._set(y, x, char, color)
def drawTTkString(self, pos, text, width=None, color=TTkColor.RST, alignment=TTkK.NONE, forceColor=False):
'''
NOTE:
drawText is one of the most abused functions,
there is some redundant code here in order to reduce the footprint
'''
if not self._visible: return
# Check the size and bounds
x,y = pos
if y<0 or y>=self._height : return
lentxt = text.termWidth()
if width is None or width<0:
width = lentxt
if x+width<0 or x>=self._width : return
text = text.align(width=width, alignment=alignment, color=color)
txt, colors = text.tab2spaces().getData()
if forceColor:
colors=[color]*len(colors)
a,b = max(0,-x), min(len(txt),self._width-x)
for i in range(a,b):
#self._set(y, x+i, txt[i-x], colors[i-x])
self._data[y][x+i] = txt[i]
if colors[i] == TTkColor.RST != color:
self._colors[y][x+i] = color.mod(x+i,y)
else:
self._colors[y][x+i] = colors[i].mod(x+i,y)
# Check the full wide chars on the edge of the two canvasses
if self._data[y][x+a] == '':
self._data[y][x+a] = TTkCfg.theme.unicodeWideOverflowCh[0]
self._colors[y][x+a] = TTkCfg.theme.unicodeWideOverflowColor
if ( len(ch:=self._data[y][x+b-1])==1
and unicodedata.east_asian_width(ch)=='W'):
self._data[y][x+b-1] = TTkCfg.theme.unicodeWideOverflowCh[1]
self._colors[y][x+b-1] = TTkCfg.theme.unicodeWideOverflowColor
def drawText(self, pos, text, width=None, color=TTkColor.RST, alignment=TTkK.NONE, forceColor=False):
'''
NOTE:
@ -185,6 +209,8 @@ class TTkCanvas:
there is some redundant code here in order to reduce the footprint
'''
if not self._visible: return
if isinstance(text, TTkString):
return self.drawTTkString(pos, text, width, color, alignment, forceColor)
# Check the size and bounds
x,y = pos
@ -196,39 +222,26 @@ class TTkCanvas:
if x+width<0 or x>=self._width : return
if isinstance(text, TTkString):
text = text.align(width=width, alignment=alignment, color=color)
txt, colors = text.tab2spaces().getData()
if forceColor:
colors=[color]*len(colors)
for i in range(max(0,-x), min(len(txt),self._width-x)):
#self._set(y, x+i, txt[i-x], colors[i-x])
self._data[y][x+i] = txt[i]
if colors[i] == TTkColor.RST != color:
self._colors[y][x+i] = color.mod(x+i,y)
else:
self._colors[y][x+i] = colors[i].mod(x+i,y)
text = text.replace('\t',' ')
if lentxt < width:
pad = width-lentxt
if alignment in [TTkK.NONE, TTkK.LEFT_ALIGN]:
text = text + " "*pad
elif alignment == TTkK.RIGHT_ALIGN:
text = " "*pad + text
elif alignment == TTkK.CENTER_ALIGN:
p1 = pad//2
p2 = pad-p1
text = " "*p1 + text+" "*p2
elif alignment == TTkK.JUSTIFY:
# TODO: Text Justification
text = text + " "*pad
else:
text = text.replace('\t',' ')
if lentxt < width:
pad = width-lentxt
if alignment in [TTkK.NONE, TTkK.LEFT_ALIGN]:
text = text + " "*pad
elif alignment == TTkK.RIGHT_ALIGN:
text = " "*pad + text
elif alignment == TTkK.CENTER_ALIGN:
p1 = pad//2
p2 = pad-p1
text = " "*p1 + text+" "*p2
elif alignment == TTkK.JUSTIFY:
# TODO: Text Justification
text = text + " "*pad
else:
text=text[:width]
text=text[:width]
arr = list(text)
for i in range(0, min(len(arr),self._width-x)):
self._set(y, x+i, arr[i], color)
arr = list(text)
for i in range(0, min(len(arr),self._width-x)):
self._set(y, x+i, arr[i], color)
def drawBoxTitle(self, pos, size, text, align=TTkK.CENTER_ALIGN, color=TTkColor.RST, colorText=TTkColor.RST, grid=0):
if not self._visible: return
@ -555,7 +568,7 @@ class TTkCanvas:
self._set(y,x+width-1, mb[5], color)
off = 0
for i in shortcuts:
self._set(y,x+i+off, text[i], shortcutColor)
self._set(y,x+i+off, text.charAt(i), shortcutColor)
def execPaint(self, winw, winh):
pass
@ -599,8 +612,26 @@ class TTkCanvas:
hslice = h if y+h < by+bh else by+bh-y
for iy in range(yoffset,hslice):
self._data[y+iy][x+xoffset:x+wslice] = canvas._data[iy][xoffset:wslice]
self._colors[y+iy][x+xoffset:x+wslice] = canvas._colors[iy][xoffset:wslice]
a, b = x+xoffset, x+wslice
self._data[y+iy][a:b] = canvas._data[iy][xoffset:wslice]
self._colors[y+iy][a:b] = canvas._colors[iy][xoffset:wslice]
# Check the full wide chars on the edge of the two canvasses
if self._data[y+iy][a]=='':
self._data[y+iy][a] = TTkCfg.theme.unicodeWideOverflowCh[0]
self._colors[y+iy][a] = TTkCfg.theme.unicodeWideOverflowColor
if ( len(ch:=self._data[y+iy][b-1])==1
and unicodedata.east_asian_width(ch)=='W'):
self._data[y+iy][b-1] = TTkCfg.theme.unicodeWideOverflowCh[1]
self._colors[y+iy][b-1] = TTkCfg.theme.unicodeWideOverflowColor
if ( a and len(ch:=self._data[y+iy][a-1])==1
and unicodedata.east_asian_width(ch)=='W'):
self._data[y+iy][a-1] = TTkCfg.theme.unicodeWideOverflowCh[1]
self._colors[y+iy][a-1] = TTkCfg.theme.unicodeWideOverflowColor
if ( b<self._width-1 and self._data[y+iy][b]=='' ):
self._data[y+iy][b] = TTkCfg.theme.unicodeWideOverflowCh[0]
self._colors[y+iy][b] = TTkCfg.theme.unicodeWideOverflowColor
def pushToTerminal(self, x, y, w, h):
# TTkLog.debug("pushToTerminal")

172
TermTk/TTkCore/string.py

@ -23,7 +23,9 @@
# SOFTWARE.
import re
import unicodedata
from TermTk.TTkCore.cfg import TTkCfg
from TermTk.TTkCore.constant import TTkK
from TermTk.TTkCore.color import TTkColor, _TTkColor
@ -56,7 +58,7 @@ class TTkString():
# Combination of constructors (Highly Unrecommended)
str7 = TTkString("test 7", color=TTkColor.fg('#FF0000'))
'''
__slots__ = ('_text','_colors','_baseColor','_hasTab')
__slots__ = ('_text','_colors','_baseColor','_hasTab','_hasSpecialWidth')
def __init__(self, text="", color=None):
if issubclass(type(text), TTkString):
@ -67,6 +69,7 @@ class TTkString():
self._baseColor = TTkColor.RST if color is None else color
self._text, self._colors = TTkString._parseAnsi(str(text), self._baseColor)
self._hasTab = '\t' in self._text
self._checkWidth()
# raise AttributeError(f"{type(text)} not supported in TTkString")
@staticmethod
@ -85,6 +88,12 @@ class TTkString():
colret += [color]*(len(text)-pos)
return txtret, colret
def termWidth(self):
if self._hasSpecialWidth:
return self._termWidthW()
else:
return len(self)
def __len__(self):
return len(self._text)
@ -106,6 +115,7 @@ class TTkString():
ret._colors = self._colors
ret._baseColor = other
ret._hasTab = '\t' in ret._text
ret._checkWidth()
return ret
def __radd__(self, other):
@ -118,6 +128,7 @@ class TTkString():
ret._text = other + self._text
ret._colors = [self._baseColor]*len(other) + self._colors
ret._hasTab = '\t' in ret._text
ret._checkWidth()
return ret
def __setitem__(self, index, value):
@ -134,11 +145,21 @@ class TTkString():
def __gt__(self, other): return self._text > other if type(other) is str else self._text > other._text
def __ge__(self, other): return self._text >= other if type(other) is str else self._text >= other._text
def isdigit(self):
return self._text.isdigit()
def lstrip(self, ch):
ret = TTkString()
ret._text = self._text.lstrip(ch)
ret._colors = self._colors[-len(ret._text):]
return ret
def charAt(self, pos):
return self._text[pos]
def setCharAt(self, pos, char):
self._text = self._text[:pos]+char+self._text[pos+1:]
self._checkWidth()
return self
def colorAt(self, pos):
@ -160,15 +181,16 @@ class TTkString():
ret._colors += self._colors[0:pos]
for s in slices[1:]:
c = self._colors[pos]
lentxt = len(ret._text)
lentxt = ret.termWidth()
spaces = tabSpaces - (lentxt+tabSpaces)%tabSpaces
ret._text += " "*spaces + s
ret._colors += [c]*spaces + self._colors[pos+1:pos+1+len(s)]
ret._checkWidth()
pos+=len(s)+1
return ret
def tabCharPos(self, pos, tabSpaces=4, alignTabRight=False):
'''Return the char position in the string from the position in its representation with the tab solved
'''Return the char position in the string from the position in its representation with the tab and variable char sizes are solved
i.e.
@ -176,12 +198,14 @@ class TTkString():
pos X = 11
tab2Spaces |----------|---------------------|
Tabs |--| | |-| |-| |
_text Lorem ipsum dolor sit amet,
chars .....t .....t .....t ...t.....
ret x = 8 (tab is a char)
Tabs |-| | |-| |-| |
_text L😁rem ipsum dolor sit amet,
chars .. ...t .....t .....t ...t.....
ret x = 7 (tab is a char)
'''
if not self._hasTab: return pos
if not self._hasTab and not self._hasSpecialWidth: return pos
if self._hasSpecialWidth: return self._tabCharPosWideChar(pos, tabSpaces, alignTabRight)
slices = self._text.split("\t")
postxt = 0 # position of the text
lentxt = 0 # length of the text with resolved tabs
@ -202,6 +226,37 @@ class TTkString():
postxt += 1
return len(self._text)
def _tabCharPosWideChar(self, pos, tabSpaces=4, alignTabRight=False):
'''Return the char position in the string from the position in its representation with the tab and variable char sizes are solved
i.e.
::
pos X = 11
tab2Spaces |----------|---------------------|
Tabs |-| | |-| |-| |
_text L😁rem ipsum dolor sit amet,
chars .. ...t .....t .....t ...t.....
ret x = 7 (tab is a char)
'''
# get pos in the slice:
dx = pos
pp = 0
for i,ch in enumerate(self._text):
if ch=='\t':
pp += tabSpaces - (pp+tabSpaces)%tabSpaces
elif unicodedata.east_asian_width(ch) == 'W':
pp += 2
elif unicodedata.category(ch) in ('Me','Mn'):
pass
else:
pp += 1
if dx < pp:
return i
return len(self._text)
def toAscii(self):
''' Return the ascii representation of the string '''
return self._text
@ -227,7 +282,7 @@ class TTkString():
:param alignment: the alignment of the text to the full width :class:`~TermTk.TTkCore.constant.TTkConstant.Alignment.NONE`
:type alignment: :class:`~TermTk.TTkCore.constant.TTkConstant.Alignment`, optional
'''
lentxt = len(self._text)
lentxt = self.termWidth()
if not width or width == lentxt: return self
ret = TTkString()
@ -249,11 +304,32 @@ class TTkString():
# TODO: Text Justification
ret._text = self._text + " " *pad
ret._colors = self._colors + [color]*pad
elif self._hasSpecialWidth:
# Trim the string to a fixed size taking care of the variable width unicode chars
rt = ""
sz = 0
for ch in self._text:
if unicodedata.category(ch) in ('Me','Mn'):
rt += ch
continue
if sz == width:
ret._text = rt
ret._colors = self._colors[:len(rt)]
break
elif sz > width:
ret._text = rt[:-1]+TTkCfg.theme.unicodeWideOverflowCh[1]
ret._colors = self._colors[:len(ret._text)]
ret._colors[-1] = TTkCfg.theme.unicodeWideOverflowColor
break
rt += ch
sz += 2 if unicodedata.east_asian_width(ch) == 'W' else 1
else:
# Legacy, trim the string
ret._text = self._text[:width]
ret._colors = self._colors[:width]
ret._hasTab = '\t' in ret._text
ret._checkWidth()
return ret
@ -301,6 +377,7 @@ class TTkString():
ret._text = self._text.replace(*args, **kwargs)
ret._hasTab = '\t' in ret._text
ret._checkWidth()
return ret
@ -321,6 +398,7 @@ class TTkString():
ret = TTkString()
ret._text += self._text
ret._hasTab = self._hasTab
ret._hasSpecialWidth = self._hasSpecialWidth
if match:
ret._colors += self._colors
start=0
@ -359,6 +437,7 @@ class TTkString():
ret = TTkString()
ret._text += self._text
ret._hasTab = self._hasTab
ret._hasSpecialWidth = self._hasSpecialWidth
if match:
ret._colors += self._colors
start=0
@ -389,6 +468,7 @@ class TTkString():
ret._text = self._text[fr:to]
ret._colors = self._colors[fr:to]
ret._hasTab = '\t' in ret._text
ret._checkWidth()
return ret
def split(self, separator ):
@ -413,7 +493,10 @@ class TTkString():
return ret
def getData(self):
return (self._text,self._colors)
if self._hasSpecialWidth:
return self._getDataW()
else:
return (tuple(self._text), self._colors)
def search(self, regexp, ignoreCase=False):
''' Return the **re.match** of the **regexp**
@ -425,6 +508,9 @@ class TTkString():
'''
return re.search(regexp, self._text, re.IGNORECASE if ignoreCase else 0)
def find(self, *args, **kwargs):
return self._text.find(*args, **kwargs)
def findall(self, regexp, ignoreCase=False):
''' FindAll the **regexp** matches in the string
@ -450,3 +536,69 @@ class TTkString():
for s in strings[1:]:
ret += self + s
return ret
# Zero/Half/Normal sized chars helpers:
@staticmethod
def _isSpecialWidthChar(ch):
return ( unicodedata.east_asian_width(ch) == 'W' or
unicodedata.category(ch) in ('Me','Mn') )
@staticmethod
def _getWidthText(txt):
return ( len(txt) +
sum(unicodedata.east_asian_width(ch) == 'W' for ch in txt) -
sum(unicodedata.category(ch) in ('Me','Mn') for ch in txt) )
@staticmethod
def _getLenTextWoZero(txt):
return ( len(txt) -
sum(unicodedata.category(ch) in ('Me','Mn') for ch in txt) )
def nextPos(self, pos):
pos += 1
for i,ch in enumerate(self._text[pos:]):
if not unicodedata.category(ch) in ('Me','Mn'):
return pos+i
return len(self._text)
def prevPos(self, pos):
# from TermTk.TTkCore.log import TTkLog
# TTkLog.debug(f"->{self._text[:pos]}<- {pos=}")
# TTkLog.debug(f"{str(reversed(self._text[:pos]))} {pos=}")
for i,ch in enumerate(reversed(self._text[:pos])):
# TTkLog.debug(f"{i}---> {ch} ")
if not unicodedata.category(ch) in ('Me','Mn'):
return pos-i-1
return 0
def _checkWidth(self):
self._hasSpecialWidth = (
any(unicodedata.east_asian_width(ch) == 'W' for ch in self._text) or
any(unicodedata.category(ch) in ('Me','Mn') for ch in self._text) )
def _termWidthW(self):
''' String displayed length
This value consider the displayed size (Zero, Half, Full) of each character.
'''
return ( len(self._text) +
sum(unicodedata.east_asian_width(ch) == 'W' for ch in self._text) -
sum(unicodedata.category(ch) in ('Me','Mn') for ch in self._text) )
def _getDataW(self):
retTxt = []
retCol = []
for i,ch in enumerate(self._text):
if unicodedata.east_asian_width(ch) == 'W':
retTxt += (ch,'')
retCol += (self._colors[i],self._colors[i])
elif unicodedata.category(ch) in ('Me','Mn'):
if retTxt:
retTxt[-1]+=ch
#else:
# retTxt = [f"{ch}"]
# retCol = [TTkColor.RST]
else:
retTxt.append(ch)
retCol.append(self._colors[i])
return (retTxt, retCol)

15
TermTk/TTkGui/textcursor.py

@ -283,12 +283,14 @@ class TTkTextCursor():
currPos = self.position().toNum()
def moveRight(cID,p,_):
if p.pos < len(self._document._dataLines[p.line]):
self.setPosition(p.line, p.pos+1, moveMode, cID=cID)
nextPos = self._document._dataLines[p.line].nextPos(p.pos)
self.setPosition(p.line, nextPos, moveMode, cID=cID)
elif p.line < len(self._document._dataLines)-1:
self.setPosition(p.line+1, 0, moveMode, cID=cID)
def moveLeft(cID,p,_):
if p.pos > 0:
self.setPosition(p.line, p.pos-1, moveMode, cID=cID)
prevPos = self._document._dataLines[p.line].prevPos(p.pos)
self.setPosition(p.line, prevPos, moveMode, cID=cID)
elif p.line > 0:
self.setPosition(p.line-1, len(self._document._dataLines[p.line-1]) , moveMode, cID=cID)
def moveUpDown(offset):
@ -327,10 +329,13 @@ class TTkTextCursor():
# the newline is not replaced
for p in self._properties:
if not p.hasSelection():
line = p.position.line
line = p.position.line
pos = p.position.pos
lenWoZero = TTkString._getLenTextWoZero(text)
size = len(self._document._dataLines[line])
pos = min(size,pos+len(text))
for _ in range(lenWoZero):
pos = self._document._dataLines[line].nextPos(pos)
pos = min(size,pos)
p.anchor.set(line,pos)
return self.insertText(text)
@ -447,7 +452,7 @@ class TTkTextCursor():
splitAfter = self._document._dataLines[line].substring(fr=pos)
xFrom = pos
xTo = pos
selectRE = '[a-zA-Z0-9:,./]*'
selectRE = '[^ \t\r\n\(\)\[\]\.\,\+\-\*\/]*'
if m := splitBefore.search(selectRE+'$'):
xFrom -= len(m.group(0))
if m := splitAfter.search('^'+selectRE):

6
TermTk/TTkGui/textwrap.py

@ -91,7 +91,7 @@ class TTkTextWrap():
return
while len(l):
fl = l.tab2spaces(self._tabSpaces)
if len(fl) <= w:
if fl.termWidth() <= w:
self._lines.append((i,(fr,fr+len(l)+1)))
l=[]
else:
@ -113,7 +113,7 @@ class TTkTextWrap():
for i, (dt, (fr, to)) in enumerate(self._lines):
if dt == line and fr <= pos <= to:
l = self._textDocument._dataLines[dt].substring(fr,pos).tab2spaces(self._tabSpaces)
return len(l), i
return l.termWidth(), i
return 0,0
def screenToDataPosition(self, x, y):
@ -135,5 +135,5 @@ class TTkTextWrap():
x = max(0,x)
s = self._textDocument._dataLines[dt].substring(fr,to)
x = s.tabCharPos(x, self._tabSpaces)
x = len(s.substring(0,x).tab2spaces(self._tabSpaces))
x = s.substring(0,x).tab2spaces(self._tabSpaces).termWidth()
return x, y

13
TermTk/TTkTemplates/text.py

@ -22,10 +22,16 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from TermTk.TTkCore.string import TTkString
class TText():
#__slots__ = ('_text')
def __init__(self, *args, **kwargs):
self._text = kwargs.get('text', "" )
text = kwargs.get('text', TTkString() )
if issubclass(type(text), TTkString):
self._text = text
else:
self._text = TTkString(text)
def textUpdated(self, text): pass
@ -36,6 +42,9 @@ class TText():
@text.setter
def text(self, text):
if self.text != text:
self._text = text
if issubclass(type(text), TTkString):
self._text = text
else:
self._text = TTkString(text)
self.textUpdated(text)

29
TermTk/TTkTestWidgets/logviewer.py

@ -26,6 +26,7 @@ import os
from TermTk.TTkCore.constant import TTkK
from TermTk.TTkCore.log import TTkLog
from TermTk.TTkCore.color import TTkColor
from TermTk.TTkCore.string import TTkString
from TermTk.TTkCore.signal import pyTTkSlot
from TermTk.TTkAbstract.abstractscrollarea import TTkAbstractScrollArea
from TermTk.TTkAbstract.abstractscrollview import TTkAbstractScrollView
@ -35,7 +36,7 @@ class _TTkLogViewer(TTkAbstractScrollView):
def __init__(self, *args, **kwargs):
TTkAbstractScrollView.__init__(self, *args, **kwargs)
self._name = kwargs.get('name' , '_TTkLogViewer' )
self._messages = [""]
self._messages = [TTkString()]
self._cwd = os.getcwd()
self._follow = kwargs.get('follow' , False )
TTkLog.installMessageHandler(self.loggingCallback)
@ -46,7 +47,7 @@ class _TTkLogViewer(TTkAbstractScrollView):
self.update()
def viewFullAreaSize(self) -> (int, int):
w = max( len(m) for m in self._messages)
w = max( m.termWidth() for m in self._messages)
h = len(self._messages)
return w , h
@ -55,13 +56,13 @@ class _TTkLogViewer(TTkAbstractScrollView):
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,"_"))
if mode == TTkLog.InfoMsg: logType = TTkString("INFO " ,TTkColor.fg("#00ff00"))
elif mode == TTkLog.DebugMsg: logType = TTkString("DEBUG" ,TTkColor.fg("#00ffff"))
elif mode == TTkLog.ErrorMsg: logType = TTkString("ERROR" ,TTkColor.fg("#ff0000"))
elif mode == TTkLog.FatalMsg: logType = TTkString("FATAL" ,TTkColor.fg("#ff0000"))
elif mode == TTkLog.WarningMsg: logType = TTkString("WARNING ",TTkColor.fg("#ff0000"))
elif mode == TTkLog.CriticalMsg: logType = TTkString("CRITICAL",TTkColor.fg("#ff0000"))
self._messages.append(logType+TTkString(f": {context.file}:{context.line} {message}".replace(self._cwd,"_")))
offx, offy = self.getViewOffsets()
_,h = self.size()
if self._follow or offy == len(self._messages)-h-1:
@ -73,14 +74,8 @@ class _TTkLogViewer(TTkAbstractScrollView):
def paintEvent(self):
ox,oy = self.getViewOffsets()
_,h = self.size()
for y, message in enumerate(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)
for y, message in enumerate(self._messages[oy:oy+h]):
self._canvas.drawTTkString(pos=(-ox,y),text=message)
class TTkLogViewer(TTkAbstractScrollArea):
__slots__ = ('_logView')

16
TermTk/TTkTestWidgets/testwidget.py

@ -33,14 +33,14 @@ from TermTk.TTkWidgets.frame import *
class _TestContent(TTkWidget):
def paintEvent(self):
# TTkLog.debug(f"Test Paint - {self._name}")
y=0; self._canvas.drawText(pos=(-5,y),color=TTkColor.fg("#ff0000"),text=" Lorem ipsum dolor sit amet,")
y+=1; self._canvas.drawText(pos=(0,y),color=TTkColor.fg("#ff8800"),text="consectetur adipiscing elit,")
y+=1; self._canvas.drawText(pos=(0,y),color=TTkColor.fg("#ffff00"),text="sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.")
y+=1; self._canvas.drawText(pos=(0,y),color=TTkColor.fg("#00ff00"),text="Ut enim ad minim veniam,")
y+=1; self._canvas.drawText(pos=(0,y),color=TTkColor.fg("#00ffff"),text="quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.")
y+=1; self._canvas.drawText(pos=(0,y),color=TTkColor.fg("#0088ff"),text="Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.")
y+=1; self._canvas.drawText(pos=(0,y),color=TTkColor.fg("#0000ff"),text="Excepteur sint occaecat cupidatat non proident,")
y+=1; self._canvas.drawText(pos=(0,y),color=TTkColor.fg("#ff00ff"),text="sunt in culpa qui officia deserunt mollit anim id est laborum.")
y=0; self._canvas.drawText(pos=(-5,y),text=TTkString(color=TTkColor.fg("#ff0000") ,text=" L😎rem ipsum dolor sit amet,"))
y+=1; self._canvas.drawText(pos=(0,y), text=TTkString(color=TTkColor.fg("#ff8800") ,text="consectetur adipiscing elit,"))
y+=1; self._canvas.drawText(pos=(0,y), text=TTkString(color=TTkColor.fg("#ffff00") ,text="sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."))
y+=1; self._canvas.drawText(pos=(0,y), text=TTkString(color=TTkColor.fg("#00ff00") ,text="Ut enim ad minim veniam,"))
y+=1; self._canvas.drawText(pos=(0,y), text=TTkString(color=TTkColor.fg("#00ffff") ,text="quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."))
y+=1; self._canvas.drawText(pos=(0,y), text=TTkString(color=TTkColor.fg("#0088ff") ,text="Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur."))
y+=1; self._canvas.drawText(pos=(0,y), text=TTkString(color=TTkColor.fg("#0000ff") ,text="Excepteur sint occaecat cupidatat non proident,"))
y+=1; self._canvas.drawText(pos=(0,y), text=TTkString(color=TTkColor.fg("#ff00ff") ,text="sunt in culpa qui officia deserunt mollit anim id est laborum."))
y+=1; self._canvas.drawGrid(
pos=(0,y),size=(self._width,self._height-y),
hlines=(2,5,7), vlines=(4,7,15,30),

2
TermTk/TTkTheme/draw_ascii.py

@ -126,3 +126,5 @@ class TTkTheme():
'X','X','X','X','X','X','X','X','X','X','X','X','X','X','X','X',
'X','X','X','X','X','X','X','X','X','X','X','X','X','X','X','X',
'X','X','X','X','X','X','X','X','X','X','X','X','X','X','X','X')
unicodeWideOverflowCh = ('<','>')

2
TermTk/TTkTheme/draw_utf8.py

@ -223,3 +223,5 @@ class TTkTheme():
'','','','','','','','','','','','','','','','',
'','','','','','','','','','','','','','','','',
'','','','','','','','','','','','','','','','')
unicodeWideOverflowCh = ('','')

7
TermTk/TTkTheme/theme.py

@ -51,6 +51,7 @@ class TTkTheme():
menuBar = draw_utf8.TTkTheme.menuBar
tab = draw_utf8.TTkTheme.tab
braille = draw_utf8.TTkTheme.braille
unicodeWideOverflowCh = draw_utf8.TTkTheme.unicodeWideOverflowCh
fileNameColor = TTkColor.RST # Simil NerdTree purple
'''Default to **TTkColor.RST # Simil NerdTree purple**'''
@ -82,6 +83,7 @@ class TTkTheme():
TTkTheme.menuBar = theme['draw'].TTkTheme.menuBar
TTkTheme.tab = theme['draw'].TTkTheme.tab
TTkTheme.braille = theme['draw'].TTkTheme.braille
TTkTheme.unicodeWideOverflowCh = theme['draw'].TTkTheme.unicodeWideOverflowCh
TTkTheme.fileIcon = theme['file'].FileIcon
TTkHelper.updateAll()
@ -236,4 +238,7 @@ class TTkTheme():
textEditLineNumberWrapcharColor = TTkColor.fg("#888888")+TTkColor.bg("#333333")
'''Default to **TTkColor.fg("#aaaaaa")+TTkColor.bg("#333333")**'''
textEditLineNumberSeparatorColor = TTkColor.fg("#444444")
'''Default to **TTkColor.fg("#444444")**'''
'''Default to **TTkColor.fg("#444444")**'''
unicodeWideOverflowColor = TTkColor.fg("#888888")+TTkColor.bg("#000088")
'''Default to **TTkColor.fg("#888888")**+**TTkColor.bg("#000088")**'''

12
TermTk/TTkWidgets/Fancy/tableview.py

@ -25,6 +25,7 @@
from TermTk.TTkCore.constant import TTkK
from TermTk.TTkCore.signal import pyTTkSlot, pyTTkSignal
from TermTk.TTkCore.color import TTkColor
from TermTk.TTkCore.string import TTkString
from TermTk.TTkWidgets.widget import TTkWidget
from TermTk.TTkLayouts.gridlayout import TTkGridLayout
from TermTk.TTkAbstract.abstractscrollview import TTkAbstractScrollView
@ -35,7 +36,7 @@ class _TTkFancyTableViewHeader(TTkWidget):
TTkWidget.__init__(self, *args, **kwargs)
self._name = kwargs.get('name' , '_TTkFancyTableViewHeader' )
self._columns = kwargs.get('columns' , [-1] )
self._header = [""]*len(self._columns)
self._header = [TTkString()]*len(self._columns)
self._alignments = [TTkK.NONE]*len(self._columns)
self._headerColor = kwargs.get('headerColor' , TTkColor.BOLD )
self.setMaximumHeight(1)
@ -49,11 +50,12 @@ class _TTkFancyTableViewHeader(TTkWidget):
def setHeader(self, header):
if len(header) != len(self._columns):
return
self._header = header
self._header = [TTkString(i) if isinstance(i,str) else i if issubclass(type(i), TTkString) else TTkString() for i in header]
def setColumnSize(self, columns):
self._columns = columns
self._header += [""]*(len(self._columns)-len(self._header))
self._header += [TTkString()]*(len(self._columns)-len(self._header))
self._alignments = [TTkK.NONE]*len(self._columns)
def paintEvent(self):
@ -198,7 +200,7 @@ class _TTkFancyTableView(TTkAbstractScrollView):
def appendItem(self, item, id=None):
if len(item) != len(self._columns):
return
textItem = [i if isinstance(i,str) else "" for i in item]
textItem = [TTkString(i) if isinstance(i,str) else i if issubclass(type(i), TTkString) else TTkString() for i in item]
widgetItem = [i if isinstance(i,TTkWidget) else None for i in item]
if id is not None:
self._tableDataId.append(id)
@ -212,7 +214,7 @@ class _TTkFancyTableView(TTkAbstractScrollView):
def insertItem(self, index, item, id=None):
if len(item) != len(self._columns):
return#
textItem = [i if isinstance(i,str) else "" for i in item]
textItem = [TTkString(i) if isinstance(i,str) else i if issubclass(type(i), TTkString) else TTkString() for i in item]
widgetItem = [i if isinstance(i,TTkWidget) else None for i in item]
if id is not None:
self._tableDataId.insert(index, id)

3
TermTk/TTkWidgets/Fancy/treewidget.py

@ -24,6 +24,7 @@
from TermTk.TTkCore.constant import TTkK
from TermTk.TTkCore.log import TTkLog
from TermTk.TTkCore.string import TTkString
from TermTk.TTkCore.signal import pyTTkSlot, pyTTkSignal
from TermTk.TTkWidgets.widget import TTkWidget
from TermTk.TTkWidgets.checkbox import TTkCheckbox
@ -52,7 +53,7 @@ class _TTkDisplayedTreeItem(TTkWidget):
self._name = kwargs.get('name' , '_TTkDisplayedTreeItem' )
self._depth = kwargs.get('depth' , 0 )
self._text = kwargs.get('text' , "" )
self._text = TTkString(kwargs.get('text' , "" ))
self._id = kwargs.get('id' , 0 )
self._treeWidgetItem = kwargs.get('treeWidgetItem', None)
self._isLeaf = self._treeWidgetItem.childIndicatorPolicy() == TTkK.DontShowIndicator

5
TermTk/TTkWidgets/TTkModelView/treewidget.py

@ -24,6 +24,7 @@
from TermTk.TTkCore.cfg import TTkCfg
from TermTk.TTkCore.constant import TTkK
from TermTk.TTkCore.string import TTkString
from TermTk.TTkWidgets.TTkModelView.treewidgetitem import TTkTreeWidgetItem
from TermTk.TTkAbstract.abstractscrollview import TTkAbstractScrollView
from TermTk.TTkCore.signal import pyTTkSignal, pyTTkSlot
@ -250,9 +251,9 @@ class TTkTreeWidget(TTkAbstractScrollView):
if _icon:
_icon = ' '+_icon+' '
if _il==0:
_data.append(' '*_level+_icon+_child.data(_il))
_data.append(TTkString(' '*_level+_icon+_child.data(_il)))
else:
_data.append(_icon+_child.data(_il))
_data.append(TTkString(_icon+_child.data(_il)))
self._cache.append(TTkTreeWidget._Cache(
item = _child,

4
TermTk/TTkWidgets/TTkModelView/treewidgetitem.py

@ -24,6 +24,7 @@
from TermTk.TTkCore.cfg import TTkCfg
from TermTk.TTkCore.constant import TTkK
from TermTk.TTkCore.string import TTkString
from TermTk.TTkCore.signal import pyTTkSlot
from TermTk.TTkAbstract.abstractitemmodel import TTkAbstractItemModel
@ -41,7 +42,8 @@ class TTkTreeWidgetItem(TTkAbstractItemModel):
# self.refreshData = pyTTkSignal(TTkTreeWidgetItem)
super().__init__(*args, **kwargs)
self._children = []
self._data = args[0] if len(args)>0 and type(args[0])==list else ['']
data = args[0] if len(args)>0 and type(args[0])==list else [TTkString()]
self._data = [i if issubclass(type(i), TTkString) else TTkString(i) if isinstance(i,str) else TTkString() for i in data]
self._alignment = [TTkK.LEFT_ALIGN]*len(self._data)
self._parent = kwargs.get('parent', None)
self._childIndicatorPolicy = kwargs.get('childIndicatorPolicy', TTkK.DontShowIndicatorWhenChildless)

4
TermTk/TTkWidgets/combobox.py

@ -27,6 +27,7 @@ from TermTk.TTkCore.constant import TTkK
from TermTk.TTkCore.log import TTkLog
from TermTk.TTkCore.signal import pyTTkSlot, pyTTkSignal
from TermTk.TTkCore.helper import TTkHelper
from TermTk.TTkCore.string import TTkString
from TermTk.TTkLayouts.gridlayout import TTkGridLayout
from TermTk.TTkWidgets.widget import TTkWidget
from TermTk.TTkWidgets.list_ import TTkList
@ -43,7 +44,6 @@ class TTkComboBox(TTkWidget):
self.currentTextChanged = pyTTkSignal(str)
self.editTextChanged = pyTTkSignal(str)
TTkWidget.__init__(self, *args, **kwargs)
self._name = kwargs.get('name' , 'TTkComboBox' )
# self.checked = pyTTkSignal()
self._lineEdit = TTkLineEdit(parent=self)
self._list = kwargs.get('list', [] )
@ -120,7 +120,7 @@ class TTkComboBox(TTkWidget):
text = self._list[self._id]
w = self.width()
self._canvas.drawText(pos=(1,0), text=text, width=w-2, alignment=self._textAlign, color=color)
self._canvas.drawTTkString(pos=(1,0), text=TTkString(text), width=w-2, alignment=self._textAlign, color=color)
self._canvas.drawText(pos=(0,0), text="[", color=borderColor)
if self._editable:
self._canvas.drawText(pos=(w-3,0), text="[^]", color=borderColor)

64
TermTk/TTkWidgets/lineedit.py

@ -25,10 +25,11 @@
import re
from TermTk.TTkCore.cfg import TTkCfg
from TermTk.TTkCore.constant import TTkK
from TermTk.TTkCore.helper import TTkHelper
from TermTk.TTkCore.string import TTkString
from TermTk.TTkCore.signal import pyTTkSlot, pyTTkSignal
from TermTk.TTkWidgets.widget import *
from TermTk.TTkWidgets.widget import TTkWidget
'''
@ -48,11 +49,10 @@ class TTkLineEdit(TTkWidget):
self.textChanged = pyTTkSignal(str)
self.textEdited = pyTTkSignal(str)
TTkWidget.__init__(self, *args, **kwargs)
self._name = kwargs.get('name' , 'TTkLineEdit' )
self._inputType = kwargs.get('inputType' , TTkK.Input_Text )
self._text = kwargs.get('text' , '' )
self._text = TTkString(kwargs.get('text' , '' ))
if self._inputType & TTkK.Input_Number and\
not self._text.lstrip('-').isdigit(): self._text = ""
not self._text.lstrip('-').isdigit(): self._text = TTkString()
self._color = TTkCfg.theme.lineEditTextColor
self._offset = 0
self._cursorPos = 0
@ -67,7 +67,7 @@ class TTkLineEdit(TTkWidget):
def setText(self, text, cursorPos=0x1000):
if text != self._text:
self.textChanged.emit(text)
self._text = text
self._text = TTkString(text)
self._cursorPos = max(0,min(cursorPos, len(text)))
self._pushCursor()
@ -82,12 +82,13 @@ class TTkLineEdit(TTkWidget):
# Align the text and the offset and the cursor to the current view
self._offset = max(0, min(self._offset, len(self._text)-w))
# Scroll to the right if reached the edge
if self._cursorPos - self._offset > w:
self._offset = self._cursorPos-w
if self._cursorPos - self._offset < 0:
self._offset = self._cursorPos
cursorPos = self._text.substring(to=self._cursorPos).termWidth()
if cursorPos - self._offset > w:
self._offset = cursorPos-w
if cursorPos - self._offset < 0:
self._offset = cursorPos
TTkHelper.moveCursor(self,self._cursorPos-self._offset,0)
TTkHelper.moveCursor(self,cursorPos-self._offset,0)
if self._replace:
TTkHelper.showCursor(TTkK.Cursor_Blinking_Block)
else:
@ -116,9 +117,7 @@ class TTkLineEdit(TTkWidget):
self._canvas.drawText(pos=(0,0), text=text, color=color, width=w)
def mousePressEvent(self, evt):
txtPos = evt.x+self._offset
if txtPos > len(self._text):
txtPos = len(self._text)
txtPos = self._text.tabCharPos(evt.x+self._offset)
self._cursorPos = txtPos
self._selectionFrom = txtPos
self._selectionTo = txtPos
@ -126,7 +125,7 @@ class TTkLineEdit(TTkWidget):
return True
def mouseDragEvent(self, evt) -> bool:
txtPos = evt.x+self._offset
txtPos = self._text.tabCharPos(evt.x+self._offset)
self._selectionFrom = max(0, min(txtPos,self._cursorPos))
self._selectionTo = min(len(self._text),max(txtPos,self._cursorPos))
if self._selectionFrom < self._selectionTo:
@ -135,17 +134,17 @@ class TTkLineEdit(TTkWidget):
return True
def mouseDoubleClickEvent(self, evt) -> bool:
before = self._text[:self._cursorPos]
after = self._text[self._cursorPos:]
before = self._text.substring(to=self._cursorPos)
after = self._text.substring(fr=self._cursorPos)
self._selectionFrom = len(before)
self._selectionTo = len(before)
selectRE = '[a-zA-Z0-9:,./]*'
selectRE = '[^ \t\r\n\(\)\[\]\.\,\+\-\*\/]*'
if m := re.search(selectRE+'$',before):
if m := before.search(selectRE+'$'):
self._selectionFrom -= len(m.group(0))
if m := re.search('^'+selectRE,after):
if m := after.search('^'+selectRE):
self._selectionTo += len(m.group(0))
# TTkLog.debug("x"*self._selectionFrom)
@ -177,13 +176,11 @@ class TTkLineEdit(TTkWidget):
elif evt.key == TTkK.Key_Left:
if self._selectionFrom < self._selectionTo:
self._cursorPos = self._selectionTo
if self._cursorPos > 0:
self._cursorPos -= 1
self._cursorPos = self._text.prevPos(self._cursorPos)
elif evt.key == TTkK.Key_Right:
if self._selectionFrom < self._selectionTo:
self._cursorPos = self._selectionTo-1
if self._cursorPos < len(self._text):
self._cursorPos += 1
self._cursorPos = self._text.nextPos(self._cursorPos)
elif evt.key == TTkK.Key_End:
self._cursorPos = len(self._text)
elif evt.key == TTkK.Key_Home:
@ -192,17 +189,18 @@ class TTkLineEdit(TTkWidget):
self._replace = not self._replace
elif evt.key == TTkK.Key_Delete:
if self._selectionFrom < self._selectionTo:
self._text = self._text[:self._selectionFrom] + self._text[self._selectionTo:]
self._text = self._text.substring(to=self._selectionFrom) + self._text.substring(fr=self._selectionTo)
self._cursorPos = self._selectionFrom
else:
self._text = self._text[:self._cursorPos] + self._text[self._cursorPos+1:]
self._text = self._text.substring(to=self._cursorPos) + self._text.substring(fr=self._text.nextPos(self._cursorPos))
elif evt.key == TTkK.Key_Backspace:
if self._selectionFrom < self._selectionTo:
self._text = self._text[:self._selectionFrom] + self._text[self._selectionTo:]
self._text = self._text.substring(to=self._selectionFrom) + self._text.substring(fr=self._selectionTo)
self._cursorPos = self._selectionFrom
elif self._cursorPos > 0:
self._text = self._text[:self._cursorPos-1] + self._text[self._cursorPos:]
self._cursorPos -= 1
prev = self._text.prevPos(self._cursorPos)
self._text = self._text.substring(to=prev) + self._text.substring(fr=self._cursorPos)
self._cursorPos = prev
if self._inputType & TTkK.Input_Number and \
not self._text.lstrip('-').isdigit():
@ -216,15 +214,15 @@ class TTkLineEdit(TTkWidget):
text = self._text
if self._selectionFrom < self._selectionTo:
pre = text[:self._selectionFrom]
post = text[self._selectionTo:]
pre = text.substring(to=self._selectionFrom)
post = text.substring(fr=self._selectionTo)
self._cursorPos = self._selectionFrom
else:
pre = text[:self._cursorPos]
pre = text.substring(to=self._cursorPos)
if self._replace:
post = text[self._cursorPos+1:]
post = text.substring(fr=self._cursorPos+1)
else:
post = text[self._cursorPos:]
post = text.substring(fr=self._cursorPos)
text = pre + evt.key + post
if self._inputType & TTkK.Input_Number and \

4
TermTk/TTkWidgets/menubar.py

@ -79,10 +79,10 @@ class TTkMenuButton(TTkAbstractListItem):
self._menu = []
while self.text.find('&') != -1:
index = self.text.find('&')
shortcut = self.text[index+1]
shortcut = self.text.charAt(index+1)
TTkHelper.addShortcut(self, shortcut)
self._shortcut.append(index)
self.text = self.text[:index]+self.text[index+1:]
self.text = self.text.substring(to=index)+self.text.substring(fr=index+1)
txtlen = len(self.text)
self.resize(txtlen,1)
self.setMinimumSize(txtlen+2,1)

5
TermTk/TTkWidgets/texedit.py

@ -522,7 +522,7 @@ class TTkTextEditView(TTkAbstractScrollView):
for y, l in enumerate(subLines):
t = outLines[l[0]-subLines[0][0]]
self._canvas.drawText(pos=(-ox,y), text=t.substring(l[1][0],l[1][1]).tab2spaces(self._textWrap._tabSpaces))
self._canvas.drawTTkString(pos=(-ox,y), text=t.substring(l[1][0],l[1][1]).tab2spaces(self._textWrap._tabSpaces))
if self._lineWrapMode == TTkK.FixedWidth:
self._canvas.drawVLine(pos=(self._textWrap._wrapWidth,0), size=h, color=TTkCfg.theme.treeLineColor)
@ -533,7 +533,7 @@ class TTkTextEdit(TTkAbstractScrollArea):
'_textEditView',
'_lineNumberView', '_lineNumber',
# Forwarded Methods
'clear', 'setText', 'append', 'isReadOnly', 'setReadOnly'
'clear', 'setText', 'append', 'isReadOnly', 'setReadOnly', 'document',
'wrapWidth', 'setWrapWidth',
'lineWrapMode', 'setLineWrapMode',
'wordWrapMode', 'setWordWrapMode',
@ -562,6 +562,7 @@ class TTkTextEdit(TTkAbstractScrollArea):
self.clear = self._textEditView.clear
self.setText = self._textEditView.setText
self.append = self._textEditView.append
self.document = self._textEditView.document
self.isReadOnly = self._textEditView.isReadOnly
self.setReadOnly = self._textEditView.setReadOnly
self.textCursor = self._textEditView.textCursor

6
demo/demo.py

@ -52,12 +52,6 @@ from showcase.dragndrop import demoDnD
from showcase.dndtabs import demoDnDTabs
from showcase.sigmask import demoSigmask
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 getSentence(a,b):
return " ".join([getWord() for i in range(0,random.randint(a,b))])
def stupidPythonHighlighter(txt):
def _colorize(regex, txt, color):
ret = txt

75
demo/showcase/_showcasehelper.py

@ -0,0 +1,75 @@
# MIT License
#
# Copyright (c) 2021 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import sys, os, random
sys.path.append(os.path.join(sys.path[0],'../..'))
import TermTk as ttk
zc1 = chr(0x07a6) # Zero width chars oަ
zc2 = chr(0x20D7) # Zero width chars o
zc3 = chr(0x065f) # Zero width chars oٟ
utfwords = [
f"--Zero{zc1}{zc2}{zc3}-1-", f"--Zero-2{zc1}{zc2}{zc3}-", f"--Ze{zc1}{zc2}{zc3}ro-3-", f"{zc1}{zc2}{zc3}--Zero-4-",
"Lorem", "i🙻sum", "d😮l😱r", "sit", "am😎t,", "c😱nsectetur", "adi🙻iscing", "elit,", "sed", "do", "eiusmod", "t😜mpor", "incididunt", "ut", "labore", "et", "dolore", "magna", "aliqua.", "Ut", "enim", "ad", "minim", "veniam,", "quis", "nostrud", "exercitation", "ullamco", "laboris", "nisi", "ut", "aliq😞ip", "ex", "ea", "comm😞do", "cons😿quat.", "Duis", "aute", "irure", "dolor", "in", "reprehenderit", "in", "voluptate", "velit", "esse", "cillum", "dolore", "eu", "fugiat", "nulla", "pariatur.", "Excepteur", "sint", "occaecat", "cupidatat", "non", "proident,", "sunt", "in", "cul🙻a", "qui", "officia", "deserunt", "mollit", "anim", "id", "est", "laborum."]
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 randColor():
return [
ttk.TTkColor.RST,
ttk.TTkColor.fg('#FFFF00'),
ttk.TTkColor.fg('#00FFFF'),
ttk.TTkColor.fg('#FF00FF'),
ttk.TTkColor.fg('#0000FF')+ttk.TTkColor.bg('#00FF00'),
ttk.TTkColor.fg('#00FF00')+ttk.TTkColor.UNDERLINE,
ttk.TTkColor.fg('#FF0000')+ttk.TTkColor.STRIKETROUGH,
][random.randint(0,6)]
def getWord():
return random.choice(words)
def getWords(n):
www = [random.choice(words) for _ in range(n)]
return " ".join(www)
def getSentence(a,b):
return " ".join([getWords(random.randint(1,4)) for _ in range(0,random.randint(a,b))])
def getUtfWord():
return random.choice(utfwords)
def getUtfWords(n):
www = [random.choice(utfwords) for _ in range(n)]
return " ".join(www)
def getUtfSentence(a,b):
return " ".join([getUtfWords(random.randint(1,4)) for _ in range(0,random.randint(a,b))])
def getUtfColoredWords(n):
www = [random.choice(utfwords) for _ in range(n)]
return ttk.TTkString(" ".join(www), randColor())
def getUtfColoredSentence(a,b):
return ttk.TTkString(" ").join([getUtfColoredWords(random.randint(1,4)) for _ in range(0,random.randint(a,b))])
def main():
root = ttk.TTk()
ttk.TTkLabel(parent=root, text=getUtfColoredSentence(20,50))
root.mainloop()
if __name__ == "__main__":
main()

13
demo/showcase/fancytable.py

@ -28,11 +28,8 @@ import 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 getSentence(a,b):
return " ".join([getWord() for i in range(0,random.randint(a,b))])
sys.path.append(os.path.join(sys.path[0],'..'))
from showcase._showcasehelper import getUtfSentence, getUtfWord
table_ii = 1000
@ -62,7 +59,7 @@ def demoFancyTable(root=None):
))
table1.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(8,30), getWord(), getWord()))
table1.appendItem((str(i), getUtfWord(), getUtfSentence(8,30), getUtfWord(), getUtfWord()))
table1.appendItem((" - ","This is the end", "Beautiful friend, This is the end My only friend", "the end", "..."))
# Attach the add Event
@ -70,14 +67,14 @@ def demoFancyTable(root=None):
def add():
global table_ii
table_ii+=1
table1.appendItem((str(table_ii), getWord(), getSentence(8,30), getWord(), getWord()))
table1.appendItem((str(table_ii), getUtfWord(), getUtfSentence(8,30), getUtfWord(), getUtfWord()))
btn1.clicked.connect(add)
def addMany():
global table_ii
for i in range(0, 500):
table_ii+=1
table1.appendItem((str(table_ii), getWord(), getSentence(8,30), getWord(), getWord()))
table1.appendItem((str(table_ii), getUtfWord(), getUtfSentence(8,30), getUtfWord(), getUtfWord()))
table1.appendItem((" - ","This is the end", "Beautiful friend, This is the end My only friend", "the end", "..."))
btn2.clicked.connect(addMany)

9
demo/showcase/fancytree.py

@ -33,11 +33,8 @@ import argparse
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 getSentence(a,b):
return " ".join([getWord() for i in range(0,random.randint(a,b))])
sys.path.append(os.path.join(sys.path[0],'..'))
from showcase._showcasehelper import getUtfSentence, getUtfWord
def demoFancyTree(root=None):
# tw = ttk.TTkFancyTreeWidget(parent=rootTree1)
@ -82,7 +79,7 @@ def demoFancyTree(root=None):
def updateChildren(item):
if item.children(): return
for _ in range(0,random.randint(3,8)):
child = ttk.TTkFancyTreeWidgetItem([getWord(),getWord(),getWord()])
child = ttk.TTkFancyTreeWidgetItem([getUtfWord(),getUtfWord(),getUtfWord()])
if random.randint(0,10)>5:
child.setChildIndicatorPolicy(ttk.TTkK.ShowIndicator)
child.refreshData.connect(updateChildren)

17
demo/showcase/formwidgets.py

@ -28,11 +28,8 @@ import 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 getSentence(a,b):
return " ".join([getWord() for i in range(0,random.randint(a,b))])
sys.path.append(os.path.join(sys.path[0],'..'))
from showcase._showcasehelper import getUtfSentence
def demoFormWidgets(root=None):
win_form1_grid_layout = ttk.TTkGridLayout(columnMinWidth=1)
@ -47,11 +44,11 @@ def demoFormWidgets(root=None):
row +=1; win_form1_grid_layout.addWidget(ttk.TTkLabel(text='Combo Box'),row,0)
win_form1_grid_layout.addWidget(ttk.TTkComboBox(list=['One','Two','Some Long Sentence That Is Not a Written Number','Three']),row,2)
row +=1; win_form1_grid_layout.addWidget(ttk.TTkLabel(text='Combo long Box'),row,0)
win_form1_grid_layout.addWidget(ttk.TTkComboBox(list=[getSentence(1,4) for i in range(100)]),row,2)
win_form1_grid_layout.addWidget(ttk.TTkComboBox(list=[getUtfSentence(1,4) for i in range(100)]),row,2)
row +=1; win_form1_grid_layout.addWidget(ttk.TTkLabel(text='Combo Box Edit. Bottom Insert'),row,0)
win_form1_grid_layout.addWidget(comboEdit1 := ttk.TTkComboBox(list=[getSentence(1,4) for i in range(10)]),row,2)
win_form1_grid_layout.addWidget(comboEdit1 := ttk.TTkComboBox(list=[getUtfSentence(1,4) for i in range(10)]),row,2)
row +=1; win_form1_grid_layout.addWidget(ttk.TTkLabel(text='Combo Box Edit. Top Insert'),row,0)
win_form1_grid_layout.addWidget(comboEdit2 := ttk.TTkComboBox(list=[getSentence(1,4) for i in range(10)]),row,2)
win_form1_grid_layout.addWidget(comboEdit2 := ttk.TTkComboBox(list=[getUtfSentence(1,4) for i in range(10)]),row,2)
comboEdit1.setEditable(True)
comboEdit2.setEditable(True)
@ -60,9 +57,9 @@ def demoFormWidgets(root=None):
row +=1; win_form1_grid_layout.addWidget(ttk.TTkLabel(text='Line Edit Test 1'),row,0)
win_form1_grid_layout.addWidget(ttk.TTkLineEdit(text='Line Edit Test 1'),row,2)
row += 1; win_form1_grid_layout.addWidget(ttk.TTkLabel(text='Line Edit Test 2'),row,0)
win_form1_grid_layout.addWidget(ttk.TTkLineEdit(text='Line Edit Test 2'),row,2)
win_form1_grid_layout.addWidget(ttk.TTkLineEdit(text='Line Edit Test 2 😎 -'),row,2)
row += 1; win_form1_grid_layout.addWidget(ttk.TTkLabel(text='Line Edit Test 3'),row,0)
win_form1_grid_layout.addWidget(ttk.TTkLineEdit(text='Line Edit Test 3'),row,2)
win_form1_grid_layout.addWidget(ttk.TTkLineEdit(text='Line Edit Test 3 oަ -'),row,2)
row += 1; win_form1_grid_layout.addWidget(ttk.TTkLabel(text='Line Edit Test 4'),row,0)
win_form1_grid_layout.addWidget(ttk.TTkLineEdit(text='Line Edit Test 4'),row,2)
row += 1; win_form1_grid_layout.addWidget(ttk.TTkLabel(text='Line Edit Test 5'),row,0)

10
demo/showcase/list.py

@ -24,12 +24,12 @@
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)
sys.path.append(os.path.join(sys.path[0],'..'))
from showcase._showcasehelper import getUtfWord
def demoList(root= None):
# Define the main Layout
@ -67,8 +67,8 @@ def demoList(root= None):
# populate the lists with random entries
for i in range(100):
listWidgetSingle.addItem(f"{i}) {getWord()} {getWord()}")
listWidgetMulti.addItem(f"{getWord()} {getWord()}")
listWidgetSingle.addItem(f"{i}) {getUtfWord()} {getUtfWord()}")
listWidgetMulti.addItem(f"{getUtfWord()} {getUtfWord()}")
return splitter

42
demo/showcase/textedit.py

@ -30,22 +30,8 @@ import argparse
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 randColor():
return [
ttk.TTkColor.RST,
ttk.TTkColor.fg('#FFFF00'),
ttk.TTkColor.fg('#00FFFF'),
ttk.TTkColor.fg('#FF00FF'),
ttk.TTkColor.fg('#0000FF')+ttk.TTkColor.bg('#00FF00'),
ttk.TTkColor.fg('#00FF00')+ttk.TTkColor.UNDERLINE,
ttk.TTkColor.fg('#FF0000')+ttk.TTkColor.STRIKETROUGH,
][random.randint(0,6)]
def getWords(n):
www = [random.choice(words) for _ in range(n)]
return ttk.TTkString(" ".join(www), randColor())
def getSentence(a,b,i):
return ttk.TTkString(" ").join([f"{i} "]+[getWords(random.randint(1,4)) for i in range(0,random.randint(a,b))])
sys.path.append(os.path.join(sys.path[0],'..'))
from showcase._showcasehelper import getUtfColoredSentence
class superSimpleHorizontalLine(ttk.TTkWidget):
def paintEvent(self):
@ -77,6 +63,28 @@ def demoTextEdit(root=None, document=None):
with open(os.path.join(os.path.dirname(os.path.abspath(__file__)),'textedit.ANSI.txt')) as f:
te.append(f.read())
# Test Variable sized chars
te.append(ttk.TTkString("Test Variable sized chars\n",ttk.TTkColor.UNDERLINE+ttk.TTkColor.BOLD))
te.append( "Emoticons: -😁😂😍😎----")
te.append( " --😐😁😂😍😎-")
te.append("")
te.append( " UTF-8: £ @ £ ¬ ` 漢 _ _ あ _ _")
te.append( " |.|.|.|.|.||.|.|.||.|.|.")
te.append("")
zc1 = chr(0x07a6)
zc2 = chr(0x20D7)
zc3 = chr(0x065f)
te.append( " - | | | | | -")
te.append(f"Zero Size: - o{zc1} o{zc2} o{zc3} o{zc1}{zc2} o{zc1}{zc2}{zc3} -")
te.append( " - | | | | | -")
te.append("")
te.append(f"Plus Tabs: -\t😁\t😍\to{zc1}{zc2}{zc3}\t😎\to{zc1}{zc2}{zc3}\t😂-")
te.append("")
# Test Tabs
te.append(ttk.TTkString("Tabs Test\n",ttk.TTkColor.UNDERLINE+ttk.TTkColor.BOLD))
te.append("Word\tAnother Word\tYet more words")
@ -91,7 +99,7 @@ def demoTextEdit(root=None, document=None):
te.append("-------tab\ttab\ttab\ttab\ttab\ttab\ttab\ttab\ttab\ttab\ttab\ttab\n")
te.append(ttk.TTkString("Random TTkString Input Test\n",ttk.TTkColor.UNDERLINE+ttk.TTkColor.BOLD))
te.append(ttk.TTkString('\n').join([ getSentence(3,10,i) for i in range(50)]))
te.append(ttk.TTkString('\n').join([ getUtfColoredSentence(3,10) for _ in range(50)]))
te.append(ttk.TTkString("-- The Very END --",ttk.TTkColor.UNDERLINE+ttk.TTkColor.BOLD))

9
demo/showcase/tree.py

@ -33,11 +33,8 @@ import argparse
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 getSentence(a,b):
return " ".join([getWord() for i in range(0,random.randint(a,b))])
sys.path.append(os.path.join(sys.path[0],'..'))
from showcase._showcasehelper import getUtfWord
def demoTree(root=None):
tw = ttk.TTkTree(parent=root)
@ -77,7 +74,7 @@ def demoTree(root=None):
def updateChildren(item):
if item.children(): return
for _ in range(0,random.randint(3,8)):
child = ttk.TTkTreeWidgetItem([getWord(),getWord(),getWord()])
child = ttk.TTkTreeWidgetItem([getUtfWord(),getUtfWord(),getUtfWord()])
if random.randint(0,10)>5:
child.setChildIndicatorPolicy(ttk.TTkK.ShowIndicator)
item.addChild(child)

6
docs/MDNotes/TODO.md

@ -5,10 +5,12 @@
- [ ] Remove Duplicate functionalities (i.e. Widget)
- [ ] Use @property/@setter when possible
- [ ] Uniform the setter/getter/signal/slots
- [ ] [UTF-8] Handle "Fullwidth" forms characters
- [x] [UTF-8] Handle "Fullwidth" forms characters
https://en.wikipedia.org/wiki/Halfwidth_and_fullwidth_forms
https://en.wikipedia.org/wiki/Halfwidth_and_Fullwidth_Forms_(Unicode_block)
https://stackoverflow.com/questions/68412744/count-length-of-value-within-a-cell-with-full-width-characters
- [ ] Handle Zero Width Joiner (i.e.👩🔧 -> 👩🔧👩🏻🔧👩🏻🔧👩🏼🔧👩🏽🔧👩🏾🔧👩🏿🔧):
https://github.com/luchr/WidthInTerminals
- [ ] Support Hyperlink: (gnome-terminal)
https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
- [x] Process child events before parent
@ -45,7 +47,7 @@
- [ ] Allow dynamic depth change
- [x] Define a gradient feature
## Canvas Class
- [ ] Have a look to the Unicode chartable: https://www.utf8-chartable.de/unicode-utf8-table.pl
- [x] Have a look to the Unicode chartable: https://www.utf8-chartable.de/unicode-utf8-table.pl
## Signal/Slots
- [x] Implement Signal/Slots

98
tests/test.draw.006.py

@ -0,0 +1,98 @@
#!/usr/bin/env python3
# MIT License
#
# Copyright (c) 2022 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import sys, os
import logging
import time
sys.path.append(os.path.join(sys.path[0],'..'))
from TermTk import TTkLog
from TermTk.TTkCore import TTkColor
from TermTk.TTkCore import TTkHelper
from TermTk.TTkCore import TTkString
from TermTk.TTkCore import TTkTerm
# TTkLog.use_default_file_logging()
TTkLog.use_default_stdout_logging()
# TTkTerm.init(mouse=False)
TTkLog.info("Starting")
s1 = TTkString("-😁😂😍😎----")
s2 = TTkString("--😐😁😂😍😎-")
zc1 = chr(0x07a6)
zc2 = chr(0x20D7)
zc3 = chr(0x065f)
s3 = TTkString(f"Zero Size: - o{zc1} o{zc2} o{zc3} o{zc1}{zc2} o{zc1}{zc2}{zc3} -")
s4 = TTkString("This is a normal string")
s5 = TTkString(f"-😁😂😍😎- o{zc1} o{zc2} o{zc3} o{zc1}{zc2} o{zc1}{zc2}{zc3} -")
s6 = TTkString(f"{zc1} o{zc2} o{zc3} o{zc1}{zc2} o{zc1}{zc2}{zc3} -")
print(f"o{zc1}{zc2}{zc3} - Zero")
print(f"{zc1}{zc2}{zc3} - Zero")
# Examples from:
# https://github.com/luchr/WidthInTerminals
print( "🔧 = \U0001F527 ")
print(f"👩 = {chr(0x1F469)} ")
print(f"👩🔧 = {chr(0x1F469)}{chr(0x1F527)} ")
print(f"👩🔧 = {chr(0x1F469)}{chr(0x0200D)}{chr(0x1F527)} ")
print(f"👩🏻🔧 = {chr(0x1F469)}{chr(0x1F3FB)}{chr(0x0200D)}{chr(0x1F527)}")
print(f"👩🏻🔧 = {chr(0x1F469)}{chr(0x1F3FB)}{chr(0x0200D)}{chr(0x1F527)}")
print(f"👩🏼🔧 = {chr(0x1F469)}{chr(0x1F3FC)}{chr(0x0200D)}{chr(0x1F527)}")
print(f"👩🏽🔧 = {chr(0x1F469)}{chr(0x1F3FD)}{chr(0x0200D)}{chr(0x1F527)}")
print(f"👩🏾🔧 = {chr(0x1F469)}{chr(0x1F3FE)}{chr(0x0200D)}{chr(0x1F527)}")
print(f"👩🏿🔧 = {chr(0x1F469)}{chr(0x1F3FF)}{chr(0x0200D)}{chr(0x1F527)}")
s01 = TTkString(f"🔧 = {chr(0x1F527)} ") # 1F527 : 🔧
s01 = TTkString(f"👩 = {chr(0x1F469)} ") # 1F469 : 👩
s01 = TTkString(f"👩🔧 = {chr(0x1F469)}{chr(0x1F527)} ") # 1F469 1F527 : 👩🔧
s01 = TTkString(f"👩🔧 = {chr(0x1F469)}{chr(0x0200D)}{chr(0x1F527)} ") # 1F469 200D 1F527 : 👩🔧
s01 = TTkString(f"👩🏻🔧 = {chr(0x1F469)}{chr(0x1F3FB)}{chr(0x0200D)}{chr(0x1F527)}") # 1F469 1F3FB 200D 1F527 : 👩🏻🔧
s01 = TTkString(f"👩🏻🔧 = {chr(0x1F469)}{chr(0x1F3FB)}{chr(0x0200D)}{chr(0x1F527)}") # 1F469 1F3FB 200D 1F527 : 👩🏻🔧
s01 = TTkString(f"👩🏼🔧 = {chr(0x1F469)}{chr(0x1F3FC)}{chr(0x0200D)}{chr(0x1F527)}") # 1F469 1F3FC 200D 1F527 : 👩🏼🔧
s01 = TTkString(f"👩🏽🔧 = {chr(0x1F469)}{chr(0x1F3FD)}{chr(0x0200D)}{chr(0x1F527)}") # 1F469 1F3FD 200D 1F527 : 👩🏽🔧
s01 = TTkString(f"👩🏾🔧 = {chr(0x1F469)}{chr(0x1F3FE)}{chr(0x0200D)}{chr(0x1F527)}") # 1F469 1F3FE 200D 1F527 : 👩🏾🔧
s01 = TTkString(f"👩🏿🔧 = {chr(0x1F469)}{chr(0x1F3FF)}{chr(0x0200D)}{chr(0x1F527)}") # 1F469 1F3FF 200D 1F527 : 👩🏿🔧
print(s1.getData()[0])
print(s2.getData()[0])
print(s3.getData()[0])
print(s4.getData()[0])
print(s5.getData()[0])
print(s6.getData()[0])
s5.tabCharPos(1,tabSpaces=4)
s5.tabCharPos(5,tabSpaces=4)
s5.tabCharPos(9,tabSpaces=4)
# time.sleep(5)
TTkLog.info("Ending")
# TTkTerm.exit()

66
tests/test.draw.007.py

@ -0,0 +1,66 @@
#!/usr/bin/env python3
# MIT License
#
# Copyright (c) 2022 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import sys, os
import logging
import time
sys.path.append(os.path.join(sys.path[0],'..'))
from TermTk import TTkLog
from TermTk.TTkCore import TTkColor
from TermTk.TTkCore import TTkHelper
from TermTk.TTkCore import TTkString
from TermTk.TTkCore import TTkTerm
from TermTk.TTkCore import TTkCanvas
# TTkLog.use_default_file_logging()
TTkLog.use_default_stdout_logging()
# TTkTerm.init(mouse=False)
TTkLog.info("Starting")
s1 = TTkString("-😁😂😍😎----")
s2 = TTkString("--😐😁😂😍😎-")
zc1 = chr(0x07a6)
zc2 = chr(0x20D7)
zc3 = chr(0x065f)
s3 = TTkString(f"Zero Size: - o{zc1} o{zc2} o{zc3} o{zc1}{zc2} o{zc1}{zc2}{zc3} -")
s4 = TTkString("This is a normal string")
s5 = TTkString(f"-😁- o{zc1} o{zc2} o{zc3} o{zc1}{zc2} o{zc1}{zc2}{zc3} -")
s6 = TTkString(f"{zc1} o{zc2} o{zc3} o{zc1}{zc2} o{zc1}{zc2}{zc3} -")
TTkLog.debug(f"o{zc1}{zc2}{zc3} - Zero")
TTkLog.debug(f"{zc1}{zc2}{zc3} - Zero")
canvas = TTkCanvas(width=100,height=100)
canvas.drawTTkString(pos=(0,0),text=s1,width=4)
canvas.drawTTkString(pos=(0,0),text=s1,width=11)
canvas.drawTTkString(pos=(0,0),text=s5,width=8)
canvas.drawTTkString(pos=(0,0),text=s6,width=4)
TTkLog.info("Ending")

7
tests/test.metaclass.001.py

@ -21,7 +21,6 @@
# 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.
class A_Meta(type):
def __new__(mcs, name, bases, d):
print(f"{mcs=}")
@ -40,7 +39,7 @@ print(f"{A=}")
a = A(1,2,3,4)
print(f"{a=}\n")
print(f"A ---> {a=}\n")
class B(A_Meta):
def __init__(self, *args, **kwargs):
@ -50,7 +49,7 @@ class B(A_Meta):
b = B("NB",(),{})
print(f"{b=}\n")
print(f"B ---> {b=}\n")
class C():
def __init__(self) -> None:
@ -68,4 +67,4 @@ class E(C,D):
e = E()
e.pippo()
print(f"{issubclass(E,D)=} {issubclass(E,C)=}")
print(f"E,D ---> {issubclass(E,D)=} {issubclass(E,C)=}")

50
tests/test.metaclass.002.py

@ -0,0 +1,50 @@
#!/usr/bin/env python3
# MIT License
#
# Copyright (c) 2022 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
class A():
test = True
def __new__(cls, *args, **kwargs):
print(f"New: {args=} {kwargs=} {cls=}")
if kwargs.get('mod'):
return super().__new__(A_Mod)
return super().__new__(cls)
def __init__(self, *args, **kwargs):
print(f"Init: {args=} {kwargs=}")
class A_Mod(A):
test = False
def __init__(self, *args, **kwargs):
print(f"Init: Mod {args=} {kwargs=}")
a = A(1,2,3,aa=1,bb=2,cc=3)
print(f"A ---> {a=} {a.test=}")
b = A(1,2,3,aa=1,bb=2,cc=3,mod=4)
print(f"B ---> {b=} {b.test=}")
def test(x):
print(f"Test {x=}")
return x == 5
print(f"{any(test(x) for x in range(10))=}")

1
tests/timeit/.gitignore vendored

@ -0,0 +1 @@
wcwidth

88
tests/timeit/00.template.py

@ -0,0 +1,88 @@
#!/usr/bin/env python3
# MIT License
#
# Copyright (c) 2021 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import sys, os
import timeit
import random
sys.path.append(os.path.join(sys.path[0],'../..'))
sys.path.append(os.path.join(sys.path[0],'.'))
import TermTk as ttk
def test1():
return 1
def test2():
return 1
def test3():
return 1
def test4():
return 1
def test5():
return 1
def test6():
return 1
def test7():
return 1
def test8():
return 1
def test9():
return 1
def test10():
return 1
def test11():
return 1
def test12():
return 1
loop = 100
result = timeit.timeit('test1()', globals=globals(), number=loop)
print(f"1 {result / loop:.10f} - {result / loop} {test1()}")
result = timeit.timeit('test2()', globals=globals(), number=loop)
print(f"2 {result / loop:.10f} - {result / loop} {test2()}")
result = timeit.timeit('test3()', globals=globals(), number=loop)
print(f"3 {result / loop:.10f} - {result / loop} {test3()}")
result = timeit.timeit('test4()', globals=globals(), number=loop)
print(f"4 {result / loop:.10f} - {result / loop} {test4()}")
result = timeit.timeit('test5()', globals=globals(), number=loop)
print(f"5 {result / loop:.10f} - {result / loop} {test5()}")
result = timeit.timeit('test6()', globals=globals(), number=loop)
print(f"6 {result / loop:.10f} - {result / loop} {test6()}")
result = timeit.timeit('test7()', globals=globals(), number=loop)
print(f"7 {result / loop:.10f} - {result / loop} {test7()}")
result = timeit.timeit('test8()', globals=globals(), number=loop)
print(f"8 {result / loop:.10f} - {result / loop} {test8()}")
result = timeit.timeit('test9()', globals=globals(), number=loop)
print(f"9 {result / loop:.10f} - {result / loop} {test9()}")
result = timeit.timeit('test10()', globals=globals(), number=loop)
print(f"10 {result / loop:.10f} - {result / loop} {test10()}")
result = timeit.timeit('test11()', globals=globals(), number=loop)
print(f"11 {result / loop:.10f} - {result / loop} {test11()}")
result = timeit.timeit('test12()', globals=globals(), number=loop)
print(f"12 {result / loop:.10f} - {result / loop} {test12()}")

158
tests/timeit/04.wcwidth.bisearch.py

@ -0,0 +1,158 @@
#!/usr/bin/env python3
# MIT License
#
# Copyright (c) 2021 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import sys, os
import timeit
import random
from wcwidth import *
from functools import lru_cache
sys.path.append(os.path.join(sys.path[0],'../..'))
sys.path.append(os.path.join(sys.path[0],'.'))
import TermTk as ttk
# Try to create a table with ~200 entries
print(f"Create Table...")
table = []
base = 0x1000
for _ in range(200):
incr = random.randint(0x10,0x200)
table.append((base,base+incr))
base += incr + random.randint(0x10,0x100)
table = tuple(table)
print(f"Create Done!!!")
for a,b in table:
print(f"0x{a:06x}, 0x{b:06x}")
print(f"Create Set...")
tset = []
for a,b in table:
for v in range(a,b+1):
tset.append(v)
tset = set(tset)
print(f"Create Set DONE!!!")
print(f"len tset 0x{len(tset):04x}")
print(f"Create CharSetStringTest...")
cstr = ""
for _ in range(0x4000):
cstr += chr(random.randint(0xA0,0x40000))
print(f"Create CharSetStringTest DONE!!!")
@lru_cache(maxsize=3)
def ttt(val):
return random.randint(10,100)
print(f"{ttt(1)=}")
print(f"{ttt(2)=}")
print(f"{ttt(3)=}")
print(f"{ttt(1)=}")
print(f"{ttt(2)=}")
print(f"{ttt(3)=}")
print(f"{ttt(4)=}")
print(f"{ttt(1)=}")
print(f"{ttt(3)=}")
print(f"{ttt(2)=}")
def _bisearch(ucs, table):
lbound = 0
ubound = len(table) - 1
if ucs < table[0][0] or ucs > table[ubound][1]:
return 0
while ubound >= lbound:
mid = (lbound + ubound) // 2
if ucs > table[mid][1]:
lbound = mid + 1
elif ucs < table[mid][0]:
ubound = mid - 1
else:
return 1
return 0
@lru_cache(maxsize=1000)
def _bicache(ucs, table):
lbound = 0
ubound = len(table) - 1
if ucs < table[0][0] or ucs > table[ubound][1]:
return 0
while ubound >= lbound:
mid = (lbound + ubound) // 2
if ucs > table[mid][1]:
lbound = mid + 1
elif ucs < table[mid][0]:
ubound = mid - 1
else:
return 1
return 0
def test1():
cw = 0
for ch in cstr:
cw += _bisearch(ord(ch), table)
return cw
def test2():
cw = 0
for ch in cstr:
cw += _bicache(ord(ch), table)
return cw
def test3():
return wcswidth(cstr)
def test4():
cw = 0
for ch in cstr:
cw += 1 if ord(ch) in tset else 0
return cw
def test5():
cw = sum([1 if ord(ch) in tset else 0 for ch in cstr])
return cw
def test6():
return sum([ord(ch) in tset for ch in cstr])
loop = 100
result = timeit.timeit('test4()', globals=globals(), number=loop)
print(f"{result / loop:.10f} - {result / loop} {test4()}")
result = timeit.timeit('test5()', globals=globals(), number=loop)
print(f"{result / loop:.10f} - {result / loop} {test4()}")
result = timeit.timeit('test6()', globals=globals(), number=loop)
print(f"{result / loop:.10f} - {result / loop} {test4()}")
result = timeit.timeit('test3()', globals=globals(), number=loop)
print(f"{result / loop:.10f} - {result / loop} {test3()}")
result = timeit.timeit('test1()', globals=globals(), number=loop)
print(f"{result / loop:.10f} - {result / loop} {test1()}")
result = timeit.timeit('test2()', globals=globals(), number=loop)
print(f"{result / loop:.10f} - {result / loop} {test2()}")

249
tests/timeit/05.wcwidth.bisearch.py

@ -0,0 +1,249 @@
#!/usr/bin/env python3
# MIT License
#
# Copyright (c) 2021 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import sys, os
import timeit
import random
import unicodedata
import wcwidth
from functools import lru_cache
sys.path.append(os.path.join(sys.path[0],'../..'))
sys.path.append(os.path.join(sys.path[0],'.'))
import TermTk as ttk
_unicode_version = "13.0.0"
zw = wcwidth.ZERO_WIDTH[_unicode_version]
# zwcf = wcwidth.ZERO_WIDTH_CF
we = wcwidth.WIDE_EASTASIAN[_unicode_version]
zwcf = [
0, # Null (Cc)
0x034F, # Combining grapheme joiner (Mn)
0x200B, # Zero width space
0x200C, # Zero width non-joiner
0x200D, # Zero width joiner
0x200E, # Left-to-right mark
0x200F, # Right-to-left mark
0x2028, # Line separator (Zl)
0x2029, # Paragraph separator (Zp)
0x202A, # Left-to-right embedding
0x202B, # Right-to-left embedding
0x202C, # Pop directional formatting
0x202D, # Left-to-right override
0x202E, # Right-to-left override
0x2060, # Word joiner
0x2061, # Function application
0x2062, # Invisible times
0x2063, # Invisible separator
]
def set2binmask(s):
ret = []
for v in s:
id = v >> 5
mask = v & 0x1F
bit = 1 << mask
if id >= len(ret):
ret += [0]*(id-len(ret)+2)
ret[id] |= bit
return ret
print(f"Create Set...")
zset = []
for a,b in zw:
for v in range(a,b+1):
zset.append(v)
for v in zwcf:
zset.append(v)
zset = set(zset)
wset = []
for a,b in we:
for v in range(a,b+1):
wset.append(v)
wset = set(wset)
print(f"Create Set DONE!!!")
print(f"Create CharSetStringTest...")
cstr = ""
for _ in range(0x4000):
cstr += chr(random.randint(0x100,0x20000))
print(f"Create CharSetStringTest DONE!!!")
# print(f"{set2binmask(zset)}")
bzset = set2binmask(zset)
bwset = set2binmask(wset)
print(f"len zset 0x{len(zset):04x}")
print(f"len zset 0x{len(bzset):04x}")
print(f"len wset 0x{len(wset):04x}")
print(f"len wset 0x{len(bwset):04x}")
print(f"len cstr 0x{len(cstr):04x}")
print([f"'{ch}':{unicodedata.east_asian_width(ch)}:{unicodedata.category(ch)}" for ch in cstr])
# @lru_cache(maxsize=3)
# def ttt(val):
# return random.randint(10,100)
#
# print(f"{ttt(1)=}")
# print(f"{ttt(2)=}")
# print(f"{ttt(3)=}")
# print(f"{ttt(1)=}")unicodedata.category
# print(f"{ttt(1)=}")
# print(f"{ttt(3)=}")
# print(f"{ttt(2)=}")
def _bisearch(ucs, table):
lbound = 0
ubound = len(table) - 1
if ucs < table[0][0] or ucs > table[ubound][1]:
return 0
while ubound >= lbound:
mid = (lbound + ubound) // 2
if ucs > table[mid][1]:
lbound = mid + 1
elif ucs < table[mid][0]:
ubound = mid - 1
else:
return 1
return 0
@lru_cache(maxsize=1000)
def _bicache(ucs, table):
lbound = 0
ubound = len(table) - 1
if ucs < table[0][0] or ucs > table[ubound][1]:
return 0
while ubound >= lbound:
mid = (lbound + ubound) // 2
if ucs > table[mid][1]:
lbound = mid + 1
elif ucs < table[mid][0]:
ubound = mid - 1
else:
return 1
return 0
def test1():
cw = 0
for ch in cstr:
cw += _bisearch(ord(ch), zw)
return cw
def test2():
cw = 0
for ch in cstr:
cw += _bicache(ord(ch), zw)
return cw
def test3():
return wcwidth.wcswidth(cstr)
def test4():
cw = 0
for ch in cstr:
cw += 1 if ord(ch) in wset else 0
return cw
def test5():
cw = sum([1 if ord(ch) in wset else 0 for ch in cstr])
return cw
def test6():
return len(cstr) + sum([ord(ch) in wset for ch in cstr]) - sum([ord(ch) in zset for ch in cstr])
def test7():
return len(cstr) + sum([bwset[ord(ch)>>5]>>(ord(ch)&0x1F)&1 for ch in cstr]) - sum([bzset[ord(ch)>>5]>>(ord(ch)&0x1F)&1 for ch in cstr])
def test8():
return len(cstr) + sum([bwset[ord(ch)>>5]>>(ord(ch)&0x1F)&1 for ch in cstr]) - sum([ord(ch) in zset for ch in cstr])
def test9():
return len(cstr) + sum([0!=(bwset[ord(ch)>>5]&(1<<(ord(ch)&0x1F))) for ch in cstr]) - sum([ord(ch) in zset for ch in cstr])
def test10():
return ( len(cstr) +
sum(['W'==unicodedata.east_asian_width(ch) for ch in cstr]) -
sum(['Me'==(c:=unicodedata.category(ch)) or 'Mn'==c for ch in cstr]) )
def test11():
return ( len(cstr) +
sum([unicodedata.east_asian_width(ch) == 'W' for ch in cstr]) -
sum([unicodedata.category(ch) in ('Me','Mn') for ch in cstr]) )
def test12():
retTxt = []
retCol = []
for i,ch in enumerate(cstr):
if unicodedata.east_asian_width(ch) == 'W':
retTxt += (ch,'')
retCol += (ch,ch)
if unicodedata.category(ch) in ('Me','Mn'):
retTxt[-1]+=ch
else:
retTxt.append(ch)
retCol.append(ch)
return (len(retTxt), len(retCol))
loop = 100
result = timeit.timeit('test4()', globals=globals(), number=loop)
print(f"4 {result / loop:.10f} - {result / loop} {test4()}")
result = timeit.timeit('test5()', globals=globals(), number=loop)
print(f"5 {result / loop:.10f} - {result / loop} {test5()}")
result = timeit.timeit('test6()', globals=globals(), number=loop)
print(f"6 {result / loop:.10f} - {result / loop} {test6()}")
result = timeit.timeit('test10()', globals=globals(), number=loop)
print(f"10 {result / loop:.10f} - {result / loop} {test10()}")
result = timeit.timeit('test11()', globals=globals(), number=loop)
print(f"11 {result / loop:.10f} - {result / loop} {test11()}")
result = timeit.timeit('test7()', globals=globals(), number=loop)
print(f"7 {result / loop:.10f} - {result / loop} {test7()}")
result = timeit.timeit('test8()', globals=globals(), number=loop)
print(f"8 {result / loop:.10f} - {result / loop} {test8()}")
result = timeit.timeit('test9()', globals=globals(), number=loop)
print(f"9 {result / loop:.10f} - {result / loop} {test9()}")
result = timeit.timeit('test12()', globals=globals(), number=loop)
print(f"12 {result / loop:.10f} - {result / loop} {test12()}")
result = timeit.timeit('test3()', globals=globals(), number=loop)
print(f"3w {result / loop:.10f} - {result / loop} {test3()}")
result = timeit.timeit('test1()', globals=globals(), number=loop)
print(f"1w {result / loop:.10f} - {result / loop} {test1()}")
result = timeit.timeit('test2()', globals=globals(), number=loop)
print(f"2w {result / loop:.10f} - {result / loop} {test2()}")

143
tests/timeit/06.functionPointer.py

@ -0,0 +1,143 @@
#!/usr/bin/env python3
# MIT License
#
# Copyright (c) 2021 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import sys, os
import timeit
import random
sys.path.append(os.path.join(sys.path[0],'../..'))
sys.path.append(os.path.join(sys.path[0],'.'))
import TermTk as ttk
class A():
def test(self):
return 1
class B(A):
def test(self):
return 2
class C(B):
def test(self):
return 3
class D():
__slots__ = ('test')
def __init__(self, sw=True):
if sw:
self.test = self._testA
else:
self.test = self._testB
def _testA(self):
return 11
def _testB(self):
return 12
class E():
__slots__ = ('_sw')
def __init__(self, sw=True):
self._sw = sw
def test(self):
if self._sw:
return 21
else:
return 22
class F():
__slots__ = ('_sw')
def __init__(self, sw=True):
self._sw = sw
def test(self):
if self._sw:
return self._testA()
else:
return self._testB()
def _testA(self):
return 31
def _testB(self):
return 32
a = A()
b = B()
c = C()
da = D(sw=True)
db = D(sw=False)
ea = E(sw=True)
eb = E(sw=False)
fa = F(sw=True)
fb = F(sw=False)
def test1():
return a.test()
def test2():
return b.test()
def test3():
return c.test()
def test4():
return da.test()
def test5():
return db.test()
def test6():
return ea.test()
def test7():
return eb.test()
def test8():
return fa.test()
def test9():
return fb.test()
def test10(): return None
def test11(): return None
def test12(): return None
loop = 100000
result = timeit.timeit('test1()', globals=globals(), number=loop)
print(f"1a {result / loop:.10f} - {result / loop} {test1()}")
result = timeit.timeit('test2()', globals=globals(), number=loop)
print(f"2b {result / loop:.10f} - {result / loop} {test2()}")
result = timeit.timeit('test3()', globals=globals(), number=loop)
print(f"3c {result / loop:.10f} - {result / loop} {test3()}")
result = timeit.timeit('test4()', globals=globals(), number=loop)
print(f"4da {result / loop:.10f} - {result / loop} {test4()}")
result = timeit.timeit('test5()', globals=globals(), number=loop)
print(f"5db {result / loop:.10f} - {result / loop} {test5()}")
result = timeit.timeit('test6()', globals=globals(), number=loop)
print(f"6ea {result / loop:.10f} - {result / loop} {test6()}")
result = timeit.timeit('test7()', globals=globals(), number=loop)
print(f"7eb {result / loop:.10f} - {result / loop} {test7()}")
result = timeit.timeit('test8()', globals=globals(), number=loop)
print(f"8fa {result / loop:.10f} - {result / loop} {test8()}")
result = timeit.timeit('test9()', globals=globals(), number=loop)
print(f"9fb {result / loop:.10f} - {result / loop} {test9()}")
result = timeit.timeit('test10()', globals=globals(), number=loop)
print(f"10 {result / loop:.10f} - {result / loop} {test10()}")
result = timeit.timeit('test11()', globals=globals(), number=loop)
print(f"11 {result / loop:.10f} - {result / loop} {test11()}")
result = timeit.timeit('test12()', globals=globals(), number=loop)
print(f"12 {result / loop:.10f} - {result / loop} {test12()}")

5
tools/check.import.sh

@ -29,8 +29,9 @@ __check(){
-e "ttk.py:import platform" \
-e "clipboard.py:import importlib.util" \
-e "filebuffer.py:import threading" \
-e "texedit.py:from math import log10, ceil"
-e "texedit.py:from math import log10, ceil" \
-e "canvas.py:import unicodedata" \
-e "string.py:import unicodedata"
} ;
if __check ; then

56
tools/gen.utf8.sizeMap.py

@ -0,0 +1,56 @@
#!/usr/bin/env python3
# MIT License
#
# Copyright (c) 2022 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import urllib.request
import re
URI='http://www.unicode.org/Public/UCD/latest/ucd/DerivedAge.txt'
response = urllib.request.urlopen(URI)
# Matching:
# "FE24..FE26 ; 5.1 # [3] COMBINING MACRON LEFT HALF..COMBINING CONJOINING MACRON"
# "10190..1019B ; 5.1 # [12] ROMAN SEXTANS SIGN..ROMAN CENTURIAL SIGN"
rangeMatch = re.compile(r'^([0-9A-F]+)\.\.([0-9A-F]+) *; ([0-9\.]+) (#.*)$')
# Matching:
# "A95F ; 5.1 # REJANG SECTION MARK"
# "1093F ; 5.1 # LYDIAN TRIANGULAR MARK"
singleMatch = re.compile(r'^([0-9A-F]+) *; ([0-9\.]+) (#.*)$')
for line in response.readlines():
# print(line.decode('utf-8'))
if m := rangeMatch.match(line.decode('utf-8')):
rfr = m.group(1)
rto = m.group(2)
rver = m.group(3)
rdesc = m.group(4)
print(f"{rver=} {rfr=} {rto=} {rdesc=}")
elif m := singleMatch.match(line.decode('utf-8')):
rm = m.group(1)
rver = m.group(2)
rdesc = m.group(3)
print(f"{rver=} {rm=} {rdesc=}")
Loading…
Cancel
Save