1

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

我查看了文档,但似乎缺少循环部分:

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 有效,try 也有效,但回调只运行一次

 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"

也就是说,当事件发生但没有回调时,打印会一次又一次地重复。

4

1 回答 1

3

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

您应该将其Deferred视为具有两种不同类型(尽管高度相关)的用途。

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

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

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

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

Deferred.callback是您用来向Deferred. 而且,重要的是,aDeferred只能给出一个结果。每个Deferred实例代表单个操作的完成或单个事件的发生。

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

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

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因此可能没有意义Deferred)。

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

由于通过 this 方法beginAsynchronousTest传递给返回的addCallback方法,因此它将使用初始化的协议实例(在本例中为)调用。 将在它的实现给出结果后立即调用 - 换句话说,一旦建立连接就会调用它。建立 TCP 连接需要一些任意时间,因为它涉及通过任意网络链接与任意其他计算机交换数据 - 不知道这些资源需要多长时间才能完成它们的连接设置部分。DeferredClientCreator.connectTCPClientCreatorModbusClientProtocolbeginAsynchronousTestDeferredClientCreator

一旦beginAsynchronousTest被调用,您就有一个连接 - 由ModbusClientProtocol传递给它的实例表示。这是您的程序中可以开始执行多项操作的点(例如,每次单击按钮时执行某项操作)。

此时,Deferred您的程序开始(myobjectx在上面的代码片段中调用)已完成并且不再有用或有趣,因此您将不再使用它。

相反,您将调用ModbusClientProtocol(read_coilswrite_coil您想做的任何其他方法) 的方法。这些方法中的每一个都可能返回一个全新的Deferred表示该特定操作的结果。您将希望addCallback与这些一起使用以了解它们的结果。

人们经常绊倒的另一个地方是弄清楚如何进行这些额外的方法调用。如果您要向主体添加代码,beginAsynchronousTest那么如何执行此操作相当简单:

reading = client.read_coils(1, 1)

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

关键是要记住,只要您有对连接的引用,您就可以使用它。在beginAsynchronousTest你的体内有一个对它的引用——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属性将不再有用。它仍然是对ModbusClientProtocol连接的对象的引用,但是由于该协议实例不再具有连接,因此很难做任何有用的事情。当连接丢失或至少开始再次忽略按钮按下时,您可能希望重新禁用用户界面。

另外,请注意ClientCreator实际来自twisted.internet.protocolnot MODBUSLIB.protocol

于 2013-10-14T12:48:04.363 回答