Andrej Mitrović
Andrej Mitrović

Reputation: 3342

How can the -default option for Tk buttons be used?

The docs for Ttk::button's -default option state that it's supposed to be used in dialog boxes, however the only dialog box function I know of is tk_dialog, which can't take buttons as arguments but only the button titles.

Does anyone have a working example of a Ttk::button with it's -default option set to active, where upon running the app and the user hitting the enter key this button is invoked? Here are my attempts:

I've attempted to lay out a button directly in the main window:

package require Tk

ttk::button .button -text "text" -default active -command "puts sometext"
bind .button <Return> { .button invoke }

pack .button

Pressing enter does nothing by default, I would first have to tab to select the button and then hitting enter will work.

I've also tried injecting buttons into tk_dialog, thinking the following might work:

package require Tk

tk_dialog .dg "Title" "Question" "" "" \
[ttk::button .button1 -text "Yes" -default disabled] \
[ttk::button .button2 -text "No" -default active]

But that just creates two buttons ".button1" and ".button2", and neither of them are the default-selected one. (this makes sense since the 4th parameter is empty). tk_dialog itself can specify the default button, but I cannot pass custom buttons to it, it only seems to accept strings for the button names.

The reason I'm asking this is because I'm writing a language binding to Tk and have to figure out which settings should be exposed. I've looked at Tkinter for Python, and it doesn't seem to wrap the -default option for buttons. Is this option ever used in Tk, and if so could you give me a proper working example? Thanks.

Upvotes: 2

Views: 1887

Answers (3)

Johannes Kuhn
Johannes Kuhn

Reputation: 15163

As patthoyts said, it is just a display option. On my machine it looks like this (with Tile)

enter image description here

The "Selected" button has currently the focus.

The script I used to create this is simple:

grid [ttk::button .b -default active -text Ok] [ttk::button .c -default disabled -text Cancel] -sticky nesw
grid [ttk::button .b -text Normal] [ttk::button .c -text Selected] -sticky nesw
grid rowconfigure . all -weight 1
grid columnconfigure . all -weight 1

Upvotes: 0

patthoyts
patthoyts

Reputation: 33193

The -default option is indeed just a display indicator. What it does depends on the theme you are using. On Windows XP and above, the default active button will be bright blue. On other themes it may have a highlighted border or different edge relied. On the old classic theme it was some huge sunken border.

To actually have something happen when you hit either Enter or Escape you must bind the <Return> and <Escape> events to the relevant buttons as mentioned already.

Don't use tk_dialog. Its really old and very useless and the style doesn't conform to any modern windowing system at all.

Here's a snippet of code I use in one app to make dialogs look sensible:

proc ::tkchat::Dialog {w args} {
    lappend args -class Dialog
    set dlg [eval [linsert $args 0 toplevel $w]]
    catch {wm attributes $w -type dialog}
    wm transient $dlg [winfo parent $dlg]
    wm group $dlg .
    return $dlg
}

This shows a few useful things to be done. First, we have a default class for the toplevel so we can allow default configuration options for dialogs. We also set the -type attribute where supported which will set the Extended Window Manager property that lets modern X window managers style the window as a dialog and not some other kind of transient window. We also then mark it as transient for the parent window -- so the window managers know that this toplevel is actually associated with the given parent or owner window (the taskbar can avoid showing it as another application).

This would be used in something like the following:

set dlg [Dialog .options]
wm withdraw $dlg
wm title $dlg "Options"

# ... create child windows and pack / grid them

set b_ok [ttk::button $dlg.ok -text OK -underline 0 -default active \
              -command [list [namespace origin EditOptionsClose] $dlg ok $pages]]
set b_cn [ttk::button $dlg.cancel -text Cancel -underline 0 \
              -command [list [namespace origin EditOptionsClose] $dlg cancel $pages]]

bind $dlg <Return> [list $b_ok invoke]
bind $dlg <Escape> [list $b_cn invoke]
bind $dlg <Alt-o>  [list focus $b_ok]
bind $dlg <Alt-c>  [list focus $b_cn]

wm protocol $dlg WM_DELETE_WINDOW [list $b_cn invoke]
wm resizable $dlg 0 0
catch {::tk::PlaceWindow $dlg widget .}
wm deiconify $dlg
tkwait visibility $dlg
focus $b_ok ; grab $dlg
tkwait variable [namespace which -variable _editoptions]
grab release $dlg
destroy $dlg

So quite a lot going on there. We create then withdraw the dialog. This improves the performance as we place lots of children into the toplevel as by being withdrawn we can defer the geometry calculations until we have to map the whole thing. Then create the buttons and everything else and get them placed onto the toplevel. I've shown just the buttons to illustrate the -default option and also the use of -underline to show the accelerator keys.

Then bindings - Return and Escape should always be handled along with the WM_DELETE_WINDOW protocol message (thats the X button provided by the window manager or Alt-F4 on Windows). This one is also then made non-resizable and we place it over the application window using the Tk library helper function tk::PlaceWindow. Finally, we map the window and set the keyboard focus onto the active widget.

The above covers pretty much everything for a well behaved dialog I think.

Upvotes: 1

Brad Lanam
Brad Lanam

Reputation: 5723

The documentation (for -default) is poor and out of date.

-default active was only a display option, and did not affect what the key did. Using the old style button, -default active will turn on the sunken relief surrounding the button in the highlight background area, as an indicator that the button was the default. It's also a little confusing in that 'active' has nothing to do with the button state, and everything to do with the highlight.

The highlight background area is no longer supported for ttk:: widgets, and though the configuration options are accepted, they are not used.

If you want the key to do something particular in your dialog, bind it to the main window, rather than to a particular button:

bind . <Return> {.button2 invoke}

Upvotes: 0

Related Questions