Reputation: 3794
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.
Upvotes: 0
Views: 1035
Reputation: 41872
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()
Upvotes: 2