3

I am using Python 3.3 on Windows 7. I have a tkinter application where one Button fires up a tkinter.simpledialog.Dialog. Like this (this is a complete, runnable example):

import tkinter
import tkinter.simpledialog

class Mainframe(tkinter.Frame):
    def __init__(self, parent):
        super(Mainframe, self).__init__(parent)
        self.parent = parent
        self.button = tkinter.Button(self, text="Open Dialog")
        open_dialog_op = lambda ev: self.open_dialog(ev)
        self.button.bind("<Button-1>", open_dialog_op)
        self.button.bind("<Return>", open_dialog_op)
        self.button.pack(side=tkinter.LEFT)

    def open_dialog(self, event):
        dialog = tkinter.simpledialog.Dialog(self.parent, "My Dialog")
        self.button.config(relief=tkinter.RAISED)  # does not help!

root = tkinter.Tk()
Mainframe(root).pack()
root.mainloop()

The behavior:

  • if you focus the "Open Dialog" Button and type RETURN, all works nicely
  • if you mouse-click the Button, the dialog appears all fine, but
  • when the dialog closes, the "Open Dialog" Button is shown in its depressed (tkinter.SUNKEN, if I'm not mistaken?) state.
  • (Interestingly, while the dialog is open, the Button is shown normally. The depressed look starts only once the dialog closes.)
  • I have tried to repair the problem by simply calling button.config(relief=tkinter.RAISED), but that does not appear to do anything at all in this case.

(Actually, my full application starts showing the button as depressed right after it's clicked, not only once the dialog closes again. I find this a lot more logical: The simpledialog local event loop grabs all events because the simpledialog is modal; this could include the <ButtonRelease-1> mouse event at the Button?)

Questions:

  1. Why does this happen?
  2. Why might the behavior in my full application differ?
  3. How can I avoid or repair both?
Lutz Prechelt
  • 36,608
  • 11
  • 63
  • 88

2 Answers2

3

It's happening because you are partially overriding the default bindings which do the Right Thing.

If you want a button to execute a function on a button activation the proper way to do this is to add a command option to the button. The reason I use "activation" instead of "press" is — as your code shows — there is more than one way to activate a button in tk: button-presses, return-presses, accelerator key presses, etc.

The code you wrote does not replace the rather large set of default bindings. The answer by iCodez does correctly fix the most obvious defects, but letting the default bindings stand and using command= will work for the cases that you haven't tested (e.g. keyboard-only operation).

msw
  • 42,753
  • 9
  • 87
  • 112
0

I have a simple, workaround solution for the third question. Actually, you yourself had the answer in your post. Change this line:

self.button.bind("<Button-1>", open_dialog_op)

to this:

self.button.bind("<ButtonRelease-1>", open_dialog_op)

Now, open_dialog_op is bound to the release of the mouse click. Meaning, the simpledialog will only open once the button comes back up (which is what its behavior should be).

However, I'd also like to make a suggestion. Why not use tkinter.messagebox.askokcancel for this? It is the same as the Windows' system prompts. See below:

tkinter.simpledialog.Dialog dialog:

enter image description here

tkinter.messagebox.askokcancel dialog:

enter image description here

  • The answer to the first question of why this happens, is because the raising of the button happens on a button release, and the release isn't processed because the code is overriding the default behavior, preventing the release from being processed until the dialog is dismissed. The proper solution isn't to add a binding on ``, but to instead use the built-in `command` attribute, which handles all the bindings and behavior properly. – Bryan Oakley Oct 05 '13 at 14:56