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

2025-02-21 08:48:00
admin
原创
24
摘要:问题描述:我有这个可滚动的框架(实际上是画布内的框架)。import Tkinter as tk class Scrollbarframe(): def __init__(self, parent,xsize,ysize,xcod,ycod): def ScrollAll(event)...

问题描述:

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

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(int(-1*(event.delta/120)), "units")

请注意,这self.canvas.bind_all有点误导——更正确的做法是调用,root.bind_all但我不知道您定义根窗口的内容或方式。无论如何,这两个调用是同义词。

平台差异:

  • 在 Windows 上,您绑定到<MouseWheel>并需要event.delta除以 120(或根据您希望滚动的速度计算的其他因子)

  • 在 OSX 上,您绑定到并且无需修改<MouseWheel>即可使用event.delta

  • 在 X11 系统上,您需要绑定到<Button-4><Button-5>,并且需要除以event.delta120(或根据您想要滚动的速度计算的其他因子)

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

编辑:在较新的 Python 版本中,canvas.yview_scroll需要一个整数(参见:pathName yview scroll number what

解决方案 2:

根据@BryanOakley 的回答,这里有一种仅滚动焦点小部件(即鼠标光标当前位于其上的小部件)的方法。

绑定到位于画布内的可滚动框架<Enter>并发生事件,方式如下(是画布内的框架):<Leave>`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")

解决方案 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()

解决方案 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:

作为上述内容的补充,“delta”比例因子很容易计算,因为可以通过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...'

解决方案 6:

如果你感兴趣

如何同时滚动2个列表框

#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()

解决方案 7:

这种方法在Linux上对我有用:

canvas.bind('<Button-4>', lambda e: canvas.yview_scroll(int(-1*e.num), 'units'))
canvas.bind('<Button-5>', lambda e: canvas.yview_scroll(int(e.num), 'units'))

解决方案 8:

Mikhail T. 的回答对我来说非常有效。以下可能是其他人可能觉得有用的更通用的设置(我真的需要开始回馈)

def _setup_mousewheel(self,frame,canvas):
    frame.bind('<Enter>', lambda *args, passed=canvas: self._bound_to_mousewheel(*args,passed))
    frame.bind('<Leave>', lambda *args, passed=canvas: self._unbound_to_mousewheel(*args,passed))

def _bound_to_mousewheel(self, event, canvas):
    canvas.bind_all("<MouseWheel>", lambda *args, passed=canvas: self._on_mousewheel(*args,passed))

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

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

然后设置一个画布/框架用于鼠标滚轮滚动只需:

self._setup_mousewheel(frame, canvas)

解决方案 9:

def onmousewheel(widget, command):
    widget.bind("<Enter>", lambda _: widget.bind_all('<MouseWheel>',command ))
    widget.bind("<Leave>", lambda _: widget.unbind_all('<MouseWheel>'))

onmousewheel(canvas, lambda e:  canvas.yview_scroll(int(-1*(e.delta)), "units"))

只需滚动您想要的框架的紧凑解决方案。

感谢所有分享解决方案的人。

解决方案 10:

self.canvas = Canvas(...)
self.canvas.bind_all("<MouseWheel>", lambda event: self._on_mousewheel(where_to_scroll= (-1 * event.delta)))
...
def _on_mousewheel(self, where_to_scroll):
        self.canvas.yview("scroll", where_to_scroll, "units")

bind_all 意味着 = <“发生时您想要针对的事情”>,然后执行某些操作()

1- 我们希望目标鼠标滚轮 = <“MouseWheel”>

2 - 我们希望滚动到使用鼠标滚轮时鼠标滚轮移动的位置 = lambda 事件:self._on_mousewheel(where_to_scroll=(-1 * event.delta))

当我们使用 bind_all 或 bind 方法时,它们会给我们一个变量。因此,我们使用 lambda 来获取它并在函数内部使用它

该变量具有属性,其中一个属性是 delta,它给出鼠标滚轮方向的整数值

我把它乘以 -1,因为 delta 给了我错误的方向,我想是因为我 macOS 系统,如果给了你错误的方向,试试像我一样把它乘以 -1

“更新”

我在画布内创建了一个框架,然后在新框架上使用 bind,而不是在画布上使用 bind_all,因为之前出现了问题,每次我使用鼠标滚轮时,它都会调用 _on_mousewheel() 方法,即使我不在画布内。因此,创建一个框架并使用 bind 为我解决了这个问题

喜欢这个教程 - https://www.youtube.com/watch?v=0WafQCaok6g

解决方案 11:

上面评论中有一个人遇到了 MouseWheelevent.delta未填充的问题。我遇到了同样的问题,因此滚动只能以单位步长递增。虽然这确实有效,但对于我的应用程序来说太慢了。

为了解决这个问题,我不得不采取以下措施:

# mouse wheel callback for scrolling
...
self.prevtime = 0
self.sliceinc = 0
...
def mousewheel(self,event,key=None):

    if key is None:
        # if events are separated by < 100 msec, accumulate and return
        if abs(event.time-self.prevtime) < 100:
            if event.num == 4:
                self.sliceinc += 1
            elif event.num == 5:
                self.sliceinc -= 1
            self.prevtime = event.time
            if abs(self.sliceinc) == 5:
                self.canvas.get_tk_widget().event_generate('<<MyMouseWheel>>',when="tail",x=event.x,y=event.y)
            return
        # otherwise, just process the event and update gui
        else:
            self.prevtime = event.time
            if event.num == 4:
                self.sliceinc = 1
            elif event.num == 5:
                self.sliceinc = -1

    newslice = self.currentslice.get() + self.sliceinc

    self.currentslice.set(newslice)
    self.doGuiUpdates()

    # when the virtual event callback finally happens, re-initialize
    if key == 'Key':
        self.prevtime = 0
        self.sliceinc = 0

绑定如下:

    self.canvas.get_tk_widget().bind('<<MyMouseWheel>>',EventCallback(self.mousewheel,key='Key'))

EventCallback 是一个用于将 kwargs 传递给回调的便捷类:

class EventCallback():
    def __init__(self, callback, *args, **kwargs):
        self.callback = callback
        self.args = args
        self.kwargs = kwargs

    def __call__(self,event):
        return self.callback(event,*self.args, **self.kwargs)

简而言之,MouseWheel 事件是在不更新 GUI 的情况下累积的,并且在处理完所有待处理的 MouseWheel 事件后会生成一个虚拟事件来运行,该事件将累积总数作为对 GUI 的更新一次性应用。

解决方案 12:

我正在试验,找到了这个简单的方法。就像我们使用 Up 和 Down 绑定一样

SCROLL_STEP = 100

self.window.bind("<Down>", self.scroll_down)
self.window.bind("<Up>", self.scroll_up)

def scroll_down(self, e):
    self.scroll += SCROLL_STEP
    self.draw()

def scroll_up(self, e):
    if self.scroll > 0:
        self.scroll -= SCROLL_STEP
        self.draw()

我们可以在 Mousescroll 的情况下使用相同的方法,像这样

# Linux: Binding to <Button-4> and <Button-5> is being used
self.canvas.bind('<Button-4>', self.scroll_up)
self.canvas.bind('<Button-5>', self.scroll_down) 

def scroll_down(self, e):
    self.scroll += SCROLL_STEP
    self.draw()

def scroll_up(self, e):
    if self.scroll > 0:
        self.scroll -= SCROLL_STEP
        self.draw()

开始时 self.scroll = 0。

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1343  
  信创产业的蓬勃发展推动着各行业数字化转型加速,数据库迁移作为其中关键一环,面临诸多挑战。信创数据库迁移旨在将传统数据库平稳过渡到信创环境,以满足自主可控、安全可靠的需求。这一过程涉及技术、业务等多方面因素,稍有不慎就可能出现各种问题,影响业务的正常运行。深入探讨信创数据库迁移过程中的常见问题及解决方案,对于保障迁移工作...
2027年信创国产化   41  
  随着信息技术的飞速发展,信创国产化成为了国家战略的重要组成部分。国产化信创产品名录涵盖了众多领域,其在各个关键应用场景中发挥着重要作用。而信创国产化操作系统作为其中的核心环节,具备五大核心优势,为我国信息技术产业的自主可控发展提供了坚实支撑。关键应用场景之办公领域在办公领域,国产化信创产品有着广泛且深入的应用。如今,越...
国产信创系统   37  
  随着信息技术的飞速发展,信创国产化操作系统在政府部门的推广应用具有重要的战略意义。它不仅关乎国家信息安全,更是推动国内信息技术产业自主创新、实现科技自立自强的关键举措。在当前复杂的国际形势下,政府部门积极推广信创国产化操作系统,对于保障国家政务信息的安全稳定运行,提升信息技术的自主可控能力,具有不可替代的重要作用。推广...
信创产品有哪些   28  
  在企业数字化转型的进程中,信创数据库解决方案的选择至关重要。它不仅关乎企业数据的安全存储与管理,更影响着企业业务的稳定运行与未来发展。合适的信创数据库能够助力企业在复杂多变的市场环境中提升竞争力,保障数据主权与安全。然而,面对市场上众多的信创数据库产品和解决方案,企业往往感到困惑,不知如何做出正确的选择。接下来,我们将...
信创电脑   24  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用