tkinter:将鼠标滚轮绑定到滚动条

Posted

技术标签:

【中文标题】tkinter:将鼠标滚轮绑定到滚动条【英文标题】:tkinter: binding mousewheel to scrollbar 【发布时间】:2013-06-25 16:58:28 【问题描述】:

我有这个可滚动的框架(实际上是画布内的框架)。

import Tkinter as tk
class Scrollbarframe():
    def __init__(self, parent,xsize,ysize,xcod,ycod):
        def ScrollAll(event):
                canvas1.configure(scrollregion=canvas1.bbox("all"),width=xsize,height=ysize,bg='white')
        self.parent=parent
        self.frame1=tk.Frame(parent,bg='white')
        self.frame1.place(x=xcod,y=ycod)
        canvas1=tk.Canvas(self.frame1)
        self.frame2=tk.Frame(canvas1,bg='white',relief='groove',bd=1,width=1230,height=430)
        scrollbar1=tk.Scrollbar(self.frame1,orient="vertical",command=canvas1.yview)
        canvas1.configure(yscrollcommand=scrollbar1.set)
        scrollbar1.pack(side="right",fill="y")
        canvas1.pack(side="left")
        canvas1.create_window((0,0),window=self.frame2,anchor='nw')
        self.frame2.bind("<Configure>",ScrollAll)

我想将鼠标滚轮绑定到滚动条,以便用户可以向下滚动框架,而无需使用滚动条上的箭头按钮。环顾四周后,我像这样为我的canvas1 添加了绑定

self.frame1.bind("<MouseWheel>", self.OnMouseWheel)

这是函数:

def OnMouseWheel(self,event):
    self.scrollbar1.yview("scroll",event.delta,"units")
    return "break" 

但是当我使用鼠标滚轮时滚动条不会移动。谁能帮我这个?我想要的是当用户使用鼠标滚轮(在框架区域内/滚动条上)时,画布应该自动向上或向下滚动。

【问题讨论】:

【参考方案1】:

也许最简单的解决方案是为鼠标滚轮进行全局绑定。然后无论鼠标下方的小部件或键盘焦点是哪个小部件,它都会触发。然后,您可以无条件地滚动画布,或者您可以聪明地找出应该滚动哪个窗口。

例如,在 Windows 上,您会执行以下操作:

self.canvas = Canvas(...)
self.canvas.bind_all("<MouseWheel>", self._on_mousewheel)
...
def _on_mousewheel(self, event):
    self.canvas.yview_scroll(-1*(event.delta/120), "units")

请注意,self.canvas.bind_all 有点误导——您更正确地应该调用root.bind_all,但我不知道您定义什么或如何定义您的根窗口。无论如何,这两个调用是同义词。

平台差异:

在 Windows 上,您绑定到 &lt;MouseWheel&gt;,您需要将 event.delta 除以 120(或其他一些因素,具体取决于您希望滚动的速度) 在 OSX 上,您绑定到 &lt;MouseWheel&gt; 并且您需要使用 event.delta 而无需修改 在 X11 系统上,您需要绑定到 &lt;Button-4&gt;&lt;Button-5&gt;,并且您需要将 event.delta 除以 120(或其他因素,具体取决于您想要滚动的速度)

还有更精细的解决方案,涉及虚拟事件并确定哪个窗口具有焦点或位于鼠标下方,或者通过绑定传递画布窗口引用,但希望这可以帮助您入门。

【讨论】:

