tkinter 帧与滚动条 - 元素溢出到其他帧

0 投票
1 回答
33 浏览
提问于 2025-04-14 16:06

我想做一个用户界面,里面有两个垂直堆叠的框:

  • frame1: 输入设置
  • frame2: 绘图

我需要很多参数,所以需要滚动条。我尝试了不同的类来实现带滚动条的框,但它们似乎都有同样的问题。

问题是,虽然我有一个能用的滚动条,但它并不能隐藏那些在框的可见区域之外的元素。这就导致这些元素溢出到其他框里:

picture

你可以看到,顶部框外的元素还是以某种方式显示出来了,继续出现在第二个框里。

下面是理解这个问题的代码。

有什么建议吗?

import tkinter as tk
from tkinter import ttk
from tkinter.constants import *

class VerticalScrolledFrame(ttk.Frame):
    def __init__(self,  *args, **kw):
        ttk.Frame.__init__(self,  *args, **kw)
 
        # Create a canvas object and a vertical scrollbar for scrolling it.
        vscrollbar = ttk.Scrollbar(self, orient=VERTICAL)
        vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE)
        self.canvas = tk.Canvas(self, bd=0, highlightthickness=0,  yscrollcommand=vscrollbar.set)
        self.canvas.pack(side=LEFT, fill=BOTH, expand=TRUE)
        vscrollbar.config(command = self.canvas.yview)
 
        # Reset the view
        self.canvas.xview_moveto(0)
        self.canvas.yview_moveto(0)
 
        # Create a frame inside the canvas which will be scrolled with it.
        self.interior = ttk.Frame(self.canvas)
        self.interior.bind('<Configure>', self._configure_interior)
        self.canvas.bind('<Configure>', self._configure_canvas)
        self.interior_id = self.canvas.create_window(0, 0, window=self.interior, anchor=NW)
 
 
    def _configure_interior(self, event):
        # Update the scrollbars to match the size of the inner frame.
        size = (self.interior.winfo_reqwidth(), self.interior.winfo_reqheight())
        self.canvas.config(scrollregion=(0, 0, size[0], size[1]))
        if self.interior.winfo_reqwidth() != self.canvas.winfo_width():
            # Update the canvas's width to fit the inner frame.
            self.canvas.config(width = self.interior.winfo_reqwidth())
         
    def _configure_canvas(self, event):
        if self.interior.winfo_reqwidth() != self.canvas.winfo_width():
            # Update the inner frame's width to fill the canvas.
            self.canvas.itemconfigure(self.interior_id, width=self.canvas.winfo_width())

#Create 2 vertically stacked frames and add it to the root
root = tk.Tk()
root.geometry('1280x1024')
root.resizable(width=True,height=True)
topframe =     ttk.LabelFrame(root,text='Top', width=1280, height=200) 
mainframe =    ttk.LabelFrame(root,text='Results', width=1280, height=1024-200)
topframe.grid(row=0,column=0,sticky='news')
mainframe.grid(row=1,column=0,sticky='news')

#Create scrolled frame
frame = VerticalScrolledFrame()
#Add many elements to it, that would require a scrollbar
for i in range(20):
    ttk.Label(text=f'Entry {i}').grid(in_=frame.interior,row=i,column=0,sticky='news')

#Resizing the frames
frame.grid(in_=topframe,row=0,column=0,sticky='news')
topframe.columnconfigure(0,weight=1)
topframe.rowconfigure(0,weight=1)


root.mainloop()

我尝试了很多方法,但都没用。一开始我用相对宽度和高度设置了顶部框/主框。而且我程序中的顶部框实际上是一个笔记本,但这并没有解决溢出的问题。

1 个回答

0

问题在于,tkinter使用父子关系来决定控件的叠放顺序(也就是哪些控件会在其他控件的上面)。因为你把标签创建为根窗口的子控件,所以它们在叠放顺序中会比画布里面的框架要高。

最简单的解决办法是把标签放到内框架里面,或者继续使用 in_,把标签设置为 frame(也就是 VerticalScrolledFrame 的实例)的子控件。

for i in range(20):
    ttk.Label(frame, text=f'Entry{i}').grid(in_=frame.interior,...)

如果想深入了解叠放顺序,可以查看 Tcler's wiki上的叠放顺序。虽然例子都是用tcl写的,但转换成tkinter的语法其实很简单。

撰写回答