Browse Source

refactor: Improve typing (#535)

pull/540/head
Pier CeccoPierangioliEugenio 4 months ago committed by GitHub
parent
commit
2885f21bc4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 8
      demo/showcase/formwidgets01.py
  2. 8
      demo/showcase/formwidgets02.py
  3. 26
      libs/pyTermTk/TermTk/TTkCore/constant.py
  4. 8
      libs/pyTermTk/TermTk/TTkCore/helper.py
  5. 10
      libs/pyTermTk/TermTk/TTkWidgets/checkbox.py
  6. 104
      libs/pyTermTk/TermTk/TTkWidgets/combobox.py
  7. 2
      libs/pyTermTk/TermTk/TTkWidgets/datetime_time.py
  8. 10
      libs/pyTermTk/TermTk/TTkWidgets/frame.py
  9. 177
      libs/pyTermTk/TermTk/TTkWidgets/lineedit.py
  10. 8
      tests/t.ui/test.ui.009.widgets.form.py
  11. 11
      tools/check.import.sh

8
demo/showcase/formwidgets01.py

@ -62,14 +62,14 @@ def demoFormWidgets(root=None):
win_form1_grid_layout.addWidget(ttk.TTkLineEdit(text='Line Edit Test 3 oަ -'),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 Input Number'),row,0) row += 1; win_form1_grid_layout.addWidget(ttk.TTkLabel(text='Line Edit Input Number'),row,0)
win_form1_grid_layout.addWidget(ttk.TTkLineEdit(text='123456', inputType=ttk.TTkK.Input_Number),row,2) win_form1_grid_layout.addWidget(ttk.TTkLineEdit(text='123456', inputType=ttk.TTkK.InputType.Input_Number),row,2)
row += 1; win_form1_grid_layout.addWidget(ttk.TTkLabel(text='Line Edit Input Wrong Number'),row,0) row += 1; win_form1_grid_layout.addWidget(ttk.TTkLabel(text='Line Edit Input Wrong Number'),row,0)
win_form1_grid_layout.addWidget(ttk.TTkLineEdit(text='No num Text', inputType=ttk.TTkK.Input_Number),row,2) win_form1_grid_layout.addWidget(ttk.TTkLineEdit(text='No num Text', inputType=ttk.TTkK.InputType.Input_Number),row,2)
row += 1; win_form1_grid_layout.addWidget(ttk.TTkLabel(text='Line Edit Input Password'),row,0) row += 1; win_form1_grid_layout.addWidget(ttk.TTkLabel(text='Line Edit Input Password'),row,0)
win_form1_grid_layout.addWidget(ttk.TTkLineEdit(text='Password', inputType=ttk.TTkK.Input_Password),row,2) win_form1_grid_layout.addWidget(ttk.TTkLineEdit(text='Password', inputType=ttk.TTkK.InputType.Input_Password),row,2)
row += 1; win_form1_grid_layout.addWidget(ttk.TTkLabel(text='Line Edit Number Password'),row,0) row += 1; win_form1_grid_layout.addWidget(ttk.TTkLabel(text='Line Edit Number Password'),row,0)
win_form1_grid_layout.addWidget(ttk.TTkLineEdit(text='Password', inputType=ttk.TTkK.Input_Password+ttk.TTkK.Input_Number),row,2) win_form1_grid_layout.addWidget(ttk.TTkLineEdit(text='Password', inputType=ttk.TTkK.InputType.Input_Password|ttk.TTkK.InputType.Input_Number),row,2)
row += 1; win_form1_grid_layout.addWidget(ttk.TTkLabel(text='Spinbox (default [0,99])'),row,0) row += 1; win_form1_grid_layout.addWidget(ttk.TTkLabel(text='Spinbox (default [0,99])'),row,0)
win_form1_grid_layout.addWidget(ttk.TTkSpinBox(),row,2) win_form1_grid_layout.addWidget(ttk.TTkSpinBox(),row,2)

8
demo/showcase/formwidgets02.py

@ -76,17 +76,17 @@ def demoFormWidgets(root=None):
win_form1_grid_layout.addWidget(_en_dis_cb := ttk.TTkCheckbox(text=" en/dis", checked=True),row,3); _en_dis_cb.clicked.connect(_wid.setEnabled) win_form1_grid_layout.addWidget(_en_dis_cb := ttk.TTkCheckbox(text=" en/dis", checked=True),row,3); _en_dis_cb.clicked.connect(_wid.setEnabled)
row += 1; win_form1_grid_layout.addWidget(ttk.TTkLabel(text='Line Edit Input Number'),row,0) row += 1; win_form1_grid_layout.addWidget(ttk.TTkLabel(text='Line Edit Input Number'),row,0)
win_form1_grid_layout.addWidget(_wid := ttk.TTkLineEdit(text='123456', inputType=ttk.TTkK.Input_Number),row,2) win_form1_grid_layout.addWidget(_wid := ttk.TTkLineEdit(text='123456', inputType=ttk.TTkK.InputType.Input_Number),row,2)
win_form1_grid_layout.addWidget(_en_dis_cb := ttk.TTkCheckbox(text=" en/dis", checked=True),row,3); _en_dis_cb.clicked.connect(_wid.setEnabled) win_form1_grid_layout.addWidget(_en_dis_cb := ttk.TTkCheckbox(text=" en/dis", checked=True),row,3); _en_dis_cb.clicked.connect(_wid.setEnabled)
row += 1; win_form1_grid_layout.addWidget(ttk.TTkLabel(text='Line Edit Input Wrong Number'),row,0) row += 1; win_form1_grid_layout.addWidget(ttk.TTkLabel(text='Line Edit Input Wrong Number'),row,0)
win_form1_grid_layout.addWidget(_wid := ttk.TTkLineEdit(text='No num Text', inputType=ttk.TTkK.Input_Number),row,2) win_form1_grid_layout.addWidget(_wid := ttk.TTkLineEdit(text='No num Text', inputType=ttk.TTkK.InputType.Input_Number),row,2)
win_form1_grid_layout.addWidget(_en_dis_cb := ttk.TTkCheckbox(text=" en/dis", checked=True),row,3); _en_dis_cb.clicked.connect(_wid.setEnabled) win_form1_grid_layout.addWidget(_en_dis_cb := ttk.TTkCheckbox(text=" en/dis", checked=True),row,3); _en_dis_cb.clicked.connect(_wid.setEnabled)
row += 1; win_form1_grid_layout.addWidget(ttk.TTkLabel(text='Line Edit Input Password'),row,0) row += 1; win_form1_grid_layout.addWidget(ttk.TTkLabel(text='Line Edit Input Password'),row,0)
win_form1_grid_layout.addWidget(_wid := ttk.TTkLineEdit(text='Password', inputType=ttk.TTkK.Input_Password),row,2) win_form1_grid_layout.addWidget(_wid := ttk.TTkLineEdit(text='Password', inputType=ttk.TTkK.InputType.Input_Password),row,2)
win_form1_grid_layout.addWidget(_en_dis_cb := ttk.TTkCheckbox(text=" en/dis", checked=True),row,3); _en_dis_cb.clicked.connect(_wid.setEnabled) win_form1_grid_layout.addWidget(_en_dis_cb := ttk.TTkCheckbox(text=" en/dis", checked=True),row,3); _en_dis_cb.clicked.connect(_wid.setEnabled)
row += 1; win_form1_grid_layout.addWidget(ttk.TTkLabel(text='Line Edit Number Password'),row,0) row += 1; win_form1_grid_layout.addWidget(ttk.TTkLabel(text='Line Edit Number Password'),row,0)
win_form1_grid_layout.addWidget(_wid := ttk.TTkLineEdit(text='Password', inputType=ttk.TTkK.Input_Password+ttk.TTkK.Input_Number),row,2) win_form1_grid_layout.addWidget(_wid := ttk.TTkLineEdit(text='Password', inputType=ttk.TTkK.InputType.Input_Password|ttk.TTkK.InputType.Input_Number),row,2)
win_form1_grid_layout.addWidget(_en_dis_cb := ttk.TTkCheckbox(text=" en/dis", checked=True),row,3); _en_dis_cb.clicked.connect(_wid.setEnabled) win_form1_grid_layout.addWidget(_en_dis_cb := ttk.TTkCheckbox(text=" en/dis", checked=True),row,3); _en_dis_cb.clicked.connect(_wid.setEnabled)
row += 1; win_form1_grid_layout.addWidget(ttk.TTkLabel(text='Spinbox (default [0,99])'),row,0) row += 1; win_form1_grid_layout.addWidget(ttk.TTkLabel(text='Spinbox (default [0,99])'),row,0)

26
libs/pyTermTk/TermTk/TTkCore/constant.py

@ -194,7 +194,7 @@ class TTkConstant:
Background=0x02 Background=0x02
'''The color type returned is Background''' '''The color type returned is Background'''
class CheckState(int): class CheckState(IntEnum):
''' This class type is used to describe the check status. ''' This class type is used to describe the check status.
.. autosummary:: .. autosummary::
@ -213,7 +213,7 @@ class TTkConstant:
PartiallyChecked = CheckState.PartiallyChecked PartiallyChecked = CheckState.PartiallyChecked
Checked = CheckState.Checked Checked = CheckState.Checked
class InsertPolicy(int): class InsertPolicy(IntEnum):
'''Specifies what the :py:class:`TTkComboBox` should do when a new string is entered by the user. '''Specifies what the :py:class:`TTkComboBox` should do when a new string is entered by the user.
.. autosummary:: .. autosummary::
@ -423,10 +423,26 @@ class TTkConstant:
Cursor_Blinking_Bar = 0x0006 Cursor_Blinking_Bar = 0x0006
Cursor_Steady_Bar = 0x0007 Cursor_Steady_Bar = 0x0007
class InputType(Flag):
'''
This enum type describes the input validation types for text input widgets.
.. autosummary::
Input_Text
Input_Number
Input_Password
'''
Input_Text = 0x01
'''Accept any text input (default)'''
Input_Number = 0x02
'''Accept only numeric input (integers and decimals)'''
Input_Password = 0x04
'''Password input type (deprecated - use :py:class:`TTkLineEdit.EchoMode.Password` instead)'''
# Input types # Input types
Input_Text = 0x01 Input_Text = InputType.Input_Text
Input_Number = 0x02 Input_Number = InputType.Input_Number
Input_Password = 0x04 Input_Password = InputType.Input_Password
# Alignment # Alignment
class Alignment(IntEnum): class Alignment(IntEnum):

8
libs/pyTermTk/TermTk/TTkCore/helper.py

@ -127,7 +127,7 @@ class TTkHelper:
return TTkGlbl.term_w, TTkGlbl.term_h return TTkGlbl.term_w, TTkGlbl.term_h
@staticmethod @staticmethod
def rootOverlay(widget: TTkWidget) -> Optional[TTkWidget]: def rootOverlay(widget: Optional[TTkWidget]) -> Optional[TTkWidget]:
if not widget: if not widget:
return None return None
if not TTkHelper._overlay: if not TTkHelper._overlay:
@ -174,7 +174,7 @@ class TTkHelper:
return False return False
@staticmethod @staticmethod
def isOverlay(widget: TTkWidget) -> bool: def isOverlay(widget: Optional[TTkWidget]) -> bool:
return TTkHelper.rootOverlay(widget) is not None return TTkHelper.rootOverlay(widget) is not None
@staticmethod @staticmethod
@ -241,8 +241,8 @@ class TTkHelper:
bkFocus.setFocus() bkFocus.setFocus()
@staticmethod @staticmethod
def removeOverlayAndChild(widget: TTkWidget) -> None: def removeOverlayAndChild(widget: Optional[TTkWidget]) -> None:
if not TTkHelper._rootWidget: if not TTkHelper._rootWidget or not widget:
return return
if not TTkHelper.isOverlay(widget): if not TTkHelper.isOverlay(widget):
return return

10
libs/pyTermTk/TermTk/TTkWidgets/checkbox.py

@ -22,11 +22,13 @@
__all__ = ['TTkCheckbox'] __all__ = ['TTkCheckbox']
from typing import Optional
from TermTk.TTkCore.constant import TTkK from TermTk.TTkCore.constant import TTkK
from TermTk.TTkCore.cfg import TTkCfg from TermTk.TTkCore.cfg import TTkCfg
from TermTk.TTkCore.color import TTkColor from TermTk.TTkCore.color import TTkColor
from TermTk.TTkCore.canvas import TTkCanvas from TermTk.TTkCore.canvas import TTkCanvas
from TermTk.TTkCore.string import TTkString from TermTk.TTkCore.string import TTkString, TTkStringType
from TermTk.TTkCore.signal import pyTTkSignal, pyTTkSlot from TermTk.TTkCore.signal import pyTTkSignal, pyTTkSlot
from TermTk.TTkCore.TTkTerm.inputkey import TTkKeyEvent from TermTk.TTkCore.TTkTerm.inputkey import TTkKeyEvent
from TermTk.TTkCore.TTkTerm.inputmouse import TTkMouseEvent from TermTk.TTkCore.TTkTerm.inputmouse import TTkMouseEvent
@ -93,9 +95,9 @@ class TTkCheckbox(TTkWidget):
'clicked', 'stateChanged', 'toggled' 'clicked', 'stateChanged', 'toggled'
) )
def __init__(self, *, def __init__(self, *,
text:TTkString='', text:TTkStringType='',
checked:bool=False, checked:bool=False,
checkStatus:TTkK.CheckState = None, checkStatus:Optional[TTkK.CheckState] = None,
tristate:bool=False, tristate:bool=False,
**kwargs) -> None: **kwargs) -> None:
''' '''
@ -115,7 +117,7 @@ class TTkCheckbox(TTkWidget):
self.clicked = pyTTkSignal(bool) self.clicked = pyTTkSignal(bool)
self.toggled = pyTTkSignal(bool) self.toggled = pyTTkSignal(bool)
self._text = TTkString(text) self._text = text if isinstance(text,TTkString) else TTkString(text)
if checkStatus is not None : if checkStatus is not None :
self._checkStatus = checkStatus self._checkStatus = checkStatus

