#!/usr/bin/env python

"""
Test of embedding matplotlib, and using it to render a mathematical equation.

Original demo by Chris Barker   2010-12-15

Ergonomic, cropping and file saving additions by Ray Pasco     2010-12-15

Tested using :

Windows   6.1.7600
Python    2.6.5 (r265:79096, Mar 19 2010, 21:48:26) [MSC v.1500 32 bit (Intel)]
Wx         2.8.11.0
Matplotlib 1.0.0
PIL        1.1.7

"""

import time

import wx

# These imports take an incredibly long time.
# Let the user know this is normal and expected.
print '\n----  Importing Matplotlib and Related Modules...'
start = time.clock()
#
import matplotlib
matplotlib.use( 'WXAgg' )   # AggDraw for wxPython.
import matplotlib.figure
import matplotlib.backends.backend_wxagg
#
end = time.clock()
print '      Import Time =', end-start, 'seconds'

#------------------------------------------------------------------------------

class MathPanel( wx.Panel ) :
    """
    The MathPanel is a very simple panel with just and MPL figure on it,
    it will automatically render text in the middle of the figure
    """
    
    def __init__( self, parent ) :
        
        wx.Panel.__init__( self, parent=parent, size=(500, 200) )
        
        #-----
        
        # initialize matplotlib stuff.
        # To always make figure backgrounds be initialized to white 
        # edit file [ \Lib\site-packages\matplotlib\mpl-data\matplotlibrc ]
        # line [ figure.facecolor : 0.75 ] in section [ ### FIGURE ] to 
        # [ figure.facecolor : 1.00 ]. Who would want a gray background color by default ?
        #
        # Create the "figure" and set its background color to white,
        self.figure = matplotlib.figure.Figure( None, facecolor='white' )
        self.canvas = matplotlib.backends.backend_wxagg.FigureCanvasWxAgg(  \
                          self, -1, self.figure )
        
        self._SetSize()
        
        self.Bind( wx.EVT_SIZE, self._SetSize )
        
        self.TeX = ''
        self.font_size = 20
        
        self.RenderEquation()
        
    #end __init__
    
    #----------------------------------
    
    def SetTeX( self, str ) :
        
        self.TeX = '$%s$' % (str)
        self.RenderEquation()

    #----------------------------------
    
    def RenderEquation( self ) :
        
        self.renderError = False        # Can save to file only if False
        
        try :
            self.figure.clear()
            self.figure.text( 0.05, 0.5, self.TeX, size=self.font_size )
            self.canvas.draw()
            
        except matplotlib.pyparsing.ParseFatalException :
            
            self.renderError = True     # Can't save current rendering to a file.
            
            self.figure.clear()
            self.figure.text( 0.05, 0.5, 'Parsing Error in MathTeX', size=self.font_size )
            self.canvas.draw()
            
        #end try
            
    #end def
        
    #----------------------------------
    
    def _SetSize( self, evt=None ) :
        
        pixels = self.GetSize()
        self.SetSize( pixels )
        self.canvas.SetSize( pixels )
        self.figure.set_size_inches( float( pixels[0] ) / self.figure.get_dpi(), 
                                     float( pixels[1] ) / self.figure.get_dpi()  )
    #end def
        
#------------------------------------------------------------------------------

