fix threading issue on reactor stop, and add server and client example to documentation

This commit is contained in:
Thomas Hansen 2011-08-31 19:36:34 -05:00
parent 609b03bb37
commit d57f58d5c3
2 changed files with 131 additions and 33 deletions

View File

@ -13,39 +13,136 @@ passed on the the threadedselect reactors interleave function, these
are the arguments one would usually pass to twisted's reactor.startRunning are the arguments one would usually pass to twisted's reactor.startRunning
.. warning:: .. warning::
Unlike the default twisted reactor, the installed reactor will not handle Unlike the default twisted reactor, the installed reactor will not handle
any signals unnless you set the 'installSignalHandlers' keyword argument any signals unnless you set the 'installSignalHandlers' keyword argument
to 1 explicitly. This is done to allow kivy to handle teh signals as to 1 explicitly. This is done to allow kivy to handle teh signals as
usual, unless you specifically want the twisted reactor to handle the usual, unless you specifically want the twisted reactor to handle the
signals (e.g. SIGINT). signals (e.g. SIGINT).
Here is a brief example kivy application that has a twisted server running Here is a brief example kivy application that has a twisted server running
inside it:: inside it. It based mostly on simple Echo example from the twisted docs,
''' which you can find here:
Test app using the Echo server from the twisted documentation
you can find simpleserv.py and simpleclient.py here:
http://twistedmatrix.com/documents/current/core/examples/simpleserv.py http://twistedmatrix.com/documents/current/core/examples/simpleserv.py
http://twistedmatrix.com/documents/current/core/examples/simpleclient.py http://twistedmatrix.com/documents/current/core/examples/simpleclient.py
'''
echo_server_app.py::
#install_twisted_rector must be called before importing and using the reactor
from kivy.support import install_twisted_reactor
install_twisted_reactor()
from twisted.internet import reactor
from twisted.internet import protocol
class EchoProtocol(protocol.Protocol):
def dataReceived(self, data):
response = self.factory.app.handle_message(data)
self.transport.write(response)
class EchoFactory(protocol.Factory):
protocol = EchoProtocol
def __init__(self, app):
self.app = app
from kivy.app import App from kivy.app import App
from kivy.uix.button import Button from kivy.uix.label import Label
class TwistedTestApp(App): class TwistedServerApp(App):
def build(self): def build(self):
self.setup_twisted_server() self.label = Label(text="server started\n")
return Button(text="Hello" ) reactor.listenTCP(8000, EchoFactory(self))
return self.label
def setup_twisted_server(self): def handle_message(self, msg):
from kivy.support import install_twisted_reactor response = "echo: <%s>" % msg
install_twisted_reactor() if msg == "ping": response = "pong"
if msg == "plop": response = "kivy rocks"
self.label.text = "received: %s\n" % msg
self.label.text += "responded: %s\n" % response
return response
from twisted.internet import reactor, protocol
from simpleserv import Echo
factory = protocol.ServerFactory()
factory.protocol = Echo
reactor.listenTCP(8000, factory)
if __name__ == '__main__': if __name__ == '__main__':
setup_twisted_server() TwistedServerApp().run()
TwistedTestApp().run()
echo_client_app.py::
#install_twisted_rector must be called before importing the reactor
from kivy.support import install_twisted_reactor
install_twisted_reactor()
#A simple Client that send messages to the echo server
from twisted.internet import reactor, protocol
class EchoClient(protocol.Protocol):
def connectionMade(self):
self.factory.app.print_message("connected successfully")
self.factory.app.echo_transport = self.transport
def dataReceived(self, data):
self.factory.app.print_message(data)
class EchoFactory(protocol.ClientFactory):
protocol = EchoClient
def __init__(self, app):
self.app = app
def clientConnectionLost(self, conn, reason):
self.app.print_message("connection lost")
def clientConnectionFailed(self, conn, reason):
self.app.print_message("connection failed")
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.boxlayout import BoxLayout
#A simple kivy App, with a textbox to enter messages, and
#a large label to display all the messages received from
#the server
class TwistedClientApp(App):
echo_transport = None
def build(self):
root = self.setup_gui()
self.connect_to_server()
return root
def setup_gui(self):
self.textbox = TextInput(size_hint_y=.1, multiline=False)
self.textbox.bind(on_text_validate=self.send_message)
self.label = Label(text='connecting...\n')
self.layout = BoxLayout(orientation='vertical')
self.layout.add_widget(self.label)
self.layout.add_widget(self.textbox)
return self.layout
def connect_to_server(self):
reactor.connectTCP('localhost', 8000, EchoFactory(self))
def send_message(self, *args):
msg = self.textbox.text
if msg and self.echo_transport:
self.echo_transport.write(str(self.textbox.text))
self.textbox.text = ""
def print_message(self, msg):
self.label.text += msg + "\n"
if __name__ == '__main__':
TwistedClientApp().run()
Run echo_server_app.py first, and then launch echo_client_app.py. The server
will, reply with simple echo messages to anything the client app sends, when
you hit enter after typing something in teh textbox.

View File

@ -65,25 +65,26 @@ def install_twisted_reactor(*args, **kwargs):
#now we can import twisted reactor as usual #now we can import twisted reactor as usual
from twisted.internet import reactor from twisted.internet import reactor
from kivy.base import EventLoop from kivy.base import EventLoop
from kivy.core.window import Window
from kivy.clock import Clock from kivy.clock import Clock
from kivy.logger import Logger from kivy.logger import Logger
import thread, threading
#hook up rector to our reactor wake function #hook up rector to our reactor wake function
def reactor_wake(twisted_loop_next): def reactor_wake(twisted_loop_next):
'''called whenever twisted needs to do work '''called whenever twisted needs to do work
''' '''
twisted_loop_next()
def call_twisted(*args):
Logger.trace("Entering Twisted event loop")
twisted_loop_next()
Clock.schedule_once(call_twisted, -1)
reactor.interleave(reactor_wake, *args, **kwargs) reactor.interleave(reactor_wake, *args, **kwargs)
#make sure twisted reactor is shutdown if eventloop exists #make sure twisted reactor is shutdown if eventloop exists
def reactor_stop(*args): def reactor_stop(*args):
'''will shutdown the twisted reactor main loop''' '''will shutdown the twisted reactor main loop
from twisted.internet import reactor '''
Logger.debug("Shutting down twisted reactor") if not reactor._stopped:
reactor._mainLoopShutdown() Logger.debug("Shutting down twisted reactor" )
reactor.stop()
if threading.current_thread() != reactor.workerThread:
thread.exit()
EventLoop.add_stop_callback(reactor_stop) EventLoop.add_stop_callback(reactor_stop)