Tkinter代码在Windows上运行,但在Linux上运行

时间:2016-04-02 06:25:55

标签: python linux tkinter python-multithreading

我在windows中编写了这段代码,但是当我将它复制到我的覆盆子pi(运行Jessie Debian)时,它会给出运行时错误

我的代码:

from Tkinter import *
from PIL import ImageTk,Image
from time import sleep
import thread
root = Tk()
img=ImageTk.PhotoImage(Image.open("1.jpg").resize((root.winfo_screenwidth()-3,root.winfo_screenheight()-3)))
panel = Label(root, image = img)
panel.pack(side = "bottom", fill = "both", expand = "yes")
if __name__ == '__main__':
    thread.start_new_thread(root.mainloop,())
    for i in xrange(1,5):
        sleep(1)
        img2 = ImageTk.PhotoImage(Image.open(str(i)+".jpg").resize((root.winfo_screenwidth()-3,root.winfo_screenheight()-3)))
        panel.configure(image = img2)
        panel.image = img2

在linux中执行时会出错:

Unhandled exception in thread started by <bound method Tk.mainloop of <Tkinter.Tk instance at 0x75b605d0>>
Traceback (most recent call last):
  File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 1124, in mainloop
    self.tk.mainloop(n)
RuntimeError: Calling Tcl from different appartment

如何在两个平台上运行此代码

1 个答案:

答案 0 :(得分:5)

无法从不同的线程调用Tcl函数。它在Windows上运行的事实可能是因为检查此条件是错误的。

您需要做的是拥有一个与Tk对话的“UI线程”。通常,所有这个线程明确地做的是:

  • 设置所有小部件和事件处理程序
  • 致电root.mainloop()

如果您希望稍后更改UI,则应在事件处理程序中执行此操作。如果要执行某些代码以响应非UI事件,可以使用rootafter注册自定义事件处理程序或调度要在主循环上执行的函数。

from Tkinter import *
from PIL import ImageTk, Image

root = Tk()
panel = Label(root)
panel.pack(side = "bottom", fill = "both", expand = "yes")

def showImage(i):
    img = ImageTk.PhotoImage(Image.open(str(i)+".jpg"))
    img = img.resize((root.winfo_screenwidth()-3, root.winfo_screenheight()-3))
    panel.configure(image = img)
    panel.image = img

class Task(object):
    # A little helper to wrap a function with arguments.
    def __init__(self, fun, *args, **kwargs):
        self.fun = fun
        self.args = args
        self.kwargs = kwargs
    def __call__(self):
        self.fun(*self.args, **self.kwargs)

if __name__ == '__main__':
    for i in range(1, 5):
        # run showImage(i) on the mainloop after (i - 1) seconds
        root.after((i - 1) * 1000, Task(showImage, i))
    root.mainloop()

如果需要,您可以从其他线程安排功能:

import thread

def doStuff():
    for i in range(1, 5):
        sleep(1)
        root.after(0, Task(showImage, i))    

thread.start_new_thread(doStuff, ())

注意:我创建了Task以便轻松指定我希望使用给定showImage运行i。通常,functools.partial会很好,但由于dynamic create method and decorator, got error 'functools.partial' object has no attribute '__module__'

,显然它与Tkinter的效果不佳

您还可以执行以下操作:

(lambda ii: lambda: showImage(ii))(i)

但是当你稍后返回代码时,这可能会让人感到困惑。

如果你没有预料到有其他任务,你可以这样做:

def showImage(i):
    def newfun():
        img = ImageTk.PhotoImage(Image.open(str(i)+".jpg"))
        img = img.resize((root.winfo_screenwidth()-3, root.winfo_screenheight()-3))
        panel.configure(image = img)
        panel.image = img
    return newfun