问题
Wrote the following code to display a chessboard using Tkinter in Python:
import tkinter as tk
class Gui(tk.Tk):
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super().__new__(cls, *args, **kwargs)
cls._instance.__initialized = False
return cls._instance
def __init__(self):
if self.__initialized:
return
self.__initialized = True
super().__init__()
class Color(object):
white =(0xff,0xff,0xff)
black =(0x00,0x00,0x00)
class Tile(tk.PhotoImage):
@staticmethod
def putTile(image, color, width, height, coordinates):
pix = "#%02x%02x%02x" % color
coor = (coordinates[0]*width, coordinates[1]*height)
line = "{" + " ".join([pix]*width) + "}"
image.put(" ".join([line]*height),coor)
class Grid(tk.PhotoImage):
def __init__(self,grid):
super().__init__(width=10*len(grid), height=10*len(grid[0]))
for x in range(len(grid)):
for y in range(len(grid[x])):
Tile.putTile(self,Color.white if grid[x][y]==1 else
Color.black,10,10,(x,y))
class ChessBoard(Grid):
chessboard = 4 * ([4 * [0,1]] + [4 * [1,0]])
def __init__(self):
super().__init__(self.chessboard)
So Gui() is implemented as a singleton pattern. Also tk.Tk.__init__() is made to be called only once, otherwice I got a window everytime Gui() was called.
I would expect the following to display a window with a chessboard:
Case 1:
label = tk.Label(Gui(), image=ChessBoard())
label.pack()
Gui().mainloop()
This creates an empty window without errors or warnings. A print statement shows that method tilePut is indeed called.
Only when I add an additional Gui() statement in my program, as shown below, everyting works perfectly and a chessboard is printed.
Case 2:
Gui()
label = tk.Label(Gui(), image=ChessBoard())
label.pack()
Gui().mainloop()
So I guess the image.put call requires a Gui() to exist. Though if I try the following code:
Case 3:
board = ChessBoard()
label = tk.Label(Gui(), image=board)
label.pack()
Gui().mainloop()
I get an error about calling image.put too soon. Considering I do not get this same error in case 1, I am surprised case 1 does not work. Can anyone explain why?
回答1:
The answer to your questions boils down to two factors:
- The root window must exist before you can create an image
- You must keep a reference to the image or tkinter will destroy the image data when it does garbage collection.
The proper way to use this code would be to first create an instance of Gui, then create Chessboard and save what is returned in a variable. You can then use these references in the rest of your code.
This is the common way to accomplish that:
root = Gui()
chessboard = ChessBoard()
label = tk.Label(root, image=chessboard)
label.pack()
root.mainloop()
Since you are defining Gui as a singleton, the following will also work. However, I think the use of a singleton adds complexity and makes the code less clear since it looks like you are creating three instances of Gui:
Gui()
chessboard = ChessBoard()
label = tk.Label(Gui(), image=chessboard)
label.pack()
Gui().mainloop()
来源:https://stackoverflow.com/questions/39316971/issue-using-singleton-pattern-with-tkinter-photoimage-tk-in-python