Browse Source

Merge pull request #22 from ceccopierangiolieugenio/TextEdit

Text Edit Widget
pull/23/head 0.8.0a
Ceccopierangiolieugenio 4 years ago committed by GitHub
parent
commit
b98556ef73
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      README.md
  2. 3
      TermTk/TTkCore/TTkTerm/term.py
  3. 125
      TermTk/TTkCore/string.py
  4. 2
      TermTk/TTkWidgets/scrollbar.py
  5. 287
      TermTk/TTkWidgets/texedit.py
  6. 4
      TermTk/TTkWidgets/widget.py
  7. 3
      demo/demo.py
  8. 69
      demo/showcase/textedit.py
  9. 3
      docs/MDNotes/TODO.md

3
README.md

@ -90,8 +90,9 @@ cprofilev -f profiler.txt
- [Textual](https://github.com/Textualize/textual) - TUI (Text User Interface) framework for Python inspired by modern web development
- [Rich](https://github.com/Textualize/rich) - Python library for rich text and beautiful formatting in the terminal
- [PyCuT](https://github.com/ceccopierangiolieugenio/pyCuT) - terminal graphic library loosely based on QT api (my previous failed attempt)
- [pyTooling.TerminalUI](https://github.com/pyTooling/pyTooling.TerminalUI) - A set of helpers to implement a text user interface (TUI) in a terminal.
- Non Python
- [Turbo Vision](http://tvision.sourceforge.net)
- [ncurses](https://en.wikipedia.org/wiki/Ncurses)
- [tui.el](https://github.com/ebpa/tui.el) - An experimental text-based UI framework for Emacs modeled after React
- [tui.el](https://github.com/ebpa/tui.el) - An experimental text-based UI framework for Emacs modeled after React

3
TermTk/TTkCore/TTkTerm/term.py

@ -29,9 +29,6 @@ except Exception as e:
print(f'ERROR: {e}')
exit(1)
from tkinter import OFF
from turtle import onclick
class TTkTerm():
CLEAR = "\033[2J\033[0;0f" # Clear screen and set cursor to position 0,0
ALT_SCREEN = "\033[?1049h" #* Switch to alternate screen

125
TermTk/TTkCore/string.py

@ -22,7 +22,6 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from hashlib import new
import re
from TermTk.TTkCore.constant import TTkK
@ -31,11 +30,40 @@ from TermTk.TTkCore.signal import pyTTkSlot, pyTTkSignal
from TermTk.TTkCore.color import TTkColor, _TTkColor
class TTkString():
''' TermTk String Helper
The TTkString constructor creates a terminal String object.
:param text: text of the string, defaults to ""
:type text: str, optional
:param color: the color of the string, defaults to :class:`~TermTk.TTkCore.color.TTkColor.RST`
:type color: :class:`~TermTk.TTkCore.color.TTkColor`, optional
Example:
.. code:: python
# No params Constructor
str1 = TTkString() + "test 1"
str2 = TTkString() + TTkColor.BOLD + "test 2"
# Indexed params constructor
str3 = TTkString("test 3")
str4 = TTkString("test 4", TTkColor.ITALIC)
# Named params constructor
str5 = TTkString(text="test 5")
str6 = TTkString(text="test 6", color=TTkColor.ITALIC+TTkColor.bg("000044"))
# Combination of constructors (Highly Unrecommended)
str7 = TTkString("test 7", color=TTkColor.fg('#FF0000'))
'''
__slots__ = ('_text','_colors','_baseColor')
def __init__(self):
self._baseColor = TTkColor.RST
self._text = ""
self._colors = []
def __init__(self, text="", color=TTkColor.RST):
self._text = text
self._baseColor = color
self._colors = [self._baseColor]*len(self._text)
def __len__(self):
return len(self._text)
@ -84,9 +112,11 @@ class TTkString():
def __ge__(self, other): return self._text >= other._text
def toAscii(self):
''' Return the ascii representation of the string '''
return self._text
def toAansi(self):
''' Return the ansii (terminal colors/events) representation of the string '''
out = ""
color = None
for ch, col in zip(self._text, self._colors):
@ -97,6 +127,15 @@ class TTkString():
return out
def align(self, width=None, color=TTkColor.RST, alignment=TTkK.NONE):
''' Align the string
:param width: the new width
:type width: int, optional
:param color: the color of the padding, defaults to :class:`~TermTk.TTkCore.color.TTkColor.RST`
:type color: :class:`~TermTk.TTkCore.color.TTkColor`, optional
: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)
if not width or width == lentxt: return self
@ -126,6 +165,17 @@ class TTkString():
return ret
def replace(self, *args, **kwargs):
''' **replace** (*old*, *new*, *count*)
Replace "**old**" match with "**new**" string for "**count**" times
:param old: the match to be placed
:type old: str
:param new: the match to replace
:type new: str, optional
:param count: the number of occurrences
:type count: int, optional
'''
old = args[0]
new = args[1]
count = args[2] if len(args)==3 else 0x1000000
@ -160,7 +210,20 @@ class TTkString():
return ret
def setColor(self, color, match=None, posFrom=0, posTo=0):
def setColor(self, color, match=None, posFrom=None, posTo=None):
''' Set the color of the entire string or a slice of it
If only the color is specified, the entore sting is colorized
:param color: the color to be used, defaults to :class:`~TermTk.TTkCore.color.TTkColor.RST`
:type color: :class:`~TermTk.TTkCore.color.TTkColor`
:param match: the match to colorize
:type match: str, optional
:param posFrom: the initial position of the color
:type posFrom: int, optional
:param posTo: the final position of the color
:type posTo: int, optional
'''
ret = TTkString()
ret._text += self._text
if match:
@ -171,21 +234,37 @@ class TTkString():
start = pos+lenMatch
for i in range(pos, pos+lenMatch):
ret._colors[i] = color
elif posFrom == posTo == None:
ret._colors = [color]*len(self._text)
elif posFrom < posTo:
ret._colors += self._colors
for i in range(posFrom, posTo):
ret._colors[i] = color
else:
ret._colors = [color]*len(self._text)
ret._colors += self._colors
return ret
def substring(self, fr=None, to=None):
''' Return the substring
:param fr: the starting of the slice, defaults to 0
:type fr: int, optional
:param to: the ending of the slice, defaults to the end of the string
:type to: int, optional
'''
ret = TTkString()
ret._text = self._text[fr:to]
ret._colors = self._colors[fr:to]
return ret
def split(self, separator ):
''' Split the string using a separator
.. note:: Only a one char separator is currently supported
:param separator: the "**char**" separator to be used
:type separator: str
'''
ret = []
pos = 0
if len(separator)==1:
@ -203,7 +282,37 @@ class TTkString():
return (self._text,self._colors)
def search(self, regexp, ignoreCase=False):
''' Return the **re.match** of the **regexp**
:param regexp: the regular expression to be matched
:type regexp: str
:param ignoreCase: Ignore case, defaults to **False**
:type ignoreCase: bool
'''
return re.search(regexp, self._text, re.IGNORECASE if ignoreCase else 0)
def findall(self, regexp, ignoreCase=False):
return re.findall(regexp, self._text, re.IGNORECASE if ignoreCase else 0)
''' FindAll the **regexp** matches in the string
:param regexp: the regular expression to be matched
:type regexp: str
:param ignoreCase: Ignore case, defaults to **False**
:type ignoreCase: bool
'''
return re.findall(regexp, self._text, re.IGNORECASE if ignoreCase else 0)
def getIndexes(self, char):
return [i for i,c in enumerate(self._text) if c==char]
def join(self, strings):
''' Join the input strings using the current as separator
:param strings: the list of strings to be joined
:type strings: list
'''
if not strings:
return TTkString()
ret = TTkString() + strings[0]
for s in strings[1:]:
ret += self + s
return ret

2
TermTk/TTkWidgets/scrollbar.py

@ -22,8 +22,6 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import math
from TermTk.TTkCore.constant import TTkK
from TermTk.TTkCore.log import TTkLog
from TermTk.TTkCore.signal import pyTTkSlot, pyTTkSignal

287
TermTk/TTkWidgets/texedit.py

@ -22,7 +22,6 @@
# 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.TTkLayouts.gridlayout import TTkGridLayout
@ -32,35 +31,45 @@ from TermTk.TTkWidgets.scrollbar import TTkScrollBar
from TermTk.TTkAbstract.abstractscrollarea import TTkAbstractScrollArea
from TermTk.TTkAbstract.abstractscrollview import TTkAbstractScrollView
class _TTkTextEditView(TTkAbstractScrollView):
__slots__ = ('_lines', '_hsize')
__slots__ = (
'_lines', '_hsize',
'_cursorPos', '_cursorParams', '_selectionFrom', '_selectionTo',
'_replace',
'_readOnly'
)
def __init__(self, *args, **kwargs):
super().__init__(self, *args, **kwargs)
self._name = kwargs.get('name' , '_TTkTextEditView' )
self._readOnly = True
self._hsize = 0
self._lines = []
self._lines = ['']
self._replace = False
self._cursorPos = (0,0)
self._selectionFrom = (0,0)
self._selectionTo = (0,0)
self._cursorParams = None
self.setFocusPolicy(TTkK.ClickFocus + TTkK.TabFocus)
@pyTTkSlot(str)
def setText(self, text):
self._lines = [line for line in text.split('\n')]
self.viewMoveTo(0, 0)
self._updateSize()
self.viewChanged.emit()
self.update()
def isReadOnly(self) -> bool :
return self._readOnly
def setReadOnly(self, ro):
self._readOnly = ro
@pyTTkSlot(str)
def setLines(self, lines):
self._lines = lines
def setText(self, text):
if type(text) == str:
text = TTkString() + text
self._lines = text.split('\n')
self.viewMoveTo(0, 0)
self._updateSize()
self.viewChanged.emit()
self.update()
def _updateSize(self):
self._hsize = 0
for l in self._lines:
self._hsize = max(self._hsize, len(l))
self._hsize = max( [ len(l) for l in self._lines ] )
def viewFullAreaSize(self) -> (int, int):
return self._hsize, len(self._lines)
@ -68,17 +77,257 @@ class _TTkTextEditView(TTkAbstractScrollView):
def viewDisplayedSize(self) -> (int, int):
return self.size()
def _pushCursor(self):
if self._readOnly or not self.hasFocus():
return
ox, oy = self.getViewOffsets()
x = self._cursorPos[0]-ox
y = self._cursorPos[1]-oy
if x > self.width() or y>=self.height() or \
x<0 or y<0:
TTkHelper.hideCursor()
return
# Avoid the show/move cursor to be called again if in the same position
if self._cursorParams and \
self._cursorParams['pos'] == (x,y) and \
self._cursorParams['replace'] == self._replace:
return
self._cursorParams = {'pos': (x,y), 'replace': self._replace}
TTkHelper.moveCursor(self,x,y)
if self._replace:
TTkHelper.showCursor(TTkK.Cursor_Blinking_Block)
else:
TTkHelper.showCursor(TTkK.Cursor_Blinking_Bar)
self.update()
def _setCursorPos(self, x, y):
y = max(0,min(y,len(self._lines)-1))
# The replace cursor need to be aligned to the char
# The Insert cursor must be placed between chars
if self._replace:
x = max(0,min(x,len(self._lines[y])-1))
else:
x = max(0,min(x,len(self._lines[y])))
self._cursorPos = (x,y)
self._selectionFrom = (x,y)
self._selectionTo = (x,y)
self._scrolToInclude(x,y)
def _scrolToInclude(self, x, y):
# Scroll the area (if required) to include the position x,y
_,_,w,h = self.geometry()
offx, offy = self.getViewOffsets()
offx = max(min(offx, x),x-w)
offy = max(min(offy, y),y-h+1)
self.viewMoveTo(offx, offy)
def _selection(self) -> bool:
return self._selectionFrom != self._selectionTo
def _eraseSelection(self):
if self._selection(): # delete selection
sx1,sy1 = self._selectionFrom
sx2,sy2 = self._selectionTo
self._cursorPos = self._selectionFrom
self._selectionTo = self._selectionFrom
self._lines[sy1] = self._lines[sy1].substring(to=sx1) + \
self._lines[sy2].substring(fr=sx2)
self._lines = self._lines[:sy1+1] + self._lines[sy2+1:]
def mousePressEvent(self, evt) -> bool:
if self._readOnly:
return super().mousePressEvent(evt)
ox, oy = self.getViewOffsets()
y = max(0,min(evt.y + oy,len(self._lines)))
x = max(0,min(evt.x + ox,len(self._lines[y])))
self._cursorPos = (x,y)
self._selectionFrom = (x,y)
self._selectionTo = (x,y)
# TTkLog.debug(f"{self._cursorPos=}")
self.update()
return True
def mouseDragEvent(self, evt) -> bool:
if self._readOnly:
return super().mouseDragEvent(evt)
ox, oy = self.getViewOffsets()
y = max(0,min(evt.y + oy,len(self._lines)))
x = max(0,min(evt.x + ox,len(self._lines[y])))
cx = self._cursorPos[0]
cy = self._cursorPos[1]
if y < cy: # Mouse Dragged above the cursor
self._selectionFrom = ( x, y )
self._selectionTo = ( cx, cy )
elif y > cy: # Mouse Dragged below the cursor
self._selectionFrom = ( cx, cy )
self._selectionTo = ( x, y )
else: # Mouse on the same line of the cursor
self._selectionFrom = ( min(cx,x), y )
self._selectionTo = ( max(cx,x), y )
self._scrolToInclude(x,y)
self.update()
return True
def mouseDoubleClickEvent(self, evt) -> bool:
if self._readOnly:
return super().mouseDoubleClickEvent(evt)
ox, oy = self.getViewOffsets()
y = max(0,min(evt.y + oy,len(self._lines)))
x = max(0,min(evt.x + ox,len(self._lines[y])))
self._cursorPos = (x,y)
before = self._lines[y].substring(to=x)
after = self._lines[y].substring(fr=x)
xFrom = len(before)
xTo = len(before)
selectRE = '[a-zA-Z0-9:,./]*'
if m := before.search(selectRE+'$'):
xFrom -= len(m.group(0))
if m := after.search('^'+selectRE):
xTo += len(m.group(0))
self._selectionFrom = ( xFrom, y )
self._selectionTo = ( xTo, y )
self.update()
return True
def mouseTapEvent(self, evt) -> bool:
if self._readOnly:
return super().mouseTapEvent(evt)
ox, oy = self.getViewOffsets()
y = max(0,min(evt.y + oy,len(self._lines)))
x = max(0,min(evt.x + ox,len(self._lines[y])))
self._cursorPos = (x,y)
self._selectionFrom = ( 0 , y )
self._selectionTo = ( len(self._lines[y]) , y )
self.update()
return True
def mouseReleaseEvent(self, evt) -> bool:
if self._readOnly:
return super().mouseReleaseEvent(evt)
ox, oy = self.getViewOffsets()
y = max(0,min(evt.y + oy,len(self._lines)))
x = max(0,min(evt.x + ox,len(self._lines[y])))
self._cursorPos = (x,y)
self.update()
return True
def keyEvent(self, evt):
if self._readOnly:
return super().keyEvent(evt)
if evt.type == TTkK.SpecialKey:
_,_,w,h = self.geometry()
cx = self._cursorPos[0]
cy = self._cursorPos[1]
# Don't Handle the special tab key, for now
if evt.key == TTkK.Key_Tab:
return False
if evt.key == TTkK.Key_Up: self._setCursorPos(cx , cy-1)
elif evt.key == TTkK.Key_Down: self._setCursorPos(cx , cy+1)
elif evt.key == TTkK.Key_Left: self._setCursorPos(cx-1, cy )
elif evt.key == TTkK.Key_Right: self._setCursorPos(cx+1, cy )
elif evt.key == TTkK.Key_End: self._setCursorPos(len(self._lines[cy]) , cy )
elif evt.key == TTkK.Key_Home: self._setCursorPos(0 , cy )
elif evt.key == TTkK.Key_PageUp: self._setCursorPos(cx , cy - h)
elif evt.key == TTkK.Key_PageDown: self._setCursorPos(cx , cy + h)
elif evt.key == TTkK.Key_Insert:
self._replace = not self._replace
self._setCursorPos(cx , cy)
elif evt.key == TTkK.Key_Delete:
if self._selection():
self._eraseSelection()
else:
l = self._lines[cy]
if cx < len(l): # Erase next caracter on the same line
self._lines[cy] = l.substring(to=cx) + l.substring(fr=cx+1)
elif (cy+1)<len(self._lines): # End of the line, remove "\n" and merge with the next line
self._lines[cy] += self._lines[cy+1]
self._lines = self._lines[:cy+1] + self._lines[cy+2:]
self._setCursorPos(cx, cy)
elif evt.key == TTkK.Key_Backspace:
if self._selection():
self._eraseSelection()
else:
l = self._lines[cy]
if cx > 0: # Erase the previous character
cx-=1
self._lines[cy] = l.substring(to=cx) + l.substring(fr=cx+1)
self._setCursorPos(cx, cy)
elif cy>0: # Beginning of the line, remove "\n" and merge with the previous line
cx = len(self._lines[cy-1])
self._lines[cy-1] += l
self._lines = self._lines[:cy] + self._lines[cy+1:]
self._setCursorPos(cx, cy-1)
elif evt.key == TTkK.Key_Enter:
self._eraseSelection()
l = self._lines[cy]
self._lines[cy] = l.substring(to=cx)
self._lines = self._lines[:cy+1] + [l.substring(fr=cx)] + self._lines[cy+1:]
self._setCursorPos(0,cy+1)
self.update()
return True
else: # Input char
self._eraseSelection()
cpx,cpy = self._cursorPos
l = self._lines[cpy]
self._lines[cpy] = l.substring(to=cpx) + evt.key + l.substring(fr=cpx)
self._setCursorPos(cpx+1,cpy)
self.update()
return True
return super().keyEvent(evt)
def focusInEvent(self):
self.update()
def focusOutEvent(self):
TTkHelper.hideCursor()
def paintEvent(self):
ox, oy = self.getViewOffsets()
for y, t in enumerate(self._lines[oy:]):
if self.hasFocus():
color = TTkCfg.theme.lineEditTextColorFocus
selectColor = TTkCfg.theme.lineEditTextColorSelected
else:
color = TTkCfg.theme.lineEditTextColor
selectColor = TTkCfg.theme.lineEditTextColorSelected
h = self.height()
for y, t in enumerate(self._lines[oy:oy+h]):
if self._selectionFrom[1] <= y+oy <= self._selectionTo[1]:
pf = 0 if y+oy > self._selectionFrom[1] else self._selectionFrom[0]
pt = len(t) if y+oy < self._selectionTo[1] else self._selectionTo[0]
t = t.setColor(color=selectColor, posFrom=pf, posTo=pt )
self._canvas.drawText(pos=(-ox,y), text=t)
self._pushCursor()
class TTkTextEdit(TTkAbstractScrollArea):
__slots__ = ('_textEditView', 'setText', 'setColoredLines')
__slots__ = (
'_textEditView',
# Forwarded Methods
'setText', 'isReadOnly', 'setReadOnly'
)
def __init__(self, *args, **kwargs):
super().__init__(self, *args, **kwargs)
self._name = kwargs.get('name' , 'TTkTextEdit' )
self._textEditView = _TTkTextEditView()
self.setViewport(self._textEditView)
self.setText = self._textEditView.setText
self.setLines = self._textEditView.setLines
self.isReadOnly = self._textEditView.isReadOnly
self.setReadOnly = self._textEditView.setReadOnly

4
TermTk/TTkWidgets/widget.py

@ -22,8 +22,6 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import time
from TermTk.TTkCore.cfg import TTkCfg, TTkGlbl
from TermTk.TTkCore.constant import TTkK
from TermTk.TTkCore.log import TTkLog
@ -55,7 +53,7 @@ class TTkWidget(TMouseEvents,TKeyEvents):
The TTkWidget class is the base class of all user interface objects
:param str name: the name of the widget, defaults to ""
:param name: the name of the widget, defaults to ""
:type name: str, optional
:param parent: the parent widget, defaults to None
:type parent: :class:`TTkWidget`, optional

3
demo/demo.py

@ -46,6 +46,7 @@ from showcase.colorpicker import demoColorPicker
from showcase.tree import demoTree
from showcase.fancytable import demoFancyTable
from showcase.fancytree import demoFancyTree
from showcase.textedit import demoTextEdit
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():
@ -149,6 +150,7 @@ def demoShowcase(root=None, border=True):
listMenu.addItem(f"Widgets")
tabWidgets = ttk.TTkTabWidget(parent=mainFrame, border=False, visible=False)
tabWidgets.addTab(demoFormWidgets(), " Form Test ")
tabWidgets.addTab(demoTextEdit(), " Text Edit ")
tabWidgets.addTab(demoList(), " List Test ")
tabWidgets.addTab(demoTree(), " Tree Test")
tabWidgets.addTab(demoTab(), " Tab Test ")
@ -156,6 +158,7 @@ def demoShowcase(root=None, border=True):
tabWidgets.addTab(demoFancyTree(), " Old Tree ")
tabWidgetsSources = [
'showcase/formwidgets.py',
'showcase/textedit.py',
'showcase/list.py',
'showcase/tree.py',
'showcase/tab.py',

69
demo/showcase/textedit.py

@ -0,0 +1,69 @@
#!/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 os
import sys
import random
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')
][random.randint(0,3)]
def getWord():
return ttk.TTkString(random.choice(words),randColor())
def getSentence(a,b,i):
return ttk.TTkString(" ").join([f"{i} "]+[getWord() for i in range(0,random.randint(a,b))])
def demoTextEdit(root=None):
te = ttk.TTkTextEdit(parent=root)
te.setReadOnly(False)
te.setText(ttk.TTkString('\n').join([ getSentence(5,25,i) for i in range(50)]))
return te
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:
rootTree = root
root.setLayout(ttk.TTkGridLayout())
else:
rootTree = ttk.TTkWindow(parent=root,pos = (0,0), size=(70,40), title="Test Text Edit", layout=ttk.TTkGridLayout(), border=True)
demoTextEdit(rootTree)
root.mainloop()
if __name__ == "__main__":
main()

3
docs/MDNotes/TODO.md

@ -11,6 +11,9 @@
- [ ] Support Hyperlink: (gnome-terminal)
https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
- [x] Process child events before parent
- [ ] Rewrite the way focus is handled
https://doc.qt.io/qt-5/focus.html
Ref: https://github.com/ceccopierangiolieugenio/scripts/blob/master/Programming/python/pyqt5/textedit.001.py
## Terminal Helper
- [ ] Events

Loading…
Cancel
Save