Search code examples
pythonclassooptkinterbutton

Python Tkinter with OOP. How to define function for changing button attributes?


I am learning Object Oriented Programming for Python using Tkinter. I want to create a Class for each type of widgets (right now considering only Buttons).

I am passing the Button class to my main App class. Below is the code I have so far:

import tkinter as tk
from tkinter import ttk

class Buttons:
    def __init__(self):
        pass
    def buttons(self,text,width,cmd,col,row):
        self.text = text
        self.width = width
        self.cmd = cmd
        self.col = col
        self.row = row
        ttk.Button(text=self.text, width=self.width, command=self.cmd).grid(column=self.col,row=self.row)

class App(tk.Tk, Buttons):
    def __init__(self):
        super().__init__()
        self.geometry('100x100')
        self.resizable(0,0)
        b1 = self.buttons('Btn1',10,lambda: changeButton(),0,0)
        b2 = self.buttons('Btn2',10,lambda: changeButton(),0,1)
        self.mainloop()
        
# where should I place this function? How can I change button attributes?
def changeButton(self):
    b1['text'] = 'Done'   # If button 1 pressed
    b2['text'] = 'OKAY'   # If button 2 pressed

if __name__=="__main__":
    app = App()

I have two questions -

  1. Where should I place the function 'changeButton()' and why?
  2. If I have 2 buttons, and when pressed, if they should change their texts to something else; is it possible to do that via a single function? Or should there be a 2 functions?

Thank you

When I try the above code, I get the error:

line 20, in <lambda>  
    b1 = self.buttons('Btn1',10,lambda: changeButton(),0,0)
                                        ^^^^^^^^^^^^^^
TypeError: changeButton() missing 1 required positional argument: 'self'

Solution

  • There are many ways to perform this task, I've added comments inside the code for convenience reasons.

    import tkinter as tk
    from tkinter import ttk
    
    
    class MyButton(ttk.Button):
        def __init__(self, text, width, cmd, col, row):
            super().__init__()
            self.textvar = tk.StringVar()  # create a StringVar object
            self.textvar.set(text)  # set the value of the StringVar object
            self.config(textvariable=self.textvar, width=width)
            self.grid(column=col,row=row)
            self.bind('<Button-1>', cmd)  # binding the cmd function with the Mouse '<Button-1>' event to MyButton object
    
    
    class App(tk.Tk, MyButton):
        def __init__(self):
            super().__init__()
            self.geometry('100x100')
            self.resizable(0,0)
            MyButton('Btn1', 10, changeButton, 0, 0)
            MyButton('Btn2', 10, changeButton, 0, 1)
            self.mainloop()
    
    
    # where should I place this function? How can I change button attributes?
    def changeButton(event):
        """
        event is the Mouse '<Button-1>' event object containing information about the event
        event.widget is the MyButton object that generated the event
        event.widget.textvar is the StringVar object of the MyButton object
        event.widget.textvar.get() returns the value of the StringVar object
        event.widget.textvar.set() sets the value of the StringVar object
    
        print(dir(event)) to see all the attributes of the event object
        """
        text = (
            'Done' if event.widget.textvar.get() == 'Btn1' else
            'OKAY' if event.widget.textvar.get() == 'Btn2' else
            'Btn1' if event.widget.textvar.get() == 'Done' else
            'Btn2'
        )
        event.widget.textvar.set(text)
    
    
    if __name__=="__main__":
        app = App()