How do i stop an event from being processed or switch what function is called for it?
Revised Code:
from Tkinter import *
class GUI
The answer provided by Bryan generally works well, but as arcra has underlined, it might not. If you experience the problem of not being able to correctly unbind a stacked callback, modifying the official source - if it is still the same! - might be a solution.
Here follow my 2 cents for those who still find themselves stuck with the problem: please, override the unbind() method, DO NOT EDIT IT DIRECTLY.
This way, in fact, you do not need to manually change the official source code on your workstation (thus breaking the package management, or reintroducing the problem at the next package update, or having the same issue on another client, ...):
import tkinter as tk
class PatchedCanvas(tk.Canvas):
def unbind(self, sequence, funcid=None):
'''
See:
http://stackoverflow.com/questions/6433369/
deleting-and-changing-a-tkinter-event-binding-in-python
'''
if not funcid:
self.tk.call('bind', self._w, sequence, '')
return
func_callbacks = self.tk.call(
'bind', self._w, sequence, None).split('\n')
new_callbacks = [
l for l in func_callbacks if l[6:6 + len(funcid)] != funcid]
self.tk.call('bind', self._w, sequence, '\n'.join(new_callbacks))
self.deletecommand(funcid)
Then, instead of instantiating your failing widget (in my example I use the Canvas) like this
myCanvas = tk.Canvas(...)
you will simply instantiate it from your patched version that will need an update if and only if the official source will be updated and fixed:
myCanvas = PatchedCanvas(...)
The unbind method is currently defined in the Misc class, from which BaseWidget inherits it and then, consequenty, Widget, TopLevel, Button, ...
You can simply just call bind()
again with the new function for the event. Since you are not making use of the third parameter, add
, in bind()
this will just overwrite whatever is already there. By default this parameter is ''
but it also accepts "+"
, which will add a callback to the callbacks already triggered by that event.
If you start using that optional argument however you will need to use the unbind()
function to remove individual callbacks. When you call bind()
a funcid
is returned. You can pass this funcid
as the second parameter to unbind()
.
Example:
self.btn_funcid = self.DrawArea.bind("<Button 1>", self.my_button_callback, "+")
# Then some time later, to remove just the 'my_button_callback':
self.DrawArea.unbind("<Button 1>", self.btn_funcid)
# But if you want to remove all of the callbacks for the event:
self.DrawArea.unbind("<Button 1>")
For me, unbinding a single callback wasn't working, but I found a solution.
I can see this is an old question, but for those who, like myself, find this question when facing the same problem, this is what I did to make it work.
You will need to open the source file Tkinter.py and search for the unbind method of the Misc class (if you are using eclipse it's easy to know the file's location and the line in which this function is defined by pressing the F3 key when the cursor is over an .unbind function call in your code).
When you find it, you should see something like this:
def unbind(self, sequence, funcid=None):
"""Unbind for this widget for event SEQUENCE the
function identified with FUNCID."""
self.tk.call('bind', self._w, sequence, '')
if funcid:
self.deletecommand(funcid)
You need to change it to look somethins like this:
def unbind(self, sequence, funcid=None):
"""Unbind for this widget for event SEQUENCE the
function identified with FUNCID."""
if not funcid:
self.tk.call('bind', self._w, sequence, '')
return
func_callbacks = self.tk.call('bind', self._w, sequence, None).split('\n')
new_callbacks = [l for l in func_callbacks if l[6:6 + len(funcid)] != funcid]
self.tk.call('bind', self._w, sequence, '\n'.join(new_callbacks))
self.deletecommand(funcid)
That should do the trick!