如何识别循环中创建的按钮?

时间:2016-09-12 09:24:57

标签: python tkinter minesweeper

我正在尝试使用tkinter在python上编写扫雷游戏。我开始时使用10x10的二维列表创建一个按钮网格。然后我使用循环创建了每个按钮,因此我不必手动创建每个按钮并将其剪辑。

self.b=[[0 for x in range(1,12)] for y in range(1,12)] #The 2 dimensional list
for self.i in range(1,11):
     for self.j in range(1,11):
            self.b[self.i][self.j]=tkinter.Button(root,text = ("     "),command = lambda: self.delete()) # creating the button
            self.b[self.i][self.j].place(x=xp,y=yp) # placing the button
            xp+=26 #because the width and height of the button is 26
        yp+=26
        xp=0

基本上我希望按钮在按下时消失。问题是我不知道如何让程序专门删除我按下的按钮,因为所有按钮完全相同。创建删除功能时:

def delete(self):
    self.b[???][???].destroy()

我不知道如何让程序知道用户按下哪个按钮,因此可以删除该特定按钮。

问题: 有没有办法让每个按钮都有一些独特的东西,可以区别于其他按钮?比如给每个按钮分配一个特定的坐标,所以当按下按钮(2,3)时,数字2和3被传递到删除功能,所以删除功能可以删除按钮(2,3)?

5 个答案:

答案 0 :(得分:4)

在循环中创建按钮时,我们可以创建(实际获得)唯一标识。

例如:如果我们创建一个按钮:

button = Button(master, text="text")

我们可以立即识别出来:

print(button)
> <tkinter.Button object .140278326922376>

如果我们将这个标识存储到一个列表中,并将一个命令指定给按钮,并在创建过程中链接到它们的索引,我们可以在按下时获得它们的特定标识。

我们必须要做的就是按下按钮后按索引获取按钮的标识。

为了能够使用索引作为参数为按钮设置命令,我们使用functools'partial

