OOP Workshop

Version5: Keyboard Interaction

import turtle
import random
import math

turtle.setup(500, 500)
screen = turtle.Screen()
turtle.title("Turtle Keys")
turtle.pensize(3)

colors = ("orange", "pink", "yellow", "green", "blue", "purple", "magenta", "cyan")


class Button:
    buttonShape = "Line"
    buttonColor = "Orange"
    buttonX = 0
    buttonY = 0
    width = 100
    height = 50

    def __init__(self, buttonShape, buttonColor, buttonX, buttonY, width, height):
        self.buttonShape = buttonShape
        self.buttonColor = buttonColor
        self.buttonX = buttonX
        self.buttonY = buttonY
        self.width = width
        self.height = height

    def drawButtonShape(self, aTurtle):
        aTurtle.pensize(20)
        aTurtle.penup()
        aTurtle.goto(self.buttonX, self.buttonY + 10)
        aTurtle.pendown()
        aTurtle.forward(self.width)

    def drawTheButton(self, aTurtle):

        global selectedButton
        if (self is selectedButton or self is selectedColorButton):
            aTurtle.color("Red")
        else:
            aTurtle.color("Black")

        aTurtle.setheading(0)
        aTurtle.fillcolor(self.buttonColor)
        aTurtle.penup()
        aTurtle.goto(self.buttonX, self.buttonY)
        aTurtle.pendown()

        aTurtle.begin_fill()

        self.drawButtonShape(aTurtle)  # Potential Polymorphic call to child class

        aTurtle.end_fill()

    def drawShape(self, x, y, aTurtle):
        aTurtle.goto(x, y)

    def isClicked(self, x, y):
        return self.buttonX < x < self.buttonX + self.width and \
               self.buttonY < y < self.buttonY + self.height

    def getColor(self):
        return self.buttonColor


class RectangleButton(Button):
    def __init__(self, buttonColor, buttonX, buttonY, width, height):
        super().__init__("Rectangle", buttonColor, buttonX, buttonY, width, height)

    def drawRectangle(self, aTurtle, width, height):
        aTurtle.setheading(0)
        aTurtle.forward(width)
        aTurtle.left(90)
        aTurtle.forward(height)
        aTurtle.left(90)
        aTurtle.forward(width)
        aTurtle.left(90)
        aTurtle.forward(height)

    def drawButtonShape(self, aTurtle):
        aTurtle.pensize(2)
        self.drawRectangle(aTurtle, self.width, self.height)

    def drawShape(self, x, y, aTurtle):
        self.drawRectangle(aTurtle, x - aTurtle.xcor(), y - aTurtle.ycor())


class TriangleButton(Button):
    def __init__(self, buttonColor, buttonX, buttonY, width, height):
        super().__init__("Triangle", buttonColor, buttonX, buttonY, width, height)

    def drawTriangle(self, aTurtle, width, heading):
        aTurtle.setheading(heading)
        aTurtle.forward(width)
        aTurtle.left(120)
        aTurtle.forward(width)
        aTurtle.left(120)
        aTurtle.forward(width)

    def drawButtonShape(self, aTurtle):
        aTurtle.pensize(2)
        self.drawTriangle(aTurtle, self.width, 0)

    def drawShape(self, x, y, aTurtle):
        sign = 1 if x - aTurtle.xcor() > 0 else -1
        self.drawTriangle(aTurtle, (sign) * math.sqrt((x - aTurtle.xcor()) ** 2 + (y - aTurtle.ycor()) ** 2),
                          math.degrees(math.atan((y - aTurtle.ycor()) / (x - aTurtle.xcor()))))


