Whipfred
Whipfred

Reputation: 23

NameError (Class) in python

I'm currently working on a project (for fun) which consists of creating a fully playable Chess game! I tried to make a "structure" in my project and separated all my classes in different files... I also have a luncher.py file that you need to execute to launch the game. But I get an error when executing it:

Traceback (most recent call last):
  File "d:/Paul/Document/Python/Projet Echecs/luncher.py", line 12, in <module>
    from classes.ChessBoard import *
  File "d:\Paul\Document\Python\Projet Echecs\classes\ChessBoard.py", line 4, in <module>
    from classes.Pieces import *
  File "d:\Paul\Document\Python\Projet Echecs\classes\Pieces.py", line 15, in <module>
    class Pieces(ChessBoard):
NameError: name 'ChessBoard' is not defined

Here is the structure of the project:

Here is the code of each file:

luncher.py:

'''Ce fichier est le fichier root du projet! 
C'est lui qu'i faut executer pour le lancer!
'''

#On importe les modules necessaires
from tkinter import *
from classes.ChessBoard import *

#On creer un fenetre, puis l'échiquier (On appelle la classe ChessBoard)

fenetre = Tk()
fenetre.title("Echecs")
test = ChessBoard(fenetre, 650, 650)
fenetre.mainloop()

ChessBoard.py:

# On importe tous les modules necessaire au bon fonctionnement de la classe
from tkinter import *
from classes.Pieces import *
import os

# Creation de la classe

class ChessBoard:
    # On initialise l'objet
    def __init__(self, window, height=650, width=650, color="#546760", wdCase=76):
        '''
        Parametre obligatoire: - une fenetre TKinter
        Parametres facultatifs: -hauteur de la fenetre
                                - largeur de la fenetre
                                - couleur des cases
                                - largeur des cases
        '''

        self.height = height
        self.width = width
        self.backgroundColor = color
        self.wdCase = wdCase
        self.window = window
        self.game = self.getGame()


        self.rows = ["1", "2", "3", "4", "5", "6", "7", "8"]
        self.columns = ["A","B","C","D","E","F","G","H"]

        # On creer le plateau
        self.board = Canvas(self.window, height=self.height, width=self.width)
        self.board.pack()

        self.case = [[self.board.create_rectangle(i * self.wdCase, j * self.wdCase, (i+1) * self.wdCase, (j+1) * self.wdCase,fill = "#F3F3F3")for i in range(8)] for j in range(8)]
        self.board.create_line(2, 2, 2, self.wdCase * 8)
        self.board.create_line(2, 2, self.wdCase * 8, 2)

        # On implemente les couleurs des cases qui peuvent etre modifiées en passant color=.... en parametre

        for i in range(0,8,2):
            for j in range(1,9,2):
                self.board.itemconfigure(self.case[i][j],fill=self.backgroundColor)
                self.board.itemconfigure(self.case[j][i],fill=self.backgroundColor)

        # On implemente les labels de lignes et de colonnes : A-F et de 1-8
        # On créer une variable locale pour les coordonnées des lettres et des chiffres a place dans le Canvas
        textPositionX = self.wdCase / 2
        textPositionY = self.wdCase / 2
        a = (self.wdCase) * 8 + (self.wdCase / 4)

        for i in range(8):

            self.board.create_text(textPositionX, a, text=self.columns[i])
            self.board.create_text(a, textPositionY, text=self.rows[-i - 1])
            textPositionX += self.wdCase
            textPositionY += self.wdCase

        # On peut maintenant générer les pieces

        test = Rook(303, 532, "Noir")

    def getGame(self):
        """
            Cette méthode de la classe ChessBoard permet de récuperer une partie en cours si il en existe
            une et dans le cas contraire créer une nouvelle partie.

            Dans tous les cas, la methode renvoie un tableau décrivant la disposition des pieces sur le plateau.
        """

        # On verifie si le fichier saves.txt existe
        if os.path.exists("saves.txt"):
            # Si il existe, on l'ouvre et on recupere les donnees
            with open("save.txt") as save:
                #A FINIR
                pass
        else:
            #Sinon on creer une nouvelle partie
            piece = [["."]*8]*8
            piece[0] = ["TN", "CN", "FN", "DN", "RN", "FN", "CN", "TN"]
            piece[1] = ["PN"]*8
            piece[7] = ["TB", "CB", "FB", "DB", "RB", "FB", "CB", "TB"]
            piece[6] = ["PB"]*8

            return piece

