如何识别在循环中创建的按钮? [英] How can I identify buttons, created in a loop?

查看:18
本文介绍了如何识别在循环中创建的按钮?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 tkinter 在 python 上编写扫雷游戏.我首先使用 10x10 的二维列表创建了一个按钮网格.然后我使用循环创建了每个按钮,这样我就不必手动创建每个按钮并将它们夹在上面.

self.b=[[0 for x in range(1,12)] for y in range(1,12)] #二维列表对于范围(1,11)中的 self.i:对于范围(1,11)中的 self.j:self.b[self.i][self.j]=tkinter.Button(root,text = (" "),command = lambda: self.delete()) # 创建按钮self.b[self.i][self.j].place(x=xp,y=yp) # 放置按钮xp+=26 #因为按钮的宽高都是26yp+=26xp=0

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

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

我不知道如何让程序知道用户按下了哪个按钮,因此它可以删除那个特定的按钮.

问题:有没有办法让每个按钮都有一些独特的东西,让它与其他按钮区分开来?说给每个按钮分配一个特定的坐标,那么当按钮(2,3)被按下时,数字2和3被传递给删除函数,所以删除函数可以删除按钮(2,3)?

解决方案

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

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

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

我们可以立即识别:

打印(按钮)><tkinter.Button对象.140278326922376>

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

我们唯一要做的就是在按下按钮后通过索引获取按钮的标识.

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

简化示例(python3)

在下面的简化示例中,我们在循环中创建按钮,将它们的身份添加到列表中 (button_identities).身份是通过查找它来获取的:bname = (button_identities[n]).

现在我们有了身份,接下来我们可以让按钮做任何事情,包括编辑或杀死自己,因为我们有了它的身份.

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

from tkinter import *从 functools 导入部分赢 = Tk()button_identities = []定义更改(n):# 获取索引和身份(bname)的函数打印(n)bname = (button_identities[n])bname.configure(text = "点击")对于范围内的 i (5):# 创建按钮,分配一个唯一的参数 (i) 来运行函数 (change)button = Button(win, width=10, text=str(i), command=partial(change, i))按钮.pack()# 将按钮的标识添加到列表中:button_identities.append(按钮)# 只是为了显示发生了什么:打印(button_identities)win.mainloop()

或者如果我们让它销毁按钮一旦点击:

from tkinter import *从 functools 导入部分赢 = Tk()button_identities = []定义更改(n):# 获取索引和身份(bname)的函数打印(n)bname = (button_identities[n])bname.destroy()对于范围内的 i (5):# 创建按钮,分配一个唯一的参数 (i) 来运行函数 (change)button = Button(win, width=10, text=str(i), command=partial(change, i))button.place(x=0, y=i*30)# 将按钮的标识添加到列表中:button_identities.append(按钮)# 只是为了显示发生了什么:打印(button_identities)win.mainloop()

矩阵的简化代码(python3):

在下面的示例中,我使用了

#!/usr/bin/env python3从 tkinter 导入 *从 functools 导入部分从 itertools 导入产品# 生成按钮的坐标集位置 = 产品(范围(10),范围(10))button_ids = []定义更改(i):# 获取按钮的身份,销毁它bname = (button_ids[i])bname.destroy()赢 = Tk()框架 = 框架(赢)框架.pack()对于范围内的我(10):# 塑造网格setsize = Canvas(frame, width=30, height=0).grid(row=11, column=i)setsize = Canvas(frame, width=0, height=30).grid(row=i, column=11)对于 i,枚举(位置)中的项目:button = Button(frame, command=partial(change, i))button.grid(row=item[0], column=item[1],sticky="n,e,s,w")button_ids.append(按钮)win.minsize(宽=270,高=270)win.title("方格太多")win.mainloop()

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

由于 product() 还生成按钮的 x,y 坐标,我们可以额外存储坐标(在示例中的 coords 中),并且通过坐标识别按钮的身份.

在下面的例子中,函数 hide_by_coords(): 通过坐标销毁按钮,这在类似 minesweeper 的游戏中很有用.例如,单击一个按钮也会破坏右侧的按钮:

#!/usr/bin/env python3从 tkinter 导入 *从 functools 导入部分从 itertools 导入产品位置 = 产品(范围(10),范围(10))button_ids = [];坐标 = []定义更改(i):bname = (button_ids[i])bname.destroy()# 通过坐标销毁另一个按钮#(在这种情况下在当前的旁边)button_nextto = 坐标[i]button_nextto = (button_nextto[0] + 1, button_nextto[1])hide_by_coords(button_nextto)def hide_by_coords(xy):# 这个函数可以通过坐标销毁按钮# 在矩阵中 (topleft = (0, 0).参数是一个元组尝试:索引 = coords.index(xy)按钮 = button_ids[索引]button.destroy()除了(索引错误,值错误):经过赢 = Tk()框架 = 框架(赢)框架.pack()对于范围内的我(10):# 塑造网格setsize = Canvas(frame, width=30, height=0).grid(row=11, column=i)setsize = Canvas(frame, width=0, height=30).grid(row=i, column=11)对于我,枚举(位置)中的项目:button = Button(frame, command=partial(change, i))button.grid(column=item[0], row=item[1],sticky="n,e,s,w")button_ids.append(按钮)coords.append(item)win.minsize(宽=270,高=270)win.title("方格太多")win.mainloop()

I am trying to program a minesweeper game on python using tkinter. I started off by creating a grid of buttons using a two dimensional list of 10x10. Then I created each button using a loop just so I don't have to manually create every single button and clip them on.

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

Basically I want the button to disappear upon being pressed. The problem is that I don't know how to let the program delete specifically the button that I pressed, as all the buttons are exactly the same. When creating the delete function:

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

I don't know how to let the program know which button it was that the user presses, so it can delete that specific one.

The question: Is there a way to let each button have something unique that allows it to be differentiated from the other buttons? Say assign each button a specific coordinate, so when button (2,3) is pressed, the numbers 2 and 3 are passed onto the delete function, so the delete function can delete button (2,3)?

解决方案

While creating buttons in a loop, we can create (actually get) the unique identity.

For example: if we create a button:

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

we can identify it immediately:

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

If we store this identity into a list and asign a command to the button(s), linked to their index during creation, we can get their specific identity when pressed.

The only thing we have to to then is to fetch the button's identity by index, once the button is pressed.

To be able to set a command for the buttons with the index as argument, we use functools' partial.

Simplified example (python3)

In the simplified example below, we create the buttons in a loop, add their identities to the list (button_identities). The identity is fetched by looking it up with: bname = (button_identities[n]).

Now we have the identity, we can subsequently make the button do anything, including editing- or killing itself, since we have its identity.

In the example below, pressing the button will change its label to "clicked"

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()

Or if we make it destroy the buttons once clicked:

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()

Simplified code for your matrix (python3):

In the example below, I used itertools's product() to generate the coordinates for the matrix.

#!/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()

More options, destroying a button by coordinates

Since product() also produces the x,y coordinates of the button(s), we can additionally store the coordinates (in coords in the example), and identify the button's identity by coordinates.

In the example below, the function hide_by_coords(): destroys the button by coordinates, which can be useful in minesweeper -like game. As an example, clicking one button als destroys the one on the right:

#!/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()

这篇关于如何识别在循环中创建的按钮?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