-1

我有一个对象数据库,您可以在列表框中查看数据库中的项目,并且有一个用于删除项目和创建项目的按钮。创建项目会打开项目类的对话窗口,然后项目的数据将存储在数据库中。我用我的设置的一个非常简单的副本重现了这个问题(见下面的代码)。

每次添加新项目时,添加成功(下次打开数据库对话框时它就在那里),但列表框没有插入项目,当我关闭数据库对话框时出现以下错误:

Tkinter 回调 Traceback 中的异常(最近一次调用最后一次):
文件“C:\Python33\lib\tkinter__init__.py”,第 1442 行,调用 返回 self.func(*args) 文件“”,第 21 行,在 addRecord 文件中C:\Python33\lib\tkinter__init__.py",第 2604 行,插入 self.tk.call((self._w, 'insert', index) + elements) _tkinter.TclError: invalid command name ".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 ==================================================================

提前致谢

4

2 回答 2

2

您正在创建多个Tk. Tkinter 的设计不是这样工作的,你会得到意想不到的行为。您需要重构您的代码,以便您Tk只创建一次的实例。如果您需要多个窗口,请创建Toplevel.

...时间流逝...问题中的代码已更新...

在您的问题的更新版本中,您现在正在创建一个实例Tk,然后创建一个实例Toplevel。这很好。但是,您也mainloop不止一次打电话,这是一个问题。更糟糕的是,您正在重新定义self.root无疑是问题的一部分。您必须mainloop在整个程序中只调用一次。

于 2013-04-20T12:15:29.897 回答
1

布莱恩·奥克利(Bryan Oakley)指导我解决了我遇到的确切问题:

(1) 我在我的 OtherObject 类中创建了第二个 Tk 对象。

(2) 我在 OtherObject 类中调用了 mainloop。

应该只存在一个 Tk 对象,并且无论要显示多少个窗口,都应该只调用一次 mainloop。

于 2013-05-10T01:58:10.307 回答