Issue
I'm using Python/tkinter for a GUI and I found a strange behaviour.
I'm building a very small dialog for selecting the language for my app. Then when closing it, I find that my 'result' variable in the dialog is gone.
Here's the snippet:
import tkinter as tk
from tkinter.simpledialog import Dialog
from tkinter import ttk, LEFT, ACTIVE
from tkinter.ttk import Button, Frame
class LanguageDialog(Dialog):
lang_dict = {
'italiano': 'it',
'español': 'es',
'english': 'en',
'galego': 'gl',
}
def __init__(self, parent, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
self.selected_language = tk.StringVar()
self.result = 'en' # default value
def _on_cmb_change(self, event):
"""
Keeps updated the result variable with the code I want in the end
"""
print(self.lang_dict[self.selected_language.get()])
self.result = self.lang_dict[self.selected_language.get()]
def body(self, master):
self.selected_language = tk.StringVar()
ops = tuple(self.lang_dict.keys())
cmb_lang = ttk.Combobox(self, values=ops, state='readonly',
textvariable=self.selected_language)
cmb_lang.pack(side=tk.TOP)
cmb_lang.bind('<<ComboboxSelected>>', self._on_cmb_change)
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)
self.bind("<Return>", self.ok)
box.pack()
if __name__ == '__main__':
root = tk.Tk()
d = LanguageDialog(root)
print(f'After the dialog, {d.result}')
root.mainloop()
I believe I have some misconception on how to use the dialog. While debugging I saw the dialog triggers some destroy() method, but it seems like it is probably coming again to the initializer and executing the
self.result = 'en'
line, even though I didn't create or invoke the dialog again through LanguageDialog().
I searched the web for similar examples and found that they are using the dialog in the same way, for example, here
Solution
If you look into the source code of simpledialog.Dialog
class, you will find that wait_window()
is executed at the end of Dialog.__init__()
which tries to make the window like a modal dialog.
So super().__init__(...)
inside LanguageDialog.__init__()
will not return until the dialog is closed. When the dialog is closed, self.result
is reset to 'en'.
You should move the line, self.result = 'en'
into the beginning of body()
(just like self.selected_language
) and then remove the __init__()
function.
Below is the modified code:
import tkinter as tk
from tkinter.simpledialog import Dialog
from tkinter import ttk, LEFT, ACTIVE
from tkinter.ttk import Button, Frame
class LanguageDialog(Dialog):
lang_dict = {
'italiano': 'it',
'español': 'es',
'english': 'en',
'galego': 'gl',
}
def _on_cmb_change(self, event):
"""
Keeps updated the result variable with the code I want in the end
"""
print(self.lang_dict[self.selected_language.get()])
self.result = self.lang_dict[self.selected_language.get()]
def body(self, master):
self.selected_language = tk.StringVar()
# initial self.result to 'en' here
self.result = 'en' # default value
ops = tuple(self.lang_dict.keys())
cmb_lang = ttk.Combobox(self, values=ops, state='readonly',
textvariable=self.selected_language)
cmb_lang.pack(side=tk.TOP)
cmb_lang.bind('<<ComboboxSelected>>', self._on_cmb_change)
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)
self.bind("<Return>", self.ok)
box.pack()
if __name__ == '__main__':
root = tk.Tk()
root.withdraw() # hide the root window
d = LanguageDialog(root)
print(f'After the dialog, {d.result}')
#root.mainloop() # don't need to call mainloop()
Answered By - acw1668
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.