You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

236 lines
9.2 KiB

# MIT License
#
# Copyright (c) 2023 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 TermTk as ttk
from .superobj.superwidget import SuperWidget
from .superobj.superlayout import SuperLayout
class _SignalSlotItem(ttk.TTkTreeWidgetItem):
__slots__ = ('_sender', '_signal', '_receiver', '_slot', '_designer', '_signalData', '_slotData', '_avoidRecursion')
def __init__(self, designer, *args, **kwargs):
self._designer = designer
self._sender = ttk.TTkComboBox(height=1, list=['<sender>' ], index=0)
self._signal = ttk.TTkComboBox(height=1, list=['<signal>' ], index=0)
self._receiver = ttk.TTkComboBox(height=1, list=['<receiver>'], index=0)
self._slot = ttk.TTkComboBox(height=1, list=['<slot>' ], 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._designer.widgetNameChanged.connect( self._widgetNameChanged)
self._designer.weModified.connect(self.updateWidgets)
self.updateWidgets()
super().__init__([self._sender,self._signal,self._receiver,self._slot], *args, **kwargs)
def isValid(self):
curSender = str(self._sender.currentText())
curReceiver = str(self._receiver.currentText())
curSignal = str(self._signal.currentText())
curSlot = str(self._slot.currentText())
if curSender=='<sender>' or curReceiver == '<receiver>':
return False
ret = False
for ccName in self._signalData:
if curSignal in self._signalData[ccName]:
ret = True
break
for ccName in self._slotData:
if curSlot in self._slotData[ccName]:
ret &= True
break
return ret
def importConnection(self, connection):
self._sender.setCurrentText(connection['sender'])
self._receiver.setCurrentText(connection['receiver'])
self._signal.setCurrentText(connection['signal'])
self._slot.setCurrentText(connection['slot'])
def dumpDict(self):
curSender = str(self._sender.currentText())
curReceiver = str(self._receiver.currentText())
curSignal = str(self._signal.currentText())
curSlot = str(self._slot.currentText())
return {
'sender': curSender,
'receiver': curReceiver,
'signal': curSignal,
'slot': curSlot }
@ttk.pyTTkSlot()
def updateWidgets(self):
self._widgetNameChanged()
@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}")
@ttk.pyTTkSlot(str, str)
def _widgetNameChanged(self, oldName='', newName=''):
names = [w.name() for w in self._designer.getWidgets()]
def _setCB(cb:ttk.TTkComboBox, base):
text = cb.currentText()
text = text if text!=oldName else newName
cb.clear()
cb.addItems([base]+names)
cb.setCurrentText(text)
_setCB(self._sender, '<sender>')
_setCB(self._receiver, '<receiver>')
@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 str(curSlot) in self._slotData[c]: continue
filter = self._slotData[c][curSlot]['type']
break
signals = ['<signal>']
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 str(curSignal) in self._signalData[c]: continue
filter = self._signalData[c][curSignal]['type']
break
slots = ['<slot>']
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._designer.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', '_designer')
def __init__(self, designer, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setLayout(ttk.TTkGridLayout())
self._items = []
self._designer = designer
self._detail = ttk.TTkTree()
self._detail.setHeaderLabels(["Sender","Signal","Receiver","Slot"])
self.layout().addWidget(addb := ttk.TTkButton(border=False, text="ADD"),1,1)
self.layout().addWidget(delb := ttk.TTkButton(border=False, text="DEL"),1,2)
self.layout().addWidget(ttk.TTkLabel(text=" Signal/Slot Editor "),1,3)
self.layout().addWidget(self._detail,2,1,1,3)
addb.clicked.connect(self._addStuff)
delb.clicked.connect(self._delStuff)
def _addStuff(self):
item = _SignalSlotItem(self._designer)
self._items.append(item)
self._detail.addTopLevelItem(item)
def _delStuff(self):
if not (items := self._detail.selectedItems()):
return
for item in items:
if (index := self._detail.indexOfTopLevelItem(item)) is not None:
self._detail.takeTopLevelItem(index)
def importConnections(self, connections):
self._items = []
self._detail.clear()
for c in connections:
item = _SignalSlotItem(self._designer)
item.importConnection(c)
self._items.append(item)
self._detail.addTopLevelItem(item)
def dumpDict(self):
ret = []
for i in self._items:
if i.isValid():
ret.append(i.dumpDict())
return ret