Pieces.py

'''Ce module contient la classe Piece'''

# On importe les modules necessaires
from tkinter import *
from classes.ChessBoard import *

# On peu modifier la variable path pour charger les images
path = "D:\Paul\Document\Python\Projet Echecs\img\Pieces\\"


class Pieces(ChessBoard):

    def __init__(self, X, Y, color):
        self.color = color
        self.coordinateX = X
        self.coordinatY = Y

and finally Rook.py:

from tkinter import *
from classes.ChessBoard import *
from classes.Pieces import *


class Rook(Pieces):

    def __init__(self, X, Y, color):

        Pieces.__init__(self, X, Y, color)

        self.img = PhotoImage(file=path + "T{}.gif".format(self.color[0].upper()))
        self.board.create_image(self.coordinatX, self.coordinatY, anchor=NW, image=self.img)

Upvotes: 2

Views: 277

Answers (1)

Martijn Pieters
Martijn Pieters

Reputation: 1121484

You have a circular import.

In ChessBoard you import Pieces:

from classes.Pieces import *

At this point ChessBoard is basically empty, because the only line that completed executing is from tkinter import *.

And during the import of Pieces you then import that empty ChessBoard module:

from classes.ChessBoard import *

There is no ChessBoard class at this point; all you imported are the tkinter names.

You can see this reflected in the traceback:

  File "d:/Paul/Document/Python/Projet Echecs/luncher.py", line 12, in <module>
    from classes.ChessBoard import *

ChessBoard is imported by luncher.py

  File "d:\Paul\Document\Python\Projet Echecs\classes\ChessBoard.py", line 4, in <module>
    from classes.Pieces import *

which, on line 4, imports Pieces, and

  File "d:\Paul\Document\Python\Projet Echecs\classes\Pieces.py", line 15, in <module>
    class Pieces(ChessBoard):

tries to use ChessBoard, but that class is only defined several lines after line 4.

If you must have access to the Pieces class in ChessBoard, you will have to move the import line to below the class ChessBoard definition.

However, I think your fundamental mistake here is to make the Pieces class inherit from ChessBoard at all. Chess pieces are not the chess board! If you must share some functionality between the ChessBoard class and the Pieces class, factor that out into a separate module that both then use.

Class inheritance is a way of sharing functionality where this makes sense and to categorise your classes. The chess board, and the pieces on the board, don't share functionality usually; the board doesn't make moves, or belong to one player or the other, for example.

Not inheriting from the ChessBoard class would simplify your other two modules to:

'''Ce module contient la classe Piece'''

# On peu modifier la variable path pour charger les images
path = "D:\\Paul\\Document\\Python\\Projet Echecs\\img\\Pieces\\"

class Pieces:
    def __init__(self, board, X, Y, color):
        self.board
        self.color = color
        self.coordinateX = X
        self.coordinatY = Y

and

from tkinter import *
from classes.Pieces import path, Pieces

class Rook(Pieces):
    def __init__(self, board, X, Y, color):
        Pieces.__init__(self, board, X, Y, color)

        self.img = PhotoImage(file=path + "T{}.gif".format(self.color[0].upper()))
        self.board.create_image(self.coordinatX, self.coordinatY, anchor=NW, image=self.img)

Note that I also gave the Pieces class a board argument, as you use that in Rook.__init__(). So when you create the Rook instance, pass in the board with:

test = Rook(self, 303, 532, "Noir")

Upvotes: 3

Related Questions