From c1352f337af0bbc46fc032889bc02e44b22932a9 Mon Sep 17 00:00:00 2001 From: Eugenio Parodi Date: Sun, 19 Mar 2023 18:51:01 +0000 Subject: [PATCH] Basic handling of the signals slots editor --- TermTk/TTkUiTools/properties/button.py | 4 +- TermTk/TTkUiTools/properties/checkbox.py | 10 +- .../TTkUiTools/properties/resizableframe.py | 2 +- TermTk/TTkUiTools/properties/widget.py | 15 +- TermTk/TTkWidgets/combobox.py | 8 +- TermTk/TTkWidgets/widget.py | 2 + ttkDesigner/app/signalsloteditor.py | 136 +++++++++++++++--- 7 files changed, 147 insertions(+), 30 deletions(-) diff --git a/TermTk/TTkUiTools/properties/button.py b/TermTk/TTkUiTools/properties/button.py index 56857c3d..9f96448b 100644 --- a/TermTk/TTkUiTools/properties/button.py +++ b/TermTk/TTkUiTools/properties/button.py @@ -42,8 +42,8 @@ TTkButtonProperties = { 'set': {'cb':TTkButton.setChecked, 'type':bool } }, }, 'signals' : { - 'clicked' : {'name': 'clicked', 'type' : None}, - 'toggled' : {'name': 'toggled', 'type' : bool}, + 'clicked()' : {'name': 'clicked', 'type' : None}, + 'toggled(bool)' : {'name': 'toggled', 'type' : bool}, }, 'slots' : {} } diff --git a/TermTk/TTkUiTools/properties/checkbox.py b/TermTk/TTkUiTools/properties/checkbox.py index a8985362..720c5b85 100644 --- a/TermTk/TTkUiTools/properties/checkbox.py +++ b/TermTk/TTkUiTools/properties/checkbox.py @@ -57,12 +57,12 @@ TTkCheckboxProperties = { }, }, 'signals' : { - 'clicked' : {'name' : 'clicked', 'type' : bool}, - 'stateChanged' : {'name' : 'stateChanged', 'type' : TTkK.CheckState}, + 'clicked(bool)' : {'name' : 'clicked', 'type' : bool}, + 'stateChanged(CheckState)' : {'name' : 'stateChanged', 'type' : TTkK.CheckState}, }, 'slots' : { - 'setChecked' : {'cb' : TTkCheckbox.setChecked , 'type' : bool}, - 'setCheckState' : {'cb' : TTkCheckbox.setCheckState , 'type' : TTkK.CheckState}, - 'setText' : {'cb' : TTkCheckbox.setText , 'type' : str} + 'setChecked(bool)' : {'cb' : TTkCheckbox.setChecked , 'type' : bool}, + 'setCheckState(CheckState)' : {'cb' : TTkCheckbox.setCheckState , 'type' : TTkK.CheckState}, + 'setText(str)' : {'cb' : TTkCheckbox.setText , 'type' : str} } } diff --git a/TermTk/TTkUiTools/properties/resizableframe.py b/TermTk/TTkUiTools/properties/resizableframe.py index b0bbf405..1158424b 100644 --- a/TermTk/TTkUiTools/properties/resizableframe.py +++ b/TermTk/TTkUiTools/properties/resizableframe.py @@ -22,4 +22,4 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -TTkResizableFrameProperties = {} +TTkResizableFrameProperties = {'properties':{}, 'signals':{}, 'slots':{}} diff --git a/TermTk/TTkUiTools/properties/widget.py b/TermTk/TTkUiTools/properties/widget.py index e46fc016..b80dea32 100644 --- a/TermTk/TTkUiTools/properties/widget.py +++ b/TermTk/TTkUiTools/properties/widget.py @@ -104,5 +104,18 @@ TTkWidgetProperties = { 'init': {'name':'toolTip', 'type':TTkString } , 'get': { 'cb':TTkWidget.toolTip, 'type':TTkString } , 'set': { 'cb':TTkWidget.setToolTip, 'type':TTkString } }, - },'signals' : {},'slots' : {} + },'signals' : { + 'focusChanged(bool)' : {'name' : 'focusChanged', 'type':bool}, + 'sizeChanged(int,int)' : {'name' : 'sizeChanged', 'type':(int, int)} + },'slots' : { + 'show()' : {'cb': TTkWidget.show, 'type':None}, + 'hide()' : {'cb': TTkWidget.hide, 'type':None}, + 'close()' : {'cb': TTkWidget.close, 'type':None}, + 'setFocus()' : {'cb': TTkWidget.setFocus, 'type':None}, + 'setVisible(bool)' : {'cb': TTkWidget.setVisible, 'type':bool}, + 'setEnabled(bool)': {'cb': TTkWidget.setEnabled, 'type':bool}, + 'setDisabled(bool)': {'cb': TTkWidget.setDisabled,'type':bool}, + 'raiseWidget()' : {'cb': TTkWidget.raiseWidget,'type':None}, + 'lowerWidget()' : {'cb': TTkWidget.lowerWidget,'type':None}, + } } diff --git a/TermTk/TTkWidgets/combobox.py b/TermTk/TTkWidgets/combobox.py index 8a26feb2..cbb9fc4a 100644 --- a/TermTk/TTkWidgets/combobox.py +++ b/TermTk/TTkWidgets/combobox.py @@ -190,6 +190,7 @@ class TTkComboBox(TTkWidget): def setCurrentIndex(self, index): '''setCurrentIndex''' if index > len(self._list)-1: return + if self._id == index: return self._id = index if self._editable: self._lineEdit.setText(self._list[self._id]) @@ -204,8 +205,11 @@ class TTkComboBox(TTkWidget): if self._editable: self.setEditText(text) else: - if id := self._list.index(text): - self.setCurrentIndex(id) + if text not in self._list: + id = 0 + else: + id = self._list.index(text) + self.setCurrentIndex(id) @pyTTkSlot(str) def setEditText(self, text): diff --git a/TermTk/TTkWidgets/widget.py b/TermTk/TTkWidgets/widget.py index 543fd3c0..e3ca8518 100644 --- a/TermTk/TTkWidgets/widget.py +++ b/TermTk/TTkWidgets/widget.py @@ -584,12 +584,14 @@ class TTkWidget(TMouseEvents,TKeyEvents, TDragEvents): self._canvas.hide() self.update(repaint=False, updateParent=True) + @pyTTkSlot() def raiseWidget(self): if self._parent is not None and \ self._parent.rootLayout() is not None: self._parent.raiseWidget() self._parent.rootLayout().raiseWidget(self) + @pyTTkSlot() def lowerWidget(self): if self._parent is not None and \ self._parent.rootLayout() is not None: diff --git a/ttkDesigner/app/signalsloteditor.py b/ttkDesigner/app/signalsloteditor.py index b630d4c1..7e2b5b6c 100644 --- a/ttkDesigner/app/signalsloteditor.py +++ b/ttkDesigner/app/signalsloteditor.py @@ -26,19 +26,131 @@ from .superobj.superwidget import SuperWidget from .superobj.superlayout import SuperLayout class _SignalSlotItem(ttk.TTkTreeWidgetItem): - __slots__ = ('_sender', '_signal', '_receiver', '_slot') - def __init__(self, *args, **kwargs): + __slots__ = ('_sender', '_signal', '_receiver', '_slot', '_windowEditor', '_signalData', '_slotData', '_avoidRecursion') + def __init__(self, windowEditor, *args, **kwargs): + self._windowEditor = windowEditor self._sender = ttk.TTkComboBox(height=1, list=['' ], index=0) self._signal = ttk.TTkComboBox(height=1, list=['' ], index=0) self._receiver = ttk.TTkComboBox(height=1, list=[''], index=0) self._slot = ttk.TTkComboBox(height=1, list=['' ], index=0) + self._signalData = {} + self._slotData = {} + self._avoidRecursion = False + self._sender.currentTextChanged.connect( self._senderChanged) + self._signal.currentTextChanged.connect( self._signalChanged) + self._receiver.currentTextChanged.connect(self._receiverChanged) + self._slot.currentTextChanged.connect( self._slotChanged) + self.updateWidgets() super().__init__([self._sender,self._signal,self._receiver,self._slot], *args, **kwargs) - def updateWidgets(self, widgets): - names = [w.name() for w in widgets] + def updateWidgets(self): + names = [w.name() for w in self._getWidgets()] self._sender.addItems(names) self._receiver.addItems(names) + @staticmethod + def typeToString(t): + if type(t) in (list,tuple): + return ",".join([_SignalSlotItem.typeToString(x) for x in t]) + return {bool : 'bool', + int : 'int', + float : 'float', + complex : 'complex', + str : 'str', + None : ''}.get(t,f"UNDEFINED {t}") + + def _getWidgets(self): + widgets = [] + def _getItems(layoutItem): + if layoutItem.layoutItemType == ttk.TTkK.WidgetItem: + superThing = layoutItem.widget() + if issubclass(type(superThing), SuperWidget): + widgets.append(superThing._wid) + for c in superThing.layout().children(): + _getItems(c) + _getItems(self._windowEditor.getTTk().widgetItem()) + return widgets + + @ttk.pyTTkSlot(str) + def _senderChanged(self, text): + self._signalData, _ = self.getSignalSlot(text) + self._refreshSignals() + + def _refreshSignals(self): + curSignal = str(self._signal.currentText()) + curSlot = str(self._slot.currentText()) + filter = None + for c in self._slotData: + if not curSlot in self._slotData[c]: continue + filter = self._slotData[c][curSlot]['type'] + break + + signals = [''] + for ccName in self._signalData: + signals.append(ttk.TTkString(f"{ccName}:",ttk.TTkColor.fg("#FFFF88")+ttk.TTkColor.UNDERLINE)) + for s in self._signalData[ccName]: + if filter in (None,self._signalData[ccName][s]['type']): + signals.append(s) + self._signal.clear() + self._signal.addItems(signals) + self._signal.setCurrentText(curSignal) + + @ttk.pyTTkSlot(str) + def _signalChanged(self, text): + if self._avoidRecursion: return + self._avoidRecursion = True + self._refreshSlots() + self._avoidRecursion = False + + + @ttk.pyTTkSlot(str) + def _receiverChanged(self, text): + _, self._slotData = self.getSignalSlot(text) + self._refreshSlots() + + def _refreshSlots(self): + curSignal = self._signal.currentText() + curSlot = self._slot.currentText() + filter = 'ALL' + for c in self._signalData: + if not curSignal in self._signalData[c]: continue + filter = self._signalData[c][curSignal]['type'] + break + + slots = [''] + for ccName in self._slotData: + slots.append(ttk.TTkString(f"{ccName}:",ttk.TTkColor.fg("#FFFF88")+ttk.TTkColor.UNDERLINE)) + for s in self._slotData[ccName]: + if filter in ('ALL',self._slotData[ccName][s]['type']) or not self._slotData[ccName][s]['type']: + slots.append(s) + + self._slot.clear() + self._slot.addItems(slots) + self._slot.setCurrentText(curSlot) + + @ttk.pyTTkSlot(str) + def _slotChanged(self, text): + if self._avoidRecursion: return + self._avoidRecursion = True + self._refreshSignals() + self._avoidRecursion = False + + def getSignalSlot(self, name): + if not (widget := {w.name():w for w in self._getWidgets()}.get(name,None)): + return {},{} + ttk.TTkLog.debug(f"Selected {widget=}") + slots = {} + signals = {} + for cc in reversed(type(widget).__mro__): + if issubclass(cc, ttk.TTkWidget) or issubclass(cc, ttk.TTkLayout): + ccName = cc.__name__ + if ccName in ttk.TTkUiProperties: + if ttk.TTkUiProperties[ccName]['slots']: + slots[ccName] = ttk.TTkUiProperties[ccName]['slots'] + if ttk.TTkUiProperties[ccName]['signals']: + signals[ccName] = ttk.TTkUiProperties[ccName]['signals'] + return signals,slots + class SignalSlotEditor(ttk.TTkWidget): __slots__ = ('_items', '_windowEditor') def __init__(self, windowEditor, *args, **kwargs): @@ -57,21 +169,7 @@ class SignalSlotEditor(ttk.TTkWidget): addb.clicked.connect(self._addStuff) def _addStuff(self): - item = _SignalSlotItem() - item.updateWidgets(self._getWidgets()) + item = _SignalSlotItem(self._windowEditor) self._items.append(item) self._detail.addTopLevelItem(item) - def _getWidgets(self): - widgets = [] - def _getItems(layoutItem): - if layoutItem.layoutItemType == ttk.TTkK.WidgetItem: - superThing = layoutItem.widget() - if issubclass(type(superThing), SuperWidget): - widgets.append(superThing._wid) - - for c in superThing.layout().children(): - _getItems(c) - _getItems(self._windowEditor.getTTk().widgetItem()) - return widgets -