问题
I defined a tkinter checkbutton widget to create check boxes based on a user defined list and return a new list including the selected elements. It works as expected when I run it directly on IPython console (I use Spyder3 most of the time). It also works if I save the function as a Python script and call it as a module's function in Python console. However, if I run the script containing this function, it behaves weird that all the change of those check buttons will not reflect to the real value at all; all of the values are False no matter what items I selected. The followings are the code and example:
CheckBox.py :
import tkinter as tk
from tkinter.messagebox import askyesno
import sys
def CheckBox(picks, *Args):
class checkboxfromlist(tk.Frame):
def __init__(self, parent, picks):
tk.Frame.__init__(self, parent)
self.vars = []
for pick in picks:
var = tk.BooleanVar()
chk = tk.Checkbutton(self, text=pick, variable=var, onvalue=True, offvalue=False)
chk.pack()
self.vars.append(var)
def state(self):
return list(map((lambda var: var.get()), self.vars))
class MainWindow(tk.Tk):
def __init__(self, picks, text):
tk.Tk.__init__(self)
self.resizable(width=False, height=False)
self.title('Select desired download')
self.protocol('WM_DELETE_WINDOW', self.on_exit)
self.l = tk.Label(self, text='Please select desired download {}'.format(text))
self.l.pack()
self.ChkBox = checkboxfromlist(self, picks)
self.ChkBox.pack()
self.Button = tk.Button(self, text='Ok', width=40, height=2, command=self.quit)
self.Button.pack()
# Make popup window at the centre
self.update_idletasks()
w = self.winfo_screenwidth()
h = self.winfo_screenheight()
size = tuple(int(_) for _ in self.geometry().split('+')[0].split('x'))
x = w/2 - size[0]/2
y = h/2 - size[1]/2
self.geometry('%dx%d+%d+%d' % (size + (x, y)))
def on_exit(self):
# When you click x to exit, this function is called
if askyesno("Exit", "Do you want to quit the application?"):
self.destroy()
sys.exit(0)
text = ' for '.join(Args)
App = MainWindow(picks, text)
App.mainloop()
mask = App.ChkBox.state()
App.destroy()
selected = [item for index, item in enumerate(picks) if mask[index]]
return selected
Here is the snapshot of the widget: widget screenshot
These work:
- Paste the code directly in IPython console then run CheckBox(MyList, 'item')
- Input Python in terminal, then import CheckBox, run CheckBox.CheckBox(MyList, 'item')
These don't work:
Run as a script either in spyder, terminal, or double-click
if __name__ == '__main__':
CheckBox(MyList, 'item')
Run as a external module in a script in spyder, terminal, or double-click
import CheckBox
if __name__ == '__main__':
CheckBox.CheckBox(MyList, 'item')
I tried using Python 3.5.2 on Ubuntu and 3.6.4 on Windows 10 and both of them gave me the same problem. I have spent whole day figuring out what was going on but with no luck. Any comment would be very much appreciated.
Edit:
I found that the widget works until I add the following code at the beginning of the main function:
root = tk.Tk()
root.withdraw()
The reason I do so is because there will be some user prompt widget later during the process. Without root.withdraw(), there will be always a blank window on the screen. The effect of the above code last until I restart Spyder3; this means I can't make my code work again by simply delete the above code. Here's the snapshots of the program:
This works:
Screenshot of Spyder
As you can see, the print out list is exactly the items I selected (I run it once then run it again to take the snapshot of the widget).
This doesn't:
Screenshot of Spyder
The print out list is empty no matter how I selected the items.
PS: fuck_tk is exactly the same as CheckBox.py I post above.
Edit2:
I found out that in order to make all the widget works without showing the blank main tkinter window, I need to make all of my tkinter messageboxes as individual functions as below:
def User_Confirm():
root = tk.Tk()
root.withdraw()
answer = askquestion('test', 'tkinter sucks')
root.destroy()
return answer
However, this workaround sound very stupid to me. Is there any elegant way to hide the main tkinter window while not influence the functionality of my CheckBox widget?
来源:https://stackoverflow.com/questions/49627304/cant-make-tkinter-checkbutton-work-normally-when-running-as-script