Bokeh: Determine which model called an on_change event handler - python

I have the following scenario where I want to execute a function with on_change:
def update_func:
calling_widget = < I need the name of the widget here: "SelectorWidget" >
do_something
SelectorWidget = MultiSelect(title="A widget", value = "default", options = option_list)
SelectorWidget.on_change('value', update_func)
The same update_func will be used on different widgets, and I'd like to be able to get the name of the Widget which triggered the function each time.
Any ideas?

Try:
from functools import partial
# add a widget arg to standard callback signature
def update_func(attr, old, new, widget):
# do something with widget
SelectorWidget = MultiSelect(title="A widget",
value="default",
options=option_list)
# use partial to make a callback callable with widget arg bound
SelectorWidget.on_change('value',
partial(update_func, widget=SelectorWidget))

Related

How to send extra **Kwargs to event click method of an ipyvuetify button

I am using ipyvuetify to create a dashboard including several buttons.
here the code for one of them:
import ipyvuetify as vue
from ipywidgets import Output
out = Output()
mybtn = vue.Btn(color='primary', children=['Run'])
def btn_method(widget, event, data):
with out:
print('btn was clicked')
mybtn.on_event('click', btn_method)
display(mybtn,out)
My problem is that when clicking a button the actions carried out depend on some varaibles. The question is how can I send to the button extra data, i.e. a dictionary of data.
Pseudo code:
import ipyvuetify as vue
from ipywidgets import Output
out = Output()
mybtn = vue.Btn(color='primary', children=['Run'])
# t followoing does not work, nor it gives an error:
def btn_method(widget, event, data, **kwargs):
with out:
print('btn was clicked')
print(len(kwargs))
if myvar==4:
#do this
pass
else:
#do that
pass
mybtn.on_event('click', btn_method, kwargs={'myvar':4})
display(mybtn,out)
Of course the error is: on_event() got an unexpected keyword argument 'kwargs', i.e. you are not supposed to send other vars into the on_event method.
Any idea how to proceed?
thanks
you can simply set your dictionary as an additional attribute of the button object and read it inside the callback function.
mybtn = vue.Btn(color='primary', children=['Run'])
def btn_method(widget, event, data):
with out:
print('btn was clicked')
print(widget.kwargs)
if widget.kwargs['myvar']==4:
#do this
pass
else:
#do that
pass
mybtn.on_event('click', btn_method)
mybtn.kwargs={'myvar':4}
One possibility is to use functools.partial to make a new callback
function on-the-fly, that would have the extra args you want to pass already defined:
import functools
def btn_method(widget, event, data, **kwargs):
with out:
print('btn was clicked')
print(len(kwargs))
if kwargs.get("myvar")==4:
#do this
pass
else:
#do that
pass
mybtn.on_event('click', functools.partial(btn_method, myvar=4))
(untested code)

Using callbacks to run function using the current values in a class