class MathFrame( wx.Frame ) :
    
    def __init__( self ) :
        
        wx.Frame.__init__( self, None, -1, pos=(10, 10), 
                           title='Matplotlib Math EquationRenderer Test'  )
        self.ClientSize = (800, 275)
        
        # Frames need an initial panel to provide tab traversal and
        # cross-platform background color capabilities.
        frmPanel = wx.Panel( self )
        
        #-----
        
        self.math_panel = MathPanel( frmPanel )
        
        self.input_box = wx.TextCtrl( frmPanel, size=(500, -1) )
        self.input_box.Font = wx.Font( 10,
                                       wx.FONTFAMILY_TELETYPE,
                                       wx.FONTSTYLE_NORMAL,
                                       wx.FONTWEIGHT_NORMAL )
        self.input_box.Bind( wx.EVT_TEXT, self.OnText )

        # Set a default equation. Show som special symbols. 
        equation = r'Goober = {\min(\int\ (\ {\delta{(\ \pi{}*\frac{\sum(\ a+\O\o\l\S\P\L\{b\}\ )\ } {( c-d )}})}\ )}'
        self.input_box.Value = equation
        
        label_stTxt = wx.StaticText( frmPanel, label='Type some TeX here :' )
        
        saveBtn = wx.Button( frmPanel, label='Save Equation to File' )
        saveBtn.Bind( wx.EVT_LEFT_DOWN, self.OnSaveToFileBtn )
        
        exitBtn = wx.Button( frmPanel, label='Exit' )
        exitBtn.Bind( wx.EVT_LEFT_DOWN, self.OnExit )
        
        #-----  Layout
        
        frmPnl_vertSzr = wx.BoxSizer( wx.VERTICAL )
        
        frmPnl_vertSzr.Add( label_stTxt,     proportion=0, flag=wx.TOP|wx.LEFT, border=5 )
        frmPnl_vertSzr.Add( self.input_box,  proportion=0, flag=wx.GROW|wx.ALL, border=5 )
        frmPnl_vertSzr.Add( self.math_panel, proportion=1, flag=wx.GROW )
        frmPnl_vertSzr.Add( saveBtn, proportion=0, 
                            flag=wx.ALIGN_CENTER|wx.BOTTOM|wx.TOP, border=10 )
        frmPnl_vertSzr.Add( exitBtn, proportion=0, 
                            flag=wx.ALIGN_CENTER|wx.BOTTOM,        border=10 )
        
        frmPanel.SetSizerAndFit( frmPnl_vertSzr )
        
    #end __init__
    
    #----------------------------------
    
    def OnText( self, evt ) :
        
        self.math_panel.SetTeX( self.input_box.Value )
    
    #----------------------------------
    
    def OnExit( self, evt ) :
        
        self.Close()
        
    #----------------------------------
    
    def OnSaveToFileBtn( self, event ) :
        
        if not self.math_panel.renderError :
        
            filename = 'Rendered_Equation.png'
            self.math_panel.figure.savefig( filename, dpi=300 )
            print '\n----  Equation Graphic Saved to File [ %s ]' % filename
            
            # See if the PIL package is installed.
            pilIsInstalled = True
            try :
                # Try to crop the image to its near-minimum extent.
                import Image        # PIL (Python Image Library)
                import ImageChops   # Image Channel Operations library
                import ImageStat    # Image Statistics library
                import ImageOps     # Various whole image operations.
                
            except :
                pilIsInstalled = False      # Image will not get cropped.
            #end try
            
            if pilIsInstalled :
                
                pilImg = Image.open( filename )
                
                # Find the ordinates of the minimally enclosing bounding box.
                #
                # Examine a simplified version of the original image
                invertedImage = ImageChops.invert( pilImg.convert( 'L' ) )
                
                # Get the bounding box's ordinates.
                box = invertedImage.getbbox()
                
                ## Add back a bit of padding all around the minimum extent.
                #xLeft, yTop, xRight, yRight = box
                #xLeft  -= 10    # arbitrary padding amount
                #yTop   -= 10
                #xRight += 10
                #yRight += 10
                #box = (xLeft, yTop, xRight, yRight)     # the expanded extent box
                
                # Crop the original image.
                pilImg = pilImg.crop( box )
                
                # Add back a thin border.
                pilImg = ImageOps.expand( pilImg, border=10, fill=(255, 255, 255) )
                
                # Save it to a disk file. Only PNG and TIFF formats are non-destructive.
                pilImg.save( filename )
                print '      Cropped Equation Graphic Saved'
                
            #end if
           
        else :
            print '\n---- Figure NOT Saved to File Due to the Previous Rendering Error.'
        #end if
        
    #end def
    
#end MathFrame class

#==============================================================================
        
if __name__ == '__main__' :

    # What packages are installed ?
    import os, sys, platform
    print
    if os.name == 'nt' :
        print 'Windows  ', platform.win32_ver()[1]
    else :
        print 'Platform ', platform.system()
    #end if
    print 'Python   ', sys.version
    
    addon_pkgs = [ ('Wx        ', 'wx.VERSION_STRING'), 
                   ('Matplotlib', 'matplotlib.__version__'), ]
                   
    try :
        import Image        # Doesn't need to be installed.
        pilStr = 'PIL       '
        pilAddonStr = 'Image.VERSION'
        addon_pkgs.append( (pilStr      , pilAddonStr) )
    except :
        pass
    #end try
    
    for addonStr, attribute in addon_pkgs :
        try :
            print addonStr, eval( attribute )
        except NameError :
            print
        #end try
    #end for
    print
    
    #----
    
    app = wx.App( redirect=False )
    appFrame = MathFrame().Show()
    app.MainLoop()

#end if
