Tkinter crashing on garbage collected variable: _tkinter.TclError: can't set “PY_VAR1”:

六月ゝ 毕业季﹏ 提交于 2020-01-26 04:45:49

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!