Tkinter renaming reversal: move modules back in their place.
This commit is contained in:
parent
248e3a8b7f
commit
33cece05b9
|
@ -0,0 +1,49 @@
|
|||
# dialog.py -- Tkinter interface to the tk_dialog script.
|
||||
|
||||
from tkinter import *
|
||||
from tkinter import _cnfmerge
|
||||
|
||||
if TkVersion <= 3.6:
|
||||
DIALOG_ICON = 'warning'
|
||||
else:
|
||||
DIALOG_ICON = 'questhead'
|
||||
|
||||
|
||||
class Dialog(Widget):
|
||||
def __init__(self, master=None, cnf={}, **kw):
|
||||
cnf = _cnfmerge((cnf, kw))
|
||||
self.widgetName = '__dialog__'
|
||||
Widget._setup(self, master, cnf)
|
||||
self.num = self.tk.getint(
|
||||
self.tk.call(
|
||||
'tk_dialog', self._w,
|
||||
cnf['title'], cnf['text'],
|
||||
cnf['bitmap'], cnf['default'],
|
||||
*cnf['strings']))
|
||||
try: Widget.destroy(self)
|
||||
except TclError: pass
|
||||
def destroy(self): pass
|
||||
|
||||
def _test():
|
||||
d = Dialog(None, {'title': 'File Modified',
|
||||
'text':
|
||||
'File "Python.h" has been modified'
|
||||
' since the last time it was saved.'
|
||||
' Do you want to save it before'
|
||||
' exiting the application.',
|
||||
'bitmap': DIALOG_ICON,
|
||||
'default': 0,
|
||||
'strings': ('Save File',
|
||||
'Discard Changes',
|
||||
'Return to Editor')})
|
||||
print d.num
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
t = Button(None, {'text': 'Test',
|
||||
'command': _test,
|
||||
Pack: {}})
|
||||
q = Button(None, {'text': 'Quit',
|
||||
'command': t.quit,
|
||||
Pack: {}})
|
||||
t.mainloop()
|
|
@ -0,0 +1,274 @@
|
|||
"""File selection dialog classes.
|
||||
|
||||
Classes:
|
||||
|
||||
- FileDialog
|
||||
- LoadFileDialog
|
||||
- SaveFileDialog
|
||||
|
||||
"""
|
||||
|
||||
from Tkinter import *
|
||||
from Dialog import Dialog
|
||||
|
||||
import os
|
||||
import fnmatch
|
||||
|
||||
|
||||
dialogstates = {}
|
||||
|
||||
|
||||
class FileDialog:
|
||||
|
||||
"""Standard file selection dialog -- no checks on selected file.
|
||||
|
||||
Usage:
|
||||
|
||||
d = FileDialog(master)
|
||||
fname = d.go(dir_or_file, pattern, default, key)
|
||||
if fname is None: ...canceled...
|
||||
else: ...open file...
|
||||
|
||||
All arguments to go() are optional.
|
||||
|
||||
The 'key' argument specifies a key in the global dictionary
|
||||
'dialogstates', which keeps track of the values for the directory
|
||||
and pattern arguments, overriding the values passed in (it does
|
||||
not keep track of the default argument!). If no key is specified,
|
||||
the dialog keeps no memory of previous state. Note that memory is
|
||||
kept even when the dialog is canceled. (All this emulates the
|
||||
behavior of the Macintosh file selection dialogs.)
|
||||
|
||||
"""
|
||||
|
||||
title = "File Selection Dialog"
|
||||
|
||||
def __init__(self, master, title=None):
|
||||
if title is None: title = self.title
|
||||
self.master = master
|
||||
self.directory = None
|
||||
|
||||
self.top = Toplevel(master)
|
||||
self.top.title(title)
|
||||
self.top.iconname(title)
|
||||
|
||||
self.botframe = Frame(self.top)
|
||||
self.botframe.pack(side=BOTTOM, fill=X)
|
||||
|
||||
self.selection = Entry(self.top)
|
||||
self.selection.pack(side=BOTTOM, fill=X)
|
||||
self.selection.bind('<Return>', self.ok_event)
|
||||
|
||||
self.filter = Entry(self.top)
|
||||
self.filter.pack(side=TOP, fill=X)
|
||||
self.filter.bind('<Return>', self.filter_command)
|
||||
|
||||
self.midframe = Frame(self.top)
|
||||
self.midframe.pack(expand=YES, fill=BOTH)
|
||||
|
||||
self.filesbar = Scrollbar(self.midframe)
|
||||
self.filesbar.pack(side=RIGHT, fill=Y)
|
||||
self.files = Listbox(self.midframe, exportselection=0,
|
||||
yscrollcommand=(self.filesbar, 'set'))
|
||||
self.files.pack(side=RIGHT, expand=YES, fill=BOTH)
|
||||
btags = self.files.bindtags()
|
||||
self.files.bindtags(btags[1:] + btags[:1])
|
||||
self.files.bind('<ButtonRelease-1>', self.files_select_event)
|
||||
self.files.bind('<Double-ButtonRelease-1>', self.files_double_event)
|
||||
self.filesbar.config(command=(self.files, 'yview'))
|
||||
|
||||
self.dirsbar = Scrollbar(self.midframe)
|
||||
self.dirsbar.pack(side=LEFT, fill=Y)
|
||||
self.dirs = Listbox(self.midframe, exportselection=0,
|
||||
yscrollcommand=(self.dirsbar, 'set'))
|
||||
self.dirs.pack(side=LEFT, expand=YES, fill=BOTH)
|
||||
self.dirsbar.config(command=(self.dirs, 'yview'))
|
||||
btags = self.dirs.bindtags()
|
||||
self.dirs.bindtags(btags[1:] + btags[:1])
|
||||
self.dirs.bind('<ButtonRelease-1>', self.dirs_select_event)
|
||||
self.dirs.bind('<Double-ButtonRelease-1>', self.dirs_double_event)
|
||||
|
||||
self.ok_button = Button(self.botframe,
|
||||
text="OK",
|
||||
command=self.ok_command)
|
||||
self.ok_button.pack(side=LEFT)
|
||||
self.filter_button = Button(self.botframe,
|
||||
text="Filter",
|
||||
command=self.filter_command)
|
||||
self.filter_button.pack(side=LEFT, expand=YES)
|
||||
self.cancel_button = Button(self.botframe,
|
||||
text="Cancel",
|
||||
command=self.cancel_command)
|
||||
self.cancel_button.pack(side=RIGHT)
|
||||
|
||||
self.top.protocol('WM_DELETE_WINDOW', self.cancel_command)
|
||||
# XXX Are the following okay for a general audience?
|
||||
self.top.bind('<Alt-w>', self.cancel_command)
|
||||
self.top.bind('<Alt-W>', self.cancel_command)
|
||||
|
||||
def go(self, dir_or_file=os.curdir, pattern="*", default="", key=None):
|
||||
if key and dialogstates.has_key(key):
|
||||
self.directory, pattern = dialogstates[key]
|
||||
else:
|
||||
dir_or_file = os.path.expanduser(dir_or_file)
|
||||
if os.path.isdir(dir_or_file):
|
||||
self.directory = dir_or_file
|
||||
else:
|
||||
self.directory, default = os.path.split(dir_or_file)
|
||||
self.set_filter(self.directory, pattern)
|
||||
self.set_selection(default)
|
||||
self.filter_command()
|
||||
self.selection.focus_set()
|
||||
self.top.wait_visibility() # window needs to be visible for the grab
|
||||
self.top.grab_set()
|
||||
self.how = None
|
||||
self.master.mainloop() # Exited by self.quit(how)
|
||||
if key:
|
||||
directory, pattern = self.get_filter()
|
||||
if self.how:
|
||||
directory = os.path.dirname(self.how)
|
||||
dialogstates[key] = directory, pattern
|
||||
self.top.destroy()
|
||||
return self.how
|
||||
|
||||
def quit(self, how=None):
|
||||
self.how = how
|
||||
self.master.quit() # Exit mainloop()
|
||||
|
||||
def dirs_double_event(self, event):
|
||||
self.filter_command()
|
||||
|
||||
def dirs_select_event(self, event):
|
||||
dir, pat = self.get_filter()
|
||||
subdir = self.dirs.get('active')
|
||||
dir = os.path.normpath(os.path.join(self.directory, subdir))
|
||||
self.set_filter(dir, pat)
|
||||
|
||||
def files_double_event(self, event):
|
||||
self.ok_command()
|
||||
|
||||
def files_select_event(self, event):
|
||||
file = self.files.get('active')
|
||||
self.set_selection(file)
|
||||
|
||||
def ok_event(self, event):
|
||||
self.ok_command()
|
||||
|
||||
def ok_command(self):
|
||||
self.quit(self.get_selection())
|
||||
|
||||
def filter_command(self, event=None):
|
||||
dir, pat = self.get_filter()
|
||||
try:
|
||||
names = os.listdir(dir)
|
||||
except os.error:
|
||||
self.master.bell()
|
||||
return
|
||||
self.directory = dir
|
||||
self.set_filter(dir, pat)
|
||||
names.sort()
|
||||
subdirs = [os.pardir]
|
||||
matchingfiles = []
|
||||
for name in names:
|
||||
fullname = os.path.join(dir, name)
|
||||
if os.path.isdir(fullname):
|
||||
subdirs.append(name)
|
||||
elif fnmatch.fnmatch(name, pat):
|
||||
matchingfiles.append(name)
|
||||
self.dirs.delete(0, END)
|
||||
for name in subdirs:
|
||||
self.dirs.insert(END, name)
|
||||
self.files.delete(0, END)
|
||||
for name in matchingfiles:
|
||||
self.files.insert(END, name)
|
||||
head, tail = os.path.split(self.get_selection())
|
||||
if tail == os.curdir: tail = ''
|
||||
self.set_selection(tail)
|
||||
|
||||
def get_filter(self):
|
||||
filter = self.filter.get()
|
||||
filter = os.path.expanduser(filter)
|
||||
if filter[-1:] == os.sep or os.path.isdir(filter):
|
||||
filter = os.path.join(filter, "*")
|
||||
return os.path.split(filter)
|
||||
|
||||
def get_selection(self):
|
||||
file = self.selection.get()
|
||||
file = os.path.expanduser(file)
|
||||
return file
|
||||
|
||||
def cancel_command(self, event=None):
|
||||
self.quit()
|
||||
|
||||
def set_filter(self, dir, pat):
|
||||
if not os.path.isabs(dir):
|
||||
try:
|
||||
pwd = os.getcwd()
|
||||
except os.error:
|
||||
pwd = None
|
||||
if pwd:
|
||||
dir = os.path.join(pwd, dir)
|
||||
dir = os.path.normpath(dir)
|
||||
self.filter.delete(0, END)
|
||||
self.filter.insert(END, os.path.join(dir or os.curdir, pat or "*"))
|
||||
|
||||
def set_selection(self, file):
|
||||
self.selection.delete(0, END)
|
||||
self.selection.insert(END, os.path.join(self.directory, file))
|
||||
|
||||
|
||||
class LoadFileDialog(FileDialog):
|
||||
|
||||
"""File selection dialog which checks that the file exists."""
|
||||
|
||||
title = "Load File Selection Dialog"
|
||||
|
||||
def ok_command(self):
|
||||
file = self.get_selection()
|
||||
if not os.path.isfile(file):
|
||||
self.master.bell()
|
||||
else:
|
||||
self.quit(file)
|
||||
|
||||
|
||||
class SaveFileDialog(FileDialog):
|
||||
|
||||
"""File selection dialog which checks that the file may be created."""
|
||||
|
||||
title = "Save File Selection Dialog"
|
||||
|
||||
def ok_command(self):
|
||||
file = self.get_selection()
|
||||
if os.path.exists(file):
|
||||
if os.path.isdir(file):
|
||||
self.master.bell()
|
||||
return
|
||||
d = Dialog(self.top,
|
||||
title="Overwrite Existing File Question",
|
||||
text="Overwrite existing file %r?" % (file,),
|
||||
bitmap='questhead',
|
||||
default=1,
|
||||
strings=("Yes", "Cancel"))
|
||||
if d.num != 0:
|
||||
return
|
||||
else:
|
||||
head, tail = os.path.split(file)
|
||||
if not os.path.isdir(head):
|
||||
self.master.bell()
|
||||
return
|
||||
self.quit(file)
|
||||
|
||||
|
||||
def test():
|
||||
"""Simple test program."""
|
||||
root = Tk()
|
||||
root.withdraw()
|
||||
fd = LoadFileDialog(root)
|
||||
loadfile = fd.go(key="test")
|
||||
fd = SaveFileDialog(root)
|
||||
savefile = fd.go(key="test")
|
||||
print loadfile, savefile
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test()
|
|
@ -0,0 +1,41 @@
|
|||
import sys, os
|
||||
|
||||
# Delay import _tkinter until we have set TCL_LIBRARY,
|
||||
# so that Tcl_FindExecutable has a chance to locate its
|
||||
# encoding directory.
|
||||
|
||||
# Unfortunately, we cannot know the TCL_LIBRARY directory
|
||||
# if we don't know the tcl version, which we cannot find out
|
||||
# without import Tcl. Fortunately, Tcl will itself look in
|
||||
# <TCL_LIBRARY>\..\tcl<TCL_VERSION>, so anything close to
|
||||
# the real Tcl library will do.
|
||||
|
||||
prefix = os.path.join(sys.prefix,"tcl")
|
||||
if not os.path.exists(prefix):
|
||||
# devdir/../tcltk/lib
|
||||
prefix = os.path.join(sys.prefix, os.path.pardir, "tcltk", "lib")
|
||||
prefix = os.path.abspath(prefix)
|
||||
# if this does not exist, no further search is needed
|
||||
if os.path.exists(prefix):
|
||||
if not os.environ.has_key("TCL_LIBRARY"):
|
||||
for name in os.listdir(prefix):
|
||||
if name.startswith("tcl"):
|
||||
tcldir = os.path.join(prefix,name)
|
||||
if os.path.isdir(tcldir):
|
||||
os.environ["TCL_LIBRARY"] = tcldir
|
||||
# Compute TK_LIBRARY, knowing that it has the same version
|
||||
# as Tcl
|
||||
import _tkinter
|
||||
ver = str(_tkinter.TCL_VERSION)
|
||||
if not os.environ.has_key("TK_LIBRARY"):
|
||||
v = os.path.join(prefix, 'tk'+ver)
|
||||
if os.path.exists(os.path.join(v, "tclIndex")):
|
||||
os.environ['TK_LIBRARY'] = v
|
||||
# We don't know the Tix version, so we must search the entire
|
||||
# directory
|
||||
if not os.environ.has_key("TIX_LIBRARY"):
|
||||
for name in os.listdir(prefix):
|
||||
if name.startswith("tix"):
|
||||
tixdir = os.path.join(prefix,name)
|
||||
if os.path.isdir(tixdir):
|
||||
os.environ["TIX_LIBRARY"] = tixdir
|
|
@ -0,0 +1,43 @@
|
|||
# A ScrolledText widget feels like a text widget but also has a
|
||||
# vertical scroll bar on its right. (Later, options may be added to
|
||||
# add a horizontal bar as well, to make the bars disappear
|
||||
# automatically when not needed, to move them to the other side of the
|
||||
# window, etc.)
|
||||
#
|
||||
# Configuration options are passed to the Text widget.
|
||||
# A Frame widget is inserted between the master and the text, to hold
|
||||
# the Scrollbar widget.
|
||||
# Most methods calls are inherited from the Text widget; Pack methods
|
||||
# are redirected to the Frame widget however.
|
||||
|
||||
from tkinter import *
|
||||
from tkinter import _cnfmerge
|
||||
|
||||
class ScrolledText(Text):
|
||||
def __init__(self, master=None, cnf=None, **kw):
|
||||
if cnf is None:
|
||||
cnf = {}
|
||||
if kw:
|
||||
cnf = _cnfmerge((cnf, kw))
|
||||
fcnf = {}
|
||||
for k in cnf.keys():
|
||||
if type(k) == ClassType or k == 'name':
|
||||
fcnf[k] = cnf[k]
|
||||
del cnf[k]
|
||||
self.frame = Frame(master, **fcnf)
|
||||
self.vbar = Scrollbar(self.frame, name='vbar')
|
||||
self.vbar.pack(side=RIGHT, fill=Y)
|
||||
cnf['name'] = 'text'
|
||||
Text.__init__(self, self.frame, **cnf)
|
||||
self.pack(side=LEFT, fill=BOTH, expand=1)
|
||||
self['yscrollcommand'] = self.vbar.set
|
||||
self.vbar['command'] = self.yview
|
||||
|
||||
# Copy geometry methods of self.frame -- hack!
|
||||
methods = Pack.__dict__.keys()
|
||||
methods = methods + Grid.__dict__.keys()
|
||||
methods = methods + Place.__dict__.keys()
|
||||
|
||||
for m in methods:
|
||||
if m[0] != '_' and m != 'config' and m != 'configure':
|
||||
setattr(self, m, getattr(self.frame, m))
|
|
@ -0,0 +1,112 @@
|
|||
"""A simple but flexible modal dialog box."""
|
||||
|
||||
|
||||
from Tkinter import *
|
||||
|
||||
|
||||
class SimpleDialog:
|
||||
|
||||
def __init__(self, master,
|
||||
text='', buttons=[], default=None, cancel=None,
|
||||
title=None, class_=None):
|
||||
if class_:
|
||||
self.root = Toplevel(master, class_=class_)
|
||||
else:
|
||||
self.root = Toplevel(master)
|
||||
if title:
|
||||
self.root.title(title)
|
||||
self.root.iconname(title)
|
||||
self.message = Message(self.root, text=text, aspect=400)
|
||||
self.message.pack(expand=1, fill=BOTH)
|
||||
self.frame = Frame(self.root)
|
||||
self.frame.pack()
|
||||
self.num = default
|
||||
self.cancel = cancel
|
||||
self.default = default
|
||||
self.root.bind('<Return>', self.return_event)
|
||||
for num in range(len(buttons)):
|
||||
s = buttons[num]
|
||||
b = Button(self.frame, text=s,
|
||||
command=(lambda self=self, num=num: self.done(num)))
|
||||
if num == default:
|
||||
b.config(relief=RIDGE, borderwidth=8)
|
||||
b.pack(side=LEFT, fill=BOTH, expand=1)
|
||||
self.root.protocol('WM_DELETE_WINDOW', self.wm_delete_window)
|
||||
self._set_transient(master)
|
||||
|
||||
def _set_transient(self, master, relx=0.5, rely=0.3):
|
||||
widget = self.root
|
||||
widget.withdraw() # Remain invisible while we figure out the geometry
|
||||
widget.transient(master)
|
||||
widget.update_idletasks() # Actualize geometry information
|
||||
if master.winfo_ismapped():
|
||||
m_width = master.winfo_width()
|
||||
m_height = master.winfo_height()
|
||||
m_x = master.winfo_rootx()
|
||||
m_y = master.winfo_rooty()
|
||||
else:
|
||||
m_width = master.winfo_screenwidth()
|
||||
m_height = master.winfo_screenheight()
|
||||
m_x = m_y = 0
|
||||
w_width = widget.winfo_reqwidth()
|
||||
w_height = widget.winfo_reqheight()
|
||||
x = m_x + (m_width - w_width) * relx
|
||||
y = m_y + (m_height - w_height) * rely
|
||||
if x+w_width > master.winfo_screenwidth():
|
||||
x = master.winfo_screenwidth() - w_width
|
||||
elif x < 0:
|
||||
x = 0
|
||||
if y+w_height > master.winfo_screenheight():
|
||||
y = master.winfo_screenheight() - w_height
|
||||
elif y < 0:
|
||||
y = 0
|
||||
widget.geometry("+%d+%d" % (x, y))
|
||||
widget.deiconify() # Become visible at the desired location
|
||||
|
||||
def go(self):
|
||||
self.root.wait_visibility()
|
||||
self.root.grab_set()
|
||||
self.root.mainloop()
|
||||
self.root.destroy()
|
||||
return self.num
|
||||
|
||||
def return_event(self, event):
|
||||
if self.default is None:
|
||||
self.root.bell()
|
||||
else:
|
||||
self.done(self.default)
|
||||
|
||||
def wm_delete_window(self):
|
||||
if self.cancel is None:
|
||||
self.root.bell()
|
||||
else:
|
||||
self.done(self.cancel)
|
||||
|
||||
def done(self, num):
|
||||
self.num = num
|
||||
self.root.quit()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
def test():
|
||||
root = Tk()
|
||||
def doit(root=root):
|
||||
d = SimpleDialog(root,
|
||||
text="This is a test dialog. "
|
||||
"Would this have been an actual dialog, "
|
||||
"the buttons below would have been glowing "
|
||||
"in soft pink light.\n"
|
||||
"Do you believe this?",
|
||||
buttons=["Yes", "No", "Cancel"],
|
||||
default=0,
|
||||
cancel=2,
|
||||
title="Test Dialog")
|
||||
print d.go()
|
||||
t = Button(root, text='Test', command=doit)
|
||||
t.pack()
|
||||
q = Button(root, text='Quit', command=t.quit)
|
||||
q.pack()
|
||||
t.mainloop()
|
||||
|
||||
test()
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,110 @@
|
|||
# Symbolic constants for Tk
|
||||
|
||||
# Booleans
|
||||
NO=FALSE=OFF=0
|
||||
YES=TRUE=ON=1
|
||||
|
||||
# -anchor and -sticky
|
||||
N='n'
|
||||
S='s'
|
||||
W='w'
|
||||
E='e'
|
||||
NW='nw'
|
||||
SW='sw'
|
||||
NE='ne'
|
||||
SE='se'
|
||||
NS='ns'
|
||||
EW='ew'
|
||||
NSEW='nsew'
|
||||
CENTER='center'
|
||||
|
||||
# -fill
|
||||
NONE='none'
|
||||
X='x'
|
||||
Y='y'
|
||||
BOTH='both'
|
||||
|
||||
# -side
|
||||
LEFT='left'
|
||||
TOP='top'
|
||||
RIGHT='right'
|
||||
BOTTOM='bottom'
|
||||
|
||||
# -relief
|
||||
RAISED='raised'
|
||||
SUNKEN='sunken'
|
||||
FLAT='flat'
|
||||
RIDGE='ridge'
|
||||
GROOVE='groove'
|
||||
SOLID = 'solid'
|
||||
|
||||
# -orient
|
||||
HORIZONTAL='horizontal'
|
||||
VERTICAL='vertical'
|
||||
|
||||
# -tabs
|
||||
NUMERIC='numeric'
|
||||
|
||||
# -wrap
|
||||
CHAR='char'
|
||||
WORD='word'
|
||||
|
||||
# -align
|
||||
BASELINE='baseline'
|
||||
|
||||
# -bordermode
|
||||
INSIDE='inside'
|
||||
OUTSIDE='outside'
|
||||
|
||||
# Special tags, marks and insert positions
|
||||
SEL='sel'
|
||||
SEL_FIRST='sel.first'
|
||||
SEL_LAST='sel.last'
|
||||
END='end'
|
||||
INSERT='insert'
|
||||
CURRENT='current'
|
||||
ANCHOR='anchor'
|
||||
ALL='all' # e.g. Canvas.delete(ALL)
|
||||
|
||||
# Text widget and button states
|
||||
NORMAL='normal'
|
||||
DISABLED='disabled'
|
||||
ACTIVE='active'
|
||||
# Canvas state
|
||||
HIDDEN='hidden'
|
||||
|
||||
# Menu item types
|
||||
CASCADE='cascade'
|
||||
CHECKBUTTON='checkbutton'
|
||||
COMMAND='command'
|
||||
RADIOBUTTON='radiobutton'
|
||||
SEPARATOR='separator'
|
||||
|
||||
# Selection modes for list boxes
|
||||
SINGLE='single'
|
||||
BROWSE='browse'
|
||||
MULTIPLE='multiple'
|
||||
EXTENDED='extended'
|
||||
|
||||
# Activestyle for list boxes
|
||||
# NONE='none' is also valid
|
||||
DOTBOX='dotbox'
|
||||
UNDERLINE='underline'
|
||||
|
||||
# Various canvas styles
|
||||
PIESLICE='pieslice'
|
||||
CHORD='chord'
|
||||
ARC='arc'
|
||||
FIRST='first'
|
||||
LAST='last'
|
||||
BUTT='butt'
|
||||
PROJECTING='projecting'
|
||||
ROUND='round'
|
||||
BEVEL='bevel'
|
||||
MITER='miter'
|
||||
|
||||
# Arguments to xview/yview
|
||||
MOVETO='moveto'
|
||||
SCROLL='scroll'
|
||||
UNITS='units'
|
||||
PAGES='pages'
|
|
@ -0,0 +1,321 @@
|
|||
"""Drag-and-drop support for Tkinter.
|
||||
|
||||
This is very preliminary. I currently only support dnd *within* one
|
||||
application, between different windows (or within the same window).
|
||||
|
||||
I an trying to make this as generic as possible -- not dependent on
|
||||
the use of a particular widget or icon type, etc. I also hope that
|
||||
this will work with Pmw.
|
||||
|
||||
To enable an object to be dragged, you must create an event binding
|
||||
for it that starts the drag-and-drop process. Typically, you should
|
||||
bind <ButtonPress> to a callback function that you write. The function
|
||||
should call Tkdnd.dnd_start(source, event), where 'source' is the
|
||||
object to be dragged, and 'event' is the event that invoked the call
|
||||
(the argument to your callback function). Even though this is a class
|
||||
instantiation, the returned instance should not be stored -- it will
|
||||
be kept alive automatically for the duration of the drag-and-drop.
|
||||
|
||||
When a drag-and-drop is already in process for the Tk interpreter, the
|
||||
call is *ignored*; this normally averts starting multiple simultaneous
|
||||
dnd processes, e.g. because different button callbacks all
|
||||
dnd_start().
|
||||
|
||||
The object is *not* necessarily a widget -- it can be any
|
||||
application-specific object that is meaningful to potential
|
||||
drag-and-drop targets.
|
||||
|
||||
Potential drag-and-drop targets are discovered as follows. Whenever
|
||||
the mouse moves, and at the start and end of a drag-and-drop move, the
|
||||
Tk widget directly under the mouse is inspected. This is the target
|
||||
widget (not to be confused with the target object, yet to be
|
||||
determined). If there is no target widget, there is no dnd target
|
||||
object. If there is a target widget, and it has an attribute
|
||||
dnd_accept, this should be a function (or any callable object). The
|
||||
function is called as dnd_accept(source, event), where 'source' is the
|
||||
object being dragged (the object passed to dnd_start() above), and
|
||||
'event' is the most recent event object (generally a <Motion> event;
|
||||
it can also be <ButtonPress> or <ButtonRelease>). If the dnd_accept()
|
||||
function returns something other than None, this is the new dnd target
|
||||
object. If dnd_accept() returns None, or if the target widget has no
|
||||
dnd_accept attribute, the target widget's parent is considered as the
|
||||
target widget, and the search for a target object is repeated from
|
||||
there. If necessary, the search is repeated all the way up to the
|
||||
root widget. If none of the target widgets can produce a target
|
||||
object, there is no target object (the target object is None).
|
||||
|
||||
The target object thus produced, if any, is called the new target
|
||||
object. It is compared with the old target object (or None, if there
|
||||
was no old target widget). There are several cases ('source' is the
|
||||
source object, and 'event' is the most recent event object):
|
||||
|
||||
- Both the old and new target objects are None. Nothing happens.
|
||||
|
||||
- The old and new target objects are the same object. Its method
|
||||
dnd_motion(source, event) is called.
|
||||
|
||||
- The old target object was None, and the new target object is not
|
||||
None. The new target object's method dnd_enter(source, event) is
|
||||
called.
|
||||
|
||||
- The new target object is None, and the old target object is not
|
||||
None. The old target object's method dnd_leave(source, event) is
|
||||
called.
|
||||
|
||||
- The old and new target objects differ and neither is None. The old
|
||||
target object's method dnd_leave(source, event), and then the new
|
||||
target object's method dnd_enter(source, event) is called.
|
||||
|
||||
Once this is done, the new target object replaces the old one, and the
|
||||
Tk mainloop proceeds. The return value of the methods mentioned above
|
||||
is ignored; if they raise an exception, the normal exception handling
|
||||
mechanisms take over.
|
||||
|
||||
The drag-and-drop processes can end in two ways: a final target object
|
||||
is selected, or no final target object is selected. When a final
|
||||
target object is selected, it will always have been notified of the
|
||||
potential drop by a call to its dnd_enter() method, as described
|
||||
above, and possibly one or more calls to its dnd_motion() method; its
|
||||
dnd_leave() method has not been called since the last call to
|
||||
dnd_enter(). The target is notified of the drop by a call to its
|
||||
method dnd_commit(source, event).
|
||||
|
||||
If no final target object is selected, and there was an old target
|
||||
object, its dnd_leave(source, event) method is called to complete the
|
||||
dnd sequence.
|
||||
|
||||
Finally, the source object is notified that the drag-and-drop process
|
||||
is over, by a call to source.dnd_end(target, event), specifying either
|
||||
the selected target object, or None if no target object was selected.
|
||||
The source object can use this to implement the commit action; this is
|
||||
sometimes simpler than to do it in the target's dnd_commit(). The
|
||||
target's dnd_commit() method could then simply be aliased to
|
||||
dnd_leave().
|
||||
|
||||
At any time during a dnd sequence, the application can cancel the
|
||||
sequence by calling the cancel() method on the object returned by
|
||||
dnd_start(). This will call dnd_leave() if a target is currently
|
||||
active; it will never call dnd_commit().
|
||||
|
||||
"""
|
||||
|
||||
|
||||
import tkinter
|
||||
|
||||
|
||||
# The factory function
|
||||
|
||||
def dnd_start(source, event):
|
||||
h = DndHandler(source, event)
|
||||
if h.root:
|
||||
return h
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
# The class that does the work
|
||||
|
||||
class DndHandler:
|
||||
|
||||
root = None
|
||||
|
||||
def __init__(self, source, event):
|
||||
if event.num > 5:
|
||||
return
|
||||
root = event.widget._root()
|
||||
try:
|
||||
root.__dnd
|
||||
return # Don't start recursive dnd
|
||||
except AttributeError:
|
||||
root.__dnd = self
|
||||
self.root = root
|
||||
self.source = source
|
||||
self.target = None
|
||||
self.initial_button = button = event.num
|
||||
self.initial_widget = widget = event.widget
|
||||
self.release_pattern = "<B%d-ButtonRelease-%d>" % (button, button)
|
||||
self.save_cursor = widget['cursor'] or ""
|
||||
widget.bind(self.release_pattern, self.on_release)
|
||||
widget.bind("<Motion>", self.on_motion)
|
||||
widget['cursor'] = "hand2"
|
||||
|
||||
def __del__(self):
|
||||
root = self.root
|
||||
self.root = None
|
||||
if root:
|
||||
try:
|
||||
del root.__dnd
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def on_motion(self, event):
|
||||
x, y = event.x_root, event.y_root
|
||||
target_widget = self.initial_widget.winfo_containing(x, y)
|
||||
source = self.source
|
||||
new_target = None
|
||||
while target_widget:
|
||||
try:
|
||||
attr = target_widget.dnd_accept
|
||||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
new_target = attr(source, event)
|
||||
if new_target:
|
||||
break
|
||||
target_widget = target_widget.master
|
||||
old_target = self.target
|
||||
if old_target is new_target:
|
||||
if old_target:
|
||||
old_target.dnd_motion(source, event)
|
||||
else:
|
||||
if old_target:
|
||||
self.target = None
|
||||
old_target.dnd_leave(source, event)
|
||||
if new_target:
|
||||
new_target.dnd_enter(source, event)
|
||||
self.target = new_target
|
||||
|
||||
def on_release(self, event):
|
||||
self.finish(event, 1)
|
||||
|
||||
def cancel(self, event=None):
|
||||
self.finish(event, 0)
|
||||
|
||||
def finish(self, event, commit=0):
|
||||
target = self.target
|
||||
source = self.source
|
||||
widget = self.initial_widget
|
||||
root = self.root
|
||||
try:
|
||||
del root.__dnd
|
||||
self.initial_widget.unbind(self.release_pattern)
|
||||
self.initial_widget.unbind("<Motion>")
|
||||
widget['cursor'] = self.save_cursor
|
||||
self.target = self.source = self.initial_widget = self.root = None
|
||||
if target:
|
||||
if commit:
|
||||
target.dnd_commit(source, event)
|
||||
else:
|
||||
target.dnd_leave(source, event)
|
||||
finally:
|
||||
source.dnd_end(target, event)
|
||||
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# The rest is here for testing and demonstration purposes only!
|
||||
|
||||
class Icon:
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.canvas = self.label = self.id = None
|
||||
|
||||
def attach(self, canvas, x=10, y=10):
|
||||
if canvas is self.canvas:
|
||||
self.canvas.coords(self.id, x, y)
|
||||
return
|
||||
if self.canvas:
|
||||
self.detach()
|
||||
if not canvas:
|
||||
return
|
||||
label = tkinter.Label(canvas, text=self.name,
|
||||
borderwidth=2, relief="raised")
|
||||
id = canvas.create_window(x, y, window=label, anchor="nw")
|
||||
self.canvas = canvas
|
||||
self.label = label
|
||||
self.id = id
|
||||
label.bind("<ButtonPress>", self.press)
|
||||
|
||||
def detach(self):
|
||||
canvas = self.canvas
|
||||
if not canvas:
|
||||
return
|
||||
id = self.id
|
||||
label = self.label
|
||||
self.canvas = self.label = self.id = None
|
||||
canvas.delete(id)
|
||||
label.destroy()
|
||||
|
||||
def press(self, event):
|
||||
if dnd_start(self, event):
|
||||
# where the pointer is relative to the label widget:
|
||||
self.x_off = event.x
|
||||
self.y_off = event.y
|
||||
# where the widget is relative to the canvas:
|
||||
self.x_orig, self.y_orig = self.canvas.coords(self.id)
|
||||
|
||||
def move(self, event):
|
||||
x, y = self.where(self.canvas, event)
|
||||
self.canvas.coords(self.id, x, y)
|
||||
|
||||
def putback(self):
|
||||
self.canvas.coords(self.id, self.x_orig, self.y_orig)
|
||||
|
||||
def where(self, canvas, event):
|
||||
# where the corner of the canvas is relative to the screen:
|
||||
x_org = canvas.winfo_rootx()
|
||||
y_org = canvas.winfo_rooty()
|
||||
# where the pointer is relative to the canvas widget:
|
||||
x = event.x_root - x_org
|
||||
y = event.y_root - y_org
|
||||
# compensate for initial pointer offset
|
||||
return x - self.x_off, y - self.y_off
|
||||
|
||||
def dnd_end(self, target, event):
|
||||
pass
|
||||
|
||||
class Tester:
|
||||
|
||||
def __init__(self, root):
|
||||
self.top = tkinter.Toplevel(root)
|
||||
self.canvas = tkinter.Canvas(self.top, width=100, height=100)
|
||||
self.canvas.pack(fill="both", expand=1)
|
||||
self.canvas.dnd_accept = self.dnd_accept
|
||||
|
||||
def dnd_accept(self, source, event):
|
||||
return self
|
||||
|
||||
def dnd_enter(self, source, event):
|
||||
self.canvas.focus_set() # Show highlight border
|
||||
x, y = source.where(self.canvas, event)
|
||||
x1, y1, x2, y2 = source.canvas.bbox(source.id)
|
||||
dx, dy = x2-x1, y2-y1
|
||||
self.dndid = self.canvas.create_rectangle(x, y, x+dx, y+dy)
|
||||
self.dnd_motion(source, event)
|
||||
|
||||
def dnd_motion(self, source, event):
|
||||
x, y = source.where(self.canvas, event)
|
||||
x1, y1, x2, y2 = self.canvas.bbox(self.dndid)
|
||||
self.canvas.move(self.dndid, x-x1, y-y1)
|
||||
|
||||
def dnd_leave(self, source, event):
|
||||
self.top.focus_set() # Hide highlight border
|
||||
self.canvas.delete(self.dndid)
|
||||
self.dndid = None
|
||||
|
||||
def dnd_commit(self, source, event):
|
||||
self.dnd_leave(source, event)
|
||||
x, y = source.where(self.canvas, event)
|
||||
source.attach(self.canvas, x, y)
|
||||
|
||||
def test():
|
||||
root = tkinter.Tk()
|
||||
root.geometry("+1+1")
|
||||
tkinter.Button(command=root.quit, text="Quit").pack()
|
||||
t1 = Tester(root)
|
||||
t1.top.geometry("+1+60")
|
||||
t2 = Tester(root)
|
||||
t2.top.geometry("+120+60")
|
||||
t3 = Tester(root)
|
||||
t3.top.geometry("+240+60")
|
||||
i1 = Icon("ICON1")
|
||||
i2 = Icon("ICON2")
|
||||
i3 = Icon("ICON3")
|
||||
i1.attach(t1.canvas)
|
||||
i2.attach(t2.canvas)
|
||||
i3.attach(t3.canvas)
|
||||
root.mainloop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
test()
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,70 @@
|
|||
# tk common colour chooser dialogue
|
||||
#
|
||||
# this module provides an interface to the native color dialogue
|
||||
# available in Tk 4.2 and newer.
|
||||
#
|
||||
# written by Fredrik Lundh, May 1997
|
||||
#
|
||||
# fixed initialcolor handling in August 1998
|
||||
#
|
||||
|
||||
#
|
||||
# options (all have default values):
|
||||
#
|
||||
# - initialcolor: colour to mark as selected when dialog is displayed
|
||||
# (given as an RGB triplet or a Tk color string)
|
||||
#
|
||||
# - parent: which window to place the dialog on top of
|
||||
#
|
||||
# - title: dialog title
|
||||
#
|
||||
|
||||
from tkinter.commondialog import Dialog
|
||||
|
||||
|
||||
#
|
||||
# color chooser class
|
||||
|
||||
class Chooser(Dialog):
|
||||
"Ask for a color"
|
||||
|
||||
command = "tk_chooseColor"
|
||||
|
||||
def _fixoptions(self):
|
||||
try:
|
||||
# make sure initialcolor is a tk color string
|
||||
color = self.options["initialcolor"]
|
||||
if type(color) == type(()):
|
||||
# assume an RGB triplet
|
||||
self.options["initialcolor"] = "#%02x%02x%02x" % color
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
def _fixresult(self, widget, result):
|
||||
# to simplify application code, the color chooser returns
|
||||
# an RGB tuple together with the Tk color string
|
||||
if not result:
|
||||
return None, None # canceled
|
||||
r, g, b = widget.winfo_rgb(result)
|
||||
return (r/256, g/256, b/256), result
|
||||
|
||||
|
||||
#
|
||||
# convenience stuff
|
||||
|
||||
def askcolor(color = None, **options):
|
||||
"Ask for a color"
|
||||
|
||||
if color:
|
||||
options = options.copy()
|
||||
options["initialcolor"] = color
|
||||
|
||||
return Chooser(**options).show()
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# test stuff
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
print "color", askcolor()
|
|
@ -0,0 +1,60 @@
|
|||
# base class for tk common dialogues
|
||||
#
|
||||
# this module provides a base class for accessing the common
|
||||
# dialogues available in Tk 4.2 and newer. use filedialog,
|
||||
# colorchooser, and messagebox to access the individual
|
||||
# dialogs.
|
||||
#
|
||||
# written by Fredrik Lundh, May 1997
|
||||
#
|
||||
|
||||
from tkinter import *
|
||||
|
||||
class Dialog:
|
||||
|
||||
command = None
|
||||
|
||||
def __init__(self, master=None, **options):
|
||||
|
||||
# FIXME: should this be placed on the module level instead?
|
||||
if TkVersion < 4.2:
|
||||
raise TclError, "this module requires Tk 4.2 or newer"
|
||||
|
||||
self.master = master
|
||||
self.options = options
|
||||
if not master and options.get('parent'):
|
||||
self.master = options['parent']
|
||||
|
||||
def _fixoptions(self):
|
||||
pass # hook
|
||||
|
||||
def _fixresult(self, widget, result):
|
||||
return result # hook
|
||||
|
||||
def show(self, **options):
|
||||
|
||||
# update instance options
|
||||
for k, v in options.items():
|
||||
self.options[k] = v
|
||||
|
||||
self._fixoptions()
|
||||
|
||||
# we need a dummy widget to properly process the options
|
||||
# (at least as long as we use Tkinter 1.63)
|
||||
w = Frame(self.master)
|
||||
|
||||
try:
|
||||
|
||||
s = w.tk.call(self.command, *w._options(self.options))
|
||||
|
||||
s = self._fixresult(w, s)
|
||||
|
||||
finally:
|
||||
|
||||
try:
|
||||
# get rid of the widget
|
||||
w.destroy()
|
||||
except:
|
||||
pass
|
||||
|
||||
return s
|
|
@ -0,0 +1,215 @@
|
|||
#
|
||||
# Instant Python
|
||||
# $Id: tkFileDialog.py 36560 2004-07-18 06:16:08Z tim_one $
|
||||
#
|
||||
# tk common file dialogues
|
||||
#
|
||||
# this module provides interfaces to the native file dialogues
|
||||
# available in Tk 4.2 and newer, and the directory dialogue available
|
||||
# in Tk 8.3 and newer.
|
||||
#
|
||||
# written by Fredrik Lundh, May 1997.
|
||||
#
|
||||
|
||||
#
|
||||
# options (all have default values):
|
||||
#
|
||||
# - defaultextension: added to filename if not explicitly given
|
||||
#
|
||||
# - filetypes: sequence of (label, pattern) tuples. the same pattern
|
||||
# may occur with several patterns. use "*" as pattern to indicate
|
||||
# all files.
|
||||
#
|
||||
# - initialdir: initial directory. preserved by dialog instance.
|
||||
#
|
||||
# - initialfile: initial file (ignored by the open dialog). preserved
|
||||
# by dialog instance.
|
||||
#
|
||||
# - parent: which window to place the dialog on top of
|
||||
#
|
||||
# - title: dialog title
|
||||
#
|
||||
# - multiple: if true user may select more than one file
|
||||
#
|
||||
# options for the directory chooser:
|
||||
#
|
||||
# - initialdir, parent, title: see above
|
||||
#
|
||||
# - mustexist: if true, user must pick an existing directory
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
from tkCommonDialog import Dialog
|
||||
|
||||
class _Dialog(Dialog):
|
||||
|
||||
def _fixoptions(self):
|
||||
try:
|
||||
# make sure "filetypes" is a tuple
|
||||
self.options["filetypes"] = tuple(self.options["filetypes"])
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
def _fixresult(self, widget, result):
|
||||
if result:
|
||||
# keep directory and filename until next time
|
||||
import os
|
||||
# convert Tcl path objects to strings
|
||||
try:
|
||||
result = result.string
|
||||
except AttributeError:
|
||||
# it already is a string
|
||||
pass
|
||||
path, file = os.path.split(result)
|
||||
self.options["initialdir"] = path
|
||||
self.options["initialfile"] = file
|
||||
self.filename = result # compatibility
|
||||
return result
|
||||
|
||||
|
||||
#
|
||||
# file dialogs
|
||||
|
||||
class Open(_Dialog):
|
||||
"Ask for a filename to open"
|
||||
|
||||
command = "tk_getOpenFile"
|
||||
|
||||
def _fixresult(self, widget, result):
|
||||
if isinstance(result, tuple):
|
||||
# multiple results:
|
||||
result = tuple([getattr(r, "string", r) for r in result])
|
||||
if result:
|
||||
import os
|
||||
path, file = os.path.split(result[0])
|
||||
self.options["initialdir"] = path
|
||||
# don't set initialfile or filename, as we have multiple of these
|
||||
return result
|
||||
if not widget.tk.wantobjects() and "multiple" in self.options:
|
||||
# Need to split result explicitly
|
||||
return self._fixresult(widget, widget.tk.splitlist(result))
|
||||
return _Dialog._fixresult(self, widget, result)
|
||||
|
||||
class SaveAs(_Dialog):
|
||||
"Ask for a filename to save as"
|
||||
|
||||
command = "tk_getSaveFile"
|
||||
|
||||
|
||||
# the directory dialog has its own _fix routines.
|
||||
class Directory(Dialog):
|
||||
"Ask for a directory"
|
||||
|
||||
command = "tk_chooseDirectory"
|
||||
|
||||
def _fixresult(self, widget, result):
|
||||
if result:
|
||||
# convert Tcl path objects to strings
|
||||
try:
|
||||
result = result.string
|
||||
except AttributeError:
|
||||
# it already is a string
|
||||
pass
|
||||
# keep directory until next time
|
||||
self.options["initialdir"] = result
|
||||
self.directory = result # compatibility
|
||||
return result
|
||||
|
||||
#
|
||||
# convenience stuff
|
||||
|
||||
def askopenfilename(**options):
|
||||
"Ask for a filename to open"
|
||||
|
||||
return Open(**options).show()
|
||||
|
||||
def asksaveasfilename(**options):
|
||||
"Ask for a filename to save as"
|
||||
|
||||
return SaveAs(**options).show()
|
||||
|
||||
def askopenfilenames(**options):
|
||||
"""Ask for multiple filenames to open
|
||||
|
||||
Returns a list of filenames or empty list if
|
||||
cancel button selected
|
||||
"""
|
||||
options["multiple"]=1
|
||||
return Open(**options).show()
|
||||
|
||||
# FIXME: are the following perhaps a bit too convenient?
|
||||
|
||||
def askopenfile(mode = "r", **options):
|
||||
"Ask for a filename to open, and returned the opened file"
|
||||
|
||||
filename = Open(**options).show()
|
||||
if filename:
|
||||
return open(filename, mode)
|
||||
return None
|
||||
|
||||
def askopenfiles(mode = "r", **options):
|
||||
"""Ask for multiple filenames and return the open file
|
||||
objects
|
||||
|
||||
returns a list of open file objects or an empty list if
|
||||
cancel selected
|
||||
"""
|
||||
|
||||
files = askopenfilenames(**options)
|
||||
if files:
|
||||
ofiles=[]
|
||||
for filename in files:
|
||||
ofiles.append(open(filename, mode))
|
||||
files=ofiles
|
||||
return files
|
||||
|
||||
|
||||
def asksaveasfile(mode = "w", **options):
|
||||
"Ask for a filename to save as, and returned the opened file"
|
||||
|
||||
filename = SaveAs(**options).show()
|
||||
if filename:
|
||||
return open(filename, mode)
|
||||
return None
|
||||
|
||||
def askdirectory (**options):
|
||||
"Ask for a directory, and return the file name"
|
||||
return Directory(**options).show()
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# test stuff
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Since the file name may contain non-ASCII characters, we need
|
||||
# to find an encoding that likely supports the file name, and
|
||||
# displays correctly on the terminal.
|
||||
|
||||
# Start off with UTF-8
|
||||
enc = "utf-8"
|
||||
import sys
|
||||
|
||||
# See whether CODESET is defined
|
||||
try:
|
||||
import locale
|
||||
locale.setlocale(locale.LC_ALL,'')
|
||||
enc = locale.nl_langinfo(locale.CODESET)
|
||||
except (ImportError, AttributeError):
|
||||
pass
|
||||
|
||||
# dialog for openening files
|
||||
|
||||
openfilename=askopenfilename(filetypes=[("all files", "*")])
|
||||
try:
|
||||
fp=open(openfilename,"r")
|
||||
fp.close()
|
||||
except:
|
||||
print "Could not open File: "
|
||||
print sys.exc_info()[1]
|
||||
|
||||
print "open", openfilename.encode(enc)
|
||||
|
||||
# dialog for saving files
|
||||
|
||||
saveasfilename=asksaveasfilename()
|
||||
print "saveas", saveasfilename.encode(enc)
|
|
@ -0,0 +1,216 @@
|
|||
# Tkinter font wrapper
|
||||
#
|
||||
# written by Fredrik Lundh, February 1998
|
||||
#
|
||||
# FIXME: should add 'displayof' option where relevant (actual, families,
|
||||
# measure, and metrics)
|
||||
#
|
||||
|
||||
__version__ = "0.9"
|
||||
|
||||
import tkinter
|
||||
|
||||
# weight/slant
|
||||
NORMAL = "normal"
|
||||
ROMAN = "roman"
|
||||
BOLD = "bold"
|
||||
ITALIC = "italic"
|
||||
|
||||
def nametofont(name):
|
||||
"""Given the name of a tk named font, returns a Font representation.
|
||||
"""
|
||||
return Font(name=name, exists=True)
|
||||
|
||||
class Font:
|
||||
|
||||
"""Represents a named font.
|
||||
|
||||
Constructor options are:
|
||||
|
||||
font -- font specifier (name, system font, or (family, size, style)-tuple)
|
||||
name -- name to use for this font configuration (defaults to a unique name)
|
||||
exists -- does a named font by this name already exist?
|
||||
Creates a new named font if False, points to the existing font if True.
|
||||
Raises _tkinter.TclError if the assertion is false.
|
||||
|
||||
the following are ignored if font is specified:
|
||||
|
||||
family -- font 'family', e.g. Courier, Times, Helvetica
|
||||
size -- font size in points
|
||||
weight -- font thickness: NORMAL, BOLD
|
||||
slant -- font slant: ROMAN, ITALIC
|
||||
underline -- font underlining: false (0), true (1)
|
||||
overstrike -- font strikeout: false (0), true (1)
|
||||
|
||||
"""
|
||||
|
||||
def _set(self, kw):
|
||||
options = []
|
||||
for k, v in kw.items():
|
||||
options.append("-"+k)
|
||||
options.append(str(v))
|
||||
return tuple(options)
|
||||
|
||||
def _get(self, args):
|
||||
options = []
|
||||
for k in args:
|
||||
options.append("-"+k)
|
||||
return tuple(options)
|
||||
|
||||
def _mkdict(self, args):
|
||||
options = {}
|
||||
for i in range(0, len(args), 2):
|
||||
options[args[i][1:]] = args[i+1]
|
||||
return options
|
||||
|
||||
def __init__(self, root=None, font=None, name=None, exists=False, **options):
|
||||
if not root:
|
||||
root = tkinter._default_root
|
||||
if font:
|
||||
# get actual settings corresponding to the given font
|
||||
font = root.tk.splitlist(root.tk.call("font", "actual", font))
|
||||
else:
|
||||
font = self._set(options)
|
||||
if not name:
|
||||
name = "font" + str(id(self))
|
||||
self.name = name
|
||||
|
||||
if exists:
|
||||
self.delete_font = False
|
||||
# confirm font exists
|
||||
if self.name not in root.tk.call("font", "names"):
|
||||
raise tkinter._tkinter.TclError, "named font %s does not already exist" % (self.name,)
|
||||
# if font config info supplied, apply it
|
||||
if font:
|
||||
root.tk.call("font", "configure", self.name, *font)
|
||||
else:
|
||||
# create new font (raises TclError if the font exists)
|
||||
root.tk.call("font", "create", self.name, *font)
|
||||
self.delete_font = True
|
||||
# backlinks!
|
||||
self._root = root
|
||||
self._split = root.tk.splitlist
|
||||
self._call = root.tk.call
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.name == other.name and isinstance(other, Font)
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self.cget(key)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self.configure(**{key: value})
|
||||
|
||||
def __del__(self):
|
||||
try:
|
||||
if self.delete_font:
|
||||
self._call("font", "delete", self.name)
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
raise
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def copy(self):
|
||||
"Return a distinct copy of the current font"
|
||||
return Font(self._root, **self.actual())
|
||||
|
||||
def actual(self, option=None):
|
||||
"Return actual font attributes"
|
||||
if option:
|
||||
return self._call("font", "actual", self.name, "-"+option)
|
||||
else:
|
||||
return self._mkdict(
|
||||
self._split(self._call("font", "actual", self.name))
|
||||
)
|
||||
|
||||
def cget(self, option):
|
||||
"Get font attribute"
|
||||
return self._call("font", "config", self.name, "-"+option)
|
||||
|
||||
def config(self, **options):
|
||||
"Modify font attributes"
|
||||
if options:
|
||||
self._call("font", "config", self.name,
|
||||
*self._set(options))
|
||||
else:
|
||||
return self._mkdict(
|
||||
self._split(self._call("font", "config", self.name))
|
||||
)
|
||||
|
||||
configure = config
|
||||
|
||||
def measure(self, text):
|
||||
"Return text width"
|
||||
return int(self._call("font", "measure", self.name, text))
|
||||
|
||||
def metrics(self, *options):
|
||||
"""Return font metrics.
|
||||
|
||||
For best performance, create a dummy widget
|
||||
using this font before calling this method."""
|
||||
|
||||
if options:
|
||||
return int(
|
||||
self._call("font", "metrics", self.name, self._get(options))
|
||||
)
|
||||
else:
|
||||
res = self._split(self._call("font", "metrics", self.name))
|
||||
options = {}
|
||||
for i in range(0, len(res), 2):
|
||||
options[res[i][1:]] = int(res[i+1])
|
||||
return options
|
||||
|
||||
def families(root=None):
|
||||
"Get font families (as a tuple)"
|
||||
if not root:
|
||||
root = tkinter._default_root
|
||||
return root.tk.splitlist(root.tk.call("font", "families"))
|
||||
|
||||
def names(root=None):
|
||||
"Get names of defined fonts (as a tuple)"
|
||||
if not root:
|
||||
root = tkinter._default_root
|
||||
return root.tk.splitlist(root.tk.call("font", "names"))
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# test stuff
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
root = tkinter.Tk()
|
||||
|
||||
# create a font
|
||||
f = Font(family="times", size=30, weight=NORMAL)
|
||||
|
||||
print f.actual()
|
||||
print f.actual("family")
|
||||
print f.actual("weight")
|
||||
|
||||
print f.config()
|
||||
print f.cget("family")
|
||||
print f.cget("weight")
|
||||
|
||||
print names()
|
||||
|
||||
print f.measure("hello"), f.metrics("linespace")
|
||||
|
||||
print f.metrics()
|
||||
|
||||
f = Font(font=("Courier", 20, "bold"))
|
||||
print f.measure("hello"), f.metrics("linespace")
|
||||
|
||||
w = tkinter.Label(root, text="Hello, world", font=f)
|
||||
w.pack()
|
||||
|
||||
w = tkinter.Button(root, text="Quit!", command=root.destroy)
|
||||
w.pack()
|
||||
|
||||
fb = Font(font=w["font"]).copy()
|
||||
fb.config(weight=BOLD)
|
||||
|
||||
w.config(font=fb)
|
||||
|
||||
tkinter.mainloop()
|
|
@ -0,0 +1,132 @@
|
|||
# tk common message boxes
|
||||
#
|
||||
# this module provides an interface to the native message boxes
|
||||
# available in Tk 4.2 and newer.
|
||||
#
|
||||
# written by Fredrik Lundh, May 1997
|
||||
#
|
||||
|
||||
#
|
||||
# options (all have default values):
|
||||
#
|
||||
# - default: which button to make default (one of the reply codes)
|
||||
#
|
||||
# - icon: which icon to display (see below)
|
||||
#
|
||||
# - message: the message to display
|
||||
#
|
||||
# - parent: which window to place the dialog on top of
|
||||
#
|
||||
# - title: dialog title
|
||||
#
|
||||
# - type: dialog type; that is, which buttons to display (see below)
|
||||
#
|
||||
|
||||
from tkinter.commondialog import Dialog
|
||||
|
||||
#
|
||||
# constants
|
||||
|
||||
# icons
|
||||
ERROR = "error"
|
||||
INFO = "info"
|
||||
QUESTION = "question"
|
||||
WARNING = "warning"
|
||||
|
||||
# types
|
||||
ABORTRETRYIGNORE = "abortretryignore"
|
||||
OK = "ok"
|
||||
OKCANCEL = "okcancel"
|
||||
RETRYCANCEL = "retrycancel"
|
||||
YESNO = "yesno"
|
||||
YESNOCANCEL = "yesnocancel"
|
||||
|
||||
# replies
|
||||
ABORT = "abort"
|
||||
RETRY = "retry"
|
||||
IGNORE = "ignore"
|
||||
OK = "ok"
|
||||
CANCEL = "cancel"
|
||||
YES = "yes"
|
||||
NO = "no"
|
||||
|
||||
|
||||
#
|
||||
# message dialog class
|
||||
|
||||
class Message(Dialog):
|
||||
"A message box"
|
||||
|
||||
command = "tk_messageBox"
|
||||
|
||||
|
||||
#
|
||||
# convenience stuff
|
||||
|
||||
# Rename _icon and _type options to allow overriding them in options
|
||||
def _show(title=None, message=None, _icon=None, _type=None, **options):
|
||||
if _icon and "icon" not in options: options["icon"] = _icon
|
||||
if _type and "type" not in options: options["type"] = _type
|
||||
if title: options["title"] = title
|
||||
if message: options["message"] = message
|
||||
res = Message(**options).show()
|
||||
# In some Tcl installations, Tcl converts yes/no into a boolean
|
||||
if isinstance(res, bool):
|
||||
if res: return YES
|
||||
return NO
|
||||
return res
|
||||
|
||||
def showinfo(title=None, message=None, **options):
|
||||
"Show an info message"
|
||||
return _show(title, message, INFO, OK, **options)
|
||||
|
||||
def showwarning(title=None, message=None, **options):
|
||||
"Show a warning message"
|
||||
return _show(title, message, WARNING, OK, **options)
|
||||
|
||||
def showerror(title=None, message=None, **options):
|
||||
"Show an error message"
|
||||
return _show(title, message, ERROR, OK, **options)
|
||||
|
||||
def askquestion(title=None, message=None, **options):
|
||||
"Ask a question"
|
||||
return _show(title, message, QUESTION, YESNO, **options)
|
||||
|
||||
def askokcancel(title=None, message=None, **options):
|
||||
"Ask if operation should proceed; return true if the answer is ok"
|
||||
s = _show(title, message, QUESTION, OKCANCEL, **options)
|
||||
return s == OK
|
||||
|
||||
def askyesno(title=None, message=None, **options):
|
||||
"Ask a question; return true if the answer is yes"
|
||||
s = _show(title, message, QUESTION, YESNO, **options)
|
||||
return s == YES
|
||||
|
||||
def askyesnocancel(title=None, message=None, **options):
|
||||
"Ask a question; return true if the answer is yes, None if cancelled."
|
||||
s = _show(title, message, QUESTION, YESNOCANCEL, **options)
|
||||
# s might be a Tcl index object, so convert it to a string
|
||||
s = str(s)
|
||||
if s == CANCEL:
|
||||
return None
|
||||
return s == YES
|
||||
|
||||
def askretrycancel(title=None, message=None, **options):
|
||||
"Ask if operation should be retried; return true if the answer is yes"
|
||||
s = _show(title, message, WARNING, RETRYCANCEL, **options)
|
||||
return s == RETRY
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# test stuff
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
print "info", showinfo("Spam", "Egg Information")
|
||||
print "warning", showwarning("Spam", "Egg Warning")
|
||||
print "error", showerror("Spam", "Egg Alert")
|
||||
print "question", askquestion("Spam", "Question?")
|
||||
print "proceed", askokcancel("Spam", "Proceed?")
|
||||
print "yes/no", askyesno("Spam", "Got it?")
|
||||
print "yes/no/cancel", askyesnocancel("Spam", "Want it?")
|
||||
print "try again", askretrycancel("Spam", "Try again?")
|
|
@ -0,0 +1,319 @@
|
|||
#
|
||||
# An Introduction to Tkinter
|
||||
# tkSimpleDialog.py
|
||||
#
|
||||
# Copyright (c) 1997 by Fredrik Lundh
|
||||
#
|
||||
# fredrik@pythonware.com
|
||||
# http://www.pythonware.com
|
||||
#
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# dialog base class
|
||||
|
||||
'''Dialog boxes
|
||||
|
||||
This module handles dialog boxes. It contains the following
|
||||
public symbols:
|
||||
|
||||
Dialog -- a base class for dialogs
|
||||
|
||||
askinteger -- get an integer from the user
|
||||
|
||||
askfloat -- get a float from the user
|
||||
|
||||
askstring -- get a string from the user
|
||||
'''
|
||||
|
||||
from Tkinter import *
|
||||
|
||||
class Dialog(Toplevel):
|
||||
|
||||
'''Class to open dialogs.
|
||||
|
||||
This class is intended as a base class for custom dialogs
|
||||
'''
|
||||
|
||||
def __init__(self, parent, title = None):
|
||||
|
||||
'''Initialize a dialog.
|
||||
|
||||
Arguments:
|
||||
|
||||
parent -- a parent window (the application window)
|
||||
|
||||
title -- the dialog title
|
||||
'''
|
||||
Toplevel.__init__(self, parent)
|
||||
|
||||
# If the master is not viewable, don't
|
||||
# make the child transient, or else it
|
||||
# would be opened withdrawn
|
||||
if parent.winfo_viewable():
|
||||
self.transient(parent)
|
||||
|
||||
if title:
|
||||
self.title(title)
|
||||
|
||||
self.parent = parent
|
||||
|
||||
self.result = None
|
||||
|
||||
body = Frame(self)
|
||||
self.initial_focus = self.body(body)
|
||||
body.pack(padx=5, pady=5)
|
||||
|
||||
self.buttonbox()
|
||||
|
||||
self.wait_visibility() # window needs to be visible for the grab
|
||||
self.grab_set()
|
||||
|
||||
if not self.initial_focus:
|
||||
self.initial_focus = self
|
||||
|
||||
self.protocol("WM_DELETE_WINDOW", self.cancel)
|
||||
|
||||
if self.parent is not None:
|
||||
self.geometry("+%d+%d" % (parent.winfo_rootx()+50,
|
||||
parent.winfo_rooty()+50))
|
||||
|
||||
self.initial_focus.focus_set()
|
||||
|
||||
self.wait_window(self)
|
||||
|
||||
def destroy(self):
|
||||
'''Destroy the window'''
|
||||
self.initial_focus = None
|
||||
Toplevel.destroy(self)
|
||||
|
||||
#
|
||||
# construction hooks
|
||||
|
||||
def body(self, master):
|
||||
'''create dialog body.
|
||||
|
||||
return widget that should have initial focus.
|
||||
This method should be overridden, and is called
|
||||
by the __init__ method.
|
||||
'''
|
||||
pass
|
||||
|
||||
def buttonbox(self):
|
||||
'''add standard button box.
|
||||
|
||||
override if you do not want the standard buttons
|
||||
'''
|
||||
|
||||
box = Frame(self)
|
||||
|
||||
w = Button(box, text="OK", width=10, command=self.ok, default=ACTIVE)
|
||||
w.pack(side=LEFT, padx=5, pady=5)
|
||||
w = Button(box, text="Cancel", width=10, command=self.cancel)
|
||||
w.pack(side=LEFT, padx=5, pady=5)
|
||||
|
||||
self.bind("<Return>", self.ok)
|
||||
self.bind("<Escape>", self.cancel)
|
||||
|
||||
box.pack()
|
||||
|
||||
#
|
||||
# standard button semantics
|
||||
|
||||
def ok(self, event=None):
|
||||
|
||||
if not self.validate():
|
||||
self.initial_focus.focus_set() # put focus back
|
||||
return
|
||||
|
||||
self.withdraw()
|
||||
self.update_idletasks()
|
||||
|
||||
try:
|
||||
self.apply()
|
||||
finally:
|
||||
self.cancel()
|
||||
|
||||
def cancel(self, event=None):
|
||||
|
||||
# put focus back to the parent window
|
||||
if self.parent is not None:
|
||||
self.parent.focus_set()
|
||||
self.destroy()
|
||||
|
||||
#
|
||||
# command hooks
|
||||
|
||||
def validate(self):
|
||||
'''validate the data
|
||||
|
||||
This method is called automatically to validate the data before the
|
||||
dialog is destroyed. By default, it always validates OK.
|
||||
'''
|
||||
|
||||
return 1 # override
|
||||
|
||||
def apply(self):
|
||||
'''process the data
|
||||
|
||||
This method is called automatically to process the data, *after*
|
||||
the dialog is destroyed. By default, it does nothing.
|
||||
'''
|
||||
|
||||
pass # override
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# convenience dialogues
|
||||
|
||||
class _QueryDialog(Dialog):
|
||||
|
||||
def __init__(self, title, prompt,
|
||||
initialvalue=None,
|
||||
minvalue = None, maxvalue = None,
|
||||
parent = None):
|
||||
|
||||
if not parent:
|
||||
import Tkinter
|
||||
parent = Tkinter._default_root
|
||||
|
||||
self.prompt = prompt
|
||||
self.minvalue = minvalue
|
||||
self.maxvalue = maxvalue
|
||||
|
||||
self.initialvalue = initialvalue
|
||||
|
||||
Dialog.__init__(self, parent, title)
|
||||
|
||||
def destroy(self):
|
||||
self.entry = None
|
||||
Dialog.destroy(self)
|
||||
|
||||
def body(self, master):
|
||||
|
||||
w = Label(master, text=self.prompt, justify=LEFT)
|
||||
w.grid(row=0, padx=5, sticky=W)
|
||||
|
||||
self.entry = Entry(master, name="entry")
|
||||
self.entry.grid(row=1, padx=5, sticky=W+E)
|
||||
|
||||
if self.initialvalue:
|
||||
self.entry.insert(0, self.initialvalue)
|
||||
self.entry.select_range(0, END)
|
||||
|
||||
return self.entry
|
||||
|
||||
def validate(self):
|
||||
|
||||
import tkMessageBox
|
||||
|
||||
try:
|
||||
result = self.getresult()
|
||||
except ValueError:
|
||||
tkMessageBox.showwarning(
|
||||
"Illegal value",
|
||||
self.errormessage + "\nPlease try again",
|
||||
parent = self
|
||||
)
|
||||
return 0
|
||||
|
||||
if self.minvalue is not None and result < self.minvalue:
|
||||
tkMessageBox.showwarning(
|
||||
"Too small",
|
||||
"The allowed minimum value is %s. "
|
||||
"Please try again." % self.minvalue,
|
||||
parent = self
|
||||
)
|
||||
return 0
|
||||
|
||||
if self.maxvalue is not None and result > self.maxvalue:
|
||||
tkMessageBox.showwarning(
|
||||
"Too large",
|
||||
"The allowed maximum value is %s. "
|
||||
"Please try again." % self.maxvalue,
|
||||
parent = self
|
||||
)
|
||||
return 0
|
||||
|
||||
self.result = result
|
||||
|
||||
return 1
|
||||
|
||||
|
||||
class _QueryInteger(_QueryDialog):
|
||||
errormessage = "Not an integer."
|
||||
def getresult(self):
|
||||
return int(self.entry.get())
|
||||
|
||||
def askinteger(title, prompt, **kw):
|
||||
'''get an integer from the user
|
||||
|
||||
Arguments:
|
||||
|
||||
title -- the dialog title
|
||||
prompt -- the label text
|
||||
**kw -- see SimpleDialog class
|
||||
|
||||
Return value is an integer
|
||||
'''
|
||||
d = _QueryInteger(title, prompt, **kw)
|
||||
return d.result
|
||||
|
||||
class _QueryFloat(_QueryDialog):
|
||||
errormessage = "Not a floating point value."
|
||||
def getresult(self):
|
||||
return float(self.entry.get())
|
||||
|
||||
def askfloat(title, prompt, **kw):
|
||||
'''get a float from the user
|
||||
|
||||
Arguments:
|
||||
|
||||
title -- the dialog title
|
||||
prompt -- the label text
|
||||
**kw -- see SimpleDialog class
|
||||
|
||||
Return value is a float
|
||||
'''
|
||||
d = _QueryFloat(title, prompt, **kw)
|
||||
return d.result
|
||||
|
||||
class _QueryString(_QueryDialog):
|
||||
def __init__(self, *args, **kw):
|
||||
if kw.has_key("show"):
|
||||
self.__show = kw["show"]
|
||||
del kw["show"]
|
||||
else:
|
||||
self.__show = None
|
||||
_QueryDialog.__init__(self, *args, **kw)
|
||||
|
||||
def body(self, master):
|
||||
entry = _QueryDialog.body(self, master)
|
||||
if self.__show is not None:
|
||||
entry.configure(show=self.__show)
|
||||
return entry
|
||||
|
||||
def getresult(self):
|
||||
return self.entry.get()
|
||||
|
||||
def askstring(title, prompt, **kw):
|
||||
'''get a string from the user
|
||||
|
||||
Arguments:
|
||||
|
||||
title -- the dialog title
|
||||
prompt -- the label text
|
||||
**kw -- see SimpleDialog class
|
||||
|
||||
Return value is a string
|
||||
'''
|
||||
d = _QueryString(title, prompt, **kw)
|
||||
return d.result
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
root = Tk()
|
||||
root.update()
|
||||
|
||||
print askinteger("Spam", "Egg count", initialvalue=12*12)
|
||||
print askfloat("Spam", "Egg weight\n(in tons)", minvalue=1, maxvalue=100)
|
||||
print askstring("Spam", "Egg label")
|
|
@ -0,0 +1,956 @@
|
|||
# LogoMation-like turtle graphics
|
||||
|
||||
"""
|
||||
Turtle graphics is a popular way for introducing programming to
|
||||
kids. It was part of the original Logo programming language developed
|
||||
by Wally Feurzeig and Seymour Papert in 1966.
|
||||
|
||||
Imagine a robotic turtle starting at (0, 0) in the x-y plane. Give it
|
||||
the command turtle.forward(15), and it moves (on-screen!) 15 pixels in
|
||||
the direction it is facing, drawing a line as it moves. Give it the
|
||||
command turtle.left(25), and it rotates in-place 25 degrees clockwise.
|
||||
|
||||
By combining together these and similar commands, intricate shapes and
|
||||
pictures can easily be drawn.
|
||||
"""
|
||||
|
||||
from math import * # Also for export
|
||||
from time import sleep
|
||||
import tkinter
|
||||
|
||||
speeds = ['fastest', 'fast', 'normal', 'slow', 'slowest']
|
||||
|
||||
class Error(Exception):
|
||||
pass
|
||||
|
||||
class RawPen:
|
||||
|
||||
def __init__(self, canvas):
|
||||
self._canvas = canvas
|
||||
self._items = []
|
||||
self._tracing = 1
|
||||
self._arrow = 0
|
||||
self._delay = 10 # default delay for drawing
|
||||
self._angle = 0.0
|
||||
self.degrees()
|
||||
self.reset()
|
||||
|
||||
def degrees(self, fullcircle=360.0):
|
||||
""" Set angle measurement units to degrees.
|
||||
|
||||
Example:
|
||||
>>> turtle.degrees()
|
||||
"""
|
||||
# Don't try to change _angle if it is 0, because
|
||||
# _fullcircle might not be set, yet
|
||||
if self._angle:
|
||||
self._angle = (self._angle / self._fullcircle) * fullcircle
|
||||
self._fullcircle = fullcircle
|
||||
self._invradian = pi / (fullcircle * 0.5)
|
||||
|
||||
def radians(self):
|
||||
""" Set the angle measurement units to radians.
|
||||
|
||||
Example:
|
||||
>>> turtle.radians()
|
||||
"""
|
||||
self.degrees(2.0*pi)
|
||||
|
||||
def reset(self):
|
||||
""" Clear the screen, re-center the pen, and set variables to
|
||||
the default values.
|
||||
|
||||
Example:
|
||||
>>> turtle.position()
|
||||
[0.0, -22.0]
|
||||
>>> turtle.heading()
|
||||
100.0
|
||||
>>> turtle.reset()
|
||||
>>> turtle.position()
|
||||
[0.0, 0.0]
|
||||
>>> turtle.heading()
|
||||
0.0
|
||||
"""
|
||||
canvas = self._canvas
|
||||
self._canvas.update()
|
||||
width = canvas.winfo_width()
|
||||
height = canvas.winfo_height()
|
||||
if width <= 1:
|
||||
width = canvas['width']
|
||||
if height <= 1:
|
||||
height = canvas['height']
|
||||
self._origin = float(width)/2.0, float(height)/2.0
|
||||
self._position = self._origin
|
||||
self._angle = 0.0
|
||||
self._drawing = 1
|
||||
self._width = 1
|
||||
self._color = "black"
|
||||
self._filling = 0
|
||||
self._path = []
|
||||
self.clear()
|
||||
canvas._root().tkraise()
|
||||
|
||||
def clear(self):
|
||||
""" Clear the screen. The turtle does not move.
|
||||
|
||||
Example:
|
||||
>>> turtle.clear()
|
||||
"""
|
||||
self.fill(0)
|
||||
canvas = self._canvas
|
||||
items = self._items
|
||||
self._items = []
|
||||
for item in items:
|
||||
canvas.delete(item)
|
||||
self._delete_turtle()
|
||||
self._draw_turtle()
|
||||
|
||||
def tracer(self, flag):
|
||||
""" Set tracing on if flag is True, and off if it is False.
|
||||
Tracing means line are drawn more slowly, with an
|
||||
animation of an arrow along the line.
|
||||
|
||||
Example:
|
||||
>>> turtle.tracer(False) # turns off Tracer
|
||||
"""
|
||||
self._tracing = flag
|
||||
if not self._tracing:
|
||||
self._delete_turtle()
|
||||
self._draw_turtle()
|
||||
|
||||
def forward(self, distance):
|
||||
""" Go forward distance steps.
|
||||
|
||||
Example:
|
||||
>>> turtle.position()
|
||||
[0.0, 0.0]
|
||||
>>> turtle.forward(25)
|
||||
>>> turtle.position()
|
||||
[25.0, 0.0]
|
||||
>>> turtle.forward(-75)
|
||||
>>> turtle.position()
|
||||
[-50.0, 0.0]
|
||||
"""
|
||||
x0, y0 = start = self._position
|
||||
x1 = x0 + distance * cos(self._angle*self._invradian)
|
||||
y1 = y0 - distance * sin(self._angle*self._invradian)
|
||||
self._goto(x1, y1)
|
||||
|
||||
def backward(self, distance):
|
||||
""" Go backwards distance steps.
|
||||
|
||||
The turtle's heading does not change.
|
||||
|
||||
Example:
|
||||
>>> turtle.position()
|
||||
[0.0, 0.0]
|
||||
>>> turtle.backward(30)
|
||||
>>> turtle.position()
|
||||
[-30.0, 0.0]
|
||||
"""
|
||||
self.forward(-distance)
|
||||
|
||||
def left(self, angle):
|
||||
""" Turn left angle units (units are by default degrees,
|
||||
but can be set via the degrees() and radians() functions.)
|
||||
|
||||
When viewed from above, the turning happens in-place around
|
||||
its front tip.
|
||||
|
||||
Example:
|
||||
>>> turtle.heading()
|
||||
22
|
||||
>>> turtle.left(45)
|
||||
>>> turtle.heading()
|
||||
67.0
|
||||
"""
|
||||
self._angle = (self._angle + angle) % self._fullcircle
|
||||
self._draw_turtle()
|
||||
|
||||
def right(self, angle):
|
||||
""" Turn right angle units (units are by default degrees,
|
||||
but can be set via the degrees() and radians() functions.)
|
||||
|
||||
When viewed from above, the turning happens in-place around
|
||||
its front tip.
|
||||
|
||||
Example:
|
||||
>>> turtle.heading()
|
||||
22
|
||||
>>> turtle.right(45)
|
||||
>>> turtle.heading()
|
||||
337.0
|
||||
"""
|
||||
self.left(-angle)
|
||||
|
||||
def up(self):
|
||||
""" Pull the pen up -- no drawing when moving.
|
||||
|
||||
Example:
|
||||
>>> turtle.up()
|
||||
"""
|
||||
self._drawing = 0
|
||||
|
||||
def down(self):
|
||||
""" Put the pen down -- draw when moving.
|
||||
|
||||
Example:
|
||||
>>> turtle.down()
|
||||
"""
|
||||
self._drawing = 1
|
||||
|
||||
def width(self, width):
|
||||
""" Set the line to thickness to width.
|
||||
|
||||
Example:
|
||||
>>> turtle.width(10)
|
||||
"""
|
||||
self._width = float(width)
|
||||
|
||||
def color(self, *args):
|
||||
""" Set the pen color.
|
||||
|
||||
Three input formats are allowed:
|
||||
|
||||
color(s)
|
||||
s is a Tk specification string, such as "red" or "yellow"
|
||||
|
||||
color((r, g, b))
|
||||
*a tuple* of r, g, and b, which represent, an RGB color,
|
||||
and each of r, g, and b are in the range [0..1]
|
||||
|
||||
color(r, g, b)
|
||||
r, g, and b represent an RGB color, and each of r, g, and b
|
||||
are in the range [0..1]
|
||||
|
||||
Example:
|
||||
|
||||
>>> turtle.color('brown')
|
||||
>>> tup = (0.2, 0.8, 0.55)
|
||||
>>> turtle.color(tup)
|
||||
>>> turtle.color(0, .5, 0)
|
||||
"""
|
||||
if not args:
|
||||
raise Error, "no color arguments"
|
||||
if len(args) == 1:
|
||||
color = args[0]
|
||||
if type(color) == type(""):
|
||||
# Test the color first
|
||||
try:
|
||||
id = self._canvas.create_line(0, 0, 0, 0, fill=color)
|
||||
except tkinter.TclError:
|
||||
raise Error, "bad color string: %r" % (color,)
|
||||
self._set_color(color)
|
||||
return
|
||||
try:
|
||||
r, g, b = color
|
||||
except:
|
||||
raise Error, "bad color sequence: %r" % (color,)
|
||||
else:
|
||||
try:
|
||||
r, g, b = args
|
||||
except:
|
||||
raise Error, "bad color arguments: %r" % (args,)
|
||||
assert 0 <= r <= 1
|
||||
assert 0 <= g <= 1
|
||||
assert 0 <= b <= 1
|
||||
x = 255.0
|
||||
y = 0.5
|
||||
self._set_color("#%02x%02x%02x" % (int(r*x+y), int(g*x+y), int(b*x+y)))
|
||||
|
||||
def _set_color(self,color):
|
||||
self._color = color
|
||||
self._draw_turtle()
|
||||
|
||||
def write(self, text, move=False):
|
||||
""" Write text at the current pen position.
|
||||
|
||||
If move is true, the pen is moved to the bottom-right corner
|
||||
of the text. By default, move is False.
|
||||
|
||||
Example:
|
||||
>>> turtle.write('The race is on!')
|
||||
>>> turtle.write('Home = (0, 0)', True)
|
||||
"""
|
||||
x, y = self._position
|
||||
x = x-1 # correction -- calibrated for Windows
|
||||
item = self._canvas.create_text(x, y,
|
||||
text=str(text), anchor="sw",
|
||||
fill=self._color)
|
||||
self._items.append(item)
|
||||
if move:
|
||||
x0, y0, x1, y1 = self._canvas.bbox(item)
|
||||
self._goto(x1, y1)
|
||||
self._draw_turtle()
|
||||
|
||||
def fill(self, flag):
|
||||
""" Call fill(1) before drawing the shape you
|
||||
want to fill, and fill(0) when done.
|
||||
|
||||
Example:
|
||||
>>> turtle.fill(1)
|
||||
>>> turtle.forward(100)
|
||||
>>> turtle.left(90)
|
||||
>>> turtle.forward(100)
|
||||
>>> turtle.left(90)
|
||||
>>> turtle.forward(100)
|
||||
>>> turtle.left(90)
|
||||
>>> turtle.forward(100)
|
||||
>>> turtle.fill(0)
|
||||
"""
|
||||
if self._filling:
|
||||
path = tuple(self._path)
|
||||
smooth = self._filling < 0
|
||||
if len(path) > 2:
|
||||
item = self._canvas._create('polygon', path,
|
||||
{'fill': self._color,
|
||||
'smooth': smooth})
|
||||
self._items.append(item)
|
||||
self._path = []
|
||||
self._filling = flag
|
||||
if flag:
|
||||
self._path.append(self._position)
|
||||
|
||||
def begin_fill(self):
|
||||
""" Called just before drawing a shape to be filled.
|
||||
Must eventually be followed by a corresponding end_fill() call.
|
||||
Otherwise it will be ignored.
|
||||
|
||||
Example:
|
||||
>>> turtle.begin_fill()
|
||||
>>> turtle.forward(100)
|
||||
>>> turtle.left(90)
|
||||
>>> turtle.forward(100)
|
||||
>>> turtle.left(90)
|
||||
>>> turtle.forward(100)
|
||||
>>> turtle.left(90)
|
||||
>>> turtle.forward(100)
|
||||
>>> turtle.end_fill()
|
||||
"""
|
||||
self._path = [self._position]
|
||||
self._filling = 1
|
||||
|
||||
def end_fill(self):
|
||||
""" Called after drawing a shape to be filled.
|
||||
|
||||
Example:
|
||||
>>> turtle.begin_fill()
|
||||
>>> turtle.forward(100)
|
||||
>>> turtle.left(90)
|
||||
>>> turtle.forward(100)
|
||||
>>> turtle.left(90)
|
||||
>>> turtle.forward(100)
|
||||
>>> turtle.left(90)
|
||||
>>> turtle.forward(100)
|
||||
>>> turtle.end_fill()
|
||||
"""
|
||||
self.fill(0)
|
||||
|
||||
def circle(self, radius, extent = None):
|
||||
""" Draw a circle with given radius.
|
||||
The center is radius units left of the turtle; extent
|
||||
determines which part of the circle is drawn. If not given,
|
||||
the entire circle is drawn.
|
||||
|
||||
If extent is not a full circle, one endpoint of the arc is the
|
||||
current pen position. The arc is drawn in a counter clockwise
|
||||
direction if radius is positive, otherwise in a clockwise
|
||||
direction. In the process, the direction of the turtle is
|
||||
changed by the amount of the extent.
|
||||
|
||||
>>> turtle.circle(50)
|
||||
>>> turtle.circle(120, 180) # half a circle
|
||||
"""
|
||||
if extent is None:
|
||||
extent = self._fullcircle
|
||||
frac = abs(extent)/self._fullcircle
|
||||
steps = 1+int(min(11+abs(radius)/6.0, 59.0)*frac)
|
||||
w = 1.0 * extent / steps
|
||||
w2 = 0.5 * w
|
||||
l = 2.0 * radius * sin(w2*self._invradian)
|
||||
if radius < 0:
|
||||
l, w, w2 = -l, -w, -w2
|
||||
self.left(w2)
|
||||
for i in range(steps):
|
||||
self.forward(l)
|
||||
self.left(w)
|
||||
self.right(w2)
|
||||
|
||||
def heading(self):
|
||||
""" Return the turtle's current heading.
|
||||
|
||||
Example:
|
||||
>>> turtle.heading()
|
||||
67.0
|
||||
"""
|
||||
return self._angle
|
||||
|
||||
def setheading(self, angle):
|
||||
""" Set the turtle facing the given angle.
|
||||
|
||||
Here are some common directions in degrees:
|
||||
|
||||
0 - east
|
||||
90 - north
|
||||
180 - west
|
||||
270 - south
|
||||
|
||||
Example:
|
||||
>>> turtle.setheading(90)
|
||||
>>> turtle.heading()
|
||||
90
|
||||
>>> turtle.setheading(128)
|
||||
>>> turtle.heading()
|
||||
128
|
||||
"""
|
||||
self._angle = angle
|
||||
self._draw_turtle()
|
||||
|
||||
def window_width(self):
|
||||
""" Returns the width of the turtle window.
|
||||
|
||||
Example:
|
||||
>>> turtle.window_width()
|
||||
640
|
||||
"""
|
||||
width = self._canvas.winfo_width()
|
||||
if width <= 1: # the window isn't managed by a geometry manager
|
||||
width = self._canvas['width']
|
||||
return width
|
||||
|
||||
def window_height(self):
|
||||
""" Return the height of the turtle window.
|
||||
|
||||
Example:
|
||||
>>> turtle.window_height()
|
||||
768
|
||||
"""
|
||||
height = self._canvas.winfo_height()
|
||||
if height <= 1: # the window isn't managed by a geometry manager
|
||||
height = self._canvas['height']
|
||||
return height
|
||||
|
||||
def position(self):
|
||||
""" Return the current (x, y) location of the turtle.
|
||||
|
||||
Example:
|
||||
>>> turtle.position()
|
||||
[0.0, 240.0]
|
||||
"""
|
||||
x0, y0 = self._origin
|
||||
x1, y1 = self._position
|
||||
return [x1-x0, -y1+y0]
|
||||
|
||||
def setx(self, xpos):
|
||||
""" Set the turtle's x coordinate to be xpos.
|
||||
|
||||
Example:
|
||||
>>> turtle.position()
|
||||
[10.0, 240.0]
|
||||
>>> turtle.setx(10)
|
||||
>>> turtle.position()
|
||||
[10.0, 240.0]
|
||||
"""
|
||||
x0, y0 = self._origin
|
||||
x1, y1 = self._position
|
||||
self._goto(x0+xpos, y1)
|
||||
|
||||
def sety(self, ypos):
|
||||
""" Set the turtle's y coordinate to be ypos.
|
||||
|
||||
Example:
|
||||
>>> turtle.position()
|
||||
[0.0, 0.0]
|
||||
>>> turtle.sety(-22)
|
||||
>>> turtle.position()
|
||||
[0.0, -22.0]
|
||||
"""
|
||||
x0, y0 = self._origin
|
||||
x1, y1 = self._position
|
||||
self._goto(x1, y0-ypos)
|
||||
|
||||
def towards(self, *args):
|
||||
"""Returs the angle, which corresponds to the line
|
||||
from turtle-position to point (x,y).
|
||||
|
||||
Argument can be two coordinates or one pair of coordinates
|
||||
or a RawPen/Pen instance.
|
||||
|
||||
Example:
|
||||
>>> turtle.position()
|
||||
[10.0, 10.0]
|
||||
>>> turtle.towards(0,0)
|
||||
225.0
|
||||
"""
|
||||
if len(args) == 2:
|
||||
x, y = args
|
||||
else:
|
||||
arg = args[0]
|
||||
if isinstance(arg, RawPen):
|
||||
x, y = arg.position()
|
||||
else:
|
||||
x, y = arg
|
||||
x0, y0 = self.position()
|
||||
dx = x - x0
|
||||
dy = y - y0
|
||||
return (atan2(dy,dx) / self._invradian) % self._fullcircle
|
||||
|
||||
def goto(self, *args):
|
||||
""" Go to the given point.
|
||||
|
||||
If the pen is down, then a line will be drawn. The turtle's
|
||||
orientation does not change.
|
||||
|
||||
Two input formats are accepted:
|
||||
|
||||
goto(x, y)
|
||||
go to point (x, y)
|
||||
|
||||
goto((x, y))
|
||||
go to point (x, y)
|
||||
|
||||
Example:
|
||||
>>> turtle.position()
|
||||
[0.0, 0.0]
|
||||
>>> turtle.goto(50, -45)
|
||||
>>> turtle.position()
|
||||
[50.0, -45.0]
|
||||
"""
|
||||
if len(args) == 1:
|
||||
try:
|
||||
x, y = args[0]
|
||||
except:
|
||||
raise Error, "bad point argument: %r" % (args[0],)
|
||||
else:
|
||||
try:
|
||||
x, y = args
|
||||
except:
|
||||
raise Error, "bad coordinates: %r" % (args[0],)
|
||||
x0, y0 = self._origin
|
||||
self._goto(x0+x, y0-y)
|
||||
|
||||
def _goto(self, x1, y1):
|
||||
x0, y0 = self._position
|
||||
self._position = map(float, (x1, y1))
|
||||
if self._filling:
|
||||
self._path.append(self._position)
|
||||
if self._drawing:
|
||||
if self._tracing:
|
||||
dx = float(x1 - x0)
|
||||
dy = float(y1 - y0)
|
||||
distance = hypot(dx, dy)
|
||||
nhops = int(distance)
|
||||
item = self._canvas.create_line(x0, y0, x0, y0,
|
||||
width=self._width,
|
||||
capstyle="round",
|
||||
fill=self._color)
|
||||
try:
|
||||
for i in range(1, 1+nhops):
|
||||
x, y = x0 + dx*i/nhops, y0 + dy*i/nhops
|
||||
self._canvas.coords(item, x0, y0, x, y)
|
||||
self._draw_turtle((x,y))
|
||||
self._canvas.update()
|
||||
self._canvas.after(self._delay)
|
||||
# in case nhops==0
|
||||
self._canvas.coords(item, x0, y0, x1, y1)
|
||||
self._canvas.itemconfigure(item, arrow="none")
|
||||
except tkinter.TclError:
|
||||
# Probably the window was closed!
|
||||
return
|
||||
else:
|
||||
item = self._canvas.create_line(x0, y0, x1, y1,
|
||||
width=self._width,
|
||||
capstyle="round",
|
||||
fill=self._color)
|
||||
self._items.append(item)
|
||||
self._draw_turtle()
|
||||
|
||||
def speed(self, speed):
|
||||
""" Set the turtle's speed.
|
||||
|
||||
speed must one of these five strings:
|
||||
|
||||
'fastest' is a 0 ms delay
|
||||
'fast' is a 5 ms delay
|
||||
'normal' is a 10 ms delay
|
||||
'slow' is a 15 ms delay
|
||||
'slowest' is a 20 ms delay
|
||||
|
||||
Example:
|
||||
>>> turtle.speed('slow')
|
||||
"""
|
||||
try:
|
||||
speed = speed.strip().lower()
|
||||
self._delay = speeds.index(speed) * 5
|
||||
except:
|
||||
raise ValueError("%r is not a valid speed. speed must be "
|
||||
"one of %s" % (speed, speeds))
|
||||
|
||||
|
||||
def delay(self, delay):
|
||||
""" Set the drawing delay in milliseconds.
|
||||
|
||||
This is intended to allow finer control of the drawing speed
|
||||
than the speed() method
|
||||
|
||||
Example:
|
||||
>>> turtle.delay(15)
|
||||
"""
|
||||
if int(delay) < 0:
|
||||
raise ValueError("delay must be greater than or equal to 0")
|
||||
self._delay = int(delay)
|
||||
|
||||
def _draw_turtle(self, position=[]):
|
||||
if not self._tracing:
|
||||
self._canvas.update()
|
||||
return
|
||||
if position == []:
|
||||
position = self._position
|
||||
x,y = position
|
||||
distance = 8
|
||||
dx = distance * cos(self._angle*self._invradian)
|
||||
dy = distance * sin(self._angle*self._invradian)
|
||||
self._delete_turtle()
|
||||
self._arrow = self._canvas.create_line(x-dx,y+dy,x,y,
|
||||
width=self._width,
|
||||
arrow="last",
|
||||
capstyle="round",
|
||||
fill=self._color)
|
||||
self._canvas.update()
|
||||
|
||||
def _delete_turtle(self):
|
||||
if self._arrow != 0:
|
||||
self._canvas.delete(self._arrow)
|
||||
self._arrow = 0
|
||||
|
||||
|
||||
_root = None
|
||||
_canvas = None
|
||||
_pen = None
|
||||
_width = 0.50 # 50% of window width
|
||||
_height = 0.75 # 75% of window height
|
||||
_startx = None
|
||||
_starty = None
|
||||
_title = "Turtle Graphics" # default title
|
||||
|
||||
class Pen(RawPen):
|
||||
|
||||
def __init__(self):
|
||||
global _root, _canvas
|
||||
if _root is None:
|
||||
_root = tkinter.Tk()
|
||||
_root.wm_protocol("WM_DELETE_WINDOW", self._destroy)
|
||||
_root.title(_title)
|
||||
|
||||
if _canvas is None:
|
||||
# XXX Should have scroll bars
|
||||
_canvas = tkinter.Canvas(_root, background="white")
|
||||
_canvas.pack(expand=1, fill="both")
|
||||
|
||||
setup(width=_width, height= _height, startx=_startx, starty=_starty)
|
||||
|
||||
RawPen.__init__(self, _canvas)
|
||||
|
||||
def _destroy(self):
|
||||
global _root, _canvas, _pen
|
||||
root = self._canvas._root()
|
||||
if root is _root:
|
||||
_pen = None
|
||||
_root = None
|
||||
_canvas = None
|
||||
root.destroy()
|
||||
|
||||
def _getpen():
|
||||
global _pen
|
||||
if not _pen:
|
||||
_pen = Pen()
|
||||
return _pen
|
||||
|
||||
class Turtle(Pen):
|
||||
pass
|
||||
|
||||
"""For documentation of the following functions see
|
||||
the RawPen methods with the same names
|
||||
"""
|
||||
|
||||
def degrees(): _getpen().degrees()
|
||||
def radians(): _getpen().radians()
|
||||
def reset(): _getpen().reset()
|
||||
def clear(): _getpen().clear()
|
||||
def tracer(flag): _getpen().tracer(flag)
|
||||
def forward(distance): _getpen().forward(distance)
|
||||
def backward(distance): _getpen().backward(distance)
|
||||
def left(angle): _getpen().left(angle)
|
||||
def right(angle): _getpen().right(angle)
|
||||
def up(): _getpen().up()
|
||||
def down(): _getpen().down()
|
||||
def width(width): _getpen().width(width)
|
||||
def color(*args): _getpen().color(*args)
|
||||
def write(arg, move=0): _getpen().write(arg, move)
|
||||
def fill(flag): _getpen().fill(flag)
|
||||
def begin_fill(): _getpen().begin_fill()
|
||||
def end_fill(): _getpen().end_fill()
|
||||
def circle(radius, extent=None): _getpen().circle(radius, extent)
|
||||
def goto(*args): _getpen().goto(*args)
|
||||
def heading(): return _getpen().heading()
|
||||
def setheading(angle): _getpen().setheading(angle)
|
||||
def position(): return _getpen().position()
|
||||
def window_width(): return _getpen().window_width()
|
||||
def window_height(): return _getpen().window_height()
|
||||
def setx(xpos): _getpen().setx(xpos)
|
||||
def sety(ypos): _getpen().sety(ypos)
|
||||
def towards(*args): return _getpen().towards(*args)
|
||||
|
||||
def done(): _root.mainloop()
|
||||
def delay(delay): return _getpen().delay(delay)
|
||||
def speed(speed): return _getpen().speed(speed)
|
||||
|
||||
for methodname in dir(RawPen):
|
||||
""" copies RawPen docstrings to module functions of same name """
|
||||
if not methodname.startswith("_"):
|
||||
eval(methodname).__doc__ = RawPen.__dict__[methodname].__doc__
|
||||
|
||||
|
||||
def setup(**geometry):
|
||||
""" Sets the size and position of the main window.
|
||||
|
||||
Keywords are width, height, startx and starty:
|
||||
|
||||
width: either a size in pixels or a fraction of the screen.
|
||||
Default is 50% of screen.
|
||||
height: either the height in pixels or a fraction of the screen.
|
||||
Default is 75% of screen.
|
||||
|
||||
Setting either width or height to None before drawing will force
|
||||
use of default geometry as in older versions of turtle.py
|
||||
|
||||
startx: starting position in pixels from the left edge of the screen.
|
||||
Default is to center window. Setting startx to None is the default
|
||||
and centers window horizontally on screen.
|
||||
|
||||
starty: starting position in pixels from the top edge of the screen.
|
||||
Default is to center window. Setting starty to None is the default
|
||||
and centers window vertically on screen.
|
||||
|
||||
Examples:
|
||||
>>> setup (width=200, height=200, startx=0, starty=0)
|
||||
|
||||
sets window to 200x200 pixels, in upper left of screen
|
||||
|
||||
>>> setup(width=.75, height=0.5, startx=None, starty=None)
|
||||
|
||||
sets window to 75% of screen by 50% of screen and centers
|
||||
|
||||
>>> setup(width=None)
|
||||
|
||||
forces use of default geometry as in older versions of turtle.py
|
||||
"""
|
||||
|
||||
global _width, _height, _startx, _starty
|
||||
|
||||
width = geometry.get('width',_width)
|
||||
if width >= 0 or width is None:
|
||||
_width = width
|
||||
else:
|
||||
raise ValueError, "width can not be less than 0"
|
||||
|
||||
height = geometry.get('height',_height)
|
||||
if height >= 0 or height is None:
|
||||
_height = height
|
||||
else:
|
||||
raise ValueError, "height can not be less than 0"
|
||||
|
||||
startx = geometry.get('startx', _startx)
|
||||
if startx >= 0 or startx is None:
|
||||
_startx = _startx
|
||||
else:
|
||||
raise ValueError, "startx can not be less than 0"
|
||||
|
||||
starty = geometry.get('starty', _starty)
|
||||
if starty >= 0 or starty is None:
|
||||
_starty = starty
|
||||
else:
|
||||
raise ValueError, "startx can not be less than 0"
|
||||
|
||||
|
||||
if _root and _width and _height:
|
||||
if 0 < _width <= 1:
|
||||
_width = _root.winfo_screenwidth() * +width
|
||||
if 0 < _height <= 1:
|
||||
_height = _root.winfo_screenheight() * _height
|
||||
|
||||
# center window on screen
|
||||
if _startx is None:
|
||||
_startx = (_root.winfo_screenwidth() - _width) / 2
|
||||
|
||||
if _starty is None:
|
||||
_starty = (_root.winfo_screenheight() - _height) / 2
|
||||
|
||||
_root.geometry("%dx%d+%d+%d" % (_width, _height, _startx, _starty))
|
||||
|
||||
def title(title):
|
||||
"""Set the window title.
|
||||
|
||||
By default this is set to 'Turtle Graphics'
|
||||
|
||||
Example:
|
||||
>>> title("My Window")
|
||||
"""
|
||||
|
||||
global _title
|
||||
_title = title
|
||||
|
||||
def demo():
|
||||
reset()
|
||||
tracer(1)
|
||||
up()
|
||||
backward(100)
|
||||
down()
|
||||
# draw 3 squares; the last filled
|
||||
width(3)
|
||||
for i in range(3):
|
||||
if i == 2:
|
||||
fill(1)
|
||||
for j in range(4):
|
||||
forward(20)
|
||||
left(90)
|
||||
if i == 2:
|
||||
color("maroon")
|
||||
fill(0)
|
||||
up()
|
||||
forward(30)
|
||||
down()
|
||||
width(1)
|
||||
color("black")
|
||||
# move out of the way
|
||||
tracer(0)
|
||||
up()
|
||||
right(90)
|
||||
forward(100)
|
||||
right(90)
|
||||
forward(100)
|
||||
right(180)
|
||||
down()
|
||||
# some text
|
||||
write("startstart", 1)
|
||||
write("start", 1)
|
||||
color("red")
|
||||
# staircase
|
||||
for i in range(5):
|
||||
forward(20)
|
||||
left(90)
|
||||
forward(20)
|
||||
right(90)
|
||||
# filled staircase
|
||||
fill(1)
|
||||
for i in range(5):
|
||||
forward(20)
|
||||
left(90)
|
||||
forward(20)
|
||||
right(90)
|
||||
fill(0)
|
||||
tracer(1)
|
||||
# more text
|
||||
write("end")
|
||||
|
||||
def demo2():
|
||||
# exercises some new and improved features
|
||||
speed('fast')
|
||||
width(3)
|
||||
|
||||
# draw a segmented half-circle
|
||||
setheading(towards(0,0))
|
||||
x,y = position()
|
||||
r = (x**2+y**2)**.5/2.0
|
||||
right(90)
|
||||
pendown = True
|
||||
for i in range(18):
|
||||
if pendown:
|
||||
up()
|
||||
pendown = False
|
||||
else:
|
||||
down()
|
||||
pendown = True
|
||||
circle(r,10)
|
||||
sleep(2)
|
||||
|
||||
reset()
|
||||
left(90)
|
||||
|
||||
# draw a series of triangles
|
||||
l = 10
|
||||
color("green")
|
||||
width(3)
|
||||
left(180)
|
||||
sp = 5
|
||||
for i in range(-2,16):
|
||||
if i > 0:
|
||||
color(1.0-0.05*i,0,0.05*i)
|
||||
fill(1)
|
||||
color("green")
|
||||
for j in range(3):
|
||||
forward(l)
|
||||
left(120)
|
||||
l += 10
|
||||
left(15)
|
||||
if sp > 0:
|
||||
sp = sp-1
|
||||
speed(speeds[sp])
|
||||
color(0.25,0,0.75)
|
||||
fill(0)
|
||||
|
||||
# draw and fill a concave shape
|
||||
left(120)
|
||||
up()
|
||||
forward(70)
|
||||
right(30)
|
||||
down()
|
||||
color("red")
|
||||
speed("fastest")
|
||||
fill(1)
|
||||
for i in range(4):
|
||||
circle(50,90)
|
||||
right(90)
|
||||
forward(30)
|
||||
right(90)
|
||||
color("yellow")
|
||||
fill(0)
|
||||
left(90)
|
||||
up()
|
||||
forward(30)
|
||||
down();
|
||||
|
||||
color("red")
|
||||
|
||||
# create a second turtle and make the original pursue and catch it
|
||||
turtle=Turtle()
|
||||
turtle.reset()
|
||||
turtle.left(90)
|
||||
turtle.speed('normal')
|
||||
turtle.up()
|
||||
turtle.goto(280,40)
|
||||
turtle.left(24)
|
||||
turtle.down()
|
||||
turtle.speed('fast')
|
||||
turtle.color("blue")
|
||||
turtle.width(2)
|
||||
speed('fastest')
|
||||
|
||||
# turn default turtle towards new turtle object
|
||||
setheading(towards(turtle))
|
||||
while ( abs(position()[0]-turtle.position()[0])>4 or
|
||||
abs(position()[1]-turtle.position()[1])>4):
|
||||
turtle.forward(3.5)
|
||||
turtle.left(0.6)
|
||||
# turn default turtle towards new turtle object
|
||||
setheading(towards(turtle))
|
||||
forward(4)
|
||||
write("CAUGHT! ", move=True)
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
demo()
|
||||
sleep(3)
|
||||
demo2()
|
||||
done()
|
Loading…
Reference in New Issue