class CircleButton(Button):
    def __init__(self, buttonColor, buttonX, buttonY, width, height):
        super().__init__("Circle", buttonColor, buttonX, buttonY, width, height)

    def drawCircle(self, aTurtle, radius, heading):
        aTurtle.setheading(heading)
        aTurtle.circle(radius)

    def drawButtonShape(self, aTurtle):
        aTurtle.pensize(2)
        aTurtle.penup()
        aTurtle.setheading(0)
        aTurtle.forward(self.width / 2)
        aTurtle.pendown()
        self.drawCircle(aTurtle, self.width / 2, 0)

    def drawShape(self, x, y, aTurtle):
        sign = 1 if x - aTurtle.xcor() > 0 else -1
        self.drawCircle(aTurtle, (sign) * math.sqrt((x - aTurtle.xcor()) ** 2 + (y - aTurtle.ycor()) ** 2) / 2,
                        math.degrees(math.atan((y - aTurtle.ycor()) / (x - aTurtle.xcor()))) - 90)


buttonTurtle = turtle.Turtle()
buttonTurtle.speed(20)
selectedButton = Button("Line", "Orange", -130, 220, 40, 20)
shapeButtons = []
shapeButtons.append(selectedButton)
shapeButtons.append(RectangleButton("Green", -70, 200, 100, 40))
shapeButtons.append(TriangleButton("Blue", 40, 200, 40, 40))
shapeButtons.append(CircleButton("Brown", 90, 200, 40, 40))


def updateButtons():
    for i in range(len(shapeButtons)):
        shapeButtons[i].drawTheButton(buttonTurtle)


def anyShapeSelected(x, y):
    global selectedButton
    for i in range(len(shapeButtons)):
        shapeB = shapeButtons[i]

        if (shapeB.isClicked(x, y)):
            print(shapeB.buttonShape, "was clicked")
            if (shapeB is selectedButton):
                return False
            selectedButton = shapeB
            return True

    return False


class RandomColorButton(CircleButton):
    def getColor(self):  # Overriding the default behaviour of the getColor method in the Button base class
        global colors
        return colors[random.randint(0, len(colors) - 1)]


colorButtons = []
selectedColorButton = RandomColorButton("White", -(len(colors) + 1) * 30 / 2, 170, 20, 20)
colorButtons.append(selectedColorButton)
for i in range(len(colors)):
    colorButtons.append(CircleButton(colors[i], -(len(colors) + 1) * 30 / 2 + (i + 1) * 30, 170, 20, 20))


def updateColorButtons():
    for i in range(len(colorButtons)):
        colorButtons[i].drawTheButton(buttonTurtle)


def anyColorSelected(x, y):
    global selectedColorButton
    for i in range(len(colorButtons)):
        colorB = colorButtons[i]

        if (colorB.isClicked(x, y)):
            print(colorB.buttonColor, "was clicked")
            if (colorB is selectedColorButton):
                return False
            selectedColorButton = colorB
            return True

    return False


drawing = False


def goto_and_do_whatever_required(x, y):
    global drawing
    if (drawing):
        return

    drawing = True

    if (y > 170):
        if (anyShapeSelected(x, y)):
            updateButtons()

        if (anyColorSelected(x, y)):
            updateColorButtons()
        drawing = False
        return

    turtle.color(selectedColorButton.getColor())
    selectedButton.drawShape(x, y, turtle)

    drawing = False


screen.onscreenclick(goto_and_do_whatever_required)

updateButtons()
updateColorButtons()


def upkeyfunc():
    turtle.forward(15)


def leftkeyfunc():
    turtle.left(15)


def rightkeyfunc():
    turtle.right(15)


def downkeyfunc():
    turtle.back(15)


def u_keyfunc():
    turtle.penup()


def d_keyfunc():
    turtle.pendown()


turtle.onkeypress(upkeyfunc, "Up")
turtle.onkeypress(leftkeyfunc, "Left")
turtle.onkeypress(rightkeyfunc, "Right")
turtle.onkeypress(downkeyfunc, "Down")

turtle.onkeypress(u_keyfunc, "U")
turtle.onkeypress(u_keyfunc, "u")

turtle.onkeypress(d_keyfunc, "D")
turtle.onkeypress(d_keyfunc, "d")

turtle.listen()

turtle.done()