Browse Source

Finalised signal/slot editor in ttkDesigner

pull/99/head
Eugenio Parodi 3 years ago
parent
commit
9a5984c1ba
  1. 6
      TermTk/TTkUiTools/properties/checkbox.py
  2. 18
      TermTk/TTkUiTools/properties/widget.py
  3. 39
      TermTk/TTkUiTools/uiloader.py
  4. 5
      TermTk/TTkWidgets/widget.py
  5. 0
      tests/timeit/11.signals.slots.01.py
  6. 81
      tests/timeit/11.signals.slots.02.py
  7. 103
      tests/timeit/11.signals.slots.03.py
  8. 17
      ttkDesigner/app/designer.py
  9. 35
      ttkDesigner/app/signalsloteditor.py
  10. 12
      ttkDesigner/app/windoweditor.py

6
TermTk/TTkUiTools/properties/checkbox.py

@ -61,8 +61,8 @@ TTkCheckboxProperties = {
'stateChanged(CheckState)' : {'name' : 'stateChanged', 'type' : TTkK.CheckState}, 'stateChanged(CheckState)' : {'name' : 'stateChanged', 'type' : TTkK.CheckState},
}, },
'slots' : { 'slots' : {
'setChecked(bool)' : {'cb' : TTkCheckbox.setChecked , 'type' : bool}, 'setChecked(bool)' : {'name' : 'setChecked' , 'type' : bool},
'setCheckState(CheckState)' : {'cb' : TTkCheckbox.setCheckState , 'type' : TTkK.CheckState}, 'setCheckState(CheckState)' : {'name' : 'setCheckState' , 'type' : TTkK.CheckState},
'setText(str)' : {'cb' : TTkCheckbox.setText , 'type' : str} 'setText(str)' : {'name' : 'setText' , 'type' : str}
} }
} }

18
TermTk/TTkUiTools/properties/widget.py

@ -108,14 +108,14 @@ TTkWidgetProperties = {
'focusChanged(bool)' : {'name' : 'focusChanged', 'type':bool}, 'focusChanged(bool)' : {'name' : 'focusChanged', 'type':bool},
'sizeChanged(int,int)' : {'name' : 'sizeChanged', 'type':(int, int)} 'sizeChanged(int,int)' : {'name' : 'sizeChanged', 'type':(int, int)}
},'slots' : { },'slots' : {
'show()' : {'cb': TTkWidget.show, 'type':None}, 'show()' : {'name': 'show', 'type':None},
'hide()' : {'cb': TTkWidget.hide, 'type':None}, 'hide()' : {'name': 'hide', 'type':None},
'close()' : {'cb': TTkWidget.close, 'type':None}, 'close()' : {'name': 'close', 'type':None},
'setFocus()' : {'cb': TTkWidget.setFocus, 'type':None}, 'setFocus()' : {'name': 'setFocus', 'type':None},
'setVisible(bool)' : {'cb': TTkWidget.setVisible, 'type':bool}, 'setVisible(bool)' : {'name': 'setVisible', 'type':bool},
'setEnabled(bool)': {'cb': TTkWidget.setEnabled, 'type':bool}, 'setEnabled(bool)': {'name': 'setEnabled', 'type':bool},
'setDisabled(bool)': {'cb': TTkWidget.setDisabled,'type':bool}, 'setDisabled(bool)': {'name': 'setDisabled', 'type':bool},
'raiseWidget()' : {'cb': TTkWidget.raiseWidget,'type':None}, 'raiseWidget()' : {'name': 'raiseWidget', 'type':None},
'lowerWidget()' : {'cb': TTkWidget.lowerWidget,'type':None}, 'lowerWidget()' : {'name': 'lowerWidget', 'type':None},
} }
} }

39
TermTk/TTkUiTools/uiloader.py

