traits and thread

classic Classic list List threaded Threaded
12 messages Options
Reply | Threaded
Open this post in threaded view
|

traits and thread

Simone Gabbriellini-3
Hello List.

I have a small traits application with a GUI. This application has three buttons, "setup" "step" and "go".

When "setup" is clicked, the app instantiates a class Model(). This instance has a method analyze() which does some computations. When I click "step", this method is called once by the step() method of Model. When I click "go", this method is called repeatedly by the go() method, but I would like to see progress on the GUI...

This is the code of my handler:

class BNSHandler(Handler):
    model = Instance(Model.Model)
    def do_setup(self, info):
            self.model = Model.Model(info)
        def do_step(self, info):
                self.model.step()
        def do_go(self, info):
                self.model.go()

where should I put a call to a thread? Should all my Model class be a threading.Thread class?
Sorry for the simple question, but It's the first time I have to deal with a thread issue, so any hint on how to approach this problem is more than welcome!

best,
Simone
_______________________________________________
Enthought-Dev mailing list
[hidden email]
https://mail.enthought.com/mailman/listinfo/enthought-dev
Reply | Threaded
Open this post in threaded view
|

Re: traits and thread

bryce hendrix-2
On Mon, Mar 14, 2011 at 3:48 PM, Simone Gabbriellini <[hidden email]> wrote:
Hello List.

I have a small traits application with a GUI. This application has three buttons, "setup" "step" and "go".

When "setup" is clicked, the app instantiates a class Model(). This instance has a method analyze() which does some computations. When I click "step", this method is called once by the step() method of Model. When I click "go", this method is called repeatedly by the go() method, but I would like to see progress on the GUI...

This is the code of my handler:

class BNSHandler(Handler):
   model = Instance(Model.Model)
   def do_setup(self, info):
           self.model = Model.Model(info)
       def do_step(self, info):
               self.model.step()
       def do_go(self, info):
               self.model.go()

where should I put a call to a thread? Should all my Model class be a threading.Thread class?
Sorry for the simple question, but It's the first time I have to deal with a thread issue, so any hint on how to approach this problem is more than welcome!


Simone,

I am not 100% clear on why you need threads? It does sound like you want a progress dialog though. There is one in pyface (both Qt and wx backends), with an example. The progress dialog was also been wrapped and can be used as a Traits editor.

Bryce

_______________________________________________
Enthought-Dev mailing list
[hidden email]
https://mail.enthought.com/mailman/listinfo/enthought-dev
Reply | Threaded
Open this post in threaded view
|

Re: traits and thread

Robert Kern
In reply to this post by Simone Gabbriellini-3
On Mon, Mar 14, 2011 at 3:48 PM, Simone Gabbriellini
<[hidden email]> wrote:

> Hello List.
>
> I have a small traits application with a GUI. This application has three buttons, "setup" "step" and "go".
>
> When "setup" is clicked, the app instantiates a class Model(). This instance has a method analyze() which does some computations. When I click "step", this method is called once by the step() method of Model. When I click "go", this method is called repeatedly by the go() method, but I would like to see progress on the GUI...
>
> This is the code of my handler:
>
> class BNSHandler(Handler):
>    model = Instance(Model.Model)

