Python Forum
[Tkinter] Button click problem using OOP
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[Tkinter] Button click problem using OOP
#1
Can anyone help me with a problem illustrated in the code below. I am instantiating three tkinter buttons and saving the objects in a list which is I think a fairly standard way of doing things. The buttons can then be managed, colour change, text etc by referencing them via their index in the list. But if a button is clicked it seems impossible to detect which button it was. The only button object that is "active", if you want to call it that, is the last one that was instantiated in the For loop. Which ever button is clicked, it is that object that handles the click event. Is there a way of detecting which button is clicked when they have been instantiated this way. Thank you for sharing your expertise.

JohnB

import tkinter as tk
root=tk.Tk()

class Butman(tk.Frame):
    def __init__(self,MyNumber):
        self.MyNumber = MyNumber
        tk.Frame.__init__(self)
        
    def SetCol(self,col):
        self.but.config(bg = col)
        
    def SetText(self,Text):
        self.but.config(text = Text)



def DoSomething():
    print ("something done")
    
        
frame2 = tk.Frame(bg="grey", height = 720 ,width = 1280)
frame2.pack()
ButObj = []
for x in range(0,3):
    B = Butman(x)
    B.but=tk.Button(frame2, state="normal", text="sometext",compound= tk.CENTER,font=('comicsans', 
    16),relief=tk.FLAT,command = lambda: DoSomething())
    B.but.place(relx=x/4, rely=x/4, relwidth=0.18, relheight=0.18)
    ButObj.append(B)
    
ButObj[1].SetCol("green")
B.SetCol("red")
Reply
#2
Why not pass the button as an argument to the function?
import tkinter as tk
root = tk.Tk()

def button_pressed(button):
    label.configure(text=button['text'])
     
         
frame = tk.Frame()
frame.pack()

b1 = tk.Button(frame, text='Hello')
b1.configure(command = lambda b = b1: button_pressed(b))
b1.grid(row=0, column=0)

b2 = tk.Button(frame, text='Goodbye')
b2.configure(command = lambda b = b2: button_pressed(b))
b2.grid(row=1, column=0)

label = tk.Label(frame, text = 'Button Text')
label.grid(row=2, column=0)
Reply
#3
I think you kind of over thought it, the class isn't needed
Quote: Is there a way of detecting which button is clicked when they have been instantiated this way.
Yes by indexing a list and using functool -> partial.
here's an example:
import tkinter as tk
from functools import partial


root= tk.Tk()

def do_something(index):
    button_list[index].config(bg='green',fg='white')


frame= tk.Frame(bg='gray60', width= 1280, height= 720)
frame.pack()
button_list= []
for x in range(0,3):
    text_='Button {0}'.format(x)
    button_list.append(tk.Button(frame,text=text_,compound='center',
                       font=('comicsans',16,'italic'),
                       command= partial(do_something,x),
                       relief= tk.FLAT))
    button_list[-1].place(relx=x/4, rely=x/4, relwidth=0.18, relheight=0.18)

root.mainloop()
Gribouillis likes this post
Reply
#4
Thank you for your replies. I have learnt something from both of them.
I could pass the button as an argument as you suggest deanhistad but I need to configure up to 20 buttons, so I would prefer them to be configured in a loop.

Which using partial functions does. I've never come across partial functions or the Functools module before. Having researched it a bit I can see you need to generate a new function of the partial function, and call the new function supplying any if needed additional arguments. Can't quite see how a new function of the partial(do_something,x) is made/called when a button is clicked.
Another puzzle your solution has produced is why command = lambda:do_something(x) does not work. Why do you need to use a partial function. You would think that x, the list index basically, would be passed to the do_something function and the correct button would be actioned. What happens is the same as the Class scenario where the last button made is always actioned which ever button is clicked.

Anyway, having got all that clear in my mind, it's great when solutions provide more questions, that's how we learn.
Reply
#5
command = lambda:do_something(x) does not work because lambdas are evaluated when called. IN this example I make three buttons and all are set to call DoSomething and passing the argument I.
import tkinter as tk

buttons = []

def DoSomething(args):
    print(args)

root = tk.Tk()
f = tk.Frame()
f.pack()

for i in range(3):
    b = tk.Button(f, command = lambda : DoSomething(i), text=str(i))
    b.grid(row=0, column=i)
    buttons.append(b)
When I press any button the program prints "2". Why is that?

How about answer this. What is the value of i? In my Python console I type i and it responds "2". I press a button, the button executes the lambda. The lambda calls DoSomething and passes the value of i.

To lock in the "i" argument in the callback you can use a partial function. The partial function arguments are frozen when the partial function is created. You are not actually setting the "command" to DoSomething(i), you are setting it to DoSomething(0) or DoSomething(1). You can do something similar with a lambda.
import tkinter as tk

buttons = []

def DoSomething(args):
    print(args)

root = tk.Tk()
f = tk.Frame()
f.pack()

for i in range(3):
    b = tk.Button(f, command = lambda x = i: DoSomething(x), text=str(i))
    b.grid(row=0, column=i)
    buttons.append(b)
Reply
#6
Thanks for the great explanation deanhystad. Yes I asked the console to print i, and yes it equalled 2. I like the lambda solution, giving arguments before the colon which are evaluated at run rather than at execution. In fact this is a solution to my first post where I make objects of the buttons.

All's good Thanks
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  tkinter - touchscreen, push the button like click the mouse John64 5 747 Jan-06-2024, 03:45 PM
Last Post: deanhystad
  Figure Gets Larger Every time I click a show button joshuagreineder 2 1,273 Aug-11-2022, 06:25 AM
Last Post: chinky
  problem with radio button crook79 3 3,628 Aug-12-2021, 02:30 PM
Last Post: deanhystad
  [Tkinter] Modify Class on Button Click KDog 4 3,907 May-11-2021, 08:43 PM
Last Post: KDog
  tkinter python button position problem Nick_tkinter 3 3,490 Jan-31-2021, 05:15 AM
Last Post: deanhystad
  tkinter | Button color text on Click Maryan 2 3,317 Oct-09-2020, 08:56 PM
Last Post: Maryan
  Closing window on button click not working kenwatts275 4 3,675 May-03-2020, 01:59 PM
Last Post: deanhystad
  Problem about image and button scotesse 5 2,886 Apr-27-2020, 10:09 AM
Last Post: scotesse
  Problem with Submit button Tkinter Reldaing 2 3,613 Jan-05-2020, 01:58 AM
Last Post: balenaucigasa
  [Tkinter] Checking button click in Tkinter GalaxyCoyote 3 7,306 Oct-20-2019, 03:28 AM
Last Post: GalaxyCoyote

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020