Browse Source

cleanup, refactor: fix-editor

micha 10 months ago
parent
commit
1ac39af5e5
8 changed files with 1010 additions and 1030 deletions
  1. 24 83
      _LibreLightDesk.py
  2. 1 0
      lib/colorpicker.py
  3. 0 19
      lib/fixlib.py
  4. 0 1
      lib/jsbc.py
  5. 188 1
      lib/libtk.py
  6. 5 925
      tkgui/GUI.py
  7. 5 1
      tkgui/draw.py
  8. 787 0
      tkgui/fix.py

+ 24 - 83
_LibreLightDesk.py

@@ -205,18 +205,13 @@ modes.modes["SELECT"] = 0
 modes.modes["CFG-BTN"] = 0
 modes.modes["LABEL"] = 0
 
-def xcb(mode,value=None):
-    cprint("xcb","MODE CALLBACK",mode,value)
-    if mode == "REC-FX":
-        cprint("xcb",modes.val("REC-FX"))
-#modes.set_cb(xcb)
 
 POS   = ["PAN","TILT","MOTION"]
 COLOR = ["RED","GREEN","BLUE","COLOR"]
 BEAM  = ["GOBO","G-ROT","PRISMA","P-ROT","FOCUS","SPEED"]
 INT   = ["DIM","SHUTTER","STROBE","FUNC"]
-#client = chat.tcp_sender(port=50001)
 
+#client = chat.tcp_sender(port=50001)
 
 def set_exec_fader_cfg(nr,val,label="",color=""):
     exec_wing = window_manager.get_obj(name="EXEC-WING") 
@@ -292,35 +287,21 @@ if __name__ == "__main__":
             r1_server.poll(cb=JCB)
             time.sleep(1/90)
     thread.start_new_thread(server1_loop,()) # SERVER
-# remote input - end
-#chat.dbg=1
 
-class DEVENT():
-    def __init__(self):
-        #if "keysym" in dir(event):
-        #if "Escape" == 
-        #event.keysym:
-        #event.num == 1:
-        self.keysym = ""
-        self.num = 1
-        self.type = ""
 
 import lib.jsbc as JSBC
 
 if __name__ == "__main__":
-    # external GUI
     r_server = chat.Server(port=30003,cb=JSBC.JSCB)
     def server_loop():
         while 1:
             r_server.poll(cb=JSBC.JSCB)
-            #time.sleep(1/90)
     thread.start_new_thread(server_loop,()) # SERVER
 
 
 import lib.fifo as FIFO
 
 if __name__ == "__main__":
-    # external GUI
     f_server = FIFO.read_loop() #chat.Server(port=30003,cb=JSBC.JSCB)
     f_server.loop(sleep=1)
 
@@ -761,26 +742,10 @@ def save_show_as(fname,new=0):
     if baselib.create_new_show_path(fpath):
         return save_show(fpath,new)
 
-        
-def wheel(event,d=None):
-    cprint("wheel",event,d)
-    
-
 
 
-class Element():
-    def __init__(self):
-        self.__data = {}
-    def set(self,key,val):
-        self.__data[key] = val
-
-
-        
 import lib.baselib as baselib
 
-def hex_to_rgb(hex):
-  return tuple(int(hex[i:i+2], 16) for i in (0, 2, 4)) 
-
 class cb():
     def __init__(self,win):
         self.win = win
@@ -1728,6 +1693,8 @@ def loops(**args):
     thread.start_new_thread(refresher_fix.loop,())
     thread.start_new_thread(refresher_exec.loop,())
 
+thread.start_new_thread(loops,())
+
 class window_create_sdl_buffer():
     def __init__(self,args,cls,data,cb_ok=None,scroll=0,gui=None):
         self.args   = args.copy()
@@ -1748,13 +1715,14 @@ def open_sdl_window():
     if "--easy" not in sys.argv:
         movewin.startup_all_sdl()
 
-thread.start_new_thread(loops,())
+thread.start_new_thread(open_sdl_window,())
+
+
 mc_fix = MC_FIX()
 def mc_fix_loop():
     time.sleep(5)
     while 1:
         try:
-            #print(1)
             data = FIXTURES.fixtures 
             mc_fix.set(index="fix",data=data)
         except Exception as e:
@@ -1762,7 +1730,6 @@ def mc_fix_loop():
         time.sleep(1/10)
 
 thread.start_new_thread(mc_fix_loop,())
-thread.start_new_thread(open_sdl_window,())
 
 
 if __run_main:
@@ -1811,6 +1778,7 @@ if __run_main:
     data.append({"text":"---"})
     data.append({"text":"SDL-STAGE"})
     data.append({"text":"SDL-Shader"})
+    data.append({"text":"TABLE"})
 
     #data.append({"text":"MASTER-WING"})
 
@@ -1884,17 +1852,13 @@ if __run_main:
     # =======================================================================
     name="SDL-VPU"
     def sdl_config():
-        cmd="nohup /usr/bin/python3 /opt/LibreLight/Xdesk/tksdl/config.py &"
-        cmd="/usr/bin/python3 /opt/LibreLight/Xdesk/tksdl/dmx.py " #&"
         cmd="python3 /opt/LibreLight/Xdesk/vpu/watchdog_vpu.py -single"
         print(cmd)
-        #os.popen(cmd)
 
         def xyz123(cmd):
             os.system(cmd)
         thread.start_new_thread(xyz123,(cmd,))
         return [None,None,None]
-    #class window_create_sdl_buffer():
     args = {"title":name,"master":0,"width":W1,"height":H1,"left":L1,"top":TOP}
     geo = libwin.split_window_position(pos_list,name)
     if geo:
@@ -1910,7 +1874,6 @@ if __run_main:
         window_manager.top(name)
 
     # =======================================================================
-    #python3 /opt/LibreLight/Xdesk/vpu/watchdog_vpu.py -single
     name="SDL-OSZI"
     def sdl_config():
         cmd="python3 /opt/LibreLight/ASP/monitor/oszi_grid.py" 
@@ -1935,19 +1898,15 @@ if __run_main:
         window_manager.top(name)
 
     # =======================================================================
-    #python3 /opt/LibreLight/Xdesk/vpu/watchdog_vpu.py -single
     name="SDL-DMX"
     def sdl_config():
-        cmd="nohup /usr/bin/python3 /opt/LibreLight/Xdesk/tksdl/config.py &"
         cmd="/usr/bin/python3 /opt/LibreLight/Xdesk/tksdl/dmx.py " #&"
         print(cmd)
-        #os.popen(cmd)
 
         def xyz123(cmd):
             os.system(cmd)
         thread.start_new_thread(xyz123,(cmd,))
         return [None,None,None]
-    #class window_create_sdl_buffer():
     args = {"title":name,"master":0,"width":W1,"height":H1,"left":L1,"top":TOP}
     geo = libwin.split_window_position(pos_list,name)
     if geo:
@@ -1963,19 +1922,15 @@ if __run_main:
         window_manager.top(name)
 
     # =======================================================================
-    #python3 /opt/LibreLight/Xdesk/vpu/watchdog_vpu.py -single
     name="RAY-DMX"
     def sdl_config():
-        cmd="nohup /usr/bin/python3 /opt/LibreLight/Xdesk/tksdl/config.py &"
         cmd="/usr/bin/python3 /opt/LibreLight/Xdesk/tkray/dmx.py " #&"
         print(cmd)
-        #os.popen(cmd)
 
         def xyz123(cmd):
             os.system(cmd)
         thread.start_new_thread(xyz123,(cmd,))
         return [None,None,None]
-    #class window_create_sdl_buffer():
     args = {"title":name,"master":0,"width":W1,"height":H1,"left":L1,"top":TOP}
     geo = libwin.split_window_position(pos_list,name)
     if geo:
@@ -1995,13 +1950,11 @@ if __run_main:
     def sdl_config():
         cmd="/usr/bin/python3 /opt/LibreLight/Xdesk//3d/stage_3d.py " #&"
         print(cmd)
-        #os.popen(cmd)
 
         def xyz123(cmd):
             os.system(cmd)
         thread.start_new_thread(xyz123,(cmd,))
         return [None,None,None]
-    #class window_create_sdl_buffer():
     args = {"title":name,"master":0,"width":W1,"height":H1,"left":L1,"top":TOP}
     geo = libwin.split_window_position(pos_list,name)
     if geo:
@@ -2131,14 +2084,14 @@ if __run_main:
     data=[]
     for i in range(12*6):
         data.append({"text"+str(i):"test"})
-    
-    cls = GUI_FixtureEditor
-    #cls = GUI_FaderLayout
+
+    import tkgui.fix as guifix
+
+    cls = guifix.GUI_FixtureEditor
     cb_ok = None
 
     c = libtk.window_create_buffer(args=args,cls=cls,data=data,cb_ok=cb_ok,gui=master,scroll=0)
     window_manager.new(None,name,wcb=c)
-    #window_manager.top(name)
 
     
     # =======================================================================
@@ -2302,8 +2255,6 @@ if __run_main:
     geo = libwin.split_window_position(pos_list,name)
     if geo:
         args.update(geo)
-    #w = libtk.WindowContainer(args)
-    #draw_colorpicker(master,w.tk,FIXTURES,master)
     cls = draw_colorpicker #(master,w.tk,FIXTURES,master)
     data = [FIXTURES,master]
     cb_ok = None #FIXTURES
@@ -2314,31 +2265,21 @@ if __run_main:
         window_manager.top(name)
 
     # =======================================================================
-    name="TableA"
-    #w = libtk.WindowContainer(name,master=0,width=W1,height=H1,left=L1,top=TOP)
-    #space_font = tk.font.Font(family="FreeSans", size=1 ) #, weight="bold")
-    #x=TableFrame(root=w.tk)#,left=80,top=620)
-    #w.show()
-    #data =[]
-    #for a in range(40):
-    #    data.append(["E","E{}".format(a+1)])
-
-    #x.draw(data=data,head=["E","C"],config=[12,5,5])
-    #w=x.bframe
-
-    #window_manager.new(w,name)
-
-
-
-    master.render()
-    window_manager.top("Table")
-    #w = frame_fix #WindowContainer("OLD",master=0,width=W1,height=500,left=130,top=TOP)
-    #window_manager.new(w,name)
-
+    name="TABLE"
+    args = {"title":name,"master":0,"width":600,"height":113,"left":L1+5,"top":TOP+5+HTB*2+H1}
+    geo = libwin.split_window_position(pos_list,name)
+    if geo:
+        args.update(geo)
+    #cls = draw_colorpicker #(master,w.tk,FIXTURES,master)
+    cls = TableFrame #(root=w.tk)#,left=80,top=620)
+    data = [FIXTURES,master]
+    cb_ok = None #FIXTURES
 
+    c = libtk.window_create_buffer(args=args,cls=cls,data=data,cb_ok=cb_ok,gui=master,scroll=0)
+    window_manager.new(None,name,wcb=c)
+    if libwin.split_window_show(pos_list,_filter=name):
+        window_manager.top(name)
 
