Reputation: 2784
I need that when a user click a button, that button fire the callback and pass her some parameters, each time, parameters will be different.
I've looked at documentation but appear that the part for looping is missing:
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. ]
the if is working, the try also, but the callback run just once
if happen this:
try:
print "I'm here!"
myobjectx.addCallback(test,x,y,z)
myobjectx.callback()
except:
...
Just to get the idea of how this work:
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?
I'm looking at the pymodbus library example of async client:
https://pymodbus.readthedocs.org/en/latest/examples/asynchronous-client.html
I've got 2 files:
MAINPROCESS
MODBUSLIB
from MAINPROCESS I call
myobjectx = MODBUSLIB.protocol.ClientCreator(reactor, ModbusClientProtocol
).connectTCP("localhost", Defaults.Port)
then in a function triggered by an if:
if ('Gigiisclicked' in existkeys):
myobjectx.addCallback(beginAsynchronousTest)
myobjectx.callback(beginAsynchronousTest)
print "executed"
and this is, the print is repeated again and again when the event occours but the callback no.
Upvotes: 1
Views: 459
Reputation: 48325
I think one misunderstanding here is about how instances of Deferred
are meant to be used.
You should think of Deferred
as having two different kinds of (albeit highly related) uses.
One use is to be able to publish an event from some code that knows how to notice that events have happened to other code that might be interested to know that the event has happened.
One example of this use is ClientCreator.connectTCP
: the implementation of this API knows when a TCP connection attempt has succeeded (or failed) and uses a Deferred
to publish this information to other code. Code that is using Deferred
like this is code that actually instantiates Deferred
(eg, d = Deferred()
) and that later uses Deferred.callback
and Deferred.errback
.
The other use of Deferred
is to allow code that is interested in events that has happened to learn that those events have happened. For example, this your application that wants a TCP connection in order to exchange data - but needs to wait while one is being set up before it can proceed. Code that is using Deferred
like this is code that uses Deferred.addCallback
or Deferred.addErrback
(also, Deferred.cancel
in recent versions of Twisted).
Deferred.addCallback
is the API you use to specify what code to run when a Deferred
eventually gets a result.
Deferred.callback
is the API that you use to supply a result to a Deferred
. And, importantly, a Deferred
can only ever be given one result. Each Deferred
instance represents the completion of a single operation or the occurrence of a single event.
There are certain some exceptions and some further subtleties but a good rule of thumb is that if your code didn't instantiate the Deferred
then your code should not use its callback
(or errback
) methods. Calling one of those is the job for whatever code created the Deferred
.
Given that, I hope it's clear that the use of Deferred
APIs in this code has some problems that need to be addressed:
myobjectx = MODBUSLIB.protocol.ClientCreator(reactor, ModbusClientProtocol
).connectTCP("localhost", Defaults.Port)
...
if ('Gigiisclicked' in existkeys):
myobjectx.addCallback(beginAsynchronousTest)
myobjectx.callback(beginAsynchronousTest)
print "executed"
Most directly, you shouldn't be calling myobjectx.callback
here. That's ClientCreator.connectTCP
's job (on top of that, beginAsynchronousTest
probably doesn't make sense as a result for this Deferred
to have).
Instead, I think you want to use methods of the ModbusClientProtocol
instance that ClientCreator.connectTCP
will eventually create for you. In the example you linked to, notice that beginAsynchronousTest
is defined to accept one argument named client
.
Since beginAsynchronousTest
is passed to the addCallback
method of the Deferred
returned by ClientCreator.connectTCP
this means it will be called with an instance of the protocol the ClientCreator
was initialized with (in this case, ModbusClientProtocol
). beginAsynchronousTest
will be called as soon as the Deferred
is given its result by the implementation of ClientCreator
- in other words, it will be called as soon as the connection is established. Setting up a TCP connection takes a somewhat arbitrary amount of time since it involves exchanging data with arbitrary other computers over arbitrary network links - there's no telling how long those resources will take to complete their part of the connection setup.
Once beginAsynchronousTest
is called you have a connection - represented by the ModbusClientProtocol
instance passed in to it. This is the point in your program where you might be able to start doing multiple things (for example, doing something each time a button is clicked).
At this point the Deferred
your program started out with (called myobjectx
in the snippets of code above) is done and no longer useful or interesting so you won't be using it anymore.
Instead, you'll be calling methods of ModbusClientProtocol
(read_coils
or write_coil
or whatever else you want to do). Each of these methods probably returns a brand new Deferred
representing the result of that particular operation. You'll want to use addCallback
with these in order to learn about their results.
The other place people often stumble is figuring out how to make these additional method calls. If you're adding code to the body of beginAsynchronousTest
then it's fairly straightforward how to do this:
reading = client.read_coils(1, 1)
However, I suspect you won't want to add your button handling code to the body of beginAsynchronousTest
. Instead, you probably have an event handler somewhere else in your program that gets called any time a button has been pressed. Fortunately, it's not much more complicated to deal with this situation.
The key is just to remember that any time you have a reference to the connection you'll be able to use it. Inside the body of beginAsynchronousTest
you have a reference to it - the client
parameter. You can make this reference available to other parts of your program too: setting an attribute on an object that is shared by the necessary parts of your program is one common, fairly good way to do this.
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)
Notice how the client
attribute of ButtonModbusSomething
starts off as None
and how buttonClicked
needs to check for this case. As mentioned above, setting up a connection can take some time and the only way you know how long is to wait for _connected
to be called. This check ensures that if a button is clicked before the connection exists that the event is just ignored (you may want to handle this more nicely - for example, by starting with the user interface in a disabled state and then switching it on only when a connection is set up).
Also, I've left out the code that you probably also want to handle your connection being lost. When this happens, the client
attribute is no longer useful. It is still a reference to the ModbusClientProtocol
which was connected, but since that protocol instance no longer has a connection it's hard to do anything useful with. You will probably want to re-disable the user interface when the connection is lost or at least start ignoring button presses again.
Also, notice that ClientCreator
actually comes from twisted.internet.protocol
not MODBUSLIB.protocol
.
Upvotes: 3