Hi Glyph, Thank you so much for your detailed explanation. You are a life saver!
Best, Jessica 2015-05-01 3:39 GMT+08:00 Glyph Lefkowitz <gl...@twistedmatrix.com>: > > On Apr 29, 2015, at 12:33 AM, Jessica Tsui <jesadj...@gmail.com> wrote: > > Hi, it's me again. I am still working on the kivy app, and now I am trying > to add a function to the app - the server side has two seconds to decide if > he wants to alter the original data sent from one client to other clients. > If he would like to do so, he has to press a button. If he does not press > the button in 2 seconds, the original data will be sent to other clients > automatically. > > Right now I am trying to achieve that with the following code - by calling > the MultiClientEcho().dataReceived(msg, "censored") line in the function as > if MultiClientEcho received another data, however once i click the button, > the program crashed and said (MultiClientEcho().dataReceived(msg, > "censored") > TypeError: __init__() takes exactly 3 arguments (1 given)) > > > The first problem here is that "MultiEchoClient()" means "create a *new* > MultiEchoClient". > Since MultiEchoClient.__init__ takes "factory" and "app" parameters, you > tried to create it with 1 parameter (just "self", which is passed > implicitly) instead of the required 3 (self, factory, app). > > The second problem, once you've addressed that, is that you almost > certainly don't want to create a new MultiEchoClient :). It's not clear to > me *which* MultiEchoClient you are trying to send this message to. > > The third problem is that you should never call dataReceived yourself. > dataReceived is a method invoked by Twisted to tell your protocol that > data has arrived. > > The fourth problem is that you're treating dataReceived as delivering a > discrete message. It doesn't; it delivers a segment of some data in the > stream coming from a client. This is our most popular FAQ; basically, you > need to use a NetstringReceiver or something to ensure you're getting > complete messages in dataReceived: > https://twistedmatrix.com/trac/wiki/FrequentlyAskedQuestions#Whyisprotocol.dataReceivedcalledwithonlypartofthedataIcalledtransport.writewith > > Hopefully once you've addressed all these it will work more like you want > it to :). > > I wonder how can I fix this and achieve the function I am aiming at? > > > I've put other comments inline in the code below. > > import kivy > from kivy.app import App > from kivy.uix.label import Label > from kivy.uix.scatter import Scatter > from kivy.uix.boxlayout import BoxLayout > from kivy.uix.scrollview import ScrollView > from kivy.uix.button import Button > from kivy.graphics.vertex_instructions import Rectangle > from kivy.graphics.context_instructions import Color > from kivy.graphics.instructions import Instruction > from kivy.base import runTouchApp > from kivy.lang import Builder > import socket > > You don't actually use "socket" anywhere in this module so you don't need > to import it :). > > from kivy.core.window import Window > import pygame > import random > from kivy.support import install_twisted_reactor > install_twisted_reactor() > from twisted.internet import protocol, defer > from time import sleep > from twisted.internet import reactor, task > from twisted.protocols.basic import LineReceiver > from twisted.internet.protocol import Protocol, Factory > > censored = 0 > > class MultiClientEcho(protocol.Protocol): > def __init__(self, factory, app): > self.factory = factory > self.app = app > > def connectionMade(self): > self.factory.clients.append(self) > > def dataReceived(self, data): > > storedmessage = self.factory.app.handle_message(data) > > global censored > > Rather than making this a global variable, consider putting it (as with > "app" and with "clients") onto the factory, so you can instantiate multiple > MultiClientEchoFactory instances in one process. > > def f(data): > for client in self.factory.clients: > > This is a bit too much indentation - you should stick to 4 spaces per > indent for stylistic reasons :). > > client.transport.write(data) > print "this will run in 1 sec after it's scheduled: %s" % > data > > if censored == 0 and storedmessage: > reactor.callLater(2, f, data) > > You're not hanging on to the result of this callLater call, which means > you are giving up any way of stopping this call from happening in the > future. If you want to allow the caller to cancel it, note that > reactor.callLater returns an IDelayedCall, which has a "cancel()" method > that stops it from happening if it hasn't happened yet. See the API > documentation here; > https://twistedmatrix.com/documents/15.1.0/api/twisted.internet.interfaces.IReactorTime.callLater.html > > > # client.transport.write(data) > elif censored == 1: > reactor.callLater(0, f, 'censored') > censored == 0 > > I think maybe you mean "censored = 0" here? "censored == 0" just means > "compare censored to 0" which will create a True or False value but > otherwise do nothing; in this context, it pretty much means "do nothing". > > > def connectionLost(self, reason): > self.factory.clients.remove(self) > > > class MultiClientEchoFactory(protocol.Factory): > protocol = MultiClientEcho > > def __init__(self, app): > self.clients = [] > self.app = app > > def buildProtocol(self, addr): > return MultiClientEcho(self, self.app) > > > class ServerApp(App): > def build(self): > self.label = Label(text="server started\n") > > > self.approve_btn = Button(text="approve") > # self.approve_btn.bind(on_release=self.send_message) > self.banned_btn = Button(text="banned") > self.banned_btn.bind(on_release=self.banned_message) > self.layout = BoxLayout(orientation='vertical', spacing=10) > > reactor.listenTCP(8000, MultiClientEchoFactory(self)) > > Rather than calling listenTCP directly, you should be using some kind of > Endpoint here; TCP4ServerEndpoint if you just want to hard code port 8000, > or serverFromString if you want to allow your user to customize it. See > this document: > https://twistedmatrix.com/documents/15.1.0/core/howto/endpoints.html > > self.layout.add_widget(self.label) > self.layout.add_widget(self.banned_btn) > > return self.layout > > def banned_message(self, msg): > global censored > censored = 1 > self.label.text += "censored\n" > MultiClientEcho().dataReceived(msg, "censored") > print censored > return censored > > def handle_message(self, msg): > self.label.text += "%s\n" % msg > return msg > > > if __name__ == '__main__': > ServerApp().run() > > for i in range(0,1): > print 1-i > sleep(0.1) > > > > > Thanks again for using Twisted! > > -g > > > _______________________________________________ > Twisted-Python mailing list > Twisted-Python@twistedmatrix.com > http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-python > >
_______________________________________________ Twisted-Python mailing list Twisted-Python@twistedmatrix.com http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-python