Drawing is locking up the window, or atleast causing slow down.

I've got a task that I want to show progress with over a few custom progress
bars, what I've noticed is that the actual drawing, or maybe the events that get
sent is actually slowing the main task down. (I've included some demo code of a
bunch of my custom bars and some native ones to compare the difference). So I
believe something isn't threading quite right somewhere, maybe in something
underlying. This is much worse when I run on mac than on windows, but from the
demo code you can see that the native bars allow the threaded worker task to
complete faster. How can I stop gui updates slowing down the worker threads?

I thought maybe its because the bars are getting asked to draw too many times
but I think I'm right in saying the refresh I call in setValue just invalidates
the bar and it will only redraw when it is able to.

Things get really slow if you start off the native ones then starts the custom
bars, surely as the work is threaded doing this shouldn't affect the speed of
the individual tasks. Also sometimes running the custom bars locks up the GUI as
well.

I looked into wx.UpdateUIEvent.SetUpdateInterval to see if I could reduce the
number of times it was drawing but I couldn't get it to work properly.

Any ideas what I'm doing wrong / what is wrong / of any fixes?

I guess I just need a little more understanding about what's actually happening
to be able to understand how to solve this.

import wx,time,threading
from wx.lib import newevent
from wx.lib.embeddedimage import PyEmbeddedImage

diagLinesPattern = PyEmbeddedImage(
    "iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAYAAACp8Z5+AAAAGXRFWHRTb2Z0d2FyZQBBZG9i"
    "ZSBJbWFnZVJlYWR5ccllPAAAACRJREFUeNpi/P//PwMMMDIyKjKABKCCimAamQNTDeeAMECA"
    "AQCApx5rSauzmwAAAABJRU5ErkJggg==")

Gauge1Event, EVT_GAUGE_1= newevent.NewEvent()
Gauge2Event, EVT_GAUGE_2= newevent.NewEvent()

class CustomGauge(wx.Panel):

    themes= {"blue": {"mainbar":wx.Colour(149,207,251),
                      "pattern":wx.Colour(110,190,250),
                      "border" :wx.Colour(92,173,234),
                      "gradient":wx.Colour(92,173,234,60),
                      "height":16},
             "green":{"mainbar":wx.Colour(177,208,90),
                      "pattern":wx.Colour(150,191,60),
                      "border" :wx.Colour(112,171,0),
                      "gradient":wx.Colour(112,171,0,60),
                      "height":22}}

    def
__init__(self,parent,bgColour,id=-1,theme="blue",pos=wx.DefaultPosition,size=wx.
DefaultSize,style=wx.TRANSPARENT_WINDOW|wx.NO_BORDER,name=wx.ControlNameStr):
        self.parent= parent
        idd = wx.NewId()
        if theme not in self.themes.keys():
            theme="blue"
        self.theme= self.themes.get(theme)
        size= wx.Size(-1,self.theme.get("height"))
        wx.Panel.__init__(self,parent,id,pos,size,style,name)
        self.bgColour= bgColour
        self.Bind(wx.EVT_PAINT, self.OnPaint, id=id)
        #self.Bind(wx.EVT_UPDATE_UI, self.OnPaint)
        self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)

        self.patternBmp= diagLinesPattern.GetBitmap()
        #self.patternBmp.SetHeight(4)
        #self.patternBmp.SetWidth(4)

        self._value=0.
        self._range=1.

    def setValue(self,value):
        if value<0:
            value=0
        elif value>100:
            value=100
        self._value= value
        self.Refresh()
        #self.Update()

    def setTheme(self,theme):
        if theme not in self.themes.keys():
            theme="blue"
        self.theme= self.themes.get(theme)
        self.Refresh()

    def OnPaint(self,event):
        dc= wx.BufferedPaintDC(self)
        self.Draw(dc)
        event.Skip()

    def Draw(self,dc):
        dc= wx.GCDC(dc)
        width,height= self.GetClientSize()
        if not width or not height:
            return

        dc.SetBackground(wx.Brush(self.bgColour))
        dc.Clear()

        #Rounded rect
        dc.SetPen(wx.Pen(wx.Colour(219,219,219),1))
        dc.SetBrush(wx.Brush(wx.Colour(230,230,230)))
        rect= wx.Rect(1,0,width-1,height-2)
        dc.DrawRoundedRectangleRect(rect,1)

        #Top line
        dc.SetPen(wx.Pen(wx.Colour(199,199,199),1))
        dc.DrawLine(2,0,width-2,0)

        #top shadow
        dc.SetPen(wx.Pen(wx.Colour(221,221,221),1))
        dc.DrawLine(2,1,width-2,1)

        #bottom highlight
        dc.SetPen(wx.Pen(wx.Colour(255,255,255,128),1))
        dc.DrawLine(1,height-2,width-1,height-2)

        if self._value > 0:
            #mainbar
            dc.SetPen(wx.Pen(self.theme.get("border"),1))
            dc.SetBrush(wx.Brush(self.theme.get("mainbar")))
            barwidth= (width-1)*(self._value/self._range)
            rect= wx.Rect(1,0,barwidth,height-2)
            dc.DrawRoundedRectangleRect(rect,1)

            #pattern
            brush= wx.Brush(self.theme.get("mainbar"),wx.STIPPLE)
            brush.SetStipple(self.patternBmp)
            dc.SetBrush(brush)
            dc.DrawRoundedRectangleRect(rect,1)

            #gradient
            rect= wx.Rect(2,1,barwidth-2,height-4)
            
