Reputation: 2794
I have a simple dialog in my app whose purpose is to allow the user to edit some text properties. The relevant part of the code simplifies to:
from tkinter.simpledialog import Dialog
from tkinter.scrolledtext import ScrolledText
class PropertiesDialog(Dialog):
def __init__(self, root):
Dialog.__init__(self, root)
def body(self, master):
Label(master, text="Description").grid(row=0, column=0, sticky=W)
self.project_description = ScrolledText(master, height=10, width=40)
self.project_description.grid(row=0, column=1, sticky=W)
Unfortunately, using the <Return>
key when typing in the ScrolledText
field is not interpreted as a line break but as an "OK" by the Dialog, which therefore closes. I thought binding that key to a handler that does nothing would help, but inserting self.bind("<Return>", self.my_return_handler)
at the beginning of my dialog's body
method does not intercept anything and inserting it right after Dialog.__init__(self, root)
raises the following error:
Exception in Tkinter callback
Traceback (most recent call last):
File "/usr/lib/python3.7/tkinter/__init__.py", line 1705, in __call__
return self.func(*args)
File "guitk.py", line 142, in edit_project_properties
ProjectPropertiesDialog(self.main_window, None)
File "guitk.py", line 337, in __init__
self.bind("<Return>", self.return_handler)
File "/usr/lib/python3.7/tkinter/__init__.py", line 1251, in bind
return self._bind(('bind', self._w), sequence, func, add)
File "/usr/lib/python3.7/tkinter/__init__.py", line 1206, in _bind
self.tk.call(what + (sequence, cmd))
_tkinter.TclError: bad window path name ".!projectpropertiesdialog"
How do I fix this? Is there a better suited approach?
Upvotes: 1
Views: 1009
Reputation: 142641
If you use
import tkinter.simpledialog
print(tkinter.simpledialog.__file__)
then you get path to source code and you will see where it binds <Return>
and then you can remove it.
I copied original buttonbox()
from source code and commented line which binds <Return>
from tkinter import *
from tkinter.simpledialog import Dialog
from tkinter.scrolledtext import ScrolledText
class PropertiesDialog(Dialog):
def body(self, master):
Label(master, text="Description").grid(row=0, column=0, sticky=W)
self.project_description = ScrolledText(master, height=10, width=40)
self.project_description.grid(row=0, column=1, sticky=W)
self.project_description.focus()
def buttonbox(self):
'''add standard button box.
override if you do not want the standard buttons
'''
box = Frame(self)
w = Button(box, text="OK", width=10, command=self.ok, default=ACTIVE)
w.pack(side=LEFT, padx=5, pady=5)
w = Button(box, text="Cancel", width=10, command=self.cancel)
w.pack(side=LEFT, padx=5, pady=5)
#self.bind("<Return>", self.ok) # <-- remove it
self.bind("<Escape>", self.cancel)
box.pack()
root = Tk()
PropertiesDialog(root)
root.mainloop()
BTW: In source code __init__
you can see it excutes body
before buttonbox
so you can't unbind()
it in body
because it will bind it after this unbind()
. You can also see that __init__
works in different way then normal class - __init__
waits for closing window - so using it inside __init__
will be executed after closing Dialog window.
EDIT: Info for other users - as Anthony Labarre mentioned in comment it is simpler method
def buttonbox(self):
super().buttonbox()
self.unbind("<Return>")
#self.bind("<Control-Return>", self.ok) # bind other combination to OK
from tkinter import *
from tkinter.simpledialog import Dialog
from tkinter.scrolledtext import ScrolledText
class PropertiesDialog(Dialog):
def body(self, master):
Label(master, text="Description").grid(row=0, column=0, sticky=W)
self.project_description = ScrolledText(master, height=10, width=40)
self.project_description.grid(row=0, column=1, sticky=W)
self.project_description.focus()
def buttonbox(self):
super().buttonbox()
self.unbind("<Return>")
#self.bind("<Control-Return>", self.ok) # bind other combination to OK
root = Tk()
PropertiesDialog(root)
root.mainloop()
Upvotes: 3