From bd6af80f7c05ee9f3b76e6bf38cefb51ae41bb5c Mon Sep 17 00:00:00 2001 From: Eugenio Parodi Date: Mon, 2 Jan 2023 22:25:22 +0100 Subject: [PATCH] Added tristate and properties to checkbox, --- TermTk/TTkCore/constant.py | 32 ++++---- TermTk/TTkTestWidgets/domtreeview.py | 6 ++ TermTk/TTkWidgets/checkbox.py | 111 +++++++++++++++++++++++---- demo/showcase/formwidgets.py | 6 +- 4 files changed, 120 insertions(+), 35 deletions(-) diff --git a/TermTk/TTkCore/constant.py b/TermTk/TTkCore/constant.py index f9a1c108..f2475652 100644 --- a/TermTk/TTkCore/constant.py +++ b/TermTk/TTkCore/constant.py @@ -34,7 +34,7 @@ class TTkConstant: DEP_24: int = 0x18 # Color Type - class ColorType: + class ColorType(int): Foreground = 0x01 Background = 0x02 Modifier = 0x03 @@ -78,7 +78,7 @@ class TTkConstant: HORIZONTAL = 0x01 VERTICAL = 0x02 - class ScrollBarPolicy: + class ScrollBarPolicy(int): ScrollBarAsNeeded = 0x00 ScrollBarAlwaysOff = 0x01 ScrollBarAlwaysOn = 0x02 @@ -87,7 +87,7 @@ class TTkConstant: ScrollBarAlwaysOff = ScrollBarPolicy.ScrollBarAlwaysOff ScrollBarAlwaysOn = ScrollBarPolicy.ScrollBarAlwaysOn - class CheckState: + class CheckState(int): ''' This class type is used to describe the check status. .. autosummary:: @@ -103,7 +103,7 @@ class TTkConstant: PartiallyChecked = CheckState.PartiallyChecked Checked = CheckState.Checked - class InsertPolicy: + class InsertPolicy(int): NoInsert = 0x00 # The string will not be inserted into the combobox. InsertAtTop = 0x01 # The string will be inserted as the first item in the combobox. # InsertAtCurrent = 0x02 # The current item will be replaced by the string. @@ -112,7 +112,7 @@ class TTkConstant: # InsertBeforeCurrent = 0x05 # The string is inserted before the current item in the combobox. # InsertAlphabetically = 0x06 # The string is inserted in the alphabetic order in the combobox. - class ChildIndicatorPolicy: + class ChildIndicatorPolicy(int): ShowIndicator = 0x00 #The controls for expanding and collapsing will be shown for this item even if there are no children. DontShowIndicator = 0x01 #The controls for expanding and collapsing will never be shown even if there are children. If the node is forced open the user will not be able to expand or collapse the item. DontShowIndicatorWhenChildless = 0x02 #The controls for expanding and collapsing will be shown if the item contains children. @@ -121,7 +121,7 @@ class TTkConstant: DontShowIndicator = ChildIndicatorPolicy.DontShowIndicator DontShowIndicatorWhenChildless = ChildIndicatorPolicy.DontShowIndicatorWhenChildless - class SortOrder: + class SortOrder(int): AscendingOrder = 0x00 DescendingOrder = 0x01 @@ -137,7 +137,7 @@ class TTkConstant: # InsertAlphabetically = InsertPolicy.InsertAlphabetically # Keys - class MouseKey(): + class MouseKey(int): '''Input Mouse Key Events reported by :class:`~TermTk.TTkCore.TTkTerm.inputmouse.TTkMouseEvent` -> :class:`~TermTk.TTkCore.TTkTerm.inputmouse.TTkMouseEvent.key` @@ -165,7 +165,7 @@ class TTkConstant: MiddleButton = MouseKey.MiddleButton Wheel = MouseKey.Wheel - class WrapMode(): + class WrapMode(int): '''Those constants describes how text is wrapped in a document.''' # NoWrap = 0x00 # '''Text is not wrapped at all.''' @@ -184,7 +184,7 @@ class TTkConstant: WrapAnywhere = WrapMode.WrapAnywhere WrapAtWordBoundaryOrAnywhere = WrapMode.WrapAtWordBoundaryOrAnywhere - class LineWrapMode(): + class LineWrapMode(int): NoWrap = 0x00 WidgetWidth = 0x01 FixedWidth = 0x03 @@ -195,7 +195,7 @@ class TTkConstant: # Events - class MouseEvent(): + class MouseEvent(int): '''Input Mouse Event Events reported by :class:`~TermTk.TTkCore.TTkTerm.inputmouse.TTkMouseEvent` -> :class:`~TermTk.TTkCore.TTkTerm.inputmouse.TTkMouseEvent.evt` @@ -231,7 +231,7 @@ class TTkConstant: Input_Password = 0x04 # Alignment - class Alignment: + class Alignment(int): ''' This class type is used to describe alignment. .. autosummary:: @@ -257,7 +257,7 @@ class TTkConstant: CENTER_ALIGN = Alignment.CENTER_ALIGN JUSTIFY = Alignment.JUSTIFY - class FileMode(): + class FileMode(int): AnyFile = 0 #The name of a file, whether it exists or not. # ExistingFile = 1 #The name of a single existing file. Directory = 2 #The name of a directory. Both files and directories are displayed. However, the native Windows file dialog does not support displaying files in the directory chooser. @@ -269,7 +269,7 @@ class TTkConstant: # ExistingFiles = FileMode.ExistingFiles # LayoutItem Types - class LayoutItemTypes: + class LayoutItemTypes(int): '''Types used internally in :mod:`~TermTk.TTkLayouts`''' LayoutItem = 0x01 '''Item Type Layout''' @@ -280,7 +280,7 @@ class TTkConstant: LayoutItem = LayoutItemTypes.LayoutItem WidgetItem = LayoutItemTypes.WidgetItem - class WindowFlag: + class WindowFlag(int): # FramelessWindowHint = 0x00000800 # ''' Produces a borderless window.''' # CustomizeWindowHint = 0x02000000 @@ -308,7 +308,7 @@ class TTkConstant: # WindowStaysOnBottomHint = 0x04000000 # ''' Informs the window system that the window should stay on bottom of all other windows.''' - class KeyType(): + class KeyType(int): '''Input Key Types Key type reported by :class:`~TermTk.TTkCore.TTkTerm.inputkey.TTkKeyEvent` -> :class:`~TermTk.TTkCore.TTkTerm.inputkey.TTkKeyEvent.key` @@ -322,7 +322,7 @@ class TTkConstant: SpecialKey = KeyType.SpecialKey - class KeyModifier(): + class KeyModifier(int): '''Input :class:`~TermTk.TTkCore.constant.TTkConstant.KeyType.SpecialKey` modifiers Modifier reported by :class:`~TermTk.TTkCore.TTkTerm.inputkey.TTkKeyEvent` -> :class:`~TermTk.TTkCore.TTkTerm.inputkey.TTkKeyEvent.mod` diff --git a/TermTk/TTkTestWidgets/domtreeview.py b/TermTk/TTkTestWidgets/domtreeview.py index 7688678a..49465f1f 100644 --- a/TermTk/TTkTestWidgets/domtreeview.py +++ b/TermTk/TTkTestWidgets/domtreeview.py @@ -207,6 +207,12 @@ class TTkDomTreeView(TTkWidget): fcb.stateChanged.connect(_boundFlags( prop['set']['cb'], prop['get']['cb'], domw, lambda v: v==TTkK.Checked, flags[fl])) + if prop['get']['type'] == 'singleflag' and 'set' in prop: + flags = prop['get']['flags'] + items = [(k,v) for k,v in flags.items()] + value = TTkComboBox(list=[n for n,_ in items], height=1) + value.setCurrentIndex([cs for _,cs in items].index(getval)) + value.currentTextChanged.connect(_bound(prop['set']['cb'],domw, lambda v:flags[v])) elif prop['get']['type'] == bool and 'set' in prop: # value = TTkComboBox(list=['True','False']) # value.setCurrentIndex(0 if getval else 1) diff --git a/TermTk/TTkWidgets/checkbox.py b/TermTk/TTkWidgets/checkbox.py index 21d2ab12..c2b73368 100644 --- a/TermTk/TTkWidgets/checkbox.py +++ b/TermTk/TTkWidgets/checkbox.py @@ -39,6 +39,11 @@ class TTkCheckbox(TTkWidget): [ ]CheckBox + **Partially Checked** + :: + + [/]CheckBox + :Demo: `formwidgets.py `_ :param str text: the text shown on the checkbox, defaults to "" @@ -57,6 +62,11 @@ class TTkCheckbox(TTkWidget): :param checked: True if checked otherwise False :type checked: bool + :param checkStatus: The state of the checkbox + :type checkStatus: :class:`~TermTk.TTkCore.constant.TTkConstant.CheckState` + :param tristate: | This property holds whether the checkbox is a tri-state checkbox + | The default is false, i.e., the checkbox has only two states. + :type tristate: bool .. py:method:: stateChanged(state) :signal: @@ -68,7 +78,7 @@ class TTkCheckbox(TTkWidget): ''' __slots__ = ( - '_checked', '_text', + '_checkStatus', '_text', '_tristate', # Signals 'clicked', 'stateChanged' ) @@ -76,23 +86,57 @@ class TTkCheckbox(TTkWidget): TTkWidget.__init__(self, *args, **kwargs) self._name = kwargs.get('name' , 'TTkCheckbox' ) # Define Signals - self.stateChanged = pyTTkSignal(int) + self.stateChanged = pyTTkSignal(TTkK.CheckState) self.clicked = pyTTkSignal(bool) - self._checked = kwargs.get('checked', False ) + if 'checkStatus' in kwargs: + self._checkStatus = kwargs.get('checkStatus', TTkK.Unchecked ) + else: + self._checkStatus = TTkK.Checked if kwargs.get('checked', False ) else TTkK.Unchecked + self._tristate = kwargs.get('tristate', False) self._text = TTkString(kwargs.get('text', '' )) self.setMinimumSize(3 + len(self._text), 1) self.setMaximumHeight(1) self.setFocusPolicy(TTkK.ClickFocus + TTkK.TabFocus) + def isTristate(self): + ''' This property holds whether the checkbox is a tri-state checkbox + + :return: bool + ''' + return self._tristate + + def setTristate(self, tristate): + ''' Enable/Disable the tristate property + + :param tristate: + :type tristate: bool + ''' + if tristate == self._tristate: return + self._tristate = tristate + self.update() + + def isChecked(self): + ''' This property holds whether the button is checked + + :return: bool - True if :class:`~TermTk.TTkCore.constant.TTkConstant.CheckState.Checked` or :class:`~TermTk.TTkCore.constant.TTkConstant.CheckState.PartiallyChecked` + ''' + return self._checkStatus != TTkK.Unchecked + + def setChecked(self, state): + ''' Set the check status + + :param state: + :type tate: bool + ''' + self.setCheckState(TTkK.Checked if state else TTkK.Unchecked) + + def checkState(self): ''' Retrieve the state of the checkbox :return: :class:`~TermTk.TTkCore.constant.TTkConstant.CheckState` : the checkbox status ''' - if self._checked: - return TTkK.Checked - else: - return TTkK.Unchecked + return self._checkStatus def setCheckState(self, state): ''' Sets the checkbox's check state. @@ -100,7 +144,9 @@ class TTkCheckbox(TTkWidget): :param state: state of the checkbox :type state: :class:`~TermTk.TTkCore.constant.TTkConstant.CheckState` ''' - self._checked = state == TTkK.Checked + if self._checkStatus == state: return + if state==TTkK.PartiallyChecked and not self._tristate: return + self._checkStatus = state self.update() def paintEvent(self): @@ -114,15 +160,22 @@ class TTkCheckbox(TTkWidget): xColor = TTkCfg.theme.checkboxContentColor self._canvas.drawText(pos=(0,0), color=borderColor ,text="[ ]") self._canvas.drawText(pos=(3,0), color=textColor ,text=self._text) - if self._checked: - self._canvas.drawText(pos=(1,0), color=xColor ,text="X") - else: - self._canvas.drawText(pos=(1,0), color=xColor ,text=" ") + text = { + TTkK.Checked : "X", + TTkK.Unchecked : " ", + TTkK.PartiallyChecked: "/"}.get(self._checkStatus, " ") + self._canvas.drawText(pos=(1,0), color=xColor ,text=text) def _pressEvent(self): - self._checked = not self._checked - self.clicked.emit(self._checked) - self.stateChanged.emit(self.checkState()) + self._checkStatus = { + TTkK.Unchecked: TTkK.PartiallyChecked, + TTkK.PartiallyChecked: TTkK.Checked, + TTkK.Checked: TTkK.Unchecked, + }.get(self._checkStatus,TTkK.Unchecked) + if not self._tristate and self._checkStatus == TTkK.PartiallyChecked: + self._checkStatus = TTkK.Checked + self.clicked.emit(self._checkStatus!=TTkK.Unchecked) + self.stateChanged.emit(self._checkStatus) self.update() return True @@ -136,3 +189,31 @@ class TTkCheckbox(TTkWidget): self._pressEvent() return True return False + + _ttkProperties = { + 'tristate' : { + 'init': {'name':'tristate', 'type':bool } , + 'get': {'cb':isTristate, 'type':bool } , + 'set': {'cb':setTristate, 'type':bool } }, + 'checked' : { + 'init': {'name':'checked', 'type':bool } , + 'get': {'cb':isChecked, 'type':bool } , + 'set': {'cb':setChecked, 'type':bool } }, + 'Check State' : { + 'init': { 'name':'checked', 'type':'singleflag', + 'flags': { + 'Checked' : TTkK.Checked , + 'Unchecked' : TTkK.Unchecked , + 'Partially Checked': TTkK.PartiallyChecked } }, + 'get' : { 'cb':checkState, 'type':'singleflag', + 'flags': { + 'Checked' : TTkK.Checked , + 'Unchecked' : TTkK.Unchecked , + 'Partially Checked': TTkK.PartiallyChecked } }, + 'set' : { 'cb':setCheckState, 'type':'singleflag', + 'flags': { + 'Checked' : TTkK.Checked , + 'Unchecked' : TTkK.Unchecked , + 'Partially Checked': TTkK.PartiallyChecked } }, + }, + } \ No newline at end of file diff --git a/demo/showcase/formwidgets.py b/demo/showcase/formwidgets.py index 9603ec03..0cf9d3f9 100755 --- a/demo/showcase/formwidgets.py +++ b/demo/showcase/formwidgets.py @@ -60,10 +60,6 @@ def demoFormWidgets(root=None): 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 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) - 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) win_form1_grid_layout.addWidget(ttk.TTkLineEdit(text='123456', inputType=ttk.TTkK.Input_Number),row,2) @@ -84,6 +80,8 @@ def demoFormWidgets(root=None): win_form1_grid_layout.addWidget(ttk.TTkCheckbox(text='CheckBox 1'),row,2) row += 1; win_form1_grid_layout.addWidget(ttk.TTkLabel(text='Checkbox Checked'),row,0) win_form1_grid_layout.addWidget(ttk.TTkCheckbox(text='CheckBox 2', checked=True),row,2) + row += 1; win_form1_grid_layout.addWidget(ttk.TTkLabel(text='Checkbox Tristate'),row,0) + win_form1_grid_layout.addWidget(ttk.TTkCheckbox(text='CheckBox 3', checkStatus=ttk.TTkK.PartiallyChecked, tristate=True),row,2) row += 1; win_form1_grid_layout.addWidget(ttk.TTkLabel(text='Radio Button (Default)'),row,0) win_form1_grid_layout.addWidget(ttk.TTkRadioButton(text='RadioButton 1'),row,2)