Tkinter buttons bellow grid to close and save to file

Question:

I’m trying to create a grid of buttons that change colour with Tkinter.

from tkinter import *

class App():
    def __init__(self, root):
        self.root = root
        buttonQ = Button(self.root, text = "Quit", command = endProgam())
        buttonS = Button(self.root, text = "Save", command = saveToFile())

def Function(self):
    self.grid = []
    for i in range(5):
        row = []
        for j in range(5):
            row.append(Button(self.root,width=6,height=3,command=lambda i=i, j=j: self.Click1(i, j),background='gray'))
            row[-1].grid(row=i,column=j)
        self.grid.append(row)

def Click1(self, i, j):
    orig_color = self.grid[i][j].cget('bg')
    #print(orig_color)
    if orig_color=="red":
        self.grid[i][j]["bg"]="gray"
    else:
        self.grid[i][j]["bg"]="red"
    #self.grid[i][j]["bg"]="red"
    #self.grid[i][j].configure(background="blue")

def endProgam(self):
    # top.quit()
    top.destroy() 

def saveToFile(self):
    # save matrix to file 
    top.destroy() 

root = Tk()
app = App(root)
app.Function()
root.mainloop()

My problem is that I cannot add 2 buttons below the grid, one to quit and one to save into a file values based on the button colours (0-grey and 1-red as a matrix) and then quit.

File "--", line 37, in <module>
app = App(root)


File "--", line 6, in __init__
    buttonQ = Button(self.root, text = "Quit", command = endProgam())
TypeError: endProgam() missing 1 required positional argument: 'self'

It’s my first time coding in Python with Tkinter, so please be gentle 🙂

Asked By: LAffair

||

Answers:

First, your indentation levels for your Class are off. The methods need to be indented another level or you’ll get a TypeError for each method.

Second, for buttonQ and buttonS, make sure you are referencing the instance of the class, i.e.:

buttonQ = Button(self.root, text = "Quit", command = endProgam)
buttonS = Button(self.root, text = "Save", command = saveToFile)

should be:

buttonQ = Button(self.root, text = "Quit", command = self.endProgam)
buttonS = Button(self.root, text = "Save", command = self.saveToFile)

(Note the use of self)

As far as actually placing the buttons, I would recommend creating an additional frame to manage the layouts separately. You can create and place these just like widgets and it helps make managing the layouts much simpler.

For example:

class App():
    def __init__(self, root):
        self.root = root
        self.TopFrame = Frame(root) # Create a top frame to place the original grid
        self.BottomFrame = Frame(root) # Create a frame for the additional buttons
        self.TopFrame.grid(row=0) # Place the Frame itself
        self.BottomFrame.grid(row=6) # Place the new Frame directly below the first

        # Changed to an instance variable to reference in Function method
        buttonQ = Button(self.BottomFrame, text="Quit", command=self.endProgam)
        buttonS = Button(self.BottomFrame, text="Save", command=self.saveToFile)
        buttonS.grid(row=0, column=0, padx=10)
        buttonQ.grid(row=0, column=1, padx=10)

    def Function(self):
        self.grid = []
        for i in range(5):
            row = []
            for j in range(5):
                row.append(Button(self.TopFrame,width=6,height=3,command=lambda i=i, j=j: self.Click1(i, j),background='gray'))
                row[-1].grid(row=i,column=j)
            self.grid.append(row)

Notice the new TopFrame and BottomFrame. The grid buttons are now sitting on the TopFrame while the BottomFrame contains the two new button widgets.
You’ll find that placing separate layout objects in its own frame will make managing more complex layouts much simpler.

Answered By: Mogarrr