104
libs/pyTermTk/TermTk/TTkWidgets/combobox.py

@ -22,6 +22,8 @@
__all__ = ['TTkComboBox'] __all__ = ['TTkComboBox']
from typing import Dict,Any,List,Optional
from TermTk.TTkCore.cfg import TTkCfg from TermTk.TTkCore.cfg import TTkCfg
from TermTk.TTkCore.constant import TTkK from TermTk.TTkCore.constant import TTkK
from TermTk.TTkCore.log import TTkLog from TermTk.TTkCore.log import TTkLog
@ -41,7 +43,7 @@ from TermTk.TTkWidgets.resizableframe import TTkResizableFrame
class _TTkComboBoxPopup(TTkResizableFrame): class _TTkComboBoxPopup(TTkResizableFrame):
classStyle = TTkResizableFrame.classStyle classStyle:Dict[str,Dict[str,Any]] = TTkResizableFrame.classStyle
classStyle['default'] |= {'searchColor': TTkColor.fg("#FFFF00")} classStyle['default'] |= {'searchColor': TTkColor.fg("#FFFF00")}
__slots__ = ('_list', __slots__ = ('_list',
@ -66,13 +68,13 @@ class _TTkComboBoxPopup(TTkResizableFrame):
def paintEvent(self, canvas:TTkCanvas) -> None: def paintEvent(self, canvas:TTkCanvas) -> None:
super().paintEvent(canvas) super().paintEvent(canvas)
if text := self._list.search(): if str_text := self._list.search():
w = self.width()-6 w = self.width()-6
color = self.currentStyle()['searchColor'] color = self.currentStyle()['searchColor']
if len(text) > w: if len(str_text) > w:
text = TTkString("",TTkColor.BG_BLUE+TTkColor.FG_CYAN)+TTkString(text[-w+1:],color) text = TTkString("",TTkColor.BG_BLUE+TTkColor.FG_CYAN)+TTkString(str_text[-w+1:],color)
else: else:
text = TTkString(text,color) text = TTkString(str_text,color)
canvas.drawText(pos=(1,0), text=f"{text}") canvas.drawText(pos=(1,0), text=f"{text}")
canvas.drawTTkString(pos=(3,0), text=text) canvas.drawTTkString(pos=(3,0), text=text)
@ -103,24 +105,52 @@ class TTkComboBox(TTkContainer):
__slots__ = ('_list', '_id', '_lineEdit', '_editable', '_insertPolicy', '_textAlign', '_popupFrame', __slots__ = ('_list', '_id', '_lineEdit', '_editable', '_insertPolicy', '_textAlign', '_popupFrame',
#signals #signals
'currentIndexChanged', 'currentTextChanged', 'editTextChanged') 'currentIndexChanged', 'currentTextChanged', 'editTextChanged')
currentIndexChanged:pyTTkSignal
'''
This signal is emitted when the index in the combobox changes either through user interaction or programmatically.
:param index: the new current index
:type index: int
'''
currentTextChanged:pyTTkSignal
'''
This signal is emitted when the text of the current item changes. The text is passed as parameter.
:param text: the new current text
:type text: str
'''
editTextChanged:pyTTkSignal
'''
This signal is emitted when the text in the combobox's line edit widget is changed. This signal is only emitted when the combobox is editable.
:param text: the new text in the line edit
:type text: str
'''
_list:List[str]
_popupFrame:Optional[_TTkComboBoxPopup]
def __init__(self, *, def __init__(self, *,
list:list = None, list:Optional[List[str]] = None,
index:int = -1, index:int = -1,
insertPolicy:TTkK.InsertPolicy = TTkK.InsertAtBottom, insertPolicy:TTkK.InsertPolicy = TTkK.InsertAtBottom,
textAlign:TTkK.Alignment = TTkK.CENTER_ALIGN, textAlign:TTkK.Alignment = TTkK.CENTER_ALIGN,
editable:bool = False, editable:bool = False,
**kwargs) -> None: **kwargs) -> None:
''' '''
:param list: the list of the items selectable by this combobox, defaults to "[]" :param list: the list of the items selectable by this combobox, defaults to []
:type list: list(str), optional :type list: list[str], optional
:param index: the initial selected index, defaults to -1 (no selection)
:type index: int, optional
:param insertPolicy: the policy used to determine where user-inserted items should appear in the combobox, defaults to :py:class:`TTkConstant.InsertPolicy.InsertAtBottom` :param insertPolicy: the policy used to determine where user-inserted items should appear in the combobox, defaults to :py:class:`TTkK.InsertPolicy.InsertAtBottom`
:type insertPolicy: :py:class:`TTkConstant.InsertPolicy`, optional :type insertPolicy: :py:class:`TTkK.InsertPolicy`, optional
:param textAlign: This enum type is used to define the text alignment, defaults to :py:class:`TTkConstant.Alignment.CENTER_ALIGN` :param textAlign: the text alignment for the displayed text, defaults to :py:class:`TTkK.Alignment.CENTER_ALIGN`
:tye textAlign: :py:class:`TTkConstant.Alignment`, optional :type textAlign: :py:class:`TTkK.Alignment`, optional
:param editable: This property holds whether the combo box can be edited by the user, defaults to False :param editable: whether the combo box can be edited by the user, defaults to False
:type editable: bool, optional :type editable: bool, optional
''' '''
@ -142,7 +172,7 @@ class TTkComboBox(TTkContainer):
self.setMaximumHeight(1) self.setMaximumHeight(1)
def _lineEditChanged(self) -> None: def _lineEditChanged(self) -> None:
text = self._lineEdit.text() text = self._lineEdit.text().toAscii()
self._id=-1 self._id=-1
if text in self._list: if text in self._list:
self._id = self._list.index(text) self._id = self._list.index(text)
@ -169,9 +199,10 @@ class TTkComboBox(TTkContainer):
self.editTextChanged.emit(text) self.editTextChanged.emit(text)
def textAlign(self) -> TTkK.Alignment: def textAlign(self) -> TTkK.Alignment:
'''his property holds the displayed text alignment '''This property holds the displayed text alignment
:return: :py:class:`TTkConstant.Alignment` :return: the current text alignment
:rtype: :py:class:`TTkK.Alignment`
''' '''
return self._textAlign return self._textAlign
@ -179,32 +210,32 @@ class TTkComboBox(TTkContainer):
'''This property holds the displayed text alignment '''This property holds the displayed text alignment
:param align: :param align:
:type align: :py:class:`TTkConstant.Alignment` :type align: :py:class:`TTkK.Alignment`
''' '''
if self._textAlign != align: if self._textAlign != align:
self._textAlign = align self._textAlign = align
self.update() self.update()
def addItem(self, text:TTkString): def addItem(self, text:str):
''' '''
Adds an item to the combobox with the given text. Adds an item to the combobox with the given text.
The item is appended to the list of existing items. The item is appended to the list of existing items.
:param text: :param text: the text of the item to add
:type text: :py:class:`TTkString` :type text: str
''' '''
self._list.append(text) self._list.append(text)
self.update() self.update()
def addItems(self, items:list[TTkString]) -> None: def addItems(self, items:list[str]) -> None:
''' '''
Adds a list of items to the combobox with the given text. Adds a list of items to the combobox with the given text.
The items are appended to the list of existing items. The items are appended to the list of existing items.
:param items: :param items:
:type items: list[:py:class:`TTkString`] :type items: list[str]
''' '''
for item in items: for item in items:
self.addItem(item) self.addItem(item)
@ -217,11 +248,12 @@ class TTkComboBox(TTkContainer):
self._id = -1 self._id = -1
self.update() self.update()
def lineEdit(self) -> TTkLineEdit: def lineEdit(self) -> Optional[TTkLineEdit]:
''' '''
Returns the :py:class:`TTkLineEdit` widget used if the editable flag is enabled Returns the :py:class:`TTkLineEdit` widget used if the editable flag is enabled
:return: :py:class:`TTkLineEdit` :return: the line edit if available, None otherwise
:rtype: :py:class:`TTkLineEdit` | None
''' '''
return self._lineEdit if self._editable else None return self._lineEdit if self._editable else None
@ -247,23 +279,25 @@ class TTkComboBox(TTkContainer):
else: else:
canvas.drawText(pos=(w-2,0), text="^]", color=borderColor) canvas.drawText(pos=(w-2,0), text="^]", color=borderColor)
def currentText(self) -> TTkString: def currentText(self) -> str:
''' '''
Returns the selected text Returns the selected text
:return: :py:class:`TTkString` :return: the current text
:rtype: str
''' '''
if self._editable: if self._editable:
return self._lineEdit.text() return self._lineEdit.text().toAscii()
elif self._id >= 0: elif self._id >= 0:
return self._list[self._id] return self._list[self._id]
return "" return ""
def currentIndex(self) -> int: def currentIndex(self) -> int:
''' '''
Return the current seleccted index. Return the current selected index.
:return: int :return: the current index, -1 if no selection
:rtype: int
''' '''
return self._id return self._id
@ -287,12 +321,12 @@ class TTkComboBox(TTkContainer):
self.update() self.update()
@pyTTkSlot(str) @pyTTkSlot(str)
def setCurrentText(self, text:TTkString) -> None: def setCurrentText(self, text:str) -> None:
''' '''
Set the selected Text Set the selected Text
:param text: :param text:
:type text: :py:class:`TTkString` :type text: str
''' '''
if self._editable: if self._editable:
self.setEditText(text) self.setEditText(text)
@ -318,7 +352,8 @@ class TTkComboBox(TTkContainer):
''' '''
Retrieve the insert policy used when a new item is added if the combobox editable flag is true. Retrieve the insert policy used when a new item is added if the combobox editable flag is true.
:return: :py:class:`TTkK.InsertPolicy` :return: the current insert policy
:rtype: :py:class:`TTkK.InsertPolicy`
''' '''
return self._insertPolicy return self._insertPolicy
@ -335,7 +370,8 @@ class TTkComboBox(TTkContainer):
''' '''
This field holds the editable status of this widget. This field holds the editable status of this widget.
:return: bool :return: True if editable, False otherwise
:rtype: bool
''' '''
return self._editable return self._editable
@ -355,7 +391,7 @@ class TTkComboBox(TTkContainer):
self.setFocusPolicy(TTkK.ClickFocus | TTkK.TabFocus) self.setFocusPolicy(TTkK.ClickFocus | TTkK.TabFocus)
@pyTTkSlot(str) @pyTTkSlot(str)
def _callback(self, label:TTkString) -> None: def _callback(self, label:str) -> None:
if self._editable: if self._editable:
self._lineEdit.setText(label) self._lineEdit.setText(label)
self.setCurrentIndex(self._list.index(label)) self.setCurrentIndex(self._list.index(label))

2
libs/pyTermTk/TermTk/TTkWidgets/datetime_time.py

@ -262,7 +262,7 @@ class TTkTime(TTkWidget):
self._state.selected = _FieldSelected.NONE self._state.selected = _FieldSelected.NONE
return True return True
else: else:
if '0' <= evt.key <= '9': if isinstance(evt.key,str) and '0' <= evt.key <= '9':
value = int(evt.key) value = int(evt.key)
secondDigit = self._state.secondDigit secondDigit = self._state.secondDigit
self._state.secondDigit = not secondDigit self._state.secondDigit = not secondDigit

10
libs/pyTermTk/TermTk/TTkWidgets/frame.py

@ -22,8 +22,10 @@
__all__ = ['TTkFrame'] __all__ = ['TTkFrame']
from typing import Dict,Any
from TermTk.TTkCore.constant import TTkK from TermTk.TTkCore.constant import TTkK
from TermTk.TTkCore.string import TTkString from TermTk.TTkCore.string import TTkString, TTkStringType
from TermTk.TTkCore.color import TTkColor from TermTk.TTkCore.color import TTkColor
from TermTk.TTkWidgets.container import TTkContainer from TermTk.TTkWidgets.container import TTkContainer
@ -45,7 +47,7 @@ class TTkFrame(TTkContainer):
Demo2: `splitter.py <https://github.com/ceccopierangiolieugenio/pyTermTk/blob/main/demo/showcase/splitter.py>`_ Demo2: `splitter.py <https://github.com/ceccopierangiolieugenio/pyTermTk/blob/main/demo/showcase/splitter.py>`_
''' '''
classStyle = { classStyle:Dict[str,Dict[str,Any]] = {
'default': {'color': TTkColor.fg("#dddddd")+TTkColor.bg("#222222"), 'default': {'color': TTkColor.fg("#dddddd")+TTkColor.bg("#222222"),
'fillColor': TTkColor.RST, 'fillColor': TTkColor.RST,
'borderColor': TTkColor.RST}, 'borderColor': TTkColor.RST},
@ -58,7 +60,7 @@ class TTkFrame(TTkContainer):
'_border','_title', '_titleAlign', '_border','_title', '_titleAlign',
'_menubarTop', '_menubarTopPosition', '_menubarBottom', '_menubarBottomPosition') '_menubarTop', '_menubarTopPosition', '_menubarBottom', '_menubarBottomPosition')
def __init__(self, *, def __init__(self, *,
title:TTkString='', title:TTkStringType='',
border:bool=True, border:bool=True,
titleAlign:TTkK.Alignment=TTkK.CENTER_ALIGN, titleAlign:TTkK.Alignment=TTkK.CENTER_ALIGN,
**kwargs) -> None: **kwargs) -> None:
@ -72,7 +74,7 @@ class TTkFrame(TTkContainer):
''' '''
self._titleAlign = titleAlign self._titleAlign = titleAlign
self._title = TTkString(title) self._title = title if isinstance(title,TTkString) else TTkString(title)
self._border = border self._border = border
self._menubarBottomPosition = 0 self._menubarBottomPosition = 0
self._menubarTop = None self._menubarTop = None

177
libs/pyTermTk/TermTk/TTkWidgets/lineedit.py

@ -22,6 +22,10 @@
__all__ = ['TTkLineEdit'] __all__ = ['TTkLineEdit']
from enum import IntEnum
from typing import Optional
from TermTk.TTkCore.cfg import TTkCfg from TermTk.TTkCore.cfg import TTkCfg
from TermTk.TTkCore.log import TTkLog from TermTk.TTkCore.log import TTkLog
from TermTk.TTkCore.constant import TTkK from TermTk.TTkCore.constant import TTkK
@ -42,10 +46,53 @@ from TermTk.TTkWidgets.widget import TTkWidget
<--> Offset <--> Offset
''' '''
class TTkLineEdit(TTkWidget): class TTkLineEdit(TTkWidget):
'''TTkLineEdit''' ''' TTkLineEdit:
A single-line text editor widget for user input. (`demo <https://ceccopierangiolieugenio.github.io/pyTermTk-Docs/sandbox/sandbox.html?filePath=demo/showcase/lineedit.py>`__)
::
Standard: |Hello World________|
Password: |**********_________|
Number: |123.45_____________|
.. code:: python
import TermTk as ttk
root = ttk.TTk()
# Basic text input
edit = ttk.TTkLineEdit(parent=root, pos=(1,1), size=(20,1), text="Hello")
# Password input
password = ttk.TTkLineEdit(parent=root, pos=(1,2), size=(20,1),
echoMode=ttk.TTkLineEdit.EchoMode.Password)
# Numeric input
number = ttk.TTkLineEdit(parent=root, pos=(1,3), size=(20,1),
inputType=ttk.TTkK.InputType.Input_Number)
# Connect to signal
edit.textEdited.connect(lambda text: ttk.TTkLog.debug(f"Edited: {text}"))
root.mainloop()
:param text: Initial text content
:type text: :py:class:`TTkString` or str, optional
:param hint: Placeholder text shown when empty
:type hint: :py:class:`TTkString` or str, optional
:param inputType: Input validation type (TTkK.InputType.Input_Text or TTkK.InputType.Input_Number)
:type inputType: int, optional
:param echoMode: How text is displayed (Normal, NoEcho, Password, PasswordEchoOnEdit)
:type echoMode: :py:class:`EchoMode`, optional
'''
class EchoMode(IntEnum):
''' EchoMode:
class EchoMode(int): Defines how text input is displayed in the line edit.
'''EchoMode''' '''
Normal = 0x00 Normal = 0x00
'''Display characters as they are entered. This is the default.''' '''Display characters as they are entered. This is the default.'''
NoEcho = 0x01 NoEcho = 0x01
@ -73,10 +120,37 @@ class TTkLineEdit(TTkWidget):
'_hint', '_hint',
# Signals # Signals
'returnPressed', 'textChanged', 'textEdited' ) 'returnPressed', 'textChanged', 'textEdited' )
returnPressed:pyTTkSignal
'''
This signal is emitted when the Return or Enter key is pressed.
'''
textChanged:pyTTkSignal
'''
This signal is emitted whenever the text changes programmatically or through user input.
:param text: The new text content
:type text: str
'''
textEdited:pyTTkSignal
'''
This signal is emitted whenever the text is edited by the user (not programmatically).
:param text: The edited text content
:type text: str
'''
_text:TTkString
_hint:TTkString
_cursorPos:int
_inputType:TTkK.InputType
def __init__(self, *, def __init__(self, *,
text:TTkString='', text:TTkStringType='',
hint:TTkString='', hint:TTkStringType='',
inputType:int=TTkK.Input_Text, inputType:TTkK.InputType=TTkK.InputType.Input_Text,
echoMode:EchoMode=EchoMode.Normal, echoMode:EchoMode=EchoMode.Normal,
**kwargs) -> None: **kwargs) -> None:
# Signals # Signals
@ -88,8 +162,8 @@ class TTkLineEdit(TTkWidget):
self._selectionFrom = 0 self._selectionFrom = 0
self._selectionTo = 0 self._selectionTo = 0
self._replace=False self._replace=False
self._text = TTkString(text) self._text = text if isinstance(text,TTkString) else TTkString(text)
self._hint = TTkString(hint) self._hint = hint if isinstance(hint,TTkString) else TTkString(hint)
self._inputType = inputType self._inputType = inputType
self._echoMode = echoMode self._echoMode = echoMode
self._clipboard = TTkClipboard() self._clipboard = TTkClipboard()
@ -101,8 +175,14 @@ class TTkLineEdit(TTkWidget):
self.enableWidgetCursor() self.enableWidgetCursor()
@pyTTkSlot(TTkStringType) @pyTTkSlot(TTkStringType)
def setText(self, text:TTkStringType, cursorPos=0x1000): def setText(self, text:TTkStringType, cursorPos:int=0x1000) -> None:
'''setText''' ''' Set the text content of the line edit
:param text: The new text to display
:type text: :py:class:`TTkString` or str
:param cursorPos: Position to place the cursor (defaults to end of text)
:type cursorPos: int, optional
'''
if text != self._text: if text != self._text:
self.textChanged.emit(text) self.textChanged.emit(text)
self._text = TTkString(text) self._text = TTkString(text)
@ -110,35 +190,59 @@ class TTkLineEdit(TTkWidget):
self._cursorPos = max(0,min(cursorPos, len(text))) self._cursorPos = max(0,min(cursorPos, len(text)))
self._pushCursor() self._pushCursor()
def text(self): def text(self) -> TTkString:
'''text''' ''' Get the current text content
:return: The current text
:rtype: :py:class:`TTkString`
'''
return self._text return self._text
def inputType(self): def inputType(self) -> TTkK.InputType:
'''inputType''' ''' Get the current input validation type
:return: The input type (:py:class:`TTkK.InputType.Input_Text` or :py:class:`TTkK.InputType.Input_Number`)
:rtype: :py:class:`TTkK.InputType`
'''
return self._inputType return self._inputType
def setInputType(self, inputType): def setInputType(self, inputType:TTkK.InputType) -> None:
'''inputType''' ''' Set the input validation type
if bool(inputType & TTkK.Input_Text) and bool(inputType & TTkK.Input_Number):
When set to Input_Number, only numeric values (including decimals) are accepted.
:param inputType: The input type to validate against
:type inputType: :py:class:`TTkK.InputType`
'''
if bool(inputType & TTkK.InputType.Input_Text) and bool(inputType & TTkK.InputType.Input_Number):
return return
# Kept here for retrocompatibility # Kept here for retrocompatibility
if inputType & TTkK.Input_Password: if inputType & TTkK.InputType.Input_Password:
TTkLog.warn("TTkK.Input_Password is deprecated, use the EchoMode instead") TTkLog.warn("TTkK.InputType.Input_Password is deprecated, use the EchoMode instead")
self._echoMode = TTkLineEdit.EchoMode.Password self._echoMode = TTkLineEdit.EchoMode.Password
inputType &= ~TTkK.Input_Password inputType &= ~TTkK.InputType.Input_Password
if inputType & ~(TTkK.Input_Text|TTkK.Input_Number): if inputType & ~(TTkK.InputType.Input_Text|TTkK.InputType.Input_Number):
return return
self._inputType = inputType & (TTkK.Input_Text|TTkK.Input_Number) if inputType else TTkK.Input_Text self._inputType = inputType & (TTkK.InputType.Input_Text|TTkK.InputType.Input_Number) if inputType else TTkK.InputType.Input_Text
if ( self._inputType == TTkK.Input_Number and if ( self._inputType == TTkK.InputType.Input_Number and
not self._isFloat(self._text)): not self._isFloat(self._text)):
self._text = TTkString('0') self._text = TTkString('0')
self.update() self.update()
def echoMode(self) -> EchoMode: def echoMode(self) -> EchoMode:
''' Get the current echo mode
:return: The current echo mode
:rtype: :py:class:`EchoMode`
'''
return self._echoMode return self._echoMode
def setEchoMode(self, echoMode:EchoMode): def setEchoMode(self, echoMode:EchoMode):
''' Set how text is displayed
:param echoMode: The echo mode (Normal, NoEcho, Password, PasswordEchoOnEdit)
:type echoMode: :py:class:`EchoMode`
'''
self._echoMode = echoMode self._echoMode = echoMode
self.update() self.update()
@ -229,12 +333,16 @@ class TTkLineEdit(TTkWidget):
@pyTTkSlot() @pyTTkSlot()
def copy(self): def copy(self):
''' Copy the selected text to the clipboard
'''
if self._selectionFrom >= self._selectionTo: return if self._selectionFrom >= self._selectionTo: return
txt = self._text.substring(fr=self._selectionFrom,to=self._selectionTo) txt = self._text.substring(fr=self._selectionFrom,to=self._selectionTo)
self._clipboard.setText(txt) self._clipboard.setText(txt)
@pyTTkSlot() @pyTTkSlot()
def cut(self): def cut(self):
''' Cut the selected text to the clipboard
'''
self.copy() self.copy()
self._text = self._text.substring(to=self._selectionFrom) + self._text.substring(fr=self._selectionTo) self._text = self._text.substring(to=self._selectionFrom) + self._text.substring(fr=self._selectionTo)
self._cursorPos = self._selectionFrom self._cursorPos = self._selectionFrom
@ -242,11 +350,20 @@ class TTkLineEdit(TTkWidget):
@pyTTkSlot() @pyTTkSlot()
def paste(self): def paste(self):
''' Paste text from the clipboard at the cursor position
'''
txt = self._clipboard.text() txt = self._clipboard.text()
self.pasteEvent(txt) self.pasteEvent(txt)
def pasteEvent(self, txt:str): def pasteEvent(self, txt:str):
txt = TTkString().join(txt.split('\n')) ''' Handle paste event with custom text
:param txt: The text to paste
:type txt: str
:return: True if the event was handled
:rtype: bool
'''
ttk_txt = TTkString(''.join(txt.split('\n')))
text = self._text text = self._text
@ -261,11 +378,11 @@ class TTkLineEdit(TTkWidget):
else: else:
post = text.substring(fr=self._cursorPos) post = text.substring(fr=self._cursorPos)
text = pre + txt + post text = pre + ttk_txt + post
if ( self._inputType & TTkK.Input_Number and if ( self._inputType & TTkK.InputType.Input_Number and
not self._isFloat(text) ): not self._isFloat(text) ):
return True return True
self.setText(text, self._cursorPos+txt.termWidth()) self.setText(text, self._cursorPos+ttk_txt.termWidth())
self._pushCursor() self._pushCursor()
self.textEdited.emit(self._text) self.textEdited.emit(self._text)
@ -323,7 +440,7 @@ class TTkLineEdit(TTkWidget):
text = self._text.substring(to=prev) + self._text.substring(fr=self._cursorPos) text = self._text.substring(to=prev) + self._text.substring(fr=self._cursorPos)
cursorPos = prev cursorPos = prev
if ( self._inputType & TTkK.Input_Number and if ( self._inputType & TTkK.InputType.Input_Number and
not self._isFloat(self._text) ): not self._isFloat(self._text) ):
self.setText('0', 1) self.setText('0', 1)
else: else:
@ -333,7 +450,7 @@ class TTkLineEdit(TTkWidget):
if evt.key == TTkK.Key_Enter: if evt.key == TTkK.Key_Enter:
self.returnPressed.emit() self.returnPressed.emit()
else: elif isinstance(evt.key,str):
text = self._text text = self._text
if self._selectionFrom < self._selectionTo: if self._selectionFrom < self._selectionTo:
@ -348,7 +465,7 @@ class TTkLineEdit(TTkWidget):
post = text.substring(fr=self._cursorPos) post = text.substring(fr=self._cursorPos)
text = pre + evt.key + post text = pre + evt.key + post
if ( self._inputType & TTkK.Input_Number and if ( self._inputType & TTkK.InputType.Input_Number and
( evt.key in (' ') or not self._isFloat(text) )): ( evt.key in (' ') or not self._isFloat(text) )):
return True return True
self.setText(text, self._cursorPos+1) self.setText(text, self._cursorPos+1)