I struggled to think of a good title so I'll just explain it here. I'm using Python in Maya, which has some event callback options, so you can do something like on save: run function. I have a user interface class, which I'd like it to update when certain events are triggered, which I can do, but I'm looking for a cleaner way of doing it.
Here is a basic example similar to what I have:
class test(object):
def __init__(self, x=0):
self.x = x
def run_this(self):
print self.x
def display(self):
print 'load user interface'
#Here's the main stuff that used to be just 'test().display()'
try:
callbacks = [callback1, callback2, ...]
except NameError:
pass
else:
for i in callbacks:
try:
OpenMaya.MEventMessage.removeCallback(i)
except RuntimeError:
pass
ui = test(5)
callback1 = OpenMaya.MEventMessage.addEventCallback('SomeEvent', ui.run_this)
callback2 = OpenMaya.MEventMessage.addEventCallback('SomeOtherEvent', ui.run_this)
callback3 = ......
ui.display()
The callback persists until Maya is restarted, but you can remove it using removeCallback if you pass it the value that is returned from addEventCallback. The way I have currently is just check if the variable is set before you set it, which is a lot more messy than the previous one line of test().display()
Would there be a way that I can neatly do it in the function? Something where it'd delete the old one if I ran the test class again or something similar?
There are two ways you might want to try this.
You can an have a persistent object which represents your callback manager, and allow it to hook and unhook itself.
import maya.api.OpenMaya as om
import maya.cmds as cmds
om.MEventMessage.getEventNames()
class CallbackHandler(object):
def __init__(self, cb, fn):
self.callback = cb
self.function = fn
self.id = None
def install(self):
if self.id:
print "callback is currently installed"
return False
self.id = om.MEventMessage.addEventCallback(self.callback, self.function)
return True
def uninstall(self):
if self.id:
om.MEventMessage.removeCallback(self.id)
self.id = None
return True
else:
print "callback not currently installed"
return False
def __del__(self):
self.uninstall()
def test_fn(arg):
print "callback fired 2", arg
cb = CallbackHandler('NameChanged', test_fn)
cb.install()
# callback is active
cb.uninstall()
# callback not active
cb.install()
# callback on again
del(cb) # or cb = None
# callback gone again
In this version you'd store the CallbackHandlers you create for as long as you want the callback to persist and then manually uninstall them or let them fall out of scope when you don't need them any more.
Another option would be to create your own object to represent the callbacks and then add or remove any functions you want it to trigger in your own code. This keeps the management entirely on your side instead of relying on the api, which could be good or bad depending on your needs. You'd have an Event() class which was callable (using __call__() and it would have a list of functions to fire then its' __call__() was invoked by Maya. There's an example of the kind of event handler object you'd want here

What does the "widget" do in PyGTK

The sample code is like this:
class Something(gtk.Window):
def __init__(self):
...
treeview = gtk.TreeView(store)
tree_selection = treeview.get_selection()
tree_selection.set_mode(gtk.SELECTION_SINGLE)
tree_selection.connect("changed", self.onSelectionChanged)
...
def onSelectionChanged(self, tree_selection):
(model, pathlist) = tree_selection.get_selected()
self.selectedValue = model.get_value(pathlist, 0) - 1
How was the tree_selection passed in into onSelectionChanged function? I see many other similar uses, such as:
def onDestroy(self, widget):
gtk.main_quit()
What can we use this "widget" in the second param?
When you connect a handler (like onSelectionChanged) to a signal (like "changed"), the handler is added to the end of the list of signal handlers of that object.
When the object then emits the signal, it will call handlers (or "callbacks") in this list, pasing itself as the first argument.
This is true for all signals: the first argument is allways the object emitting the signal.
This way, you can e.g. call the get_selected() method of the TreeSelection object that called your onSelectionChanged method: you access it through the tree_selection parameter.

Python - tkinter - How to capture bind function

from tkinter import StringVar, messagebox, Entry, Tk
def accept(event):
acceptInput=messagebox.askquestion("Input Assessment","do you accept this input?")
return acceptInput
window=Tk()
userInput=StringVar()
e=Entry(window,textvariable=userInput)
e.pack()
e.bind('<Return>',accept)
window.mainloop()
My question is: How do I capture the returned value of the accept function?
I've tried:
e.bind('<Return>',a=accept.get())
and
a=e.bind('<Return>',accept).get()
bound functions don't "return". Your callback needs to set a global variable or call some other function. For example:
def accept(event):
global acceptInput
acceptInput=messagebox.askquestion("Input Assessment","do you accept this input?")
... or ...
def accept(event):
acceptInput=messagebox.askquestion("Input Assessment", "do you accept this input?")
do_something(acceptInput)
It is up to you to define what you want to do in do_something (eg: write the data to disk, show an error, play a song, etc), or how you want to use the global variable in some other function.
Generally speaking, these things are easiest to accomplish if pieces of your application are instances of a class -- Then accept can just set an attribute on the class. In this case, you might want to bind that functionality up in the Entry:
class AcceptEntry(Entry):
def __init__(self, *args, **kwargs):
Entry.__init__(self, *args, **kwargs)
self.bind('<Return>', self.accept)
self.acceptInput = None
def accept(self, event):
self.acceptInput = messagebox.askquestion("Input Assessment",
"do you accept this input?")
For the function bind, the <Return> doesn't mean the return of a function. Instead, it does mean the event "Enter key" that is pressed by the user.
So, if you like to get the response from the messagebox, then you have do it with other ways. Possibly using with another StringVar() option with your messagebox, or using any global variable.

