Search code examples
pythonclasspython-imaging-libraryturtle-graphicspython-turtle

Python turtle project producing error: object has no attribute


I am trying to complete a final project for my Python course. The requirements are as follows:

  1. Use the concepts of object-based programming—classes, objects, and methods
  2. Draw two-dimensional shape
  3. Develop recursive algorithms to draw recursive shapes
  4. Write a nested loop to process a two-dimensional grid

I have written some code but I am having trouble getting it to produce a result in the turtle screen:

import turtle
from PIL import Image
kel = turtle.Turtle()

class Draw_pattern(object):
    def __init__(self,posx,posy,size):
        self.posx = posx
        self.posy = posy
        self.size = size

    def draw_grid(self,initx, inity):
        # Drawing grid
        kel.penup()
        kel.color("black")
        for i in range(7):
            kel.goto(self.posx, self.posy)
            kel.pendown()
            kel.forward(self.size*6)
            self.posy = self.posy+self.size;
            self.posx = initx;
            kel.penup()

        self.posx = initx;
        self.posy = inity;
        kel.left(90)
        for i in range(7):
            kel.goto(self.posx, self.posy)
            kel.pendown()
            kel.forward(self.size*6)
            self.posx = self.posx+self.size;
            self.posy = inity;
            kel.penup()

def draw_shape(self,initx, inity):
    self.posx = initx;
    self.posy = inity;
# Drawing yellow portion
    kel.penup()
    kel.goto(self.posx, self.posy)
    kel.pendown()
    kel.fillcolor("yellow")
    kel.begin_fill()
    kel.goto(self.posx, self.posy+self.size*6)
    kel.goto(self.posx+self.size*6, self.posy+self.size*6)
    kel.goto(self.posx+self.size*6, self.posy)
    kel.goto(self.posx, self.posy)
    kel.end_fill()

# Drawing red portion
    kel.penup()
    kel.goto(self.posx, self.posy+self.size*2)
    kel.pendown()
    kel.fillcolor("red")
    kel.begin_fill()
    kel.goto(self.posx+self.size, self.posy+self.size*3)
    kel.goto(self.posx, self.posy+self.size*4)
    kel.goto(self.posx+self.size*2, self.posy+self.size*4)
    kel.goto(self.posx+self.size*2, self.posy+self.size*6)
    kel.goto(self.posx+self.size*3, self.posy+self.size*5)
    kel.goto(self.posx+self.size*4, self.posy+self.size*6)
    kel.goto(self.posx+self.size*4, self.posy+self.size*4)
    kel.goto(self.posx+self.size*6, self.posy+self.size*4)
    kel.goto(self.posx+self.size*5, self.posy+self.size*3)
    kel.goto(self.posx+self.size*6, self.posy+self.size*2)
    kel.goto(self.posx+self.size*4, self.posy+self.size*2)
    kel.goto(self.posx+self.size*4, self.posy)
    kel.goto(self.posx+self.size*3, self.posy+self.size)
    kel.goto(self.posx+self.size*2, self.posy)
    kel.goto(self.posx+self.size*2, self.posy+self.size*2)
    kel.goto(self.posx, self.posy+self.size*2)
    kel.end_fill()

 # recursive function to draw repeating shape
def repeat_shape(self,intx, inty):
    if intx>=3 | inty>=3:
        return
    else:
        self.draw_shape(intx*180, inty*180)
        kel.right(90)
        self.repeat_shape(intx+1,inty)
        self.repeat_shape(intx,inty+1)

class image_play(object):
    def __init__(self,im_name):
        self.im_name = im_name

def rgb_to_gray_image(self):
    im = Image.open(self.im_name)
    im = im.convert('LA')

     # editing pixels of image to white
def loop_over_image(self):
    im = Image.open(self.im_name)
    width, height = im.size
    # nested loop over all pixels of image
    for i in range(width):
        for j in range(height):
            im[i][j] = 255;

