Python3 tk.Scrollbar和焦点

时间:2014-11-21 07:08:33

标签: python python-3.x tkinter tk

我正在尝试组建一个UI,它必须向用户显示一长串输入选择。我已将输入字段放入带滚动条的框架中,并使用条形图滚动。但是当用户从视口中跳出标签时,滚动条不会自动跟随它。

我遇到的另一个问题是窗口没有调整大小以适应内容;如果我在self.frame.grid()之后调用populate()窗口会对内容执行一些奇怪的内容和大小,但如果调整窗口大小或内容行数超过高度,则滚动条不再有效的屏幕。我希望窗口为min(maxSizeForRows, screenHeight)并且固定宽度但不是高度。

我很感激任何帮助或指示让它发挥作用。

import tkinter as tk
import tkinter.ttk as ttk

class Test(tk.Canvas):
    def __init__(self, root):
        super().__init__(root, borderwidth=0)
        self.root = root

        self.vsb = tk.Scrollbar(root, orient="vertical", command=self.yview)
        self.configure(yscrollcommand=self.vsb.set)
        self.vsb.pack(side="right", fill="y")

        self.frame = tk.Frame(self)
        self.create_window((4,4), window=self.frame, anchor="nw", tags="self.frame")

        self.pack(side="left", fill="both", expand=True)

        self.frame.bind("<Configure>", self.onFrameConfigure)

        self.populate()


    def onFrameConfigure(self, event):
        self.configure(scrollregion=self.bbox("all"))


    def addRow(self, label, ent1, ent2):
        row = self.row

        lab = tk.Label(self.frame, text=label)
        lab.grid(row=row, column=0, sticky='W')

        sv = tk.StringVar()
        sv.set(ent1)
        e1 = tk.Entry(self.frame, width=16, justify=tk.RIGHT, textvariable=sv)
        e1.grid(row=row, column=1)

        sv = tk.StringVar()
        sv.set(ent2)
        e2 = tk.Entry(self.frame, width=16, justify=tk.RIGHT, textvariable=sv)
        e2.grid(row=row, column=2)

        self.row += 1


    def populate(self):

        self.row = 0
        for i in range(0, 10):
            self.addRow("Greeting", "Hello", "World")
            self.addRow("Parting", "Ciao", "Banana")
            self.addRow("Food", "Pepperoni", "Pizza")
            self.addRow("Drink", "Cold", "Beer")
            self.addRow("Actor", "Liam", "Neeson")
            self.addRow("Occupation", "Software", "Engineer")
            self.addRow("Languages", "Python", "Perl")
            self.addRow("Browsers", "Chrome", "Firefox")
            self.addRow("Places", "Irvine", "London")
            self.addRow("States", "Cali", "Texas")


if __name__ == "__main__":
    root = tk.Tk()
    app = Test(root=root)
    app.pack(side="top", fill="both", expand=True)
    app.mainloop()

1 个答案:

答案 0 :(得分:2)

要解决tabbed-to小部件位于视口之外的问题,您需要为条目小部件添加绑定,以便在它们获得焦点时适当地滚动画布。这只需要一点点数学。它看起来像这样:

def __init__(...):
    ...
    # this makes it possible to scroll one pixel at a time
    self.configure(yscrollincrement=1)
    ...

def addRow(...):
    ...
    e1.bind("<FocusIn>", self.scroll_into_view)
    e2.bind("<FocusIn>", self.scroll_into_view)
    ...

def scroll_into_view(self, event):
    widget_top = event.widget.winfo_y()
    widget_bottom = widget_top + event.widget.winfo_height()
    canvas_top = self.canvasy(0)
    canvas_bottom = canvas_top + self.winfo_height()

    if widget_bottom > canvas_bottom:
        # subtract 4, because the frame is at 4,4 rather than 0,0
        delta = int(canvas_bottom - widget_bottom) - 4
        self.yview_scroll(-delta, "units")
    elif widget_top < canvas_top:
        delta = int(widget_top - canvas_top) + 4
        self.yview_scroll(delta, "units")

我的数学可能有点偏差,并且有一种方法可以避免对那里的4进行硬编码,我会将其作为练习留给读者。

至于调整大小问题,如果我理解正确,那么你需要绑定画布的<Configure>事件,这样当画布大小改变时,你可以改变框架的宽度。看起来像这样:

def __init__(...):
    ...
    self.bind("<Configure>", self.onCanvasConfigure)
    ...

def onCanvasConfigure(self, event):
    # subtract 8 to account for a border/margin
    self.itemconfigure('self.frame', width=self.winfo_width()-8)

您可能需要调整常量才能获得正确的行为,但希望这可以指向正确的方向。