pythonpython-3.xturtle-graphicssupersuperclass

How to use python superclass and inheritance (turtle graphics example)?


I've been playing around with turtle graphics, trying to better understand objects and I've encountered a little issue. I've learned about the super() function and wanted to use it:

from turtle import Turtle, _Screen, Screen    

class turtle(Turtle):
    def __init__(self):
        super().__init__(shape="circle")            

wn = Screen()
tortoise = turtle()

So if I do this, everything works like charm. I get a screen and I can use any Turtle method on my turtle class.

from turtle import Turtle, _Screen, Screen

class window(_Screen):
    def __init__(self):
        super().__init__()
        self.setup(500,500)
        self.screensize(1000,1000)
        self.title("Title")
        self.bgcolor("black")

wn = window()

This will also work great, I get what I ask: black window of 1000x1000 in a 500x500 box with slide bars. No problem. But, if I want to combine these:

from turtle import Turtle, _Screen, Screen

class window(_Screen):
    def __init__(self):
        super().__init__()
        self.setup(500,500)
        self.screensize(1000,1000)
        self.title("Title")
        self.bgcolor("black")

class turtle(Turtle):
    def __init__(self):
        super().__init__(shape="circle")

wn = window()
tortoise = turtle()

It will display the screen, but I will get an error from the turtle:

AttributeError: '_Screen' object has no attribute '_mode'

I have been looking through the turtle module and found that the Screen() function does this:

def Screen():
    """Return the singleton screen object.
    If none exists at the moment, create a new one and return it,
    else return the existing one."""
    if Turtle._screen is None:
        Turtle._screen = _Screen()
    return Turtle._screen

So I modified the window class to include this:

from turtle import Turtle, _Screen, Screen

class window(_Screen):
    def __init__(self):
        Turtle._screen = Screen()
        super().__init__()
        #self.setup(500,500)
        #self.screensize(1000,1000)
        #self.title("Title")
        #self.bgcolor("black")

class turtle(Turtle):
    def __init__(self):
        super().__init__(shape="circle")

wn = window()
tortoise = turtle()

It works like this, I will get a white window, with a round turtle. But if I uncomment the setup, the screensize or the bgcolor part, I will get an error:

AttributeError: 'window' object has no attribute '_tracing'

or

AttributeError: 'window' object has no attribute 'cv'

So I would again have to declare some parameters before the __init__ in the window class, to make it work. It seems there is something I'm missing. Why does the turtle inherit everything and works great, but the _Screen is not initializing with all it's necessary parameters?


Solution

  • As you've discovered, the turtle Screen is an onion of many layers. There are two issues we need to address: the _Screen class invokes it's super's (TurtleScreen's) initializer in a way that isn't subclass friendly; the Screen() function is called from lots of places and it hardcodes which class creates the screen. So let's address both issues:

    import turtle
    
    class MyScreen(turtle._Screen):
        def __init__(self):
            super().__init__()
            turtle.TurtleScreen.__init__(self, MyScreen._canvas)
    
            self.setup(500, 500)
            self.screensize(1000, 1000)
            self.title("Title")
            self.bgcolor("black")
    
    def MyScreenFunction():
        if turtle.Turtle._screen is None:
            turtle.Turtle._screen = MyScreen()
        return turtle.Turtle._screen
    
    turtle.Screen = MyScreenFunction
    
    class MyTurtle(turtle.Turtle):
        def __init__(self):
            super().__init__(shape="circle")
    
    wn = turtle.Screen()
    
    tortoise = MyTurtle()
    tortoise.color('white')
    
    tortoise.circle(100)
    
    wn.mainloop()
    

    However, there may be a better way to go. Turtle can be used both standalone as above, and embedded in a tkinter program. The embedded approach uses RawTurtle, TurtleScreen and optionally Scrolled Canvas. These classes may be easier to subclass and the better approach might be to build your own equivalent of standalone turtle through embedding turtle in tkinter and subclassing as needed.