我尝试使用它(此处为 linux)但无法使其工作,直到我注意到 - 我想知道为什么 - event.delta 始终为零。通过简单地调用 yview_scroll(direction,"units") 解决它 @Bryan Oakley - 如果应用程序中只有一个滚动画布,上述方法可以正常工作。但是如果有两个或更多,你怎么能限制滚动到一个或另一个? @JDM:您可以使用winfo_containing 找出光标下的画布,然后滚动该画布。 @BryanOakley:好的,我想我明白了。我从另一个方向着手,使用小部件的 事件来触发 .bind_all 和 .unbind 调用。真正的麻烦在于弄清楚为什么 Tkinter 接受了 .bind_all 的回调,但抱怨它需要一个字符串来代替 .unbind。 (我已经排除了全局 unbind 或 unbind_all,因为我不想破坏可能存在的其他绑定。)无论如何,经过大量搜索,我终于找到了一篇显示正确字符串语法的文章:mail.python.org/pipermail//tkinter-discuss/2012-May/003152.html跨度> 根据您对self.canvas.bind_all('&lt;MouseWheel&gt;', lambda event: self.canvas.yview_scroll(int(-1*(event.delta/120)), "units")) 的回答,我在此单行代码中发现了很多价值。使用 Python 3.4,如果数字未转换为 int,解释器将抛出 _tkinter.TclError: expected integer but got "1.0"【参考方案2】:

根据@BryanOakley 的回答,这是一种仅滚动焦点小部件(即当前鼠标光标所在的小部件)的方法。

绑定到位于画布内的可滚动框架上发生的&lt;Enter&gt;&lt;Leave&gt; 事件,方法如下(scrollframe 是画布内的框架):

    ...

    self.scrollframe.bind('<Enter>', self._bound_to_mousewheel)
    self.scrollframe.bind('<Leave>', self._unbound_to_mousewheel)

    return None

def _bound_to_mousewheel(self, event):
    self.canv.bind_all("<MouseWheel>", self._on_mousewheel)

def _unbound_to_mousewheel(self, event):
    self.canv.unbind_all("<MouseWheel>")

def _on_mousewheel(self, event):
    self.canv.yview_scroll(int(-1*(event.delta/120)), "units")

【讨论】:

这应该是选择的答案,因为它提供了大多数应用程序都需要的有针对性的方法。 谢谢,这种方法对我有用。即使鼠标没有悬停在其上方,我的可滚动框架也在滚动,这将我的树视图滚动在它旁边的列中,它有自己的内置滚动。 优秀。为我工作。 正如上文提到的 Bryan,您始终可以解析事件并决定是处理还是忽略。该事件包含一个小部件项,因此您可以说 if event.widget in list_of_widgets_to_process: 优秀(为我工作)。如果有水平滚动条,一个明显的改进(但不是要求)是对水平滚动条做同样的事情(这对我很有用)。只需在_bound_to_mousewheel 方法中添加对"&lt;Shift-MouseWheel&gt;" 的绑定,并为此事件添加另一个方法。【参考方案3】:

此链接为您提供了如何使用滚轮的示例。

http://www.daniweb.com/software-development/python/code/217059/using-the-mouse-wheel-with-tkinter-python

我希望这会有所帮助!

# explore the mouse wheel with the Tkinter GUI toolkit
# Windows and Linux generate different events
# tested with Python25
import Tkinter as tk
def mouse_wheel(event):
    global count
    # respond to Linux or Windows wheel event
    if event.num == 5 or event.delta == -120:
        count -= 1
    if event.num == 4 or event.delta == 120:
        count += 1
    label['text'] = count
count = 0
root = tk.Tk()
root.title('turn mouse wheel')
root['bg'] = 'darkgreen'
# with Windows OS
root.bind("<MouseWheel>", mouse_wheel)
# with Linux OS
root.bind("<Button-4>", mouse_wheel)
root.bind("<Button-5>", mouse_wheel)
label = tk.Label(root, font=('courier', 18, 'bold'), width=10)
label.pack(padx=40, pady=40)
root.mainloop()

【讨论】:

很好,工作示例。只需在 Py3 上用 tkinter 替换 Tkinter 如果此链接失效,此答案将毫无用处。 "Always quote the most relevant part of an important link, in case the target site is unreachable or goes permanently offline." 请编辑您的问题以避免这种情况。【参考方案4】:

为了摆脱奇怪的因素 120,我们可以只看 event.delta 值的符号。这使得在 Windows、Linux 和 Mac OS 下使用相同的处理程序变得容易。

