wxasync versus wx yield

I’ve been playing with wxasync and think
it’s fantastic, however I’ve also just discovered wx.Yield
(deprecated) and friends – wx.GetApp().Yield() wx.SafeYield(),
wx.YieldIfNeeded()

  Am I right in assuming that these Yield functions will allow the

wx mainloop to process pending events and redraw windows/controls
if required?

  If so, does this mean my use case of running a longer task, and

updating some controls in the process, can also be achieved in an
event handler and using wx Yield to update the GUI? (e.g.
downloading a file to a usb device)

  I was under the impression that long running tasks needed to be

handled in a thread (I had been using wx.lib.delayedresult
producer/consumer functions in the past, which I found a little
painful).

  I'm sure there are some advantages to using wxasync (e.g. waiting

for IO and such), but for simple use cases wx Yield might to the
job just as well (and presumably also works with python <
3.5). Also one less python module dependency to manage.

  Thanks,

  Brendan.
  Am I right in assuming that these Yield functions will allow the

wx mainloop to process pending events and redraw windows/controls
if required?

Sort of. wx.Yield basically runs a temporary message loop long enough to drain any pending messages.

  If so, does this mean my use case of running a longer task, and

updating some controls in the process, can also be achieved in an
event handler and using wx Yield to update the GUI? (e.g.
downloading a file to a usb device)

Yes, but remember that your GUI will be frozen in between calls to wx.Yield. Downloading a file can take a long time, and that results in a very poor user experience. You could not, for example, have a “Cancel” button to cancel the transfer.

  I was under the impression that long running tasks needed to be

handled in a thread (I had been using wx.lib.delayedresult
producer/consumer functions in the past, which I found a little
painful).

That IS the correct method. wx.Yield is a hack, left over from the 16-bit Windows days.

···

On Jul 14, 2018, at 7:41 PM, Brendan Simon (eTRIX) brendan.simon@etrix.com.au wrote:


Tim Roberts, timr@probo.com
Providenza & Boekelheide, Inc.

Thanks for your response Tim. Ok, sounds like wx.Yield would work if called regularly enough to update the GUI. Mind you, the same thing has to happen if using wxAsync/ascyncio (i.e. you have to yield to allow other tasks to run, one of which is wxAsyncApp which is running the wx mainloop). e.g. using “await asyncio.sleep(0)” or awaiting on some other coroutine that yields.

Since wx.Yield is considered a hack (is that a general consensus?) I will opt for using wxAysnc as my goto architecture for most things, and only look to using threads if absolutely necessary. There’s lots of examples of using asyncio with threads too (with asyncio having some “*_threadsafe()” functions, etc), so it’s an easy jump to migrate to using threads (not using wx.lib.delayedresult)

Cheers, Brendan.

···

On Monday, 16 July 2018 11:48:45 UTC+10, Tim Roberts wrote:

On Jul 14, 2018, at 7:41 PM, Brendan Simon (eTRIX) brenda...@etrix.com.au wrote:

  Am I right in assuming that these Yield functions will allow the

wx mainloop to process pending events and redraw windows/controls
if required?

Sort of. wx.Yield basically runs a temporary message loop long enough to drain any pending messages.

  If so, does this mean my use case of running a longer task, and

updating some controls in the process, can also be achieved in an
event handler and using wx Yield to update the GUI? (e.g.
downloading a file to a usb device)

Yes, but remember that your GUI will be frozen in between calls to wx.Yield. Downloading a file can take a long time, and that results in a very poor user experience. You could not, for example, have a “Cancel” button to cancel the transfer.

  I was under the impression that long running tasks needed to be

handled in a thread (I had been using wx.lib.delayedresult
producer/consumer functions in the past, which I found a little
painful).

That IS the correct method. wx.Yield is a hack, left over from the 16-bit Windows days.

Ah, we programmers are such a judgmental crew.
It’s not really a “hack”, but it is definitely considered
“outdated”. In a modern, responsive app, all UI tasks should be
quick, quick quick. Anything that takes long enough to require a
wx.Yield should rightly be handled in another thread.
So, you can use it, but you are required to feel bad about it. :wink:

