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 - [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 - [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) - [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 - Non Python
- [Turbo Vision](http://tvision.sourceforge.net) - [Turbo Vision](http://tvision.sourceforge.net)
- [ncurses](https://en.wikipedia.org/wiki/Ncurses) - [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}') print(f'ERROR: {e}')
exit(1) exit(1)
from tkinter import OFF
from turtle import onclick
class TTkTerm(): class TTkTerm():
CLEAR = "\033[2J\033[0;0f" # Clear screen and set cursor to position 0,0 CLEAR = "\033[2J\033[0;0f" # Clear screen and set cursor to position 0,0
ALT_SCREEN = "\033[?1049h" #* Switch to alternate screen 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 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE. # SOFTWARE.
from hashlib import new
import re import re
from TermTk.TTkCore.constant import TTkK from TermTk.TTkCore.constant import TTkK
@ -31,11 +30,40 @@ from TermTk.TTkCore.signal import pyTTkSlot, pyTTkSignal
from TermTk.TTkCore.color import TTkColor, _TTkColor from TermTk.TTkCore.color import TTkColor, _TTkColor
class TTkString(): 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') __slots__ = ('_text','_colors','_baseColor')
def __init__(self):
self._baseColor = TTkColor.RST def __init__(self, text="", color=TTkColor.RST):
self._text = "" self._text = text
self._colors = [] self._baseColor = color
self._colors = [self._baseColor]*len(self._text)
def __len__(self): def __len__(self):
return len(self._text) return len(self._text)
@ -84,9 +112,11 @@ class TTkString():
def __ge__(self, other): return self._text >= other._text def __ge__(self, other): return self._text >= other._text
def toAscii(self): def toAscii(self):
''' Return the ascii representation of the string '''
return self._text return self._text
def toAansi(self): def toAansi(self):
''' Return the ansii (terminal colors/events) representation of the string '''
out = "" out = ""
color = None color = None
for ch, col in zip(self._text, self._colors): for ch, col in zip(self._text, self._colors):
@ -97,6 +127,15 @@ class TTkString():
return out return out
def align(self, width=None, color=TTkColor.RST, alignment=TTkK.NONE): 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) lentxt = len(self._text)
if not width or width == lentxt: return self if not width or width == lentxt: return self
@ -126,6 +165,17 @@ class TTkString():
return ret return ret
def replace(self, *args, **kwargs): 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] old = args[0]
new = args[1] new = args[1]
count = args[2] if len(args)==3 else 0x1000000 count = args[2] if len(args)==3 else 0x1000000
@ -160,7 +210,20 @@ class TTkString():
return ret 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 = TTkString()
ret._text += self._text ret._text += self._text
if match: if match:
@ -171,21 +234,37 @@ class TTkString():
start = pos+lenMatch start = pos+lenMatch
for i in range(pos, pos+lenMatch): for i in range(pos, pos+lenMatch):
ret._colors[i] = color ret._colors[i] = color
elif posFrom == posTo == None:
ret._colors = [color]*len(self._text)
elif posFrom < posTo: elif posFrom < posTo:
ret._colors += self._colors ret._colors += self._colors
for i in range(posFrom, posTo): for i in range(posFrom, posTo):
ret._colors[i] = color ret._colors[i] = color
else: else:
ret._colors = [color]*len(self._text) ret._colors += self._colors
return ret return ret
def substring(self, fr=None, to=None): 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 = TTkString()
ret._text = self._text[fr:to] ret._text = self._text[fr:to]
ret._colors = self._colors[fr:to] ret._colors = self._colors[fr:to]
return ret return ret
def split(self, separator ): 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 = [] ret = []
pos = 0 pos = 0
if len(separator)==1: if len(separator)==1:
@ -203,7 +282,37 @@ class TTkString():
return (self._text,self._colors) return (self._text,self._colors)
def search(self, regexp, ignoreCase=False): 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) return re.search(regexp, self._text, re.IGNORECASE if ignoreCase else 0)
def findall(self, regexp, ignoreCase=False): 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 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE. # SOFTWARE.
import math
from TermTk.TTkCore.constant import TTkK from TermTk.TTkCore.constant import TTkK
from TermTk.TTkCore.log import TTkLog from TermTk.TTkCore.log import TTkLog
from TermTk.TTkCore.signal import pyTTkSlot, pyTTkSignal 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 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE. # SOFTWARE.
import os
from TermTk.TTkCore.log import TTkLog from TermTk.TTkCore.log import TTkLog
from TermTk.TTkWidgets.widget import * from TermTk.TTkWidgets.widget import *
from TermTk.TTkLayouts.gridlayout import TTkGridLayout 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.abstractscrollarea import TTkAbstractScrollArea
from TermTk.TTkAbstract.abstractscrollview import TTkAbstractScrollView from TermTk.TTkAbstract.abstractscrollview import TTkAbstractScrollView
class _TTkTextEditView(TTkAbstractScrollView): class _TTkTextEditView(TTkAbstractScrollView):
__slots__ = ('_lines', '_hsize') __slots__ = (
'_lines', '_hsize',
'_cursorPos', '_cursorParams', '_selectionFrom', '_selectionTo',
'_replace',
'_readOnly'
)
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(self, *args, **kwargs) super().__init__(self, *args, **kwargs)
self._name = kwargs.get('name' , '_TTkTextEditView' ) self._name = kwargs.get('name' , '_TTkTextEditView' )
self._readOnly = True
self._hsize = 0 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): def isReadOnly(self) -> bool :
self._lines = [line for line in text.split('\n')] return self._readOnly
self.viewMoveTo(0, 0)
self._updateSize() def setReadOnly(self, ro):
self.viewChanged.emit() self._readOnly = ro
self.update()
@pyTTkSlot(str) @pyTTkSlot(str)
def setLines(self, lines): def setText(self, text):
self._lines = lines if type(text) == str:
text = TTkString() + text
self._lines = text.split('\n')
self.viewMoveTo(0, 0) self.viewMoveTo(0, 0)
self._updateSize() self._updateSize()
self.viewChanged.emit() self.viewChanged.emit()
self.update() self.update()
def _updateSize(self): def _updateSize(self):
self._hsize = 0 self._hsize = max( [ len(l) for l in self._lines ] )
for l in self._lines:
self._hsize = max(self._hsize, len(l))
def viewFullAreaSize(self) -> (int, int): def viewFullAreaSize(self) -> (int, int):
return self._hsize, len(self._lines) return self._hsize, len(self._lines)
@ -68,17 +77,257 @@ class _TTkTextEditView(TTkAbstractScrollView):
def viewDisplayedSize(self) -> (int, int): def viewDisplayedSize(self) -> (int, int):
return self.size() 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): def paintEvent(self):
ox, oy = self.getViewOffsets() 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._canvas.drawText(pos=(-ox,y), text=t)
self._pushCursor()
class TTkTextEdit(TTkAbstractScrollArea): class TTkTextEdit(TTkAbstractScrollArea):
__slots__ = ('_textEditView', 'setText', 'setColoredLines') __slots__ = (
'_textEditView',
# Forwarded Methods
'setText', 'isReadOnly', 'setReadOnly'
)
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(self, *args, **kwargs) super().__init__(self, *args, **kwargs)
self._name = kwargs.get('name' , 'TTkTextEdit' ) self._name = kwargs.get('name' , 'TTkTextEdit' )
self._textEditView = _TTkTextEditView() self._textEditView = _TTkTextEditView()
self.setViewport(self._textEditView) self.setViewport(self._textEditView)
self.setText = self._textEditView.setText 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 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE. # SOFTWARE.
import time
from TermTk.TTkCore.cfg import TTkCfg, TTkGlbl from TermTk.TTkCore.cfg import TTkCfg, TTkGlbl
from TermTk.TTkCore.constant import TTkK from TermTk.TTkCore.constant import TTkK
from TermTk.TTkCore.log import TTkLog 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 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 :type name: str, optional
:param parent: the parent widget, defaults to None :param parent: the parent widget, defaults to None
:type parent: :class:`TTkWidget`, optional :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.tree import demoTree
from showcase.fancytable import demoFancyTable from showcase.fancytable import demoFancyTable
from showcase.fancytree import demoFancyTree 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."] 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(): def getWord():
@ -149,6 +150,7 @@ def demoShowcase(root=None, border=True):
listMenu.addItem(f"Widgets") listMenu.addItem(f"Widgets")
tabWidgets = ttk.TTkTabWidget(parent=mainFrame, border=False, visible=False) tabWidgets = ttk.TTkTabWidget(parent=mainFrame, border=False, visible=False)
tabWidgets.addTab(demoFormWidgets(), " Form Test ") tabWidgets.addTab(demoFormWidgets(), " Form Test ")
tabWidgets.addTab(demoTextEdit(), " Text Edit ")
tabWidgets.addTab(demoList(), " List Test ") tabWidgets.addTab(demoList(), " List Test ")
tabWidgets.addTab(demoTree(), " Tree Test") tabWidgets.addTab(demoTree(), " Tree Test")
tabWidgets.addTab(demoTab(), " Tab Test ") tabWidgets.addTab(demoTab(), " Tab Test ")
@ -156,6 +158,7 @@ def demoShowcase(root=None, border=True):
tabWidgets.addTab(demoFancyTree(), " Old Tree ") tabWidgets.addTab(demoFancyTree(), " Old Tree ")
tabWidgetsSources = [ tabWidgetsSources = [
'showcase/formwidgets.py', 'showcase/formwidgets.py',
'showcase/textedit.py',
'showcase/list.py', 'showcase/list.py',
'showcase/tree.py', 'showcase/tree.py',
'showcase/tab.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) - [ ] Support Hyperlink: (gnome-terminal)
https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
- [x] Process child events before parent - [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 ## Terminal Helper
- [ ] Events - [ ] Events

Loading…
Cancel
Save