簡體   English   中英

Tkinter 可滾動框架上的滾動條在不應該以 2 倍速度滾動時

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

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM