1 """Assorted Tk-related subroutines used in Grail."""
8 def _clear_entry_widget(event
):
11 widget
.delete(0, INSERT
)
13 def install_keybindings(root
):
14 root
.bind_class('Entry', '<Control-u>', _clear_entry_widget
)
17 def make_toplevel(master
, title
=None, class_
=None):
18 """Create a Toplevel widget.
20 This is a shortcut for a Toplevel() instantiation plus calls to
21 set the title and icon name of the widget.
26 widget
= Toplevel(master
, class_
=class_
)
28 widget
= Toplevel(master
)
31 widget
.iconname(title
)
34 def set_transient(widget
, master
, relx
=0.5, rely
=0.3, expose
=1):
35 """Make an existing toplevel widget transient for a master.
37 The widget must exist but should not yet have been placed; in
38 other words, this should be called after creating all the
39 subwidget but before letting the user interact.
42 widget
.withdraw() # Remain invisible while we figure out the geometry
43 widget
.transient(master
)
44 widget
.update_idletasks() # Actualize geometry information
45 if master
.winfo_ismapped():
46 m_width
= master
.winfo_width()
47 m_height
= master
.winfo_height()
48 m_x
= master
.winfo_rootx()
49 m_y
= master
.winfo_rooty()
51 m_width
= master
.winfo_screenwidth()
52 m_height
= master
.winfo_screenheight()
54 w_width
= widget
.winfo_reqwidth()
55 w_height
= widget
.winfo_reqheight()
56 x
= m_x
+ (m_width
- w_width
) * relx
57 y
= m_y
+ (m_height
- w_height
) * rely
58 widget
.geometry("+%d+%d" % (x
, y
))
60 widget
.deiconify() # Become visible at the desired location
64 def make_scrollbars(parent
, hbar
, vbar
, pack
=1, class_
=None, name
=None,
67 """Subroutine to create a frame with scrollbars.
69 This is used by make_text_box and similar routines.
71 Note: the caller is responsible for setting the x/y scroll command
72 properties (e.g. by calling set_scroll_commands()).
74 Return a tuple containing the hbar, the vbar, and the frame, where
75 hbar and vbar are None if not requested.
79 if name
: frame
= Frame(parent
, class_
=class_
, name
=name
)
80 else: frame
= Frame(parent
, class_
=class_
)
82 if name
: frame
= Frame(parent
, name
=name
)
83 else: frame
= Frame(parent
)
86 frame
.pack(fill
=BOTH
, expand
=1)
91 vbar
= Scrollbar(frame
, takefocus
=takefocus
)
92 vbar
.pack(fill
=Y
, side
=RIGHT
)
94 vbarframe
= Frame(frame
, borderwidth
=0)
95 vbarframe
.pack(fill
=Y
, side
=RIGHT
)
96 vbar
= Scrollbar(frame
, name
="vbar", takefocus
=takefocus
)
97 vbar
.pack(in_
=vbarframe
, expand
=1, fill
=Y
, side
=TOP
)
98 sbwidth
= vbar
.winfo_reqwidth()
99 corner
= Frame(vbarframe
, width
=sbwidth
, height
=sbwidth
)
101 corner
.pack(side
=BOTTOM
)
106 hbar
= Scrollbar(frame
, orient
=HORIZONTAL
, name
="hbar",
108 hbar
.pack(fill
=X
, side
=BOTTOM
)
112 return hbar
, vbar
, frame
115 def set_scroll_commands(widget
, hbar
, vbar
):
117 """Link a scrollable widget to its scroll bars.
119 The scroll bars may be empty.
124 widget
['yscrollcommand'] = (vbar
, 'set')
125 vbar
['command'] = (widget
, 'yview')
128 widget
['xscrollcommand'] = (hbar
, 'set')
129 hbar
['command'] = (widget
, 'xview')
135 def make_text_box(parent
, width
=0, height
=0, hbar
=0, vbar
=1,
136 fill
=BOTH
, expand
=1, wrap
=WORD
, pack
=1,
137 class_
=None, name
=None, takefocus
=None):
139 """Subroutine to create a text box.
142 - a both-ways filling and expanding frame, containing:
143 - a text widget on the left, and
144 - possibly a vertical scroll bar on the right.
145 - possibly a horizonta; scroll bar at the bottom.
147 Return the text widget and the frame widget.
150 hbar
, vbar
, frame
= make_scrollbars(parent
, hbar
, vbar
, pack
,
151 class_
=class_
, name
=name
,
154 widget
= Text(frame
, wrap
=wrap
, name
="text")
155 if width
: widget
.config(width
=width
)
156 if height
: widget
.config(height
=height
)
157 widget
.pack(expand
=expand
, fill
=fill
, side
=LEFT
)
159 set_scroll_commands(widget
, hbar
, vbar
)
164 def make_list_box(parent
, width
=0, height
=0, hbar
=0, vbar
=1,
165 fill
=BOTH
, expand
=1, pack
=1, class_
=None, name
=None,
168 """Subroutine to create a list box.
170 Like make_text_box().
172 hbar
, vbar
, frame
= make_scrollbars(parent
, hbar
, vbar
, pack
,
173 class_
=class_
, name
=name
,
176 widget
= Listbox(frame
, name
="listbox")
177 if width
: widget
.config(width
=width
)
178 if height
: widget
.config(height
=height
)
179 widget
.pack(expand
=expand
, fill
=fill
, side
=LEFT
)
181 set_scroll_commands(widget
, hbar
, vbar
)
186 def make_canvas(parent
, width
=0, height
=0, hbar
=1, vbar
=1,
187 fill
=BOTH
, expand
=1, pack
=1, class_
=None, name
=None,
190 """Subroutine to create a canvas.
192 Like make_text_box().
196 hbar
, vbar
, frame
= make_scrollbars(parent
, hbar
, vbar
, pack
,
197 class_
=class_
, name
=name
,
200 widget
= Canvas(frame
, scrollregion
=(0, 0, width
, height
), name
="canvas")
201 if width
: widget
.config(width
=width
)
202 if height
: widget
.config(height
=height
)
203 widget
.pack(expand
=expand
, fill
=fill
, side
=LEFT
)
205 set_scroll_commands(widget
, hbar
, vbar
)
211 def make_form_entry(parent
, label
, borderwidth
=None):
213 """Subroutine to create a form entry.
216 - a horizontally filling and expanding frame, containing:
217 - a label on the left, and
218 - a text entry on the right.
220 Return the entry widget and the frame widget.
224 frame
= Frame(parent
)
227 label
= Label(frame
, text
=label
)
228 label
.pack(side
=LEFT
)
230 if borderwidth
is None:
231 entry
= Entry(frame
, relief
=SUNKEN
)
233 entry
= Entry(frame
, relief
=SUNKEN
, borderwidth
=borderwidth
)
234 entry
.pack(side
=LEFT
, fill
=X
, expand
=1)
238 # This is a slightly modified version of the function above. This
239 # version does the proper alighnment of labels with their fields. It
240 # should probably eventually replace make_form_entry altogether.
242 # The one annoying bug is that the text entry field should be
243 # expandable while still aligning the colons. This doesn't work yet.
245 def make_labeled_form_entry(parent
, label
, entrywidth
=20, entryheight
=1,
246 labelwidth
=0, borderwidth
=None,
248 """Subroutine to create a form entry.
251 - a horizontally filling and expanding frame, containing:
252 - a label on the left, and
253 - a text entry on the right.
255 Return the entry widget and the frame widget.
257 if label
and label
[-1] != ':': label
= label
+ ':'
259 frame
= Frame(parent
)
261 label
= Label(frame
, text
=label
, width
=labelwidth
, anchor
=E
)
262 label
.pack(side
=LEFT
)
264 if borderwidth
is None:
265 entry
= Entry(frame
, relief
=SUNKEN
, width
=entrywidth
)
267 entry
= Entry(frame
, relief
=SUNKEN
, width
=entrywidth
,
268 borderwidth
=borderwidth
)
269 entry
.pack(side
=RIGHT
, expand
=1, fill
=X
)
272 entry
= make_text_box(frame
, entrywidth
, entryheight
, 1, 1,
274 frame
.pack(fill
=BOTH
, expand
=1)
276 return entry
, frame
, label
279 def make_double_frame(master
=None, class_
=None, name
=None, relief
=RAISED
,
281 """Create a pair of frames suitable for 'hosting' a dialog."""
283 if class_
: frame
= Frame(master
, class_
=class_
, name
=name
)
284 else: frame
= Frame(master
, name
=name
)
286 if class_
: frame
= Frame(master
, class_
=class_
)
287 else: frame
= Frame(master
)
288 top
= Frame(frame
, name
="topframe", relief
=relief
,
289 borderwidth
=borderwidth
)
290 bottom
= Frame(frame
, name
="bottomframe")
291 bottom
.pack(fill
=X
, padx
='1m', pady
='1m', side
=BOTTOM
)
292 top
.pack(expand
=1, fill
=BOTH
, padx
='1m', pady
='1m')
293 frame
.pack(expand
=1, fill
=BOTH
)
295 top
.pack(expand
=1, fill
=BOTH
, padx
='2m', pady
='2m')
297 return frame
, top
, bottom
300 def make_group_frame(master
, name
=None, label
=None, fill
=Y
,
301 side
=None, expand
=None, font
=None):
302 """Create nested frames with a border and optional label.
304 The outer frame is only used to provide the decorative border, to
305 control packing, and to host the label. The inner frame is packed
306 to fill the outer frame and should be used as the parent of all
307 sub-widgets. Only the inner frame is returned.
310 font
= font
or "-*-helvetica-medium-r-normal-*-*-100-*-*-*-*-*-*"
311 outer
= Frame(master
, borderwidth
=2, relief
=GROOVE
)
312 outer
.pack(expand
=expand
, fill
=fill
, side
=side
)
314 Label(outer
, text
=label
, font
=font
, anchor
=W
).pack(fill
=X
)
315 inner
= Frame(master
, borderwidth
='1m', name
=name
)
316 inner
.pack(expand
=1, fill
=BOTH
, in_
=outer
)
317 inner
.forget
= outer
.forget
321 def unify_button_widths(*buttons
):
322 """Make buttons passed in all have the same width.
324 Works for labels and other widgets with the 'text' option.
329 wid
= max(wid
, len(btn
["text"]))
335 """Turn a list or tuple into a single string -- recursively."""
337 if t
in (ListType
, TupleType
):
338 msg
= string
.join(map(flatten
, msg
))
347 """Test whether a string is a Tk boolean, without error checking."""
348 if string
.lower(s
) in ('', '0', 'no', 'off', 'false'): return 0
353 """Test make_text_box(), make_form_entry(), flatten(), boolean()."""
356 entry
, eframe
= make_form_entry(root
, 'Boolean:')
357 text
, tframe
= make_text_box(root
)
358 def enter(event
, entry
=entry
, text
=text
):
359 s
= boolean(entry
.get()) and '\nyes' or '\nno'
360 text
.insert('end', s
)
361 entry
.bind('<Return>', enter
)
362 entry
.insert(END
, flatten(sys
.argv
))
366 if __name__
== '__main__':