[英]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.