1076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl#!/usr/bin/env python3
294ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy
394ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy"""
494ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy  ----------------------------------------------
594ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy      turtleDemo - Help
694ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy  ----------------------------------------------
794ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy
894ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy  This document has two sections:
994ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy
1094ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy  (1) How to use the demo viewer
1194ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy  (2) How to add your own demos to the demo repository
1294ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy
1394ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy
1494ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy  (1) How to use the demo viewer.
1594ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy
1694ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy  Select a demoscript from the example menu.
17867579972d9a1e9e077626de6d6d083e30fea110Terry Jan Reedy  The (syntax colored) source code appears in the left
1894ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy  source code window. IT CANNOT BE EDITED, but ONLY VIEWED!
1994ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy
20c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy  The demo viewer windows can be resized. The divider between text
21c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy  and canvas can be moved by grabbing it with the mouse. The text font
22d43ffc9aba8d3a594486d2488bf3e3dfb50a43b2Terry Jan Reedy  size can be changed from the menu and with Control/Command '-'/'+'.
23d43ffc9aba8d3a594486d2488bf3e3dfb50a43b2Terry Jan Reedy  It can also be changed on most systems with Control-mousewheel
24d43ffc9aba8d3a594486d2488bf3e3dfb50a43b2Terry Jan Reedy  when the mouse is over the text.
2594ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy
26c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy  Press START button to start the demo.
27c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy  Stop execution by pressing the STOP button.
28c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy  Clear screen by pressing the CLEAR button.
29c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy  Restart by pressing the START button again.
30c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy
31c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy  SPECIAL demos, such as clock.py are those which run EVENTDRIVEN.
3294ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy
3394ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy      Press START button to start the demo.
3494ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy
3594ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy      - Until the EVENTLOOP is entered everything works
3694ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy      as in an ordinary demo script.
3794ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy
3894ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy      - When the EVENTLOOP is entered, you control the
3994ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy      application by using the mouse and/or keys (or it's
4094ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy      controlled by some timer events)
4194ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy      To stop it you can and must press the STOP button.
4294ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy
4394ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy      While the EVENTLOOP is running, the examples menu is disabled.
4494ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy
4594ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy      - Only after having pressed the STOP button, you may
4694ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy      restart it or choose another example script.
4794ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy
4894ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy   * * * * * * * *
4994ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy   In some rare situations there may occur interferences/conflicts
5094ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy   between events concerning the demo script and those concerning the
5194ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy   demo-viewer. (They run in the same process.) Strange behaviour may be
5294ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy   the consequence and in the worst case you must close and restart the
5394ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy   viewer.
5494ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy   * * * * * * * *
5594ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy
5694ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy
5794ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy   (2) How to add your own demos to the demo repository
5894ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy
5994ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy   - Place the file in the same directory as turtledemo/__main__.py
6094ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy     IMPORTANT! When imported, the demo should not modify the system
6194ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy     by calling functions in other modules, such as sys, tkinter, or
6294ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy     turtle. Global variables should be initialized in main().
6394ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy
6494ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy   - The code must contain a main() function which will
6594ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy     be executed by the viewer (see provided example scripts).
6694ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy     It may return a string which will be displayed in the Label below
6794ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy     the source code window (when execution has finished.)
6894ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy
6994ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy   - In order to run mydemo.py by itself, such as during development,
7094ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy     add the following at the end of the file:
7194ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy
7294ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy    if __name__ == '__main__':
7394ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy        main()
7494ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy        mainloop()  # keep window open
7594ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy
7694ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy    python -m turtledemo.mydemo  # will then run it
7794ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy
7894ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy   - If the demo is EVENT DRIVEN, main must return the string
7994ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy     "EVENTLOOP". This informs the demo viewer that the script is
8094ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy     still running and must be stopped by the user!
8194ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy
8294ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy     If an "EVENTLOOP" demo runs by itself, as with clock, which uses
8394ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy     ontimer, or minimal_hanoi, which loops by recursion, then the
8494ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy     code should catch the turtle.Terminator exception that will be
8594ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy     raised when the user presses the STOP button.  (Paint is not such
8694ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy     a demo; it only acts in response to mouse clicks and movements.)
8794ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy"""
88076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandlimport sys
89076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandlimport os
90076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl
91076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandlfrom tkinter import *
922bac3b778f0a3efe374334b9694e99ed48431414Terry Jan Reedyfrom idlelib.colorizer import ColorDelegator, color_config
936fa5bdc6e85ec48925bc0d856b134f59d01c300fTerry Jan Reedyfrom idlelib.percolator import Percolator
946fa5bdc6e85ec48925bc0d856b134f59d01c300fTerry Jan Reedyfrom idlelib.textview import view_text
9594ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedyfrom turtledemo import __doc__ as about_turtledemo
96076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl
97076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandlimport turtle
98076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl
99076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandldemo_dir = os.path.dirname(os.path.abspath(__file__))
100c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedydarwin = sys.platform == 'darwin'
101076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl
102076cbae4b97cfb7764321f92d8506ef38366facaGeorg BrandlSTARTUP = 1
103076cbae4b97cfb7764321f92d8506ef38366facaGeorg BrandlREADY = 2
104076cbae4b97cfb7764321f92d8506ef38366facaGeorg BrandlRUNNING = 3
105076cbae4b97cfb7764321f92d8506ef38366facaGeorg BrandlDONE = 4
106076cbae4b97cfb7764321f92d8506ef38366facaGeorg BrandlEVENTDRIVEN = 5
107076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl
108076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandlmenufont = ("Arial", 12, NORMAL)
109076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandlbtnfont = ("Arial", 12, 'bold')
110c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedytxtfont = ['Lucida Console', 10, 'normal']
111c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy
112c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan ReedyMINIMUM_FONT_SIZE = 6
113c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan ReedyMAXIMUM_FONT_SIZE = 100
114c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedyfont_sizes = [8, 9, 10, 11, 12, 14, 18, 20, 22, 24, 30]
115076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl
116076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandldef getExampleEntries():
117076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl    return [entry[:-3] for entry in os.listdir(demo_dir) if
118076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl            entry.endswith(".py") and entry[0] != '_']
119076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl
12094ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedyhelp_entries = (  # (help_label,  help_doc)
12194ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy    ('Turtledemo help', __doc__),
12294ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy    ('About turtledemo', about_turtledemo),
12394ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy    ('About turtle module', turtle.__doc__),
124280aace067e7df3b706c09e27b044dfea3bacaffTerry Jan Reedy    )
125076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl
1262bac3b778f0a3efe374334b9694e99ed48431414Terry Jan Reedy
1272bac3b778f0a3efe374334b9694e99ed48431414Terry Jan Reedy
128076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandlclass DemoWindow(object):
129076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl
130b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy    def __init__(self, filename=None):
131076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl        self.root = root = turtle._root = Tk()
132b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy        root.title('Python turtle-graphics examples')
133076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl        root.wm_protocol("WM_DELETE_WINDOW", self._destroy)
134076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl
135c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy        if darwin:
1367e55db2bc566381bfda3ddcd44c048f0c627880fTerry Jan Reedy            import subprocess
1377e55db2bc566381bfda3ddcd44c048f0c627880fTerry Jan Reedy            # Make sure we are the currently activated OS X application
1387e55db2bc566381bfda3ddcd44c048f0c627880fTerry Jan Reedy            # so that our menu bar appears.
1397e55db2bc566381bfda3ddcd44c048f0c627880fTerry Jan Reedy            p = subprocess.Popen(
1407e55db2bc566381bfda3ddcd44c048f0c627880fTerry Jan Reedy                    [
1417e55db2bc566381bfda3ddcd44c048f0c627880fTerry Jan Reedy                        'osascript',
1427e55db2bc566381bfda3ddcd44c048f0c627880fTerry Jan Reedy                        '-e', 'tell application "System Events"',
1437e55db2bc566381bfda3ddcd44c048f0c627880fTerry Jan Reedy                        '-e', 'set frontmost of the first process whose '
1447e55db2bc566381bfda3ddcd44c048f0c627880fTerry Jan Reedy                              'unix id is {} to true'.format(os.getpid()),
1457e55db2bc566381bfda3ddcd44c048f0c627880fTerry Jan Reedy                        '-e', 'end tell',
1467e55db2bc566381bfda3ddcd44c048f0c627880fTerry Jan Reedy                    ],
1477e55db2bc566381bfda3ddcd44c048f0c627880fTerry Jan Reedy                    stderr=subprocess.DEVNULL,
148c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy                    stdout=subprocess.DEVNULL,)
1497e55db2bc566381bfda3ddcd44c048f0c627880fTerry Jan Reedy
150f819ef74da8d5fc915e9fab672ba592efa190f0cTerry Jan Reedy        root.grid_rowconfigure(0, weight=1)
151b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy        root.grid_columnconfigure(0, weight=1)
152b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy        root.grid_columnconfigure(1, minsize=90, weight=1)
153b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy        root.grid_columnconfigure(2, minsize=90, weight=1)
154b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy        root.grid_columnconfigure(3, minsize=90, weight=1)
155076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl
156f819ef74da8d5fc915e9fab672ba592efa190f0cTerry Jan Reedy        self.mBar = Menu(root, relief=RAISED, borderwidth=2)
157f819ef74da8d5fc915e9fab672ba592efa190f0cTerry Jan Reedy        self.mBar.add_cascade(menu=self.makeLoadDemoMenu(self.mBar),
158c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy                              label='Examples', underline=0)
159c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy        self.mBar.add_cascade(menu=self.makeFontMenu(self.mBar),
160c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy                              label='Fontsize', underline=0)
161f819ef74da8d5fc915e9fab672ba592efa190f0cTerry Jan Reedy        self.mBar.add_cascade(menu=self.makeHelpMenu(self.mBar),
162c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy                              label='Help', underline=0)
163f819ef74da8d5fc915e9fab672ba592efa190f0cTerry Jan Reedy        root['menu'] = self.mBar
164b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy
165b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy        pane = PanedWindow(orient=HORIZONTAL, sashwidth=5,
166b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy                           sashrelief=SOLID, bg='#ddd')
167b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy        pane.add(self.makeTextFrame(pane))
168b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy        pane.add(self.makeGraphFrame(pane))
169f819ef74da8d5fc915e9fab672ba592efa190f0cTerry Jan Reedy        pane.grid(row=0, columnspan=4, sticky='news')
170b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy
171b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy        self.output_lbl = Label(root, height= 1, text=" --- ", bg="#ddf",
172b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy                                font=("Arial", 16, 'normal'), borderwidth=2,
173b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy                                relief=RIDGE)
174b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy        self.start_btn = Button(root, text=" START ", font=btnfont,
175b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy                                fg="white", disabledforeground = "#fed",
176b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy                                command=self.startDemo)
177b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy        self.stop_btn = Button(root, text=" STOP ", font=btnfont,
178b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy                               fg="white", disabledforeground = "#fed",
179b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy                               command=self.stopIt)
180b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy        self.clear_btn = Button(root, text=" CLEAR ", font=btnfont,
181b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy                                fg="white", disabledforeground="#fed",
182b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy                                command = self.clearCanvas)
183f819ef74da8d5fc915e9fab672ba592efa190f0cTerry Jan Reedy        self.output_lbl.grid(row=1, column=0, sticky='news', padx=(0,5))
184f819ef74da8d5fc915e9fab672ba592efa190f0cTerry Jan Reedy        self.start_btn.grid(row=1, column=1, sticky='ew')
185f819ef74da8d5fc915e9fab672ba592efa190f0cTerry Jan Reedy        self.stop_btn.grid(row=1, column=2, sticky='ew')
186f819ef74da8d5fc915e9fab672ba592efa190f0cTerry Jan Reedy        self.clear_btn.grid(row=1, column=3, sticky='ew')
187b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy
188b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy        Percolator(self.text).insertfilter(ColorDelegator())
189b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy        self.dirty = False
190b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy        self.exitflag = False
191b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy        if filename:
192b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy            self.loadfile(filename)
1932ced87f3e6f61b777a5cc4368527e6fc8928464fTerry Jan Reedy        self.configGUI(DISABLED, DISABLED, DISABLED,
194b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy                       "Choose example from menu", "black")
195b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy        self.state = STARTUP
196076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl
197b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy
198b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy    def onResize(self, event):
199b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy        cwidth = self._canvas.winfo_width()
200b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy        cheight = self._canvas.winfo_height()
201b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy        self._canvas.xview_moveto(0.5*(self.canvwidth-cwidth)/self.canvwidth)
202b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy        self._canvas.yview_moveto(0.5*(self.canvheight-cheight)/self.canvheight)
203b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy
204b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy    def makeTextFrame(self, root):
205b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy        self.text_frame = text_frame = Frame(root)
206b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy        self.text = text = Text(text_frame, name='text', padx=5,
207b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy                                wrap='none', width=45)
2082bac3b778f0a3efe374334b9694e99ed48431414Terry Jan Reedy        color_config(text)
209b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy
210b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy        self.vbar = vbar = Scrollbar(text_frame, name='vbar')
211076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl        vbar['command'] = text.yview
212076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl        vbar.pack(side=LEFT, fill=Y)
213b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy        self.hbar = hbar = Scrollbar(text_frame, name='hbar', orient=HORIZONTAL)
214076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl        hbar['command'] = text.xview
215076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl        hbar.pack(side=BOTTOM, fill=X)
216076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl        text['yscrollcommand'] = vbar.set
217b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy        text['xscrollcommand'] = hbar.set
218d43ffc9aba8d3a594486d2488bf3e3dfb50a43b2Terry Jan Reedy
219d43ffc9aba8d3a594486d2488bf3e3dfb50a43b2Terry Jan Reedy        text['font'] = tuple(txtfont)
220d43ffc9aba8d3a594486d2488bf3e3dfb50a43b2Terry Jan Reedy        shortcut = 'Command' if darwin else 'Control'
221d43ffc9aba8d3a594486d2488bf3e3dfb50a43b2Terry Jan Reedy        text.bind_all('<%s-minus>' % shortcut, self.decrease_size)
222d43ffc9aba8d3a594486d2488bf3e3dfb50a43b2Terry Jan Reedy        text.bind_all('<%s-underscore>' % shortcut, self.decrease_size)
223d43ffc9aba8d3a594486d2488bf3e3dfb50a43b2Terry Jan Reedy        text.bind_all('<%s-equal>' % shortcut, self.increase_size)
224d43ffc9aba8d3a594486d2488bf3e3dfb50a43b2Terry Jan Reedy        text.bind_all('<%s-plus>' % shortcut, self.increase_size)
225d43ffc9aba8d3a594486d2488bf3e3dfb50a43b2Terry Jan Reedy        text.bind('<Control-MouseWheel>', self.update_mousewheel)
226d43ffc9aba8d3a594486d2488bf3e3dfb50a43b2Terry Jan Reedy        text.bind('<Control-Button-4>', self.increase_size)
227d43ffc9aba8d3a594486d2488bf3e3dfb50a43b2Terry Jan Reedy        text.bind('<Control-Button-5>', self.decrease_size)
228d43ffc9aba8d3a594486d2488bf3e3dfb50a43b2Terry Jan Reedy
229b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy        text.pack(side=LEFT, fill=BOTH, expand=1)
230b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy        return text_frame
231b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy
232b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy    def makeGraphFrame(self, root):
233b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy        turtle._Screen._root = root
234b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy        self.canvwidth = 1000
235b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy        self.canvheight = 800
236b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy        turtle._Screen._canvas = self._canvas = canvas = turtle.ScrolledCanvas(
237b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy                root, 800, 600, self.canvwidth, self.canvheight)
238b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy        canvas.adjustScrolls()
239d43ffc9aba8d3a594486d2488bf3e3dfb50a43b2Terry Jan Reedy        canvas._rootwindow.bind('<Configure>', self.onResize)
240b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy        canvas._canvas['borderwidth'] = 0
241b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy
242076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl        self.screen = _s_ = turtle.Screen()
243076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl        turtle.TurtleScreen.__init__(_s_, _s_._canvas)
244076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl        self.scanvas = _s_._canvas
245076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl        turtle.RawTurtle.screens = [_s_]
246b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy        return canvas
247076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl
248c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy    def set_txtsize(self, size):
249c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy        txtfont[1] = size
250c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy        self.text['font'] = tuple(txtfont)
251c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy        self.output_lbl['text'] = 'Font size %d' % size
252c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy
253c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy    def decrease_size(self, dummy=None):
254c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy        self.set_txtsize(max(txtfont[1] - 1, MINIMUM_FONT_SIZE))
255d43ffc9aba8d3a594486d2488bf3e3dfb50a43b2Terry Jan Reedy        return 'break'
256c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy
257c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy    def increase_size(self, dummy=None):
258c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy        self.set_txtsize(min(txtfont[1] + 1, MAXIMUM_FONT_SIZE))
259d43ffc9aba8d3a594486d2488bf3e3dfb50a43b2Terry Jan Reedy        return 'break'
260c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy
261c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy    def update_mousewheel(self, event):
262c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy        # For wheel up, event.delte = 120 on Windows, -1 on darwin.
263c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy        # X-11 sends Control-Button-4 event instead.
264d43ffc9aba8d3a594486d2488bf3e3dfb50a43b2Terry Jan Reedy        if (event.delta < 0) == (not darwin):
265d43ffc9aba8d3a594486d2488bf3e3dfb50a43b2Terry Jan Reedy            return self.decrease_size()
266d43ffc9aba8d3a594486d2488bf3e3dfb50a43b2Terry Jan Reedy        else:
267d43ffc9aba8d3a594486d2488bf3e3dfb50a43b2Terry Jan Reedy            return self.increase_size()
268c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy
2692ced87f3e6f61b777a5cc4368527e6fc8928464fTerry Jan Reedy    def configGUI(self, start, stop, clear, txt="", color="blue"):
2708b95d5e0bf00d9d0098579d29fd6bb9322071879Terry Jan Reedy        self.start_btn.config(state=start,
2718b95d5e0bf00d9d0098579d29fd6bb9322071879Terry Jan Reedy                              bg="#d00" if start == NORMAL else "#fca")
2728b95d5e0bf00d9d0098579d29fd6bb9322071879Terry Jan Reedy        self.stop_btn.config(state=stop,
2738b95d5e0bf00d9d0098579d29fd6bb9322071879Terry Jan Reedy                             bg="#d00" if stop == NORMAL else "#fca")
2748b95d5e0bf00d9d0098579d29fd6bb9322071879Terry Jan Reedy        self.clear_btn.config(state=clear,
2758b95d5e0bf00d9d0098579d29fd6bb9322071879Terry Jan Reedy                              bg="#d00" if clear == NORMAL else"#fca")
276076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl        self.output_lbl.config(text=txt, fg=color)
277076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl
278f819ef74da8d5fc915e9fab672ba592efa190f0cTerry Jan Reedy    def makeLoadDemoMenu(self, master):
279f819ef74da8d5fc915e9fab672ba592efa190f0cTerry Jan Reedy        menu = Menu(master)
280076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl
281076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl        for entry in getExampleEntries():
282c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy            def load(entry=entry):
283c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy                self.loadfile(entry)
284f819ef74da8d5fc915e9fab672ba592efa190f0cTerry Jan Reedy            menu.add_command(label=entry, underline=0,
285c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy                             font=menufont, command=load)
286c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy        return menu
287076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl
288c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy    def makeFontMenu(self, master):
289c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy        menu = Menu(master)
290c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy        menu.add_command(label="Decrease (C-'-')", command=self.decrease_size,
291c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy                         font=menufont)
292c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy        menu.add_command(label="Increase (C-'+')", command=self.increase_size,
293c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy                         font=menufont)
294c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy        menu.add_separator()
295c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy
296c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy        for size in font_sizes:
297c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy            def resize(size=size):
298c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy                self.set_txtsize(size)
299c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy            menu.add_command(label=str(size), underline=0,
300c6a1c02cce3a12683cd951193c8f9ce30d79f6dcTerry Jan Reedy                             font=menufont, command=resize)
301f819ef74da8d5fc915e9fab672ba592efa190f0cTerry Jan Reedy        return menu
302076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl
303f819ef74da8d5fc915e9fab672ba592efa190f0cTerry Jan Reedy    def makeHelpMenu(self, master):
304f819ef74da8d5fc915e9fab672ba592efa190f0cTerry Jan Reedy        menu = Menu(master)
305076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl
306280aace067e7df3b706c09e27b044dfea3bacaffTerry Jan Reedy        for help_label, help_file in help_entries:
307280aace067e7df3b706c09e27b044dfea3bacaffTerry Jan Reedy            def show(help_label=help_label, help_file=help_file):
30894ee51ed9ee1df1890b9d2493a64bc484a522da2Terry Jan Reedy                view_text(self.root, help_label, help_file)
309f819ef74da8d5fc915e9fab672ba592efa190f0cTerry Jan Reedy            menu.add_command(label=help_label, font=menufont, command=show)
310f819ef74da8d5fc915e9fab672ba592efa190f0cTerry Jan Reedy        return menu
311076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl
312076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl    def refreshCanvas(self):
3138450c5331548e396d31639776c91e191a5f3147eTerry Jan Reedy        if self.dirty:
3148450c5331548e396d31639776c91e191a5f3147eTerry Jan Reedy            self.screen.clear()
3158450c5331548e396d31639776c91e191a5f3147eTerry Jan Reedy            self.dirty=False
316076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl
317076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl    def loadfile(self, filename):
3188450c5331548e396d31639776c91e191a5f3147eTerry Jan Reedy        self.clearCanvas()
3198450c5331548e396d31639776c91e191a5f3147eTerry Jan Reedy        turtle.TurtleScreen._RUNNING = False
320076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl        modname = 'turtledemo.' + filename
321076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl        __import__(modname)
322076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl        self.module = sys.modules[modname]
323076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl        with open(self.module.__file__, 'r') as f:
324076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl            chars = f.read()
325076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl        self.text.delete("1.0", "end")
326076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl        self.text.insert("1.0", chars)
327076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl        self.root.title(filename + " - a Python turtle graphics example")
3282ced87f3e6f61b777a5cc4368527e6fc8928464fTerry Jan Reedy        self.configGUI(NORMAL, DISABLED, DISABLED,
329076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl                       "Press start button", "red")
330076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl        self.state = READY
331076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl
332076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl    def startDemo(self):
333076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl        self.refreshCanvas()
334076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl        self.dirty = True
335076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl        turtle.TurtleScreen._RUNNING = True
3362ced87f3e6f61b777a5cc4368527e6fc8928464fTerry Jan Reedy        self.configGUI(DISABLED, NORMAL, DISABLED,
337076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl                       "demo running...", "black")
338076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl        self.screen.clear()
339076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl        self.screen.mode("standard")
340076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl        self.state = RUNNING
341076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl
342076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl        try:
343076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl            result = self.module.main()
344076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl            if result == "EVENTLOOP":
345076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl                self.state = EVENTDRIVEN
346076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl            else:
347076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl                self.state = DONE
348076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl        except turtle.Terminator:
34980a1803193bcaef7a50324974117c3aa3fb0e48fSerhiy Storchaka            if self.root is None:
35080a1803193bcaef7a50324974117c3aa3fb0e48fSerhiy Storchaka                return
351076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl            self.state = DONE
352076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl            result = "stopped!"
353076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl        if self.state == DONE:
3542ced87f3e6f61b777a5cc4368527e6fc8928464fTerry Jan Reedy            self.configGUI(NORMAL, DISABLED, NORMAL,
355076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl                           result)
356076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl        elif self.state == EVENTDRIVEN:
357076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl            self.exitflag = True
3582ced87f3e6f61b777a5cc4368527e6fc8928464fTerry Jan Reedy            self.configGUI(DISABLED, NORMAL, DISABLED,
359076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl                           "use mouse/keys or STOP", "red")
360076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl
361076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl    def clearCanvas(self):
362076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl        self.refreshCanvas()
363076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl        self.screen._delete("all")
364076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl        self.scanvas.config(cursor="")
3652ced87f3e6f61b777a5cc4368527e6fc8928464fTerry Jan Reedy        self.configGUI(NORMAL, DISABLED, DISABLED)
366076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl
367076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl    def stopIt(self):
368076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl        if self.exitflag:
369076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl            self.clearCanvas()
370076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl            self.exitflag = False
3712ced87f3e6f61b777a5cc4368527e6fc8928464fTerry Jan Reedy            self.configGUI(NORMAL, DISABLED, DISABLED,
372076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl                           "STOPPED!", "red")
3738450c5331548e396d31639776c91e191a5f3147eTerry Jan Reedy        turtle.TurtleScreen._RUNNING = False
374b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy
375b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy    def _destroy(self):
37680a1803193bcaef7a50324974117c3aa3fb0e48fSerhiy Storchaka        turtle.TurtleScreen._RUNNING = False
377b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy        self.root.destroy()
37880a1803193bcaef7a50324974117c3aa3fb0e48fSerhiy Storchaka        self.root = None
379b03f0422c57ef9a19c3d62728a949020fbf608cbTerry Jan Reedy
380076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl
381b8352e7253e61e24f6ba3f06cda6135a3c976884Terry Jan Reedydef main():
382076cbae4b97cfb7764321f92d8506ef38366facaGeorg Brandl    demo = DemoWindow()
383b8352e7253e61e24f6ba3f06cda6135a3c976884Terry Jan Reedy    demo.root.mainloop()
384b8352e7253e61e24f6ba3f06cda6135a3c976884Terry Jan Reedy
385b8352e7253e61e24f6ba3f06cda6135a3c976884Terry Jan Reedyif __name__ == '__main__':
386b8352e7253e61e24f6ba3f06cda6135a3c976884Terry Jan Reedy    main()
387