Было бы очень полезно иметь объекты Turtle, содержащие текст, например целочисленные значения, которые можно использовать для отображения различных головоломок и игр, и к которым могут быть прикреплены собственные обработчики щелчков.
Вот загвоздка и (две) причины, которые подходят к использованию штампа (), как предложено в других ответах, не будут работать. Во-первых, вы не можете щелкнуть на скрытой черепахе:
from turtle import *
def doit(x, y):
print("Just do it!")
yertle = Turtle()
# comment out the following line if you want `onlick()` to work
yertle.hideturtle()
yertle.shape('square')
yertle.stamp()
yertle.onclick(doit)
done()
Штампы не являются интерактивными объектами. Во-вторых, вы даже не можете нажать на черепаху, которая находится за чернилами, оставленными этой или другой черепахой:
from turtle import *
def doit(x, y):
print("Just do it!")
yertle = Turtle()
yertle.shape('square')
yertle.fillcolor('white')
yertle.onclick(doit)
myrtle = Turtle()
myrtle.shape('turtle')
myrtle.penup()
myrtle.sety(-16)
# comment out the following line if you want `onlick()` to work
myrtle.write('X', align='center', font=('Courier', 32, 'bold'))
myrtle.goto(100, 100) # move myrtle out of the way of clicking
done()
Если вы нажмете на букву «X», ничего не произойдет, если вам не удастся ударить часть квадрата сразу за буквой. Я считаю, что, хотя мы думаем о 'X' как о мертвых чернилах над нашей живой черепахой , на уровне tkinter они оба похожи, возможно, оба способны принимать события, поэтому один скрывает щелчок на другом.
Итак, как мы можем это сделать? Подход, который я собираюсь использовать, заключается в создании плитки черепахи с изображением, где изображения создаются путем записи в растровые изображения:
tileset.py
from turtle import Screen, Turtle, Shape
from PIL import Image, ImageDraw, ImageFont, ImageTk
DEFAULT_FONT_FILE = "/Library/Fonts/Courier New Bold.ttf" # adjust for your system
DEFAULT_POINT_SIZE = 32
DEFAULT_OUTLINE_SIZE = 1
DEFAULT_OUTLINE_COLOR = 'black'
DEFAULT_BACKGROUND_COLOR = 'white'
class Tile(Turtle):
def __init__(self, shape, size):
super().__init__(shape)
self.penup()
self.size = size
def tile_size(self):
return self.size
class TileSet():
def __init__(self, font_file=DEFAULT_FONT_FILE, point_size=DEFAULT_POINT_SIZE, background_color=DEFAULT_BACKGROUND_COLOR, outline_size=DEFAULT_OUTLINE_SIZE, outline_color=DEFAULT_OUTLINE_COLOR):
self.font = ImageFont.truetype(font_file, point_size)
self.image = Image.new("RGB", (point_size, point_size))
self.draw = ImageDraw.Draw(self.image)
self.background_color = background_color
self.outline_size = outline_size
self.outline_color = outline_color
def register_image(self, string):
width, height = self.draw.textsize(string, font=self.font)
image = Image.new("RGB", (width + self.outline_size*2, height + self.outline_size*2), self.background_color)
draw = ImageDraw.Draw(image)
tile_size = (width + self.outline_size, height + self.outline_size)
draw.rectangle([(0, 0), tile_size], outline=self.outline_color)
draw.text((0, 0), string, font=self.font, fill="#000000")
photo_image = ImageTk.PhotoImage(image)
shape = Shape("image", photo_image)
Screen()._shapes[string] = shape # underpinning, not published API
return tile_size
def make_tile(self, string):
tile_size = self.register_image(string)
return Tile(string, tile_size)
Помимо изображения, единственные отличия экземпляра Tile от экземпляра Turtle - это дополнительный метод tile_size()
для возврата его ширины и высота как правило, c черепахи не могут этого сделать в случае изображений. А перо плитки - вверх в начале, вместо вниз .
Я нарисовал пару вопросов и ответов SO:
И пока я на нем, этот ответ был обновлен, чтобы быть более независимым от системы:
Чтобы продемонстрировать, как работают мои наборы плиток , вот хорошо известный 15 пазлов реализовано с их помощью. Он создает два набора плиток, один с белым фоном и один с красным (розовым) фоном:
from tileset import TileSet
from turtle import Screen
from functools import partial
from random import shuffle
SIZE = 4
OFFSETS = [(-1, 0), (0, -1), (1, 0), (0, 1)]
def slide(tile, row, col, x, y):
tile.onclick(None) # disable handler inside handler
for dy, dx in OFFSETS:
try:
if row + dy >= 0 <= col + dx and matrix[row + dy][col + dx] == None:
matrix[row][col] = None
row, col = row + dy, col + dx
matrix[row][col] = tile
width, height = tile.tile_size()
x, y = tile.position()
tile.setposition(x + dx * width, y - dy * height)
break
except IndexError:
pass
tile.onclick(partial(slide, tile, row, col))
screen = Screen()
matrix = [[None for _ in range(SIZE)] for _ in range(SIZE)]
white_tiles = TileSet(background_color='white')
red_tiles = TileSet(background_color='pink')
tiles = []
parity = True
for number in range(1, SIZE * SIZE):
string = str(number).rjust(2)
tiles.append(white_tiles.make_tile(string) if parity else red_tiles.make_tile(string))
parity = not parity
if number % SIZE == 0:
parity = not parity
shuffle(tiles)
width, height = tiles[0].tile_size()
offset_width, offset_height = width * 1.5, height * 1.5
for row in range(SIZE):
for col in range(SIZE):
if row == SIZE - 1 == col:
break
tile = tiles.pop(0)
width, height = tile.tile_size()
tile.goto(col * width - offset_width, offset_height - row * height)
tile.onclick(partial(slide, tile, row, col))
matrix[row][col] = tile
screen.mainloop()
Если вы нажмете на числовую плитку рядом с пустым пространством, она переместится в пустое пространство, иначе ничего не произойдет. Этот код не гарантирует решения головоломки - половина не будет решена из-за случайного перемешивания. Это просто демонстрация, мельчайшие детали и сами плитки оставлены на ваше усмотрение.
введите описание изображения здесь