# Mouse wheel handler for Mac, Windows and Linux
# Windows, Mac: Binding to <MouseWheel> is being used
# Linux: Binding to <Button-4> and <Button-5> is being used

def MouseWheelHandler(event):
    global count

    def delta(event):
        if event.num == 5 or event.delta < 0:
            return -1 
        return 1 

    count += delta(event)
    print(count)

import tkinter
root = tkinter.Tk()
count = 0
root.bind("<MouseWheel>",MouseWheelHandler)
root.bind("<Button-4>",MouseWheelHandler)
root.bind("<Button-5>",MouseWheelHandler)
root.mainloop()

【讨论】:

【参考方案5】:

如果你有兴趣

如何同时滚动两个列表框

#listbox scrollbar

from tkinter import *
root = Tk()

def scrolllistbox2(event):
    listbox2.yview_scroll(int(-1*(event.delta/120)), "units")


scrollbar = Scrollbar(root)
#scrollbar.pack(side=RIGHT, fill=Y)
listbox = Listbox(root)
listbox.pack()
for i in range(100):
    listbox.insert(END, i)
# attach listbox to scrollbar
listbox.config(yscrollcommand=scrollbar.set)
listbox.bind("<MouseWheel>", scrolllistbox2)

listbox2 = Listbox(root)
listbox2.pack()
for i in range(100):
    listbox2.insert(END, i+100)
listbox2.config(yscrollcommand=scrollbar.set)

#scrollbar.config(command=listbox.yview)

root.mainloop()

或者...

from tkinter import *
root = Tk()
root.geometry("400x400")
def scrolllistbox(event):
    ''' scrolling both listbox '''
    listbox2.yview_scroll(int(-1*(event.delta/120)), "units")
    listbox1.yview_scroll(int(-1*(event.delta/120)), "units")


def random_insert():
    ''' adding some numbers to the listboxes '''
    for i in range(100):
        listbox1.insert(END, i)
        listbox2.insert(END, i + 100)

# SCROLLBAR
scrollbar = Scrollbar(root)
#scrollbar.pack(side=RIGHT, fill=Y)

# LISTBOX 1
listbox1 = Listbox(root)
listbox1.pack()
# attach listbox to scrollbar with yscrollcommand
# listbox1.config(yscrollcommand=scrollbar.set)

# The second one
listbox2 = Listbox(root)
listbox2.pack()
listbox2.config(yscrollcommand=scrollbar.set)
# scroll the first one when you're on the second one
# listbox2.bind("<MouseWheel>", scrolllistbox)
root.bind("<MouseWheel>", scrolllistbox)

# scroll also the second list when you're on the first
listbox1.bind("<MouseWheel>", scrolllistbox)

random_insert()
#scrollbar.config(command=listbox.yview)

root.mainloop()

【讨论】:

【参考方案6】:

作为对上述内容的补充,“增量”比例因子很容易计算,因为平台信息可通过 sysplatform 模块(可能还有其他模块)获得。

def my_mousewheel_handler(event):
    if sys.platform == 'darwin': # for OS X # also, if platform.system() == 'Darwin':
        delta = event.delta
    else:                            # for Windows, Linux
        delta = event.delta // 120   # event.delta is some multiple of 120
    if event.widget in (widget1, widget2, ):
        'do some really cool stuff...'

【讨论】:

以上是关于tkinter:将鼠标滚轮绑定到滚动条的主要内容,如果未能解决你的问题,请参考以下文章

如何将鼠标滚轮滚动添加到垂直滚动条或滚动区域?

网页不能用鼠标滚轮控制滚动条 我鼠标滚轮上下滚动。。但网页没反应。这是怎么回事。。怎么修复。。

用鼠标滚轮拖动滚动条的时候老自动往上去

ulli鼠标滚轮水平滚动

如何区分手动滚动(通过鼠标滚轮/滚动条)和 Javascript/jQuery 滚动?

JS怎么禁止鼠标滚轮的单击 IE下! 注意是滚轮去单击 而不是滚动!