3

I define GCanvas, an extension of Canvas. My intention is to bind to GCanvas at the class level. It isn't working.

I also tried to bind to tk.Canvas and it doesn't work either. Binding to root or to the GCanvas instance works fine. (Neither of this alternatives is useful to me, but I just tried them to see what happened.). Running OS X, El Capitan.

import Tkinter as tk

class GCanvas(tk.Canvas, object):

    def __init__(self, master, **kwargs):
        tk.Canvas.__init__(self, master, kwargs)

    @staticmethod
    def enter(e):
        print "enter", e.widget, e.x, e.y

    @staticmethod
    def leave(e):
        print "leave", e.widget

    @staticmethod
    def motion(e):
        print "motion", e.widget, e.x, e.y

approach = "bindinstance"

root = tk.Tk()
gc = GCanvas(root, width=400, height=300)
print "root is", root, "gc is", gc
gc.pack()

if approach == "bindGCanvas":
    print "binding to GCanvas"
    root.bind_class(GCanvas, '<Enter>', GCanvas.enter)
    root.bind_class(GCanvas, '<Leave>', GCanvas.leave)
    #root.bind_class(GCanvas, '<Motion>', GCanvas.motion)
elif approach == "bindCanvas":
    print "binding to Canvas"
    root.bind_class(tk.Canvas, '<Enter>', GCanvas.enter)
    root.bind_class(tk.Canvas, '<Leave>', GCanvas.leave)
    #root.bind_class(tk.Canvas, '<Motion>', GCanvas.motion)
elif approach == "bindinstance":
    print "binding to instance"
    gc.bind('<Enter>', GCanvas.enter)
    gc.bind('<Leave>', GCanvas.leave)
    #gc.bind('<Motion>', GCanvas.motion)
else:
    print "binding to root"
    root.bind('<Enter>', GCanvas.enter)
    root.bind('<Leave>', GCanvas.leave)
    #root.bind('<Motion>', GCanvas.motion)

root.mainloop()
Eduardo
  • 1,235
  • 16
  • 27

2 Answers2

6

The "class" in bind_class refers to the internal class name used by the tk library, not the python class name. More precisely, in this context it refers to a bind tag, which happens to be the same name as the tk class, which also happens to be the same name as one of the core Tkinter classes (eg: Toplevel, Canvas, etc).

To bind to GCanvas at the class level, the simplest thing would be to add a bind tag named GCanvas to your canvas, as in the following example:

class GCanvas(tk.Canvas, object):
    def __init__(self, master, **kwargs):
        ...
        # get the current bind tags
        bindtags = list(self.bindtags())

        # add our custom bind tag before the Canvas bind tag
        index = bindtags.index("Canvas")
        bindtags.insert(index, "GCanvas")

        # save the bind tags back to the widget
        self.bindtags(tuple(bindtags))

You can then use bind_class like so:

root.bind_class("GCanvas", "<Enter>", GCanvas.enter)
root.bind_class("GCanvas", "<Leave>", GCanvas.leave)

For more information about bind tags, see these answers to some other tkinter questions:

Community
  • 1
  • 1
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
0

An express solution I found to resolve a binding conflict between two text() boxes. Listing the bindtags to sort the path of the widget. The first occurrence in list. The binding was triggered on both widgets at the same time.

self.frame2 = Frame(self)
self.frame2.pack()
self.textDomain = Text(self.frame2, width=21, height=10, wrap=WORD, borderwidth=2, relief=GROOVE)
self.textDomain.pack()
self.bindtags = list(self.textDomain.bindtags())
self.textDomain.bind_class(str(self.bindtags[0]), "<Return>", lambda _: self.verifytextBox("textDomain"))
self.textDomain.bind_class(str(self.bindtags[0]), "<Leave>", lambda _: self.verifytextBox("textDomain"))
self.textExtension = Text(self.frame2, width=21, height=10, wrap=WORD, borderwidth=2, relief=GROOVE)
self.textExtension.pack()
self.bindtags = list(self.textExtension.bindtags())
self.textExtension.bind_class(str(self.bindtags[0]), "<Return>", lambda _: self.verifytextBox("textExtension"))
self.textExtension.bind_class(str(self.bindtags[0]), "<Leave>", lambda _: self.verifytextBox("textExtension"))
Wolf
  • 9,679
  • 7
  • 62
  • 108