By the way, you shouldn't store things on a plain Handler. A single
Handler instance may be used multiple times simultaneously. That's why
the UIInfo gets passed to the Handler methods. You can get the edited
object using info.object (e.g. you could do "info.object.model =
Model.Model()").

We do have subclasses Controller and ModelView which are intended to
have multiple instances. See the Traits UI User's Guide chapter on
Handlers for more information.

>    def do_setup(self, info):
>            self.model = Model.Model(info)
>        def do_step(self, info):
>                self.model.step()
>        def do_go(self, info):
>                self.model.go()
>
> where should I put a call to a thread? Should all my Model class be a threading.Thread class?

You probably want to keep threading stuff out of the Model. It's more
of a UI concern.

The simplest thing you can do is to just use a plain Thread:

  def do_go(self, info):
    t = threading.Thread(target=self.model.go)
    t.setDaemon(True)
    t.run()

You will need to do more work to keep your object safe, though. You
don't want people clicking the step button in the middle. You also
want to have some way to stop the thread before it's finished.
Consequently, you will need to use Locks and some more state to
communicate between your UI and the work thread. This can be done on
the Controller/ModelView or on an auxiliary object.

class ThreadedController(Controller):
    worker = Instance(Model.Model)
    running = Bool(False)
    should_stop = Bool(False)

    # NOTE: Can't use Instance() because they are factory functions.
    thread = Any()
    lock = Any()
    def _lock_default(self):
        return threading.RLock()

    def do_setup(self, info):
        self.worker = Model.Model(info.object)

    def do_step(self, info):
        if not self.running:
            with self.lock:
                self.worker.step()

    def do_go(self, info):
        if not self.running:
            self.thread = threading.Thread(target=self.worker.go)
            self.thread.daemon = True
            self.thread.run()

    def _go_loop(self):
        self.running = True
        while True:
            if self.should_stop:
                self.running = False
                break
            with self.lock:
                self.worker.step()


Please note that this is untested. There may be deadlocks or weird
interactions left in the code, but I think it will work.

You need to be careful about Trait notifications. Most GUI toolkits
require that call that affects the UI must come from the main thread.
Plain Traits UIs are usually fairly safe; the editors usually use
.on_trait_change() dynamic handlers that use the dispatch='ui'
argument to make sure that the handlers are called in the UI thread.
But you may run into places where we have goofed.

--
Robert Kern
Enthought
_______________________________________________
Enthought-Dev mailing list
[hidden email]
https://mail.enthought.com/mailman/listinfo/enthought-dev
Reply | Threaded
Open this post in threaded view
|

Re: traits and thread

Simone Gabbriellini-3
In reply to this post by bryce hendrix-2

Il giorno 14/mar/2011, alle ore 23.33, bryce hendrix ha scritto:

> On Mon, Mar 14, 2011 at 3:48 PM, Simone Gabbriellini <[hidden email]> wrote:
> Hello List.
>
> I have a small traits application with a GUI. This application has three buttons, "setup" "step" and "go".
>
> When "setup" is clicked, the app instantiates a class Model(). This instance has a method analyze() which does some computations. When I click "step", this method is called once by the step() method of Model. When I click "go", this method is called repeatedly by the go() method, but I would like to see progress on the GUI...
>
> This is the code of my handler:
>
> class BNSHandler(Handler):
>    model = Instance(Model.Model)
>    def do_setup(self, info):
>            self.model = Model.Model(info)
>        def do_step(self, info):
>                self.model.step()
>        def do_go(self, info):
>                self.model.go()
>
> where should I put a call to a thread? Should all my Model class be a threading.Thread class?
> Sorry for the simple question, but It's the first time I have to deal with a thread issue, so any hint on how to approach this problem is more than welcome!
>
>
> Simone,
>
> I am not 100% clear on why you need threads? It does sound like you want a progress dialog though. There is one in pyface (both Qt and wx backends), with an example. The progress dialog was also been wrapped and can be used as a Traits editor.
>
> Bryce

Sorry, my bad, I omitted that my GUI has plots, and this way they stay frozen until go() has finished, and only at the end of the go() method they are updated.

best,
Simone

_______________________________________________
Enthought-Dev mailing list
[hidden email]
https://mail.enthought.com/mailman/listinfo/enthought-dev
Reply | Threaded
Open this post in threaded view
|

Re: traits and thread

Simone Gabbriellini-3
In reply to this post by Robert Kern
Dear Robert Kern

thank you very much for the explanations and for the detailed code! I will try it immediately.

best regards,
Simone

Il giorno 14/mar/2011, alle ore 23.34, Robert Kern ha scritto:

> On Mon, Mar 14, 2011 at 3:48 PM, Simone Gabbriellini
> <[hidden email]> wrote:
>> Hello List.
>>
>> I have a small traits application with a GUI. This application has three buttons, "setup" "step" and "go".
>>
>> When "setup" is clicked, the app instantiates a class Model(). This instance has a method analyze() which does some computations. When I click "step", this method is called once by the step() method of Model. When I click "go", this method is called repeatedly by the go() method, but I would like to see progress on the GUI...
>>
>> This is the code of my handler:
>>
>> class BNSHandler(Handler):
>>    model = Instance(Model.Model)
>
> By the way, you shouldn't store things on a plain Handler. A single
> Handler instance may be used multiple times simultaneously. That's why
> the UIInfo gets passed to the Handler methods. You can get the edited
> object using info.object (e.g. you could do "info.object.model =
> Model.Model()").
>
> We do have subclasses Controller and ModelView which are intended to
> have multiple instances. See the Traits UI User's Guide chapter on
> Handlers for more information.
>
>>    def do_setup(self, info):
>>            self.model = Model.Model(info)
>>        def do_step(self, info):
>>                self.model.step()
>>        def do_go(self, info):
>>                self.model.go()
>>
>> where should I put a call to a thread? Should all my Model class be a threading.Thread class?
>
> You probably want to keep threading stuff out of the Model. It's more
> of a UI concern.
>
> The simplest thing you can do is to just use a plain Thread:
>
>  def do_go(self, info):
>    t = threading.Thread(target=self.model.go)
>    t.setDaemon(True)
>    t.run()
>
> You will need to do more work to keep your object safe, though. You
> don't want people clicking the step button in the middle. You also
> want to have some way to stop the thread before it's finished.
> Consequently, you will need to use Locks and some more state to
> communicate between your UI and the work thread. This can be done on
> the Controller/ModelView or on an auxiliary object.
>
> class ThreadedController(Controller):
>    worker = Instance(Model.Model)
>    running = Bool(False)
>    should_stop = Bool(False)
>
>    # NOTE: Can't use Instance() because they are factory functions.
>    thread = Any()
>    lock = Any()
>    def _lock_default(self):
>        return threading.RLock()
>
>    def do_setup(self, info):
>        self.worker = Model.Model(info.object)
>
>    def do_step(self, info):
>        if not self.running:
>            with self.lock:
>                self.worker.step()
>
>    def do_go(self, info):
>        if not self.running:
>            self.thread = threading.Thread(target=self.worker.go)
>            self.thread.daemon = True
>            self.thread.run()
>
>    def _go_loop(self):
>        self.running = True
>        while True:
>            if self.should_stop:
>                self.running = False
>                break
>            with self.lock:
>                self.worker.step()
>
>
> Please note that this is untested. There may be deadlocks or weird
> interactions left in the code, but I think it will work.
>
> You need to be careful about Trait notifications. Most GUI toolkits
> require that call that affects the UI must come from the main thread.
> Plain Traits UIs are usually fairly safe; the editors usually use
> .on_trait_change() dynamic handlers that use the dispatch='ui'
> argument to make sure that the handlers are called in the UI thread.
> But you may run into places where we have goofed.
>
> --
> Robert Kern
> Enthought
> _______________________________________________
> Enthought-Dev mailing list
> [hidden email]
> https://mail.enthought.com/mailman/listinfo/enthought-dev

_______________________________________________
Enthought-Dev mailing list
[hidden email]
https://mail.enthought.com/mailman/listinfo/enthought-dev
Reply | Threaded
Open this post in threaded view
|

Re: traits and thread

Jonathan Blakes
In reply to this post by Simone Gabbriellini-3
On 14/03/11 22:35, [hidden email] wrote:
>      def do_go(self, info):
>          if not self.running:
>              self.thread = threading.Thread(target=self.worker.go)
>              self.thread.daemon = True
>              self.thread.run()

I think you need "self.thread.start()" instead of "self.thread.run()".

Jon
_______________________________________________
Enthought-Dev mailing list
[hidden email]
https://mail.enthought.com/mailman/listinfo/enthought-dev
Reply | Threaded
Open this post in threaded view
|

Re: traits and thread

Simone Gabbriellini-3
In reply to this post by Robert Kern
I am trying your code, and I guess it works correctly, but I also guess I have a problem in my Model.go() method, because it freezes the GUI no matter the thread... here's the function

def step(self):
        self.collaboration()
        # update time
        self.tick+=1
        # analyze new network
        if self.tick % 10 == 0:
            self.analyze()

def go(self):
        i = 0
        while i in range(0,100):
                self.step()
                i += 1

when I call Model.step(), at the tenth time the GUI refresh,  while with Model.go() the GUI is refreshed only out of the while condition... is there any better way to write this code?

any hint more than welcome

best,
simone

Il giorno 14/mar/2011, alle ore 23.34, Robert Kern ha scritto:

> On Mon, Mar 14, 2011 at 3:48 PM, Simone Gabbriellini
> <[hidden email]> wrote:
>> Hello List.
>>
>> I have a small traits application with a GUI. This application has three buttons, "setup" "step" and "go".
>>
>> When "setup" is clicked, the app instantiates a class Model(). This instance has a method analyze() which does some computations. When I click "step", this method is called once by the step() method of Model. When I click "go", this method is called repeatedly by the go() method, but I would like to see progress on the GUI...
>>
>> This is the code of my handler:
>>
>> class BNSHandler(Handler):
>>    model = Instance(Model.Model)
>
> By the way, you shouldn't store things on a plain Handler. A single
> Handler instance may be used multiple times simultaneously. That's why
> the UIInfo gets passed to the Handler methods. You can get the edited
> object using info.object (e.g. you could do "info.object.model =
> Model.Model()").
>
> We do have subclasses Controller and ModelView which are intended to
> have multiple instances. See the Traits UI User's Guide chapter on
> Handlers for more information.
>
>>    def do_setup(self, info):
>>            self.model = Model.Model(info)
>>        def do_step(self, info):
>>                self.model.step()
>>        def do_go(self, info):
>>                self.model.go()
>>
>> where should I put a call to a thread? Should all my Model class be a threading.Thread class?
>
> You probably want to keep threading stuff out of the Model. It's more
> of a UI concern.
>
> The simplest thing you can do is to just use a plain Thread:
>
>  def do_go(self, info):
>    t = threading.Thread(target=self.model.go)
>    t.setDaemon(True)
>    t.run()
>
> You will need to do more work to keep your object safe, though. You
> don't want people clicking the step button in the middle. You also
> want to have some way to stop the thread before it's finished.
> Consequently, you will need to use Locks and some more state to
> communicate between your UI and the work thread. This can be done on
> the Controller/ModelView or on an auxiliary object.
>
> class ThreadedController(Controller):
>    worker = Instance(Model.Model)
>    running = Bool(False)
>    should_stop = Bool(False)
>
>    # NOTE: Can't use Instance() because they are factory functions.
>    thread = Any()
>    lock = Any()
>    def _lock_default(self):
>        return threading.RLock()
>
>    def do_setup(self, info):
>        self.worker = Model.Model(info.object)
>
>    def do_step(self, info):
>        if not self.running:
>            with self.lock:
>                self.worker.step()
>
>    def do_go(self, info):
>        if not self.running:
>            self.thread = threading.Thread(target=self.worker.go)
>            self.thread.daemon = True
>            self.thread.run()
>
>    def _go_loop(self):
>        self.running = True
>        while True:
>            if self.should_stop:
>                self.running = False
>                break
>            with self.lock:
>                self.worker.step()
>
>
> Please note that this is untested. There may be deadlocks or weird
> interactions left in the code, but I think it will work.
>
> You need to be careful about Trait notifications. Most GUI toolkits
> require that call that affects the UI must come from the main thread.
> Plain Traits UIs are usually fairly safe; the editors usually use
> .on_trait_change() dynamic handlers that use the dispatch='ui'
> argument to make sure that the handlers are called in the UI thread.
> But you may run into places where we have goofed.
>
> --
> Robert Kern
> Enthought
> _______________________________________________
> Enthought-Dev mailing list
> [hidden email]
> https://mail.enthought.com/mailman/listinfo/enthought-dev

_______________________________________________
Enthought-Dev mailing list
[hidden email]
https://mail.enthought.com/mailman/listinfo/enthought-dev
Reply | Threaded
Open this post in threaded view
|

Re: traits and thread

Robert Kern
On Tue, Mar 15, 2011 at 12:11 PM, Simone Gabbriellini
<[hidden email]> wrote:
> I am trying your code, and I guess it works correctly, but I also guess I have a problem in my Model.go() method, because it freezes the GUI no matter the thread...

Did you incorporate Jonathan's correction (thread.start() rather than
thread.run())?

If so, then I don't know without seeing a complete example of your code.

--
Robert Kern
Enthought
_______________________________________________
Enthought-Dev mailing list
[hidden email]
https://mail.enthought.com/mailman/listinfo/enthought-dev
Reply | Threaded
Open this post in threaded view
|

Re: traits and thread

Simone Gabbriellini-3
ok, I missed that one, now it works!

I have to admit it is not really fluid, when I click repeatedly on the step button everything is fluid, while if I hit the go button the GUI is updated slowly and apparently at random...

simone

Il giorno 15/mar/2011, alle ore 19.00, Robert Kern ha scritto:

> On Tue, Mar 15, 2011 at 12:11 PM, Simone Gabbriellini
> <[hidden email]> wrote:
>> I am trying your code, and I guess it works correctly, but I also guess I have a problem in my Model.go() method, because it freezes the GUI no matter the thread...
>
> Did you incorporate Jonathan's correction (thread.start() rather than
> thread.run())?
>
> If so, then I don't know without seeing a complete example of your code.
>
> --
> Robert Kern
> Enthought
> _______________________________________________
> Enthought-Dev mailing list
> [hidden email]
> https://mail.enthought.com/mailman/listinfo/enthought-dev

_______________________________________________
Enthought-Dev mailing list
[hidden email]
https://mail.enthought.com/mailman/listinfo/enthought-dev
Reply | Threaded
Open this post in threaded view
|

Re: traits and thread

bryce hendrix-2


On Tue, Mar 15, 2011 at 1:21 PM, Simone Gabbriellini <[hidden email]> wrote:
ok, I missed that one, now it works!

I have to admit it is not really fluid, when I click repeatedly on the step button everything is fluid, while if I hit the go button the GUI is updated slowly and apparently at random...

Its updated whenever wx gets a few idle cycles. If you want the ui updated regularly, you can to call Yield(). I think this will work if you'd like a backend independent way of forcing the GUI to update:

import enthought.pyface.gui
enthought.pyface.gui.GUI.process_events()

Bryce

_______________________________________________
Enthought-Dev mailing list
[hidden email]
https://mail.enthought.com/mailman/listinfo/enthought-dev
Reply | Threaded
Open this post in threaded view
|

Re: traits and thread

Simone Gabbriellini-3
I've managed to synchronize GUI and Model by adding a time.sleep(0.3) in the go() method.

BTW, this Controller class just adds a new thread each time I click the go button. Is this an intended behavior?

class ThreadedController(Controller):
    worker = Instance(Model.Model)
    # Instantiate the model
    running = Bool(False)
    should_stop = Bool(False)
    thread = Any()
    lock = Any()  
    def _lock_default(self):
        return threading.RLock()
    def do_setup(self, info):
        self.worker = Model.Model(info)
    def do_step(self, info):
        if not self.running:
            with self.lock:
                self.worker.step()
    def do_go(self, info):
        if not self.running:
            self.thread = threading.Thread(target=self.worker.go)
            self.thread.daemon = True
            self.thread.start()
    def _go_loop(self):
        self.running = True
        while True:
            if self.should_stop:
                self.running = False
                break
            with self.lock:
                self.worker.step()

Il giorno 15/mar/2011, alle ore 19.29, bryce hendrix ha scritto:

>
>
> On Tue, Mar 15, 2011 at 1:21 PM, Simone Gabbriellini <[hidden email]> wrote:
> ok, I missed that one, now it works!
>
> I have to admit it is not really fluid, when I click repeatedly on the step button everything is fluid, while if I hit the go button the GUI is updated slowly and apparently at random...
>
> Its updated whenever wx gets a few idle cycles. If you want the ui updated regularly, you can to call Yield(). I think this will work if you'd like a backend independent way of forcing the GUI to update:
>
> import enthought.pyface.gui
> enthought.pyface.gui.GUI.process_events()
>
> Bryce

_______________________________________________
Enthought-Dev mailing list
[hidden email]
https://mail.enthought.com/mailman/listinfo/enthought-dev
Reply | Threaded
Open this post in threaded view
|

Re: traits and thread

Robert Kern
On Tue, Mar 15, 2011 at 3:15 PM, Simone Gabbriellini
<[hidden email]> wrote:
> I've managed to synchronize GUI and Model by adding a time.sleep(0.3) in the go() method.
>
> BTW, this Controller class just adds a new thread each time I click the go button. Is this an intended behavior?

It was an example that you can use as a starting point. You will have
to explore further to customize the behavior to be exactly what you
want. You may want to explore starting a single thread in do_setup,
having the loop use a threading.Condition() or threading.Event() to
wait until do_go() activates them:

  http://docs.python.org/library/threading#condition-objects

--
Robert Kern
Enthought
_______________________________________________
Enthought-Dev mailing list
[hidden email]
https://mail.enthought.com/mailman/listinfo/enthought-dev