我正在尝试组建一个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()
答案 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)
您可能需要调整常量才能获得正确的行为,但希望这可以指向正确的方向。