views:

82

answers:

2

I'm trying to build a Python application using pyGTK, treads, and sockets. I'm having this weird error, but given all the modules involved, I'm not entirely sure where the error is. I did a little debugging with some print statements to narrow things down a bit and I think the error is somewhere in this snippet of code:

    self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    self.sock.connect(("localhost", 5005))
    self.collectingThread = threading.Thread(target=self.callCollect)
    self.collectingThread.daemon = True
    self.collectingThread.start()

def callCollect(self):
    gobject.timeout_add(500, self.collectData)

def collectData(self):
    print "hello"
    try:
        print self.sock.recv(1024)
    except:
        print "except"
    print "return"
    return True

So basically what I'm trying to do is setup a socket, connect to a "server" script (which is really just another python script running locally), and create a separate thread to collect all incoming data from the server script. This thread is set to run the collectData method every 500 milliseconds.

After inserting the print statements into the collectData method here is what I notice when running the program:

-Initially the GUI is fully functional

-then the following is printed in the terminal:

hello
**all data received from server script and printed here**
return
hello

-after the text is printed in the terminal, the GUI becomes completely nonfunctional (buttons cant be pressed, etc.) and I have to force quit to close the application

What seems to be happening is that the thread prints "hello", prints the data from the server, and prints "return". 500 milliseconds later, it runs the collectData method again, prints "hello", then tries to print data from the server. However, because there is no data left it raises an exception, but for some unknown reason it doesn't execute the code in the exception block and everything just hangs from there.

Any idea on what is going wrong?

A: 

No, obviously the sock.recv call blocks because the socket wasn't closed yet and socket receives are blocking by default. Make sure you close the connection at some point.

It would make more sense to run the receive call in a new thread, or else it might block the GUI because your current implementation runs the recv call in the GUI thread (using timeout_add). The way you're currently doing it only makes sense if the answer is received very fast and/or you have to access widgets.

By the way, creating a new thread for calling gobject.timeout_add is totally unnecessary. timeout_add() and idle_add() register the specified callback function and return immediately. The GTK event loop then automatically executes the callback after the timeout (or on idle status for idle_add).

AndiDog
Would it be enough to just set the socket to non-blocking and leave everything in the same thread? And could I leave the socket open (so it could continue to receive data) and close it when the user attempts to close the window?
pythonBOI
@pythonBOI: That would be more of a hack, but works if you don't have a lot of sockets like that. You cannot open an infinite number of sockets, so you should close the socket at some point. I don't know which communication protocol you're using so I can't help you with that.
AndiDog
+1  A: 

timeout_add is scheduling the action to happen on the main thread -- so the recv just blocks the main thread (when it's just waiting for data) and therefore the GUI, so, no exception unless you put a timeout or set the socket to non-blocking.

You need to delegate the receiving to the thread from the scheduled action rather than viceversa to get the effect you're after: have the thread e.g. wait on an event object, and the scheduled action signal that event every 500 milliseconds.

Alex Martelli
It's "timeout_add", not "timeout_call".
AndiDog
@Andi, oops, thanks, editing to fix the thinko;-).
Alex Martelli
How exactly would I explicitly tell the secondary thread to wait on an event object and its corresponding signal? I checked the pyGTK tutorial for different events but couldn't find anything on sockets. Sorry, I'm very new to pyGTK.
pythonBOI
@pythonBOI, I was thinking of Python's `threading` module, not GTK -- see http://docs.python.org/library/threading.html#event-objects -- make a global event object `x` with `x = threading.Event()` before creating the subthread, have the subthread block waiting for `x` by calling `x.wait()`, have the main thread wake up the subthread via `x.set()` (then put the event object back to rest state by calling `x.clear()`). No necessary connection w/sockets or with GTK, just general-purpose Python threading stuff!
Alex Martelli