issue using Singleton pattern with tkinter Photoimage/Tk in python

六眼飞鱼酱① 提交于 2019-12-13 08:19:47

问题


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:

  1. The root window must exist before you can create an image
  2. 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!