簡體   English   中英

為Tkinter中的一組小部件添加滾動條

[英]Adding a scrollbar to a group of widgets in Tkinter

我正在使用 Python 來解析日志文件中的條目,並使用 Tkinter 顯示條目內容,到目前為止一切都很好。 output 是 label 小部件的網格,但有時行數多於屏幕上可以顯示的行數。 我想加一個滾動條,看起來應該很容易,但我想不通。

文檔暗示只有列表、文本框、Canvas 和條目小部件支持滾動條界面。 這些似乎都不適合顯示小部件網格。 可以在 Canvas 小部件中放置任意小部件,但您似乎必須使用絕對坐標,所以我無法使用網格布局管理器?

我試過將小部件網格放入框架中,但這似乎不支持滾動條界面,所以這不起作用:

mainframe = Frame(root, yscrollcommand=scrollbar.set)

任何人都可以提出解決此限制的方法嗎? 我不想不得不在 PyQt 中重寫並將我的可執行圖像大小增加這么多,只是為了添加一個滾動條!

概述<\/h2>

您只能將滾動條與一些小部件相關聯,並且根小部件和Frame<\/code>不屬於該小部件組。

至少有幾種方法可以做到這一點。 如果您需要一組簡單的垂直或水平小部件,您可以使用文本小部件和window_create<\/code>方法來添加小部件。 這種方法很簡單,但不允許小部件的復雜布局。

一個更常見的通用解決方案是創建一個畫布小部件並將滾動條與該小部件相關聯。 然后,在該畫布中嵌入包含標簽小部件的框架。 確定框架的寬度\/高度並將其輸入到畫布scrollregion<\/code>選項中,以使滾動區域與框架的大小完全匹配。

為什么將小部件放在框架中而不是直接放在畫布中? 附加到畫布的滾動條只能滾動使用create_<\/code>方法之一創建的項目。 您不能使用pack<\/code> 、 place<\/code>或grid<\/code>滾動添加到畫布的項目。 通過使用框架,您可以在框架內使用這些方法,然后為框架調用一次create_window<\/code> 。

直接在畫布上繪制文本項並不難,因此如果框架嵌入在畫布中的解決方案看起來過於復雜,您可能需要重新考慮這種方法。 由於您正在創建一個網格,因此每個文本項的坐標將非常容易計算,特別是如果每​​一行的高度相同(如果您使用單一字體,則可能是這樣)。

要直接在畫布上繪圖,只需弄清楚您正在使用的字體的行高(並且有相應的命令)。 然后,每個 y 坐標為row*(lineheight+spacing)<\/code> 。 x 坐標將是基於每列中最寬項的固定數字。 如果您為所有內容為其所在的列指定標簽,則可以使用單個命令調整列中所有項目的 x 坐標和寬度。

面向對象的解決方案<\/h2>

這是使用面向對象方法的框架嵌入畫布解決方案的示例:

程序解決方案<\/h2>

這是一個不使用類的解決方案:

使其可滾動<\/h1>

使用這個方便的類使包含您的小部件的框架可滾動。 跟着這些步驟:

  1. 創建框架<\/li>
  2. 顯示它(包、網格等)<\/li>
  3. 使其可滾動<\/li>
  4. 在其中添加小部件<\/li>
  5. 調用 update() 方法<\/li><\/ol>
     self.canvas.config(scrollregion=self.canvas.bbox(self.windows_item))


    使用示例<\/h2>
    root = tk.Tk() header = ttk.Frame(root) body = ttk.Frame(root) footer = ttk.Frame(root) header.pack() body.pack() footer.pack() ttk.Label(header, text="The header").pack() ttk.Label(footer, text="The Footer").pack() scrollable_body = Scrollable(body, width=32) for i in range(30): ttk.Button(scrollable_body, text="I'm a button in the scrollable frame").grid() scrollable_body.update() root.mainloop()<\/code><\/pre>"

擴展類tk.Frame以支持可滾動的 Frame
此類獨立於要滾動的小部件,可用於替換標准tk.Frame

在此處輸入圖像描述


import tkinter as tk

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, **kwargs):
        tk.Frame.__init__(self, parent, **kwargs)

        # The Scrollbar, layout to the right
        vsb = tk.Scrollbar(self, orient="vertical")
        vsb.pack(side="right", fill="y")

        # The Canvas which supports the Scrollbar Interface, layout to the left
        self.canvas = tk.Canvas(self, borderwidth=0, background="#ffffff")
        self.canvas.pack(side="left", fill="both", expand=True)

        # Bind the Scrollbar to the self.canvas Scrollbar Interface
        self.canvas.configure(yscrollcommand=vsb.set)
        vsb.configure(command=self.canvas.yview)

        # The Frame to be scrolled, layout into the canvas
        # 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((4, 4), window=self.scrolled_frame, anchor="nw")

        # Configures the scrollregion of the Canvas dynamically
        self.scrolled_frame.bind("<Configure>", self.on_configure)

    def on_configure(self, event):
        """Set the scroll region to encompass the scrolled frame"""
        self.canvas.configure(scrollregion=self.canvas.bbox("all"))

用法:

class App(tk.Tk):
    def __init__(self):
        super().__init__()

        sbf = ScrollbarFrame(self)
        self.grid_rowconfigure(0, weight=1)
        self.grid_columnconfigure(0, weight=1)
        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)


if __name__ == "__main__":
    App().mainloop()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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