[英]Tkinter Scrollbar on scrollable frame scrolls at 2x speed when it shouldn't
我正在尝试使用 windows 使用此问题中找到的代码在 Tkinter 中创建一个可滚动的框架。 我还想让框架可以使用鼠标滚轮滚动,所以我从这个问题复制了 _on_mousewheel() function 。 现在的问题是,由于鼠标滚轮绑定到 canvas,on_mousewheel() function 使滚动条以 2 倍的速度滚动,当在其上使用鼠标滚轮时,它是正常速度。 第二个问题的答案提到了这个问题,但没有说明如何解决。
代码:
import tkinter as tk
from tkinter import *
from PIL import Image, ImageTk
SCROLL_CANVAS_H = 500
SCROLL_CANVAS_W = 1000
class ScrollbarFrame(tk.Frame):
"""
Extends class tk.Frame to support a scrollable Frame
This class is independent from the widgets to be scrolled and
can be used to replace a standard tk.Frame
"""
def __init__(self, parent, height, width):
""" Initiates a scrollable frame with labels using specified
height and width. Canvas is scrollable both over canvas and scrollbar.
"""
super().__init__(parent)
self.height = height
self.width = width
# Place the scrollbar on self, layout to the right
self.v_scrollbar = tk.Scrollbar(self, orient="vertical")
self.v_scrollbar.pack(side="right", fill="y")
# The Canvas which supports the Scrollbar Interface,
# placed on self and layed out to the left.
self.canvas = tk.Canvas(self, borderwidth=0, background="#ffffff",
height = height,
width = width)
self.canvas.pack(side="left", fill="both", expand=True)
# Attach scrollbar action to scroll of canvas
self.canvas.configure(yscrollcommand=self.v_scrollbar.set)
self.v_scrollbar.configure(command=self.canvas.yview)
## Allow canvas to be scrolled using mousewheel while hovering
## over the canvas region.
#self.canvas.bind_all("<MouseWheel>", self._on_mousewheel)
# Place a frame on the canvas, this frame will hold the child widgets
# All widgets to be scrolled have to use this Frame as parent
self.scrolled_frame = tk.Frame(self.canvas, background=self.canvas.cget('bg'))
self.canvas.create_window((0, 0), window=self.scrolled_frame, anchor="nw")
self.canvas.bind_all('<MouseWheel>', self.on_mousewheel)
# Configures the scrollregion of the Canvas dynamically
self.scrolled_frame.bind("<Configure>", self.on_configure)
# Reset the scroll region to encompass the inner frame
self.scrolled_frame.bind("<Configure>", self.on_frame_configure)
def on_configure(self, event):
"""Set the scroll region to encompass the scrolled frame"""
self.canvas.configure(scrollregion=self.canvas.bbox("all"))
def on_mousewheel(self, event):
"""Allows canvas to be scrolled using mousewheel while hovering
over canvas.
"""
self.canvas.yview_scroll(-1 * (event.delta // 120), "units")
def on_frame_configure(self, event):
"""Reset the scroll region to encompass the inner frame"""
self.canvas.configure(scrollregion=self.canvas.bbox("all"))
class App(tk.Tk):
def __init__(self):
#initializes self as root
tk.Tk.__init__(self)
# add a new scrollable frame
sbf = ScrollbarFrame(self, height = 500, width = 1000)
sbf.grid(row=0, column=0, sticky='nsew')
#sbf.pack(side="top", fill="both", expand=True)
# Some data, layout into the sbf.scrolled_frame
frame = sbf.scrolled_frame
for row in range(50):
text = "%s" % row
tk.Label(frame, text=text,
width=3, borderwidth="1", relief="solid") \
.grid(row=row, column=0)
text = "this is the second column for row %s" % row
tk.Label(frame, text=text,
background=sbf.scrolled_frame.cget('bg')) \
.grid(row=row, column=1)
text = "this is the third column for row %s" % row
tk.Label(frame, text=text,
background=sbf.scrolled_frame.cget('bg')) \
.grid(row=row, column=2)
if __name__ == "__main__":
root = App()
root.mainloop()
在 on_mousewheel() 方法中,我尝试确定是 canvas 还是使用鼠标滚轮的滚动条,然后使用 if 语句,以便 canvas 只会滚动鼠标滚轮不在滚动条上方。 但事实并非如此,滚动条在滚动时仍会以 2 倍速度滚动。
if event.widget != self.v_scrollbar:
self.canvas.yview_scroll(-1 * (event.delta // 120), "units")
我还尝试将滚动条的移动除以更大的值
if event.widget == self.v_scrollbar:
self.canvas.yview_scroll(-1 * (event.delta // 240), "units")
else:
self.canvas.yview_scroll(-1 * (event.delta // 120), "units")
但这也不起作用。 有任何想法吗?
代替yview_scroll
,使用yview_moveto
进行更精细的控制。 .02
值可能应该与可以根据具体情况进行调整的属性(例如:滚动速度)相关联。 使用此方法无需检查您是否在滚动条或 canvas 上。
def on_mousewheel(self, event):
"""Allows canvas to be scrolled using mousewheel while hovering over canvas.
"""
n = -event.delta / abs(event.delta) #1 inversion
p = self.v_scrollbar.get()[0] + (n*.02) #return scrollbar position and adjust it by a fraction
self.canvas.yview_moveto(p) #apply new position
这是一道基本的数学题。 当您调用self.canvas.yview_scroll
时,您是在告诉它要滚动多少个单位。 units取决于小部件,并且是yscrollincrement
选项的值。 如果该选项的值为 0,则该值被视为小部件高度的 1/10。
您在任何时候滚动的像素越多,滚动的速度就越快。 您只需要对传递给yview_scroll
方法的值进行基本数学运算和/或您可以将yscrollincrement
值更改为一个小的 integer > 0 以准确说明一个“单位”有多少像素。
请注意,所有平台上的event.delta
值都不相同。 在 OSX 上,该值是您应该滚动的单位数(即:不需要数学)。 在 Windows 系统上,该值始终至少为 120,因此您需要将该值除以 120 以获得要滚动的实际“单位”数。
最重要的是,您可以通过改变传递给yview_scroll
的数字和/或您正在滚动的小部件的yscrollincrement
选项的值来控制滚动速度。
如果您要说的是在 canvas 上滚动工作正常,但在滚动条上以双倍速度滚动,那可能是由于滚动条的内置行为。 可能发生的情况是您的 function 运行导致它滚动,然后默认行为运行导致它再次滚动。 您可以检查您的 function 是否仅在 canvas 上运行,或者您可以让您的 function 返回字符串“break”,以防止发生默认绑定。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.