···

Brendan Simon wrote:

        Since wx.Yield is

considered a hack (is that a general consensus?) …

-- Tim Roberts, Providenza & Boekelheide, Inc.

timr@probo.com

Ok. So I revisited threads thanks to your “encouragement” Tim :wink:

At the end of the “Long Running Tasks” wiki (LongRunningTasks - wxPyWiki) there is a simple example of using threads along with wx.CallAfter().

That looks a little simpler (less boiler plate) than using wx.lib.delayedresult. I actually converted it to use the threading module instead of the older thread module and it works great. If there are lots of gui updates required then I encapsulate them in a function and call the update function via wx.CallAfter().

Now, is there any advantages to using delayedresult? It requires a producer and consumer it would seem. What’s the point of the consumer? Surely the producer can do whatever it needs to at the end (e.g. save some state, results, or update the GUI, etc). Is the consumer nothing more than another function called by wx.CallAfter()?

The only thing I can think of is that for worker threads that do not require any GUI interaction during the process, can just return a result and the producer can then cleanup and update the GUI. i.e. the worker thread does not need to bother with wx.CallAfter() calls within it.

Thinking about it out loud (a little more), it might be useful for calling other executables that do a particular job but are not python modifiable (e.g. an binary, script, read-only python module, etc)

wxAsync still feels nice from a wx perspective (none of the “ugly” wx.CallAfter() statements), however it does rely on the asycio tasks playing nicely and “yielding” often enough to let the wx.App main loop run.

Many ways to skin a cat it would seem. Now I’m thinking using the threading module might be my goto architecture, and possibly delayedresult if I need to run an external executable?

···

On Thursday, 19 July 2018 10:27:06 UTC+10, Tim Roberts wrote:

Brendan Simon wrote:

        Since wx.Yield is

considered a hack (is that a general consensus?) …

Ah, we programmers are such a judgmental crew.



It's not really a "hack", but it is definitely considered

“outdated”. In a modern, responsive app, all UI tasks should be
quick, quick quick. Anything that takes long enough to require a
wx.Yield should rightly be handled in another thread.

So, you can use it, but you are required to feel bad about it.   ;)

Many ways to skin a cat it would seem.

Indeed.

Now I’m thinking using the threading module might be my goto architecture, and possibly delayedresult if I need to run an external executable?

I don’t think delayedresult has any special advantage for running external executables, it’s just another cat-skinner. Or IOW, just another way to deal with long-running tasks, whatever they may be. It just happens to be a better fit for some people and how they like to do things. IMO delayedresult is good if you like the pattern of “start something and keep on going while the something is percolating, when it’s done send the result to something else.” It’s similar to a fairly common pattern sometimes associated with names like Future, Deferred, Promise, etc.

···

On Friday, July 20, 2018 at 6:48:06 AM UTC-7, Brendan Simon wrote:

Robin

Now I’m thinking using the threading module might be my goto architecture, and possibly delayedresult if I need to run an external executable?

I don’t think delayedresult has any special advantage for running external executables, it’s just another cat-skinner. Or IOW, just another way to deal with long-running tasks, whatever they may be. It just happens to be a better fit for some people and how they like to do things. IMO delayedresult is good if you like the pattern of “start something and keep on going while the something is percolating, when it’s done send the result to something else.” It’s similar to a fairly common pattern sometimes associated with names like Future, Deferred, Promise, etc.

Thanks Robin. That makes sense re delayedresult.

My
usual use case is donwloading/uploading info from usb devices, with a progress bar, so delayedresult doesn’t fit as nicely (of course it does work, but the callback to the consumer is redundant).

The
advantage of wxasync is I can call wx methods directly in asyncio tasks, as they run in the same thread, whereas I need to use wx.CallAfter() when using threads.

One
caveat with wx.CallAfter is you can’t use widget properties - you have to use a callable. e.g. wx.CallAfter( myButton.Label, “g’day” ) needs to be wx.CallAfter( myButton.SetLabel, “g’day” )