binding to cursor movement doesnt change INSERT mark

I need to perform a quick check everytime the user changes the insertion point, by arrows, mouseclick, etc... so I bound it thus:
text.bind("<Button-1>", insertchanged)
def insertchanged(event):
pos=text.index(INSERT)
n=text.tag_names(INSERT)
...
but I found out that pos is still the position before the user changed it! How do I find the new position (a general solution, if possible: I have to bind it to home,end, pgup, pgdown,...)
thank you!
There are several problems with your approach. For one, you'll need to bind to just about everything in order to track the insertion point (remember: it changes every time you insert or delete anything).
Second, changes happen to the widget based on class bindings and, by default, any bindings you create will fire before the class bindings. You can work around these issues, but it's tricky. For example, to work around the event handling order, search around this site and others for "bind tags" or "bindtags".
There is, however, an almost foolproof solution. The downside is that it requires some serious Tcl voodoo: you have to replace the internal widget with a proxy that calls a callback whenever the insertion point changes. I've included a complete working example, below.
import Tkinter as tk
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.text = CustomText(self, wrap="word")
self.text.pack(side="top", fill="both", expand=True)
self.label = tk.Label(self, anchor="w")
self.label.pack(side="bottom", fill="x")
# this is where we tell the custom widget what to call
self.text.set_callback(self.callback)
def callback(self, result, *args):
'''Updates the label with the current cursor position'''
index = self.text.index("insert")
self.label.configure(text="index: %s" % index)
class CustomText(tk.Text):
def __init__(self, *args, **kwargs):
tk.Text.__init__(self, *args, **kwargs)
# Danger Will Robinson!
# Heavy voodoo here. All widget changes happen via
# an internal Tcl command with the same name as the
# widget: all inserts, deletes, cursor changes, etc
#
# The beauty of Tcl is that we can replace that command
# with our own command. The following code does just
# that: replace the code with a proxy that calls the
# original command and then calls a callback. We
# can then do whatever we want in the callback.
private_callback = self.register(self._callback)
self.tk.eval('''
proc widget_proxy {actual_widget callback args} {
# this prevents recursion if the widget is called
# during the callback
set flag ::dont_recurse(actual_widget)
# call the real tk widget with the real args
set result [uplevel [linsert $args 0 $actual_widget]]
# call the callback and ignore errors, but only
# do so on inserts, deletes, and changes in the
# mark. Otherwise we'll call the callback way too
# often.
if {! [info exists $flag]} {
if {([lindex $args 0] in {insert replace delete}) ||
([lrange $args 0 2] == {mark set insert})} {
# the flag makes sure that whatever happens in the
# callback doesn't cause the callbacks to be called again.
set $flag 1
catch {$callback $result {*}$args } callback_result
unset -nocomplain $flag
}
}
# return the result from the real widget command
return $result
}
''')
self.tk.eval('''
rename {widget} _{widget}
interp alias {{}} ::{widget} {{}} widget_proxy _{widget} {callback}
'''.format(widget=str(self), callback=private_callback))
def _callback(self, result, *args):
self.callback(result, *args)
def set_callback(self, callable):
self.callback = callable
if __name__ == "__main__":
root = tk.Tk()
frame = Example(root)
frame.pack(side="top", fill="both", expand=True)
root.mainloop()
You can use WidgetRedirector from standard library's idlelib (https://github.com/python/cpython/blob/master/Lib/idlelib/redirector.py), which seems to do what Bryan proposed:
import tkinter as tk
from idlelib.WidgetRedirector import WidgetRedirector
root = tk.Tk()
text = tk.Text(root)
text.grid()
def on_mark(*args):
print("mark", args)
return original_mark(*args)
def on_insert(*args):
print("insert", args)
return original_insert(*args)
def on_delete(*args):
print("delete", args)
return original_delete(*args)
redirector = WidgetRedirector(text)
original_mark = redirector.register("mark", on_mark)
original_insert = redirector.register("insert", on_insert)
original_delete = redirector.register("delete", on_delete)
root.mainloop()

Categories

Resources