1#!/usr/bin/env python
2import sys
3import os
4
5from Tkinter import *
6from idlelib.Percolator import Percolator
7from idlelib.ColorDelegator import ColorDelegator
8from idlelib.textView import view_file
9
10import turtle
11import time
12
13demo_dir = os.getcwd()
14if "turtleDemo.py" not in os.listdir(demo_dir):
15    print "Directory of turtleDemo must be current working directory!"
16    print "But in your case this is", demo_dir
17    sys.exit()
18
19STARTUP = 1
20READY = 2
21RUNNING = 3
22DONE = 4
23EVENTDRIVEN = 5
24
25menufont = ("Arial", 12, NORMAL)
26btnfont = ("Arial", 12, 'bold')
27txtfont = ('Lucida Console', 8, 'normal')
28
29def getExampleEntries():
30    entries1 = [entry for entry in os.listdir(demo_dir) if
31                     entry.startswith("tdemo_") and
32                     not entry.endswith(".pyc")]
33    entries2 = []
34    for entry in entries1:
35        if entry.endswith(".py"):
36            entries2.append(entry)
37        else:
38            path = os.path.join(demo_dir, entry)
39            sys.path.append(path)
40            subdir = [entry]
41            scripts = [script for script in os.listdir(path) if
42                            script.startswith("tdemo_") and
43                            script.endswith(".py")]
44            entries2.append(subdir+scripts)
45    return entries2
46
47help_entries = (  # (help_label,  help_file)
48    ('Turtledemo help', "demohelp.txt"),
49    ('About turtledemo', "about_turtledemo.txt"),
50    ('About turtle module', "about_turtle.txt"),
51    )
52
53class DemoWindow(object):
54
55    def __init__(self, filename=None):
56        self.root = root = turtle._root = Tk()
57        root.title('Python turtle-graphics examples')
58        root.wm_protocol("WM_DELETE_WINDOW", self._destroy)
59
60        root.grid_rowconfigure(1, weight=1)
61        root.grid_columnconfigure(0, weight=1)
62        root.grid_columnconfigure(1, minsize=90, weight=1)
63        root.grid_columnconfigure(2, minsize=90, weight=1)
64        root.grid_columnconfigure(3, minsize=90, weight=1)
65
66        self.mBar = Frame(root, relief=RAISED, borderwidth=2)
67        self.ExamplesBtn = self.makeLoadDemoMenu()
68        self.OptionsBtn = self.makeHelpMenu()
69        self.mBar.grid(row=0, columnspan=4, sticky='news')
70
71        pane = PanedWindow(orient=HORIZONTAL, sashwidth=5,
72                           sashrelief=SOLID, bg='#ddd')
73        pane.add(self.makeTextFrame(pane))
74        pane.add(self.makeGraphFrame(pane))
75        pane.grid(row=1, columnspan=4, sticky='news')
76
77        self.output_lbl = Label(root, height= 1, text=" --- ", bg="#ddf",
78                                font=("Arial", 16, 'normal'), borderwidth=2,
79                                relief=RIDGE)
80        self.start_btn = Button(root, text=" START ", font=btnfont,
81                                fg="white", disabledforeground = "#fed",
82                                command=self.startDemo)
83        self.stop_btn = Button(root, text=" STOP ", font=btnfont,
84                               fg="white", disabledforeground = "#fed",
85                               command=self.stopIt)
86        self.clear_btn = Button(root, text=" CLEAR ", font=btnfont,
87                                fg="white", disabledforeground="#fed",
88                                command = self.clearCanvas)
89        self.output_lbl.grid(row=2, column=0, sticky='news', padx=(0,5))
90        self.start_btn.grid(row=2, column=1, sticky='ew')
91        self.stop_btn.grid(row=2, column=2, sticky='ew')
92        self.clear_btn.grid(row=2, column=3, sticky='ew')
93
94        Percolator(self.text).insertfilter(ColorDelegator())
95        self.dirty = False
96        self.exitflag = False
97        if filename:
98            self.loadfile(filename)
99        self.configGUI(NORMAL, DISABLED, DISABLED, DISABLED,
100                       "Choose example from menu", "black")
101        self.state = STARTUP
102
103
104    def onResize(self, event):
105        cwidth = self._canvas.winfo_width()
106        cheight = self._canvas.winfo_height()
107        self._canvas.xview_moveto(0.5*(self.canvwidth-cwidth)/self.canvwidth)
108        self._canvas.yview_moveto(0.5*(self.canvheight-cheight)/self.canvheight)
109
110    def makeTextFrame(self, root):
111        self.text_frame = text_frame = Frame(root)
112        self.text = text = Text(text_frame, name='text', padx=5,
113                                wrap='none', width=45)
114
115        self.vbar = vbar = Scrollbar(text_frame, name='vbar')
116        vbar['command'] = text.yview
117        vbar.pack(side=LEFT, fill=Y)
118        self.hbar = hbar = Scrollbar(text_frame, name='hbar', orient=HORIZONTAL)
119        hbar['command'] = text.xview
120        hbar.pack(side=BOTTOM, fill=X)
121
122        text['font'] = txtfont
123        text['yscrollcommand'] = vbar.set
124        text['xscrollcommand'] = hbar.set
125        text.pack(side=LEFT, fill=BOTH, expand=1)
126        return text_frame
127
128    def makeGraphFrame(self, root):
129        turtle._Screen._root = root
130        self.canvwidth = 1000
131        self.canvheight = 800
132        turtle._Screen._canvas = self._canvas = canvas = turtle.ScrolledCanvas(
133                root, 800, 600, self.canvwidth, self.canvheight)
134        canvas.adjustScrolls()
135        canvas._rootwindow.bind('<Configure>', self.onResize)
136        canvas._canvas['borderwidth'] = 0
137
138        self.screen = _s_ = turtle.Screen()
139        turtle.TurtleScreen.__init__(_s_, _s_._canvas)
140        self.scanvas = _s_._canvas
141        turtle.RawTurtle.screens = [_s_]
142        return canvas
143
144    def configGUI(self, menu, start, stop, clear, txt="", color="blue"):
145        self.ExamplesBtn.config(state=menu)
146
147        self.start_btn.config(state=start,
148                              bg="#d00" if start == NORMAL else "#fca")
149        self.stop_btn.config(state=stop,
150                             bg="#d00" if stop == NORMAL else "#fca")
151        self.clear_btn.config(state=clear,
152                              bg="#d00" if clear == NORMAL else"#fca")
153        self.output_lbl.config(text=txt, fg=color)
154
155    def makeLoadDemoMenu(self):
156        CmdBtn = Menubutton(self.mBar, text='Examples',
157                            underline=0, font=menufont)
158        CmdBtn.pack(side=LEFT, padx="2m")
159        CmdBtn.menu = Menu(CmdBtn)
160
161        for entry in getExampleEntries():
162            def loadexample(x):
163                def emit():
164                    self.loadfile(x)
165                return emit
166            if isinstance(entry,str):
167                CmdBtn.menu.add_command(label=entry[6:-3], underline=0,
168                                        font=menufont,
169                                        command=loadexample(entry))
170            else:
171                _dir, entries = entry[0], entry[1:]
172                CmdBtn.menu.choices = Menu(CmdBtn.menu)
173                for e in entries:
174                    CmdBtn.menu.choices.add_command(
175                            label=e[6:-3], underline=0, font=menufont,
176                            command = loadexample(os.path.join(_dir,e)))
177                CmdBtn.menu.add_cascade(
178                    label=_dir[6:], menu = CmdBtn.menu.choices, font=menufont)
179
180        CmdBtn['menu'] = CmdBtn.menu
181        return CmdBtn
182
183    def makeHelpMenu(self):
184        CmdBtn = Menubutton(self.mBar, text='Help', underline=0, font = menufont)
185        CmdBtn.pack(side=LEFT, padx='2m')
186        CmdBtn.menu = Menu(CmdBtn)
187
188        for help_label, help_file in help_entries:
189            def show(help_label=help_label, help_file=help_file):
190                view_file(self.root, help_label, os.path.join(demo_dir, help_file))
191            CmdBtn.menu.add_command(label=help_label, font=menufont, command=show)
192
193        CmdBtn['menu'] = CmdBtn.menu
194        return CmdBtn
195
196    def refreshCanvas(self):
197        if not self.dirty: return
198        self.screen.clear()
199        self.dirty=False
200
201    def loadfile(self,filename):
202        self.refreshCanvas()
203        if os.path.exists(filename) and not os.path.isdir(filename):
204            # load and display file text
205            f = open(filename,'r')
206            chars = f.read()
207            f.close()
208            self.text.delete("1.0", "end")
209            self.text.insert("1.0",chars)
210            direc, fname = os.path.split(filename)
211            self.root.title(fname[6:-3]+" - a Python turtle graphics example")
212            self.module = __import__(fname[:-3])
213            self.configGUI(NORMAL, NORMAL, DISABLED, DISABLED,
214                           "Press start button", "red")
215            self.state = READY
216
217    def startDemo(self):
218        self.refreshCanvas()
219        self.dirty = True
220        turtle.TurtleScreen._RUNNING = True
221        self.configGUI(DISABLED, DISABLED, NORMAL, DISABLED,
222                       "demo running...", "black")
223        self.screen.clear()
224        self.screen.mode("standard")
225        self.state = RUNNING
226
227        try:
228            result = self.module.main()
229            if result == "EVENTLOOP":
230                self.state = EVENTDRIVEN
231            else:
232                self.state = DONE
233        except turtle.Terminator:
234            if self.root is None:
235                return
236            self.state = DONE
237            result = "stopped!"
238        if self.state == DONE:
239            self.configGUI(NORMAL, NORMAL, DISABLED, NORMAL,
240                           result)
241        elif self.state == EVENTDRIVEN:
242            self.exitflag = True
243            self.configGUI(DISABLED, DISABLED, NORMAL, DISABLED,
244                           "use mouse/keys or STOP", "red")
245
246    def clearCanvas(self):
247        self.refreshCanvas()
248        self.scanvas.config(cursor="")
249        self.configGUI(NORMAL, NORMAL, DISABLED, DISABLED)
250
251    def stopIt(self):
252        if self.exitflag:
253            self.clearCanvas()
254            self.exitflag = False
255            self.configGUI(NORMAL, NORMAL, DISABLED, DISABLED,
256                           "STOPPED!", "red")
257            turtle.TurtleScreen._RUNNING = False
258        else:
259            turtle.TurtleScreen._RUNNING = False
260
261    def _destroy(self):
262        turtle.TurtleScreen._RUNNING = False
263        self.root.destroy()
264        self.root = None
265        #sys.exit()
266
267def main():
268    demo = DemoWindow()
269    demo.root.mainloop()
270
271if __name__ == '__main__':
272    main()
273