1 """Assorted Tk-related subroutines used in Grail."""
7 def _clear_entry_widget(event
):
10 widget
.delete(0, INSERT
)
12 def install_keybindings(root
):
13 root
.bind_class('Entry', '<Control-u>', _clear_entry_widget
)
16 def make_toplevel(master
, title
=None, class_
=None):
17 """Create a Toplevel widget.
19 This is a shortcut for a Toplevel() instantiation plus calls to
20 set the title and icon name of the widget.
25 widget
= Toplevel(master
, class_
=class_
)
27 widget
= Toplevel(master
)
30 widget
.iconname(title
)
33 def set_transient(widget
, master
, relx
=0.5, rely
=0.3, expose
=1):
34 """Make an existing toplevel widget transient for a master.
36 The widget must exist but should not yet have been placed; in
37 other words, this should be called after creating all the
38 subwidget but before letting the user interact.
41 widget
.withdraw() # Remain invisible while we figure out the geometry
42 widget
.transient(master
)
43 widget
.update_idletasks() # Actualize geometry information
44 if master
.winfo_ismapped():
45 m_width
= master
.winfo_width()
46 m_height
= master
.winfo_height()
47 m_x
= master
.winfo_rootx()
48 m_y
= master
.winfo_rooty()
50 m_width
= master
.winfo_screenwidth()
51 m_height
= master
.winfo_screenheight()
53 w_width
= widget
.winfo_reqwidth()
54 w_height
= widget
.winfo_reqheight()
55 x
= m_x
+ (m_width
- w_width
) * relx
56 y
= m_y
+ (m_height
- w_height
) * rely
57 widget
.geometry("+%d+%d" % (x
, y
))
59 widget
.deiconify() # Become visible at the desired location
63 def make_scrollbars(parent
, hbar
, vbar
, pack
=1, class_
=None, name
=None,
66 """Subroutine to create a frame with scrollbars.
68 This is used by make_text_box and similar routines.
70 Note: the caller is responsible for setting the x/y scroll command
71 properties (e.g. by calling set_scroll_commands()).
73 Return a tuple containing the hbar, the vbar, and the frame, where
74 hbar and vbar are None if not requested.
78 if name
: frame
= Frame(parent
, class_
=class_
, name
=name
)
79 else: frame
= Frame(parent
, class_
=class_
)
81 if name
: frame
= Frame(parent
, name
=name
)
82 else: frame
= Frame(parent
)
85 frame
.pack(fill
=BOTH
, expand
=1)
90 vbar
= Scrollbar(frame
, takefocus
=takefocus
)
91 vbar
.pack(fill
=Y
, side
=RIGHT
)
93 vbarframe
= Frame(frame
, borderwidth
=0)
94 vbarframe
.pack(fill
=Y
, side
=RIGHT
)
95 vbar
= Scrollbar(frame
, name
="vbar", takefocus
=takefocus
)
96 vbar
.pack(in_
=vbarframe
, expand
=1, fill
=Y
, side
=TOP
)
97 sbwidth
= vbar
.winfo_reqwidth()
98 corner
= Frame(vbarframe
, width
=sbwidth
, height
=sbwidth
)
100 corner
.pack(side
=BOTTOM
)
105 hbar
= Scrollbar(frame
, orient
=HORIZONTAL
, name
="hbar",
107 hbar
.pack(fill
=X
, side
=BOTTOM
)
111 return hbar
, vbar
, frame
114 def set_scroll_commands(widget
, hbar
, vbar
):
116 """Link a scrollable widget to its scroll bars.
118 The scroll bars may be empty.
123 widget
['yscrollcommand'] = (vbar
, 'set')
124 vbar
['command'] = (widget
, 'yview')
127 widget
['xscrollcommand'] = (hbar
, 'set')
128 hbar
['command'] = (widget
, 'xview')
134 def make_text_box(parent
, width
=0, height
=0, hbar
=0, vbar
=1,
135 fill
=BOTH
, expand
=1, wrap
=WORD
, pack
=1,
136 class_
=None, name
=None, takefocus
=None):
138 """Subroutine to create a text box.
141 - a both-ways filling and expanding frame, containing:
142 - a text widget on the left, and
143 - possibly a vertical scroll bar on the right.
144 - possibly a horizonta; scroll bar at the bottom.
146 Return the text widget and the frame widget.
149 hbar
, vbar
, frame
= make_scrollbars(parent
, hbar
, vbar
, pack
,
150 class_
=class_
, name
=name
,
153 widget
= Text(frame
, wrap
=wrap
, name
="text")
154 if width
: widget
.config(width
=width
)
155 if height
: widget
.config(height
=height
)
156 widget
.pack(expand
=expand
, fill
=fill
, side
=LEFT
)
158 set_scroll_commands(widget
, hbar
, vbar
)
163 def make_list_box(parent
, width
=0, height
=0, hbar
=0, vbar
=1,
164 fill
=BOTH
, expand
=1, pack
=1, class_
=None, name
=None,
167 """Subroutine to create a list box.
169 Like make_text_box().
171 hbar
, vbar
, frame
= make_scrollbars(parent
, hbar
, vbar
, pack
,
172 class_
=class_
, name
=name
,
175 widget
= Listbox(frame
, name
="listbox")
176 if width
: widget
.config(width
=width
)
177 if height
: widget
.config(height
=height
)
178 widget
.pack(expand
=expand
, fill
=fill
, side
=LEFT
)
180 set_scroll_commands(widget
, hbar
, vbar
)
185 def make_canvas(parent
, width
=0, height
=0, hbar
=1, vbar
=1,
186 fill
=BOTH
, expand
=1, pack
=1, class_
=None, name
=None,
189 """Subroutine to create a canvas.
191 Like make_text_box().
195 hbar
, vbar
, frame
= make_scrollbars(parent
, hbar
, vbar
, pack
,
196 class_
=class_
, name
=name
,
199 widget
= Canvas(frame
, scrollregion
=(0, 0, width
, height
), name
="canvas")
200 if width
: widget
.config(width
=width
)
201 if height
: widget
.config(height
=height
)
202 widget
.pack(expand
=expand
, fill
=fill
, side
=LEFT
)
204 set_scroll_commands(widget
, hbar
, vbar
)
210 def make_form_entry(parent
, label
, borderwidth
=None):
212 """Subroutine to create a form entry.
215 - a horizontally filling and expanding frame, containing:
216 - a label on the left, and
217 - a text entry on the right.
219 Return the entry widget and the frame widget.
223 frame
= Frame(parent
)
226 label
= Label(frame
, text
=label
)
227 label
.pack(side
=LEFT
)
229 if borderwidth
is None:
230 entry
= Entry(frame
, relief
=SUNKEN
)
232 entry
= Entry(frame
, relief
=SUNKEN
, borderwidth
=borderwidth
)
233 entry
.pack(side
=LEFT
, fill
=X
, expand
=1)
237 # This is a slightly modified version of the function above. This
238 # version does the proper alighnment of labels with their fields. It
239 # should probably eventually replace make_form_entry altogether.
241 # The one annoying bug is that the text entry field should be
242 # expandable while still aligning the colons. This doesn't work yet.
244 def make_labeled_form_entry(parent
, label
, entrywidth
=20, entryheight
=1,
245 labelwidth
=0, borderwidth
=None,
247 """Subroutine to create a form entry.
250 - a horizontally filling and expanding frame, containing:
251 - a label on the left, and
252 - a text entry on the right.
254 Return the entry widget and the frame widget.
256 if label
and label
[-1] != ':': label
= label
+ ':'
258 frame
= Frame(parent
)
260 label
= Label(frame
, text
=label
, width
=labelwidth
, anchor
=E
)
261 label
.pack(side
=LEFT
)
263 if borderwidth
is None:
264 entry
= Entry(frame
, relief
=SUNKEN
, width
=entrywidth
)
266 entry
= Entry(frame
, relief
=SUNKEN
, width
=entrywidth
,
267 borderwidth
=borderwidth
)
268 entry
.pack(side
=RIGHT
, expand
=1, fill
=X
)
271 entry
= make_text_box(frame
, entrywidth
, entryheight
, 1, 1,
273 frame
.pack(fill
=BOTH
, expand
=1)
275 return entry
, frame
, label
278 def make_double_frame(master
=None, class_
=None, name
=None, relief
=RAISED
,
280 """Create a pair of frames suitable for 'hosting' a dialog."""
282 if class_
: frame
= Frame(master
, class_
=class_
, name
=name
)
283 else: frame
= Frame(master
, name
=name
)
285 if class_
: frame
= Frame(master
, class_
=class_
)
286 else: frame
= Frame(master
)
287 top
= Frame(frame
, name
="topframe", relief
=relief
,
288 borderwidth
=borderwidth
)
289 bottom
= Frame(frame
, name
="bottomframe")
290 bottom
.pack(fill
=X
, padx
='1m', pady
='1m', side
=BOTTOM
)
291 top
.pack(expand
=1, fill
=BOTH
, padx
='1m', pady
='1m')
292 frame
.pack(expand
=1, fill
=BOTH
)
294 top
.pack(expand
=1, fill
=BOTH
, padx
='2m', pady
='2m')
296 return frame
, top
, bottom
299 def make_group_frame(master
, name
=None, label
=None, fill
=Y
,
300 side
=None, expand
=None, font
=None):
301 """Create nested frames with a border and optional label.
303 The outer frame is only used to provide the decorative border, to
304 control packing, and to host the label. The inner frame is packed
305 to fill the outer frame and should be used as the parent of all
306 sub-widgets. Only the inner frame is returned.
309 font
= font
or "-*-helvetica-medium-r-normal-*-*-100-*-*-*-*-*-*"
310 outer
= Frame(master
, borderwidth
=2, relief
=GROOVE
)
311 outer
.pack(expand
=expand
, fill
=fill
, side
=side
)
313 Label(outer
, text
=label
, font
=font
, anchor
=W
).pack(fill
=X
)
314 inner
= Frame(master
, borderwidth
='1m', name
=name
)
315 inner
.pack(expand
=1, fill
=BOTH
, in_
=outer
)
316 inner
.forget
= outer
.forget
320 def unify_button_widths(*buttons
):
321 """Make buttons passed in all have the same width.
323 Works for labels and other widgets with the 'text' option.
328 wid
= max(wid
, len(btn
["text"]))
334 """Turn a list or tuple into a single string -- recursively."""
336 if t
in (ListType
, TupleType
):
337 msg
= ' '.join(map(flatten
, msg
))
346 """Test whether a string is a Tk boolean, without error checking."""
347 if s
.lower() in ('', '0', 'no', 'off', 'false'): return 0
352 """Test make_text_box(), make_form_entry(), flatten(), boolean()."""
355 entry
, eframe
= make_form_entry(root
, 'Boolean:')
356 text
, tframe
= make_text_box(root
)
357 def enter(event
, entry
=entry
, text
=text
):
358 s
= boolean(entry
.get()) and '\nyes' or '\nno'
359 text
.insert('end', s
)
360 entry
.bind('<Return>', enter
)
361 entry
.insert(END
, flatten(sys
.argv
))
365 if __name__
== '__main__':