Issue #3965: Allow repeated calls to turtle.Screen, by making it a

true singleton object.

Reviewed by Gregor Lingl.
This commit is contained in:
Martin v. Löwis 2008-09-29 22:09:07 +00:00
parent e144873071
commit e563aa4383
4 changed files with 40 additions and 34 deletions

View File

@ -94,8 +94,8 @@ class DemoWindow(object):
left_frame.pack(side=LEFT, fill=BOTH, expand=0) left_frame.pack(side=LEFT, fill=BOTH, expand=0)
self.graph_frame = g_frame = Frame(root) self.graph_frame = g_frame = Frame(root)
turtle.Screen._root = g_frame turtle._Screen._root = g_frame
turtle.Screen._canvas = turtle.ScrolledCanvas(g_frame, 800, 600, 1000, 800) turtle._Screen._canvas = turtle.ScrolledCanvas(g_frame, 800, 600, 1000, 800)
#xturtle.Screen._canvas.pack(expand=1, fill="both") #xturtle.Screen._canvas.pack(expand=1, fill="both")
self.screen = _s_ = turtle.Screen() self.screen = _s_ = turtle.Screen()
self.scanvas = _s_._canvas self.scanvas = _s_._canvas

View File

@ -40,10 +40,10 @@ The object-oriented interface uses essentially two+two classes:
:class:`ScrolledCanvas` as argument. It should be used when :mod:`turtle` is :class:`ScrolledCanvas` as argument. It should be used when :mod:`turtle` is
used as part of some application. used as part of some application.
Derived from :class:`TurtleScreen` is the subclass :class:`Screen`. Screen The function :func:`Screen` returns a singleton object of a
is implemented as sort of singleton, so there can exist only one instance of :class:`TurtleScreen` subclass. This function should be used when
Screen at a time. It should be used when :mod:`turtle` is used as a :mod:`turtle` is used as a standalone tool for doing graphics.
standalone tool for doing graphics. As a singleton object, inheriting from its class is not possible.
All methods of TurtleScreen/Screen also exist as functions, i.e. as part of All methods of TurtleScreen/Screen also exist as functions, i.e. as part of
the procedure-oriented interface. the procedure-oriented interface.

View File

