2016-04-02 2 views
1

Я написал этот код в окнах, и она работала, но когда я скопировал его в мой малиновый пи (бег на основе Jessie Debian) это дает сообщение об ошибке выполненияTkinter код работает на Windows, но не на Linux

мой код:

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 

, что я должен сделать, чтобы запустить этот код на обеих платформах

+0

вам не нужна многопоточность для такой простой программы. Вы можете легко обновлять изображение каждую секунду без потоков. –

ответ

5

Функции Tcl не могут быть вызваны из разных потоков. Тот факт, что он работает в Windows, может быть вызван ошибкой проверки этого условия.

Что вам нужно сделать, это иметь один «поток пользовательского интерфейса», который говорит с Tk. Как правило, все это нить явно делает это:

  • созданы все виджеты и обработчики событий
  • вызова root.mainloop()

Если затем вы хотите внести изменения в пользовательском интерфейсе позже, вы должны это сделать в обработчиках событий. Если вы хотите выполнить какой-либо код в ответ на не-пользовательские события, вы можете зарегистрировать собственные обработчики событий в root или планировать функции, которые будут выполняться в основном цикле, используя after.

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 бы хорошо, но, видимо, это не очень хорошо играть с Tkinter из-за dynamic create method and decorator, got error 'functools.partial' object has no attribute '__module__'

Вы также могли бы сделать что-то вроде:

(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 
+1

Ваш лямбда-захват 'i' не будет работать. Лямбда захватит ссылку на глобальную переменную, а не ее ценность. Поэтому все значения i будут 4. Лучше использовать 'functools.partial' для создания обратного вызова. – Dunes

+0

Хорошая точка, спасибо за это. – szym

+0

Sigh, http://stackoverflow.com/questions/20594193/dynamic-create-method-and-decorator-got-error-functools-partial-object-has-no делает 'functools.partial' не работать с Tkinter – szym

Смежные вопросы