8
tests/t.ui/test.ui.009.widgets.form.py

@ -67,14 +67,14 @@ row += 1; win_form1_grid_layout.addWidget(ttk.TTkLabel(text='Line Edit Test 5'),
win_form1_grid_layout.addWidget(ttk.TTkLineEdit(text='Line Edit Test 5'),row,2) win_form1_grid_layout.addWidget(ttk.TTkLineEdit(text='Line Edit Test 5'),row,2)
row += 1; win_form1_grid_layout.addWidget(ttk.TTkLabel(text='Line Edit Input Number'),row,0) row += 1; win_form1_grid_layout.addWidget(ttk.TTkLabel(text='Line Edit Input Number'),row,0)
win_form1_grid_layout.addWidget(ttk.TTkLineEdit(text='123456', inputType=ttk.TTkK.Input_Number),row,2) win_form1_grid_layout.addWidget(ttk.TTkLineEdit(text='123456', inputType=ttk.TTkK.InputType.Input_Number),row,2)
row += 1; win_form1_grid_layout.addWidget(ttk.TTkLabel(text='Line Edit Input Wrong Number'),row,0) row += 1; win_form1_grid_layout.addWidget(ttk.TTkLabel(text='Line Edit Input Wrong Number'),row,0)
win_form1_grid_layout.addWidget(ttk.TTkLineEdit(text='No num Text', inputType=ttk.TTkK.Input_Number),row,2) win_form1_grid_layout.addWidget(ttk.TTkLineEdit(text='No num Text', inputType=ttk.TTkK.InputType.Input_Number),row,2)
row += 1; win_form1_grid_layout.addWidget(ttk.TTkLabel(text='Line Edit Input Password'),row,0) row += 1; win_form1_grid_layout.addWidget(ttk.TTkLabel(text='Line Edit Input Password'),row,0)
win_form1_grid_layout.addWidget(ttk.TTkLineEdit(text='Password', inputType=ttk.TTkK.Input_Password),row,2) win_form1_grid_layout.addWidget(ttk.TTkLineEdit(text='Password', inputType=ttk.TTkK.InputType.Input_Password),row,2)
row += 1; win_form1_grid_layout.addWidget(ttk.TTkLabel(text='Line Edit Number Password'),row,0) row += 1; win_form1_grid_layout.addWidget(ttk.TTkLabel(text='Line Edit Number Password'),row,0)
win_form1_grid_layout.addWidget(ttk.TTkLineEdit(text='Password', inputType=ttk.TTkK.Input_Password+ttk.TTkK.Input_Number),row,2) win_form1_grid_layout.addWidget(ttk.TTkLineEdit(text='Password', inputType=ttk.TTkK.InputType.Input_Password|ttk.TTkK.InputType.Input_Number),row,2)
row += 1; win_form1_grid_layout.addWidget(ttk.TTkLabel(text='Checkbox'),row,0) row += 1; win_form1_grid_layout.addWidget(ttk.TTkLabel(text='Checkbox'),row,0)
win_form1_grid_layout.addWidget(ttk.TTkCheckbox(),row,2) win_form1_grid_layout.addWidget(ttk.TTkCheckbox(),row,2)

11
tools/check.import.sh

@ -4,6 +4,7 @@ __check(){
grep -r -e "^import" -e "^from" libs/pyTermTk/TermTk | grep -r -e "^import" -e "^from" libs/pyTermTk/TermTk |
grep -v -e "from TermTk" -e "import TermTk" | grep -v -e "from TermTk" -e "import TermTk" |
grep -v "from typing import" | grep -v "from typing import" |
grep -v "from enum import" |
grep -v "__init__.py:from \.[^ ]* *import" | grep -v "__init__.py:from \.[^ ]* *import" |
grep -v -e "import re" -e "import os" -e "import datetime" | grep -v -e "import re" -e "import os" -e "import datetime" |
grep -v \ grep -v \
@ -46,9 +47,7 @@ __check(){
-e "propertyanimation.py:import time, math" \ -e "propertyanimation.py:import time, math" \
-e "savetools.py:import importlib.util" \ -e "savetools.py:import importlib.util" \
-e "savetools.py:import json" \ -e "savetools.py:import json" \
-e "TTkCore/constant.py:from enum import IntEnum" |
grep -v \ grep -v \
-e "TTkTerm/term_base.py:from enum import Flag" \
-e "TTkTerm/input_mono.py:from time import time" \ -e "TTkTerm/input_mono.py:from time import time" \
-e "TTkTerm/input_mono.py:import platform" \ -e "TTkTerm/input_mono.py:import platform" \
-e "TTkTerm/input_mono.py:from ..drivers import TTkInputDriver" \ -e "TTkTerm/input_mono.py:from ..drivers import TTkInputDriver" \
@ -58,7 +57,6 @@ __check(){
-e "TTkTerm/input.py:from .input_thread import *" | -e "TTkTerm/input.py:from .input_thread import *" |
grep -v \ grep -v \
-e "TTkGui/__init__.py:import importlib.util" \ -e "TTkGui/__init__.py:import importlib.util" \
-e "TTkGui/textcursor.py:from enum import IntEnum" \
-e "TTkGui/textdocument.py:from threading import Lock" \ -e "TTkGui/textdocument.py:from threading import Lock" \
-e "TTkGui/textdocument_highlight_pygments.py:from pygments" | -e "TTkGui/textdocument_highlight_pygments.py:from pygments" |
grep -v \ grep -v \
@ -116,19 +114,12 @@ __check(){
-e "TTkTerminal/__init__.py:import platform" | -e "TTkTerminal/__init__.py:import platform" |
grep -v \ grep -v \
-e "TTkWidgets/widget.py:from __future__ import annotations" \ -e "TTkWidgets/widget.py:from __future__ import annotations" \
-e "TTkWidgets/tabwidget.py:from enum import Enum" \
-e "TTkWidgets/apptemplate.py:from enum import IntEnum" \
-e "TTkModelView/__init__.py:from importlib.util import find_spec" \ -e "TTkModelView/__init__.py:from importlib.util import find_spec" \
-e "TTkModelView/table_edit_proxy.py:from enum import Enum, auto" \
-e "TTkModelView/tablemodelcsv.py:import csv" \ -e "TTkModelView/tablemodelcsv.py:import csv" \
-e "TTkModelView/tablemodelsqlite3.py:import sqlite3" \ -e "TTkModelView/tablemodelsqlite3.py:import sqlite3" \
-e "TTkModelView/tablemodelsqlite3.py:import threading" | -e "TTkModelView/tablemodelsqlite3.py:import threading" |
grep -v \ grep -v \
-e "TTkWidgets/datetime_date.py:from enum import IntEnum,auto" \
-e "TTkWidgets/datetime_date.py:import calendar" \ -e "TTkWidgets/datetime_date.py:import calendar" \
-e "TTkWidgets/datetime_time.py:from enum import IntEnum,auto" \
-e "TTkWidgets/datetime_datetime.py:from enum import IntEnum,auto" \
-e "TTkWidgets/datetime_date_form.py:from enum import IntEnum,auto" \
-e "TTkWidgets/datetime_date_form.py:import calendar" -e "TTkWidgets/datetime_date_form.py:import calendar"
} ; } ;

Loading…
Cancel
Save