TypeError: unhashable type: 'StringVar' 关闭 tkinter 窗口

时间:2021-03-01 14:26:12

标签: python tkinter

我正在使用 Python 和 tkinter 设计一个小应用程序,但我发现了一个与 Entry 小部件相关的奇怪行为。我开始设计数据输入表单。问题是当我关闭(销毁)窗口时会产生此错误:

pydev debugger: starting (pid: 11176)
Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Program Files\Python38\lib\tkinter\__init__.py", line 1883, in __call__
    return self.func(*args)
  File "C:\Program Files\Python38\lib\tkinter\__init__.py", line 2576, in destroy
    if self._name in self.master.children:
TypeError: unhashable type: 'StringVar'

我唯一明白的是与这段代码有关:

        self._name = StringVar()
        nameEntry = Entry(frmTop, width=48, textvariable=self._name, font=entryFont)
        nameEntry.grid(column=1, row=0, sticky=(W))

这对我来说看起来不错。

为了更清楚,这是我的代码(没什么特别的):

'''
Created on 1 mar 2021

@author: massimo
'''
from tkinter import Tk, Toplevel, StringVar, N, E, S, W, Text, font
from tkinter.ttk import Button, Label, Frame, Entry, Style

class CFormProject(Toplevel):
    def __init__(self, winParent, title, **kwargs):
        super().__init__(**kwargs)
        self.parent = winParent
        self.title = title
        self.transient(winParent)
        self.grab_set()
        self._initWin()
    #-------------------------------------------------------------------------
    
    def _initWin(self):
        # [MM] config data valid inside the form
        padding = "12 12 12 12"
        # Create an object of type Font from tkinter. 
        entryFont = font.Font( family = "Century Gothic", size = 11, weight = "normal")         
        labelFont = font.Font( family = "Albany", size = 10, weight = "normal")         
        buttonFont = font.Font( family = "Albany", size = 10, weight = "normal")
        
        # [MM] this is the frame taking all the form space         
        frmWin = Frame(self, padding=padding)
        frmWin.config()
        # configure the grid
        frmWin.columnconfigure(0, weight=1)
        frmWin.grid(column=0, row=0)
        
        
        # [MM] this is the frame for the widgets to enter/show data
        frmTop = Frame(frmWin, padding=padding)
        frmTop.config()
        # configure the grid
        frmTop.columnconfigure(0, weight=1)
        frmTop.columnconfigure(1, weight=3)        
        frmTop.grid(column=0, row=0)
        # [MM] this are the widgets to enter data
        Label(frmTop, text="Project", font=labelFont).grid(column=0, row=0, sticky=(N, E))
        
        self._name = StringVar()
        nameEntry = Entry(frmTop, width=48, textvariable=self._name, font=entryFont)
        nameEntry.grid(column=1, row=0, sticky=(W))
        
        Label(frmTop, text="Description", font=labelFont).grid(column=0, row=1, sticky=(N, E))
        self.descText = Text(frmTop, height=4, width=48, wrap='none', font=entryFont)
        self.descText.grid(column=1, row=1, sticky=(W))
        self.descText.insert('1.0', 'This is a Text widget demo')


        # [MM] bottom frame: buttons only
        frmBot = Frame(frmWin, padding=padding)
        frmBot.config()
        # configure the grid
        frmBot.columnconfigure(0, weight=1)
        frmBot.columnconfigure(1, weight=1)        
        frmBot.columnconfigure(2, weight=1)        
        frmBot.columnconfigure(3, weight=1)        
        frmBot.grid(column=0, row=1)
        
        '''
        my_style = Style()
        my_style.configure('W.TButton', background='black', font=('calibri', 10, 'bold', 'underline'), foreground='white')
        print(my_style.lookup("W.TButton", "font"))
        '''
        
        Button(frmBot, text="Clear", command=self.onClearClick).grid(column=1, row=0, sticky=(W))
        Button(frmBot, text="Cancel",  command=self.onCancelClick).grid(column=2, row=0, sticky=(W))
        Button(frmBot, text="Save", command=self.onSaveClick).grid(column=3, row=0, sticky=(W))

        for child in frmWin.winfo_children(): 
            child.grid_configure(padx=5, pady=5)
        for child in frmTop.winfo_children(): 
            child.grid_configure(padx=5, pady=5)
        for child in frmBot.winfo_children(): 
            child.grid_configure(padx=10, pady=5)

        '''
        nameEntry.focus()       
        '''
#-------------------------------------------------------------------------

    def onSaveClick(self):
        self.destroy()
    #-------------------------------------------------------------------------
    
    def onCancelClick(self):
        self.destroy()
    #-------------------------------------------------------------------------
        
    def onClearClick(self):
        self.destroy()
    #-------------------------------------------------------------------------
#-------------------------------------------------------------------------
#-------------------------------------------------------------------------
    
    
if __name__ == '__main__':
    root = Tk()
    frmPrj = CFormProject(root, "Project")
    root.wait_window(frmPrj)
    root.mainloop()
#-------------------------------------------------------------------------
#-------------------------------------------------------------------------
#-------------------------------------------------------------------------

1 个答案:

答案 0 :(得分:1)

当从任何 tkinter 小部件继承时,您不能使用变量 self._name,因为 tkinter 将数据存储在那里并且您会覆盖它。将该变量更改为其他类似 self.project_name 的变量。来自 tkinter/__init__.py 的代码:

class BaseWidget(Misc):
    """Internal class."""
    def _setup(self, master, cnf):
        ...
        self._name = name
        ...

    def __init__(self, master, widgetName, cnf={}, kw={}, extra=()):
        ...
        BaseWidget._setup(self, master, cnf)