Tkinter & Threads – Исключение: не хватает места в стеке (бесконечный цикл?)

Мы реализовали распределенный чат, использующий графический интерфейс Tkinter. Когда я обновил свою систему до Fedora18, я получаю исключения при вызове события Tkinter, почти такие же, как описано loop">здесь:

Exception in thread Thread-1:
Traceback (most recent call last):
File "/usr/lib64/python2.7/threading.py", line 551, in bootstrap_inner self.run()
File "/usr/lib64/python2.7/threading.py", line 504, in run self.target(*self.__args, **self.__kwargs)
File "/hachat/peer.py", line 156, in startRecvLoop
self.processMessage(msg, addr)
File "/hachat/peer.py", line 222, in processMessage
self.gui.receive(msg) 
File "/hachat/gui.py", line 74, in receive
self.textfenster.insert(END,msg.name+": "+msg.text+'\n')
File "/usr/lib64/python2.7/lib-tk/Tkinter.py", line 2986, in insert
self.tk.call((self._w, 'insert', index, chars) + args)
TclError: out of stack space (infinite loop?)

Вот вырезка из класса gui:

import Tkinter
import ScrolledText
import tkMessageBox
import tkSimpleDialog
import threading 

class gui(object):

    def __init__(self, parent):
        self.root = Tkinter.Tk()             
        self.textfenster = ScrolledText.ScrolledText(self.fpopup,width=90,height=24,background='white')
        self.textfenster.pack(fill=Tkinter.BOTH, expand=Tkinter.YES)

    def run(self):
        self.guiRunThread = threading.Thread(target=self.root.mainloop())
        self.guiRunThread.daemon = True
        self.guiRunThread.start()

    def receive(self,msg):
        self.textfenster.insert(Tkinter.END,msg.name+": "+msg.text+'\n')
        self.textfenster.see(Tkinter.END)

Исключение появляется только в моей системе, причина, по-видимому, в том, что tk не был скомпилирован с поддержкой потоков. Я должен избавиться от этого Exeption - поскольку программа распространяется, ее нужно запускать на разных системах. Поэтому я спрашиваю, как избавиться от этого исключения, а также подсказку, чтобы получить tk для поддержки потоков. Я использую Python версии 2.7.3, Tcl/Tk версии 8.5. import Tkinter; Tkinter.Tk().tk.eval("puts $tcl_platform(threaded)") также возвращает исключение.


person wedel    schedule 25.01.2013    source источник
comment
Если вы просите внести изменения в Tkinter, спрашивайте не здесь.   -  person Bryan Oakley    schedule 25.01.2013
comment
Нет, я не прошу изменить сам tkinter. Только как заставить это работать без исключения и как заставить tk включить многопоточность.   -  person wedel    schedule 25.01.2013
comment
@wedel вы не можете вызывать gui.receive из другого потока, независимо от того, компилируете ли вы все с поддержкой потоков или нет. Другой вопрос - позвонить event_generate, что совсем другое. Измените свой код, чтобы использовать Queue Python для связи между потоками.   -  person mmgp    schedule 25.01.2013
comment
@mmgp: Ты прав! gui.receive вызывается не из другого потока, а извне gui-потока.   -  person wedel    schedule 25.01.2013
comment
@wedel Наверное, я не понял, что ты только что сказал. В чем разница между вызовом из другого потока и вызовом из-за пределов графического потока?   -  person mmgp    schedule 25.01.2013
comment
@mmgp gui.receive вызывается из другого метода вне потока gui.mainloop. это работает! только в моей системе выбрасывается исключение.   -  person wedel    schedule 26.01.2013
comment
@wedel, это именно то, что я говорю вам / НЕ / делать.   -  person mmgp    schedule 26.01.2013
comment
Держите весь код GUI в одном потоке. Действительно. Имейте столько других рабочих потоков, сколько хотите, но наборы инструментов GUI обычно пишутся как однопоточные (потому что они достаточно сложны даже с этим упрощением, большое спасибо!), так что будьте любезны и храните свой фактический код GUI в одном потоке.   -  person Donal Fellows    schedule 26.01.2013
comment
@DonalFellows Поправьте меня, если я ошибаюсь. Но поток с target=self.root.mainloop(), как показано выше, является одним потоком. Метод gui.receive просто вызывает Tkinter.insert и не является частью этого потока. Это действительно работает на всех других системах!   -  person wedel    schedule 27.01.2013
comment
Внутри Tkinter в любом случае должен проталкивать все через один базовый поток ОС (поскольку именно так работает Tk, как и многие другие наборы инструментов GUI; многопоточные инструменты GUI были опробованы и оказались чрезвычайно запутанными для кода). Это действительно так? Не знаю, если честно! Tkinter довольно сложен внутри; Я не помню, чтобы видел какую-либо обработку потоков, но она может быть там. Я бы все равно не кодил с этим таким образом, хотя…   -  person Donal Fellows    schedule 27.01.2013


Ответы (2)


Я решил проблему с очередями для связи с Tk. См. пример Mutli-threading python с Tkinter!

person wedel    schedule 27.01.2013

Этот работает:

from Tkinter import *
from ScrolledText import ScrolledText
from threading import Thread

scrolled = None

def start():
    global scrolled
    root = Tk()
    scrolled = ScrolledText(root)
    scrolled.pack(fill=BOTH, expand=YES)
    return root

Thread(target=start().mainloop).start()

print scrolled.get(0.0, END)
person Stungkuling S. Fandiño    schedule 02.04.2015