0

I'm trying to make a turtle version of the 8-Queens Puzzle using Python Turtle.

I've make a start, but have hit a block with the fact that the click event on a custom Turtle object seems to only fire once. I know screen click events fire multiple times, so is this a feature of Turtle instances? What am I missing please?

import turtle

screen = turtle.Screen()
screen.reset()
SIZE = 40
screen.register_shape('box', ((-SIZE/2, SIZE/2), (SIZE/2, SIZE/2), (SIZE/2, -SIZE/2), (-SIZE/2, -SIZE/2)))
screen.register_shape('images/queenlogo40x40.gif')

class Box(turtle.Turtle):
    def __init__(self, x=0, y=0, place_color='green'):
        super(Box, self).__init__()
        self.place_color = place_color
        self.speed(0)
        self.penup()
        self.shape("box")
        self.color(place_color)
        self.setpos(x, y)
        self.has_queen = False
        self.onclick(self.click_handler)

    def click_handler(self, x, y):
        print("start:" , self.has_queen)

        if self.has_queen:
            self.shape('box')
            self.has_queen = False
        else:
            self.shape('images/queenlogo40x40.gif')
            self.has_queen = True

        print("end:" , self.has_queen)


    def __str__(self):
        """ Print piece details """
        return "({0}, {1}), {2}".format(self.xcor(), self.ycor(), self.place_color())

Edit: I can fix this by adding self.onclick(self.click_handler) to the click handler, but that just seems wrong. I'm sure I've seen similar functionality without needing to rebind the event each time it's used.

1 Answer 1

2

Your example should function correctly, I don't see a conceptual problem.

But there's a glitch in turtle. The information about onclick is stored on the turtle's _item property:

self.screen._onclick(self.turtle._item, fun, btn, add)

But when you change a turtle's shape from an image to a polygon, or vice versa, it destroys the _item property:

if self._type in ["image", "polygon"]:
        screen._delete(self._item)

So your binding is lost. Note that if you change the line:

self.shape('images/queenlogo40x40.gif')

to instead be:

self.shape('turtle')

the code works fine, as you're going from polygon to polygon and _item is preserved. So adding self.onclick(self.click_handler) after the shape change is necessary when going between polygon and image.

I've reworked your code slightly below to address a couple of unrelated issues (e.g. fixed super() call for Python 3; removed incorrect parens in __str__() code.)

from turtle import Turtle, Screen

SIZE = 40

class Box(Turtle):
    def __init__(self, x=0, y=0, place_color='green'):
        super().__init__('box')

        self.speed('fastest')
        self.color(place_color)
        self.place_color = place_color
        self.has_queen = False
        self.penup()
        self.setpos(x, y)

        self.onclick(self.click_handler)

    def click_handler(self, x, y):

        print("start:", self.has_queen)

        if self.has_queen:
            self.shape('box')
        # else:
            # self.shape('turtle')
        else:
            self.shape('queenlogo40x40.gif')

        self.has_queen = not self.has_queen
        self.onclick(self.click_handler)  # redo since self.shape() may undo this

        print("end:", self.has_queen)

    def __str__(self):
        """ Print piece details """
        return "({0}, {1}), {2}".format(self.xcor(), self.ycor(), self.place_color)

screen = Screen()
screen.register_shape('box', ((-SIZE/2, SIZE/2), (SIZE/2, SIZE/2), (SIZE/2, -SIZE/2), (-SIZE/2, -SIZE/2)))
screen.register_shape('queenlogo40x40.gif')

tortoise = Box()

screen.mainloop()
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.