我想這裏的一個誤解是關於如何使用Deferred
的實例。
您應該認爲Deferred
有兩種不同的(儘管高度相關)用途。
一個用途是能夠從一些知道如何注意事件已經發生的代碼發佈事件,其他代碼可能有興趣知道事件已經發生。
此用法的一個示例是ClientCreator.connectTCP
:此API的實現知道TCP連接嘗試何時成功(或失敗)並使用Deferred
將此信息發佈到其他代碼。這樣使用Deferred
的代碼實際上是實例化Deferred
(例如,d = Deferred()
)並且稍後使用Deferred.callback
和Deferred.errback
的代碼。
Deferred
的另一個用途是允許對發生過的事件感興趣的代碼瞭解這些事件已經發生。例如,這是您的應用程序需要TCP連接以交換數據 - 但需要等待一個正在設置之前,它可以繼續。這樣使用Deferred
的代碼是使用Deferred.addCallback
或Deferred.addErrback
(在最近版本的Twisted中也是Deferred.cancel
)的代碼。
Deferred.addCallback
是您用來指定在Deferred
最終獲得結果時要運行的代碼的API。
Deferred.callback
是您用於將結果提供給Deferred
的API。而且,重要的是,一個Deferred
只能得到一個結果。每個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
的一個參數。
beginAsynchronousTest
由於被傳遞給由ClientCreator.connectTCP
返回的Deferred
的addCallback
方法,這意味着它將與ClientCreator
初始化所用的協議的實例(在此情況下,ModbusClientProtocol
)被調用。換句話說,它將盡快建立連接稱爲 - beginAsynchronousTest
將作爲Deferred
給出其結果由ClientCreator
實施儘快調用。建立一個TCP連接需要一定的時間,因爲它涉及通過任意網絡鏈路與任意其他計算機交換數據 - 不知道這些資源需要多長時間才能完成部分連接設置。
一旦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)
注意如何ButtonModbusSomething
的client
屬性開始了爲None
和buttonClicked
需要如何檢查這種情況。如上所述,建立連接可能需要一些時間,並且您需要知道等待調用_connected
需要多長時間。此檢查可確保如果在連接存在之前單擊某個按鈕,該事件將被忽略(您可能希望更好地處理該事件 - 例如,通過以禁用狀態啓動用戶界面,然後僅在連接建立)。
此外,我遺漏了代碼,你可能也想處理你的連接丟失。發生這種情況時,client
屬性不再有用。它仍然是連接的ModbusClientProtocol
的參考,但由於該協議實例不再有連接,所以很難做任何有用的操作。當連接丟失或者至少開始再次忽略按鈕時,您可能需要重新禁用用戶界面。
此外,請注意,ClientCreator
實際上來自twisted.internet.protocol
而不是MODBUSLIB.protocol
。
我很困惑。你想每次按下按鈕時運行一個回調嗎?或者你是否想要單擊一次按鈕導致重複調用回調?如果是後者,什麼決定了多少次以及回調被重新調用的時間? –
我想要一個每次按下按鈕時都會運行的回調,只需單擊一下執行關係即可。但現在只有第一次點擊工作,接下來會被忽略。 – user2239318
這個問題取決於你正在使用的GUI庫。沒有這些信息很難回答。 –