我正在使用 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()
#-------------------------------------------------------------------------
#-------------------------------------------------------------------------
#-------------------------------------------------------------------------
答案 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)