Search code examples
pythoncanvastkintera-star

Why is my Tkinter canvas displaying?


I am trying to produce a star algorithm using Tkinter 2d graphical user interface. I have modified the following code having found it originally on here:

from tkinter import *

class CellGrid(Canvas):
    def __init__(self,master, rowNumber, columnNumber, cellSize, theMap):
        Canvas.__init__(self, master, width = cellSize * columnNumber , height = cellSize * rowNumber)

        self.cellSize = cellSize

        self.grid = []
        for row in range(rowNumber):
            line = []
            for column in range(columnNumber):
                line.append(Cell(self, column, row, cellSize, theMap[row][column]))
            self.grid.append(line)

        print (self.grid[0][0].value)
        self.draw()

    def draw(self):
        for row in self.grid:
            for cell in row:
                cell.draw()

class Cell():
    colors = {
            0: 'white',    # untried
            1: 'black',    # obstacle
            2: 'green',    # start
            3: 'red',      # finish
            4: 'blue',     # open
            5: 'gray',     # closed
            6: 'orange',   # path
         }

    def __init__(self, master, x, y, size, value):
        self.master = master
        self.abs = x
        self.ord = y
        self.size= size
        self.fill = "white"
        self.value = value


    def setValue(self, value):
        self.value = value

    def draw(self):
         if self.master != None :
            if self.value == 0:
                self.fill = self.white
            elif self.value == 1:
                self.fill = self.black
            elif self.value == 2:
                self.fill = self.green
            elif self.value == 3:
                self.fill = self.red
            elif self.value == 4:
                self.fill = self.blue
            elif self.value == 5:
                self.fill = self.gray
            elif self.value == 6:
                self.fill = self.orange

            xmin = self.abs * self.size
            xmax = xmin + self.size
            ymin = self.ord * self.size
            ymax = ymin + self.size

            self.master.create_rectangle(xmin, ymin, xmax, ymax, fill = self.fill, outline = "black")

def main():
    Map = [
            [2, 0, 0, 0, 0],
            [0, 1, 1, 1, 1],
            [0, 1, 3, 0, 0],
            [0, 1, 1, 1, 0],
            [0, 0, 0, 0, 0],
          ]
    root = Tk()
    c = tk.Canvas(root, height=1000, width=1000, bg='white')
    my_gui = CellGrid(root, len(Map), len(Map[0]), 40, Map)


    root.mainloop()

But when I go to run it, nothing displays.


Solution

  • A lot of small and not-small misunderstandings are blocking the display of the star algorithm.

    Problem 1 - incomplete calling main() function.

    As @avysk suggests as comment, the first blocking point is due to the missing of main() preamble in Python.

    Just add the following code after the def main() function:

    if __name__ == '__main__':
        main()
    

    Problem 2 - typo and missing pack() to the main Canvas display.

    Because the tk symbol is not defined, the main Canvas shall be declared as follow:

    c = Canvas(root, height=1000, width=1000, bg='white')
    

    Instead of:

    c = tk.Canvas(root, height=1000, width=1000, bg='white')
    

    And after the magic missing call is pack():

    c.pack() # magic call to display the (1000 x 1000) white canvas
    

    Problem 3 - missing call to the secondary Canvas display.

    As for the main Canvas, it was missing the call of pack(), two possibilities could be used to display the secondary Canvas.

    1. Add my_gui.pack() to fit the main Canvas to the size of the secondary Canvas,
    2. Or my preferred solution, add my_gui.place(relx=0.5, rely=0.5, anchor=CENTER) to center the secondary Canvas to the main Canvas.

    To display the secondary Canvas:

    my_gui = CellGrid(c, len(Map), len(Map[0]), 40, Map)
    my_gui.place(relx=0.5, rely=0.5, anchor=CENTER)
    

    Problem 4 - misunderstanding of dictionary use in the Cell::draw() function.

    The displayed error is "AttributeError: 'Cell' object has no attribute 'green'".

    The value of the first cell self.grid[0][0].value = 2 and when drawing the cell, the following self.fill = self.green generates the error.

    Three possibilities could be used to solve that error:

    1. Replace the self.fill = self.<color> (7 cases) by self.fill = '<color>',
    2. Use the declared dictionary colors and replace the 7 cases by a unique call.

    The use of dictionary could be (sol 2):

    if 0 <= self.value <= 6: # check if value in range of colors
        self.fill = Cell.colors[self.value]
    

    Instead of the long if-elif (sol 1):

        if self.value == 0:
            self.fill = 'white' # Error: self.white
        elif self.value == 1:
            self.fill = 'black' # Error: self.black
        elif self.value == 2:
            self.fill = 'green' # Error: self.green
        elif self.value == 3:
            self.fill = 'red' # Error: self.red
        elif self.value == 4:
            self.fill = 'blue' # Error: self.blue
        elif self.value == 5:
            self.fill = 'gray' # Error: self.gray
        elif self.value == 6:
            self.fill = 'orange' # Error: self.orange