-    #if "--easy" in sys.argv:
-    #load_window_position()
 
 
     def wm_mainloop():

+ 1 - 0
lib/colorpicker.py

@@ -23,6 +23,7 @@ def r():
 def hex_to_rgb(hex):
   return tuple(int(hex[i:i+2], 16) for i in (0, 2, 4)) 
 
+
 def _cb(event,data={}):
     print("dummy cb",event)
 

+ 0 - 19
lib/fixlib.py

@@ -130,25 +130,6 @@ def _parse_fixture_name(name):
         out = {"name":name}
     return out
 
-def online_help(page):
-    print("INIT:online_help",page)
-
-    try:
-        page = page.replace("&","")
-        page = page.replace("=","")
-        page = page.replace("/","")
-        import webbrowser
-        def _cb():
-            print("online_help",page)
-            webbrowser.open("http://librelight.de/wiki/doku.php?id="+page )
-        return _cb
-    except Exception as e:
-        print("online_help Exception",e)
-        raise e
-
-    def _cb():
-        print("error online_help",page)
-    return _cb 
 
 
 

+ 0 - 1
lib/jsbc.py

@@ -42,7 +42,6 @@ def JSCB(x,sock=None):
                     MAIN.FIXTURES.encoder(str(FIX),ATTR,xval=VAL,xfade=0,xdelay=0)#,blind=0)
 
                     #print(dir(cb))
-                    #event =  DEVENT()
                     #event.num = enum
 
                     #MAIN.master.refresh_fix() # delayed

+ 188 - 1
lib/libtk.py

@@ -2,6 +2,7 @@
 
 import os
 import time
+import json
 import sys
 sys.path.insert(0,"/opt/LibreLight/Xdesk/")
 
@@ -14,7 +15,8 @@ from lib.cprint import cprint
 import lib.libwin as libwin
 import lib.baselib as baselib
 
-import json
+import tkgui.dialog  as dialoglib
+dialog = dialoglib.Dialog()
 
 
 _config = []
@@ -71,6 +73,191 @@ class scroll():
         canvas = self.canvas
         canvas.configure(scrollregion=canvas.bbox("all"))#,width=400,height=200)
 
+def test_command(a1="",a2=""):
+    print([a1,a2])
+
+def online_help(page):
+    print("INIT:online_help",page)
+
+    try:
+        #page = page.replace("&","")
+        #page = page.replace("=","")
+        page = page.replace("/","")
+        import webbrowser
+        def _cb():
+            print("online_help",page)
+            webbrowser.open("http://librelight.de/wiki/doku.php?id="+page )
+        return _cb
+    except Exception as e:
+        print("online_help Exception",e)
+        raise e
+
+    def _cb():
+        print("error online_help",page)
+    return _cb 
+
+class ELEM_FADER():
+    def __init__(self,frame,nr,cb=None,fader_cb=None,**args):
+        self.frame = frame
+        self.nr= nr
+        self.id=nr
+        self.elem = []
+        self._cb = cb
+        self._fader_cb = fader_cb
+        width=11
+        frameS = tk.Frame(self.frame,bg="#005",width=width)
+        frameS.pack(fill=tk.Y, side=tk.LEFT)
+        self.frame=frameS
+
+    def fader_event(self,a1="",a2=""):
+        if self._fader_cb:
+            self._fader_cb(a1,a2,nr=self.nr)
+
+    def event(self,a1="",a2=""):
+        if self._cb:
+            self._cb([self,"event",a1,a2])
+
+    def set_nr(self,_event=None):
+        txt= self.elem_nr["text"]
+        def _cb(data):
+            if not data:
+                print("err443",self,"_cb",data)
+                return None
+            txt = data["Value"]
+            self._set_nr(txt)
+            if self._cb:
+                self._cb([self,"set_nr",txt])
+        dialog._cb = _cb
+        dialog.askstring("ATTR","set NR:",initialvalue=txt)
+
+    def set_attr(self,_event=None):
+        txt= self.attr["text"]
+        def _cb(data):
+            if not data:
+                print("err443",self,"_cb",data)
+                return None
+            txt = data["Value"]
+            #print(self,"set_attr._cb()",txt)
+            self._set_attr(txt)
+            if self._cb:
+                self._cb([self,"set_attr",txt])
+        dialog._cb = _cb
+        dialog.askstring("ATTR","set attr:",initialvalue=txt)
+        
+    def set_label(self,name=""):
+        #print("set_label",self.b,name)
+        self.label["text"] = name
+        if self._cb:
+            self._cb([self,"set_label",name])
+
+    def set_mode(self,_event=None):
+        txt= self.mode["text"]
+        def _cb(data):
+            if not data:
+                print("err443",self,"_cb",data)
+                return None
+            txt = data["Value"]
+            print(self,"set_mode._cb()",txt)
+            #w = MAIN.WindowContainer("config",master=1,width=200,height=140,left=L1,top=TOP)
+            #w.pack()
+            self._set_mode(txt)
+            #w.show()
+            if self._cb:
+                self._cb([self,"set_mode",txt])
+        dialog._cb = _cb
+        dialog.askstring("MODE S/F:","SWITCH or FADE",initialvalue=txt)
+
+    def _set_nr(self,txt=""):
+        if type(txt) is str:
+            try: 
+                x = int(txt)
+                if x <= 0:
+                    txt = "off"
+                    self.attr["bg"] = "#fa0"
+                    self.elem_nr["bg"] = "#fa0"
+                else:
+                    self.attr["bg"] = "lightblue"
+                    self.elem_nr["bg"] = "lightblue"
+            except:pass
+            self.elem_nr["text"] = "{}".format(txt)
+        if self._cb:
+            self._cb([self,"_set_nr",txt])
+
+    def _set_attr(self,txt=""):
+        self._set_mode("-")
+        if type(txt) is str:
+            self.attr["text"] = "{}".format(txt)
+            if txt.startswith("EMPTY"):
+                self.attr["bg"] = "#fa0"
+            else:
+                if txt in MAIN._FIX_FADE_ATTR:
+                    self._set_mode("F")
+                else:
+                    self._set_mode("S")
+
+        if self._cb:
+            self._cb([self,"_set_attr",txt])
+
+
+    def _set_mode(self,txt=""):
+        if type(txt) is str:
+            txt = txt[0].upper()
+            self.mode["text"] = "{}".format(txt)
+            #print("_set_mode",[self])
+        if self._cb:
+            self._cb([self,"_set_mode",txt])
+
+    def _refresh(self):
+        pass
+
+    def pack(self,init=None,from_=255,to=0,**args):
+        width=11
+        r=0
+        c=0
+        j=0
+        self.font8 = ("FreeSans",8)
+        frameS=self.frame
+
+        self.b = tk.Button(frameS,bg="#ffa",text="{}".format(self.nr), width=4,command=test_command,font=self.font8 )
+        self.b.pack(fill=tk.BOTH, side=tk.TOP)
+        self.label = self.b
+        self.elem.append(self.b)
+        
+        self.b = tk.Scale(frameS,bg="#ffa", width=28,from_=from_,to=to,command=self.fader_event)
+        self.elem_fader = self.b
+        self.b.pack(fill=tk.Y, side=tk.TOP)
+        if init is not None:
+            self.b.set(init)
+        self.elem.append(self.b)
+        
+        self.b = tk.Button(frameS,bg="lightblue",text="0", width=4,command=self.set_nr,font=self.font8 )
+        self.elem_nr=self.b
+        self.b.pack(fill=tk.BOTH, side=tk.TOP)
+        self.elem.append(self.b)
+
+        self.b = tk.Button(frameS,bg="lightblue",text="", width=5,command=self.set_attr,font=self.font8 )
+        self.attr=self.b
+        self.b.pack(fill=tk.BOTH, side=tk.TOP)
+        self.elem.append(self.b)
+        f = tk.Frame(frameS)
+        #f.pack()
+
+
+        self.b = tk.Button(frameS,bg="lightblue",text="", width=4,command=self.set_mode,font=self.font8 )
+        self.mode=self.b
+        self.b.pack(fill=tk.BOTH, side=tk.TOP)
+        #self.b.pack(fill=tk.BOTH, side=tk.LEFT)
+        self.elem.append(self.b)
+
+        #self.b = tk.Button(frameS,bg="lightblue",text="+>", width=4,command=self.set_mode,font=self.font8 )
+        #self.xmode=self.b
+        #self.b.pack(fill=tk.BOTH, side=tk.TOP)
+        #self.elem.append(self.b)
+
+        self.b = tk.Label(frameS,bg="black",text="", width=4,font=self.font8 )
+        self.b.pack(fill=tk.BOTH, side=tk.TOP)
+        self.elem.append(self.b)
+
 
 def ScrollFrame(root,width=50,height=100,bd=1,bg="black",head=None,foot=None):
     rframe=tk.Frame(root) 

+ 5 - 925
tkgui/GUI.py

@@ -6,7 +6,8 @@ import copy
 from collections import OrderedDict
 
 import tkinter 
-import tkinter as tk
+tk = tkinter 
+
 from idlelib.tooltip import Hovertip
 
 import tkgui.dialog  as dialoglib
@@ -756,768 +757,11 @@ class GUI_PATCH():
             r+=1
 
 
