summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Tools/webchecker/tktools.py367
1 files changed, 367 insertions, 0 deletions
diff --git a/Tools/webchecker/tktools.py b/Tools/webchecker/tktools.py
new file mode 100644
index 0000000..6734530
--- /dev/null
+++ b/Tools/webchecker/tktools.py
@@ -0,0 +1,367 @@
+"""Assorted Tk-related subroutines used in Grail."""
+
+
+import string
+from types import *
+from Tkinter import *
+
+def _clear_entry_widget(event):
+ try:
+ widget = event.widget
+ widget.delete(0, INSERT)
+ except: pass
+def install_keybindings(root):
+ root.bind_class('Entry', '<Control-u>', _clear_entry_widget)
+
+
+def make_toplevel(master, title=None, class_=None):
+ """Create a Toplevel widget.
+
+ This is a shortcut for a Toplevel() instantiation plus calls to
+ set the title and icon name of the widget.
+
+ """
+
+ if class_:
+ widget = Toplevel(master, class_=class_)
+ else:
+ widget = Toplevel(master)
+ if title:
+ widget.title(title)
+ widget.iconname(title)
+ return widget
+
+def set_transient(widget, master, relx=0.5, rely=0.3, expose=1):
+ """Make an existing toplevel widget transient for a master.
+
+ The widget must exist but should not yet have been placed; in
+ other words, this should be called after creating all the
+ subwidget but before letting the user interact.
+ """
+
+ 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
+ widget.geometry("+%d+%d" % (x, y))
+ if expose:
+ widget.deiconify() # Become visible at the desired location
+ return widget
+
+
+def make_scrollbars(parent, hbar, vbar, pack=1, class_=None, name=None,
+ takefocus=0):
+
+ """Subroutine to create a frame with scrollbars.
+
+ This is used by make_text_box and similar routines.
+
+ Note: the caller is responsible for setting the x/y scroll command
+ properties (e.g. by calling set_scroll_commands()).
+
+ Return a tuple containing the hbar, the vbar, and the frame, where
+ hbar and vbar are None if not requested.
+
+ """
+ if class_:
+ if name: frame = Frame(parent, class_=class_, name=name)
+ else: frame = Frame(parent, class_=class_)
+ else:
+ if name: frame = Frame(parent, name=name)
+ else: frame = Frame(parent)
+
+ if pack:
+ frame.pack(fill=BOTH, expand=1)
+
+ corner = None
+ if vbar:
+ if not hbar:
+ vbar = Scrollbar(frame, takefocus=takefocus)
+ vbar.pack(fill=Y, side=RIGHT)
+ else:
+ vbarframe = Frame(frame, borderwidth=0)
+ vbarframe.pack(fill=Y, side=RIGHT)
+ vbar = Scrollbar(frame, name="vbar", takefocus=takefocus)
+ vbar.pack(in_=vbarframe, expand=1, fill=Y, side=TOP)
+ sbwidth = vbar.winfo_reqwidth()
+ corner = Frame(vbarframe, width=sbwidth, height=sbwidth)
+ corner.propagate(0)
+ corner.pack(side=BOTTOM)
+ else:
+ vbar = None
+
+ if hbar:
+ hbar = Scrollbar(frame, orient=HORIZONTAL, name="hbar",
+ takefocus=takefocus)
+ hbar.pack(fill=X, side=BOTTOM)
+ else:
+ hbar = None
+
+ return hbar, vbar, frame
+
+
+def set_scroll_commands(widget, hbar, vbar):
+
+ """Link a scrollable widget to its scroll bars.
+
+ The scroll bars may be empty.
+
+ """
+
+ if vbar:
+ widget['yscrollcommand'] = (vbar, 'set')
+ vbar['command'] = (widget, 'yview')
+
+ if hbar:
+ widget['xscrollcommand'] = (hbar, 'set')
+ hbar['command'] = (widget, 'xview')
+
+ widget.vbar = vbar
+ widget.hbar = hbar
+
+
+def make_text_box(parent, width=0, height=0, hbar=0, vbar=1,
+ fill=BOTH, expand=1, wrap=WORD, pack=1,
+ class_=None, name=None, takefocus=None):
+
+ """Subroutine to create a text box.
+
+ Create:
+ - a both-ways filling and expanding frame, containing:
+ - a text widget on the left, and
+ - possibly a vertical scroll bar on the right.
+ - possibly a horizonta; scroll bar at the bottom.
+
+ Return the text widget and the frame widget.
+
+ """
+ hbar, vbar, frame = make_scrollbars(parent, hbar, vbar, pack,
+ class_=class_, name=name,
+ takefocus=takefocus)
+
+ widget = Text(frame, wrap=wrap, name="text")
+ if width: widget.config(width=width)
+ if height: widget.config(height=height)
+ widget.pack(expand=expand, fill=fill, side=LEFT)
+
+ set_scroll_commands(widget, hbar, vbar)
+
+ return widget, frame
+
+
+def make_list_box(parent, width=0, height=0, hbar=0, vbar=1,
+ fill=BOTH, expand=1, pack=1, class_=None, name=None,
+ takefocus=None):
+
+ """Subroutine to create a list box.
+
+ Like make_text_box().
+ """
+ hbar, vbar, frame = make_scrollbars(parent, hbar, vbar, pack,
+ class_=class_, name=name,
+ takefocus=takefocus)
+
+ widget = Listbox(frame, name="listbox")
+ if width: widget.config(width=width)
+ if height: widget.config(height=height)
+ widget.pack(expand=expand, fill=fill, side=LEFT)
+
+ set_scroll_commands(widget, hbar, vbar)
+
+ return widget, frame
+
+
+def make_canvas(parent, width=0, height=0, hbar=1, vbar=1,
+ fill=BOTH, expand=1, pack=1, class_=None, name=None,
+ takefocus=None):
+
+ """Subroutine to create a canvas.
+
+ Like make_text_box().
+
+ """
+
+ hbar, vbar, frame = make_scrollbars(parent, hbar, vbar, pack,
+ class_=class_, name=name,
+ takefocus=takefocus)
+
+ widget = Canvas(frame, scrollregion=(0, 0, width, height), name="canvas")
+ if width: widget.config(width=width)
+ if height: widget.config(height=height)
+ widget.pack(expand=expand, fill=fill, side=LEFT)
+
+ set_scroll_commands(widget, hbar, vbar)
+
+ return widget, frame
+
+
+
+def make_form_entry(parent, label, borderwidth=None):
+
+ """Subroutine to create a form entry.
+
+ Create:
+ - a horizontally filling and expanding frame, containing:
+ - a label on the left, and
+ - a text entry on the right.
+
+ Return the entry widget and the frame widget.
+
+ """
+
+ frame = Frame(parent)
+ frame.pack(fill=X)
+
+ label = Label(frame, text=label)
+ label.pack(side=LEFT)
+
+ if borderwidth is None:
+ entry = Entry(frame, relief=SUNKEN)
+ else:
+ entry = Entry(frame, relief=SUNKEN, borderwidth=borderwidth)
+ entry.pack(side=LEFT, fill=X, expand=1)
+
+ return entry, frame
+
+# This is a slightly modified version of the function above. This
+# version does the proper alighnment of labels with their fields. It
+# should probably eventually replace make_form_entry altogether.
+#
+# The one annoying bug is that the text entry field should be
+# expandable while still aligning the colons. This doesn't work yet.
+#
+def make_labeled_form_entry(parent, label, entrywidth=20, entryheight=1,
+ labelwidth=0, borderwidth=None,
+ takefocus=None):
+ """Subroutine to create a form entry.
+
+ Create:
+ - a horizontally filling and expanding frame, containing:
+ - a label on the left, and
+ - a text entry on the right.
+
+ Return the entry widget and the frame widget.
+ """
+ if label and label[-1] != ':': label = label + ':'
+
+ frame = Frame(parent)
+
+ label = Label(frame, text=label, width=labelwidth, anchor=E)
+ label.pack(side=LEFT)
+ if entryheight == 1:
+ if borderwidth is None:
+ entry = Entry(frame, relief=SUNKEN, width=entrywidth)
+ else:
+ entry = Entry(frame, relief=SUNKEN, width=entrywidth,
+ borderwidth=borderwidth)
+ entry.pack(side=RIGHT, expand=1, fill=X)
+ frame.pack(fill=X)
+ else:
+ entry = make_text_box(frame, entrywidth, entryheight, 1, 1,
+ takefocus=takefocus)
+ frame.pack(fill=BOTH, expand=1)
+
+ return entry, frame, label
+
+
+def make_double_frame(master=None, class_=None, name=None, relief=RAISED,
+ borderwidth=1):
+ """Create a pair of frames suitable for 'hosting' a dialog."""
+ if name:
+ if class_: frame = Frame(master, class_=class_, name=name)
+ else: frame = Frame(master, name=name)
+ else:
+ if class_: frame = Frame(master, class_=class_)
+ else: frame = Frame(master)
+ top = Frame(frame, name="topframe", relief=relief,
+ borderwidth=borderwidth)
+ bottom = Frame(frame, name="bottomframe")
+ bottom.pack(fill=X, padx='1m', pady='1m', side=BOTTOM)
+ top.pack(expand=1, fill=BOTH, padx='1m', pady='1m')
+ frame.pack(expand=1, fill=BOTH)
+ top = Frame(top)
+ top.pack(expand=1, fill=BOTH, padx='2m', pady='2m')
+
+ return frame, top, bottom
+
+
+def make_group_frame(master, name=None, label=None, fill=Y,
+ side=None, expand=None, font=None):
+ """Create nested frames with a border and optional label.
+
+ The outer frame is only used to provide the decorative border, to
+ control packing, and to host the label. The inner frame is packed
+ to fill the outer frame and should be used as the parent of all
+ sub-widgets. Only the inner frame is returned.
+
+ """
+ font = font or "-*-helvetica-medium-r-normal-*-*-100-*-*-*-*-*-*"
+ outer = Frame(master, borderwidth=2, relief=GROOVE)
+ outer.pack(expand=expand, fill=fill, side=side)
+ if label:
+ Label(outer, text=label, font=font, anchor=W).pack(fill=X)
+ inner = Frame(master, borderwidth='1m', name=name)
+ inner.pack(expand=1, fill=BOTH, in_=outer)
+ inner.forget = outer.forget
+ return inner
+
+
+def unify_button_widths(*buttons):
+ """Make buttons passed in all have the same width.
+
+ Works for labels and other widgets with the 'text' option.
+
+ """
+ wid = 0
+ for btn in buttons:
+ wid = max(wid, len(btn["text"]))
+ for btn in buttons:
+ btn["width"] = wid
+
+
+def flatten(msg):
+ """Turn a list or tuple into a single string -- recursively."""
+ t = type(msg)
+ if t in (ListType, TupleType):
+ msg = string.join(map(flatten, msg))
+ elif t is ClassType:
+ msg = msg.__name__
+ else:
+ msg = str(msg)
+ return msg
+
+
+def boolean(s):
+ """Test whether a string is a Tk boolean, without error checking."""
+ if string.lower(s) in ('', '0', 'no', 'off', 'false'): return 0
+ else: return 1
+
+
+def test():
+ """Test make_text_box(), make_form_entry(), flatten(), boolean()."""
+ import sys
+ root = Tk()
+ entry, eframe = make_form_entry(root, 'Boolean:')
+ text, tframe = make_text_box(root)
+ def enter(event, entry=entry, text=text):
+ s = boolean(entry.get()) and '\nyes' or '\nno'
+ text.insert('end', s)
+ entry.bind('<Return>', enter)
+ entry.insert(END, flatten(sys.argv))
+ root.mainloop()
+
+
+if __name__ == '__main__':
+ test()