Demo/turtle/turtleDemo.py
#!/usr/bin/env python
import sys
import os

from Tkinter import *
from idlelib.Percolator import Percolator
from idlelib.ColorDelegator import ColorDelegator
from idlelib.textView import view_file

import turtle
import time

demo_dir = os.getcwd()
if "turtleDemo.py" not in os.listdir(demo_dir):
    print "Directory of turtleDemo must be current working directory!"
    print "But in your case this is", demo_dir
    sys.exit()

STARTUP = 1
READY = 2
RUNNING = 3
DONE = 4
EVENTDRIVEN = 5

menufont = ("Arial", 12, NORMAL)
btnfont = ("Arial", 12, 'bold')
txtfont = ('Lucida Console', 8, 'normal')

def getExampleEntries():
    entries1 = [entry for entry in os.listdir(demo_dir) if
                     entry.startswith("tdemo_") and
                     not entry.endswith(".pyc")]
    entries2 = []
    for entry in entries1:
        if entry.endswith(".py"):
            entries2.append(entry)
        else:
            path = os.path.join(demo_dir, entry)
            sys.path.append(path)
            subdir = [entry]
            scripts = [script for script in os.listdir(path) if
                            script.startswith("tdemo_") and
                            script.endswith(".py")]
            entries2.append(subdir+scripts)
    return entries2

help_entries = (  # (help_label,  help_file)
    ('Turtledemo help', "demohelp.txt"),
    ('About turtledemo', "about_turtledemo.txt"),
    ('About turtle module', "about_turtle.txt"),
    )