-class BaseCallback():
-    def __init__(self,cb=None,args={}):
-        self._cb=cb
-        self.args = args
-
-    def cb(self,**args):
-        print("BaseCallback.cb()")
-        print("  ",self.args)
-        print("  ",self._cb)
-        if self._cb:
-            if self.args:
-                self._cb(args=self.args) 
-            else:
-                self._cb() 
-
-
-
-
-def GUI_LOAD_FIXTURE_LIST(frame,data={"EMPTY":"None"},cb=None,bg="black"):
-    #print("__func__",__func__)
-    print("#",sys._getframe().f_code.co_name)
-    blist = data
-    blist = blist[:100]
-
-    frame.configure(bg=bg)
-
-    # table header
-    c=0
-    for r,row in enumerate(blist):
-        bg="lightgrey"
-        dbg="grey"
-        dbg="lightgrey"
-        c+=1
-        b = tk.Label(frame,bg="grey",text=str("Nr."))
-        b.grid(row=r, column=c, sticky=tk.W) #+tk.E)
-        c+=1
-        for k,v in row.items():
-            b = tk.Label(frame,bg="grey",text=k)
-            b.grid(row=r, column=c, sticky=tk.W) #+tk.E)
-            c+=1
-        break
-    
-    if not cb:
-        cb = DummyCallback()
-
-    # table data
-    for r,row in enumerate(blist):
-        c=1
-
-        bg="lightgrey"
-        #dbg="grey"
-        b = tk.Button(frame,text=r+1,anchor="w",bg=dbg,width=6,height=1,relief="sunken")
-        b.grid(row=r+1, column=c, sticky=tk.W ) #+tk.E)
-        c+=1
-        bg="lightgrey"
-        dbg="lightgrey"
-        for k,v in row.items():
-            #if v > time.strftime("%Y-%m-%d %X",  time.localtime(time.time()-3600*4)):
-            #    dbg = "lightgreen"
-            #elif v > time.strftime("%Y-%m-%d %X",  time.localtime(time.time()-3600*24*7)):
-            #    dbg = "green"
-            if "." in v:
-                #v = v.split(".",-1)
-                v = v[::1].split(".",1)[0]#[::-1]
-
-            if c == 3:
-                bg="grey"
-                dbg="grey"
-                _cb2 = BaseCallback(cb=cb,args={"key":k,"val":v,"data":row}).cb
-                b = tk.Button(frame,text=v,anchor="w",height=1,bg=bg,command=_cb2)
-            else: 
-                b = tk.Button(frame,text=v,anchor="w",bg=dbg,relief="flat",height=1)
-                b.config(activebackground=dbg)
-            b.grid(row=r+1, column=c, sticky=tk.W+tk.E)
-            c+=1
-            bg="lightgrey"
-            dbg="lightgrey"
-
-
-class GUI_FixtureEditor():
-    def __init__(self,root,frame,data,title="tilte",width=800):
-        #xfont = tk.font.Font(family="FreeSans", size=5, weight="bold")
-        self.font8 = ("FreeSans",8)
-        self.dmx=1
-        self.univ=0
-        self.elem=[]
-        self.fader_elem = []
-        self.pw = None
-        self.header=[]
-        self.data = data
-        self.fixture = []
-        self.title = title
-        self.width = width
-        #cprint("GUI:",root,title)
-        self.root = root
-        self.frame = frame
-        self.draw()
-    def draw(self):
-        root = self.frame
-
-        title = self.title
-        width = self.width
-        data = self.data
-
-        self.fader_elem = []
-
-        #Head 1
-        self.frame = tk.Frame(root,bg="grey",width=width)
-        self.frame.pack(fill="both", side=tk.TOP)
-        self.frame = tk.Frame(self.frame,bg="grey",width=width)
-        self.frame.pack(fill="both", side=tk.RIGHT)#EFT)
-
-        bg = "lightblue"
-        self.b = tk.Button(self.frame,bg=bg,text="IMPORT FROM SHOW", width=20)#,command=self.event) #bv.change_dmx)
-        self.b["command"] = self.open_fixture_list_import
-        self.b.pack( side=tk.LEFT)
-
-        self.b = tk.Button(self.frame,bg=bg,text="USER", width=5)#,command=self.event) #bv.change_dmx)
-        self.b["command"] = self.open_fixture_list_user
-        self.b.pack( side=tk.LEFT)
-
-        self.b = tk.Button(self.frame,bg=bg,text="GLOBAL", width=5)#,command=self.event) #bv.change_dmx)
-        self.b["command"] = self.open_fixture_list_global
-        self.b.pack( side=tk.LEFT)
-
-
-        self.b = tk.Label(self.frame,bg=bg ,text="")
-        self.b.pack(fill=None, side=tk.LEFT)
-
-        self.b = tk.Button(self.frame,bg=bg,text="SAVE", width=5)#,command=self.event) #bv.change_dmx)
-        self.b["command"] = self.save_fixture
-        self.b.pack( side=tk.LEFT)
-        
-
-        #self.b = tk.Button(self.frame,bg=bg,text="SAVE AS", width=5)#,command=self.event) #bv.change_dmx)
-        #self.b["command"] = self.save_as_fixture
-        #self.b.pack( side=tk.LEFT)
-
-        self.b = tk.Label(self.frame,bg="black",text="") # spacer
-        self.b.pack(fill=tk.Y, side=tk.LEFT)
-
-        self.b = tk.Button(self.frame,bg=bg,text="HELP", width=5)#,command=self.event) #bv.change_dmx)
-        self.b["command"] = fixlib.online_help("fixture-editor")
-        self.b.pack( side=tk.LEFT)
-        # HEAD 2
-        
-
-
-
-        r=0
-        c=0
-        self.frame = tk.Frame(root,bg="grey",width=width)
-        self.frame.pack(fill="both", side=tk.TOP)
-
-        #c+=1 ;r=0
-        self.b = tk.Label(self.frame,bg="#ddd",text="FIX-ID:")
-        self.b.grid(row=r,column=c) #,expand=1)
-
-        r+=1
-        self.b = tk.Button(self.frame,bg="lightblue",text="3001", width=4)
-        self.fixid=self.b
-        self.b["command"] = self.set_fixid
-        self.b.grid(row=r,column=c)
-
-        c+=1 ;r=0
-        self.b = tk.Label(self.frame,bg="#ddd",text="NAME:")
-        self.b.grid(row=r,column=c) #,expand=1)
-
-        r+=1
-        self.b = tk.Button(self.frame,bg="lightblue",text="FixName", width=13)
-        self.name=self.b
-        self.b["command"] = self.set_name
-        self.b.grid(row=r,column=c)
-
-        c+=1 ;r=0
-        self.b = tk.Label(self.frame,bg="lightblue",text="UNIV:")
-        self.b.grid(row=r,column=c)
-        r+=1
-        self.b_univ = tk.Button(self.frame,bg="lightblue",text="1", width=4)#,command=self.event) #bv.change_dmx)
-        self.entry_univ=self.b_univ
-        self.b_univ["command"] = self.event_univ
-        self.b_univ.grid(row=r,column=c)
-
-        c+=1 ;r=0
-        self.b = tk.Label(self.frame,bg="lightblue",text="DMX:")
-        self.b.grid(row=r,column=c)
-
-        r+=1
-        self.b = tk.Button(self.frame,bg="lightblue",text="1", width=4)#,command=self.event) #bv.change_dmx)
-        self.entry_dmx=self.b
-        self.b["command"] = self.event_dmx
-        self.b.grid(row=r,column=c)
-
-        c+=1 ;r=0
-        self.b = tk.Label(self.frame,bg="lightblue",text="QTY:")
-        self.b.grid(row=r,column=c)
-
-        r+=1
-        self.b = tk.Button(self.frame,bg="lightblue",text="4", width=4)#,command=self.event) #bv.change_dmx)
-        #self.entry_qty=self.b
-        self.qty=self.b
-        self.b["command"] = self.set_qty
-        #self.b["command"] = self.event_dmx
-        self.b.grid(row=r,column=c)
-
-        c+=1 ;r=0
-        r+=1
-        self.b = tk.Button(self.frame,bg="lightblue",text="PATCH", width=6)#,command=self.event) #bv.change_dmx)
-        self.b["command"] = self.do_patch
-        self.b.grid(row=r,column=c)
-
-
-        # HEAD 1
-        
-        #root = tk.Frame(root,bg="black",width=width)
-        #root.pack(fill=tk.BOTH, side=tk.TOP)
-
-        self.frame = tk.Frame(root,bg="grey",width=width)
-        self.frame.pack(fill="x", side=tk.TOP)
-
-        self.b = tk.Label(self.frame,bg="#fff",text="Fixture Editor") #,font=self.font8 )
-        self.b.pack(fill=None, side=tk.LEFT)
-
-        self.b = tk.Label(self.frame,bg="#aaa",text="FILE:") #,font=self.font8 )
-        self.b.pack(fill=None, side=tk.LEFT)
-
-        self.b_path = tk.Label(self.frame,bg="#fff",text="~/LibreLight/fixtures/lalla.json") #,font=self.font8 )
-        self.b_path.pack(fill=None, side=tk.LEFT)
-
-        self.b = tk.Label(self.frame,bg="#aaa",text="----") #,font=self.font8 )
-        self.b.pack(fill=None, side=tk.LEFT)
-
-        self.b_info = tk.Label(self.frame,bg="#fff",text="") #,font=self.font8 )
-        self.b_info.pack(fill=None, side=tk.LEFT)
-        # DATA
-        self.frame = libtk.ScrollFrame(root,bg="#003",width=2000 ,height=1000,bd=2) # fader frame
-
-
-        self.r=0
-        self.c=0
-        self.pb=12
-        self.j = 0
-
-        for page_nr in range(12):
-            self.draw_bank(page_nr)
-
-        self._event_redraw()
-
-    def draw_bank(self,page_nr):
-        title = self.title
-        width = self.width
-
-        idx = self.pb*page_nr
-        data = self.data[idx:idx+self.pb]
-
-        c = idx
-        for j,row in enumerate(data):
-            if c % self.pb == 0 or c==0:
-                h=hex(j*10)[2:].rjust(2,"0")
-                frameS = tk.Frame(self.frame,bg="#000",width=width,border=2)
-                frameS.pack(fill=tk.BOTH, side=tk.TOP)
-                bank_nr=j//self.pb+1
-                txt="BANK:{} {}-{}".format(bank_nr,bank_nr*self.pb-self.pb+1,bank_nr*self.pb) 
-                self.b = tk.Label(frameS,bg="lightblue",text=txt,width=15,font=self.font8 )
-                self.header.append(self.b)
-
-                self.b.pack(fill=None, side=tk.LEFT)
-                self.b = tk.Label(frameS,bg="black",text="" ,width=11,font=self.font8 )
-                self.b.pack(fill=tk.BOTH, side=tk.LEFT)
-
-                try:
-                    frameS = tk.Frame(self.frame,bg="#a000{}".format(h),width=width,border=2)
-                except:
-                    frameS = tk.Frame(self.frame,bg="#a0aadd",width=width,border=2)
-                self.c=0
-
-            e=  ELEM_FADER(frameS,nr=j+1,cb=self._cb,fader_cb=self._fader_cb)
-            e.pack()
-            #e.attr["bg"] = "red"
-            self.fader_elem.append(e)
-            self.elem.append(e)
-            frameS.pack(fill=tk.X, side=tk.TOP)
-            c+=1
-
-    def _fader_cb(self,arg,name="<name>",**args):
-        print("   FixtureEditor._cb",args,arg,name)
-        #print("    ",name,"_cb.args >>",args,arg[1:])
-        self.count_ch()
-    
-        try:
-            a1 = arg #arg[2]
-            nr = args["nr"] #.nr
-            j=[]
-            e = self.fader_elem[args["nr"]-1]
-            nr = int(e.elem_nr["text"]) 
-            if not nr:
-                nr = args["nr"]
-                return
-            nr_start = ( int(self.entry_dmx["text"])-1 + int(self.entry_univ["text"])*512 )
-            nr += nr_start
-
-            jdata = {'VALUE': int(a1), 'args': [] , 'FADE': 0,'DMX': str(nr)}
-            print("   ",jdata)
-            j.append(jdata)
-            MAIN.jclient_send(j)
-        except Exception as e:
-            print("exec",arg,args,nr)
-            print(e)
-            raise e
-
-    def _cb(self,arg,name="<name>",**args):
-        #print(" FixtureEditor._cb")
-        #print(" ",name,"_cb.args >>",args,arg[1:])
-        self.count_ch()
-    
-
-    def count_ch(self):
-        #print("FixtureEditor.count_ch:")
-        #e._set_attr( "---")
-        ch_s = []
-        j=-1
-        self.b_info["text"] = "xx"
-
-        for i,elem in enumerate(self.elem):
-            #print(dir(elem))
-            txt = elem.attr["text"]
-            if txt:
-                #print("count_ch:",i,txt)
-                if txt.startswith("EMPTY"):
-                    elem.attr["bg"] = "#fa0"
-                    elem.attr["activebackground"] = "#fa0"
-                else:
-                    elem.attr["bg"] = "#0f0"
-                    elem.attr["activebackground"] = "#0fa"
-                ch_s.append([i,txt])
-                j=i
-            else:
-                elem.attr["bg"] = "lightgrey"
-                elem.attr["activebackground"] = "white"
-        self.b_info["text"] = "CH's: {} USED: {}".format(j+1,len(ch_s))
-
-    def set_qty(self,_event=None):
-        txt = self.qty["text"]
-        def _cb(data):
-            if not data:
-                print("err443",self,"_cb",data)
-                return None
-            txt = data["Value"]
-            print(self,"._cb()",txt)
-            self.qty["text"] = "{}".format(txt)
-            print("set_qty",[_event,self])
-        dialog._cb = _cb
-        dialog.askstring("QTY:","QTY:",initialvalue=txt)
-
-    def do_patch(self,_event=None):
-        qty = int(self.qty["text"])
-        ID = int(self.fixid["text"])
-        univ = self.univ #int(self.univ["text"])
-        dmx = self.dmx #int(self.dmx["text"])
-        name = self.name["text"]
-        name_nr = ""
-        if "-" in name:
-            try:
-                name_nr = name.split("-")[-1]
-                name_nr = int(name_nr)
-                name = name.split("-")[:-1]
-                name = "-".join(name)
-            except:
-                name_nr = ""
-        
-        if name_nr == '':
-            if ID:
-                name_nr = ID
-            else:
-                name_nr = 9000
-        name_nr=int(name_nr)
-        print("do_patch",dmx,univ,qty)
-        DMX = dmx #univ*512 + dmx 
-        fixture = {"DMX": DMX, "UNIVERS": univ, "NAME": "D1", "TYPE": "", "VENDOR": "", "ATTRIBUT": {} , "ACTIVE": 0}
-        ATTR = []
-        max_nr = 0
-        for fader in self.fader_elem:
-            #print("patch -",fader)
-            attr = fader.attr["text"]
-            nr = fader.elem_nr["text"]
-            if nr == '':
-                continue
-            if nr == "off":
-                nr = -1
-            nr = int(nr)
-            mode = fader.mode["text"]
-            val = float(fader.elem_fader.get())
-            if not attr:
-                continue
-            if attr.startswith("EMPTY"):
-                continue
-            ATTR = OrderedDict()
-            ATTR["NR"] = nr
-            ATTR["MASTER"] = "0"
-            ATTR["MODE"] = mode
-            ATTR["VALUE"] = val
-            ATTR["ACTIVE"] = 0
-            ATTR["FX"] = ""
-            ATTR["FX2"] =  {}
-            print("patch --",nr,mode,attr)
-
-            fixture["ATTRIBUT"][attr] = ATTR
-            if nr > max_nr:
-                max_nr = nr
-
-        ok = 1
-        out=[]
-        err = []
-        msg=""
-        err2 = []
-        sucess = []
-        _fixture = fixture 
-        _dmx = _fixture["DMX"] 
-        for i in range(qty):
-            fixture = copy.deepcopy(_fixture)
-            fixture["DMX"] = _dmx 
-            print("i",i)
-            fixture["NAME"] = name + "-{:0>4}".format(name_nr)
-            fixture["ID"] = ID 
-            print(fixture)
-            fixture = fixlib.FIXTURE_CHECK_SDATA(ID,fixture)
-            #out.append(sdata)
-            out.append(fixture)
-            #fixture = copy.deepcopy(fixture)
-            if str(ID) in MAIN.FIXTURES.fixtures:
-                ok = 0
-                #err.append(" ID '{}' is in use ! ".format(ID))
-                err.append("FIX-ID '{}' ".format(ID))
-
-            if ATTR:
-                sucess.append("ID '{}' DMX:{} UNIV:{}".format(ID,fixture["DMX"],fixture["UNIVERS"]))
-            else:
-                ok = 0
-                err2.append(" NO 'attributes'  ID:'{}' ! ".format(ID))
-
-            name_nr += 1
-            ID += 1
-            _dmx += max_nr # bug offset of one fixture
-        print("OK:",ok)
-        print()
-        if err:
-            #msg+="Name:'"+name+"'\n"
-            msg+="FIX-ID is in use !\n"
-            msg+="\n"
-            msg+="\n".join(err)
-            msg+="\n"
-            msg+="\n"
-            msg+="OVERWRITE ?\n"
-            msg+="überschreiben ?\n"
-            #msg+="\n "
-            r=tkinter.messagebox.askyesno(message=msg,title="cancel/Abbruch",parent=None)
-            print("err",r)
-            if r: # if yes
-                pass
-            else:
-                return
-
-        if err2:
-            r=tkinter.messagebox.showwarning(message="PATCH ERROR 2'"+name+"'\n\n"+"\n".join(err2)+"\n\n ",title="Error",parent=None)
-            return
-
-        if sucess:
-            msg+="name:'"+name+"'\n\n"
-            msg="PATCH OK ?\n"
-            msg+="\n".join(sucess)
-            msg+="\n"
-            r=tkinter.messagebox.askyesno(message=msg,title="Execute/Ausführen",parent=None)
-            print("yes no" ,r )
-            if r:
-                for fix in out:
-                    print(";;",fix)
-                    k = str(fix["ID"])
-                    v = fix
-                    MAIN.FIXTURES.fixtures[k] = v
-
-                MAIN.FIXTURES._re_sort()
-
-
-    def set_fixid(self,_event=None):
-        txt = self.fixid["text"]
-        def _cb(data):
-            if not data:
-                print("err443",self,"_cb",data)
-                return None
-            txt = data["Value"]
-            print(self,"._cb()",txt)
-            self.fixid["text"] = "{}".format(txt)
-            print("set_fixid",[_event,self])
-        dialog._cb = _cb
-        dialog.askstring("FIXTURE ID:","ID:",initialvalue=txt)
-    def set_name(self,_event=None):
-        txt = self.name["text"]
-        def _cb(data):
-            if not data:
-                print("err443",self,"_cb",data)
-                return None
-            txt = data["Value"]
-            print(self,"._cb()",txt)
-            self.name["text"] = "{}".format(txt)
-            print("change_dmx",[_event,self])
-
-        dialog._cb = _cb
-        dialog.askstring("FIXTURE NAME:","NAME:",initialvalue=txt)
-
-
-    def save_as_fixture(self,event=None):
-        print("save_as_fix",self,event)
-        self.count_ch()
-
-    def save_fixture(self,event=None):
-        print("save_fix",self,event)
-        self.count_ch()
-
-
-    def open_fixture_list_global(self):
-        self.close_fixture_list()
-        self._open_fixture_list(mode="GLOBAL")
-    def open_fixture_list_import(self):
-        self.close_fixture_list()
-        self._open_fixture_list(mode="IMPORT")
-    def open_fixture_list_user(self):
-        self.close_fixture_list()
-        self._open_fixture_list(mode="USER")
-        
-    def _open_fixture_list(self,mode=""):
-        name = "FIXTURE-{}".format(mode)
-        line1="Fixture Library"
-        line2="CHOOS to EDIT >> DEMO MODUS"
-        line3="CHOOS to EDIT >> DEMO MODUS"
-
-        cb = None #LOAD_FIXTURE(self,"USER").cb 
-        self.pw = libtk.PopupList(name,width=600,cb=cb,left=libtk._POS_LEFT+620,bg="#333")
-        frame = self.pw.sframe(line1=line1,line2=line2) #,line3=line3)
-
-
-        def cb(event=None,args={}):
-            print("open_fixture_list.cb(")
-            print("   ",args)
-            if self.pw:
-                self.pw.w.tk.destroy()
-            data = args["data"]
-            self.b_path["text"] = data["name"] #"load_MH2"
-            self.name["text"] = data["name"] #"load_MH2"
-            self.name["text"] = data["name"] #"load_MH2"
-            xpath = data["xpath"] + "/" + data["xfname"]
-            fdata = baselib._read_sav_file(xpath)
-            n = []
-            a = []
-            m = []
-            NR = 0
-            for row in fdata:
-                #print("row:  ",row.keys())
-                #print("a-")
-                for k,fixture in row.items():#keys():
-                    #v = row[k]
-                    if "NAME" not in fixture:
-                        continue
-                    if fixture["NAME"] != args["val"]:
-                        continue
-                    
-                    self.fixture = fixture
-
-                    print("a    :",k,str(fixture)[:220],"...")
-                    #print("a    ::",type(k),":",type(fixture))
-
-
-                    attr_by_nr = fixlib.fixture_order_attr_by_nr(fixture)
-
-                    if "ATTRIBUT" in fixture:
-                        for at in attr_by_nr: #fixture["ATTRIBUT"]:
-                            print("   ",at)
-                            if at.startswith("EMPTY"):
-                                NR += 1
-                                n.append(str(NR))
-                                a.append(at)
-                                m.append("-")
-                                continue
-                            print("       ",fixture["ATTRIBUT"][at])
-
-                            if at.startswith("_"):
-                                 continue
-                            try:
-                                NR = fixture["ATTRIBUT"][at]["NR"]
-                                n.append(str(NR))
-                            except:
-                                n.append("-") 
-                            
-                            a.append(at ) #+":"+ str(fixture["ATTRIBUT"][at]["NR"]))
-
-                            if at.endswith("-FINE"):
-                                m.append("-")
-                            elif at in MAIN._FIX_FADE_ATTR: #["PAN","TILT","DIM","RED","GREEN","BLUE","CYAN","YELLOW","MAGENTA","FOCUS","ZOOM","FROST"]:
-                                m.append("F")
-                            else:
-                                m.append("S")
-                            #m.append("F")
-
-                    break # only a single fixture #no sub fixture
-
-            self._load_fix(None,n,a,m)
-            self.close_fixture_list()
-        #_x =dir(MAIN)
-        #_x.sort()
-        #for _a in _x:
-        #    print(_a)
-        blist = fixlib._load_fixture_list(mode=mode)
-        
-        r=GUI_LOAD_FIXTURE_LIST(frame,data=blist,cb=cb,bg="#333")
-
-    def close_fixture_list(self):
-        if self.pw:
-            self.pw.w.tk.destroy()
-
-    def clear(self,_event=None,attr=[]):
-        attr = [""]*100
-        mode = [""]*100
-        self._load_fix(None,attr,mode)
-        self.b_path["text"] = "clean..."
-        self.close_fixture_list()
-
-    def load(self,_event=None,attr=[]):
-        attr = ["LOAD"]*10
-        mode = ["X"]*10
-        self._load_fix(None,attr,mode)
-        self.b_path["text"] = "clean..."
-        self.close_fixture_list()
-
-    def load_EMPTY(self,_event=None,attr=[]):
-        #attr = [,"RED","GREEN","BLUE"]
-        #mode = ["F","F","F","F"]
-        self._load_mh(None)#,attr,mode)
-    def load_DIM(self,_event=None,attr=[]):
-        attr = ["DIM"]
-        mode = ["F"]
-        self._load_fix(None,attr,mode)
-        self.b_path["text"] = "load_DIM"
-        self.close_fixture_list()
-    def load_LED(self,_event=None,attr=[]):
-        attr = ["DIM","RED","GREEN","BLUE"]
-        mode = ["F","F","F","F"]
-        self._load_fix(None,attr,mode)
-        self.b_path["text"] = "load_LED"
-        self.close_fixture_list()
-    def load_MH(self,_event=None,attr=[]):
-        if not attr:
-            attr = ["PAN","PAN-FINE","TILT","TILT-FINE","SHUTTER","DIM","RED","GREEN","BLUE","GOBO"]
-        mode = []
-        for a in attr:
-            if a.endswith("-FINE"):
-                mode.append("-")
-            elif a in MAIN._FIX_FADE_ATTR: #["PAN","TILT","DIM","RED","GREEN","BLUE","CYAN","YELLOW","MAGENTA","FOCUS","ZOOM","FROST"]:
-                mode.append("F")
-            else:
-                mode.append("S")
-
-        self._load_fix(None,attr,mode)
-        self.b_path["text"] = "load_MH"
-        self.close_fixture_list()
-    def load_MH2(self,_event=None,attr=[]):
-        attr = ["PAN","PAN-FINE","TILT","TILT-FINE","SHUTTER","DIM","RED","GREEN","BLUE","GOBO","G-ROT","PRISM","P-ROT","ZOOM","CONTR"]
-        mode = ["F","F","F","F","S","F","F","F","F","S","S","S","S","F","S"]
-        self.b_path["text"] = "load_MH2"
-        self._load_fix(None,attr,mode)
-        self.close_fixture_list()
-
-    def _load_fix(self,_event=None,nrs=[],attr=[],mode=[]):
-        print("load_fixture",[_event,self])
-        #for i,e in enumerate(self.elem):
-        for i,e in enumerate(self.elem):
-            #print(self,"event",_event,e)
-            #print("event",_event,e)
-            if len(attr) > i:
-                e._set_nr( nrs[i])
-            else:
-                e._set_nr( "")
-
-            if len(attr) > i:
-                e._set_attr( attr[i])
-            else:
-                e._set_attr( "")
 
