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?
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.