Anthony Labarre
Anthony Labarre

Reputation: 2794

Tkinter disable bind to return key in a Dialog with Text

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

Answers (1)

furas
furas

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

Related Questions