Tkinter unbinding

Discussion in 'Python' started by Roger, Dec 18, 2008.

  1. Roger

    Roger Guest

    I've done a lot of googling for this topic and I fear that it's not
    possible. I have a widget that is overloaded with several bindings.
    I want to be able to unbind one method form the same Event without
    destroying all the other bindings to the same event that's associated
    to the same widget.

    For example:

    import Tkinter

    def test():
    print 'test'

    def test2():
    print 'test2'

    root = Tkinter.Tk()
    funcid1 = root.bind("<1>", lambda e: test())
    funcid2 = root.bind("<1>", lambda e: test2(), add='+')
    root.unbind("<1>", funcid2)
    root.mainloop()

    When run neither <1> binding will exist against the root because the
    unbind will unbind all the functions associated with that event.
    However, in this example, I only want to unbind test2 not test1.

    Any help is greatly appreciated. Thanks!
    Roger.
     
    Roger, Dec 18, 2008
    #1
    1. Advertising

  2. Roger

    r Guest

    w.unbind ( sequence, funcid=None )
    This method deletes bindings on w for the event described by sequence.
    If the second argument is a callback bound to that sequence, that
    callback is removed and the rest, if any, are left in place. If the
    second argument is omitted, all bindings are deleted.

    see
    http://infohost.nmt.edu/tcc/help/pubs/tkinter/universal.html
     
    r, Dec 18, 2008
    #2
    1. Advertising

  3. Roger

    Roger Guest

    >funcid1 = root.bind("<1>", lambda e: test())
    >funcid2 = root.bind("<1>", lambda e: test2(), add='+')
    >root.unbind("<1>", funcid2)


    Isn't this what I've done in my example code?
     
    Roger, Dec 18, 2008
    #3
  4. Roger

    r Guest

    Yea, my answer was really not a helping answer(sorry) just showing
    exactly why this will not work with w.unbind(). Why do you need two
    separate functions to bind the same event?? You cannot combine the
    two??
     
    r, Dec 18, 2008
    #4
  5. Roger

    Roger Guest

    On Dec 18, 11:40 am, r <> wrote:
    > Yea, my answer was really not a helping answer(sorry) just showing
    > exactly why this will not work with w.unbind(). Why do you need two
    > separate functions to bind the same event?? You cannot combine the
    > two??


    I can't combine the two in my app unfortunately. The binding is to a
    custom widget that upon it being destroyed the binding is no longer
    valid. I can work around this by being hacky but I prefer to delete
    the one binding itself which would make things cleaner.
     
    Roger, Dec 18, 2008
    #5
  6. Roger

    r Guest

    Maybe someone will chime in with an answer, sorry i could not help.
    ponder this, i must...
     
    r, Dec 18, 2008
    #6
  7. Roger

    Roger Guest

    On Dec 18, 12:49 pm, r <> wrote:
    > Maybe someone will chime in with an answer, sorry i could not help.
    > ponder this, i must...


    Regardless, thanks for your help! I truly appreciate it.

    Roger.
     
    Roger, Dec 18, 2008
    #7
  8. Roger

    Roger Guest

    On Dec 18, 12:49 pm, r <> wrote:
    > Maybe someone will chime in with an answer, sorry i could not help.
    > ponder this, i must...


    Regardless, thanks for your help! I truly appreciate it.

    Roger.
     
    Roger, Dec 18, 2008
    #8
  9. Roger

    Roger Guest

    On Dec 18, 12:49 pm, r <> wrote:
    > Maybe someone will chime in with an answer, sorry i could not help.
    > ponder this, i must...


    Regardless, thanks for your help! I truly appreciate it.

    Roger.
     
    Roger, Dec 18, 2008
    #9
  10. Roger

    Roger Guest

    On Dec 18, 12:49 pm, r <> wrote:
    > Maybe someone will chime in with an answer, sorry i could not help.
    > ponder this, i must...


    Regardless, thanks for your help! I truly appreciate it.

    Roger.
     
    Roger, Dec 18, 2008
    #10
  11. Roger

    r Guest

    On Dec 18, 1:48 pm, Roger <> wrote:
    > On Dec 18, 12:49 pm, r <> wrote:
    >
    > > Maybe someone will chime in with an answer, sorry i could not help.
    > > ponder this, i must...

    >
    > Regardless, thanks for your help! I truly appreciate it.
    >
    > Roger.


    'no problema mi amigo!'.to_english(no problem my friend!) :)
     
    r, Dec 18, 2008
    #11
  12. Roger wrote:

    > I've done a lot of googling for this topic and I fear that it's not
    > possible. I have a widget that is overloaded with several bindings.
    > I want to be able to unbind one method form the same Event without
    > destroying all the other bindings to the same event that's associated
    > to the same widget.
    >
    > For example:
    >
    > import Tkinter
    >
    > def test():
    > print 'test'
    >
    > def test2():
    > print 'test2'
    >
    > root = Tkinter.Tk()
    > funcid1 = root.bind("<1>", lambda e: test())
    > funcid2 = root.bind("<1>", lambda e: test2(), add='+')
    > root.unbind("<1>", funcid2)
    > root.mainloop()
    >
    > When run neither <1> binding will exist against the root because the
    > unbind will unbind all the functions associated with that event.
    > However, in this example, I only want to unbind test2 not test1.
    >
    > Any help is greatly appreciated. Thanks!
    > Roger.


    I believe you've discovered a bug. Aside from recommending trying
    wxWidgets, here's the source of the unbind function in Tkinter.py:

    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)

    -------------------------------------------
    First, it replaces all bindings for the sequence with the empty string,
    i.e., it deletes all bindings for that event unconditionally. THEN it
    calls deletecommand() with the funcid, who knows what that does. My Tcl
    is not so sharp.
    I have an idea for a workaround, let me see if it works...
    -Chuckk
     
    Bad Mutha Hubbard, Dec 19, 2008
    #12
  13. Bad Mutha Hubbard wrote:

    > Roger wrote:
    >
    >> I've done a lot of googling for this topic and I fear that it's not
    >> possible. I have a widget that is overloaded with several bindings.
    >> I want to be able to unbind one method form the same Event without
    >> destroying all the other bindings to the same event that's associated
    >> to the same widget.
    >>
    >> For example:
    >>
    >> import Tkinter
    >>
    >> def test():
    >> print 'test'
    >>
    >> def test2():
    >> print 'test2'
    >>
    >> root = Tkinter.Tk()
    >> funcid1 = root.bind("<1>", lambda e: test())
    >> funcid2 = root.bind("<1>", lambda e: test2(), add='+')
    >> root.unbind("<1>", funcid2)
    >> root.mainloop()
    >>
    >> When run neither <1> binding will exist against the root because the
    >> unbind will unbind all the functions associated with that event.
    >> However, in this example, I only want to unbind test2 not test1.
    >>
    >> Any help is greatly appreciated. Thanks!
    >> Roger.

    >
    > I believe you've discovered a bug. Aside from recommending trying
    > wxWidgets, here's the source of the unbind function in Tkinter.py:
    >
    > 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)
    >
    > -------------------------------------------
    > First, it replaces all bindings for the sequence with the empty string,
    > i.e., it deletes all bindings for that event unconditionally. THEN it
    > calls deletecommand() with the funcid, who knows what that does. My Tcl
    > is not so sharp.
    > I have an idea for a workaround, let me see if it works...
    > -Chuckk


    Alas, my workaround doesn't work either. Tkinter.py also states that
    calling bind with only an event sequence will return all bindings for
    that sequence; I was thinking I could then remove the function in
    question from that list and call bind again with each of the
    functions in the remaning list as argument. I had
    high hopes. The return value of calling bind with no target function
    is just about Tcl nonsense:

    #!/usr/bin/env python

    import Tkinter

    def test(event):
    print 'test'

    def test2(event):
    print 'test2'

    root = Tkinter.Tk()
    funcid1 = root.bind("<1>", test)
    funcid2 = root.bind("<1>", test2, add='+')
    print funcid1, funcid2

    bound = root.bind('<Button-1>')
    print "bound:", bound
    #root.unbind("<1>", funcid=funcid2)
    root.mainloop()

    ---------------------------
    Note that I took out the lambdas and gave event arguments to the
    functions; if you did that on purpose, because you need to call the same
    functions without events, then just ignore that...
    SO, the other workaround, which I've used, is to bind the event to a
    generic function, and have that generic function conditionally call
    the functions you want. I'll go back and try to make an example from
    your example. -Chuckk
     
    Bad Mutha Hubbard, Dec 19, 2008
    #13
  14. Roger

    Roger Guest

    > Note that I took out the lambdas and gave event arguments to the
    > functions; if you did that on purpose, because you need to call the same
    > functions without events, then just ignore that...
    > SO, the other workaround, which I've used, is to bind the event to a
    > generic function, and have that generic function conditionally call
    > the functions you want. I'll go back and try to make an example from
    > your example. -Chuckk


    Thanks Chuckk! You've done exactly what I did so far. I went through
    the source in Tkinter.py and had an inkling of what the unbind
    function was doing. I believe, and please correct me if I'm wrong,
    in: self.tk.call('bind', self._w, sequence, '') , the '' is unbinding
    all methods and I believe the self.deletecommand(funcid) is a
    workaround for a memory leak otherwise. Perhaps self.tk.call('bind',
    self._w, sequence, funcid) would work but that's a pure guess. I
    would have liked to investigate the tcl source directly to see if I
    could develop a workaround through a tk.call() but that was hitting a
    wall in terms of any documentation I could research. I'm interested
    in any workaround you may have however!

    You now, I really considered going over to wxwidgets, and I definitely
    want to try it after the current app I'm developing is complete, but
    I've so much experience with Tkinter now after banging my head against
    a wall for months (productive months mind you) on various projects,
    it's like an old persnickety friend you just can't give up. =)

    Thanks a ton!
    Roger.
     
    Roger, Dec 19, 2008
    #14
  15. Roger

    Peter Otten Guest

    Roger wrote:

    >> Note that I took out the lambdas and gave event arguments to the
    >> functions; if you did that on purpose, because you need to call the same
    >> functions without events, then just ignore that...
    >> SO, the other workaround, which I've used, is to bind the event to a
    >> generic function, and have that generic function conditionally call
    >> the functions you want. I'll go back and try to make an example from
    >> your example. -Chuckk

    >
    > Thanks Chuckk! You've done exactly what I did so far. I went through
    > the source in Tkinter.py and had an inkling of what the unbind
    > function was doing. I believe, and please correct me if I'm wrong,
    > in: self.tk.call('bind', self._w, sequence, '') , the '' is unbinding
    > all methods and I believe the self.deletecommand(funcid) is a
    > workaround for a memory leak otherwise. Perhaps self.tk.call('bind',
    > self._w, sequence, funcid) would work but that's a pure guess. I
    > would have liked to investigate the tcl source directly to see if I
    > could develop a workaround through a tk.call() but that was hitting a
    > wall in terms of any documentation I could research. I'm interested
    > in any workaround you may have however!


    The documentation for bind in tcl is here

    http://www.tcl.tk/man/tcl8.4/TkCmd/bind.htm

    and as far as I understand it doesnt support unbinding selected callbacks,
    either. I'd suggest a plain-python workaround along the lines of

    import Tkinter

    def test(event):
    print 'test'

    def test2(event):
    print 'test2'

    root = Tkinter.Tk()
    root.geometry("200x100+100+100")

    class Multiplexer:
    def __init__(self):
    self.funcs = []
    def __call__(self, event):
    for f in self.funcs:
    f(event)
    def add(self, f):
    self.funcs.append(f)
    return f
    def remove(self, f):
    self.funcs.remove(f)

    m = Multiplexer()
    m.add(test)
    m.add(test2)
    root.bind("<1>", m)

    def unbind():
    print "unbind"
    m.remove(test2)

    button = Tkinter.Button(root, text="unbind test2", command=unbind)
    button.pack()

    root.mainloop()

    Peter
     
    Peter Otten, Dec 19, 2008
    #15
  16. Roger

    Roger Guest

    > either. I'd suggest a plain-python workaround along the lines of

    Wow. You just blew my mind. I'm going to play with this. Thanks a
    lot, I've really learned a lot in just that small bit. I don't have
    much experience in playing with a lot of the 'private' calls such as
    __call__. I need to do some more reading.

    Roger.
     
    Roger, Dec 19, 2008
    #16
  17. Roger

    Peter Otten Guest

    Roger wrote:

    >> either. I'd suggest a plain-python workaround along the lines of

    >
    > Wow. You just blew my mind. I'm going to play with this. Thanks a
    > lot, I've really learned a lot in just that small bit. I don't have
    > much experience in playing with a lot of the 'private' calls such as
    > __call__. I need to do some more reading.


    Here's a non-OO variant:

    funcs = []
    def call(event):
    for f in list(funcs): # *
    f(event)
    root.bind("<1>", call)

    funcs.append(test)
    funcs.append(test2)
    root.bind("<1>", call)

    def unbind():
    print "unbind"
    funcs.remove(test2)

    I does the same, but is a bit harder to manage if you have more than one
    event/widget to deal with.

    (*) iterating over a copy of the list of functions is slightly more robust
    as it will not accidentally skip callbacks when the original list is
    modified during iteration. I suggest that you change the OO version
    accordingly.

    Peter
     
    Peter Otten, Dec 19, 2008
    #17
  18. Roger

    fabien

    Joined:
    Dec 4, 2011
    Messages:
    1
    my way to handle tcl nonsense is just to copy it...

    Here is a workaround bypassing the bug in Tkinter and calling tk directly.

    Code:
    #!/usr/bin/env python                                                                                          
    
    import Tkinter
    
    def unbind(widget, sequence, funcid):
      '''unbind funcid callback for sequence on widget                                                             
                                                                                                                   
      preserve other binding in case of multiple bindings.                                                         
      '''
      #construct a string containing existing binding minus funcid one                                             
      remain_bindings = "\n".join([e for e in widget.bind(sequence).split("\n") if e and e.find(funcid) == -1 ])
      widget.tk.call('bind', widget, sequence,remain_bindings)
    
    def test(event):
      print 'test'
    
    def test2(event):
      print 'test2'
    
    root = Tkinter.Tk()
    funcid1 = root.bind("<1>", test)
    funcid2 = root.bind("<1>", test2, add='+')
    print funcid1, funcid2
    
    #replace                                                                                                       
    #root.unbind("<1>", funcid=funcid2)                                                                            
    #by                                                                                                            
    unbind(root,"<1>", funcid=funcid2)
    
    root.mainloop()
    
     
    fabien, Dec 4, 2011
    #18
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Sin

    Re: UNbinding a socket

    Sin, Jun 23, 2003, in forum: C++
    Replies:
    2
    Views:
    13,878
  2. Michael Furman

    Re: (OT) UNbinding a socket

    Michael Furman, Jun 23, 2003, in forum: C++
    Replies:
    0
    Views:
    1,427
    Michael Furman
    Jun 23, 2003
  3. Richard Spooner

    Unbinding sockets in threads..

    Richard Spooner, Aug 3, 2004, in forum: Python
    Replies:
    0
    Views:
    348
    Richard Spooner
    Aug 3, 2004
  4. Johnny Lin

    Unbinding multiple variables

    Johnny Lin, Jan 21, 2005, in forum: Python
    Replies:
    11
    Views:
    627
    Johnny Lin
    Jan 24, 2005
  5. Roger
    Replies:
    7
    Views:
    718
    Roger
    Jan 13, 2009
Loading...

Share This Page