Files
@ 17bee25a20cb
Branch filter:
Location: light9/light9/tkdnd.py
17bee25a20cb
4.8 KiB
text/x-python
reformat
Ignore-this: 9392ba9c775fe3c5997496ddfe598f09
Ignore-this: 9392ba9c775fe3c5997496ddfe598f09
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 | from glob import glob
from os.path import join, basename
from typing import Dict, Any
class TkdndEvent(object):
"""
see http://www.ellogon.org/petasis/tcltk-projects/tkdnd/tkdnd-man-page
for details on the fields
The longer attribute names (action instead of %A) were made up for
this API.
Not all attributes are visible yet, since I have not thought
through what conversions they should receive and I don't want to
unnecessarily change their types later.
"""
substitutions = {
"%A": "action",
"%b": "button",
"%D": "data",
"%m": "modifiers",
"%T": "type",
"%W": "targetWindow",
"%X": "mouseX",
"%Y": "mouseY",
}
@classmethod
def makeEvent(cls, *args):
ev = cls()
for (k, v), arg in zip(sorted(TkdndEvent.substitutions.items()), args):
setattr(ev, v, arg)
# it would be cool for this to decode text data according to the charset in the type
for attr in ['button', 'mouseX', 'mouseY']:
setattr(ev, attr, int(getattr(ev, attr)))
return (ev,)
tclSubstitutions = ' '.join(sorted(substitutions.keys()))
def __repr__(self):
return "<TkdndEvent %r>" % self.__dict__
class Hover(object):
def __init__(self, widget, style):
self.widget, self.style = widget, style
self.oldStyle: Dict[Any, Any] = {}
def set(self, ev):
for k, v in list(self.style.items()):
self.oldStyle[k] = self.widget.cget(k)
self.widget.configure(**self.style)
return ev.action
def restore(self, ev):
self.widget.configure(**self.oldStyle)
def initTkdnd(tk, tkdndBuildDir):
"""
pass the 'tk' attribute of any Tkinter object, and the top dir of
your built tkdnd package
"""
tk.call('source', join(tkdndBuildDir, 'library/tkdnd.tcl'))
for dll in glob(
join(tkdndBuildDir,
'*tkdnd*' + tk.call('info', 'sharedlibextension'))):
tk.call('tkdnd::initialise', join(tkdndBuildDir, 'library'),
join('..', basename(dll)), 'tkdnd')
def dragSourceRegister(widget, action='copy', datatype='text/uri-list',
data=''):
"""
if the 'data' param is callable, it will be called every time to
look up the current data.
If the callable returns None (or data is None to begin with), the drag
"""
widget.tk.call('tkdnd::drag_source', 'register', widget._w)
# with normal Tkinter bind(), the result of your handler isn't
# actually returned so the drag doesn't get launched. This is a
# corrected version of what bind() does when you pass a function,
# but I don't block my tuple from getting returned (as a tcl list)
def init():
dataValue = data() if callable(data) else data
if dataValue is None:
return
return (action, datatype, dataValue)
funcId = widget._register(
init,
widget._substitute,
1 # needscleanup
)
widget.bind("<<DragInitCmd>>", funcId)
def dropTargetRegister(
widget,
typeList=None,
onDropEnter=None,
onDropPosition=None,
onDropLeave=None,
onDrop=None,
hoverStyle=None,
):
"""
the optional callbacks will be called with a TkdndEvent
argument.
onDropEnter, onDropPosition, and onDrop are supposed to return an
action (perhaps the value in event.action). The return value seems
to have no effect, but it might just be that errors are getting
silenced.
Passing hoverStyle sets onDropEnter to call
widget.configure(**hoverStyle) and onDropLeave to restore the
widget's style. onDrop is also wrapped to do a restore.
"""
if hoverStyle is not None:
hover = Hover(widget, hoverStyle)
def wrappedDrop(ev):
hover.restore(ev)
if onDrop:
return onDrop(ev)
return dropTargetRegister(widget,
typeList=typeList,
onDropEnter=hover.set,
onDropLeave=hover.restore,
onDropPosition=onDropPosition,
onDrop=wrappedDrop)
if typeList is None:
typeList = ['*']
widget.tk.call(*(['tkdnd::drop_target', 'register', widget._w] + typeList))
for sequence, handler in [
('<<DropEnter>>', onDropEnter),
('<<DropPosition>>', onDropPosition),
('<<DropLeave>>', onDropLeave),
('<<Drop>>', onDrop),
]:
if not handler:
continue
func = widget._register(handler,
subst=TkdndEvent.makeEvent,
needcleanup=1)
widget.bind(sequence, func + " " + TkdndEvent.tclSubstitutions)
|