dc.GradientFillLinear(rect,self.theme.get("gradient"),wx.Colour(255,255,255,60),
wx.NORTH)
            dc.DestroyClippingRegion()

            #highlight
            dc.SetPen(wx.Pen(wx.Colour(255,255,255,84),1))
            dc.DrawLine(2,1,barwidth-1,1)

            #shadow
            dc.SetPen(wx.Pen(wx.Colour(0,0,0,15),1))
            dc.DrawLine(0,1,0,height-2)
            dc.DrawLine(barwidth+1,1,barwidth+1,height-2)
            dc.DrawLine(1,height-1,barwidth,height-1)
            dc.SetPen(wx.Pen(wx.Colour(0,0,0,40),1))
            dc.DrawLine(1,height-2,barwidth,height-2)

    def OnEraseBackground(self,event):
        pass
    
class TestFrame(wx.Frame):
    def __init__(self,parent,id,title):
        wx.Frame.__init__(self,parent,id,title,size=(800,500))
        panel= wx.Panel(self,-1)
        bgColour= wx.Colour(230,230,230)
        panel.SetBackgroundColour(bgColour)
        self.steps=10000000
        #wx.UpdateUIEvent.SetMode(wx.UPDATE_UI_PROCESS_SPECIFIED)
        wx.UpdateUIEvent.SetUpdateInterval(500)

        sizer= wx.BoxSizer(wx.VERTICAL)
        custom_gauge_sizer= wx.BoxSizer(wx.HORIZONTAL)
        gauges_sizer= wx.BoxSizer(wx.VERTICAL)
        self.gauges=[]
        for x in xrange(10):
            g= CustomGauge(panel,bgColour,-1,"blue")
            self.gauges.append(g)
            gauges_sizer.Add(g,0,wx.EXPAND|wx.ALL^wx.BOTTOM,3)
        
