Tkinter 与 OOP 在 2 个不同的 类,有 "circular imports" 错误

Tkinter with OOP in 2 different classes, with the "circular imports" error

我正在制作国际象棋游戏。 我在 class “Window” 中创建了一个包含 Tkinter 代码的主文件。在这个class中,我创建了一个canvas。 然后我创建了第二个名为“pieces”的文件,我在其中放置了不同片段的行为。在这一个中,我有一个 superclass“Pieces”和一个 subclass“Bishop”(因为我还没有为其他作品创建 classes)

我首先尝试做的是在 class“Bishop”的构造函数中创建一个主教的图标。 我的class“Bishop”有参数“color”,所以当我们创建一个对象“Bishop”时,我们可以选择他是黑色还是白色。 所以我写道:

if self.color == "black":
    icon_path = 'black_bishop.png'
elif self.color == "white":
    icon_path = 'white_bishop.png'

self.icon = PhotoImage(file=icon_path)
main.Window.canvas.create_image(x_position, y_position, self.icon)

问题是,当我创建主教的新对象时,它会产生一个循环。

我真的不知道如何解决这个问题,一个解决方案是将整个代码放在同一个文件中,但我不喜欢它,因为它不干净。

如果你需要整个代码,我可以给你。

这是完整的错误:

Traceback (most recent call last):
  File "C:\Users\CSP\PycharmProjects\chess_game\main.py", line 2, in <module>
    import pieces
  File "C:\Users\CSP\PycharmProjects\chess_game\pieces.py", line 3, in <module>
    import main
  File "C:\Users\CSP\PycharmProjects\chess_game\main.py", line 28, in <module>
    fen = Window()
  File "C:\Users\CSP\PycharmProjects\chess_game\main.py", line 20, in __init__
    pieces.Bishop("black", 5, 5)
AttributeError: partially initialized module 'pieces' has no attribute 'Bishop' (most likely due to a circular import)

我在文件 pieces 中写了 import main,因为我想在 class Bishop 的构造函数中创建图像 ,我想它比 class Windows 更干净,因为我必须做 32 次(每件一件)而且它会很重。

而要在 class 主教中创建图像,我需要导入模块 main(因为我使用 canvas.create_image(),而 canvas 在 class Windows)

但是如果我写 import main 它会形成一个循环,那么你有解决这个问题的想法吗?

这是代码,非常简单

main.py

from tkinter import *
import pieces

#GUI class
class Window:
    def __init__(self):
        self.window = Tk()
        self.window.title("My chess game")

        self.frame = Frame(self.window, bg='#41B77F')
        self.canvas = Canvas(self.frame, width=500, height=500, bg="skyblue", bd=0, highlightthickness=0)
        self.canvas.pack()
        bishop = Bishop("black",5 , 5)
        self.frame.pack(expand=YES)


win = Window()
win.window.mainloop()

pieces.py

from tkinter import PhotoImage
import main

#superclass
class Pieces:
    def __init__(self, color, x_position, y_position):
        self.color = color
        self.x_position = x_position
        self.y_position = y_position

#subclass
class Bishop(Pieces):
    def __init__(self, color, x_position, y_position):
        super().__init__(color, x_position, y_position)

        if self.color == "black":
            icon_path = 'black_bishop.png'
        elif self.color == "white":
            icon_path = 'white_bishop.png'

        self.icon = PhotoImage(file=icon_path)
        main.Window.canvas.create_image(x_position, y_position, image=self.icon)

如果您熟悉 Model/View 编写代码的方法,它将帮助您找到解决 tkinter 应用程序的方法。在这种情况下,您可以将与视图相关的所有代码放在一个 class 中,并且所有数据都在模型 class(es).

中进行管理

对于您的情况,您可以从下图所示的结构开始并从中发展:

# model.py
from tkinter import *
class GameModel():
    # maintains game state/data
    # may include positions of pieces on board
    # number of moves made by each player
    pass

#superclass, inherits from Tk().Canvas 
class Pieces(Canvas):
    def __init__(self, color, x_position, y_position):
        self.color = color
        self.x_position = x_position
        self.y_position = y_position

#subclass
class Bishop(Pieces):
    def __init__(self, color, x_position, y_position):
        super().__init__(color, x_position, y_position)

        if self.color == "black":
            icon_path = 'black_bishop.png'
        elif self.color == "white":
            icon_path = 'white_bishop.png'

        self.icon = PhotoImage(file=icon_path)
        # main.Fenetre.canvas.create_image(x_position, y_position, image=self.icon)
        self.create_image(x_position, y_position, image=self.icon)
        # because bishop inherits from pieces which inherits from Canvas, its possible
        # to call .create_image() from within Bishop class using self.create_image()


# gameinterface.py
from tkinter import *
#GUI/View class
class GameInterface():
    def __init__(self, window: Tk()):
        self.window = window
        self.frame = Frame(self.window, bg='#41B77F')
        self.canvas = Canvas(self.frame, width=500, height=500, bg="skyblue", bd=0, highlightthickness=0)
        self.canvas.pack()
        self.frame.pack(expand=YES)

    def play(self, game: GameModel):
        # the game objects give access to state of game and all data
        self.window.mainloop()

# main.py
from tkinter import *
def main() -> None:
    """Entry point to gameplay."""
    window = Tk()
    # window = Tk()

    #set title and position at center of screen
    window.title("My chess game") 

    # game object gives you access to all relevant data
    game = GameModel()

    # app object gives you access to view 
    app = GameInterface(window)

    # app.play combines the model and the view
    app.play(game)


if __name__ == '__main__':
    main()

如果您需要引用 Bishop 中的 window,您必须将其作为参数传递:

from tkinter import PhotoImage

class Pieces:
    def __init__(self, color, x_position, y_position):
        self.color = color
        self.x_position = x_position
        self.y_position = y_position

#subclass
class Bishop(Pieces):
    def __init__(self, win, color, x_position, y_position):
        super().__init__(color, x_position, y_position)

        if self.color == "black":
            icon_path = 'black_bishop.png'
        elif self.color == "white":
            icon_path = 'white_bishop.png'

        self.icon = PhotoImage(file=icon_path)
        win.canvas.create_image(x_position, y_position, image=self.icon)