首页 > 解决方案 > 具有 2 个不同类别的 OOP 的 Tkinter,带有“循环导入”错误

问题描述

我正在做一个国际象棋游戏。我在“Window”类中用 Tkinter 代码创建了一个主文件。在这堂课中,我创建了一个画布。然后,我创建了第二个名为“pieces”的文件,其中放置了不同部分的行为。在这一个中,我有一个超类“Pieces”和一个子类“Bishop”(因为我还没有为其他作品创建类)

我首先尝试做的是在“Bishop”类的构造函数中创建一个主教图标。我的类“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)

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

并且要在类bishop中创建图像,我需要导入模块main(因为我使用canvas.create_image(),而canvas在类Windows中)

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

这是代码,非常简化

主文件

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()

碎片.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)

标签: pythonooptkintercanvas

解决方案


如果您熟悉编写代码的模型/视图方法,它将帮助您找到解决tkinter应用程序的方法。在这种情况下,您会将与视图相关的所有代码放在一个类中,并且所有数据都在 Model 类中进行管理。

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

# 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()

推荐阅读