0

I'm a little bit stuck on this problem regarding my program. I tried adding as many comments as possible to give a sense of what everything does in the code, but essentially. The program has a field and value entry box. When the "add field/value button" is clicked, more of the entry widgets are added. If this keeps occurring then obviously it'll go off screen. So I've limited the size of the application, but the problem then is I need a scrollbar. I've tried looking it up, but my frame uses grid, and everywhere they use pack which isn't compatible in this case. I get the scrollbar to appear, however it doesn't seem to work. I've seen some people use canvas, and more than one frame, etc. I'm missing something important but I don't know how do the exact same thing with a grid. Think you experts can lend me hand to get it working?

from tkinter import *
import tkinter as tk

class Insert(tk.Tk):

    def __init__(self):
        tk.Tk.__init__(self)
        
        container = tk.Frame(self)
        container.grid_columnconfigure(0, weight=1)
        container.grid_rowconfigure(0, weight=1)
        container.pack(side="top", fill="both", expand=True)

        self.frameslist = {}

        for frame in (Create,):
            frame_occurrence = frame.__name__
            active_frame = frame(parent=container, controller=self)
            self.frameslist[frame_occurrence] = active_frame
            active_frame.grid(row=0, column=0, sticky="snew")

        self.show_frame("Create")
            
    def show_frame(self, frame_occurrence):
        active_frame = self.frameslist[frame_occurrence]
        active_frame.tkraise()

class Create(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller

        #For all widgets (nested list, 2 widgets per row)
        self.inputlist = []
        #For just the entries
        self.newinputlist = []

        #Create two labels, add them into the inputlist to be iterated
        labels = [tk.Label(self, text="Field"), tk.Label(self, text="Values")]
        self.inputlist.append(labels[:])

        #Insert the labels from the list
        for toplabels in range(1):
            self.inputlist[toplabels][0].grid(row=toplabels, column=0, padx=10, pady=5)
            self.inputlist[toplabels][1].grid(row=toplabels, column=1, padx=10, pady=5)

        #Create the first two entry boxes, append them to the inputlist, and newinput list
        first_entries = [tk.Entry(self, borderwidth=5), tk.Text(self, borderwidth=5, height= 5, width=20)]
        self.newinputlist.append(first_entries[:])
        self.inputlist.append(first_entries[:])

        #Insert the entries from the newinputlist
        for x in range(0, len(self.newinputlist) + 1):
            self.newinputlist[0][x].grid(row=1, column=x, padx=10, pady=5)

        #Create two buttons (Both share same row), append them to list
        button_input_1 = [tk.Button(self, text="ADD FIELD/VALUE", command=self.add_insert), tk.Button(self, text="BACK")]
        self.inputlist.append(button_input_1[:])
        
        #Insert buttons at the bottom of the grid
        for button in range(len(self.inputlist) - 2, len(self.inputlist)):
            self.inputlist[button][0].grid(row=button, column=0, padx=10, pady=5)
            self.inputlist[button][1].grid(row=button, column=1, padx=10, pady=5)

    def add_insert(self):
        #Create two new entries, append them to the list 
        add_input = [tk.Entry(self, borderwidth=5), tk.Text(self, borderwidth=5, height= 5, width=20)]
        self.inputlist.insert(-1, add_input)
        self.newinputlist.append(add_input)
        
        #Because there are new entry boxes, old grid should be forgotten
        for widget in self.children.values():
            widget.grid_forget()

        #Use the index for the row, get all widgets and place them again
        for index, widgets in enumerate(self.inputlist):
            widget_one = widgets[0]
            widget_two = widgets[1]

            widget_one.grid(row=index, column=0, padx=10, pady=5)
            widget_two.grid(row=index, column=1, padx=10)

        #Create scrollbar when this button is pressed
        scrollbar = tk.Scrollbar(self, orient="vertical")
        scrollbar.grid(row=0, column=2, stick="ns", rowspan=len(self.inputlist) + 1)

if __name__ == "__main__":
    app = Insert()
    app.maxsize(0, 500)
    app.mainloop()
Sloxy
  • 82
  • 10
  • 1
    I suggest maybe embedding the widgets into a scrollable `tkinter.Text()`. [Here](https://stackoverflow.com/questions/66558338/tkinter-adding-widgets-in-an-embedded-frame-widget-in-a-text-widget) is a question on how to embed widgets in a Text, and of course there are many questions about how to scroll `Text`. – Sylvester Kruin Oct 13 '21 at 23:37
  • Will have a look into it, thank you Sylvester! – Sloxy Oct 13 '21 at 23:43

1 Answers1

0

You could create a Canvas and insert your Entry objects into a Frame.

Here is a simplified example that creates a 2D bank of Buttons using the canvas.create_window.

import tkinter as tk
 
root = tk.Tk()      
# essential to enable full window resizing
root.rowconfigure(0, weight = 1)
root.columnconfigure(0, weight = 1)

# scrollregion is also essential when using scrollbars
canvas = tk.Canvas(
    root, scrollregion = "0 0 2000 1000", width = 400, height = 400)
canvas.grid(row = 0, column = 0, sticky = tk.NSEW)

scroll = tk.Scrollbar(root, orient = tk.VERTICAL, command = canvas.yview)
scroll.grid(row = 0, column = 1, sticky = tk.NS)
canvas.config(yscrollcommand = scroll.set)

# I've used a labelframe instead of frame so button are neatly collected and named
frame = tk.LabelFrame(root, labelanchor = tk.N, text = "Buttonpad")

# Note I've placed buttons in frame
for fila in range(20):
    for col in range(5):
        btn = tk.Button(frame, text = f"{fila}-{col}")
        btn.grid(row = fila, column = col, sticky = tk.NSEW)

# Frame is now inserted into canvas via create_window method
item = canvas.create_window(( 2, 2 ), anchor = tk.NW,  window = frame )

root.mainloop()
Derek
  • 1,916
  • 2
  • 5
  • 15