Lutz Prechelt
Lutz Prechelt

Reputation: 39336

tkinter.simpledialog.Dialog leaves the initiating tkinter.Button depressed

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:

(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?

Upvotes: 3

Views: 4937

Answers (2)

user2555451
user2555451

Reputation:

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

Upvotes: 0

msw
msw

Reputation: 43497

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).

Upvotes: 3

Related Questions