[英]Tkinter scrollbar works only with create_window
I have a panedwindow and, in the right pane, I have a frame in a canvas that I want to scroll.我有一个窗格窗口,在右侧窗格中,我在画布中有一个要滚动的框架。 I can get it to scroll only if I call the canvas
create_window
function.只有当我调用画布
create_window
函数时,我才能让它滚动。 When I call the create_window
everything works fine, except the content frame does not expand and I have set the grid to sticky=nsew
.当我调用
create_window
一切正常,除了内容框架没有展开并且我已将网格设置为sticky=nsew
。 In the example code there is a var called x, if x is set to 1 then the frame expands and the scrollbars show up correctly but they don't work, if set to 0 the scrollbars work but the frame does not expand.在示例代码中有一个名为 x 的变量,如果 x 设置为 1,则框架展开并且滚动条正确显示但它们不起作用,如果设置为 0 滚动条工作但框架不展开。 I need the scrollbars to work if x is set to 1 or 0. You will need to resize the window to see this issue, notice that the separator widget expands to fill the frame but the scrollbars don't scroll anything when x is set to 1.
如果 x 设置为 1 或 0,我需要滚动条工作。您需要调整窗口大小以查看此问题,请注意分隔符小部件会扩展以填充框架,但当 x 设置为时滚动条不会滚动任何内容1.
I'm just about there I have it working 99% just one little glitch.我就在那里我让它工作 99% 只是一个小故障。 The separator widget does not expand to fill the content frame when the window height is shorter then the content and the window width is wider then content.
当窗口高度小于内容且窗口宽度大于内容时,分隔符小部件不会扩展以填充内容框架。 I have updated the code and added background color to highlight the issue.
我更新了代码并添加了背景颜色以突出显示问题。
import tkinter as tk
import tkinter.ttk as ttk
lorem_ipsum = 'Lorem ipsum dolor sit amet, luctus non. Litora viverra ligula'
class Scrollbar(ttk.Scrollbar):
def __init__(self, parent, canvas, **kwargs):
ttk.Scrollbar.__init__(self, parent, **kwargs)
command = canvas.xview if kwargs.get('orient', tk.VERTICAL) == tk.HORIZONTAL else canvas.yview
self.configure(command=command)
def set(self, low, high):
if float(low) > 0 or float(high) < 1:
self.grid()
else:
self.grid_remove()
ttk.Scrollbar.set(self, low, high)
class App(tk.Tk):
def __init__(self):
super().__init__()
self.rowconfigure(0, weight=1)
self.columnconfigure(0, weight=1)
self.title('Paned Window Demo')
self.geometry('420x200')
style = ttk.Style()
style.theme_use('clam')
style.configure('TPanedwindow', background='black')
pw = ttk.PanedWindow(self, orient=tk.HORIZONTAL)
left_frame = ttk.Frame(pw)
right_frame = ttk.Frame(pw)
ttk.Label(left_frame, text='Left Pane').grid()
left_frame.rowconfigure(0, weight=1)
left_frame.columnconfigure(0, weight=1)
left_frame.grid(sticky=tk.NSEW)
right_frame.rowconfigure(0, weight=1)
right_frame.columnconfigure(0, weight=1)
right_frame.grid(sticky=tk.NSEW)
pw.add(left_frame)
pw.add(right_frame)
pw.grid(sticky=tk.NSEW)
canvas = tk.Canvas(right_frame, bg=style.lookup('TFrame', 'background'))
canvas.frame = ttk.Frame(canvas)
canvas.rowconfigure(0, weight=1)
canvas.columnconfigure(0, weight=1)
canvas.grid(sticky=tk.NSEW)
canvas.frame.rowconfigure(990, weight=1)
canvas.frame.columnconfigure(0, weight=1)
canvas.frame.grid(sticky=tk.NSEW)
content = tk.Frame(canvas.frame, bg='blue')
content.rowconfigure(0, weight=1)
content.columnconfigure(0, weight=1)
content.grid(sticky=tk.NSEW)
xscroll = Scrollbar(right_frame, canvas, orient=tk.HORIZONTAL)
yscroll = Scrollbar(right_frame, canvas, orient=tk.VERTICAL)
xscroll.grid(row=990, column=0, sticky=tk.EW)
yscroll.grid(row=0, column=990, sticky=tk.NS)
for idx in range(1, 11):
tk.Label(content, bg='#aaaaaa', fg='#000000', text=f'{idx} {lorem_ipsum}').grid(sticky=tk.NW)
ttk.Separator(content, orient=tk.HORIZONTAL).grid(pady=10, sticky=tk.EW)
for idx in range(11, 21):
tk.Label(content, bg='#aaaaaa', fg='#000000', text=f'{idx} {lorem_ipsum}').grid(sticky=tk.NW)
self.window = canvas.create_window((0, 0), window=canvas.frame, anchor=tk.NW)
self.update_idletasks()
pw.sashpos(0, newpos=100)
def update_canvas(event):
content.update_idletasks()
_, _, width, height = content.bbox(tk.ALL)
if event.width < width or event.height < height:
if not self.window:
self.window = canvas.create_window((0, 0), window=canvas.frame, anchor=tk.NW)
else:
self.window = None
canvas.frame.grid(sticky=tk.NSEW)
canvas.bind('<Configure>', update_canvas)
canvas.configure(scrollregion=content.bbox(tk.ALL))
canvas.configure(xscrollcommand=xscroll.set, yscrollcommand=yscroll.set)
def main():
app = App()
app.mainloop()
if __name__ == '__main__':
main()
It is perfectly normal that the scrollbars only work with canvas.create_window()
since this is the adequate method to display a widget inside a canvas so that the canvas is aware of the widget's size.滚动条仅与
canvas.create_window()
一起使用是完全正常的,因为这是在画布内显示小部件的适当方法,以便画布知道小部件的大小。 Otherwise you are just using the canvas as a frame.否则,您只是将画布用作框架。 However, the
canvas.create_window()
method does not have a sticky
option so you have to manually change the width of the widget when the canvas changes size.但是,
canvas.create_window()
方法没有sticky
选项,因此您必须在画布更改大小时手动更改小部件的宽度。
Therefore, in your update_canvas
callback, you need to change the width of the window each time the canvas changes width if the canvas's width is larger than the content's required width:因此,在您的
update_canvas
回调中,如果画布的宽度大于内容所需的宽度,则每次画布更改宽度时都需要更改窗口的宽度:
def update_canvas(event):
# if the new canvas's width (event.width) is larger than the content's
# minimum width (content.winfo_reqwidth()) then make canvas.frame the
# same width as the canvas
if event.width > content.winfo_reqwidth():
canvas.itemconfigure(self.window, width=event.width)
Moreover there are a few useless lines in your code, in particular you don't have to grid canvas.frame
since you display it with canvas.create_window()
, see below:此外,您的代码中有一些无用的行,特别是您不必网格
canvas.frame
因为您使用canvas.create_window()
显示它,见下文:
class App(tk.Tk):
def __init__(self):
super().__init__()
self.rowconfigure(0, weight=1)
self.columnconfigure(0, weight=1)
self.title('Paned Window Demo')
self.geometry('420x200')
style = ttk.Style()
style.theme_use('clam')
style.configure('TPanedwindow', background='black')
pw = ttk.PanedWindow(self, orient=tk.HORIZONTAL)
left_frame = ttk.Frame(pw)
right_frame = ttk.Frame(pw)
ttk.Label(left_frame, text='Left Pane').grid()
left_frame.rowconfigure(0, weight=1)
left_frame.columnconfigure(0, weight=1)
left_frame.grid(sticky=tk.NSEW)
right_frame.rowconfigure(0, weight=1)
right_frame.columnconfigure(0, weight=1)
right_frame.grid(sticky=tk.NSEW)
pw.add(left_frame)
pw.add(right_frame)
pw.grid(sticky=tk.NSEW)
canvas = tk.Canvas(right_frame, bg=style.lookup('TFrame', 'background'))
canvas.frame = ttk.Frame(canvas)
# canvas.rowconfigure(0, weight=1) -> useless since no widget will be gridded in the canvas
# canvas.columnconfigure(0, weight=1) -> useless since no widget will be gridded in the canvas
canvas.grid(sticky=tk.NSEW)
canvas.frame.rowconfigure(990, weight=1)
canvas.frame.columnconfigure(0, weight=1)
# canvas.frame.grid(sticky=tk.NSEW) -> no need to grid canvas.frame since it is displayed it with canvas.create_window()
content = tk.Frame(canvas.frame, bg='blue')
content.rowconfigure(0, weight=1)
content.columnconfigure(0, weight=1)
content.grid(sticky=tk.NSEW)
xscroll = Scrollbar(right_frame, canvas, orient=tk.HORIZONTAL)
yscroll = Scrollbar(right_frame, canvas, orient=tk.VERTICAL)
xscroll.grid(row=990, column=0, sticky=tk.EW)
yscroll.grid(row=0, column=990, sticky=tk.NS)
for idx in range(1, 11):
tk.Label(content, bg='#aaaaaa', fg='#000000', text=f'{idx} {lorem_ipsum}').grid(sticky=tk.NW)
ttk.Separator(content, orient=tk.HORIZONTAL).grid(pady=10, sticky=tk.EW)
for idx in range(11, 21):
tk.Label(content, bg='#aaaaaa', fg='#000000', text=f'{idx} {lorem_ipsum}').grid(sticky=tk.NW)
self.window = canvas.create_window((0, 0), window=canvas.frame, anchor=tk.NW)
self.update_idletasks()
pw.sashpos(0, newpos=100)
def update_canvas(event):
# if the new canvas's width (event.width) is larger than the content's
# minimum width (content.winfo_reqwidth()) then make canvas.frame the
# same width as the canvas
if event.width > content.winfo_reqwidth():
canvas.itemconfigure(self.window, width=event.width)
canvas.bind('<Configure>', update_canvas)
canvas.configure(scrollregion=content.bbox(tk.ALL))
canvas.configure(xscrollcommand=xscroll.set, yscrollcommand=yscroll.set)
def main():
app = App()
app.mainloop()
if __name__ == '__main__':
main()
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.