tkinter grid identification is not working, cannot identify each pixel

Question:

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.

Asked By: Bettalion

||

Answers:

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.

Answered By: Martin Wettstein
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.