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
CodePudding user response:
Please and to root.mainloop to the end of your code.
if __name__ == '__main__':
root = tk.Tk()
d = LanguageDialog(root)
print(f'After the dialog, {d.result}')
root.mainloop()
CodePudding user response:
I solved it with a global variable not the best way but that's what I could come up now, the code below.Hope it helps you.
import tkinter as tk
from tkinter.simpledialog import Dialog
from tkinter import ttk, LEFT, ACTIVE
from tkinter.ttk import Button, Frame
value_global = 'en'
class LanguageDialog(Dialog):
def __init__(self, parent, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
self.selected_language = tk.StringVar()
self.result = value_global # default value
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()]
global value_global
value_global= self.result
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()
CodePudding user response:
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()