-            if len(mode) > i:
-                e._set_mode( mode[i]+"'")
-            else:
-                e._set_mode( "---")
-        self.count_ch() 
 
-    def event_univ(self,_event=None):
-        nr=self.univ
-        txt= self.entry_univ["text"]
-        #def _cb(txt):
-        def _cb(data):
-            if not data:
-                print("err443",self,"_cb",data)
-                return None
-            txt = data["Value"]
-            print(self,"event_univ._cb()",txt)
-            try:
-                nr = int(txt)
-            except TypeError:
-                print("--- abort ---")
-                return 0
-            self.univ = nr
-            self._event_redraw(_event)
-        dialog._cb = _cb
-        dialog.askstring("Universe","Univ 0-15",initialvalue=txt)
 
-    def event_dmx(self,_event=None):
-        nr=self.dmx
-        txt= self.entry_dmx["text"]
-        #txt = dialog.askstring("DMX","ArtNet 1-512 (7680 max)",initialvalue=txt)
-        #def _cb(txt):
-        def _cb(data):
-            if not data:
-                print("err443",self,"_cb",data)
-                return None
-            txt = data["Value"]
-            print(self,"event_dmx._cb()",txt)
-            try:
-                nr = int(txt)
-            except TypeError:
-                print("--- abort ---")
-                return 0
-            self.dmx = nr
-            if self.dmx <= 0:
-                self.dmx = 1
-            if self.dmx > 512:
-                self.univ = (self.dmx-1)//512
-                self.dmx = (self.dmx-1)%512+1
-            self._event_redraw(_event)
-        dialog._cb = _cb
-        dialog.askstring("DMX","ArtNet 1-512 (7680 max)",initialvalue=txt)
 
