0

I made a table using a frame and multiple labels and was wondering if it was possible to add a vertical scrollbar to the table so i can view more cells? And if so, is it possible to simplify the code for the table using a for loop?

Also im going to connect the table to a database to display some stuff if that information is relevant

Heres my code if needed:

from tkinter import *

multiQ = Tk()
multiQ.title("Multiple-Choice Question")
multiQ.resizable(0,0)

header = LabelFrame(multiQ, bg="white")
content = LabelFrame(multiQ, bg="white")

header.columnconfigure(0, weight=1) # Forces column to expand to fill all available space
homeButton=Button(content,width=50,height=50)
try:
    homeIcon=PhotoImage(file="yes.png")
    homeButton.config(image=homeIcon)
    homeButton.image = homeIcon
except TclError:
    print("Home")
homeButton.grid(row=0, sticky="w", padx=2, pady=2)

tableFrame = Frame(content, width=800, height=500, bg="black")
tableFrame.grid(row=1)

# allow the column inside the entryFrame to grow
tableFrame.columnconfigure(0, weight=10)

# By default the frame will shrink to whatever is inside of it and
tableFrame.grid_propagate(False)

column1= Label(tableFrame, font=("Ariel", 22, "bold"), text="Quiz", bg="#12a8e3", fg="white")
column1.grid(row=0, column=0, ipadx=99, sticky="w") #each cell width will be 264 and have a size 2 gap between horizontally adjacent cells

column1= Label(tableFrame, font=("Ariel", 22, "bold"), text="Mark", bg="#12a8e3", fg="white")
column1.grid(row=0, column=1, ipadx=99, padx=3)

column1= Label(tableFrame, font=("Ariel", 22, "bold"), text="Date", bg="#12a8e3", fg="white")
column1.grid(row=0, column=2, ipadx=99, sticky="e")

cellRow1 = Label(tableFrame, bg="white")
cellRow1.grid(row=1, column=0, ipadx=130, ipady=30, sticky="w")
cellRow1 = Label(tableFrame, bg="white")
cellRow1.grid(row=1, column=1, ipadx=133, ipady=30, padx=3)
cellRow1 = Label(tableFrame, bg="white")
cellRow1.grid(row=1, column=2, ipadx=130, ipady=30, sticky="e")

cellRow2 = Label(tableFrame, bg="white")
cellRow2.grid(row=2, column=0, ipadx=130, ipady=30, sticky="w")
cellRow2 = Label(tableFrame, bg="white")
cellRow2.grid(row=2, column=1, ipadx=133, ipady=30, padx=3)
cellRow2 = Label(tableFrame, bg="white")
cellRow2.grid(row=2, column=2, ipadx=130, ipady=30, sticky="e")

cellRow3 = Label(tableFrame, bg="white")
cellRow3.grid(row=3, column=0, ipadx=130, ipady=30, sticky="w")
cellRow3 = Label(tableFrame, bg="white")
cellRow3.grid(row=3, column=1, ipadx=133, ipady=30, padx=3)
cellRow3 = Label(tableFrame, bg="white")
cellRow3.grid(row=3, column=2, ipadx=130, ipady=30, sticky="e")

cellRow4 = Label(tableFrame, bg="white")
cellRow4.grid(row=4, column=0, ipadx=130, ipady=30, sticky="w")
cellRow4 = Label(tableFrame, bg="white")
cellRow4.grid(row=4, column=1, ipadx=133, ipady=30, padx=3)
cellRow4 = Label(tableFrame, bg="white")
cellRow4.grid(row=4, column=2, ipadx=130, ipady=30, sticky="e")

cellRow5 = Label(tableFrame, bg="white")
cellRow5.grid(row=5, column=0, ipadx=130, ipady=30, sticky="w")
cellRow5 = Label(tableFrame, bg="white")
cellRow5.grid(row=5, column=1, ipadx=133, ipady=30, padx=3)
cellRow5 = Label(tableFrame, bg="white")
cellRow5.grid(row=5, column=2, ipadx=130, ipady=30, sticky="e")

cellRow6 = Label(tableFrame, bg="white")
cellRow6.grid(row=6, column=0, ipadx=130, ipady=30, sticky="w")
cellRow6 = Label(tableFrame, bg="white")
cellRow6.grid(row=6, column=1, ipadx=133, ipady=30, padx=3)
cellRow6 = Label(tableFrame, bg="white")
cellRow6.grid(row=6, column=2, ipadx=130, ipady=30, sticky="e")

suggestionBox = Button(content, borderwidth=1, font=("Ariel", 22, "bold"), text="Based off your previous results, you could focus on:", bg="white", fg="black", relief="solid")
suggestionBox.grid(row=2, ipady=25, ipadx=70, pady=20)

header.grid(row=0, sticky='NSEW')
content.grid(row=1, sticky='NSEW')

multiQ.mainloop()

1 Answers1

0

First of all, let's address the for loop, the only thing that changes in that code is the row number, so we create a for loop iterating through the numbers 1,7 (includes 1 but does not include 7), like this:

     for num in range(1, 7):
        cellRow1 = Label(tableFrame, bg="white")
        cellRow1.grid(row=num, column=0, ipadx=130, ipady=30, sticky="w")
        cellRow1 = Label(tableFrame, bg="white")
        cellRow1.grid(row=num, column=1, ipadx=133, ipady=30, padx=3)
        cellRow1 = Label(tableFrame, bg="white")
        cellRow1.grid(row=num, column=2, ipadx=130, ipady=30, sticky="e")

