问题
I'm attempting to write a widget using Tkinter. The script uses an .ini file to store some saved settings. The interface allows the user to enter a new name to a dropdown menu, and save some text strings associated with it. A stripped down version is linked below.
I'm using String variables to track the changes to input fields. The issue I'm seeing is that occasionally, when a user goes to add another custom entry, the script will crash when attempting to update the text field with some default text. The error is "_tkinter.TclError: can't set "PY_VAR1":".
From searching extensively online, I'm fairly confident my problem is that the String variable has been automatically garbage collected and no longer exists. But I can't for the life of me find what I have to change to get this thing to work without crashing.
Link to the full runable code: https://drive.google.com/drive/folders/13Mqs6SLk6TcVRw7ofLTDlUz7nVmJ8QeV?usp=sharing
The issue is repeatable when adding new custom entries via the widget multiple times in a row.
I believe my problem may be related to the way I am tracing the string variable in line 72:
updateIP = StringVar()
updateIP.trace("w", lambda name, index, mode, sv=updateIP: updateIPAdress())
Later on, in a different method I try to set this variable with a default string when a user adds a new custom entry, line 126:
updateIP.set(defaultIP)
Usually this works perfectly fine, but on occasion crashes saying it can't set the variable, I assume because it has been deleted.
Any tips on how to get by this would be much appreciated.
Here is a minimal reproducible example, though it should be noted that the error becomes much more rare:
import tkinter as Tkinter
from tkinter import *
from tkinter import simpledialog as sdg
from tkinter import messagebox
import os
import configparser as ConfigParser
import collections # for using named tuples
import subprocess # for setting IP
def runGUI():
# set global variables
global SiteIDs
global tkvar
global popupMenu
global updateIP
global updateSM
# Build Window
frame = Tkinter.Frame(root,height=80, width=250)
frame.pack_propagate(0) # don't shrink
frame.pack()
# List of sections from param
SiteIDs = ["Address1","Address2","Address3","Add Custom..."]
# Create a Tkinter variable
tkvar = Tkinter.StringVar(root)
tkvar.set(SiteIDs[0]) # set the default option
# Build popup window
popupMenu = Tkinter.OptionMenu(frame, tkvar, *SiteIDs)
Tkinter.Label(frame, text="Site ID:",bg="black",fg="white").place(x = 61, y = 4)
popupMenu.place(x = 110, y = 4,height=21,width=124)
tkvar.trace('w', changeSiteID) # link function to change dropdown
# IP Address entry field
Tkinter.Label(frame, text="IP Address:",bg="black",fg="white").place(x = 38, y = 30)
updateIP = StringVar()
updateIP.trace("w", lambda name, index, mode, sv=updateIP: updateIPAdress()) # I think the issue could be related to this
e1 = Tkinter.Entry(frame,textvariable=updateIP)
e1.insert(0,"192.168.0.0")
e1.place(x = 110, y = 31)
# Run GUI
root.mainloop()
def changeSiteID(n,m,x):
if tkvar.get() == "Add Custom...":
addCustomSiteID()
else:
# Replace the text in the IP Address entry box
updateIP.set("192.198.0.0") #This would normally be grabbed from the ini file
# Update the last used variable
lastUsed = tkvar.get()
def updateIPAdress():
# Normally this is written to the ini file
newAddressToSave = updateIP.get()
def addCustomSiteID():
newID = sdg.askstring("Add Custom", "Enter Custom Site ID")
# If they hit cancel
if newID == None:
pass
# make sure that ID doesnt exist already, case insensitive
elif newID.lower() in (ID.lower() for ID in SiteIDs):
messagebox.showinfo("Duplicate SiteID", "The Site ID you entered already exists. Please enter a unique ID")
# Add the new ID
else:
# Add some default values
defaultIP = '192.168.0.0'
# add new ID to the menu
popupMenu.children['menu'].add_command(label=newID,command=Tkinter._setit(tkvar, newID))
# Set the selected option to the new ID
tkvar.set(newID)
#Fill the text entries with default addresses
updateIP.set(defaultIP) #This is the line the fault happens on
if __name__ == '__main__':
# Generate button GUI
root = Tkinter.Tk()
runGUI()
Full Traceback is:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\jellis\AppData\Local\Programs\Python\Python35-32\lib\tkinter\__init__.py", line 1549, in __call__
return self.func(*args)
File "C:\Users\jellis\AppData\Local\Programs\Python\Python35-32\lib\tkinter\__init__.py", line 3285, in __call__
self.__var.set(self.__value)
File "C:\Users\jellis\AppData\Local\Programs\Python\Python35-32\lib\tkinter\__init__.py", line 260, in set
return self._tk.globalsetvar(self._name, value)
_tkinter.TclError: can't set "PY_VAR0": can't set "PY_VAR2":
Traceback (most recent call last):
File "C:\Users\jellis\AppData\Local\Programs\Python\Python35-32\lib\tkinter\__init__.py", line 1549, in __call__
return self.func(*args)
File "C:\Users\jellis\Documents\Repositories\commontools\SiteTools\SetIP.py", line 223, in changeSiteID
addCustomSiteID()
File "C:\Users\jellis\Documents\Repositories\commontools\SiteTools\SetIP.py", line 217, in addCustomSiteID
updateSM.set(defaultSM)
File "C:\Users\jellis\AppData\Local\Programs\Python\Python35-32\lib\tkinter\__init__.py", line 260, in set
return self._tk.globalsetvar(self._name, value)
_tkinter.TclError: can't set "PY_VAR2":
来源:https://stackoverflow.com/questions/59684860/tkinter-crashing-on-garbage-collected-variable-tkinter-tclerror-cant-set-py