我正在尝试为游戏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()
答案 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)