Python代码问题,应用程序已被销毁Tcl错误

0 投票
3 回答
5470 浏览
提问于 2025-04-16 18:22

我正在制作一个Tkinter的图形界面,主要功能就是调用图片。不过,我一直在找合适的Tkinter文档,真是费了不少劲。

我有一行代码似乎无法按我想要的方式工作——我想把字典里的所有值都调出来,然后逐个打印,并根据每个值的名字拉取对应的图片,在调用下一个值之前完成这个过程。我试过使用dict.itervalues()和dict.values(),但就是搞不明白怎么做……

总之,这里是相关的代码片段:

for key in ansDict.iterkeys(): #using the iterkeys function... kind of
    x=key

    root = tk.Tk() # root window created (is this in the right place?)
    root.title('C H E M I S T R Y   A B C\'s')

    frameAns=tk.Frame(root)
    frameAns.grid(row=0, column=0, sticky=tk.NW)

    for i in range(len(ansDict[x])):
        print '-->' + ansDict[x][i]

    for value in ansDict.itervalues(): #This is the most important part

        for i in range(len(value)): #pulls value list from dictionary named ansDict
            picRef1 = Image.open(value[i] + '.jpg') #calls image file by the same name using PIL
            photo1 = ImageTk.PhotoImage(picRef1, master=root)

            button1 = tk.Button(frameAns, compound=tk.TOP, image=photo1, text=str(value[i]) + '\nClose me!', bg='white') #pulls up button onto which the image is pasted
            button1.grid(sticky=tk.NW, padx=2, pady=2) #places button on grid
            button1.image=photo1

            root.mainloop()

最后,代码执行到最后时,会拉出一两张图片,然后我就遇到了以下错误:

TclError: can't invoke "image" command: application has been destroyed

我搞不清楚问题出在哪里。我不能移动图片的命令,而且我需要以某种方式“保存”它,以免被销毁。我知道这里还有其他代码错误,但我觉得只要搞定这个TclError,其他问题也能解决。

如果有更简单的方法来实现这些功能,请告诉我!

3 个回答

0

这里有一个可能的方案,虽然它的结构和你的例子不太一样。这个方案是把四个100像素见方的图片一个叠一个地放在一起。我觉得你需要单独保存每个图片对象的引用,所以我把它们放在了一个叫做images的字典里。

from Tkinter import *
import os
from PIL import Image, ImageTk

image_names = { '1':'one','2':'two','3':'three','4':'four' }
images = {}

root = Tk()
root.title("HELLO")
frm = Frame(root)

for v in image_names.itervalues():
   images[v] = {}
   images[v]['i']  = Image.open("%s%s.jpg" % (os.path.dirname(__file__), v))
   images[v]['pi'] = ImageTk.PhotoImage(images[v]['i'])
   images[v]['b']  = Button(frm, image=images[v]['pi'])
   images[v]['b'].pack()

frm.pack()

mainloop()

这里有一个不错的链接,讨论了PhotoImage类。

http://effbot.org/tkinterbook/photoimage.htm

0

看起来你可能没有理解什么是事件驱动编程。你应该先创建好整个图形界面(GUI),然后把各种小部件放进去,设置好事件,最后进入一个无限循环。这个图形界面会根据你设置的事件来调用相应的回调函数。所以你程序中的某些部分确实只需要调用一次,比如:root = tk.Tk()root.mainloop()

编辑:添加了事件驱动编程的“想法示例”。

from Tkinter import *

master = Tk()

def callback():
    print "click!"

b = Button(master, text="OK", command=callback)
b.pack()

mainloop()
2

我一直在寻找一个好的解决方案,但还没有找到合适的办法。查看Tkinter.py这个类,发现Image的del值是:

def __del__(self):
    if self.name:
        try:
            self.tk.call('image', 'delete', self.name)
        except TclError:
            # May happen if the root was destroyed
            pass

这意味着如果你想要做一个极端的黑客操作,你可以按照jtp的链接设置一个PhotoImage。

photo = tk.PhotoImage(file="C:/myimage.gif")
widget["image"] = photo
widget.image = photo

然后在程序退出之前,你可以做以下黑客操作:

photo.name = None

这样可以防止它在PhotoImage删除时尝试自我清理,从而避免在del方法中调用异常。我并不真的推荐你这样做,除非你真的没有其他选择,已经走投无路了。

我会继续研究这个问题,如果找到更好的解决方案,会更新这个帖子,希望在那之前有人能给出正确的解决办法。

撰写回答