Make Tkinter canvas respond to MouseWheel event

WaterWalk toolmaster at 163.com
Wed Feb 28 21:08:00 EST 2007


Hello. When I tried to make Tkinter canvas widget respond to
MouseWheel event on Windows XP, I failed.  The canvas just doesn't
receive MouseWheel event. I used bind_all to find out which widget
receives this event and the result showed that only the top Tk widget
gets it. This is really annoying. I wonder if this problem exists on
other platform. I googled, but found no appliable solution, so I wrote
one as following. It's not perfect, but works well for me. Please
comment on my implementation, and if you have better solution, please
show it.

In this implementation, I bind the MouseWheel to the toplevel window
which contains the canvas.
To test if the canvas shall scroll, I check if the coordinates of the
mouse is within the canvas.

from Tkinter import *

class ScrolledCanvas(Canvas):
    def __init__(self, parent, xscroll=False, yscroll=False, *args,
**kargs):
        frm = Frame(parent)
        frm1 = Frame(frm)
        Canvas.__init__(self, frm1, *args, **kargs)
        self.pack(side=LEFT, fill=BOTH, expand=YES)

        frm1.pack(side=TOP, fill=BOTH, expand=YES)
        frm2 = Frame(frm)
        frm2.pack(side=BOTTOM, expand=YES, fill=X)

        if xscroll:
            xsbar = Scrollbar(frm2)
            xsbar.config(orient=HORIZONTAL)
            xsbar.config(command=self.xview)
            self.config(xscrollcommand=xsbar.set)
            xsbar.pack(fill=BOTH, expand=YES)
            self.winfo_toplevel().bind('<Control-MouseWheel>',
                                        self.onMouseWheelX)
        if yscroll:
            ysbar = Scrollbar(frm1)
            ysbar.config(orient=VERTICAL)
            ysbar.config(command=self.yview)
            self.config(yscrollcommand=ysbar.set)
            ysbar.pack(side=RIGHT, fill=BOTH, expand=YES)
            self.winfo_toplevel().bind('<MouseWheel>',
self.onMouseWheelY)
        self.pack = frm.pack # because the canvas is not contained in
a frame
        self.canvasFrame = frm
    def onMouseWheelY(self, event):
        if event.widget == self.winfo_toplevel() and \
            self.bInCanvas(event.x, event.y):
            self.yview_scroll(-event.delta, UNITS)
    def onMouseWheelX(self, event):
        if event.widget == self.winfo_toplevel() and \
            self.bInCanvas(event.x, event.y):
            self.xview_scroll(-event.delta, UNITS)
    def bInCanvas(self, x, y):
        if x > self.canvasFrame.winfo_x() and \
            y > self.canvasFrame.winfo_y() and \
            x < self.canvasFrame.winfo_x() + int(self.winfo_width())
and \
            y < self.canvasFrame.winfo_y() + int(self.winfo_height()):
            return True
        return False


def test():
    top = Frame()
    top.pack(expand=YES, fill=BOTH)
    sc = ScrolledCanvas(top, xscroll=True, yscroll=True, bg='Brown',
                        relief=SUNKEN)
    sc.config(scrollregion=(0,0,1000, 1000))
    sc.config(yscrollincrement=1)
    sc.config(xscrollincrement=1)
    sc.pack()

    for i in range(10):
            sc.create_text(150, 50+(i*100), text='spam'+str(i),
fill='beige')
    top.mainloop()

if __name__ == '__main__':
    test()




More information about the Python-list mailing list