-        
-    def _event_redraw(self,_event=None):
-        self.entry_dmx["text"] = "{}".format(self.dmx)
-        self.entry_univ["text"] = "{}".format(self.univ)
-        nr = 1 # self.univ*(512)+self.dmx # multiplay fader nr with dmx...
-        # self.b_xdmx["text"] = " {}  ".format(nr)
-
-        print("change_dmx",[_event,self])
-        for i,btn in enumerate(self.elem):
-            #print("event",_event,btn)
-            #print("btn",btn)
-            dmx=nr+i
-            nr2 = dmx%512 
-            btn.set_label("{} {}.{}\n D:{}".format(i+1,self.univ,nr2,dmx))
-            btn.nr = nr+i
 
-        pb=self.pb
-        for j,e in enumerate(self.header):
-            p=j+1
-            #p=nr/pb
-            txt="BANK:{} {}-{}".format(p,p*pb-pb+nr,(p*pb+nr)-1) 
-            print("---",j,txt,e)
-            e["text"] = txt
-        self.count_ch() 
 
 class GUI_grid():
     def __init__(self,root,data,title="tilte",width=800):
@@ -1546,168 +790,6 @@ class GUI_grid():
 
 
 
-class ELEM_FADER():
-    def __init__(self,frame,nr,cb=None,fader_cb=None,**args):
-        self.frame = frame
-        self.nr= nr
-        self.id=nr
-        self.elem = []
-        self._cb = cb
-        self._fader_cb = fader_cb
-        width=11
-        frameS = tk.Frame(self.frame,bg="#005",width=width)
-        frameS.pack(fill=tk.Y, side=tk.LEFT)
-        self.frame=frameS
-
-    def fader_event(self,a1="",a2=""):
-        if self._fader_cb:
-            self._fader_cb(a1,a2,nr=self.nr)
-
-    def event(self,a1="",a2=""):
-        if self._cb:
-            self._cb([self,"event",a1,a2])
-
-    def set_nr(self,_event=None):
-        txt= self.elem_nr["text"]
-        def _cb(data):
-            if not data:
-                print("err443",self,"_cb",data)
-                return None
-            txt = data["Value"]
-            self._set_nr(txt)
-            if self._cb:
-                self._cb([self,"set_nr",txt])
-        dialog._cb = _cb
-        dialog.askstring("ATTR","set NR:",initialvalue=txt)
-
-    def set_attr(self,_event=None):
-        txt= self.attr["text"]
-        def _cb(data):
-            if not data:
-                print("err443",self,"_cb",data)
-                return None
-            txt = data["Value"]
-            #print(self,"set_attr._cb()",txt)
-            self._set_attr(txt)
-            if self._cb:
-                self._cb([self,"set_attr",txt])
-        dialog._cb = _cb
-        dialog.askstring("ATTR","set attr:",initialvalue=txt)
-        
-    def set_label(self,name=""):
-        #print("set_label",self.b,name)
-        self.label["text"] = name
-        if self._cb:
-            self._cb([self,"set_label",name])
-
-    def set_mode(self,_event=None):
-        txt= self.mode["text"]
-        def _cb(data):
-            if not data:
-                print("err443",self,"_cb",data)
-                return None
-            txt = data["Value"]
-            print(self,"set_mode._cb()",txt)
-            #w = MAIN.WindowContainer("config",master=1,width=200,height=140,left=L1,top=TOP)
-            #w.pack()
-            self._set_mode(txt)
-            #w.show()
-            if self._cb:
-                self._cb([self,"set_mode",txt])
-        dialog._cb = _cb
-        dialog.askstring("MODE S/F:","SWITCH or FADE",initialvalue=txt)
-
-    def _set_nr(self,txt=""):
-        if type(txt) is str:
-            try: 
-                x = int(txt)
-                if x <= 0:
-                    txt = "off"
-                    self.attr["bg"] = "#fa0"
-                    self.elem_nr["bg"] = "#fa0"
-                else:
-                    self.attr["bg"] = "lightblue"
-                    self.elem_nr["bg"] = "lightblue"
-            except:pass
-            self.elem_nr["text"] = "{}".format(txt)
-        if self._cb:
-            self._cb([self,"_set_nr",txt])
-
-    def _set_attr(self,txt=""):
-        self._set_mode("-")
-        if type(txt) is str:
-            self.attr["text"] = "{}".format(txt)
-            if txt.startswith("EMPTY"):
-                self.attr["bg"] = "#fa0"
-            else:
-                if txt in MAIN._FIX_FADE_ATTR:
-                    self._set_mode("F")
-                else:
-                    self._set_mode("S")
-
-        if self._cb:
-            self._cb([self,"_set_attr",txt])
-
-
-    def _set_mode(self,txt=""):
-        if type(txt) is str:
-            txt = txt[0].upper()
-            self.mode["text"] = "{}".format(txt)
-            #print("_set_mode",[self])
-        if self._cb:
-            self._cb([self,"_set_mode",txt])
-
-    def _refresh(self):
-        pass
-
-    def pack(self,init=None,from_=255,to=0,**args):
-        width=11
-        r=0
-        c=0
-        j=0
-        self.font8 = ("FreeSans",8)
-        frameS=self.frame
-
-        self.b = tk.Button(frameS,bg="#ffa",text="{}".format(self.nr), width=4,command=test,font=self.font8 )
-        self.b.pack(fill=tk.BOTH, side=tk.TOP)
-        self.label = self.b
-        self.elem.append(self.b)
-        
-        self.b = tk.Scale(frameS,bg="#ffa", width=28,from_=from_,to=to,command=self.fader_event)
-        self.elem_fader = self.b
-        self.b.pack(fill=tk.Y, side=tk.TOP)
-        if init is not None:
-            self.b.set(init)
-        self.elem.append(self.b)
-        
-        self.b = tk.Button(frameS,bg="lightblue",text="0", width=4,command=self.set_nr,font=self.font8 )
-        self.elem_nr=self.b
-        self.b.pack(fill=tk.BOTH, side=tk.TOP)
-        self.elem.append(self.b)
-
-        self.b = tk.Button(frameS,bg="lightblue",text="", width=5,command=self.set_attr,font=self.font8 )
-        self.attr=self.b
-        self.b.pack(fill=tk.BOTH, side=tk.TOP)
-        self.elem.append(self.b)
-        f = tk.Frame(frameS)
-        #f.pack()
-
-
-        self.b = tk.Button(frameS,bg="lightblue",text="", width=4,command=self.set_mode,font=self.font8 )
-        self.mode=self.b
-        self.b.pack(fill=tk.BOTH, side=tk.TOP)
-        #self.b.pack(fill=tk.BOTH, side=tk.LEFT)
-        self.elem.append(self.b)
-
-        #self.b = tk.Button(frameS,bg="lightblue",text="+>", width=4,command=self.set_mode,font=self.font8 )
-        #self.xmode=self.b
-        #self.b.pack(fill=tk.BOTH, side=tk.TOP)
-        #self.elem.append(self.b)
-
-        self.b = tk.Label(frameS,bg="black",text="", width=4,font=self.font8 )
-        self.b.pack(fill=tk.BOTH, side=tk.TOP)
-        self.elem.append(self.b)
-
 
 
 