简化示例(python3

在下面的简化示例中,我们在循环中创建按钮,将其标识添加到列表中(button_identities)。通过以下方式查找身份:bname = (button_identities[n])

现在我们有了身份,我们随后可以让按钮执行任何,包括编辑或自杀,因为我们有自己的身份。

在下面的示例中,按下按钮会将其标签更改为“已点击”

enter image description here

from tkinter import *
from functools import partial

win = Tk()
button_identities = []

def change(n):
    # function to get the index and the identity (bname)
    print(n)
    bname = (button_identities[n])
    bname.configure(text = "clicked")

for i in range(5):
    # creating the buttons, assigning a unique argument (i) to run the function (change)
    button = Button(win, width=10, text=str(i), command=partial(change, i))
    button.pack()
    # add the button's identity to a list:
    button_identities.append(button)

# just to show what happens:
print(button_identities)

win.mainloop()

或者如果我们点击按钮销毁,那么

enter image description here

from tkinter import *
from functools import partial

win = Tk()
button_identities = []

def change(n):
    # function to get the index and the identity (bname)
    print(n)
    bname = (button_identities[n])
    bname.destroy()

for i in range(5):
    # creating the buttons, assigning a unique argument (i) to run the function (change)
    button = Button(win, width=10, text=str(i), command=partial(change, i))
    button.place(x=0, y=i*30)
    # add the button's identity to a list:
    button_identities.append(button)

# just to show what happens:
print(button_identities)

win.mainloop()

矩阵的简化代码(python3):

在下面的示例中,我使用itertools's product()生成矩阵的坐标。

enter image description here

#!/usr/bin/env python3
from tkinter import *
from functools import partial
from itertools import product

# produce the set of coordinates of the buttons
positions = product(range(10), range(10))
button_ids = []

def change(i):
    # get the button's identity, destroy it
    bname = (button_ids[i])
    bname.destroy()

win = Tk()
frame = Frame(win)
frame.pack()

for i in range(10):
    # shape the grid
    setsize = Canvas(frame, width=30, height=0).grid(row=11, column=i)
    setsize = Canvas(frame, width=0, height=30).grid(row=i, column=11)

for i, item in enumerate(positions):
    button = Button(frame, command=partial(change, i))
    button.grid(row=item[0], column=item[1], sticky="n,e,s,w")
    button_ids.append(button)

win.minsize(width=270, height=270)
win.title("Too many squares")
win.mainloop()

更多选项,按坐标销毁按钮

由于product()也产生按钮的x,y坐标,我们还可以存储坐标(在示例中为coords),并通过坐标识别按钮的标识。 / p>

在下面的示例中,函数hide_by_coords():按坐标销毁按钮,这在minesweeper类游戏中非常有用。例如,单击一个按钮也会破坏右侧的按钮:

#!/usr/bin/env python3
from tkinter import *
from functools import partial
from itertools import product

positions = product(range(10), range(10))
button_ids = []; coords = []

def change(i):
    bname = (button_ids[i])
    bname.destroy()
    # destroy another button by coordinates
    # (next to the current one in this case)
    button_nextto = coords[i]
    button_nextto = (button_nextto[0] + 1, button_nextto[1])
    hide_by_coords(button_nextto)

def hide_by_coords(xy):
    # this function can destroy a button by coordinates
    # in the matrix (topleft = (0, 0). Argument is a tuple
    try:
        index = coords.index(xy)
        button = button_ids[index]
        button.destroy()
    except (IndexError, ValueError):
        pass

win = Tk()
frame = Frame(win)
frame.pack()

for i in range(10):
    # shape the grid
    setsize = Canvas(frame, width=30, height=0).grid(row=11, column=i)
    setsize = Canvas(frame, width=0, height=30).grid(row=i, column=11)

for i, item in enumerate(positions):
    button = Button(frame, command=partial(change, i))
    button.grid(column=item[0], row=item[1], sticky="n,e,s,w")
    button_ids.append(button)
    coords.append(item)

win.minsize(width=270, height=270)
win.title("Too many squares")
win.mainloop()

答案 1 :(得分:2)

如果您只想销毁Button小部件,那么简单的方法是在创建按钮后添加回调。例如,

import Tkinter as tk

grid_size = 10

root = tk.Tk()
blank = " " * 3
for y in range(grid_size):
    for x in range(grid_size):
        b = tk.Button(root, text=blank)
        b.config(command=b.destroy)
        b.grid(column=x, row=y)

root.mainloop()

但是,如果您需要在回调中进行额外处理,例如更新按钮网格,则可以方便地将Button的网格索引存储为Button对象的属性。

from __future__ import print_function
import Tkinter as tk

class ButtonDemo(object):
    def __init__(self, grid_size):
        self.grid_size = grid_size
        self.root = tk.Tk()
        self.grid = self.button_grid()
        self.root.mainloop()

    def button_grid(self):
        grid = []
        blank = " " * 3
        for y in range(self.grid_size):
            row = []
            for x in range(self.grid_size):
                b = tk.Button(self.root, text=blank)
                b.config(command=lambda widget=b: self.delete_button(widget))
                b.grid(column=x, row=y)
                #Store row and column indices as a Button attribute
                b.position = (y, x)
                row.append(b)
            grid.append(row)
        return grid

    def delete_button(self, widget):
        y, x = widget.position
        print("Destroying", (y, x))
        widget.destroy()
        #Mark this button as invalid 
        self.grid[y][x] = None

ButtonDemo(grid_size=10)

这两个脚本都与Python 3兼容,只需将导入行更改为

即可
import tkinter as tk

答案 2 :(得分:1)

尝试按以下方式修改代码:

self.b=[[0 for x in range(10)] for y in range(10)] #The 2 dimensional list
xp = yp = 0
for i in range(10):
    for j in range(10):
        self.b[i][j]=tkinter.Button(root,text="     ",command=lambda i=i,j=j: self.delete(i,j)) # creating the button
        self.b[i][j].place(x=xp,y=yp) # placing the button
        xp+=26 #because the width and height of the button is 26
    yp+=26
    xp=0

def delete(self, i, j):
    self.b[i][j].destroy()

答案 3 :(得分:0)

以下代码在每行中生成12个按钮,4。 需要编辑的特定按钮的调用类似于调用矩阵元素。作为示例,按钮[1,1]已被编辑为背景色,按钮[2,2]已被编辑为前景色和文本。该程序已在python3.6 pycharm控制台上进行了测试

from tkinter import *
root=Tk()
Buts={}
for r in range(3):
    for c in range(4):
        Buts[(r,c)]=Button(root,text='%s/%s'%(r,c),borderwidth=10)
        Buts[r,c].grid(row=r,column=c)
Buts[1,1]['bg']='red'
Buts[2,2]['text']=['BUTTON2']
Buts[2,2]['fg']=['blue']
root.mainloop()

答案 4 :(得分:0)

有一种方法可以将参数传递给按下按钮时执行的函数:

from tkinter import *
from functools import partial
root = Tk()
root.geometry("300x200")

b = Button(root, text = "some text", command=partial(yourfunc, argument))
b.pack()

root.mainloop()