我认为这里的一个误解是关于如何使用实例Deferred
。
您应该将其Deferred
视为具有两种不同类型(尽管高度相关)的用途。
一种用途是能够从一些知道如何注意到事件已发生的代码发布事件到可能有兴趣知道事件已发生的其他代码。
这种用法的一个示例是ClientCreator.connectTCP
:此 API 的实现知道 TCP 连接尝试何时成功(或失败)并使用 aDeferred
将此信息发布到其他代码。像这样使用Deferred
的代码是实际实例化的代码Deferred
(例如,d = Deferred()
),然后使用Deferred.callback
and Deferred.errback
。
的另一个用途Deferred
是允许对发生的事件感兴趣的代码知道这些事件已经发生。例如,您的应用程序需要一个 TCP 连接以交换数据 - 但需要等待一个正在设置的应用程序才能继续。Deferred
像这样使用的代码是使用或Deferred.addCallback
的代码Deferred.addErrback
(也在Deferred.cancel
最近版本的 Twisted 中)。
Deferred.addCallback
Deferred
是用于指定最终获得结果时要运行的代码的 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 连接需要一些任意时间,因为它涉及通过任意网络链接与任意其他计算机交换数据 - 不知道这些资源需要多长时间才能完成它们的连接设置部分。Deferred
ClientCreator.connectTCP
ClientCreator
ModbusClientProtocol
beginAsynchronousTest
Deferred
ClientCreator
一旦beginAsynchronousTest
被调用,您就有一个连接 - 由ModbusClientProtocol
传递给它的实例表示。这是您的程序中可以开始执行多项操作的点(例如,每次单击按钮时执行某项操作)。
此时,Deferred
您的程序开始(myobjectx
在上面的代码片段中调用)已完成并且不再有用或有趣,因此您将不再使用它。
相反,您将调用ModbusClientProtocol
(read_coils
或write_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.protocol
not MODBUSLIB.protocol
。