@ -33,6 +33,10 @@ from TermTk.TTkUiTools.uiproperties import TTkUiProperties
class TTkUiLoader(): class TTkUiLoader():
@staticmethod @staticmethod
def loadJson(text): def loadJson(text):
return TTkUiLoader.loadDict(json.loads(text))
@staticmethod
def loadDict(ui):
def _getWidget(widProp): def _getWidget(widProp):
properties = {} properties = {}
ttkClass = globals()[widProp['class']] ttkClass = globals()[widProp['class']]
@ -137,10 +141,39 @@ class TTkUiLoader():
layout.addWidget(w) layout.addWidget(w)
return layout return layout
widgetProperty = json.loads(text) TTkLog.debug(ui)
TTkLog.debug(widgetProperty)
widget = _getWidget(ui['tui'])
def _getSignal(sender, name):
for cc in reversed(type(sender).__mro__):
if cc.__name__ in TTkUiProperties:
if not name in TTkUiProperties[cc.__name__]['signals']:
continue
signame = TTkUiProperties[cc.__name__]['signals'][name]['name']
return getattr(sender,signame)
return None
def _getSlot(receiver, name):
for cc in reversed(type(receiver).__mro__):
if cc.__name__ in TTkUiProperties:
if not name in TTkUiProperties[cc.__name__]['slots']:
continue
slotname = TTkUiProperties[cc.__name__]['slots'][name]['name']
return getattr(receiver,slotname)
return None
for conn in ui['connections']:
sender = widget.getWidgetByName(conn['sender'])
receiver = widget.getWidgetByName(conn['receiver'])
signal = conn['signal']
slot = conn['slot']
if None in (sender,receiver): continue
_getSignal(sender,signal).connect(_getSlot(receiver,slot))
return widget
return _getWidget(widgetProperty)

5
TermTk/TTkWidgets/widget.py

@ -712,3 +712,8 @@ class TTkWidget(TMouseEvents,TKeyEvents, TDragEvents):
def setToolTip(self, toolTip): def setToolTip(self, toolTip):
self._toolTip = toolTip self._toolTip = toolTip
def getWidgetByName(self, name):
for w in self.rootLayout().iterWidgets(onlyVisible=False, recurse=True):
if w._name == name:
return w
return None

0
tests/timeit/11.signals.slots.py → tests/timeit/11.signals.slots.01.py

81
tests/timeit/11.signals.slots.02.py

@ -0,0 +1,81 @@
#!/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 sys, os
import timeit
import random
import unicodedata
sys.path.append(os.path.join(sys.path[0],'../..'))
sys.path.append(os.path.join(sys.path[0],'.'))
import TermTk as ttk
def f1(a): return a
def f2(a,b): return a+b
def f3(a,b,c): return a+b+c
def f4(a,b,c,d): return a+b+c+d
def c1(*args,**argv): return f1(*args[:1])
def c2(*args,**argv): return f2(*args[:2])
def c3(*args,**argv): return f3(*args[:3])
def c4(*args,**argv): return f4(*args[:4])
def d1(*args,**argv): return f1(*args)
def d2(*args,**argv): return f2(*args)
def d3(*args,**argv): return f3(*args)
def d4(*args,**argv): return f4(*args)
def test1(v): return d1(1)
def test2(v): return d2(1,2)
def test3(v): return d3(1,2,3)
def test4(v): return d4(1,2,3,4)
def test5(v): return c1(1,2,3,4)
def test6(v): return c2(1,2,3,4)
def test7(v): return c3(1,2,3,4)
def test8(v): return c4(1,2,3,4)
loop = 200000
a=1
result = timeit.timeit('test1(a)', globals=globals(), number=loop)
print(f"1a s {result / loop:.10f} - {result / loop} {test1(a)}")
result = timeit.timeit('test2(a)', globals=globals(), number=loop)
print(f"2a {result / loop:.10f} - {result / loop} {test2(a)}")
result = timeit.timeit('test3(a)', globals=globals(), number=loop)
print(f"3a s {result / loop:.10f} - {result / loop} {test3(a)}")
result = timeit.timeit('test4(a)', globals=globals(), number=loop)
print(f"4a {result / loop:.10f} - {result / loop} {test4(a)}")
result = timeit.timeit('test5(a)', globals=globals(), number=loop)
print(f"5a s {result / loop:.10f} - {result / loop} {test5(a)}")
result = timeit.timeit('test6(a)', globals=globals(), number=loop)
print(f"6a {result / loop:.10f} - {result / loop} {test6(a)}")
result = timeit.timeit('test7(a)', globals=globals(), number=loop)
print(f"7a s {result / loop:.10f} - {result / loop} {test7(a)}")
result = timeit.timeit('test8(a)', globals=globals(), number=loop)
print(f"8a {result / loop:.10f} - {result / loop} {test8(a)}")

103
tests/timeit/11.signals.slots.03.py

