(实例化一组按钮,但只有一个工作

时间:2015-09-27 09:09:19

标签: python arrays object button tkinter

我正在尝试为游戏Go创建虚拟棋盘的GUI。应该有一个n x n网格,玩家可以放置一块黑色或白色的石头。单击图块将使其从棕褐色(默认)更改为黑色,再次单击为白色,然后单击第三次返回棕褐色。玩家可以在一个地点点击一次以在那里放置一块石头,玩家二可以点击两次(你需要稍后移除石头,所以三次点击重置它)。我创建了一个tile对象,然后使用嵌套的for循环来实例化9乘9。不幸的是,运行代码似乎只产生1个功能区块,而不是81.这个代码应该适用于任何python机器(我使用Python 3.4),因此您可以尝试运行它并亲自查看。谁能指出循环只运行一次的原因?

from tkinter import *
window = Tk()
n = 9

"""
A tile is a point on a game board where black or white pieces can be placed. If there are no pieces, it remains tan.
The basic feature is the "core" field which is a tkinter button. when the color is changed, the button is configured to represent this.
"""
class tile(object):
    core = Button(window, height = 2, width = 3, bg = "#F4C364")

    def __init__(self):
        pass

    """the cycle function makes the tile object actually change color, going between three options: black, white, or tan."""
    def cycle(self):

        color = self.core.cget("bg")

        if(color == "#F4C364"): #tan, the inital value.
            self.core.config(bg = "#111111")#white.
        elif (color == "#111111"):
            self.core.config(bg = "#DDDDDD")#black.
        else:
            self.core.config(bg = "#F4C364")#back to tan.

board = [] #create overall array
for x in range(n):
    board.append([])#add subarrays inside it
    for y in range(n):
        board[x].append(tile())#add a tile n times in each of the n subarrays
        T = board[x][y] #for clarity, T means tile
        T.core.config(command = lambda: T.cycle()) #I do this now because cycle hadn't been defined yet when I created the "core" field
        T.core.grid(row = x, column = y) #put them into tkinter.

window.mainloop()

3 个答案:

答案 0 :(得分:0)

问题是core是一个类变量,它由创建一次并由类tile的所有实例共享。它应该是每个tile实例的实例变量。

core = Button(window, height = 2, width = 3, bg = "#F4C364")移动到tile.__init__(),如下所示:

class Tile(object):
    def __init__(self):
        self.core = Button(window, height = 2, width = 3, bg = "#F4C364")

答案 1 :(得分:0)

正如mhawke在他的回答中指出的那样,你需要使core成为一个实例变量,这样每个Tile都有自己的核心。

正如我在上面的评论中提到的,你还需要修复Button的命令回调函数。您在问题中使用的代码将调用当前值.cycle()的{​​{1}}方法,该方法恰好是创建的最后一个图块。所以无论你在哪里点击最后一个瓷砖都会改变颜色。解决这个问题的一种方法是在创建时将当前磁贴作为T函数的默认参数传递。但是因为你正在使用OOP来创建你的Tile,所以有一种更好的方法,你可以在下面看到。

我对您的代码进行了一些修改。

尽管许多Tkinter示例使用lambda,但这不是一个好习惯。执行from tkinter import *时,它会将from some_module import *中的所有名称带到当前模块(您的脚本)中,这意味着您可能会意外地使用您自己的名称覆盖这些名称。更糟糕的是,如果你使用多个模块进行some_module,每个新模块的名称都会与之前模块的名称冲突,而你无法知道发生了什么,直到你开始收到神秘的错误。使用import *意味着您需要进行更多的输入,但这会使得生成的程序不易出错并且更容易阅读。

我修改了import tkinter as tk方法,以便用窗口和图块的(x,y)位置调用它(通常使用x表示水平坐标,y表示垂直坐标) 。每个Tile对象现在跟踪其当前状态,其中0 =空,1 =黑色,2 =白色。这样可以更轻松地更新颜色。因为我们已经通过窗口和(x,y),我们可以使用该信息将图块添加到网格中。磁贴还会记住位置(在self.location中),这可能会派上用场。

我修改了__init__方法,以便更新磁贴的背景颜色和 activebackground 。因此,当鼠标悬停在磁贴上时,它会变为一种颜色(大致)在其当前颜色和它点击它将转动的颜色之间的一半。 IMO,这比当鼠标悬停在它上面时总是变成浅灰色的瓷砖更好。

我还优化了创建所有切片的代码,并将它们存储在列表的列表中。

cycle

答案 2 :(得分:0)

问题的根源在于core由于您已经如何定义它而被所有类实例共享。您需要将按钮的创建移动到初始化程序中。

我还建议将命令的配置移动到按钮本身。调用者不应该(也不关心)按钮如何在内部工作。我个人认为,瓷砖继承自Button,但如果你喜欢构图而不是继承,我会坚持这一点。

示例:

class tile(object):

    def __init__(self):
        self.core = Button(window, height = 2, width = 3, bg = "#F4C364"
                           command=self.cycle)