class DemoWindow(object):

    def __init__(self, filename=None):
        self.root = root = turtle._root = Tk()
        root.title('Python turtle-graphics examples')
        root.wm_protocol("WM_DELETE_WINDOW", self._destroy)

        root.grid_rowconfigure(1, weight=1)
        root.grid_columnconfigure(0, weight=1)
        root.grid_columnconfigure(1, minsize=90, weight=1)
        root.grid_columnconfigure(2, minsize=90, weight=1)
        root.grid_columnconfigure(3, minsize=90, weight=1)

        self.mBar = Frame(root, relief=RAISED, borderwidth=2)
        self.ExamplesBtn = self.makeLoadDemoMenu()
        self.OptionsBtn = self.makeHelpMenu()
        self.mBar.grid(row=0, columnspan=4, sticky='news')

        pane = PanedWindow(orient=HORIZONTAL, sashwidth=5,
                           sashrelief=SOLID, bg='#ddd')
        pane.add(self.makeTextFrame(pane))
        pane.add(self.makeGraphFrame(pane))
        pane.grid(row=1, columnspan=4, sticky='news')

        self.output_lbl = Label(root, height= 1, text=" --- ", bg="#ddf",
                                font=("Arial", 16, 'normal'), borderwidth=2,
                                relief=RIDGE)
        self.start_btn = Button(root, text=" START ", font=btnfont,
                                fg="white", disabledforeground = "#fed",
                                command=self.startDemo)
        self.stop_btn = Button(root, text=" STOP ", font=btnfont,
                               fg="white", disabledforeground = "#fed",
                               command=self.stopIt)
        self.clear_btn = Button(root, text=" CLEAR ", font=btnfont,
                                fg="white", disabledforeground="#fed",
                                command = self.clearCanvas)
        self.output_lbl.grid(row=2, column=0, sticky='news', padx=(0,5))
        self.start_btn.grid(row=2, column=1, sticky='ew')
        self.stop_btn.grid(row=2, column=2, sticky='ew')
        self.clear_btn.grid(row=2, column=3, sticky='ew')

        Percolator(self.text).insertfilter(ColorDelegator())
        self.dirty = False
        self.exitflag = False
        if filename:
            self.loadfile(filename)
        self.configGUI(NORMAL, DISABLED, DISABLED, DISABLED,
                       "Choose example from menu", "black")
        self.state = STARTUP


    def onResize(self, event):
        cwidth = self._canvas.winfo_width()
        cheight = self._canvas.winfo_height()
        self._canvas.xview_moveto(0.5*(self.canvwidth-cwidth)/self.canvwidth)
        self._canvas.yview_moveto(0.5*(self.canvheight-cheight)/self.canvheight)

    def makeTextFrame(self, root):
        self.text_frame = text_frame = Frame(root)
        self.text = text = Text(text_frame, name='text', padx=5,
                                wrap='none', width=45)

        self.vbar = vbar = Scrollbar(text_frame, name='vbar')
        vbar['command'] = text.yview
        vbar.pack(side=LEFT, fill=Y)
        self.hbar = hbar = Scrollbar(text_frame, name='hbar', orient=HORIZONTAL)
        hbar['command'] = text.xview
        hbar.pack(side=BOTTOM, fill=X)

        text['font'] = txtfont
        text['yscrollcommand'] = vbar.set
        text['xscrollcommand'] = hbar.set
        text.pack(side=LEFT, fill=BOTH, expand=1)
        return text_frame

    def makeGraphFrame(self, root):
        turtle._Screen._root = root
        self.canvwidth = 1000
        self.canvheight = 800
        turtle._Screen._canvas = self._canvas = canvas = turtle.ScrolledCanvas(
                root, 800, 600, self.canvwidth, self.canvheight)
        canvas.adjustScrolls()
        canvas._rootwindow.bind('<Configure>', self.onResize)
        canvas._canvas['borderwidth'] = 0

        self.screen = _s_ = turtle.Screen()
        turtle.TurtleScreen.__init__(_s_, _s_._canvas)
        self.scanvas = _s_._canvas
        turtle.RawTurtle.screens = [_s_]
        return canvas

    def configGUI(self, menu, start, stop, clear, txt="", color="blue"):
        self.ExamplesBtn.config(state=menu)

        self.start_btn.config(state=start,
                              bg="#d00" if start == NORMAL else "#fca")
        self.stop_btn.config(state=stop,
                             bg="#d00" if stop == NORMAL else "#fca")
        self.clear_btn.config(state=clear,
                              bg="#d00" if clear == NORMAL else"#fca")
        self.output_lbl.config(text=txt, fg=color)

    def makeLoadDemoMenu(self):
        CmdBtn = Menubutton(self.mBar, text='Examples',
                            underline=0, font=menufont)
        CmdBtn.pack(side=LEFT, padx="2m")
        CmdBtn.menu = Menu(CmdBtn)

        for entry in getExampleEntries():
            def loadexample(x):
                def emit():
                    self.loadfile(x)
                return emit
            if isinstance(entry,str):
                CmdBtn.menu.add_command(label=entry[6:-3], underline=0,
                                        font=menufont,
                                        command=loadexample(entry))
            else:
                _dir, entries = entry[0], entry[1:]
                CmdBtn.menu.choices = Menu(CmdBtn.menu)
                for e in entries:
                    CmdBtn.menu.choices.add_command(
                            label=e[6:-3], underline=0, font=menufont,
                            command = loadexample(os.path.join(_dir,e)))
                CmdBtn.menu.add_cascade(
                    label=_dir[6:], menu = CmdBtn.menu.choices, font=menufont)

        CmdBtn['menu'] = CmdBtn.menu
        return CmdBtn

    def makeHelpMenu(self):
        CmdBtn = Menubutton(self.mBar, text='Help', underline=0, font = menufont)
        CmdBtn.pack(side=LEFT, padx='2m')
        CmdBtn.menu = Menu(CmdBtn)

        for help_label, help_file in help_entries:
            def show(help_label=help_label, help_file=help_file):
                view_file(self.root, help_label, os.path.join(demo_dir, help_file))
            CmdBtn.menu.add_command(label=help_label, font=menufont, command=show)

        CmdBtn['menu'] = CmdBtn.menu
        return CmdBtn

    def refreshCanvas(self):
        if not self.dirty: return
        self.screen.clear()
        self.dirty=False

    def loadfile(self,filename):
        self.refreshCanvas()
        if os.path.exists(filename) and not os.path.isdir(filename):
            # load and display file text
            f = open(filename,'r')
            chars = f.read()
            f.close()
            self.text.delete("1.0", "end")
            self.text.insert("1.0",chars)
            direc, fname = os.path.split(filename)
            self.root.title(fname[6:-3]+" - a Python turtle graphics example")
            self.module = __import__(fname[:-3])
            self.configGUI(NORMAL, NORMAL, DISABLED, DISABLED,
                           "Press start button", "red")
            self.state = READY

    def startDemo(self):
        self.refreshCanvas()
        self.dirty = True
        turtle.TurtleScreen._RUNNING = True
        self.configGUI(DISABLED, DISABLED, NORMAL, DISABLED,
                       "demo running...", "black")
        self.screen.clear()
        self.screen.mode("standard")
        self.state = RUNNING

        try:
            result = self.module.main()
            if result == "EVENTLOOP":
                self.state = EVENTDRIVEN
            else:
                self.state = DONE
        except turtle.Terminator:
            if self.root is None:
                return
            self.state = DONE
            result = "stopped!"
        if self.state == DONE:
            self.configGUI(NORMAL, NORMAL, DISABLED, NORMAL,
                           result)
        elif self.state == EVENTDRIVEN:
            self.exitflag = True
            self.configGUI(DISABLED, DISABLED, NORMAL, DISABLED,
                           "use mouse/keys or STOP", "red")

    def clearCanvas(self):
        self.refreshCanvas()
        self.scanvas.config(cursor="")
        self.configGUI(NORMAL, NORMAL, DISABLED, DISABLED)

    def stopIt(self):
        if self.exitflag:
            self.clearCanvas()
            self.exitflag = False
            self.configGUI(NORMAL, NORMAL, DISABLED, DISABLED,
                           "STOPPED!", "red")
            turtle.TurtleScreen._RUNNING = False
        else:
            turtle.TurtleScreen._RUNNING = False

    def _destroy(self):
        turtle.TurtleScreen._RUNNING = False
        self.root.destroy()
        self.root = None
        #sys.exit()

def main():
    demo = DemoWindow()
    demo.root.mainloop()

if __name__ == '__main__':
    main()