@ -0,0 +1,103 @@
#!/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 sys, os
import timeit
import random
import unicodedata
sys.path.append(os.path.join(sys.path[0],'../..'))
sys.path.append(os.path.join(sys.path[0],'.'))
import TermTk as ttk
def f1(a): return a
def f2(a,b): return a+b
def f3(a,b,c): return a+b+c
def f4(a,b,c,d): return a+b+c+d
ccb = {f1:1, f2:2, f3:3, f4:4}
def c1(*args,**argv): return f1(*args[:1])
def c2(*args,**argv): return f2(*args[:2])
def c3(*args,**argv): return f3(*args[:3])
def c4(*args,**argv): return f4(*args[:4])
def d1(*args,**argv): return f1(*args)
def d2(*args,**argv): return f2(*args)
def d3(*args,**argv): return f3(*args)
def d4(*args,**argv): return f4(*args)
def e1(*args,**argv):
f1(1)
f2(1,2)
f3(1,2,3)
f4(1,2,3,4)
def e2(*args,**argv):
for cb in ccb:
nargs = ccb[cb]
cb(*args[:nargs])
def e3(*args,**argv):
for cb in ccb.copy():
nargs = ccb[cb]
cb(*args[:nargs])
def e4(*args,**argv):
for cb,n in ccb.copy().items():
cb(*args[:n])
def test1(v): return d1(1)
def test2(v): return d2(1,2)
def test3(v): return d3(1,2,3)
def test4(v): return d4(1,2,3,4)
def test5(v): return e1(1,2,3,4)
def test6(v): return e2(1,2,3,4)
def test7(v): return e3(1,2,3,4)
def test8(v): return e4(1,2,3,4)
loop = 200000
a=1
result = timeit.timeit('test1(a)', globals=globals(), number=loop)
print(f"1a s {result / loop:.10f} - {result / loop} {test1(a)}")
result = timeit.timeit('test2(a)', globals=globals(), number=loop)
print(f"2a {result / loop:.10f} - {result / loop} {test2(a)}")
result = timeit.timeit('test3(a)', globals=globals(), number=loop)
print(f"3a s {result / loop:.10f} - {result / loop} {test3(a)}")
result = timeit.timeit('test4(a)', globals=globals(), number=loop)
print(f"4a {result / loop:.10f} - {result / loop} {test4(a)}")
result = timeit.timeit('test5(a)', globals=globals(), number=loop)
print(f"5a s {result / loop:.10f} - {result / loop} {test5(a)}")
result = timeit.timeit('test6(a)', globals=globals(), number=loop)
print(f"6a {result / loop:.10f} - {result / loop} {test6(a)}")
result = timeit.timeit('test7(a)', globals=globals(), number=loop)
print(f"7a s {result / loop:.10f} - {result / loop} {test7(a)}")
result = timeit.timeit('test8(a)', globals=globals(), number=loop)
print(f"8a {result / loop:.10f} - {result / loop} {test8(a)}")

17
ttkDesigner/app/designer.py

