From d57f58d5c370d39d3aa72ba430b83d25f0fd75d1 Mon Sep 17 00:00:00 2001 From: Thomas Hansen Date: Wed, 31 Aug 2011 19:36:34 -0500 Subject: [PATCH] fix threading issue on reactor stop, and add server and client example to documentation --- doc/sources/guide/other-frameworks.rst | 145 +++++++++++++++++++++---- kivy/support.py | 19 ++-- 2 files changed, 131 insertions(+), 33 deletions(-) diff --git a/doc/sources/guide/other-frameworks.rst b/doc/sources/guide/other-frameworks.rst index 2d2f41118..771461709 100644 --- a/doc/sources/guide/other-frameworks.rst +++ b/doc/sources/guide/other-frameworks.rst @@ -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 .. warning:: -Unlike the default twisted reactor, the installed reactor will not handle -any signals unnless you set the 'installSignalHandlers' keyword argument -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 -signals (e.g. SIGINT). + Unlike the default twisted reactor, the installed reactor will not handle + any signals unnless you set the 'installSignalHandlers' keyword argument + 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 + signals (e.g. SIGINT). Here is a brief example kivy application that has a twisted server running -inside it:: - ''' - Test app using the Echo server from the twisted documentation - you can find simpleserv.py and simpleclient.py here: +inside it. It based mostly on simple Echo example from the twisted docs, +which you can find here: http://twistedmatrix.com/documents/current/core/examples/simpleserv.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.uix.button import Button + from kivy.uix.label import Label - class TwistedTestApp(App): + class TwistedServerApp(App): def build(self): - self.setup_twisted_server() - return Button(text="Hello" ) + self.label = Label(text="server started\n") + reactor.listenTCP(8000, EchoFactory(self)) + return self.label - def setup_twisted_server(self): - from kivy.support import install_twisted_reactor - install_twisted_reactor() + def handle_message(self, msg): + response = "echo: <%s>" % msg + 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__': - setup_twisted_server() - TwistedTestApp().run() + TwistedServerApp().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. + diff --git a/kivy/support.py b/kivy/support.py index e3c16ea14..034df8409 100644 --- a/kivy/support.py +++ b/kivy/support.py @@ -65,25 +65,26 @@ def install_twisted_reactor(*args, **kwargs): #now we can import twisted reactor as usual from twisted.internet import reactor from kivy.base import EventLoop + from kivy.core.window import Window from kivy.clock import Clock from kivy.logger import Logger + import thread, threading #hook up rector to our reactor wake function def reactor_wake(twisted_loop_next): '''called whenever twisted needs to do work ''' - - def call_twisted(*args): - Logger.trace("Entering Twisted event loop") - twisted_loop_next() - Clock.schedule_once(call_twisted, -1) + twisted_loop_next() reactor.interleave(reactor_wake, *args, **kwargs) #make sure twisted reactor is shutdown if eventloop exists def reactor_stop(*args): - '''will shutdown the twisted reactor main loop''' - from twisted.internet import reactor - Logger.debug("Shutting down twisted reactor") - reactor._mainLoopShutdown() + '''will shutdown the twisted reactor main loop + ''' + if not reactor._stopped: + Logger.debug("Shutting down twisted reactor" ) + reactor.stop() + if threading.current_thread() != reactor.workerThread: + thread.exit() EventLoop.add_stop_callback(reactor_stop)