wx.CallAfter()
scattered about is little ugly (to me), but not a showstopper by any means. So is there a convenient/clever way to use wx.CallAfter() without explicitly calling it (e.g. via some kind of decorator?). I did
use wxAnyThread in the past with an app, but somewhere along the way it
had a performance impact and I swapped to wx.CallAfter.

Best I can come with is a wrapper function/method to update widgets of interest.

def update_progress( self, value=None, range=None ):
if value != None:
self.gauge.Value = value

if range != None:
    self.gauge.Range = range

def update_progress_threadsafe( self, value=None, range=None ):
wx.CallAfter( self.update_progress, value=value, range=range )

``

Is
there some kind of decorator I can apply to update_progress so I can call it directly from a thread, without having to write a wrapper?

So I actually looked at the source code for wxAnyThread to see how it works. There’s one aspect of it that I never realiased. The thread calling a wxAnyThread decorated function will block, until the main gui event loop processes the function and it returns a result back the calling thread. I suspect that might be the reason for the performance hit I noticed in the past (and probably was always there).

That seems suitable to some use cases (like the wxAnyThread example that shows a dialog to get some user input), but is not as efficient if just wanting to send some data to update a gauge or other controls (i.e. don’t want the thread to block as the gui can update the controls in it’s own time).

Stealing bits from wxAnyThread, would the following work (as a decorator)?

def callafter( func ):
“”“”
Method decororator allowing call from any thread

When invoked from the main thread, the function is executed immediately.
"""

def invoker( *args, **kwds ):
if wx.Thread_IsMain():
return func( *args, **kwds )
else:
return wx.CallAfter( func, *args, **kwds )

invoker.__name__ = func.__name__ invoker.doc = func.doc
return invoker

``

Disclaimer: I’ve never written a decorator :slight_smile:

Would calling wx.CallAfter even in the main GUI thread cause delays? e.g. would the main loop have to wait to be run again before processing the function? i…e could I get away without the test wx.Thread_IsMain() ?

···

On Saturday, 21 July 2018 22:11:47 UTC+10, Brendan Simon wrote:

I did
use wxAnyThread in the past with an app, but somewhere along the way it
had a performance impact and I swapped to wx.CallAfter.

Seems to work (though I couldn’t get the GUI to crash on my mac when not using the decorator and calling the update functions from a thread).

I modified it slightly (no return values, add wx.Yield). It works so I can call the same task from the main thread (and have the GUI update) or another thread.

Are there any issues that I’m unaware of ???

def callafter( func ):
“”“”
Method decororator allowing call from any thread

When invoked from the main thread, the function is executed immediately.
"""

def invoker( *args, **kwds ):
    if wx.IsMainThread():
        #print("call func directly from main thread")
        func( *args, **kwds )
        wx.Yield()
    else:
        #print("call func via wx.CallAfter from non-main thread")
        wx.CallAfter( func, *args, **kwds )

invoker.__name__ = func.__name__
invoker.__doc__  = func.__doc__
return invoker

``

···

On Saturday, 21 July 2018 23:05:17 UTC+10, Brendan Simon wrote:

Stealing bits from wxAnyThread, would the following work (as a decorator)?

This is the construct I am using successfully in my app:

import wx
import functools

def gui_call(func):
    """Decorator for a callable to be invoked on the main GUI thread"""
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        return wx.CallAfter(func, *args, **kwargs)
    return wrapper

I don't use it as a decorator though, because I usually need it as an
inline expression:

callable = gui_call(myButton.SetLabel, "g'day")

Hope it helps.

Kind regards
André

wx.CallAfter puts a message in the message queue. If that queue had previously been empty, it then fires an event, on the theory that the main loop was blocked waiting for a “new message” event. Firing hat event will make the thread “ready to run”, so it should get picked the next time the scheduler runs on an available CPU. If you have multiple CPUs, that happens very quickly.

···

On Jul 21, 2018, at 6:05 AM, Brendan Simon brendanjsimon@gmail.com wrote:

Would calling wx.CallAfter even in the main GUI thread cause delays? e.g. would the main loop have to wait to be run again before processing the function? i…e could I get away without the test wx.Thread_IsMain() ?


Tim Roberts, timr@probo.com
Providenza & Boekelheide, Inc.