pythonturtle-graphicspython-turtle

how to have a value of a class written with a associated, own turtle?


Basically I am trying to code this project of this part of the video https://www.youtube.com/watch?v=jAMegKEetx4&t=338s , the project is a typing game, where words appear from left and move to the right and you have to type them out so they disappear. I'm trying to code the project in python using the turtle module and am now stuck at creating the turtle "writer" for each word.

The class has a "memory" list in which the inputted word gets broken down into the letters, to check the first letter more easily. If the turtle, which is supposed to write the word is created within the __innit__ part ( i think its called constructor), it returns with the following code, the error:

     self.turtle.write("".join(self.memory))
TypeError: RawTurtle.write() missing 1 required positional argument: 'arg'".
                self.turtle.write("".join(self.memory))
                self.turtle.goto(Word.x, self.y)
                self.Word.x += 1
                self.turtle.clear()

and if I try creating the turtle outside of the constructor (but still within the class), all the words get written by one turtle, which results in having only one word written at the time, and then disappearing for another word and switching back and forth. How can i fix this?

As requested, a minimal code to recreate the problem

import turtle
import random
wn = turtle.Screen()
class Word():
    turtle = turtle.Turtle
    def __init__(self,word):
        self.word = word
        self.ycor = random.randint(-100,100)
        self.xcor = 0
    def write(self):
        self.xcor += 10
        turtle.goto(self.xcor,self.ycor)
        turtle.write(self.word)
word1 = Word("Banana")
word2 = Word("Test")
while True:
    word1.write()
    word2.write()
    wn.update()

Here one turtle tries to write every word of the class, however every created object is supposed to have a own turtle to write the created word. If i try creating a self.turtle, and change the function to

    def write(self):
        self.xcor += 10
        self.turtle.goto(self.xcor,self.ycor)
        self.turtle.write(self.word)

i get the following error: AttributeError: 'int' object has no attribute '_goto'. How can i change it so that each created object has its own turtle to write the word and act independently instead of one turtle trying to write created every word?


Solution

  • The following code fixes the issue you described and gives the visual effect your example intended. It also looks ahead to the next problem that you are likely to encounter, turtle can have a single handler respond to any key you type, but it won't know what key triggered it. Since you need that key, I've included a modified turtle event handler that will pass you back whatever key was typed:

    from turtle import Screen, Turtle
    from functools import partial
    from random import randint
    
    FONT = ('Arial', 18, 'normal')
    
    class Word():
        words = {}
    
        def __init__(self, string):
            self.string = string
    
            self.turtle = Turtle()
            self.turtle.hideturtle()
            self.turtle.penup()
    
            ycor = randint(-200, 200)
            xcor = -200
            self.turtle.goto(xcor, ycor)
    
            self.words[self.string] = self
    
        def write(self):
            self.turtle.forward(10)
            self.turtle.clear()
            self.turtle.write(self.string, font=FONT)
            screen.update()
    
    def _onkeypress(self, fun, key=None):
        if fun is None:
            if key is None:
                self.cv.unbind("<KeyPress>", None)
            else:
                self.cv.unbind("<KeyPress-%s>" % key, None)
        elif key is None:
            def eventfun(event):
                fun(event.char)
            self.cv.bind("<KeyPress>", eventfun)
        else:
            def eventfun(event):
                fun()
            self.cv.bind("<KeyPress-%s>" % key, eventfun)
    
    def letter(character):
        string = None
        words = Word.words
    
        for candidate in Word.words:
            if candidate.startswith(character):
                string = candidate
                break
        if string:
            words[string].turtle.clear()
            del words[string]
    
    def demonstrate():
        values = Word.words.values()
    
        for word in values:
            word.write()
    
        screen.ontimer(demonstrate, 100)
    
    screen = Screen()
    screen._onkeypress = partial(_onkeypress, screen)
    screen.tracer(False)
    
    screen.onkeypress(letter)
    screen.listen()
    
    for string in ("banana", "test", "gyro"):
        Word(string)
    
    demonstrate()
    
    screen.exitonclick()
    

    If you type the first letter of any of they words, they will disappear from the screen. You'll need to expand this to your letter by letter destruction of the word. And, I recommend you improve it by having it reuse turtles since they are global entities that don't get garbase collected.