1

I am trying to make an application that displays a grid in the middle of the screen surrounded by two bars, a top bar and a bottom bar, which contain buttons for the user to press. These buttons should be able to display no matter where the user scrolls to on the grid and should not be cut off if the window is resized. I am struggling to configure the scrollbar to track the right area and to have the grid fall off the screen when the window is resized. Here is my code so far:

from tkinter import *


def add_row(event):
    input_row = Entry(grid_frame, bd=1, text="", bg="white", relief="solid")
    input_row.grid(row=grid_frame.rows, sticky=N+S+E+W)
    Grid.rowconfigure(grid_frame, grid_frame.rows, weight=1)
    grid_frame.rows = grid_frame.rows + 1


class GridFrame(Frame):
    rows = 0

    def __init__(self, root):
        Frame.__init__(self, root, bd=1)


root = Tk(className="Main screen")
root.minsize(408, 80)

# size to quarter of screen
w, h = root.winfo_screenwidth() / 2, root.winfo_screenheight() / 2
root.geometry("%dx%d+0+0" % (w, h))

# grid_frame will resize and bars will not
Grid.rowconfigure(root, 1, weight=1)
Grid.columnconfigure(root, 0, weight=1)

myframe = Frame(root, bd=4, relief="groove")
myframe.grid(row=1, sticky=N + W + S + E)

canvas = Canvas(myframe)
grid_frame = GridFrame(canvas)
grid_frame.pack(fill=BOTH, expand=True)
grid_frame.bind("<Button-1>", add_row)
scrollbar = Scrollbar(myframe, orient="vertical", command=canvas.yview)

canvas.configure(yscrollcommand=scrollbar.set)
scrollbar.pack(side=RIGHT, fill=Y)
canvas.pack(side=LEFT, fill=BOTH, expand=True)

topBar = Frame(root, grid_frame)
label = Label(topBar, text="Top Text")
label.pack()
topBar.grid(row=0, sticky=W+N+E+S)

bottomFrame = Frame(root, grid_frame)
label = Label(bottomFrame, text="Bottom Text")
label.pack()
bottomFrame.grid(row=2, sticky=E+S+W)

mainloop()

The scrollregion I want to track is the myframe/canvas/grid_frame combination I read to use from this post. The current functionality is that the scrollbar is never in an "active" state and rows added to the grid merely shrink the grid for it to fit within the display. To add a new row, click within the grid_frame region. Any help would be greatly appreciated! Here are some images of the current UI: UI display with only a few rows UI display with many more rows

theresa
  • 11
  • 2
  • 1
    We can't run your code. You haven't defined what `GridFrame`, `TopFrame` and `BottomFrame` are. Also, you mention adding rows to the grid, but your code doesn't show that. – Bryan Oakley Jul 18 '19 at 14:07
  • 1
    Please reduce this down to a [mcve]. We shouldn't need to load padas. For the purposes of this question, you can either hard-code some data, or generate the data in a simple loop. That being said, I don't see anywhere in your code where you reconfigure the `scrollregion` attribute. – Bryan Oakley Jul 18 '19 at 15:00

1 Answers1

1

There are two major problems with your code.

First, for the canvas to be able to scroll the inner frame, the inner frame must be a canvas object created with create_window. You're adding it to the canvas with pack, which means the canvas cannot scroll it.

To fix that, use create_window instead of pack:

canvas.create_window(0, 0, anchor="nw", window=grid_frame)

Second, you must reset the scrollregion attribute whenever the contents inside the canvas change. Normally this is done in a <Configure> event handler on the frame, but you can just as easily call it in your add_row function.

For example, add the following line to the end of add_row:

canvas.configure(scrollregion=canvas.bbox("all"))

With those two changes, the scrollbars will start to work as soon as the inner frame is taller than the canvas.


The above solves the problem of the inner window being able to scroll when you add items. In the specific example of this test program, you also have the problem that your binding is on the frame. At startup the frame has a size of 1x1 so it's a bit hard to click on. Moving the binding to the canvas will make this specific demo program work better.

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685