custom_gauge_sizer.Add(gauges_sizer,1,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
        self.go_btn_2= wx.Button(panel,-1,"Go")
        custom_gauge_sizer.Add(self.go_btn_2,0,wx.LEFT,5)
        sizer.Add(custom_gauge_sizer,0,wx.EXPAND|wx.ALL^wx.BOTTOM,5)

        normal_gauge_sizer= wx.BoxSizer(wx.HORIZONTAL)
        gauges_sizer= wx.BoxSizer(wx.VERTICAL)
        self.ngauges=[]
        for x in xrange(10):
            g= wx.Gauge(panel,-1,self.steps)
            #g= PG.PyGauge(panel,-1)
            #g.SetRange(self.steps)
            self.ngauges.append(g)
            gauges_sizer.Add(g,0,wx.EXPAND|wx.ALL^wx.BOTTOM,3)
        
normal_gauge_sizer.Add(gauges_sizer,1,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
        self.go_btn_1= wx.Button(panel,-1,"Go")
        normal_gauge_sizer.Add(self.go_btn_1,0,wx.LEFT,5)
        sizer.Add(normal_gauge_sizer,0,wx.EXPAND|wx.ALL^wx.BOTTOM,5)

        self.go_btn_1.Bind(wx.EVT_BUTTON,self.OnGo1)
        self.go_btn_2.Bind(wx.EVT_BUTTON,self.OnGo2)

        self.Bind(EVT_GAUGE_1,self.Update1)
        self.Bind(EVT_GAUGE_2,self.Update2)

        panel.SetSizer(sizer)
        self.Show()
        self.UpdateWindowUI()

    def Update1(self,event):
        v= event.value
        for g in self.ngauges:
            g.SetValue(v)
        self.UpdateWindowUI()

    def Update2(self,event):
        v= event.value
        for g in self.gauges:
            g.setValue(v)
        self.UpdateWindowUI()

    def OnGo1(self,event):
        worker= Worker(self.steps,self,Gauge1Event,True)
        worker.start()

    def OnGo2(self,event):
        worker= Worker(self.steps,self,Gauge2Event,False)
        worker.start()

class Worker(threading.Thread):
    def __init__(self,step_num,target,event,send_ints=False):
        threading.Thread.__init__(self)
        self.step_num= step_num
        self.event_target= target
        self.event= event
        self.send_ints= send_ints

    def run(self):
        start=time.time()
        sent_last=0
        for x in xrange(self.step_num):
            if time.time()-sent_last>0.04:
                perc= float(x)/self.step_num
                evt= self.event(value=(x if self.send_ints else perc))
                wx.CallAfter(wx.PostEvent,self.event_target,evt)
                sent_last=time.time()
        perc= float(x)/self.step_num
        evt= self.event(value=(x if self.send_ints else perc))
        wx.CallAfter(wx.PostEvent,self.event_target,evt)

        print time.time()-start

if __name__ == "__main__":
    app= wx.App(False)
    frame= TestFrame(None,-1,"Test Gauges")
    app.MainLoop()

The loop for x in xrange(self.stepnum): will be incredibly processor
intensive as most of the time it will be repeatedly calling time.time
and failing the comparison, try using a time.sleep(0.01) so that your
thread is not hogging your system resources.

Gadget/Steve

···

On 24/01/2012 10:39 AM, Paul wrote:

I've got a task that I want to show progress with over a few custom progress
bars, what I've noticed is that the actual drawing, or maybe the events that get
sent is actually slowing the main task down. (I've included some demo code of a
bunch of my custom bars and some native ones to compare the difference). So I
believe something isn't threading quite right somewhere, maybe in something
underlying. This is much worse when I run on mac than on windows, but from the
demo code you can see that the native bars allow the threaded worker task to
complete faster. How can I stop gui updates slowing down the worker threads?

I thought maybe its because the bars are getting asked to draw too many times
but I think I'm right in saying the refresh I call in setValue just invalidates
the bar and it will only redraw when it is able to.

Things get really slow if you start off the native ones then starts the custom
bars, surely as the work is threaded doing this shouldn't affect the speed of
the individual tasks. Also sometimes running the custom bars locks up the GUI as
well.

I looked into wx.UpdateUIEvent.SetUpdateInterval to see if I could reduce the
number of times it was drawing but I couldn't get it to work properly.

Any ideas what I'm doing wrong / what is wrong / of any fixes?

I guess I just need a little more understanding about what's actually happening
to be able to understand how to solve this.

import wx,time,threading
from wx.lib import newevent
from wx.lib.embeddedimage import PyEmbeddedImage

diagLinesPattern = PyEmbeddedImage(
    "iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAYAAACp8Z5+AAAAGXRFWHRTb2Z0d2FyZQBBZG9i"
    "ZSBJbWFnZVJlYWR5ccllPAAAACRJREFUeNpi/P//PwMMMDIyKjKABKCCimAamQNTDeeAMECA"
    "AQCApx5rSauzmwAAAABJRU5ErkJggg==")

Gauge1Event, EVT_GAUGE_1= newevent.NewEvent()
Gauge2Event, EVT_GAUGE_2= newevent.NewEvent()

class CustomGauge(wx.Panel):

    themes= {"blue": {"mainbar":wx.Colour(149,207,251),
                      "pattern":wx.Colour(110,190,250),
                      "border" :wx.Colour(92,173,234),
                      "gradient":wx.Colour(92,173,234,60),
                      "height":16},
             "green":{"mainbar":wx.Colour(177,208,90),
                      "pattern":wx.Colour(150,191,60),
                      "border" :wx.Colour(112,171,0),
                      "gradient":wx.Colour(112,171,0,60),
                      "height":22}}

    def
__init__(self,parent,bgColour,id=-1,theme="blue",pos=wx.DefaultPosition,size=wx.
DefaultSize,style=wx.TRANSPARENT_WINDOW|wx.NO_BORDER,name=wx.ControlNameStr):
        self.parent= parent
        idd = wx.NewId()
        if theme not in self.themes.keys():
            theme="blue"
        self.theme= self.themes.get(theme)
        size= wx.Size(-1,self.theme.get("height"))
        wx.Panel.__init__(self,parent,id,pos,size,style,name)
        self.bgColour= bgColour
        self.Bind(wx.EVT_PAINT, self.OnPaint, id=id)
        #self.Bind(wx.EVT_UPDATE_UI, self.OnPaint)
        self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)

        self.patternBmp= diagLinesPattern.GetBitmap()
        #self.patternBmp.SetHeight(4)
        #self.patternBmp.SetWidth(4)

        self._value=0.
        self._range=1.

    def setValue(self,value):
        if value<0:
            value=0
        elif value>100:
            value=100
        self._value= value
        self.Refresh()
        #self.Update()

    def setTheme(self,theme):
        if theme not in self.themes.keys():
            theme="blue"
        self.theme= self.themes.get(theme)
        self.Refresh()

    def OnPaint(self,event):
        dc= wx.BufferedPaintDC(self)
        self.Draw(dc)
        event.Skip()

    def Draw(self,dc):
        dc= wx.GCDC(dc)
        width,height= self.GetClientSize()
        if not width or not height:
            return

        dc.SetBackground(wx.Brush(self.bgColour))
        dc.Clear()

        #Rounded rect
        dc.SetPen(wx.Pen(wx.Colour(219,219,219),1))
        dc.SetBrush(wx.Brush(wx.Colour(230,230,230)))
        rect= wx.Rect(1,0,width-1,height-2)
        dc.DrawRoundedRectangleRect(rect,1)

        #Top line
        dc.SetPen(wx.Pen(wx.Colour(199,199,199),1))
        dc.DrawLine(2,0,width-2,0)

        #top shadow
        dc.SetPen(wx.Pen(wx.Colour(221,221,221),1))
        dc.DrawLine(2,1,width-2,1)

        #bottom highlight
        dc.SetPen(wx.Pen(wx.Colour(255,255,255,128),1))
        dc.DrawLine(1,height-2,width-1,height-2)

        if self._value > 0:
            #mainbar
            dc.SetPen(wx.Pen(self.theme.get("border"),1))
            dc.SetBrush(wx.Brush(self.theme.get("mainbar")))
            barwidth= (width-1)*(self._value/self._range)
            rect= wx.Rect(1,0,barwidth,height-2)
            dc.DrawRoundedRectangleRect(rect,1)

            #pattern
            brush= wx.Brush(self.theme.get("mainbar"),wx.STIPPLE)
            brush.SetStipple(self.patternBmp)
            dc.SetBrush(brush)
            dc.DrawRoundedRectangleRect(rect,1)

            #gradient
            rect= wx.Rect(2,1,barwidth-2,height-4)
            
dc.GradientFillLinear(rect,self.theme.get("gradient"),wx.Colour(255,255,255,60),
wx.NORTH)
            dc.DestroyClippingRegion()

            #highlight
            dc.SetPen(wx.Pen(wx.Colour(255,255,255,84),1))
            dc.DrawLine(2,1,barwidth-1,1)

            #shadow
            dc.SetPen(wx.Pen(wx.Colour(0,0,0,15),1))
            dc.DrawLine(0,1,0,height-2)
            dc.DrawLine(barwidth+1,1,barwidth+1,height-2)
            dc.DrawLine(1,height-1,barwidth,height-1)
            dc.SetPen(wx.Pen(wx.Colour(0,0,0,40),1))
            dc.DrawLine(1,height-2,barwidth,height-2)

    def OnEraseBackground(self,event):
        pass
    
class TestFrame(wx.Frame):
    def __init__(self,parent,id,title):
        wx.Frame.__init__(self,parent,id,title,size=(800,500))
        panel= wx.Panel(self,-1)
        bgColour= wx.Colour(230,230,230)
        panel.SetBackgroundColour(bgColour)
        self.steps=10000000
        #wx.UpdateUIEvent.SetMode(wx.UPDATE_UI_PROCESS_SPECIFIED)
        wx.UpdateUIEvent.SetUpdateInterval(500)

        sizer= wx.BoxSizer(wx.VERTICAL)
        custom_gauge_sizer= wx.BoxSizer(wx.HORIZONTAL)
        gauges_sizer= wx.BoxSizer(wx.VERTICAL)
        self.gauges=
        for x in xrange(10):
            g= CustomGauge(panel,bgColour,-1,"blue")
            self.gauges.append(g)
            gauges_sizer.Add(g,0,wx.EXPAND|wx.ALL^wx.BOTTOM,3)
        
custom_gauge_sizer.Add(gauges_sizer,1,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
        self.go_btn_2= wx.Button(panel,-1,"Go")
        custom_gauge_sizer.Add(self.go_btn_2,0,wx.LEFT,5)
        sizer.Add(custom_gauge_sizer,0,wx.EXPAND|wx.ALL^wx.BOTTOM,5)

        normal_gauge_sizer= wx.BoxSizer(wx.HORIZONTAL)
        gauges_sizer= wx.BoxSizer(wx.VERTICAL)
        self.ngauges=
        for x in xrange(10):
            g= wx.Gauge(panel,-1,self.steps)
            #g= PG.PyGauge(panel,-1)
            #g.SetRange(self.steps)
            self.ngauges.append(g)
            gauges_sizer.Add(g,0,wx.EXPAND|wx.ALL^wx.BOTTOM,3)
        
normal_gauge_sizer.Add(gauges_sizer,1,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
        self.go_btn_1= wx.Button(panel,-1,"Go")
        normal_gauge_sizer.Add(self.go_btn_1,0,wx.LEFT,5)
        sizer.Add(normal_gauge_sizer,0,wx.EXPAND|wx.ALL^wx.BOTTOM,5)

        self.go_btn_1.Bind(wx.EVT_BUTTON,self.OnGo1)
        self.go_btn_2.Bind(wx.EVT_BUTTON,self.OnGo2)

        self.Bind(EVT_GAUGE_1,self.Update1)
        self.Bind(EVT_GAUGE_2,self.Update2)

        panel.SetSizer(sizer)
        self.Show()
        self.UpdateWindowUI()

    def Update1(self,event):
        v= event.value
        for g in self.ngauges:
            g.SetValue(v)
        self.UpdateWindowUI()

    def Update2(self,event):
        v= event.value
        for g in self.gauges:
            g.setValue(v)
        self.UpdateWindowUI()

    def OnGo1(self,event):
        worker= Worker(self.steps,self,Gauge1Event,True)
        worker.start()

    def OnGo2(self,event):
        worker= Worker(self.steps,self,Gauge2Event,False)
        worker.start()

class Worker(threading.Thread):
    def __init__(self,step_num,target,event,send_ints=False):
        threading.Thread.__init__(self)
        self.step_num= step_num
        self.event_target= target
        self.event= event
        self.send_ints= send_ints

    def run(self):
        start=time.time()
        sent_last=0
        for x in xrange(self.step_num):
            if time.time()-sent_last>0.04:
                perc= float(x)/self.step_num
                evt= self.event(value=(x if self.send_ints else perc))
                wx.CallAfter(wx.PostEvent,self.event_target,evt)
                sent_last=time.time()
        perc= float(x)/self.step_num
        evt= self.event(value=(x if self.send_ints else perc))
        wx.CallAfter(wx.PostEvent,self.event_target,evt)

        print time.time()-start

if __name__ == "__main__":
    app= wx.App(False)
    frame= TestFrame(None,-1,"Test Gauges")
    app.MainLoop()

I've got a task that I want to show progress with over a few custom progress bars, what I've noticed is that the actual drawing, or maybe the events that get sent is actually slowing the main task down.

<250 lines snipped>


The loop for x in xrange(self.stepnum): will be incredibly processor
intensive as most of the time it will be repeatedly calling time.time
and failing the comparison, try using a time.sleep(0.01) so that your
thread is not hogging your system resources.
Gadget/Steve
The comment that Che posted a few days ago would apply here - in

spades

···

:wink:

On Sat, Jan 21, 2012 at 9:15 AM, C M cmpython@gmail.com
wrote:

  For use of this list, I'd recommend:



  - Trim the email you are responding to, keeping only the relevant

parts of the ongoing thread. This makes things easier to follow,
cleaner, etc.


-- Regards
David Hughes
Forestfield Software

The loop for x in xrange(self.stepnum): will be incredibly processor
intensive as most of the time it will be repeatedly calling time.time
and failing the comparison, try using a time.sleep(0.01) so that your
thread is not hogging your system resources.

Gadget/Steve

The check is in there to stop an excessive number of events getting sent. In
practice the worker loop will be doing a lot more so time.time() can just be seen
as part of the work, even though the time.time call takes time to compute it
doesn't matter- the worker thread is supposed to be working hard and the idea of
a thread is that it shouldn't be affected by other threads, like the gui in the
main thread. It doesn't seem like a good idea putting a sleep in my main worker
thread?

That loops 10million times, with a 0.01 sleep in each iteration that's a lot of
wasted time

Slowing down the work shouldn't be the only solution to stop the gui locking and
holding up the worker, you want to worker process to be as fast as it can

Just to add, maybe you missed the point of the demo- looping to 10million should
complete very quickly- but because I'm posting the progress of this, the loop is
actually getting slowed down which is not desirable. I put on 10 bars instead of
one so the problem is magnified and more obvious.

Just a thought, could this be a GIL bottleneck? is it then not possible to
update/draw a gui at a reasonable rate?

Hi,

Just a thought, could this be a GIL bottleneck? is it then not possible to
update/draw a gui at a reasonable rate?

No, not likely.

As others have mentioned it is because you are running a tight loop in
the other threads. This loop is filling the event queue on the main
thread with millions of events. (in fact each iteration is generating
2 events the CallAfter event then the event sent by the PostEvent. You
can just use PostEvent directly.)

Each of these events needs to be dispatched and handled on the main
thread. The paint events are also in this same queue and they have to
wait their turn to be dispatched until the large batches of other
events in the queue have already been processed. Hence the slowdown.

The gauge likely only has a resolution of 1% in most cases, so if you
are just updating a gauge it doesn't make much sense to send more than
100 events. Should change your logic in the thread to only send update
events to the main thread after a whole percent of work has been
completed.

Some additional notes:

The UpdateUI events don't really have anything to do with the painting
of the screen. They are notification events to allow your application
to update the state of a control.

You would be better off calling g.Refresh() in your event handlers
instead of UpdateUI. It will allow the paint events to be processed
more efficiently in most cases, unless you really need to force the UI
update that exact second. (this becomes mostly moot if you use the
approach of throttling the events as mentioned prior.).

Cody

···

On Tue, Jan 24, 2012 at 7:25 AM, Paul <poalman@gmail.com> wrote:

Cody <codyprecord <at> gmail.com> writes:

Hi,

> Just a thought, could this be a GIL bottleneck? is it then not possible to
> update/draw a gui at a reasonable rate?
>

No, not likely.

As others have mentioned it is because you are running a tight loop in
the other threads. This loop is filling the event queue on the main
thread with millions of events. (in fact each iteration is generating
2 events the CallAfter event then the event sent by the PostEvent. You
can just use PostEvent directly.)

Each of these events needs to be dispatched and handled on the main
thread. The paint events are also in this same queue and they have to
wait their turn to be dispatched until the large batches of other
events in the queue have already been processed. Hence the slowdown.

The gauge likely only has a resolution of 1% in most cases, so if you
are just updating a gauge it doesn't make much sense to send more than
100 events. Should change your logic in the thread to only send update
events to the main thread after a whole percent of work has been
completed.

Some additional notes:

The UpdateUI events don't really have anything to do with the painting
of the screen. They are notification events to allow your application
to update the state of a control.

You would be better off calling g.Refresh() in your event handlers
instead of UpdateUI. It will allow the paint events to be processed
more efficiently in most cases, unless you really need to force the UI
update that exact second. (this becomes mostly moot if you use the
approach of throttling the events as mentioned prior.).

Cody

Thanks Cody,

A fair bit of that was added afterwards when I was tinkering to see what would
make a difference, I was comparing CallAfter with PostEvent to see if there were
any differences (and as you can see I was curious to see what happens when I did
both and forgot to put it back). The same with UIUpdates, I've not used these
before and was seeing what effect they had.

If I call g.Refresh should I remove the Refresh from setValue?

Also, it would help like you say to limit the number of events to 100-- or the
resolution of what the bar can show, so if the progress bar is expanded to cover
for instance 300 pixels, how would I send 300 progress events instead? Could I
send the width of the bar to the worker, or what about if the bar resizes? or
maybe I should limit the number of events to a few hundred as I doubt it would
be very noticeable to have more.

···

On Tue, Jan 24, 2012 at 7:25 AM, Paul <poalman <at> gmail.com> wrote:

Hi,

If I call g.Refresh should I remove the Refresh from setValue?

Refresh just queues up a PaintEvent so that your paint handler will be
called. So you only need to call it once.

Also, it would help like you say to limit the number of events to 100-- or the
resolution of what the bar can show, so if the progress bar is expanded to cover
for instance 300 pixels, how would I send 300 progress events instead? Could I
send the width of the bar to the worker, or what about if the bar resizes? or
maybe I should limit the number of events to a few hundred as I doubt it would
be very noticeable to have more.

Why do you need to know anything about the number of pixels or the
size of the bar? That has nothing to do with the total amount of work.
If you know the number of times that you will iterate you know the
total amount of work that must be done. Just calculate your ((current
iteration count / total iterations to do) * 100 ) and see if you have
gone up by 1 since the last event and if so send the update.

Cody

···

On Tue, Jan 24, 2012 at 8:43 AM, Paul <poalman@gmail.com> wrote:

Thanks Cody,

A fair bit of that was added afterwards when I was tinkering to see what would
make a difference, I was comparing CallAfter with PostEvent to see if there

were

any differences (and as you can see I was curious to see what happens when I

did

both and forgot to put it back). The same with UIUpdates, I've not used these
before and was seeing what effect they had.

If I call g.Refresh should I remove the Refresh from setValue?

Also, it would help like you say to limit the number of events to 100-- or the
resolution of what the bar can show, so if the progress bar is expanded to

cover

for instance 300 pixels, how would I send 300 progress events instead? Could I
send the width of the bar to the worker, or what about if the bar resizes? or
maybe I should limit the number of events to a few hundred as I doubt it would
be very noticeable to have more.

I've limited the number of events to 100 but it's not made a difference, I think
limiting them to every 0.04 seconds might have been limiting it more.

But how can it be the number of events that's causing the problem if the native
bars in the demo work fine? The main thread gets the same number of events in
either case

Cody <codyprecord <at> gmail.com> writes:

Hi,

>
> If I call g.Refresh should I remove the Refresh from setValue?
>

Refresh just queues up a PaintEvent so that your paint handler will be
called. So you only need to call it once.

> Also, it would help like you say to limit the number of events to 100-- or

the

> resolution of what the bar can show, so if the progress bar is expanded to

cover

> for instance 300 pixels, how would I send 300 progress events instead? Could

I

> send the width of the bar to the worker, or what about if the bar resizes?

or

> maybe I should limit the number of events to a few hundred as I doubt it

would

> be very noticeable to have more.
>

Why do you need to know anything about the number of pixels or the
size of the bar? That has nothing to do with the total amount of work.
If you know the number of times that you will iterate you know the
total amount of work that must be done. Just calculate your ((current
iteration count / total iterations to do) * 100 ) and see if you have
gone up by 1 since the last event and if so send the update.

Cody

It would be coupling the worker thread a bit too closely with the GUI but,
because the progress bar is sizeable, if you chose it to be 5px wide you can get
away with sending only 5 progress events. But if the bar is 500px wide, if you
only send 100 progress events, each percent would jump the bar 5px. I was saying
that the worker could send a suitable number of events depending on what the
resolution of the bar was.

···

On Tue, Jan 24, 2012 at 8:43 AM, Paul <poalman <at> gmail.com> wrote:

Hi,

···

On Tue, Jan 24, 2012 at 8:56 AM, Paul <poalman@gmail.com> wrote:

I've limited the number of events to 100 but it's not made a difference, I think
limiting them to every 0.04 seconds might have been limiting it more.

But how can it be the number of events that's causing the problem if the native
bars in the demo work fine? The main thread gets the same number of events in
either case

Can you attach a runnable version of that sample? The formatting gets
all messed up when you include it inline in the message.

Regards,

Cody

Can you attach a runnable version of that sample? The formatting gets
all messed up when you include it inline in the message.

Regards,

Cody

I'm viewing this through a browser and as far as I can see there's no way to
attach files?

Switch to "the new Google Groups experience" to use the new Groups Web UI. It will allow you to attach files to your messages. Try going to https://groups.google.com/d/?fromgroups, if that doesn't work then there is probably a link on your main Groups page.

···

On 1/24/12 7:32 AM, Paul wrote:

Can you attach a runnable version of that sample? The formatting gets
all messed up when you include it inline in the message.

Regards,

Cody

I'm viewing this through a browser and as far as I can see there's no way to
attach files?

--
Robin Dunn
Software Craftsman

Thanks Robin,

I’ve attached the demo

gauges.py (7.36 KB)

Posted the attachment a few hours ago but my post still hasn’t appeared… posting it again.

testgauges.py (3.44 KB)

Robin Dunn <robin <at> alldunn.com> writes:

>> Can you attach a runnable version of that sample? The formatting gets
>> all messed up when you include it inline in the message.

Cody- I posted quite a few hours ago but still nothing showing up. The code I
posted in the first post hasn't been messed up too much by the formatting, just 5
or so lines I think. The custom and normal gauge sizer lines need to go outside
the for loops in TestFrame init and the others should be clear

>
> I'm viewing this through a browser and as far as I can see there's no way to
> attach files?
>
>

Switch to "the new Google Groups experience" to use the new Groups Web
UI. It will allow you to attach files to your messages. Try going to
https://groups.google.com/d/?fromgroups, if that doesn't work then there
is probably a link on your main Groups page.

Thanks Robin!

···

On 1/24/12 7:32 AM, Paul wrote:

Robin Dunn <robin <at> alldunn.com> writes:

>> Can you attach a runnable version of that sample? The formatting gets
>> all messed up when you include it inline in the message.
>>
>> Regards,
>>
>> Cody
>>
>
> I'm viewing this through a browser and as far as I can see there's no way to
> attach files?
>
>

Switch to "the new Google Groups experience" to use the new Groups Web
UI. It will allow you to attach files to your messages. Try going to
https://groups.google.com/d/?fromgroups, if that doesn't work then there
is probably a link on your main Groups page.

Hey Robin,

Sorry to ask you directly but this has me completely baffled.

Do you know why the drawing is slowing down the other thread? If it was to do
with the event posting then I don't understand how the native bars arn't slowing
down the threads using the same method

···

On 1/24/12 7:32 AM, Paul wrote:

Sorry, it looks like your message was held in the moderation queue and I haven't been getting those notifications for some reason. (This is why there are suddenly a bunch of messages sent to the group with timestamps back to the 13th...)

I'll take a look at your sample soon.

···

On 1/31/12 12:23 PM, Paul wrote:

Robin Dunn<robin<at> alldunn.com> writes:

On 1/24/12 7:32 AM, Paul wrote:

Can you attach a runnable version of that sample? The formatting gets
all messed up when you include it inline in the message.

Regards,

Cody

I'm viewing this through a browser and as far as I can see there's no way to
attach files?

Switch to "the new Google Groups experience" to use the new Groups Web
UI. It will allow you to attach files to your messages. Try going to
https://groups.google.com/d/?fromgroups, if that doesn't work then there
is probably a link on your main Groups page.

Hey Robin,

Sorry to ask you directly but this has me completely baffled.

Do you know why the drawing is slowing down the other thread? If it was to do
with the event posting then I don't understand how the native bars arn't slowing
down the threads using the same method

--
Robin Dunn
Software Craftsman

Hi,

···

On Tue, Jan 24, 2012 at 11:37 AM, Paul <poalman@gmail.com> wrote:

Thanks Robin,

I've attached the demo

What is PythonApp.gauge? It is missing from the sample. Changing the
sample to use the generic PyGauge shows that it runs quickly. So the
problem is likely in that other gauge class which is not included.

Cody