
"""
ThreadedProcess_Try.py

Derived from "wxPython and Threads" in "Mouse vs python" by Mike Driscol
@ http://www.blog.pythonlibrary.org/2010/05/22/wxpython-and-threads/

Demonstration of a threaded process which sends back multiple
message types using a single message name.

"""

import time
import random
from threading import Thread

import wx
 
from wx.lib.pubsub import Publisher

#------------------------------------------------------------------------------

class MyFrame( wx.Frame ) :
 
    def __init__( self ) :
        
        wx.Frame.__init__( self, None, -1, 'Threading Tutorial', pos=(40, 40) )
        self.SetClientSize( (500, 500) )
        
        self.frmPanel = wx.Panel( self, wx.ID_ANY )
        
        self.btn = wx.Button( self.frmPanel, label='Start Thread' )
        self.btn.Bind( wx.EVT_BUTTON, self.OnButton )
        
        self.displayTxtCtrl = wx.TextCtrl( self.frmPanel, -1, 
                                           style=wx.TE_MULTILINE|wx.TE_READONLY )
        font = wx.Font( 9, wx.MODERN, wx.NORMAL, wx.NORMAL, False )
        self.displayTxtCtrl.SetFont( font )     #  a bigger font
        
        #-----  Lay out the two controls
        
        self.LayoutFrmPanel()
        
        #-----
        
        # Create a pubsub receiver.
        Publisher().subscribe( self.DisplayThreadMessages, 'update' )
        
    #end __init__
    
    #----------------------------------
    
    def LayoutFrmPanel( self ) :
        """ A simple and clean layout. """
        
        sizer = wx.BoxSizer( wx.VERTICAL )
        sizer.Add ( (0, 15) )                                       # a vertical spacer at the top
        sizer.Add( self.btn,            0, wx.CENTER  )             # center using its "best size"
        sizer.Add ( (0, 15) )                                       # spacer between controls
        sizer.Add( self.displayTxtCtrl, 1, wx.EXPAND|wx.CENTER )    # Expand along both axes.
        sizer.Add ( (0, 15) )                                       # bottom spacer
        
        self.frmPanel.SetSizer( sizer )
    
    #----------------------------------
    
    def OnButton( self, event ) :
        """ Creates the thread and then starts it. """
        
        self.btn.Disable()
        
        status = TestThread()               # Create and start the thread.
        statusMsg = 'OnButton():    status = %s\n\n' % (status)
        
        self.displayTxtCtrl.WriteText( '\nOnButton():    Thread started.\n' )
        self.displayTxtCtrl.WriteText( statusMsg )
        
        self.btn.Disable()
        
    #end def
    
    #----------------------------------
    
    def DisplayThreadMessages( self, msg ) :
        """ Receives data from thread and updates the display. """

        msgData = msg.data
        if isinstance( msgData, str ) :
            self.displayTxtCtrl.WriteText( 'Textual message = [ %s ]\n' % (msgData) )
        
        elif isinstance( msgData, float ) :
            self.displayTxtCtrl.WriteText( '\n' )   # A blank line separator.
            self.displayTxtCtrl.WriteText( 'Processing time was [ %s ] secs.\n' % (msgData) )
        
        elif isinstance( msgData, int ) :
            
            if (msgData == -1) :    # This flag value indicates 'Thread processing has completed'.
                self.btn.Enable()   # The GUI is now ready for another thread to start.
                self.displayTxtCtrl.WriteText( 'Integer ThreadCompletedFlag = [ %d ]\n' % (msgData) )
            
            else :
                self.displayTxtCtrl.WriteText( 'Integer Calculation Result = [ %d ]\n' % (msgData) )
        #end if
        
    #end DisplayThreadMessages def
    
#end MyFrame class

#------------------------------------------------------------------------------
#==============================================================================
#------------------------------------------------------------------------------

class TestThread( Thread ) :
    """Test Worker Thread Class."""
    
    def __init__( self ) :
        """Init Worker Thread Class."""
        
        Thread.__init__( self )
        self.start()            # Start this thread
        
    #end __init__
    
    #----------------------------------
    
    def run( self ) :   # Do NOT change this function's name.
        """ 
        Execute the worker thread's processing.
        
        NOTE:  This function overides the Publisher function of the same name.
        """
        
        wx.CallAfter( Publisher().sendMessage, 'update', 'TestThread::run():    Thread started.' )
        
        # This is the code executed in the thread.
        for result in range( 1, 6 ) :
            
            # Simulate a long and variable processing duration each loop.
            processingDuration = random.randint( 100, 1500) / 1000.0       # 0.2 to 1.5 seconds
            print processingDuration
            time.sleep( processingDuration )
            
            wx.CallAfter( self.PostResults, processingDuration )
            wx.CallAfter( self.PostResults, result )
            
        #end for
            
        wx.CallAfter( Publisher().sendMessage, 'update', 'TestThread::run():    Thread finished.' )
        wx.CallAfter( Publisher().sendMessage, 'update', -1 )
        
    #end Run def
    
    #----------------------------------
    
    def PostResults( self, result ) :
        """ Send time to GUI """
        
        Publisher().sendMessage( 'update', result )
        
    #end def
    
#end TestThread class

#==============================================================================

if __name__ == '__main__' :
    
    app = wx.PySimpleApp( redirect=False )
    
    frame = MyFrame()
    frame.Show()
    
    app.MainLoop()
    
#end if