The variable doesn't actually mean anything so you can change that to whatever, just be careful if you want to individually change the labels, for that you can use a list and indexing. Or if you can, just fit anything else into that for loop, for example, if you want to have different text for each label, make a list with every label text, and then just use the num variable.

For the scrollable frame, this is pretty complicated, so I'll just give you the code, you might want to take a moment to read through it and try and understand, put a comment if you want to know more.

from tkinter import *
import tkinter as tk
from tkinter import ttk


class MessageWindow(tk.Canvas):
    def __init__(self, container, *args, **kwargs):
        super().__init__(container, *args, **kwargs, highlightthickness=0)

        self.scrollable_frame = ttk.Frame(container)
        self.scrollable_frame.columnconfigure(0, weight=1)

        self.scrollable_window = self.create_window((0, 0), window=self.scrollable_frame, anchor="nw",
                                                    width=self.winfo_width())

        def configure_scroll_region(event):
            self.configure(scrollregion=self.bbox("all"))

        def configure_window_size(event):
            self.itemconfig(self.scrollable_window, width=self.winfo_width())

        self.bind("<Configure>", configure_window_size)
        self.scrollable_frame.bind("<Configure>", configure_scroll_region)
        self.bind_all("<MouseWheel>", self._on_mousewheel)

        scrollbar = ttk.Scrollbar(container, orient="vertical", command=self.yview)
        scrollbar.grid(row=0, column=1, sticky="NS")

        self.configure(yscrollcommand=scrollbar.set)
        self.yview_moveto(1.0)

        self.create(container)

    # https://stackoverflow.com/a/17457843/1587271
    def _on_mousewheel(self, event):
        self.yview_scroll(-int(event.delta / 120), "units")

    def create(self, container):

        header = LabelFrame(self.scrollable_frame, bg="white")
        content = LabelFrame(self.scrollable_frame, bg="white")

        header.columnconfigure(0, weight=1)  # Forces column to expand to fill all available space
        header.columnconfigure(1, weight=1)  # Forces column to expand to fill all available space
        homeButton = Button(content, width=50, height=50)
        try:
            homeIcon = PhotoImage(file="yes.png")
            homeButton.config(image=homeIcon)
            homeButton.image = homeIcon
        except TclError:
            print("Home")
        homeButton.grid(row=0, sticky="w", padx=2, pady=2)

        tableFrame = Frame(content, width=800, height=500, bg="black")
        tableFrame.grid(row=1)

        # allow the column inside the entryFrame to grow
        tableFrame.columnconfigure(0, weight=10)

        # By default the frame will shrink to whatever is inside of it and
        tableFrame.grid_propagate(False)

        column1 = Label(tableFrame, font=("Ariel", 22, "bold"), text="Quiz", bg="#12a8e3", fg="white")
        column1.grid(row=0, column=0, ipadx=99,
                     sticky="w")  # each cell width will be 264 and have a size 2 gap between horizontally adjacent cells

        column1 = Label(tableFrame, font=("Ariel", 22, "bold"), text="Mark", bg="#12a8e3", fg="white")
        column1.grid(row=0, column=1, ipadx=99, padx=3)

        column1 = Label(tableFrame, font=("Ariel", 22, "bold"), text="Date", bg="#12a8e3", fg="white")
        column1.grid(row=0, column=2, ipadx=99, sticky="e")

        for num in range(1, 7):
            cellRow1 = Label(tableFrame, bg="white")
            cellRow1.grid(row=num, column=0, ipadx=130, ipady=30, sticky="w")
            cellRow1 = Label(tableFrame, bg="white")
            cellRow1.grid(row=num, column=1, ipadx=133, ipady=30, padx=3)
            cellRow1 = Label(tableFrame, bg="white")
            cellRow1.grid(row=num, column=2, ipadx=130, ipady=30, sticky="e")



        suggestionBox = Button(content, borderwidth=1, font=("Ariel", 22, "bold"),
                               text="Based off your previous results, you could focus on:", bg="white", fg="black",
                               relief="solid")
        suggestionBox.grid(row=2, ipady=25, ipadx=70, pady=20)

        header.grid(row=0, sticky='NSEW', column=0)
        content.grid(row=1, sticky='NSEW', column=0)


multiQ = Tk()
multiQ.geometry('1000x700') #You can set this to whatever you want, the canvas will always grow
multiQ.rowconfigure(0, weight=1)  # Increase the size to max
multiQ.columnconfigure(0, weight=1)  # Increases the size to max
message_window = MessageWindow(multiQ)
message_window.grid(row=0, column=0, sticky="NSEW")
multiQ.title("Multiple-Choice Question")

multiQ.mainloop()

Now whatever code you want to do, just do that in the create function, and it'll work inside of the scrollable Canvas,

Basically what's happening is that you're just making a fixed size canvas, putting a frame in that canvas, and then moving that frame up and down, this gives the illusion that the frame is scrollable, the functions just tell the scrollbar where you are and the size of the canvas and frame, like I said, complicated, check this out for more info. https://blog.tecladocode.com/tkinter-scrollable-frames/

Salman B
  • 138
  • 11