kel.speed("fastest")

# object of class Draw_pattern
obj = Draw_pattern(0,0,30)
obj.draw_grid(0,0)
obj.repeat_shape(0,0)
AttributeErrorTraceback (most recent call last)
<ipython-input-14-8b7bbac27093> in <module>
    103 obj = Draw_pattern(0,0,30)
    104 obj.draw_grid(0,0)
--> 105 obj.repeat_shape(0,0)

AttributeError: 'Draw_pattern' object has no attribute 'repeat_shape'

Solution

  • I agree with the answer by @VasuDeo.S (+1) regarding your immediate problem. However, I see some more subtle issues with your code:

    • When it comes to your grid, your instructions say, "write a nested loop" but your two loops are sequential not nested.

    • When working with turtle, goto() is used for large jumps (and to get out of jams). For smaller movements, we tend to use forward(), backward(), left() and right() otherwise there's no difference between using turtle and a conventional line drawing graphics package.

    • Your self.posx and self.posy instance variables serve no purpose beyond being convenient local variables. They save no state that carries from one method to the other. We can eliminate them as instance variables.

    • It seems odd to have your object's instance methods rely on the global turtle named 'kel' -- it would make more sense to pass a turtle to its constructor or have it create one of its own.

    • You can take more advantage of symmetry in your designs to reduce the amount of code you need to draw them.

    I've addressed the above issues in my rework of your/Vasu's code below. (I've left out your image code as that isn't currently active.)

    from turtle import Screen, Turtle
    
    class DrawPattern():
        def __init__(self, size):
            self.size = size
    
            self.turtle = Turtle()
            self.turtle.speed("fastest")
    
        def draw_grid(self, init_x, init_y):
    
            ''' Draw a grid '''
    
            self.turtle.penup()
            self.turtle.goto(init_x, init_y)
    
            for _ in range(2):
                for _ in range(7):
    
                    self.turtle.pendown()
                    self.turtle.forward(self.size * 6)
                    self.turtle.backward(self.size * 6)
                    self.turtle.left(90)
                    self.turtle.penup()
                    self.turtle.forward(self.size)
                    self.turtle.right(90)
    
                self.turtle.right(90)
                self.turtle.forward(self.size)
    
        def repeat_shape(self, iter_x, iter_y):
            if iter_x < 3 > iter_y:
                self.draw_shape(iter_x * 180, iter_y * 180)
                self.turtle.right(90)
                self.repeat_shape(iter_x + 1, iter_y)
                self.repeat_shape(iter_x, iter_y + 1)
    
        def draw_shape(self, init_x, init_y):
    
            ''' Recursive method to draw repeating shape '''
    
            # Draw yellow portion
            self.turtle.penup()
            self.turtle.goto(init_x, init_y)
            self.turtle.setheading(90)
            self.turtle.pendown()
    
            self.turtle.fillcolor("yellow")
            self.turtle.begin_fill()
    
            for _ in range(4):
                self.turtle.forward(self.size * 6)
                self.turtle.right(90)
    
            self.turtle.end_fill()
    
            # Draw red portion
            self.turtle.penup()
            self.turtle.goto(init_x, init_y + self.size * 2)
            self.turtle.setheading(0)
            self.turtle.pendown()
    
            self.turtle.fillcolor("red")
            self.turtle.begin_fill()
    
            for _ in range(4):
                self.turtle.left(45)
                self.turtle.forward(42)
                self.turtle.left(90)
                self.turtle.forward(42)
                self.turtle.right(135)
                self.turtle.forward(self.size * 2)
                self.turtle.left(90)
                self.turtle.forward(self.size * 2)
                self.turtle.left(180)
    
            self.turtle.end_fill()
    
    # object instance of class DrawPattern
    obj = DrawPattern(30)
    obj.draw_grid(-200, -200)
    obj.repeat_shape(0, 0)
    
    screen = Screen()
    screen.exitonclick()