@@ -1807,7 +889,7 @@ class EXEC_FADER():
             self.b.set(init)
         self.elem.append(self.b)
 
-        self.b = tk.Button(frameS,bg="lightblue",text="{}".format(self.nr), width=5,command=test,font=self.font8 )
+        self.b = tk.Button(frameS,bg="lightblue",text="{}".format(self.nr), width=5,command=libtk.test_command,font=self.font8 )
         self.b.pack(fill=tk.BOTH, side=tk.TOP)
         self.label = self.b
         if 1: #self.nr <= 10:
@@ -1847,8 +929,6 @@ class EXEC_FADER():
         self.elem.append(self.b)
 
 
-def test(a1="",a2=""):
-    print([a1,a2])
 
 class GUI_ExecWingLayout():
     def __init__(self,root,frame,data,title="tilte",width=800,start=81):
@@ -1892,7 +972,7 @@ class GUI_ExecWingLayout():
                     frameS = tk.Frame(self.frame,bg="#a0aadd",width=width,border=2)
                 c=0
             #print(frameS)
-            #e= ELEM_FADER(frameS,nr=j+1,cb=self.event_cb)
+            #e= libtk.ELEM_FADER(frameS,nr=j+1,cb=self.event_cb)
             e= EXEC_FADER(frameS,nr=j+1,fader_cb=self.event_cb)
             if j >= 10:
                 e.pack(from_=400,to=0,init=100)
@@ -2043,7 +1123,7 @@ class GUI_MasterWingLayout():
                     frameS = tk.Frame(self.frame,bg="#a0aadd",width=width,border=2)
                 c=0
             #print(frameS)
-            e= ELEM_FADER(frameS,nr=j+1,fader_cb=self.event_cb)
+            e= libtk.ELEM_FADER(frameS,nr=j+1,fader_cb=self.event_cb)
             if j >= 2:
                 e.pack(from_=400,to=0,init=100)
             else:

+ 5 - 1
tkgui/draw.py

@@ -9,7 +9,7 @@ import _thread as thread
 import __main__ as MAIN
 
 import lib.mytklib as mytklib
-import lib.libtk as tklib
+import lib.libtk as libtk
 import lib.tkevent as tkevent
 
 from lib.cprint import cprint
@@ -119,6 +119,10 @@ def draw_exec(gui,xframe,EXEC):
             c+=1
             b = tk.Button(frame,bg="lightblue", text="NAME"  )
             b.grid(row=r, column=c, sticky=tk.W+tk.E)
+            c+=7
+            if i == 0:
+                b = tk.Button(frame,bg="darkgrey", text="HELP",command=libtk.online_help("0&do=index"))
+                b.grid(row=r, column=c, sticky=tk.W+tk.E)
             r+=1
             c=0
         i+=1

+ 787 - 0
tkgui/fix.py