@ -2422,7 +2422,7 @@ class RawTurtle(TPen, TNavigator):
shape=_CFG["shape"], shape=_CFG["shape"],
undobuffersize=_CFG["undobuffersize"], undobuffersize=_CFG["undobuffersize"],
visible=_CFG["visible"]): visible=_CFG["visible"]):
if isinstance(canvas, Screen): if isinstance(canvas, _Screen):
self.screen = canvas self.screen = canvas
elif isinstance(canvas, TurtleScreen): elif isinstance(canvas, TurtleScreen):
if canvas not in RawTurtle.screens: if canvas not in RawTurtle.screens:
@ -3539,29 +3539,33 @@ class RawTurtle(TPen, TNavigator):
RawPen = RawTurtle RawPen = RawTurtle
### Screen - Klasse ######################## ### Screen - Singleton ########################
class Screen(TurtleScreen): 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
class _Screen(TurtleScreen):
_root = None _root = None
_canvas = None _canvas = None
_title = _CFG["title"] _title = _CFG["title"]
# Borg-Idiom
_shared_state = {}
def __new__(cls, *args, **kwargs):
obj = object.__new__(cls, *args, **kwargs)
obj.__dict__ = cls._shared_state
return obj
def __init__(self): def __init__(self):
if Screen._root is None: # XXX there is no need for this code to be conditional,
Screen._root = self._root = _Root() # as there will be only a single _Screen instance, anyway
self._root.title(Screen._title) # XXX actually, the turtle demo is injecting root window,
# so perhaps the conditional creation of a root should be
# preserved (perhaps by passing it as an optional parameter)
if _Screen._root is None:
_Screen._root = self._root = _Root()
self._root.title(_Screen._title)
self._root.ondestroy(self._destroy) self._root.ondestroy(self._destroy)
if Screen._canvas is None: if _Screen._canvas is None:
width = _CFG["width"] width = _CFG["width"]
height = _CFG["height"] height = _CFG["height"]
canvwidth = _CFG["canvwidth"] canvwidth = _CFG["canvwidth"]
@ -3569,10 +3573,9 @@ class Screen(TurtleScreen):
leftright = _CFG["leftright"] leftright = _CFG["leftright"]
topbottom = _CFG["topbottom"] topbottom = _CFG["topbottom"]
self._root.setupcanvas(width, height, canvwidth, canvheight) self._root.setupcanvas(width, height, canvwidth, canvheight)
Screen._canvas = self._root._getcanvas() _Screen._canvas = self._root._getcanvas()
self.setup(width, height, leftright, topbottom) self.setup(width, height, leftright, topbottom)
TurtleScreen.__init__(self, Screen._canvas) TurtleScreen.__init__(self, _Screen._canvas)
Turtle._screen = self
def setup(self, width=_CFG["width"], height=_CFG["height"], def setup(self, width=_CFG["width"], height=_CFG["height"],
startx=_CFG["leftright"], starty=_CFG["topbottom"]): startx=_CFG["leftright"], starty=_CFG["topbottom"]):
@ -3626,17 +3629,17 @@ class Screen(TurtleScreen):
Example (for a Screen instance named screen): Example (for a Screen instance named screen):
>>> screen.title("Welcome to the turtle-zoo!") >>> screen.title("Welcome to the turtle-zoo!")
""" """
if Screen._root is not None: if _Screen._root is not None:
Screen._root.title(titlestring) _Screen._root.title(titlestring)
Screen._title = titlestring _Screen._title = titlestring
def _destroy(self): def _destroy(self):
root = self._root root = self._root
if root is Screen._root: if root is _Screen._root:
Turtle._pen = None Turtle._pen = None
Turtle._screen = None Turtle._screen = None
Screen._root = None _Screen._root = None
Screen._canvas = None _Screen._canvas = None
TurtleScreen._RUNNING = True TurtleScreen._RUNNING = True
root.destroy() root.destroy()
@ -3728,7 +3731,7 @@ def write_docstringdict(filename="turtle_docstringdict"):
docsdict = {} docsdict = {}
for methodname in _tg_screen_functions: for methodname in _tg_screen_functions:
key = "Screen."+methodname key = "_Screen."+methodname
docsdict[key] = eval(key).__doc__ docsdict[key] = eval(key).__doc__
for methodname in _tg_turtle_functions: for methodname in _tg_turtle_functions:
key = "Turtle."+methodname key = "Turtle."+methodname
@ -3842,14 +3845,14 @@ def _screen_docrevise(docstr):
for methodname in _tg_screen_functions: for methodname in _tg_screen_functions:
pl1, pl2 = getmethparlist(eval('Screen.' + methodname)) pl1, pl2 = getmethparlist(eval('_Screen.' + methodname))
if pl1 == "": if pl1 == "":
print ">>>>>>", pl1, pl2 print ">>>>>>", pl1, pl2
continue continue
defstr = ("def %(key)s%(pl1)s: return _getscreen().%(key)s%(pl2)s" % defstr = ("def %(key)s%(pl1)s: return _getscreen().%(key)s%(pl2)s" %
{'key':methodname, 'pl1':pl1, 'pl2':pl2}) {'key':methodname, 'pl1':pl1, 'pl2':pl2})
exec defstr exec defstr
eval(methodname).__doc__ = _screen_docrevise(eval('Screen.'+methodname).__doc__) eval(methodname).__doc__ = _screen_docrevise(eval('_Screen.'+methodname).__doc__)
for methodname in _tg_turtle_functions: for methodname in _tg_turtle_functions:
pl1, pl2 = getmethparlist(eval('Turtle.' + methodname)) pl1, pl2 = getmethparlist(eval('Turtle.' + methodname))

View File

@ -23,6 +23,9 @@ Core and Builtins
Library Library
------- -------
- Issue #3965: Allow repeated calls to turtle.Screen, by making it a
true singleton object.
- Issue #3895: It was possible to crash the interpreter when an external timer - Issue #3895: It was possible to crash the interpreter when an external timer
was used with cProfile that returned an object that could not be converted was used with cProfile that returned an object that could not be converted
into a float. into a float.