Today I read a thread at the wxPython user group about someone who was looking for a math equation editor that used Latex formulas: http://groups.google.com/group/wxpython-users/browse_thread/thread/33be14898871c5fa

Chris Barker suggested using the Matplotlib package and the Aggdraw_for_wx package. He provided a demo app that rendered and displayed a math equation. I've filled out his code with the abilities to save the rendered image to a PNG file and also to also crop the image to just the minimum that's needed using the PIL (Python Imaging Library) package. I put in the equation some fancy math symbols to show off its output at an arbitrary 300 dpi.

If PIL is not installed on your system then the output image simply won't get cropped. You'll have to do that yourself with any of a multitude of third-party image or graphics editors such as Photoshop, the GIMP or HyperSnap-DX, etc., etc.

I've got to admit that I know nothing about Matplotlib, yet I was able to guess how to create some common symbols. I suppose that if I actually read its manual I'd figure out how to draw all the Greek and special math symbols. But, no matter. Here's the demo code:

   1 #!/usr/bin/env python
   2 
   3 """
   4 Test of embedding matplotlib, and using it to render a mathematical equation.
   5 
   6 Original demo by Chris Barker   2010-12-15
   7 Ergonomic, cropping and file saving additions by Ray Pasco     2010-12-15
   8 
   9 Tested using :
  10 
  11 Windows   6.1.7600   [ Win7 64-bit running on an AMD CPU ]
  12 Python    2.6.5 (r265:79096, Mar 19 2010, 21:48:26) [MSC v.1500 32 bit (Intel)]
  13 Wx         2.8.11.0
  14 Matplotlib 1.0.0
  15 PIL        1.1.7
  16 """
  17 
  18 import time
  19 
  20 import wx
  21 
  22 # These imports take an incredibly long time. [ ~1.8 seconds ]
  23 # Let the user know this is normal and expected.
  24 print '\n----  Importing Matplotlib and Related Modules...'
  25 start = time.clock() # measure the import time
  26 #
  27 import matplotlib
  28 matplotlib.use( 'WXAgg' )   # AggDraw for wxPython.
  29 import matplotlib.figure
  30 import matplotlib.backends.backend_wxagg
  31 #
  32 end = time.clock()
  33 print '      Import Time =', end-start, 'seconds'
  34 
  35 #------------------------------------------------------------------------------
  36 
  37 class MathPanel( wx.Panel ) :
  38     """
  39     The MathPanel is a very simple panel with just and MPL figure on it,
  40     it will automatically render text in the middle of the figure
  41     """
  42 
  43     def __init__( self, parent ) :
  44 
  45         wx.Panel.__init__( self, parent=parent, size=(500, 200) )
  46 
  47         #-----
  48 
  49         # initialize matplotlib stuff.
  50         #
  51         # To make all new figure backgrounds be initialized to white
  52         # edit the file [ \Lib\site-packages\matplotlib\mpl-data\matplotlibrc ]
  53         # Find line [ figure.facecolor : 0.75 ] in section [ ### FIGURE ] and change it to
  54         # [ figure.facecolor : 1.00 ]. Who would want a gray background color by default !?
  55         #
  56         # Create the "figure" and set its background color to white.
  57         self.figure = matplotlib.figure.Figure( None, facecolor='white' )
  58         self.canvas = matplotlib.backends.backend_wxagg.FigureCanvasWxAgg(  \
                          self, -1, self.figure )
  59 
  60         self._SetSize()
  61         self.Bind( wx.EVT_SIZE, self._SetSize )
  62 
  63         self.TeX = ''
  64         self.font_size = 20
  65         self.RenderEquation()
  66 
  67     #end __init__
  68 
  69     #----------------------------------
  70 
  71     def SetTeX( self, str ) :
  72 
  73         self.TeX = '$%s$' % (str)
  74         self.RenderEquation()
  75 
  76     #----------------------------------
  77 
  78     def RenderEquation( self ) :
  79 
  80         self.renderError = False        # Can save to file only if False
  81 
  82         try :
  83             self.figure.clear()
  84             self.figure.text( 0.05, 0.5, self.TeX, size=self.font_size )
  85             self.canvas.draw()
  86 
  87         except matplotlib.pyparsing.ParseFatalException :
  88 
  89             self.renderError = True     # Don't save the Tex error message to a file !
  90             self.figure.clear()
  91             self.figure.text( 0.05, 0.5, 'Parsing Error in MathTeX', size=self.font_size )
  92             self.canvas.draw()
  93 
  94         #end try
  95 
  96     #end def
  97 
  98     #----------------------------------
  99 
 100     def _SetSize( self, evt=None ) :
 101 
 102         pixels = self.GetSize()
 103         self.SetSize( pixels )
 104         self.canvas.SetSize( pixels )
 105 
 106         dpi = self.figure.get_dpi()
 107         self.figure.set_size_inches( float( pixels[0] ) / dpi,
 108                                      float( pixels[1] ) / dpi )
 109     #end def
 110 
 111 #------------------------------------------------------------------------------
 112 
 113 class MathFrame( wx.Frame ) :
 114 
 115     def __init__( self ) :
 116 
 117         wx.Frame.__init__( self, None, -1, pos=(10, 10),
 118                            title='Matplotlib Math EquationRenderer Test'  )
 119         self.ClientSize = (800, 275)
 120 
 121         # Frames need an initial panel to provide tab traversal and
 122         # cross-platform background color capabilities.
 123         frmPanel = wx.Panel( self )
 124 
 125         #-----
 126 
 127         self.math_panel = MathPanel( frmPanel )
 128 
 129         self.input_box = wx.TextCtrl( frmPanel, size=(500, -1) )
 130         self.input_box.Font = wx.Font( 10,
 131                                        wx.FONTFAMILY_TELETYPE,
 132                                        wx.FONTSTYLE_NORMAL,
 133                                        wx.FONTWEIGHT_NORMAL )
 134         self.input_box.Bind( wx.EVT_TEXT, self.OnText )
 135 
 136         # Set a default equation. Show some fancy math symbols.
 137         equation = r'Goober = {\min(\int\ (\ {\delta{(\ \pi{}*\frac{\sum(\ a+\O\o\l\S\P\L\{b\}\ )\ } {( c-d )}})}\ )}'
 138         self.input_box.Value = equation
 139 
 140         label_stTxt = wx.StaticText( frmPanel, label='Type some TeX here :' )
 141 
 142         saveBtn = wx.Button( frmPanel, label='Save Equation to File' )
 143         saveBtn.Bind( wx.EVT_LEFT_DOWN, self.OnSaveToFileBtn )
 144 
 145         exitBtn = wx.Button( frmPanel, label='Exit' )
 146         exitBtn.Bind( wx.EVT_LEFT_DOWN, self.OnExit )
 147 
 148         #-----  Layout
 149 
 150         frmPnl_vertSzr = wx.BoxSizer( wx.VERTICAL )
 151 
 152         frmPnl_vertSzr.Add( label_stTxt,     proportion=0, flag=wx.TOP|wx.LEFT, border=5 )
 153         frmPnl_vertSzr.Add( self.input_box,  proportion=0, flag=wx.GROW|wx.ALL, border=5 )
 154         frmPnl_vertSzr.Add( self.math_panel, proportion=1, flag=wx.GROW )
 155         frmPnl_vertSzr.Add( saveBtn, proportion=0,
 156                             flag=wx.ALIGN_CENTER|wx.BOTTOM|wx.TOP, border=10 )
 157         frmPnl_vertSzr.Add( exitBtn, proportion=0,
 158                             flag=wx.ALIGN_CENTER|wx.BOTTOM,        border=10 )
 159 
 160         frmPanel.SetSizerAndFit( frmPnl_vertSzr )
 161 
 162     #end __init__
 163 
 164     #----------------------------------
 165 
 166     def OnText( self, evt ) :
 167 
 168         self.math_panel.SetTeX( self.input_box.Value )
 169 
 170     #----------------------------------
 171 
 172     def OnExit( self, evt ) :
 173 
 174         self.Close()
 175 
 176     #----------------------------------
 177 
 178     def OnSaveToFileBtn( self, event ) :
 179 
 180         if not self.math_panel.renderError :
 181 
 182             filename = 'Rendered_Equation.png'
 183             self.math_panel.figure.savefig( filename, dpi=300 )
 184             print '\n----  Equation Graphic Saved to File [ %s ]' % filename
 185 
 186             # See if the PIL package is installed.
 187             pilIsInstalled = True
 188             try :
 189                 # Try to crop the image to its near-minimum extent.
 190                 import Image        # PIL (Python Image Library)
 191                 import ImageChops   # Image Channel Operations library
 192                 import ImageStat    # Image Statistics library
 193                 import ImageOps     # Various whole image operations.
 194 
 195             except :
 196                 pilIsInstalled = False   # Image will not get auto-cropped.
 197             #end try
 198 
 199             if pilIsInstalled :      # Auto-crop the image.
 200                 pilImg = Image.open( filename )
 201 
 202                 # Find the ordinates of the minimally enclosing bounding box.
 203                 #
 204                 # Create a simplified and inverted version of the original image to examine.
 205                 invertedImage = ImageChops.invert( pilImg.convert( 'L' ) )   # BG must be black to examine.
 206 
 207                 # Get the bounding box's ordinates. Works on any image with a black background.
 208                 box = invertedImage.getbbox()                 pilImg = pilImg.crop( box )
 209 
 210                 # Add back a thin border padding. This is arbitrary, but seems reasonable.
 211                 pilImg = ImageOps.expand( pilImg, border=10, fill=(255, 255, 255) )
 212 
 213                 # Save the image to a disk file. Only PNG and TIFF formats are non-destructive.
 214                 pilImg.save( filename )
 215                 print '      Cropped Equation Graphic Saved'
 216 
 217             #end if
 218 
 219         else :
 220             print '\n---- Tex Rendering Error:  Figure Image NOT SAVED to a File.'
 221         #end if
 222 
 223     #end def
 224 
 225 #end MathFrame class
 226 
 227 #==============================================================================
 228 
 229 if __name__ == '__main__' :
 230 
 231     # What packages are installed ?
 232     import os, sys, platform
 233     print
 234     if os.name == 'nt' :
 235         print 'Windows  ', platform.win32_ver()[1]
 236     else :
 237         print 'Platform ', platform.system()
 238     #end if
 239     print 'Python   ', sys.version
 240 
 241     addon_pkgs = [ ('Wx        ', 'wx.VERSION_STRING'),
 242                    ('Matplotlib', 'matplotlib.__version__'), ]
 243 
 244     try :
 245         import Image        # Doesn't need to be installed.
 246         pilStr = 'PIL       '
 247         pilAddonStr = 'Image.VERSION'
 248         addon_pkgs.append( (pilStr      , pilAddonStr) )
 249     except :
 250         pass
 251     #end try
 252 
 253     for addonStr, attribute in addon_pkgs :
 254         try :
 255             print addonStr, eval( attribute )
 256         except NameError :
 257             print
 258         #end try
 259     #end for
 260     print
 261 
 262     #----
 263 
 264     app = wx.App( redirect=False )
 265     appFrame = MathFrame().Show()
 266     app.MainLoop()
 267 
 268 #end if

Here is the file graphic reduced in size by a factor of 3 :

MatplotlibEquationEditor (last edited 2010-12-16 19:49:46 by WinCrazy)