我有一个对象数据库,你可以在列表框中查看数据库中的项目,还有一个删除项目和创建项目的按钮。创建项目将打开项目类的对话框窗口,然后项目的数据将存储在数据库中。我用我的设置的一个非常简单的副本重现了这个问题(参见下面的代码)。
每次添加新项目时,添加成功(下次打开数据库对话框时都会出现),但是列表框没有插入项目,当我关闭数据库对话框时,我得到以下内容错误:
Tkinter回调中的异常回溯(最近一次调用最后一次):
文件“C:\ Python33 \ lib \ tkinter__init __。py”,第1442行,调用 return self.func(* args)文件“”,第21行,在addRecord文件“C:\ Python33 \ lib \ tkinter__init __。py”中,第2604行,在插入中 self.tk.call((self._w,'insert',index)+ elements) _tkinter.TclError:无效的命令名称“.50054760.50055432”
如果我只是尝试创建对象并填充其值而不调用其输入GUI(这是将内容插入到我的数据库中所必需的),则不会出现同样的问题。我在另一个线程中看到了类似的错误(抱歉,但我似乎无法再找到它),问题出在多线程上。我不知道我正在做的任何线程,也不想下载另一个包来处理tkinter线程。有任何想法吗?解决方法?我正在使用Python v3.3和64位Windows 7,如果有帮助的话。
这是我的简化数据库代码:
import tkinter
import traceback
# Test =========================================================================
class Test:
def __init__(self):
"""A database of objects' IDs and values."""
self.data = {1: 'a', 2: 'b', 3: 'c'}
#---------------------------------------------------------------------------
def addRecord(self):
"""Opens up a new item for editing and saves that ability to the
database."""
print('hi0')
newItem = OtherObject()
newItem.create(self.root)
print('hi1')
self.data[newItem.ID] = newItem.value
print('hi2')
self.listbox.insert(tkinter.END, self.formatItem(newItem.ID))
print('hi3')
#---------------------------------------------------------------------------
def delRecord(self):
"""Removes selected item from the database."""
try:
index = self.listbox.curselection()[0]
selection = self.listbox.get(index)
except:
return
ID = int(selection.split(':')[0])
self.data.pop(ID)
self.listbox.delete(index)
#---------------------------------------------------------------------------
def dataframe(self, master):
"""Assembles a tkinter frame with a scrollbar to view database objects.
(Returns Frame, Scrollbar widget, Listbox widget)
master: (Tk or Toplevel) tkinter master widget."""
frame = tkinter.Frame(master)
# scrollbar
scrollbar = tkinter.Scrollbar(frame)
scrollbar.pack(side=tkinter.LEFT, fill=tkinter.Y)
# listbox
listbox = tkinter.Listbox(frame, yscrollcommand=scrollbar.set)
listbox.pack(side=tkinter.LEFT, fill=tkinter.BOTH)
# fill listbox
for ID in self.data:
listbox.insert(tkinter.END, self.formatItem(ID))
return (frame, listbox, scrollbar)
#---------------------------------------------------------------------------
def destroyLB(self, e):
for line in traceback.format_stack():
print(line.strip())
#---------------------------------------------------------------------------
def formatItem(self, ID):
"""Creates a nice string representation of an item in the database."""
return '{0}:{1}'.format(ID, self.data[ID])
#---------------------------------------------------------------------------
def listboxSelect(self, e):
"""Manages events when the selection changes in the database interface.
e: (Event) tkinter event."""
try:
selection = self.listbox.get(self.listbox.curselection()[0])
except:
return
# set description label
ID = int(selection.split(':')[0])
self.lblstr.set(self.data[ID])
#---------------------------------------------------------------------------
def view(self):
"""Displays database interface."""
self.root = tkinter.Tk()
# listbox frame
self.frame, self.listbox, self.scrollbar = self.dataframe(self.root)
self.frame.grid(column=0, row=0)
self.listbox.bind('<<ListboxSelect>>', self.listboxSelect)
self.listbox.bind('<Destroy>', self.destroyLB)
# record display frame
self.lblstr = tkinter.StringVar()
self.lbl = tkinter.Label(self.root, textvariable=self.lblstr)
self.lbl.grid(column=1, row=0, sticky=tkinter.N)
# buttons frame
self.frame_btn = tkinter.Frame(self.root)
self.frame_btn.grid(row=1, columnspan=2, sticky=tkinter.E+tkinter.W)
# 'create new' button
self.btn_new = tkinter.Button(
self.frame_btn, text='+', command=self.addRecord)
self.btn_new.grid(row=0, column=0)
# 'delete record' button
self.btn_del = tkinter.Button(
self.frame_btn, text='-', command=self.delRecord)
self.btn_del.grid(row=0, column=1)
# display
self.root.mainloop()
# Test =========================================================================
# OtherObject ==================================================================
class OtherObject:
"""An object with an ID and value."""
def __init__ (self):
self.ID = 0
self.value = ''
#---------------------------------------------------------------------------
def create(self, master=None):
"""open a dialog for the user to entry a new object ID and value.
master: (Tk or Toplevel) tkinter master widget."""
self.stuff = tkinter.Toplevel(master)
# ID
tkinter.Label(self.stuff, text='ID: ').grid(row=0, column=0)
self.IDvar = tkinter.StringVar(self.stuff)
self.IDvar.set(self.ID)
IDwidget = tkinter.Entry(self.stuff, textvariable=self.IDvar)
IDwidget.grid(row=0, column=1)
# value
tkinter.Label(self.stuff, text='Value: ').grid(row=1, column=0)
self.valueVar = tkinter.StringVar(self.stuff)
self.valueVar.set(self.value)
valueWidget = tkinter.Entry(self.stuff, textvariable=self.valueVar)
valueWidget.grid(row=1, column=1)
# OK button
tkinter.Button(self.stuff, text='OK', command=self.OK).grid(row=2)
self.stuff.mainloop()
#---------------------------------------------------------------------------
def OK(self):
try: self.ID = int(self.IDvar.get())
except: self.ID = 0
self.value = self.valueVar.get()
self.stuff.destroy()
# OtherObject ==================================================================
提前致谢
答案 0 :(得分:2)
您正在创建多个Tk
的实例。 Tkinter的设计并不像那样,你会得到意想不到的行为。您需要重构代码,以便只创建一次Tk
实例。如果您需要多个窗口,请创建Toplevel
。
...时间过后......问题中的代码会更新......
在您的问题的更新版本中,您现在正在创建Tk
的一个实例,然后创建Toplevel
的实例。这很好。但是,您也不止一次地调用mainloop
这是一个问题。更糟糕的是,你正在重新定义self.root
,这无疑是问题的一部分。您必须在整个程序中只调用mainloop
一次。
答案 1 :(得分:0)
布莱恩·奥克利引导我解决我遇到的确切问题:
(1)我在OtherObject类中创建了第二个Tk对象。
(2)我在我的OtherObject类中调用了mainloop。
只有一个Tk对象应该存在,并且无论要显示多少个窗口,都应该只调用一次mainloop。