1

Below is the code that I am using to make a chessboard out of buttons.

from Tkinter import *

for x in xrange(8):
    for y in xrange(8:
        if((x+y)%2 == 0):
            Button(root, bg="white", width=11, height=5).grid(row=y, column=x)
        else:
            Button(root, bg="black", width=11, height=5).grid(row=y, column=x)

I know how to make a callback function for an individual button, but I am not sure how I would go about implementing a callback function for each of these 64 buttons so that, when pressed, they will return their position in the grid.

Torry Yang
  • 365
  • 3
  • 12

3 Answers3

3
def callback(event):
   x,y = event.widget.grid_location()

This example should point you into the right direction.

update: to clarify the usage of grid_location i did a quick-google and found ... a SO-post ;-) which put me to shame by providing exactly your needed solution in a more direct way:

grid_info = event.widget.grid_info()
print "row:", grid_info["row"], "column:", grid_info["column"]

so the credit should go to Bryan Oakley ;-) and this question may be called a duplicate ...

Community
  • 1
  • 1
Don Question
  • 11,227
  • 5
  • 36
  • 54
  • I am not quite sure what event should be in this case. Sorry for the seemingly dumb question. – Torry Yang Nov 06 '12 at 12:30
  • your callback gets "event" from the calling widget passed as an argument! you don't have to do anythin, it's just there! that's how event-handling with callback is supposed to work. i should further point out, that my code is not complete - grid-location requires a pixelposition in the format of (x,y), which can also be provided by e.g. the event- or event.widget-attributes – Don Question Nov 06 '12 at 13:23
1

Try binding the x and y values for each button to a lambda that can call a handler function whenever a button is pressed. Now you have the x and y position of each button press.

def handlebuttonpress(x,y):
  print 'Button x-{0} y-{1} pressed'.format(x,y)

width, height = 8, 8
for x in xrange(width):
  for y in xrange(height):
    if((x+y)%2 == 0):
        Button(root, command=lambda x=x, y=y: handlebuttonpress(x,y), bg="white", width=11, height=5).grid(row=y, column=x)
    else:
        Button(root, command=lambda x=x, y=y: handlebuttonpress(x,y), bg="black", width=11, height=5).grid(row=y, column=x)
Aesthete
  • 18,622
  • 6
  • 36
  • 45
0

Edit: I think I like @DonQuestion's event-based approach better because it does not require a different function for each button.

Below I've adapted my original code to use

    master.bind("<Button-1>", self.onclick)

to react to mouse clicks (instead of using tk.Button(command = ...)


import Tkinter as tk

class ButtonEventBlock(object):
    # http://stackoverflow.com/a/6102759/190597
    def __init__(self, master, names, cols):
        self.names = names
        self.cols = cols
        self.button = []
        for i, name in enumerate(names):
            self.button.append(tk.Button(master, text = name))
            row, col = divmod(i, self.cols)
            self.button[i].grid(sticky = tk.W+tk.E+tk.N+tk.S,
                                row = row, column = col, padx = 1, pady = 1)
        master.bind("<Button-1>", self.onclick)

    def onclick(self, event):
        info = event.widget.grid_info()        
        # print(info)
        # {'rowspan': '1', 'column': '3', 'sticky': 'nesw', 'ipady': '0', 'ipadx':
        # '0', 'columnspan': '1', 'in': <Tkinter.Tk instance at 0xab3d7ec>,
        # 'pady': '1', 'padx': '1', 'row': '0'}
        row, col = [int(info[key]) for key in ['row', 'column']]
        i = self.cols*row + col
        print(row, col, self.names[i])

names = ('One', 'Two', 'Three', 'Four', 'Five',
         'Six', 'Seven', 'Eight', 'Nine', 'Ten')

root = tk.Tk()
ButtonEventBlock(root, names, cols = 5)
root.mainloop()

enter image description here

unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
  • using `bind` is inferior to using the built-in `command` attribute. Buttons have built-in behavior for keyboard traversal, for example, which won't work with the bindings. Also, the `command` attribute honors the state of the widget (if the widget is disabled, the command doesn't fire) Maybe those features are not important for this specific app, but it may be important to other people who view this solution. – Bryan Oakley Nov 06 '12 at 14:37
  • 1
    @BryanOakley: I expect the OP will eventually want to change the Buttons to images of chess pieces on a Canvas. He'll then want to be able to drag the images to new grid locations to move the chess pieces. So I think an `bind`-based solution will better serve his future needs. – unutbu Nov 06 '12 at 14:45
  • that could be, but this specific question is about buttons, and other people will come here looking for answers to how to create button callbacks in a loop. If your answer is better for addressing items on a canvas, put that as part of the answer. As it stands, bindings are the inferior choice when talking specifically about buttons. – Bryan Oakley Nov 06 '12 at 20:33
  • @BryanOakley: I've given an answer which I think best helps the OP. Your concern for others who might visit this question is reasonable. Since you have a lot of knowledge about Tkinter, please lead positively by writing an answer so we can all learn from you. – unutbu Nov 07 '12 at 01:49