Problems with using queue in Tkinter application

I

Icarus

I'm working on a serial protocol analyzer in python. We have an
application written by someone else in MFC but we need something that
is cross platform. I intended to implement the GUI portion in Tkinter
but am having trouble.

The idea is that I will read messages from the serial port and output
them to a Tkinter Text object initially. Eventually it will have
other functionality but that's it for the short term. I've written
this little test app to experiment with putting things on the GUI via
a Queue which is polled by the Tkinter loop.

On some machines this code works fine and I get whatever I type in
displayed in the Text widget. On others I get errors like this as
soon as I start it running.

error in background error handler:
out of stack space (infinite loop?)
while executing
"::tcl::Bgerror {out of stack space (infinite loop?)} {-code 1 -level
0 -errorcode NONE -errorinfo {out of stack space (infinite loop?)
while execu..."


I don't understand why on some machines it works exactly as expected
and on others it acts the same way Tkinter does when I call functions
directly from outside the Tkinter thread. Does anyone have any
suggestions? The full code as appended below. Thanks in advance.

Code:
import Queue

class functionQueue:

    def __init__(self, root = None, timeout = 250):

        self.functionQueue = Queue.Queue()
        self.root = root
        self.timeout = timeout

        if(self.root):
            self.pop_function(root)


    def pop_function(self, root = None):

        try:
            funcArgList = self.functionQueue.get(block = False)
        except Queue.Empty:
            pass
        else:
            try:
                funcArgList[0](*funcArgList[1])
            except:
                try:
                    print "Failed to call function", funcArgList[0]
                except:
                    print "Failed to call function"

        if(root):
            root.after(self.timeout, lambda: self.pop_function
(self.root))

    def add_function(self, function, argList):

        try:
            self.functionQueue.put([function, argList])
        except:
            pass


if( __name__ == '__main__'):

    import Tkinter
    import thread

    text = Tkinter.Text()
    text.pack()

    myQueue = functionQueue(text, 50)

    def gui_loop():
        try:
            text.mainloop()
        except:
            import os
            os._exit(1)

    thread.start_new_thread(text.mainloop, ())

    while(True):
        usrInput = raw_input()

        if(usrInput == "-1"):
            import os
            os._exit(0)

        myQueue.add_function(text.insert, ['end', usrInput + "\n"])
        myQueue.add_function(text.see, ['end'])
 
P

Peter Otten

Icarus said:
I'm working on a serial protocol analyzer in python. We have an
application written by someone else in MFC but we need something that
is cross platform. I intended to implement the GUI portion in Tkinter
but am having trouble.

The idea is that I will read messages from the serial port and output
them to a Tkinter Text object initially. Eventually it will have
other functionality but that's it for the short term. I've written
this little test app to experiment with putting things on the GUI via
a Queue which is polled by the Tkinter loop.

On some machines this code works fine and I get whatever I type in
displayed in the Text widget. On others I get errors like this as
soon as I start it running.

error in background error handler:
out of stack space (infinite loop?)
while executing
"::tcl::Bgerror {out of stack space (infinite loop?)} {-code 1 -level
0 -errorcode NONE -errorinfo {out of stack space (infinite loop?)
while execu..."


I don't understand why on some machines it works exactly as expected
and on others it acts the same way Tkinter does when I call functions
directly from outside the Tkinter thread. Does anyone have any
suggestions? The full code as appended below. Thanks in advance.

Code:
import Queue

class functionQueue:

def __init__(self, root = None, timeout = 250):

self.functionQueue = Queue.Queue()
self.root = root
self.timeout = timeout

if(self.root):
self.pop_function(root)


def pop_function(self, root = None):

try:
funcArgList = self.functionQueue.get(block = False)
except Queue.Empty:
pass
else:
try:
funcArgList[0](*funcArgList[1])
except:
try:
print "Failed to call function", funcArgList[0]
except:
print "Failed to call function"

if(root):
root.after(self.timeout, lambda: self.pop_function
(self.root))

def add_function(self, function, argList):

try:
self.functionQueue.put([function, argList])
except:
pass


if( __name__ == '__main__'):

import Tkinter
import thread

text = Tkinter.Text()
text.pack()

myQueue = functionQueue(text, 50)

def gui_loop():
try:
text.mainloop()
except:
import os
os._exit(1)

thread.start_new_thread(text.mainloop, ())

while(True):
usrInput = raw_input()

if(usrInput == "-1"):
import os
os._exit(0)

myQueue.add_function(text.insert, ['end', usrInput + "\n"])
myQueue.add_function(text.see, ['end'])

I can make it work over here by putting the UI into the main thread, as
suggested by http://effbot.org/zone/tkinter-threads.htm:

import Queue
import Tkinter
import threading

class FunctionQueue:
# unchanged

def input_loop():
while True:
try:
usrInput = raw_input()
except EOFError:
break
myQueue.add_function(text.insert, ['end', usrInput + "\n"])
myQueue.add_function(text.see, ['end'])
myQueue.add_function(text.quit, [])

if __name__ == '__main__':
text = Tkinter.Text()
text.pack()

myQueue = FunctionQueue(text, 50)
threading.Thread(target=input_loop).start()
text.mainloop()

Peter
 
I

Icarus

Icarus said:
I'm working on a serial protocol analyzer in python.  We have an
application written by someone else in MFC but we need something that
is cross platform.  I intended to implement the GUI portion in Tkinter
but am having trouble.
The idea is that I will read messages from the serial port and output
them to a Tkinter Text object initially.  Eventually it will have
other functionality but that's it for the short term.  I've written
this little test app to experiment with putting things on the GUI via
a Queue which is polled by the Tkinter loop.
On some machines this code works fine and I get whatever I type in
displayed in the Text widget.  On others I get errors like this as
soon as I start it running.
error in background error handler:
out of stack space (infinite loop?)
    while executing
"::tcl::Bgerror {out of stack space (infinite loop?)} {-code 1 -level
0 -errorcode NONE -errorinfo {out of stack space (infinite loop?)
    while execu..."
I don't understand why on some machines it works exactly as expected
and on others it acts the same way Tkinter does when I call functions
directly from outside the Tkinter thread.  Does anyone have any
suggestions?  The full code as appended below.  Thanks in advance.
Code:
[/QUOTE]

import Queue[/QUOTE]
[QUOTE]
class functionQueue:[/QUOTE]
[QUOTE]
    def __init__(self, root = None, timeout = 250):[/QUOTE]
[QUOTE]
        self.functionQueue = Queue.Queue()
        self.root = root
        self.timeout = timeout[/QUOTE]
[QUOTE]
        if(self.root):
            self.pop_function(root)[/QUOTE]
[QUOTE]
    def pop_function(self, root = None):[/QUOTE]
[QUOTE]
        try:
            funcArgList = self.functionQueue.get(block = False)
        except Queue.Empty:
            pass
        else:
            try:
                funcArgList[0](*funcArgList[1])
            except:
                try:
                    print "Failed to call function", funcArgList[0]
                except:
                    print "Failed to call function"[/QUOTE]
[QUOTE]
        if(root):
            root.after(self.timeout, lambda: self.pop_function
(self.root))[/QUOTE]
[QUOTE]
    def add_function(self, function, argList):[/QUOTE]
[QUOTE]
        try:
            self.functionQueue.put([function, argList])
        except:
            pass[/QUOTE]
[QUOTE]
if( __name__ == '__main__'):[/QUOTE]
[QUOTE]
    import Tkinter
    import thread[/QUOTE]
[QUOTE]
    text = Tkinter.Text()
    text.pack()[/QUOTE]
[QUOTE]
    myQueue = functionQueue(text, 50)[/QUOTE]
[QUOTE]
    def gui_loop():
        try:
            text.mainloop()
        except:
            import os
            os._exit(1)[/QUOTE]
[QUOTE]
    thread.start_new_thread(text.mainloop, ())[/QUOTE]
[QUOTE]
    while(True):
        usrInput = raw_input()[/QUOTE]
[QUOTE]
        if(usrInput == "-1"):
            import os
            os._exit(0)[/QUOTE]
[QUOTE]
        myQueue.add_function(text.insert, ['end', usrInput + "\n"])
        myQueue.add_function(text.see, ['end'])[/QUOTE]
[QUOTE]

I can make it work over here by putting the UI into the main thread, as
suggested byhttp://effbot.org/zone/tkinter-threads.htm:

import Queue
import Tkinter
import threading

class FunctionQueue:
    # unchanged

def input_loop():
    while True:
        try:
            usrInput = raw_input()
        except EOFError:
            break
        myQueue.add_function(text.insert, ['end', usrInput + "\n"])
        myQueue.add_function(text.see, ['end'])
    myQueue.add_function(text.quit, [])

if __name__ == '__main__':
    text = Tkinter.Text()
    text.pack()

    myQueue = FunctionQueue(text, 50)
    threading.Thread(target=input_loop).start()
    text.mainloop()

Peter

Peter, thanks for the suggestion. I tried your code exactly on my box
and I still get the same results. As soon as I run the script and
every time I click on the Text box I get tcl::Bgerror ... just like I
mentioned above. I'm fairly certain that I'm not calling Tkinter
functions from any other thread but it's acting as though I am as soon
as I create the input thread.
If I comment out the input loop thread everything is fine but of
course that's not terribly useful as a logging box.
 
P

Peter Otten

Icarus said:
Icarus said:
I'm working on a serial protocol analyzer in python. We have an
application written by someone else in MFC but we need something that
is cross platform. I intended to implement the GUI portion in Tkinter
but am having trouble.
The idea is that I will read messages from the serial port and output
them to a Tkinter Text object initially. Eventually it will have
other functionality but that's it for the short term. I've written
this little test app to experiment with putting things on the GUI via
a Queue which is polled by the Tkinter loop.
On some machines this code works fine and I get whatever I type in
displayed in the Text widget. On others I get errors like this as
soon as I start it running.
error in background error handler:
out of stack space (infinite loop?)
while executing
"::tcl::Bgerror {out of stack space (infinite loop?)} {-code 1 -level
0 -errorcode NONE -errorinfo {out of stack space (infinite loop?)
while execu..."
I don't understand why on some machines it works exactly as expected
and on others it acts the same way Tkinter does when I call functions
directly from outside the Tkinter thread. Does anyone have any
suggestions? The full code as appended below. Thanks in advance.
Code:
[/QUOTE]

import Queue[/QUOTE]
[QUOTE]
class functionQueue:[/QUOTE]
[QUOTE]
def __init__(self, root = None, timeout = 250):[/QUOTE]
[QUOTE]
self.functionQueue = Queue.Queue()
self.root = root
self.timeout = timeout 

def pop_function(self, root = None):[/QUOTE]
[QUOTE]
try:
funcArgList = self.functionQueue.get(block = False)
except Queue.Empty:
pass
else:
try:
funcArgList[0](*funcArgList[1])
except:
try:
print "Failed to call function", funcArgList[0]
except:
print "Failed to call function"[/QUOTE]
[QUOTE]
if(root):
root.after(self.timeout, lambda: self.pop_function
(self.root))[/QUOTE]
[QUOTE]
def add_function(self, function, argList):[/QUOTE]
[QUOTE]
try:
self.functionQueue.put([function, argList])
except:
pass[/QUOTE]
[QUOTE]
if( __name__ == '__main__'):[/QUOTE]
[QUOTE]
import Tkinter
import thread[/QUOTE]
[QUOTE]
text = Tkinter.Text()
text.pack()[/QUOTE]
[QUOTE]
myQueue = functionQueue(text, 50)[/QUOTE]
[QUOTE]
def gui_loop():
try:
text.mainloop()
except:
import os
os._exit(1)[/QUOTE]
[QUOTE]
thread.start_new_thread(text.mainloop, ())[/QUOTE]
[QUOTE]
while(True):
usrInput = raw_input()[/QUOTE]
[QUOTE]
if(usrInput == "-1"):
import os
os._exit(0)[/QUOTE]
[QUOTE]
myQueue.add_function(text.insert, ['end', usrInput + "\n"])
myQueue.add_function(text.see, ['end'])[/QUOTE]
[QUOTE]

I can make it work over here by putting the UI into the main thread, as
suggested byhttp://effbot.org/zone/tkinter-threads.htm:

import Queue
import Tkinter
import threading

class FunctionQueue:
# unchanged

def input_loop():
while True:
try:
usrInput = raw_input()
except EOFError:
break
myQueue.add_function(text.insert, ['end', usrInput + "\n"])
myQueue.add_function(text.see, ['end'])
myQueue.add_function(text.quit, [])

if __name__ == '__main__':
text = Tkinter.Text()
text.pack()

myQueue = FunctionQueue(text, 50)
threading.Thread(target=input_loop).start()
text.mainloop()

Peter

Peter, thanks for the suggestion. I tried your code exactly on my box
and I still get the same results. As soon as I run the script and
every time I click on the Text box I get tcl::Bgerror ... just like I
mentioned above. I'm fairly certain that I'm not calling Tkinter
functions from any other thread but it's acting as though I am as soon
as I create the input thread.
If I comment out the input loop thread everything is fine but of
course that's not terribly useful as a logging box.

http://bugs.python.org/issue3835

Could tcl have been built without thread support on the failing machines?

Peter
 
M

MRAB

Icarus said:
I'm working on a serial protocol analyzer in python. We have an
application written by someone else in MFC but we need something that
is cross platform. I intended to implement the GUI portion in Tkinter
but am having trouble.

The idea is that I will read messages from the serial port and output
them to a Tkinter Text object initially. Eventually it will have
other functionality but that's it for the short term. I've written
this little test app to experiment with putting things on the GUI via
a Queue which is polled by the Tkinter loop.

On some machines this code works fine and I get whatever I type in
displayed in the Text widget. On others I get errors like this as
soon as I start it running.

error in background error handler:
out of stack space (infinite loop?)
while executing
"::tcl::Bgerror {out of stack space (infinite loop?)} {-code 1 -level
0 -errorcode NONE -errorinfo {out of stack space (infinite loop?)
while execu..."


I don't understand why on some machines it works exactly as expected
and on others it acts the same way Tkinter does when I call functions
directly from outside the Tkinter thread. Does anyone have any
suggestions? The full code as appended below. Thanks in advance.

Code:
import Queue

class functionQueue:

def __init__(self, root = None, timeout = 250):

self.functionQueue = Queue.Queue()
self.root = root
self.timeout = timeout

if(self.root):
self.pop_function(root)


def pop_function(self, root = None):

try:
funcArgList = self.functionQueue.get(block = False)
except Queue.Empty:
pass
else:
try:
funcArgList[0](*funcArgList[1])
except:
try:
print "Failed to call function", funcArgList[0]
except:
print "Failed to call function"

if(root):
root.after(self.timeout, lambda: self.pop_function
(self.root))

def add_function(self, function, argList):

try:
self.functionQueue.put([function, argList])
except:
pass


if( __name__ == '__main__'):

import Tkinter
import thread

text = Tkinter.Text()
text.pack()

myQueue = functionQueue(text, 50)

def gui_loop():
try:
text.mainloop()
except:
import os
os._exit(1)

thread.start_new_thread(text.mainloop, ())

while(True):
usrInput = raw_input()

if(usrInput == "-1"):
import os
os._exit(0)

myQueue.add_function(text.insert, ['end', usrInput + "\n"])
myQueue.add_function(text.see, ['end'])
Only an idea, but:

myQueue = functionQueue(text, 50)

runs in the main thread, so the pop_function method is calling Tkinter
methods in the main thread every 50ms.

But:

thread.start_new_thread(text.mainloop, ())

is running the Tkinter main loop in another thread.

You might be experiencing a race condition, so different timings on
different machines might cause a problem to appear sooner on one than
another.
 
I

Icarus

Icarus said:
Icarus wrote:
I'm working on a serial protocol analyzer in python.  We have an
application written by someone else in MFC but we need something that
is cross platform.  I intended to implement the GUI portion in Tkinter
but am having trouble.
The idea is that I will read messages from the serial port and output
them to a Tkinter Text object initially.  Eventually it will have
other functionality but that's it for the short term.  I've written
this little test app to experiment with putting things on the GUI via
a Queue which is polled by the Tkinter loop.
On some machines this code works fine and I get whatever I type in
displayed in the Text widget.  On others I get errors like this as
soon as I start it running.
error in background error handler:
out of stack space (infinite loop?)
while executing
"::tcl::Bgerror {out of stack space (infinite loop?)} {-code 1 -level
0 -errorcode NONE -errorinfo {out of stack space (infinite loop?)
while execu..."
I don't understand why on some machines it works exactly as expected
and on others it acts the same way Tkinter does when I call functions
directly from outside the Tkinter thread.  Does anyone have any
suggestions?  The full code as appended below.  Thanks in advance.
Code:
import Queue 
class functionQueue: 
def __init__(self, root = None, timeout = 250): 
self.functionQueue = Queue.Queue()
self.root = root
self.timeout = timeout 
if(self.root):
self.pop_function(root) 
def pop_function(self, root = None): 
try:
funcArgList = self.functionQueue.get(block = False)
except Queue.Empty:
pass
else:
try:
funcArgList[0](*funcArgList[1])
except:
try:
print "Failed to call function", funcArgList[0]
except:
print "Failed to call function" 
if(root):
root.after(self.timeout, lambda: self.pop_function
(self.root)) 
def add_function(self, function, argList): 
try:
self.functionQueue.put([function, argList])
except:
pass 
if( __name__ == '__main__'): 
import Tkinter
import thread 
text = Tkinter.Text()
text.pack() 
myQueue = functionQueue(text, 50) 
def gui_loop():
try:
text.mainloop()
except:
import os
os._exit(1) 
thread.start_new_thread(text.mainloop, ()) 
while(True):
usrInput = raw_input() 
if(usrInput == "-1"):
import os
os._exit(0) 
myQueue.add_function(text.insert, ['end', usrInput + "\n"])
myQueue.add_function(text.see, ['end'])
I can make it work over here by putting the UI into the main thread, as
suggested byhttp://effbot.org/zone/tkinter-threads.htm:
import Queue
import Tkinter
import threading
class FunctionQueue:
# unchanged
def input_loop():
while True:
try:
usrInput = raw_input()
except EOFError:
break
myQueue.add_function(text.insert, ['end', usrInput + "\n"])
myQueue.add_function(text.see, ['end'])
myQueue.add_function(text.quit, [])
if __name__ == '__main__':
text = Tkinter.Text()
text.pack()
myQueue = FunctionQueue(text, 50)
threading.Thread(target=input_loop).start()
text.mainloop()
Peter
Peter, thanks for the suggestion.  I tried your code exactly on my box
and I still get the same results.  As soon as I run the script and
every time I click on the Text box I get tcl::Bgerror ... just like I
mentioned above.  I'm fairly certain that I'm not calling Tkinter
functions from any other thread but it's acting as though I am as soon
as I create the input thread.
If I comment out the input loop thread everything is fine but of
course that's not terribly useful as a logging box.

http://bugs.python.org/issue3835

Could tcl have been built without thread support on the failing machines?

Peter

You had it Peter. I tried the "import pydoc pydoc.gui()" in the bug
report you referenced and the same thing described there occurred.
After recompiling tcl/tk with --threads-enabled and replacing the
slackware default packages with those everything is working as I
expected. Thanks for the help.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,074
Latest member
StanleyFra

Latest Threads

Top