0
votes

At this point I'm still a noob when it comes to GUI and network programming so I'm hoping this will be a very simple fix. I've got a very basic understanding of the tkinter and asyncore modules having built a handful of programs in each of them, however I'm having trouble using both of them together in a program. I put together an entire UI only to find out that I could not achieve any significant asynchronous networking functionality. For the sake of simplicity, I deconstructed the program into its simplest form to illustrate the basic problem I'm having. Heres the code:

from Tkinter import *
import asyncore, socket

class Application(object):
    def __init__(self, root):
        mainFrame = Frame(root)
        mainFrame.grid(column=1, row=1, columnspan=3, rowspan=1)
        mainButton = Button(mainFrame, text='Click', command=self.makeSocket)
        mainButton.grid(column=2, row=1, columnspan=1, rowspan=1, pady=7, padx=40)

    def makeSocket(self):
        clientSocket()

class clientSocket(asyncore.dispatcher):
    def __init__(self):
        asyncore.dispatcher.__init__(self)
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.connect(("XXX.XXX.XXX.XXX", XXXX))
        print 'init works'

    def handle_connect(self):
        print 'connect works'

root = Tk()
myApp = Application(root)
root.after_idle(asyncore.loop) 
root.mainloop()

So when I run the program and click the button, I get the string 'init works', indicating that the clientSocket object is initialized and the connection is made successfully. However, the handle_connect method doesn't run. And if I implement the handle_read method and execute a command on the server(to send data back to the client) this method isn't called either. I'm thinking that there is some general problem that is preventing the asyncore loop from running on its own. I realize that tkinters event loop could be the culprit, but I was under the impression that the after_idle method would allow non-Tkinter events to be processed while the GUI is idle. Is it the tkinter event loop that is still causing problems or could it be something else?

2
For what it's worth, asyncore is an extremely old and crufty Python technology that, while still in the Standard Library for compatibility, is a fairly poor choice for modern projects. Try Twisted Python if you want a better async framework. (And I don't recommend Twisted lightly!) - Brandon Rhodes

2 Answers

2
votes

There are several problems here, and I'm not sure which one it is.

asyncore.loop is a function that never returns, when things are working properly. root.mainloop is probably a function that never returns until you close the window. So things are likely to go wrong because at some point one loop will be starved by the other for some period of time.

(Incidentally, this is why I dislike frameworks that attempt to make their usage easier by replacing the main loop and replacing it with an event-driven system - it works great until you need to use two or more of these systems together, at which point things can get messy.)

However, you can limit the number of times that asyncore.loop will iterate. Try this instead:

def poll_asyncore_once():
    asyncore.loop(count=1)

root.after_idle(poll_asyncore_once) 

You might want to add a timeout value to the loop call too, something less than a second.

However, I would have still thought that the connection would have gone through eventually even if the GUI did end up starved of events as a result of you entering the asyncore loop. This implies something else has gone wrong, and it's possible that asyncore is raising an exception in the connect() method and TK is swallowing it. Try putting an exception handler in clientSocket.init and see how you go.

0
votes

See this recipe by Jacob Hallén showing how to use asyncore and Tkinter together (basically by means of a threading trick). (It's also expanded as recipe 9.6 in the Python Cookbook's first printed edition, and as 11.4 in the second edition).