Skip to content Skip to sidebar Skip to footer

Python Turtle Write Value In Containing Box

I want to be able to create some turtles which display values by subclassing turtle.Turtle. These turtles should display their value as text centered in their own shape. I also wan

Solution 1:

It would be very helpful to have Turtle Objects containing text such as integer values, which can be used to display a variety of puzzles and games, and can have their own click handlers attached.

Here's the rub, and the (two) reason(s) that approaches using stamp() as suggested in other answers won't work. First, you can't click on a hidden 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()

Stamps are not clickable entities. Second, you can't even click on a turtle that's behind ink left by this, or another, turtle:

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()

If you click on the letter 'X', nothing happens unless you manage to hit a portion of the square just beyond the letter. My belief is that although we think of the 'X' as dead ink over our live turtle, at the tkinter level they are both similar, possibly both capable of receiving events, so one obscures the click on the other.

So how can we do this? The approach I'm going to use is make a tile a turtle with an image where the images are generate by writing onto bitmaps:

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'classTile(Turtle):
    def__init__(self, shape, size):
        super().__init__(shape)
        self.penup()

        self.size = size

    deftile_size(self):
        return self.size

classTileSet():

    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

    defregister_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 APIreturn tile_size

    defmake_tile(self, string):
        tile_size = self.register_image(string)
        return Tile(string, tile_size)

Other than its image, the only differences a Tile instance has from a Turtle instance is an extra method tile_size() to return its width and height as generic turtles can't do this in the case of images. And a tile's pen is up at the start, instead of down.

I've drawn on a couple of SO questions and answers:

And while I'm at it, this answer has been updated to be more system independent:

To demonstrate how my tile sets work, here's the well-know 15 puzzle implemented using them. It creates two tile sets, one with white backgrounds and one with red (pink) backgrounds:

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)]

defslide(tile, row, col, x, y):
    tile.onclick(None)  # disable handler inside handlerfor 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)
                breakexcept IndexError:
            pass

    tile.onclick(partial(slide, tile, row, col))

screen = Screen()

matrix = [[Nonefor _ inrange(SIZE)] for _ inrange(SIZE)]

white_tiles = TileSet(background_color='white')
red_tiles = TileSet(background_color='pink')

tiles = []
parity = Truefor number inrange(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.5for row inrange(SIZE):
    for col inrange(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()

If you click on a number tile that's next to the blank space, it will move into the blank space, otherwise nothing happens. This code doesn't guarantee a solvable puzzle -- half won't be solvable due to the random shuffle. It's just a demonstration, the fine details of it, and the tiles themselves, are left to you.

enter image description here

Post a Comment for "Python Turtle Write Value In Containing Box"