@ -20,6 +20,8 @@
# 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 json
from TermTk import TTk, TTkK, TTkLog, TTkCfg, TTkColor, TTkTheme, TTkTerm, TTkHelper from TermTk import TTk, TTkK, TTkLog, TTkCfg, TTkColor, TTkTheme, TTkTerm, TTkHelper
from TermTk import TTkString from TermTk import TTkString
from TermTk import TTkColorGradient from TermTk import TTkColorGradient
@ -72,7 +74,7 @@ from .signalsloteditor import SignalSlotEditor
# #
class TTkDesigner(TTkGridLayout): class TTkDesigner(TTkGridLayout):
__slots__ = ('_pippo', '_main', '_windowEditor', '_toolBar') __slots__ = ('_pippo', '_main', '_windowEditor', '_toolBar', '_sigslotEditor')
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
@ -88,10 +90,9 @@ class TTkDesigner(TTkGridLayout):
# sa.viewport().layout().addWidget(WindowEditor()) # sa.viewport().layout().addWidget(WindowEditor())
self._main = TTkVBoxLayout() self._main = TTkVBoxLayout()
self._toolBar = TTkHBoxLayout() self._toolBar = TTkHBoxLayout()
self._windowEditor = WindowEditor() self._windowEditor = WindowEditor()
self._sigslotEditor = SignalSlotEditor(self._windowEditor.viewport())
self._main.addItem(self._toolBar) self._main.addItem(self._toolBar)
self._main.addWidget(self._windowEditor) self._main.addWidget(self._windowEditor)
@ -100,14 +101,14 @@ class TTkDesigner(TTkGridLayout):
centralSplit.addWidget(self._main) centralSplit.addWidget(self._main)
centralSplit.addWidget(bottonTabWidget := TTkTabWidget(border=False)) centralSplit.addWidget(bottonTabWidget := TTkTabWidget(border=False))
# centralSplit.addWidget(TTkLogViewer()) # centralSplit.addWidget(TTkLogViewer())
bottonTabWidget.addTab(sigslotEditor := SignalSlotEditor(self._windowEditor.viewport()),'Signal/Slot Editor') bottonTabWidget.addTab(self._sigslotEditor,'Signal/Slot Editor')
bottonTabWidget.addTab(TTkLogViewer(),'Logs') bottonTabWidget.addTab(TTkLogViewer(),'Logs')
mainSplit.addWidget(rightSplit := TTkSplitter(orientation=TTkK.VERTICAL)) mainSplit.addWidget(rightSplit := TTkSplitter(orientation=TTkK.VERTICAL))
rightSplit.addItem(treeInspector := TreeInspector(self._windowEditor.viewport())) rightSplit.addItem(treeInspector := TreeInspector(self._windowEditor.viewport()))
rightSplit.addItem(propertyEditor := PropertyEditor()) rightSplit.addItem(propertyEditor := PropertyEditor())
# rightSplit.addItem(sigslotEditor := SignalSlotEditor(self._windowEditor.viewport())) # rightSplit.addItem(self._sigslotEditor)
treeInspector.thingSelected.connect(lambda _,s : s.pushSuperControlWidget()) treeInspector.thingSelected.connect(lambda _,s : s.pushSuperControlWidget())
treeInspector.thingSelected.connect(propertyEditor.setDetail) treeInspector.thingSelected.connect(propertyEditor.setDetail)
@ -160,9 +161,13 @@ class TTkDesigner(TTkGridLayout):
SuperWidget.toggleHighlightLayout.emit(state) SuperWidget.toggleHighlightLayout.emit(state)
def preview(self, btn=None): def preview(self, btn=None):
jj = self._windowEditor.getJson() tui = self._windowEditor.dumpDict()
connections = self._sigslotEditor.dumpDict()
# for line in jj.split('\n'): # for line in jj.split('\n'):
# TTkLog.debug(f"{line}") # TTkLog.debug(f"{line}")
newUI = {'tui':tui,'connections':connections}
jj = json.dumps(newUI, indent=1)
widget = TTkUiLoader.loadJson(jj) widget = TTkUiLoader.loadJson(jj)
win = TTkWindow( win = TTkWindow(
title="Mr Terminal", title="Mr Terminal",

35
ttkDesigner/app/signalsloteditor.py

@ -43,6 +43,35 @@ class _SignalSlotItem(ttk.TTkTreeWidgetItem):
self.updateWidgets() self.updateWidgets()
super().__init__([self._sender,self._signal,self._receiver,self._slot], *args, **kwargs) 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 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 }
def updateWidgets(self): def updateWidgets(self):
names = [w.name() for w in self._getWidgets()] names = [w.name() for w in self._getWidgets()]
self._sender.addItems(names) self._sender.addItems(names)
@ -173,3 +202,9 @@ class SignalSlotEditor(ttk.TTkWidget):
self._items.append(item) self._items.append(item)
self._detail.addTopLevelItem(item) self._detail.addTopLevelItem(item)
def dumpDict(self):
ret = []
for i in self._items:
if i.isValid():
ret.append(i.dumpDict())
return ret

12
ttkDesigner/app/windoweditor.py

@ -20,10 +20,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.
# Yaml is not included by default
# import yaml
import json
from .superobj import SuperWidget from .superobj import SuperWidget
import TermTk as ttk import TermTk as ttk
@ -41,8 +37,8 @@ class WindowEditorView(ttk.TTkAbstractScrollView):
def getTTk(self): def getTTk(self):
return self._ttk return self._ttk
def getJson(self): def dumpDict(self):
return json.dumps(self._ttk.dumpDict(), indent=1) return self._ttk.dumpDict()
def resizeEvent(self, w, h): def resizeEvent(self, w, h):
self._ttk.resize(w-8,h-4) self._ttk.resize(w-8,h-4)
@ -68,5 +64,5 @@ class WindowEditor(ttk.TTkAbstractScrollArea):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.setViewport(wev := WindowEditorView()) self.setViewport(wev := WindowEditorView())
self.getTTk = wev.getTTk self.getTTk = wev.getTTk
self.getJson = wev.getJson self.dumpDict = wev.dumpDict

Loading…
Cancel
Save