1

I'm trying to make a scrollable grid table. I had a look at some answers and it seems the way is to make a Frame, then put a Canvas and a Scrollbar next to eachother and apply some commands for scrolling. I have this code here, but I can't figure out what is wrong with this.

import tkinter as tk

class Test:
    def __init__(self):
        self.root = tk.Tk()
        
        table_and_scrollbar_frame = tk.Frame(self.root)
        table_frame = tk.Canvas(table_and_scrollbar_frame)
        table_frame.grid(row=0, column=0)
        
        self.table_headers = ["header_1", "header_2", "header_3", "header_4"]
        for test_row in range(0,100):
            for header_to_create in self.table_headers:
                current_entry = tk.Entry(table_frame, width=25, justify='center')
                current_entry.insert(0, header_to_create + " " + str(test_row))
                current_entry.configure(state='disabled', disabledforeground='blue')
                current_entry.grid(row=test_row, column=self.table_headers.index(header_to_create))

        table_scrollbar = tk.Scrollbar(table_and_scrollbar_frame, orient='vertical', command=table_frame.yview)
        table_scrollbar.grid(row=0, column=1, sticky='ns')
        table_frame.configure(yscrollcommand=table_scrollbar.set)
        table_frame.config(scrollregion=table_frame.bbox("all"))


        table_and_scrollbar_frame.pack()
        return


if __name__ == '__main__':
    program = Test()
    program.root.mainloop()

I'm not sure what is wrong/missing here?

DoctorEvil
  • 453
  • 3
  • 6
  • 18
  • Can you add a little more context. What do you mean by scrollbar table? Are you trying to move a bunch of widgets at once with the scrollbar? – Mike - SMT Jan 30 '23 at 20:09
  • I wrote "scrollable table". Basically a table with lots of rows that can't all be shown at once on the screen. – DoctorEvil Jan 30 '23 at 20:26
  • Ok so then yes you want to scroll through a bunch of widgets. IE labels/buttons/frames. This does use a canvas and there are a few examples out there. I have at least one post on the matter as well I will see if I can find it. – Mike - SMT Jan 30 '23 at 20:31
  • There is a good post by Brian (well known tkinter guy on SO) here https://stackoverflow.com/questions/3085696/adding-a-scrollbar-to-a-group-of-widgets-in-tkinter. This post shows how to use the canvas to scroll a group of widgets. – Mike - SMT Jan 30 '23 at 20:32
  • A Canvas scrolls the *drawing objects* that have been added to it, NOT its *child widgets*, which is all you've created here. In other words, `.create_window()` is necessarily going to be called on the Canvas for this to work. Also, you're setting its `scrollregion` a bit too early - you have to let the GUI update at least once (via `root.update_idletasks()`, perhaps) before widget coordinates are correct. – jasonharper Jan 30 '23 at 20:47

1 Answers1

0

I think not perfect, but it works fine.

import tkinter as tk
from tkinter import ttk

class Test:
    def __init__(self):

        self.root = tk.Tk()
        self.container = ttk.Frame(self.root)
        self.canvas = tk.Canvas(self.container, width=600, height=400, background='grey')
        self.vscrollbar = ttk.Scrollbar(self.container, orient="vertical", command=self.canvas.yview)
        self.hscrollbar = ttk.Scrollbar(self.container, orient="horizontal", command=self.canvas.xview)
        self.scrollable_frame = ttk.Frame(self.canvas)

        self.scrollable_frame.bind(
            "<Configure>",
            lambda e: self.canvas.configure(
                scrollregion=self.canvas.bbox("all")
            )
        )

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

        self.canvas.configure(yscrollcommand=self.vscrollbar.set)
        self.canvas.configure(xscrollcommand=self.hscrollbar.set)

        table_headers = ["header_1", "header_2", "header_3", "header_4"]
        for test_row in range(0,100):
            for header_to_create in table_headers:
                current_entry = tk.Entry(self.scrollable_frame, width=25, justify='center')
                current_entry.insert(0, header_to_create + " " + str(test_row))
                current_entry.configure(state='disabled', disabledforeground='blue')
                current_entry.grid(row=test_row, column=table_headers.index(header_to_create))

        self.container.grid()
        self.canvas.grid()
        self.vscrollbar.grid(row=0, column=1, sticky="ns")
        self.hscrollbar.grid(row=1, column=0, sticky="ew")

        self.root.mainloop()

if __name__ == '__main__':
    program = Test()

If this answer your question, please accept my reply.

Hermann12
  • 1,709
  • 2
  • 5
  • 14
  • 1
    Instead of `self.canvas.create_window((0, 0), ...)`, you can use `self.canvas.create_window(0, 0, ...)`. It will save you a bit of typing :D. Also making 400 widgets isn't a good idea, `Treeview` is there for that reason. – TheLizzard Jan 31 '23 at 22:01