I want to make a user interface with two vertically stacked frames:
frame1: input settingsframe2: plotting
I need lots of parameters, requiring scrollbars. I tried different classes, implementing scrollbars with frames, but they all seem to have the same problem.
The problem is that while I have a scrollbar that works, it doesnt hide the elements outside of the visible area of the frame where the canvas is attached. This means they overflow to other frames:

So you can see the elements, outside the topframe are still shown somehow - continuing on the 2nd one.
The code to comprehend the problem below. Any suggestions?
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()
I tried a lot, but nothing worked. Initially I am setting the topframe/mainframe with place using relwidth/relheight. Also the topframe in my programm is actually a notebook, but this doesnt change the overflow problem.
The problem is that tkinter uses the parent/child relationship to determine the stacking order (ie: which widgets appear top top of other widgets in the Z axis). Since you create the labels as children of the root window, they are going to be higher in the stacking order than the frame inside the canvas inside the other frame.
The simplest solution is to either make the labels a child of the inner frame, or continue to use
in_and make the labels children offrame(the instance ofVerticalScrolledFrame)For a long discussion of stacking order, see Stacking Order on the Tcler's wiki. The examples are all in tcl rather than python, but it's a pretty easy mental translation into the syntax used by tkinter.