@@ -0,0 +1,787 @@
+#/usr/bin/python3
+
+import copy
+
+from collections import OrderedDict
+
+import tkinter 
+tk = tkinter 
+
+from idlelib.tooltip import Hovertip
+
+import tkgui.dialog  as dialoglib
+dialog = dialoglib.Dialog()
+
+import __main__ as MAIN
+
+from lib.cprint import *
+
+
+import lib.fixlib  as fixlib
+import lib.baselib as baselib
+import lib.libtk   as libtk
+
+
+
+class BaseCallback():
+    def __init__(self,cb=None,args={}):
+        self._cb=cb
+        self.args = args
+
+    def cb(self,**args):
+        print("BaseCallback.cb()")
+        print("  ",self.args)
+        print("  ",self._cb)
+        if self._cb:
+            if self.args:
+                self._cb(args=self.args) 
+            else:
+                self._cb() 
+
+
+def GUI_LOAD_FIXTURE_LIST(frame,data={"EMPTY":"None"},cb=None,bg="black"):
+    #print("__func__",__func__)
+    print("#",sys._getframe().f_code.co_name)
+    blist = data
+    blist = blist[:100]
+
+    frame.configure(bg=bg)
+
+    # table header
+    c=0
+    for r,row in enumerate(blist):
+        bg="lightgrey"
+        dbg="grey"
+        dbg="lightgrey"
+        c+=1
+        b = tk.Label(frame,bg="grey",text=str("Nr."))
+        b.grid(row=r, column=c, sticky=tk.W) #+tk.E)
+        c+=1
+        for k,v in row.items():
+            b = tk.Label(frame,bg="grey",text=k)
+            b.grid(row=r, column=c, sticky=tk.W) #+tk.E)
+            c+=1
+        break
+    
+    if not cb:
+        cb = DummyCallback()
+
+    # table data
+    for r,row in enumerate(blist):
+        c=1
+
+        bg="lightgrey"
+        #dbg="grey"
+        b = tk.Button(frame,text=r+1,anchor="w",bg=dbg,width=6,height=1,relief="sunken")
+        b.grid(row=r+1, column=c, sticky=tk.W ) #+tk.E)
+        c+=1
+        bg="lightgrey"
+        dbg="lightgrey"
+        for k,v in row.items():
+            #if v > time.strftime("%Y-%m-%d %X",  time.localtime(time.time()-3600*4)):
+            #    dbg = "lightgreen"
+            #elif v > time.strftime("%Y-%m-%d %X",  time.localtime(time.time()-3600*24*7)):
+            #    dbg = "green"
+            if "." in v:
+                #v = v.split(".",-1)
+                v = v[::1].split(".",1)[0]#[::-1]
+
+            if c == 3:
+                bg="grey"
+                dbg="grey"
+                _cb2 = BaseCallback(cb=cb,args={"key":k,"val":v,"data":row}).cb
+                b = tk.Button(frame,text=v,anchor="w",height=1,bg=bg,command=_cb2)
+            else: 
+                b = tk.Button(frame,text=v,anchor="w",bg=dbg,relief="flat",height=1)
+                b.config(activebackground=dbg)
+            b.grid(row=r+1, column=c, sticky=tk.W+tk.E)
+            c+=1
+            bg="lightgrey"
+            dbg="lightgrey"
+
+
+
+
+class GUI_FixtureEditor():
+    def __init__(self,root,frame,data,title="tilte",width=800):
+        #xfont = tk.font.Font(family="FreeSans", size=5, weight="bold")
+        self.font8 = ("FreeSans",8)
+        self.dmx=1
+        self.univ=0
+        self.elem=[]
+        self.fader_elem = []
+        self.pw = None
+        self.header=[]
+        self.data = data
+        self.fixture = []
+        self.title = title
+        self.width = width
+        #cprint("GUI:",root,title)
+        self.root = root
+        self.frame = frame
+        self.draw()
+    def draw(self):
+        root = self.frame
+
+        title = self.title
+        width = self.width
+        data = self.data
+
+        self.fader_elem = []
+
+        #Head 1
+        self.frame = tk.Frame(root,bg="grey",width=width)
+        self.frame.pack(fill="both", side=tk.TOP)
+        self.frame = tk.Frame(self.frame,bg="grey",width=width)
+        self.frame.pack(fill="both", side=tk.RIGHT)#EFT)
+
+        bg = "lightblue"
+        self.b = tk.Button(self.frame,bg=bg,text="IMPORT FROM SHOW", width=20)#,command=self.event) #bv.change_dmx)
+        self.b["command"] = self.open_fixture_list_import
+        self.b.pack( side=tk.LEFT)
+
+        self.b = tk.Button(self.frame,bg=bg,text="USER", width=5)#,command=self.event) #bv.change_dmx)
+        self.b["command"] = self.open_fixture_list_user
+        self.b.pack( side=tk.LEFT)
+
+        self.b = tk.Button(self.frame,bg=bg,text="GLOBAL", width=5)#,command=self.event) #bv.change_dmx)
+        self.b["command"] = self.open_fixture_list_global
+        self.b.pack( side=tk.LEFT)
+
+
+        self.b = tk.Label(self.frame,bg=bg ,text="")
+        self.b.pack(fill=None, side=tk.LEFT)
+
+        self.b = tk.Button(self.frame,bg=bg,text="SAVE", width=5)#,command=self.event) #bv.change_dmx)
+        self.b["command"] = self.save_fixture
+        #self.b.pack( side=tk.LEFT)
+        
+
+        #self.b = tk.Button(self.frame,bg=bg,text="SAVE AS", width=5)#,command=self.event) #bv.change_dmx)
+        #self.b["command"] = self.save_as_fixture
+        #self.b.pack( side=tk.LEFT)
+
+        self.b = tk.Label(self.frame,bg="black",text="") # spacer
+        self.b.pack(fill=tk.Y, side=tk.LEFT)
+
+        self.b = tk.Button(self.frame,bg=bg,text="HELP", width=5)#,command=self.event) #bv.change_dmx)
+        self.b["command"] = libtk.online_help("librelight:80-fixture-editor" ) #""fixture-editor")
+        self.b.pack( side=tk.LEFT)
+        # HEAD 2
+        
+
+
+
+        r=0
+        c=0
+        self.frame = tk.Frame(root,bg="grey",width=width)
+        self.frame.pack(fill="both", side=tk.TOP)
+
+        #c+=1 ;r=0
+        self.b = tk.Label(self.frame,bg="#ddd",text="FIX-ID:")
+        self.b.grid(row=r,column=c) #,expand=1)
+
+        r+=1
+        self.b = tk.Button(self.frame,bg="lightblue",text="3001", width=4)
+        self.fixid=self.b
+        self.b["command"] = self.set_fixid
+        self.b.grid(row=r,column=c)
+
+        c+=1 ;r=0
+        self.b = tk.Label(self.frame,bg="#ddd",text="NAME:")
+        self.b.grid(row=r,column=c) #,expand=1)
+
+        r+=1
+        self.b = tk.Button(self.frame,bg="lightblue",text="FixName", width=13)
+        self.name=self.b
+        self.b["command"] = self.set_name
+        self.b.grid(row=r,column=c)
+
+        c+=1 ;r=0
+        self.b = tk.Label(self.frame,bg="lightblue",text="UNIV:")
+        self.b.grid(row=r,column=c)
+        r+=1
+        self.b_univ = tk.Button(self.frame,bg="lightblue",text="1", width=4)#,command=self.event) #bv.change_dmx)
+        self.entry_univ=self.b_univ
+        self.b_univ["command"] = self.event_univ
+        self.b_univ.grid(row=r,column=c)
+
+        c+=1 ;r=0
+        self.b = tk.Label(self.frame,bg="lightblue",text="DMX:")
+        self.b.grid(row=r,column=c)
+
+        r+=1
+        self.b = tk.Button(self.frame,bg="lightblue",text="1", width=4)#,command=self.event) #bv.change_dmx)
+        self.entry_dmx=self.b
+        self.b["command"] = self.event_dmx
+        self.b.grid(row=r,column=c)
+
+        c+=1 ;r=0
+        self.b = tk.Label(self.frame,bg="lightblue",text="QTY:")
+        self.b.grid(row=r,column=c)
+
+        r+=1
+        self.b = tk.Button(self.frame,bg="lightblue",text="4", width=4)#,command=self.event) #bv.change_dmx)
+        #self.entry_qty=self.b
+        self.qty=self.b
+        self.b["command"] = self.set_qty
+        #self.b["command"] = self.event_dmx
+        self.b.grid(row=r,column=c)
+
+        c+=1 ;r=0
+        r+=1
+        self.b = tk.Button(self.frame,bg="lightblue",text="PATCH", width=6)#,command=self.event) #bv.change_dmx)
+        self.b["command"] = self.do_patch
+        self.b.grid(row=r,column=c)
+
+
+        # HEAD 1
+        
+        #root = tk.Frame(root,bg="black",width=width)
+        #root.pack(fill=tk.BOTH, side=tk.TOP)
+
+        self.frame = tk.Frame(root,bg="grey",width=width)
+        self.frame.pack(fill="x", side=tk.TOP)
+
+        self.b = tk.Label(self.frame,bg="#fff",text="Fixture Editor") #,font=self.font8 )
+        self.b.pack(fill=None, side=tk.LEFT)
+
+        self.b = tk.Label(self.frame,bg="#aaa",text="FILE:") #,font=self.font8 )
+        self.b.pack(fill=None, side=tk.LEFT)
+
+        self.b_path = tk.Label(self.frame,bg="#fff",text="~/LibreLight/fixtures/lalla.json") #,font=self.font8 )
+        self.b_path.pack(fill=None, side=tk.LEFT)
+
+        self.b = tk.Label(self.frame,bg="#aaa",text="----") #,font=self.font8 )
+        self.b.pack(fill=None, side=tk.LEFT)
+
+        self.b_info = tk.Label(self.frame,bg="#fff",text="") #,font=self.font8 )
+        self.b_info.pack(fill=None, side=tk.LEFT)
+        # DATA
+        self.frame = libtk.ScrollFrame(root,bg="#003",width=2000 ,height=1000,bd=2) # fader frame
+
+
+        self.r=0
+        self.c=0
+        self.pb=12
+        self.j = 0
+
+        for page_nr in range(12):
+            self.draw_bank(page_nr)
+
+        self._event_redraw()
+
+    def draw_bank(self,page_nr):
+        title = self.title
+        width = self.width
+
+        idx = self.pb*page_nr
+        data = self.data[idx:idx+self.pb]
+
+        c = idx
+        for j,row in enumerate(data):
+            if c % self.pb == 0 or c==0:
+                h=hex(j*10)[2:].rjust(2,"0")
+                frameS = tk.Frame(self.frame,bg="#000",width=width,border=2)
+                frameS.pack(fill=tk.BOTH, side=tk.TOP)
+                bank_nr=j//self.pb+1
+                txt="BANK:{} {}-{}".format(bank_nr,bank_nr*self.pb-self.pb+1,bank_nr*self.pb) 
+                self.b = tk.Label(frameS,bg="lightblue",text=txt,width=15,font=self.font8 )
+                self.header.append(self.b)
+
+                self.b.pack(fill=None, side=tk.LEFT)
+                self.b = tk.Label(frameS,bg="black",text="" ,width=11,font=self.font8 )
+                self.b.pack(fill=tk.BOTH, side=tk.LEFT)
+
+                try:
+                    frameS = tk.Frame(self.frame,bg="#a000{}".format(h),width=width,border=2)
+                except:
+                    frameS = tk.Frame(self.frame,bg="#a0aadd",width=width,border=2)
+                self.c=0
+
+            e= libtk.ELEM_FADER(frameS,nr=j+1,cb=self._cb,fader_cb=self._fader_cb)
+            e.pack()
+            #e.attr["bg"] = "red"
+            self.fader_elem.append(e)
+            self.elem.append(e)
+            frameS.pack(fill=tk.X, side=tk.TOP)
+            c+=1
+
+    def _fader_cb(self,arg,name="<name>",**args):
+        print("   FixtureEditor._cb",args,arg,name)
+        #print("    ",name,"_cb.args >>",args,arg[1:])
+        self.count_ch()
+    
+        try:
+            a1 = arg #arg[2]
+            nr = args["nr"] #.nr
+            j=[]
+            e = self.fader_elem[args["nr"]-1]
+            nr = int(e.elem_nr["text"]) 
+            if not nr:
+                nr = args["nr"]
+                return
+            nr_start = ( int(self.entry_dmx["text"])-1 + int(self.entry_univ["text"])*512 )
+            nr += nr_start
+
+            jdata = {'VALUE': int(a1), 'args': [] , 'FADE': 0,'DMX': str(nr)}
+            print("   ",jdata)
+            j.append(jdata)
+            MAIN.jclient_send(j)
+        except Exception as e:
+            print("exec",arg,args,nr)
+            print(e)
+            raise e
+
+    def _cb(self,arg,name="<name>",**args):
+        #print(" FixtureEditor._cb")
+        #print(" ",name,"_cb.args >>",args,arg[1:])
+        self.count_ch()
+    
+
+    def count_ch(self):
+        #print("FixtureEditor.count_ch:")
+        #e._set_attr( "---")
+        ch_s = []
+        j=-1
+        self.b_info["text"] = "xx"
+
+        for i,elem in enumerate(self.elem):
+            #print(dir(elem))
+            txt = elem.attr["text"]
+            if txt:
+                #print("count_ch:",i,txt)
+                if txt.startswith("EMPTY"):
+                    elem.attr["bg"] = "#fa0"
+                    elem.attr["activebackground"] = "#fa0"
+                else:
+                    elem.attr["bg"] = "#0f0"
+                    elem.attr["activebackground"] = "#0fa"
+                ch_s.append([i,txt])
+                j=i
+            else:
+                elem.attr["bg"] = "lightgrey"
+                elem.attr["activebackground"] = "white"
+        self.b_info["text"] = "CH's: {} USED: {}".format(j+1,len(ch_s))
+
+    def set_qty(self,_event=None):
+        txt = self.qty["text"]
+        def _cb(data):
+            if not data:
+                print("err443",self,"_cb",data)
+                return None
+            txt = data["Value"]
+            print(self,"._cb()",txt)
+            self.qty["text"] = "{}".format(txt)
+            print("set_qty",[_event,self])
+        dialog._cb = _cb
+        dialog.askstring("QTY:","QTY:",initialvalue=txt)
+
+    def do_patch(self,_event=None):
+        qty = int(self.qty["text"])
+        ID = int(self.fixid["text"])
+        univ = self.univ #int(self.univ["text"])
+        dmx = self.dmx #int(self.dmx["text"])
+        name = self.name["text"]
+        name_nr = ""
+        if "-" in name:
+            try:
+                name_nr = name.split("-")[-1]
+                name_nr = int(name_nr)
+                name = name.split("-")[:-1]
+                name = "-".join(name)
+            except:
+                name_nr = ""
+        
+        if name_nr == '':
+            if ID:
+                name_nr = ID
+            else:
+                name_nr = 9000
+        name_nr=int(name_nr)
+        print("do_patch",dmx,univ,qty)
+        DMX = dmx #univ*512 + dmx 
+        fixture = {"DMX": DMX, "UNIVERS": univ, "NAME": "D1", "TYPE": "", "VENDOR": "", "ATTRIBUT": {} , "ACTIVE": 0}
+        ATTR = []
+        max_nr = 0
+        for fader in self.fader_elem:
+            #print("patch -",fader)
+            attr = fader.attr["text"]
+            nr = fader.elem_nr["text"]
+            if nr == '':
+                continue
+            if nr == "off":
+                nr = -1
+            nr = int(nr)
+            mode = fader.mode["text"]
+            val = float(fader.elem_fader.get())
+            if not attr:
+                continue
+            if attr.startswith("EMPTY"):
+                continue
+            ATTR = OrderedDict()
+            ATTR["NR"] = nr
+            ATTR["MASTER"] = "0"
+            ATTR["MODE"] = mode
+            ATTR["VALUE"] = val
+            ATTR["ACTIVE"] = 0
+            ATTR["FX"] = ""
+            ATTR["FX2"] =  {}
+            print("patch --",nr,mode,attr)
+
+            fixture["ATTRIBUT"][attr] = ATTR
+            if nr > max_nr:
+                max_nr = nr
+
+        ok = 1
+        out=[]
+        err = []
+        msg=""
+        err2 = []
+        sucess = []
+        _fixture = fixture 
+        _dmx = _fixture["DMX"] 
+        for i in range(qty):
+            fixture = copy.deepcopy(_fixture)
+            fixture["DMX"] = _dmx 
+            print("i",i)
+            fixture["NAME"] = name + "-{:0>4}".format(name_nr)
+            fixture["ID"] = ID 
+            print(fixture)
+            fixture = fixlib.FIXTURE_CHECK_SDATA(ID,fixture)
+            #out.append(sdata)
+            out.append(fixture)
+            #fixture = copy.deepcopy(fixture)
+            if str(ID) in MAIN.FIXTURES.fixtures:
+                ok = 0
+                #err.append(" ID '{}' is in use ! ".format(ID))
+                err.append("FIX-ID '{}' ".format(ID))
+
+            if ATTR:
+                sucess.append("ID '{}' DMX:{} UNIV:{}".format(ID,fixture["DMX"],fixture["UNIVERS"]))
+            else:
+                ok = 0
+                err2.append(" NO 'attributes'  ID:'{}' ! ".format(ID))
+
+            name_nr += 1
+            ID += 1
+            _dmx += max_nr # bug offset of one fixture
+        print("OK:",ok)
+        print()
+        if err:
+            #msg+="Name:'"+name+"'\n"
+            msg+="FIX-ID is in use !\n"
+            msg+="\n"
+            msg+="\n".join(err)
+            msg+="\n"
+            msg+="\n"
+            msg+="OVERWRITE ?\n"
+            msg+="überschreiben ?\n"
+            #msg+="\n "
+            r=tkinter.messagebox.askyesno(message=msg,title="cancel/Abbruch",parent=None)
+            print("err",r)
+            if r: # if yes
+                pass
+            else:
+                return
+
+        if err2:
+            r=tkinter.messagebox.showwarning(message="PATCH ERROR 2'"+name+"'\n\n"+"\n".join(err2)+"\n\n ",title="Error",parent=None)
+            return
+
+        if sucess:
+            msg+="name:'"+name+"'\n\n"
+            msg="PATCH OK ?\n"
+            msg+="\n".join(sucess)
+            msg+="\n"
+            r=tkinter.messagebox.askyesno(message=msg,title="Execute/Ausführen",parent=None)
+            print("yes no" ,r )
+            if r:
+                for fix in out:
+                    print(";;",fix)
+                    k = str(fix["ID"])
+                    v = fix
+                    MAIN.FIXTURES.fixtures[k] = v
+
+                MAIN.FIXTURES._re_sort()
+
+
+    def set_fixid(self,_event=None):
+        txt = self.fixid["text"]
+        def _cb(data):
+            if not data:
+                print("err443",self,"_cb",data)
+                return None
+            txt = data["Value"]
+            print(self,"._cb()",txt)
+            self.fixid["text"] = "{}".format(txt)
+            print("set_fixid",[_event,self])
+        dialog._cb = _cb
+        dialog.askstring("FIXTURE ID:","ID:",initialvalue=txt)
+    def set_name(self,_event=None):
+        txt = self.name["text"]
+        def _cb(data):
+            if not data:
+                print("err443",self,"_cb",data)
+                return None
+            txt = data["Value"]
+            print(self,"._cb()",txt)
+            self.name["text"] = "{}".format(txt)
+            print("change_dmx",[_event,self])
+
+        dialog._cb = _cb
+        dialog.askstring("FIXTURE NAME:","NAME:",initialvalue=txt)
+
+
+    def save_as_fixture(self,event=None):
+        print("save_as_fix",self,event)
+        self.count_ch()
+
+    def save_fixture(self,event=None):
+        print("save_fix",self,event)
+        self.count_ch()
+
+
+    def open_fixture_list_global(self):
+        self.close_fixture_list()
+        self._open_fixture_list(mode="GLOBAL")
+    def open_fixture_list_import(self):
+        self.close_fixture_list()
+        self._open_fixture_list(mode="IMPORT")
+    def open_fixture_list_user(self):
+        self.close_fixture_list()
+        self._open_fixture_list(mode="USER")
+        
+    def _open_fixture_list(self,mode=""):
+        name = "FIXTURE-{}".format(mode)
+        line1="Fixture Library"
+        line2="CHOOS to EDIT >> DEMO MODUS"
+        line3="CHOOS to EDIT >> DEMO MODUS"
+
+        cb = None #LOAD_FIXTURE(self,"USER").cb 
+        self.pw = libtk.PopupList(name,width=600,cb=cb,left=libtk._POS_LEFT+620,bg="#333")
+        frame = self.pw.sframe(line1=line1,line2=line2) #,line3=line3)
+
+
+        def cb(event=None,args={}):
+            print("open_fixture_list.cb(")
+            print("   ",args)
+            if self.pw:
+                self.pw.w.tk.destroy()
+            data = args["data"]
+            self.b_path["text"] = data["name"] #"load_MH2"
+            self.name["text"] = data["name"] #"load_MH2"
+            self.name["text"] = data["name"] #"load_MH2"
+            xpath = data["xpath"] + "/" + data["xfname"]
+            fdata = baselib._read_sav_file(xpath)
+            n = []
+            a = []
+            m = []
+            NR = 0
+            for row in fdata:
+                #print("row:  ",row.keys())
+                #print("a-")
+                for k,fixture in row.items():#keys():
+                    #v = row[k]
+                    if "NAME" not in fixture:
+                        continue
+                    if fixture["NAME"] != args["val"]:
+                        continue
+                    
+                    self.fixture = fixture
+
+                    print("a    :",k,str(fixture)[:220],"...")
+                    #print("a    ::",type(k),":",type(fixture))
+
+
+                    attr_by_nr = fixlib.fixture_order_attr_by_nr(fixture)
+
+                    if "ATTRIBUT" in fixture:
+                        for at in attr_by_nr: #fixture["ATTRIBUT"]:
+                            print("   ",at)
+                            if at.startswith("EMPTY"):
+                                NR += 1
+                                n.append(str(NR))
+                                a.append(at)
+                                m.append("-")
+                                continue
+                            print("       ",fixture["ATTRIBUT"][at])
+
+                            if at.startswith("_"):
+                                 continue
+                            try:
+                                NR = fixture["ATTRIBUT"][at]["NR"]
+                                n.append(str(NR))
+                            except:
+                                n.append("-") 
+                            
+                            a.append(at ) #+":"+ str(fixture["ATTRIBUT"][at]["NR"]))
+
+                            if at.endswith("-FINE"):
+                                m.append("-")
+                            elif at in MAIN._FIX_FADE_ATTR: #["PAN","TILT","DIM","RED","GREEN","BLUE","CYAN","YELLOW","MAGENTA","FOCUS","ZOOM","FROST"]:
+                                m.append("F")
+                            else:
+                                m.append("S")
+                            #m.append("F")
+
+                    break # only a single fixture #no sub fixture
+
+            self._load_fix(None,n,a,m)
+            self.close_fixture_list()
+        #_x =dir(MAIN)
+        #_x.sort()
+        #for _a in _x:
+        #    print(_a)
+        blist = fixlib._load_fixture_list(mode=mode)
+        
+        r=GUI_LOAD_FIXTURE_LIST(frame,data=blist,cb=cb,bg="#333")
+
+    def close_fixture_list(self):
+        if self.pw:
+            self.pw.w.tk.destroy()
+
+    def clear(self,_event=None,attr=[]):
+        attr = [""]*100
+        mode = [""]*100
+        self._load_fix(None,attr,mode)
+        self.b_path["text"] = "clean..."
+        self.close_fixture_list()
+
+    def load(self,_event=None,attr=[]):
+        attr = ["LOAD"]*10
+        mode = ["X"]*10
+        self._load_fix(None,attr,mode)
+        self.b_path["text"] = "clean..."
+        self.close_fixture_list()
+
+    def load_EMPTY(self,_event=None,attr=[]):
+        #attr = [,"RED","GREEN","BLUE"]
+        #mode = ["F","F","F","F"]
+        self._load_mh(None)#,attr,mode)
+    def load_DIM(self,_event=None,attr=[]):
+        attr = ["DIM"]
+        mode = ["F"]
+        self._load_fix(None,attr,mode)
+        self.b_path["text"] = "load_DIM"
+        self.close_fixture_list()
+    def load_LED(self,_event=None,attr=[]):
+        attr = ["DIM","RED","GREEN","BLUE"]
+        mode = ["F","F","F","F"]
+        self._load_fix(None,attr,mode)
+        self.b_path["text"] = "load_LED"
+        self.close_fixture_list()
+    def load_MH(self,_event=None,attr=[]):
+        if not attr:
+            attr = ["PAN","PAN-FINE","TILT","TILT-FINE","SHUTTER","DIM","RED","GREEN","BLUE","GOBO"]
+        mode = []
+        for a in attr:
+            if a.endswith("-FINE"):
+                mode.append("-")
+            elif a in MAIN._FIX_FADE_ATTR: #["PAN","TILT","DIM","RED","GREEN","BLUE","CYAN","YELLOW","MAGENTA","FOCUS","ZOOM","FROST"]:
+                mode.append("F")
+            else:
+                mode.append("S")
+
+        self._load_fix(None,attr,mode)
+        self.b_path["text"] = "load_MH"
+        self.close_fixture_list()
+    def load_MH2(self,_event=None,attr=[]):
+        attr = ["PAN","PAN-FINE","TILT","TILT-FINE","SHUTTER","DIM","RED","GREEN","BLUE","GOBO","G-ROT","PRISM","P-ROT","ZOOM","CONTR"]
+        mode = ["F","F","F","F","S","F","F","F","F","S","S","S","S","F","S"]
+        self.b_path["text"] = "load_MH2"
+        self._load_fix(None,attr,mode)
+        self.close_fixture_list()
+
+    def _load_fix(self,_event=None,nrs=[],attr=[],mode=[]):
+        print("load_fixture",[_event,self])
+        #for i,e in enumerate(self.elem):
+        for i,e in enumerate(self.elem):
+            #print(self,"event",_event,e)
+            #print("event",_event,e)
+            if len(attr) > i:
+                e._set_nr( nrs[i])
+            else:
+                e._set_nr( "")
+
+            if len(attr) > i:
+                e._set_attr( attr[i])
+            else:
+                e._set_attr( "")
+
+            if len(mode) > i:
+                e._set_mode( mode[i]+"'")
+            else:
+                e._set_mode( "---")
+        self.count_ch() 
+
+    def event_univ(self,_event=None):
+        nr=self.univ
+        txt= self.entry_univ["text"]
+        #def _cb(txt):
+        def _cb(data):
+            if not data:
+                print("err443",self,"_cb",data)
+                return None
+            txt = data["Value"]
+            print(self,"event_univ._cb()",txt)
+            try:
+                nr = int(txt)
+            except TypeError:
+                print("--- abort ---")
+                return 0
+            self.univ = nr
+            self._event_redraw(_event)
+        dialog._cb = _cb
+        dialog.askstring("Universe","Univ 0-15",initialvalue=txt)
+
+    def event_dmx(self,_event=None):
+        nr=self.dmx
+        txt= self.entry_dmx["text"]
+        #txt = dialog.askstring("DMX","ArtNet 1-512 (7680 max)",initialvalue=txt)
+        #def _cb(txt):
+        def _cb(data):
+            if not data:
+                print("err443",self,"_cb",data)
+                return None
+            txt = data["Value"]
+            print(self,"event_dmx._cb()",txt)
+            try:
+                nr = int(txt)
+            except TypeError:
+                print("--- abort ---")
+                return 0
+            self.dmx = nr
+            if self.dmx <= 0:
+                self.dmx = 1
+            if self.dmx > 512:
+                self.univ = (self.dmx-1)//512
+                self.dmx = (self.dmx-1)%512+1
+            self._event_redraw(_event)
+        dialog._cb = _cb
+        dialog.askstring("DMX","ArtNet 1-512 (7680 max)",initialvalue=txt)
+
+        
+    def _event_redraw(self,_event=None):
+        self.entry_dmx["text"] = "{}".format(self.dmx)
+        self.entry_univ["text"] = "{}".format(self.univ)
+        nr = 1 # self.univ*(512)+self.dmx # multiplay fader nr with dmx...
+        # self.b_xdmx["text"] = " {}  ".format(nr)
+
+        print("change_dmx",[_event,self])
+        for i,btn in enumerate(self.elem):
+            #print("event",_event,btn)
+            #print("btn",btn)
+            dmx=nr+i
+            nr2 = dmx%512 
+            btn.set_label("{} {}.{}\n D:{}".format(i+1,self.univ,nr2,dmx))
+            btn.nr = nr+i
+
+        pb=self.pb
+        for j,e in enumerate(self.header):
+            p=j+1
+            #p=nr/pb
+            txt="BANK:{} {}-{}".format(p,p*pb-pb+nr,(p*pb+nr)-1) 
+            print("---",j,txt,e)
+            e["text"] = txt
+        self.count_ch()