Files
@ 375f48d1518a
Branch filter:
Location: light9/light9/uihelpers.py
375f48d1518a
8.2 KiB
text/x-python
mypy, flake8 setups
Ignore-this: 159ab09780ff33dec508d2d25c1628bf
Ignore-this: 159ab09780ff33dec508d2d25c1628bf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 | """all the tiny tk helper functions"""
#from Tkinter import Button
import logging, time
from rdflib import Literal
from tkinter.tix import Button, Toplevel, Tk, IntVar, Entry, DoubleVar
import tkinter
from light9.namespaces import L9
log = logging.getLogger("toplevel")
windowlocations = {
'sub' : '425x738+00+00',
'console' : '168x24+848+000',
'leveldisplay' : '144x340+870+400',
'cuefader' : '314x212+546+741',
'effect' : '24x24+0963+338',
'stage' : '823x683+37+030',
'scenes' : '504x198+462+12',
}
def bindkeys(root,key, func):
root.bind(key, func)
for w in root.winfo_children():
w.bind(key, func)
def toplevel_savegeometry(tl,name):
try:
geo = tl.geometry()
if not geo.startswith("1x1"):
f=open(".light9-window-geometry-%s" % name.replace(' ','_'),'w')
f.write(tl.geometry())
# else the window never got mapped
except Exception as e:
# it's ok if there's no saved geometry
pass
def toplevelat(name, existingtoplevel=None, graph=None, session=None):
tl = existingtoplevel or Toplevel()
tl.title(name)
lastSaved = [None]
setOnce = [False]
graphSetTime = [0]
def setPosFromGraphOnce():
"""
the graph is probably initially empty, but as soon as it gives
us one window position, we stop reading them
"""
if setOnce[0]:
return
geo = graph.value(session, L9.windowGeometry)
log.debug("setPosFromGraphOnce %s", geo)
setOnce[0] = True
graphSetTime[0] = time.time()
if geo is not None and geo != lastSaved[0]:
tl.geometry(geo)
lastSaved[0] = geo
def savePos(ev):
geo = tl.geometry()
if not isinstance(ev.widget, (Tk, tkinter.Tk)):
# I think these are due to internal widget size changes,
# not the toplevel changing
return
# this is trying to not save all the startup automatic window
# sizes. I don't have a better plan for this yet.
if graphSetTime[0] == 0 or time.time() < graphSetTime[0] + 3:
return
if not setOnce[0]:
return
lastSaved[0] = geo
log.debug("saving position %s", geo)
graph.patchObject(session, session, L9.windowGeometry, Literal(geo))
if graph is not None and session is not None:
graph.addHandler(setPosFromGraphOnce)
if name in windowlocations:
tl.geometry(positionOnCurrentDesktop(windowlocations[name]))
if graph is not None:
tl._toplevelat_funcid = tl.bind("<Configure>",
lambda ev,tl=tl,name=name: savePos(ev))
return tl
def positionOnCurrentDesktop(xform, screenWidth=1920, screenHeight=1440):
size, x, y = xform.split('+')
x = int(x) % screenWidth
y = int(y) % screenHeight
return "%s+%s+%s" % (size, x, y)
def toggle_slider(s):
if s.get() == 0:
s.set(100)
else:
s.set(0)
# for lambda callbacks
def printout(t):
print('printout', t)
def printevent(ev):
for k in dir(ev):
if not k.startswith('__'):
print('ev', k, getattr(ev,k))
def eventtoparent(ev,sequence):
"passes an event to the parent, screws up TixComboBoxes"
wid_class = str(ev.widget.__class__)
if wid_class == 'Tix.ComboBox' or wid_class == 'Tix.TixSubWidget':
return
evdict={}
for x in ['state', 'time', 'y', 'x', 'serial']:
evdict[x]=getattr(ev,x)
# evdict['button']=ev.num
par=ev.widget.winfo_parent()
if par!=".":
ev.widget.nametowidget(par).event_generate(sequence,**evdict)
#else the event made it all the way to the top, unhandled
def colorlabel(label):
"""color a label based on its own text"""
txt=label['text'] or "0"
lev=float(txt)/100
low=(80,80,180)
high=(255,55,0o50)
out = [int(l+lev*(h-l)) for h,l in zip(high,low)]
col="#%02X%02X%02X" % tuple(out)
label.config(bg=col)
# TODO: get everyone to use this
def colorfade(low, high, percent):
'''not foolproof. make sure 0 < percent < 1'''
out = [int(l+percent*(h-l)) for h,l in zip(high,low)]
col="#%02X%02X%02X" % tuple(out)
return col
def colortotuple(anytkobj, colorname):
'pass any tk object and a color name, like "yellow"'
rgb = anytkobj.winfo_rgb(colorname)
return [v / 256 for v in rgb]
class Togglebutton(Button):
"""works like a single radiobutton, but it's a button so the
label's on the button face, not to the side. the optional command
callback is called on button set, not on unset. takes a variable
just like a checkbutton"""
def __init__(self,parent,variable=None,command=None,downcolor='red',**kw):
self.oldcommand = command
Button.__init__(self,parent,command=self.invoke,**kw)
self._origbkg = self.cget('bg')
self.downcolor = downcolor
self._variable = variable
if self._variable:
self._variable.trace('w',self._varchanged)
self._setstate(self._variable.get())
else:
self._setstate(0)
self.bind("<Return>",self.invoke)
self.bind("<1>",self.invoke)
self.bind("<space>",self.invoke)
def _varchanged(self,*args):
self._setstate(self._variable.get())
def invoke(self,*ev):
if self._variable:
self._variable.set(not self.state)
else:
self._setstate(not self.state)
if self.oldcommand and self.state: # call command only when state goes to 1
self.oldcommand()
return "break"
def _setstate(self,newstate):
self.state = newstate
if newstate: # set
self.config(bg=self.downcolor,relief='sunken')
else: # unset
self.config(bg=self._origbkg,relief='raised')
return "break"
class FancyDoubleVar(DoubleVar):
def __init__(self,master=None):
DoubleVar.__init__(self,master)
self.callbacklist = {} # cbname : mode
self.namedtraces = {} # name : cbname
def trace_variable(self,mode,callback):
"""Define a trace callback for the variable.
MODE is one of "r", "w", "u" for read, write, undefine.
CALLBACK must be a function which is called when
the variable is read, written or undefined.
Return the name of the callback.
"""
cbname = self._master._register(callback)
self._tk.call("trace", "variable", self._name, mode, cbname)
# we build a list of the trace callbacks (the py functrions and the tcl functionnames)
self.callbacklist[cbname] = mode
# print "added trace:",callback,cbname
return cbname
trace=trace_variable
def disable_traces(self):
for cb,mode in list(self.callbacklist.items()):
# DoubleVar.trace_vdelete(self,v[0],k)
self._tk.call("trace", "vdelete", self._name, mode,cb)
# but no master delete!
def recreate_traces(self):
for cb,mode in list(self.callbacklist.items()):
# self.trace_variable(v[0],v[1])
self._tk.call("trace", "variable", self._name, mode,cb)
def trace_named(self, name, callback):
if name in self.namedtraces:
print("FancyDoubleVar: already had a trace named %s - replacing it" % name)
self.delete_named(name)
cbname = self.trace_variable('w',callback) # this will register in self.callbacklist too
self.namedtraces[name] = cbname
return cbname
def delete_named(self, name):
if name in self.namedtraces:
cbname = self.namedtraces[name]
self.trace_vdelete('w',cbname)
#self._tk.call("trace","vdelete",self._name,'w',cbname)
print("FancyDoubleVar: successfully deleted trace named %s" % name)
else:
print("FancyDoubleVar: attempted to delete named %s which wasn't set to any function" % name)
def get_selection(listbox):
'Given a listbox, returns first selection as integer'
selection = int(listbox.curselection()[0]) # blech
return selection
if __name__=='__main__':
root=Tk()
root.tk_focusFollowsMouse()
iv=IntVar()
def cb():
print("cb!")
t = Togglebutton(root,text="testbutton",command=cb,variable=iv)
t.pack()
Entry(root,textvariable=iv).pack()
root.mainloop()
|