(root)/
Python-3.11.7/
Lib/
turtledemo/
__main__.py
       1  #!/usr/bin/env python3
       2  
       3  """
       4    ----------------------------------------------
       5        turtleDemo - Help
       6    ----------------------------------------------
       7  
       8    This document has two sections:
       9  
      10    (1) How to use the demo viewer
      11    (2) How to add your own demos to the demo repository
      12  
      13  
      14    (1) How to use the demo viewer.
      15  
      16    Select a demoscript from the example menu.
      17    The (syntax colored) source code appears in the left
      18    source code window. IT CANNOT BE EDITED, but ONLY VIEWED!
      19  
      20    The demo viewer windows can be resized. The divider between text
      21    and canvas can be moved by grabbing it with the mouse. The text font
      22    size can be changed from the menu and with Control/Command '-'/'+'.
      23    It can also be changed on most systems with Control-mousewheel
      24    when the mouse is over the text.
      25  
      26    Press START button to start the demo.
      27    Stop execution by pressing the STOP button.
      28    Clear screen by pressing the CLEAR button.
      29    Restart by pressing the START button again.
      30  
      31    SPECIAL demos, such as clock.py are those which run EVENTDRIVEN.
      32  
      33        Press START button to start the demo.
      34  
      35        - Until the EVENTLOOP is entered everything works
      36        as in an ordinary demo script.
      37  
      38        - When the EVENTLOOP is entered, you control the
      39        application by using the mouse and/or keys (or it's
      40        controlled by some timer events)
      41        To stop it you can and must press the STOP button.
      42  
      43        While the EVENTLOOP is running, the examples menu is disabled.
      44  
      45        - Only after having pressed the STOP button, you may
      46        restart it or choose another example script.
      47  
      48     * * * * * * * *
      49     In some rare situations there may occur interferences/conflicts
      50     between events concerning the demo script and those concerning the
      51     demo-viewer. (They run in the same process.) Strange behaviour may be
      52     the consequence and in the worst case you must close and restart the
      53     viewer.
      54     * * * * * * * *
      55  
      56  
      57     (2) How to add your own demos to the demo repository
      58  
      59     - Place the file in the same directory as turtledemo/__main__.py
      60       IMPORTANT! When imported, the demo should not modify the system
      61       by calling functions in other modules, such as sys, tkinter, or
      62       turtle. Global variables should be initialized in main().
      63  
      64     - The code must contain a main() function which will
      65       be executed by the viewer (see provided example scripts).
      66       It may return a string which will be displayed in the Label below
      67       the source code window (when execution has finished.)
      68  
      69     - In order to run mydemo.py by itself, such as during development,
      70       add the following at the end of the file:
      71  
      72      if __name__ == '__main__':
      73          main()
      74          mainloop()  # keep window open
      75  
      76      python -m turtledemo.mydemo  # will then run it
      77  
      78     - If the demo is EVENT DRIVEN, main must return the string
      79       "EVENTLOOP". This informs the demo viewer that the script is
      80       still running and must be stopped by the user!
      81  
      82       If an "EVENTLOOP" demo runs by itself, as with clock, which uses
      83       ontimer, or minimal_hanoi, which loops by recursion, then the
      84       code should catch the turtle.Terminator exception that will be
      85       raised when the user presses the STOP button.  (Paint is not such
      86       a demo; it only acts in response to mouse clicks and movements.)
      87  """
      88  import sys
      89  import os
      90  
      91  from tkinter import *
      92  from idlelib.colorizer import ColorDelegator, color_config
      93  from idlelib.percolator import Percolator
      94  from idlelib.textview import view_text
      95  from turtledemo import __doc__ as about_turtledemo
      96  
      97  import turtle
      98  
      99  demo_dir = os.path.dirname(os.path.abspath(__file__))
     100  darwin = sys.platform == 'darwin'
     101  
     102  STARTUP = 1
     103  READY = 2
     104  RUNNING = 3
     105  DONE = 4
     106  EVENTDRIVEN = 5
     107  
     108  menufont = ("Arial", 12, NORMAL)
     109  btnfont = ("Arial", 12, 'bold')
     110  txtfont = ['Lucida Console', 10, 'normal']
     111  
     112  MINIMUM_FONT_SIZE = 6
     113  MAXIMUM_FONT_SIZE = 100
     114  font_sizes = [8, 9, 10, 11, 12, 14, 18, 20, 22, 24, 30]
     115  
     116  def getExampleEntries():
     117      return [entry[:-3] for entry in os.listdir(demo_dir) if
     118              entry.endswith(".py") and entry[0] != '_']
     119  
     120  help_entries = (  # (help_label,  help_doc)
     121      ('Turtledemo help', __doc__),
     122      ('About turtledemo', about_turtledemo),
     123      ('About turtle module', turtle.__doc__),
     124      )
     125  
     126  
     127  class ESC[4;38;5;81mDemoWindow(ESC[4;38;5;149mobject):
     128  
     129      def __init__(self, filename=None):
     130          self.root = root = turtle._root = Tk()
     131          root.title('Python turtle-graphics examples')
     132          root.wm_protocol("WM_DELETE_WINDOW", self._destroy)
     133  
     134          if darwin:
     135              import subprocess
     136              # Make sure we are the currently activated OS X application
     137              # so that our menu bar appears.
     138              subprocess.run(
     139                      [
     140                          'osascript',
     141                          '-e', 'tell application "System Events"',
     142                          '-e', 'set frontmost of the first process whose '
     143                                'unix id is {} to true'.format(os.getpid()),
     144                          '-e', 'end tell',
     145                      ],
     146                      stderr=subprocess.DEVNULL,
     147                      stdout=subprocess.DEVNULL,)
     148  
     149          root.grid_rowconfigure(0, weight=1)
     150          root.grid_columnconfigure(0, weight=1)
     151          root.grid_columnconfigure(1, minsize=90, weight=1)
     152          root.grid_columnconfigure(2, minsize=90, weight=1)
     153          root.grid_columnconfigure(3, minsize=90, weight=1)
     154  
     155          self.mBar = Menu(root, relief=RAISED, borderwidth=2)
     156          self.mBar.add_cascade(menu=self.makeLoadDemoMenu(self.mBar),
     157                                label='Examples', underline=0)
     158          self.mBar.add_cascade(menu=self.makeFontMenu(self.mBar),
     159                                label='Fontsize', underline=0)
     160          self.mBar.add_cascade(menu=self.makeHelpMenu(self.mBar),
     161                                label='Help', underline=0)
     162          root['menu'] = self.mBar
     163  
     164          pane = PanedWindow(root, orient=HORIZONTAL, sashwidth=5,
     165                             sashrelief=SOLID, bg='#ddd')
     166          pane.add(self.makeTextFrame(pane))
     167          pane.add(self.makeGraphFrame(pane))
     168          pane.grid(row=0, columnspan=4, sticky='news')
     169  
     170          self.output_lbl = Label(root, height= 1, text=" --- ", bg="#ddf",
     171                                  font=("Arial", 16, 'normal'), borderwidth=2,
     172                                  relief=RIDGE)
     173          if darwin:  # Leave Mac button colors alone - #44254.
     174              self.start_btn = Button(root, text=" START ", font=btnfont,
     175                                      fg='#00cc22', command=self.startDemo)
     176              self.stop_btn = Button(root, text=" STOP ", font=btnfont,
     177                                     fg='#00cc22', command=self.stopIt)
     178              self.clear_btn = Button(root, text=" CLEAR ", font=btnfont,
     179                                      fg='#00cc22', command = self.clearCanvas)
     180          else:
     181              self.start_btn = Button(root, text=" START ", font=btnfont,
     182                                      fg="white", disabledforeground = "#fed",
     183                                      command=self.startDemo)
     184              self.stop_btn = Button(root, text=" STOP ", font=btnfont,
     185                                     fg="white", disabledforeground = "#fed",
     186                                     command=self.stopIt)
     187              self.clear_btn = Button(root, text=" CLEAR ", font=btnfont,
     188                                      fg="white", disabledforeground="#fed",
     189                                      command = self.clearCanvas)
     190          self.output_lbl.grid(row=1, column=0, sticky='news', padx=(0,5))
     191          self.start_btn.grid(row=1, column=1, sticky='ew')
     192          self.stop_btn.grid(row=1, column=2, sticky='ew')
     193          self.clear_btn.grid(row=1, column=3, sticky='ew')
     194  
     195          Percolator(self.text).insertfilter(ColorDelegator())
     196          self.dirty = False
     197          self.exitflag = False
     198          if filename:
     199              self.loadfile(filename)
     200          self.configGUI(DISABLED, DISABLED, DISABLED,
     201                         "Choose example from menu", "black")
     202          self.state = STARTUP
     203  
     204  
     205      def onResize(self, event):
     206          cwidth = self.canvas.winfo_width()
     207          cheight = self.canvas.winfo_height()
     208          self.canvas.xview_moveto(0.5*(self.canvwidth-cwidth)/self.canvwidth)
     209          self.canvas.yview_moveto(0.5*(self.canvheight-cheight)/self.canvheight)
     210  
     211      def makeTextFrame(self, root):
     212          self.text_frame = text_frame = Frame(root)
     213          self.text = text = Text(text_frame, name='text', padx=5,
     214                                  wrap='none', width=45)
     215          color_config(text)
     216  
     217          self.vbar = vbar = Scrollbar(text_frame, name='vbar')
     218          vbar['command'] = text.yview
     219          vbar.pack(side=LEFT, fill=Y)
     220          self.hbar = hbar = Scrollbar(text_frame, name='hbar', orient=HORIZONTAL)
     221          hbar['command'] = text.xview
     222          hbar.pack(side=BOTTOM, fill=X)
     223          text['yscrollcommand'] = vbar.set
     224          text['xscrollcommand'] = hbar.set
     225  
     226          text['font'] = tuple(txtfont)
     227          shortcut = 'Command' if darwin else 'Control'
     228          text.bind_all('<%s-minus>' % shortcut, self.decrease_size)
     229          text.bind_all('<%s-underscore>' % shortcut, self.decrease_size)
     230          text.bind_all('<%s-equal>' % shortcut, self.increase_size)
     231          text.bind_all('<%s-plus>' % shortcut, self.increase_size)
     232          text.bind('<Control-MouseWheel>', self.update_mousewheel)
     233          text.bind('<Control-Button-4>', self.increase_size)
     234          text.bind('<Control-Button-5>', self.decrease_size)
     235  
     236          text.pack(side=LEFT, fill=BOTH, expand=1)
     237          return text_frame
     238  
     239      def makeGraphFrame(self, root):
     240          # t._Screen is a singleton class instantiated or retrieved
     241          # by calling Screen.  Since tdemo canvas needs a different
     242          # configuration, we manually set class attributes before
     243          # calling Screen and manually call superclass init after.
     244          turtle._Screen._root = root
     245  
     246          self.canvwidth = 1000
     247          self.canvheight = 800
     248          turtle._Screen._canvas = self.canvas = canvas = turtle.ScrolledCanvas(
     249                  root, 800, 600, self.canvwidth, self.canvheight)
     250          canvas.adjustScrolls()
     251          canvas._rootwindow.bind('<Configure>', self.onResize)
     252          canvas._canvas['borderwidth'] = 0
     253  
     254          self.screen = screen = turtle.Screen()
     255          turtle.TurtleScreen.__init__(screen, canvas)
     256          turtle.RawTurtle.screens = [screen]
     257          return canvas
     258  
     259      def set_txtsize(self, size):
     260          txtfont[1] = size
     261          self.text['font'] = tuple(txtfont)
     262          self.output_lbl['text'] = 'Font size %d' % size
     263  
     264      def decrease_size(self, dummy=None):
     265          self.set_txtsize(max(txtfont[1] - 1, MINIMUM_FONT_SIZE))
     266          return 'break'
     267  
     268      def increase_size(self, dummy=None):
     269          self.set_txtsize(min(txtfont[1] + 1, MAXIMUM_FONT_SIZE))
     270          return 'break'
     271  
     272      def update_mousewheel(self, event):
     273          # For wheel up, event.delta = 120 on Windows, -1 on darwin.
     274          # X-11 sends Control-Button-4 event instead.
     275          if (event.delta < 0) == (not darwin):
     276              return self.decrease_size()
     277          else:
     278              return self.increase_size()
     279  
     280      def configGUI(self, start, stop, clear, txt="", color="blue"):
     281          if darwin:  # Leave Mac button colors alone - #44254.
     282              self.start_btn.config(state=start)
     283              self.stop_btn.config(state=stop)
     284              self.clear_btn.config(state=clear)
     285          else:
     286              self.start_btn.config(state=start,
     287                                    bg="#d00" if start == NORMAL else "#fca")
     288              self.stop_btn.config(state=stop,
     289                                   bg="#d00" if stop == NORMAL else "#fca")
     290              self.clear_btn.config(state=clear,
     291                                    bg="#d00" if clear == NORMAL else "#fca")
     292          self.output_lbl.config(text=txt, fg=color)
     293  
     294      def makeLoadDemoMenu(self, master):
     295          menu = Menu(master)
     296  
     297          for entry in getExampleEntries():
     298              def load(entry=entry):
     299                  self.loadfile(entry)
     300              menu.add_command(label=entry, underline=0,
     301                               font=menufont, command=load)
     302          return menu
     303  
     304      def makeFontMenu(self, master):
     305          menu = Menu(master)
     306          menu.add_command(label="Decrease (C-'-')", command=self.decrease_size,
     307                           font=menufont)
     308          menu.add_command(label="Increase (C-'+')", command=self.increase_size,
     309                           font=menufont)
     310          menu.add_separator()
     311  
     312          for size in font_sizes:
     313              def resize(size=size):
     314                  self.set_txtsize(size)
     315              menu.add_command(label=str(size), underline=0,
     316                               font=menufont, command=resize)
     317          return menu
     318  
     319      def makeHelpMenu(self, master):
     320          menu = Menu(master)
     321  
     322          for help_label, help_file in help_entries:
     323              def show(help_label=help_label, help_file=help_file):
     324                  view_text(self.root, help_label, help_file)
     325              menu.add_command(label=help_label, font=menufont, command=show)
     326          return menu
     327  
     328      def refreshCanvas(self):
     329          if self.dirty:
     330              self.screen.clear()
     331              self.dirty=False
     332  
     333      def loadfile(self, filename):
     334          self.clearCanvas()
     335          turtle.TurtleScreen._RUNNING = False
     336          modname = 'turtledemo.' + filename
     337          __import__(modname)
     338          self.module = sys.modules[modname]
     339          with open(self.module.__file__, 'r') as f:
     340              chars = f.read()
     341          self.text.delete("1.0", "end")
     342          self.text.insert("1.0", chars)
     343          self.root.title(filename + " - a Python turtle graphics example")
     344          self.configGUI(NORMAL, DISABLED, DISABLED,
     345                         "Press start button", "red")
     346          self.state = READY
     347  
     348      def startDemo(self):
     349          self.refreshCanvas()
     350          self.dirty = True
     351          turtle.TurtleScreen._RUNNING = True
     352          self.configGUI(DISABLED, NORMAL, DISABLED,
     353                         "demo running...", "black")
     354          self.screen.clear()
     355          self.screen.mode("standard")
     356          self.state = RUNNING
     357  
     358          try:
     359              result = self.module.main()
     360              if result == "EVENTLOOP":
     361                  self.state = EVENTDRIVEN
     362              else:
     363                  self.state = DONE
     364          except turtle.Terminator:
     365              if self.root is None:
     366                  return
     367              self.state = DONE
     368              result = "stopped!"
     369          if self.state == DONE:
     370              self.configGUI(NORMAL, DISABLED, NORMAL,
     371                             result)
     372          elif self.state == EVENTDRIVEN:
     373              self.exitflag = True
     374              self.configGUI(DISABLED, NORMAL, DISABLED,
     375                             "use mouse/keys or STOP", "red")
     376  
     377      def clearCanvas(self):
     378          self.refreshCanvas()
     379          self.screen._delete("all")
     380          self.canvas.config(cursor="")
     381          self.configGUI(NORMAL, DISABLED, DISABLED)
     382  
     383      def stopIt(self):
     384          if self.exitflag:
     385              self.clearCanvas()
     386              self.exitflag = False
     387              self.configGUI(NORMAL, DISABLED, DISABLED,
     388                             "STOPPED!", "red")
     389          turtle.TurtleScreen._RUNNING = False
     390  
     391      def _destroy(self):
     392          turtle.TurtleScreen._RUNNING = False
     393          self.root.destroy()
     394          self.root = None
     395  
     396  
     397  def main():
     398      demo = DemoWindow()
     399      demo.root.mainloop()
     400  
     401  if __name__ == '__main__':
     402      main()