0

I know that this is quite an individual question, but I think it is a problem in Tkinter as I regularly encounter similar problem that I workaround and the answers could be beneficial for others as well.

The script is:

from tkinter import *
import random

class gui:
  def __init__(self):
    win=self.win=Tk()
    win.title('Ploters Data!')
  def identifier(self,x,y):
    print('id',x,y) 
    
  def createGraph(self,rows,columns):
    for xrow in range(rows+1):
      for ycolumn in range(columns+1):
        if xrow == 0 or ycolumn == 0:
          text = '--'
          if xrow == 0:
            if ycolumn==5:
              text='5'
            if ycolumn==10:
              text='10'  
          if ycolumn == 0:
            if xrow==5:
              text='5'
            if xrow==10:
              text='10'
          if xrow == ycolumn == 0:
              text='[]'
          pixel = Button(self.win,padx=10,pady=10,text=text)
          # print('click',xrow,ycolumn)
          pixel.config(command=lambda button=pixel: self.identifier(xrow,ycolumn))
          pixel.grid(row=xrow,column=ycolumn)
        else:
          pixel = Button(self.win,padx=10,pady=10)
          # print('click',xrow,ycolumn)
          pixel.config(command=lambda button=pixel: self.identifier(xrow,ycolumn))
          pixel.grid(row=xrow,column=ycolumn)
        # print(xrow,ycolumn)
    self.win.mainloop()

s=gui()
s.createGraph(15,10)

The specific problem is that when you click a button on the grid it doesn't give the correct 'coordinates', instead, it gives the last button.

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
Bettalion
  • 43
  • 5
  • `xrow` and `ycolumn` need to be bound just like `pixel`, for the same reason. Actually, `pixel` doesn't need to be bound, because the lambda *doesn't use it*. I think this should have been considered a typo or a simple logical error rather than a duplicate. – Karl Knechtel Aug 19 '22 at 13:23

1 Answers1

0

I think you overwrite the lambda function, which is somehow mutable in this example. I don't know why this happens (had the same problem with tkinter GUIs) but I know a simple workaround, now.

If you define a class that takes a function and a series of arguments and keyword arguments and just calls the function with these arguments, you can reference it as command in buttons. Using this workaround, you can omit the lambda and just call functions with arguments:

from tkinter import *
import random


class CMD: #Auxilliary function for callbacks using parameters. Syntax: CMD(function, argument1, argument2, ...)
    def __init__(s1, func, *args):
        s1.func = func
        s1.args = args
    def __call__(s1, *args):
        args = s1.args+args
        s1.func(*args)

class gui:
  def __init__(self):
    win=self.win=Tk()
    win.title('Ploters Data!')
  def identifier(self,x,y):
    print('id',x,y) 
    
  def createGraph(self,rows,columns):
    for xrow in range(rows+1):
      for ycolumn in range(columns+1):
        if xrow == 0 or ycolumn == 0:
          text = '--'
          if xrow == 0:
            if ycolumn==5:
              text='5'
            if ycolumn==10:
              text='10'  
          if ycolumn == 0:
            if xrow==5:
              text='5'
            if xrow==10:
              text='10'
          if xrow == ycolumn == 0:
              text='[]'
          pixel = Button(self.win,padx=10,pady=10,text=text,command=CMD(self.identifier,xrow,ycolumn))
          pixel.grid(row=xrow,column=ycolumn)
        else:
          pixel = Button(self.win,padx=10,pady=10,command=CMD(self.identifier,xrow,ycolumn))
          pixel.grid(row=xrow,column=ycolumn)
        # print(xrow,ycolumn)
    self.win.mainloop()

s=gui()
s.createGraph(15,10)

Using this small change, your program works fine.

Martin Wettstein
  • 2,771
  • 2
  • 9
  • 15