Search code examples
pythonwxpython

wxPython Progress bar crashes and not works as expected


I am trying to implement a simple progress bar using wx.Gauge . Before moving to my original code i want to write small script as follows:

import time
for i in range(101):
    print i
    time.sleep(0.5)

The above script is just delaying the for loop. Now the same concept for the program is hanging my Ubuntu 12.04LTS and shows full progress bar at last. Here's the code:

# -*- coding: utf-8 -*- 
import wx
import time

class MainFrame ( wx.Frame ):

    def __init__( self, parent ):
        wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = u"Progress Bar Example", pos = wx.DefaultPosition, size = wx.Size( 460,135 ), style = wx.CAPTION|wx.CLOSE_BOX|wx.TAB_TRAVERSAL )

        self.SetSizeHintsSz( wx.DefaultSize, wx.DefaultSize )

        bSizer2 = wx.BoxSizer( wx.VERTICAL )

        self.progress = wx.Gauge( self, wx.ID_ANY, 100, wx.Point( 10,10 ), wx.Size( 400,-1 ), wx.GA_HORIZONTAL|wx.GA_SMOOTH )
        self.progress.SetValue( 0 ) 
        self.progress.SetForegroundColour( wx.Colour( 255, 255, 255 ) )

        bSizer2.Add( self.progress, 0, wx.ALL, 5 )

        self.start_again = wx.Button( self, wx.ID_ANY, u"START", wx.DefaultPosition, wx.DefaultSize, 0 )
        bSizer2.Add( self.start_again, 0, wx.ALL, 5 )


        self.SetSizer( bSizer2 )
        self.Layout()

        self.Centre( wx.BOTH )

        # Connect Events
        self.start_again.Bind( wx.EVT_BUTTON, self.run_progreee_bar )

    def __del__( self ):
        pass


    # Virtual event handlers, overide them in your derived class
    def run_progreee_bar( self, event ):
        self.progress.SetValue( 0 )
        for i in range(101):
            self.progress.SetValue( i )
            self.delay()
    def delay(self):
        time.sleep(0.5)

app  = wx.App()
frame = MainFrame(None)
frame.Show()
app.MainLoop()

I believe the program is syntactically correct. I think the logic is correct too. What is the problem I'm having in this code?


Solution

  • The reason why this is happening is your counter keeps increasing faster than app.MainLoop() can redraw the progress bar. As a result if you try to interact with your widget after pressing start it hangs until the loop has ended and shows the final state. You need to use threading to perform these tasks in GUI programming. I changed your code a bit. It should work fine now.

    import wx, time, thread
    
    class MainFrame ( wx.Frame ):
    
        def __init__( self, parent ):
            wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = u"Progress Bar Example", pos = wx.DefaultPosition, size = wx.Size( 460,135 ), style = wx.CAPTION|wx.CLOSE_BOX|wx.TAB_TRAVERSAL )
    
            self.SetSizeHintsSz( wx.DefaultSize, wx.DefaultSize )
    
            bSizer2 = wx.BoxSizer( wx.VERTICAL )
    
            self.progress = wx.Gauge( self, wx.ID_ANY, 100, wx.Point( 10,10 ), wx.Size( 400,-1 ), wx.GA_HORIZONTAL|wx.GA_SMOOTH )
            self.progress.SetValue( 0 ) 
            self.progress.SetForegroundColour( wx.Colour( 255, 255, 255 ) )
    
            bSizer2.Add( self.progress, 0, wx.ALL, 5 )
    
            self.start_again = wx.Button( self, wx.ID_ANY, u"START", wx.DefaultPosition, wx.DefaultSize, 0 )
            bSizer2.Add( self.start_again, 0, wx.ALL, 5 )
    
    
            self.SetSizer( bSizer2 )
            self.Layout()
    
            self.Centre( wx.BOTH )
    
            # Connect Events
            self.start_again.Bind( wx.EVT_BUTTON, self.run_progreee_bar)
    
        def __del__( self ):
            pass
    
    
        # Virtual event handlers, overide them in your derived class
        def run_progreee_bar( self, event ):
            thread.start_new_thread(self.update_progress_bar, ())
    
        def update_progress_bar(self):
            self.start_again.Disable()
            self.progress.SetValue( 0 )
            for i in range(101):
                self.progress.SetValue( i )
                self.delay()
            self.start_again.Enable()
    
        def delay(self):
            time.sleep(0.5)
    
    app  = wx.App()
    frame = MainFrame(None)
    frame.Show()
    app.MainLoop()
    

    You might also want to read up this article.