Twisted Matrix Callback一次又一次地呼叫

时间:2013-10-07 11:42:05

标签: python loops callback twisted

我需要当用户点击按钮时,该按钮触发回调并传递一些参数,每次参数都会不同。

我查看了文档,但看起来缺少循环部分:

Looping

A common form of dependency is needing to perform the asynchronous operation all over again. The canonical example of this an HTTP redirect: when the callback for a deferred from a page request is returned, it could be the result, or it could be an empty body with the Location HTTP header set, in which case you simply perform the operation over again.

[ here is the HTTP redirect example. It also should have pictures. ]

if是否正常,尝试也是,但回调只运行一次

 if happen this:
  try:
     print "I'm here!"
     myobjectx.addCallback(test,x,y,z)  
     myobjectx.callback()

  except:
     ...

只是想知道这是如何工作的:

1) create the myobject that do  nothing for now 
2) when an event is fired prepare the callback for the myobject e execute it 
3) how can I redo the callback next time the event happen again?

我正在查看异步客户端的pymodbus库示例:

https://pymodbus.readthedocs.org/en/latest/examples/asynchronous-client.html

我有2个文件:

MAINPROCESS
MODBUSLIB

来自MAINPROCESS我打电话

myobjectx = MODBUSLIB.protocol.ClientCreator(reactor, ModbusClientProtocol
        ).connectTCP("localhost", Defaults.Port)

然后在由if:

触发的函数中
if ('Gigiisclicked' in existkeys):

      myobjectx.addCallback(beginAsynchronousTest)
      myobjectx.callback(beginAsynchronousTest)
      print "executed"

这就是,当事件发生但回调号为

时,会一次又一次地重复打印

1 个答案:

答案 0 :(得分:3)

我认为这里的一个误解是关于如何使用Deferred的实例。

你应该认为Deferred有两种不同的(虽然高度相关)用途。

一种用途是能够从某些代码发布事件,这些代码知道如何注意事件发生在可能有兴趣知道事件发生的其他代码中。

此用法的一个示例是ClientCreator.connectTCP:此API的实现知道TCP连接尝试何时成功(或失败)并使用Deferred将此信息发布到其他代码。像这样使用Deferred的代码是实际实例化Deferred的代码(例如d = Deferred()),后来使用Deferred.callbackDeferred.errback

Deferred的另一个用途是允许对已经发生的事件感兴趣的代码了解这些事件已经发生。例如,这是一个想要TCP连接以交换数据的应用程序 - 但需要在设置之前等待才能继续。像这样使用Deferred的代码是在最近的Twisted版本中使用Deferred.addCallbackDeferred.addErrbackDeferred.cancel)的代码。

Deferred.addCallback是您用来指定Deferred最终获得结果时要运行的代码的API。

Deferred.callback是您用于向Deferred提供结果的API。而且,重要的是,Deferred只能得到一个结果。每个Deferred实例表示单个操作的完成或单个事件的发生。

有一些例外和一些进一步的细微之处,但一个好的经验法则是,如果您的代码没有实例化Deferred,那么您的代码不应使用其callback(或{ {1}})方法。调用其中一个是errback创建的任何代码的工作。

鉴于此,我希望很清楚在此代码中使用Deferred API存在一些需要解决的问题:

Deferred

最直接的,你不应该在这里打电话给myobjectx = MODBUSLIB.protocol.ClientCreator(reactor, ModbusClientProtocol ).connectTCP("localhost", Defaults.Port) ... if ('Gigiisclicked' in existkeys): myobjectx.addCallback(beginAsynchronousTest) myobjectx.callback(beginAsynchronousTest) print "executed" 。那个myobjectx.callback的工作(除此之外,ClientCreator.connectTCP可能因此beginAsynchronousTest而无法理解。< / p>

相反,我认为您希望使用Deferred最终为您创建的ModbusClientProtocol实例的方法。在您链接到的示例中,请注意ClientCreator.connectTCP被定义为接受一个名为beginAsynchronousTest的参数。

由于client被传递给beginAsynchronousTest返回的addCallback的{​​{1}}方法,这意味着它将使用{{1}协议的实例进行调用用(在这种情况下,Deferred)初始化。一旦ClientCreator.connectTCP通过ClientCreator的实施得到结果,ModbusClientProtocol将被调用 - 换句话说,一旦建立连接就会调用它。设置TCP连接需要花费一定的时间,因为它涉及通过任意网络链接与任意其他计算机交换数据 - 这些资源无需知道完成部分连接设置所需的时间。

调用beginAsynchronousTest后,您会有一个连接 - 由传入其中的Deferred实例表示。这是您的程序中可以开始执行多项操作的要点(例如,每次单击按钮时都要执行某些操作)。

此时,ClientCreator您的程序开始时(在上面的代码片段中称为beginAsynchronousTest)已完成,不再有用或有趣,因此您再也无法使用它了

相反,您将调用ModbusClientProtocolDeferredmyobjectx或其他任何您想要做的事情的方法。这些方法中的每一种都可能返回一个全新的ModbusClientProtocol,表示该特定操作的结果。您希望read_coils使用这些内容以了解其结果。

人们经常绊倒的另一个地方是弄清楚如何进行这些额外的方法调用。如果您要将代码添加到write_coil的正文中,那么如何执行此操作非常简单:

Deferred

但是,我怀疑您不想将按钮处理代码添加到addCallback的正文中。相反,您可能在程序中的其他位置有一个事件处理程序,只要按下按钮就会调用该事件处理程序。幸运的是,处理这种情况并不复杂。

关键是要记住,只要您有连接的引用,您就可以使用它。在beginAsynchronousTest的正文中,你有一个对它的引用 - reading = client.read_coils(1, 1) 参数。您也可以将此引用提供给程序的其他部分:在程序的必要部分共享的对象上设置属性是一种常见的,相当好的方法。

beginAsynchronousTest

请注意beginAsynchronousTest的{​​{1}}属性如何以client开头,以及class ButtonModbusSomething(object): def __init__(self): self.client = None def connect(self): creator = MODBUSLIB.protocol.ClientCreator(reactor, ModbusClientProtocol) connecting = creator.connectTCP("localhost", Defaults.Port) connecting.addCallback(self._connected) connecting.addErrback(log.err) def _connected(self, client): self.client = client def buttonClicked(self, existkeys): if self.client is not None: if "Gigiisclicked" in existkeys: self.client.read_coil(1, 1) 如何检查此案例。如上所述,设置连接可能需要一些时间,这是您知道等待client被调用多长时间的唯一方式。此检查确保如果在存在连接之前单击按钮,则忽略该事件(您可能希望更好地处理此事件 - 例如,从处于禁用状态的用户界面开始,然后仅在建立连接)。

此外,我还遗漏了您可能还希望处理丢失连接的代码。发生这种情况时,ButtonModbusSomething属性不再有用。它仍然是对已连接的None的引用,但由于该协议实例不再具有连接,因此很难做任何有用的事情。您可能希望在连接丢失时重新禁用用户界面,或者至少再次忽略按钮按下。

另请注意,buttonClicked实际上来自_connected而不是client