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(  \
  59                           self, -1, self.figure )
  60 
  61         self._SetSize()
  62         self.Bind( wx.EVT_SIZE, self._SetSize )
  63 
  64         self.TeX = ''
  65         self.font_size = 20
  66         self.RenderEquation()
  67 
  68     #end __init__
  69 
  70     #----------------------------------
  71 
  72     def SetTeX( self, str ) :
  73 
  74         self.TeX = '$%s$' % (str)
  75         self.RenderEquation()
  76 
  77     #----------------------------------
  78 
  79     def RenderEquation( self ) :
  80 
  81         self.renderError = False        # Can save to file only if False
  82 
  83         try :
  84             self.figure.clear()
  85             self.figure.text( 0.05, 0.5, self.TeX, size=self.font_size )
  86             self.canvas.draw()
  87 
  88         except matplotlib.pyparsing.ParseFatalException :
  89 
  90             self.renderError = True     # Don't save the Tex error message to a file !
  91             self.figure.clear()
  92             self.figure.text( 0.05, 0.5, 'Parsing Error in MathTeX', size=self.font_size )
  93             self.canvas.draw()
  94 
  95         #end try
  96 
  97     #end def
  98 
  99     #----------------------------------
 100 
 101     def _SetSize( self, evt=None ) :
 102 
 103         pixels = self.GetSize()
 104         self.SetSize( pixels )
 105         self.canvas.SetSize( pixels )
 106 
 107         dpi = self.figure.get_dpi()
 108         self.figure.set_size_inches( float( pixels[0] ) / dpi,
 109                                      float( pixels[1] ) / dpi )
 110     #end def
 111 
 112 #------------------------------------------------------------------------------
 113 
 114 class MathFrame( wx.Frame ) :
 115 
 116     def __init__( self ) :
 117 
 118         wx.Frame.__init__( self, None, -1, pos=(10, 10),
 119                            title='Matplotlib Math EquationRenderer Test'  )
 120         self.ClientSize = (800, 275)
 121 
 122         # Frames need an initial panel to provide tab traversal and
 123         # cross-platform background color capabilities.
 124         frmPanel = wx.Panel( self )
 125 
 126         #-----
 127 
 128         self.math_panel = MathPanel( frmPanel )
 129 
 130         self.input_box = wx.TextCtrl( frmPanel, size=(500, -1) )
 131         self.input_box.Font = wx.Font( 10,
 132                                        wx.FONTFAMILY_TELETYPE,
 133                                        wx.FONTSTYLE_NORMAL,
 134                                        wx.FONTWEIGHT_NORMAL )
 135         self.input_box.Bind( wx.EVT_TEXT, self.OnText )
 136 
 137         # Set a default equation. Show some fancy math symbols.
 138         equation = r'Goober = {\min(\int\ (\ {\delta{(\ \pi{}*\frac{\sum(\ a+\O\o\l\S\P\L\{b\}\ )\ } {( c-d )}})}\ )}'
 139         self.input_box.Value = equation
 140 
 141         label_stTxt = wx.StaticText( frmPanel, label='Type some TeX here :' )
 142 
 143         saveBtn = wx.Button( frmPanel, label='Save Equation to File' )
 144         saveBtn.Bind( wx.EVT_LEFT_DOWN, self.OnSaveToFileBtn )
 145 
 146         exitBtn = wx.Button( frmPanel, label='Exit' )
 147         exitBtn.Bind( wx.EVT_LEFT_DOWN, self.OnExit )
 148 
 149         #-----  Layout
 150 
 151         frmPnl_vertSzr = wx.BoxSizer( wx.VERTICAL )
 152 
 153         frmPnl_vertSzr.Add( label_stTxt,     proportion=0, flag=wx.TOP|wx.LEFT, border=5 )
 154         frmPnl_vertSzr.Add( self.input_box,  proportion=0, flag=wx.GROW|wx.ALL, border=5 )
 155         frmPnl_vertSzr.Add( self.math_panel, proportion=1, flag=wx.GROW )
 156         frmPnl_vertSzr.Add( saveBtn, proportion=0,
 157                             flag=wx.ALIGN_CENTER|wx.BOTTOM|wx.TOP, border=10 )
 158         frmPnl_vertSzr.Add( exitBtn, proportion=0,
 159                             flag=wx.ALIGN_CENTER|wx.BOTTOM,        border=10 )
 160 
 161         frmPanel.SetSizerAndFit( frmPnl_vertSzr )
 162 
 163     #end __init__
 164 
 165     #----------------------------------
 166 
 167     def OnText( self, evt ) :
 168 
 169         self.math_panel.SetTeX( self.input_box.Value )
 170 
 171     #----------------------------------
 172 
 173     def OnExit( self, evt ) :
 174 
 175         self.Close()
 176 
 177     #----------------------------------
 178 
 179     def OnSaveToFileBtn( self, event ) :
 180 
 181         if not self.math_panel.renderError :
 182 
 183             filename = 'Rendered_Equation.png'
 184             self.math_panel.figure.savefig( filename, dpi=300 )
 185             print '\n----  Equation Graphic Saved to File [ %s ]' % filename
 186 
 187             # See if the PIL package is installed.
 188             pilIsInstalled = True
 189             try :
 190                 # Try to crop the image to its near-minimum extent.
 191                 import Image        # PIL (Python Image Library)
 192                 import ImageChops   # Image Channel Operations library
 193                 import ImageStat    # Image Statistics library
 194                 import ImageOps     # Various whole image operations.
 195 
 196             except :
 197                 pilIsInstalled = False   # Image will not get auto-cropped.
 198             #end try
 199 
 200             if pilIsInstalled :      # Auto-crop the image.
 201                 pilImg = Image.open( filename )
 202 
 203                 # Find the ordinates of the minimally enclosing bounding box.
 204                 #
 205                 # Create a simplified and inverted version of the original image to examine.
 206                 invertedImage = ImageChops.invert( pilImg.convert( 'L' ) )   # BG must be black to examine.
 207 
 208                 # Get the bounding box's ordinates. Works on any image with a black background.
 209                 box = invertedImage.getbbox()                 pilImg = pilImg.crop( box )
 210 
 211                 # Add back a thin border padding. This is arbitrary, but seems reasonable.
 212                 pilImg = ImageOps.expand( pilImg, border=10, fill=(255, 255, 255) )
 213 
 214                 # Save the image to a disk file. Only PNG and TIFF formats are non-destructive.
 215                 pilImg.save( filename )
 216                 print '      Cropped Equation Graphic Saved'
 217 
 218             #end if
 219 
 220         else :
 221             print '\n---- Tex Rendering Error:  Figure Image NOT SAVED to a File.'
 222         #end if
 223 
 224     #end def
 225 
 226 #end MathFrame class
 227 
 228 #==============================================================================
 229 
 230 if __name__ == '__main__' :
 231 
 232     # What packages are installed ?
 233     import os, sys, platform
 234     print
 235     if os.name == 'nt' :
 236         print 'Windows  ', platform.win32_ver()[1]
 237     else :
 238         print 'Platform ', platform.system()
 239     #end if
 240     print 'Python   ', sys.version
 241 
 242     addon_pkgs = [ ('Wx        ', 'wx.VERSION_STRING'),
 243                    ('Matplotlib', 'matplotlib.__version__'), ]
 244 
 245     try :
 246         import Image        # Doesn't need to be installed.
 247         pilStr = 'PIL       '
 248         pilAddonStr = 'Image.VERSION'
 249         addon_pkgs.append( (pilStr      , pilAddonStr) )
 250     except :
 251         pass
 252     #end try
 253 
 254     for addonStr, attribute in addon_pkgs :
 255         try :
 256             print addonStr, eval( attribute )
 257         except NameError :
 258             print
 259         #end try
 260     #end for
 261     print
 262 
 263     #----
 264 
 265     app = wx.App( redirect=False )
 266     appFrame = MathFrame().Show()
 267     app.MainLoop()
 268 
 269 #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 pool-96-245-107-85)

NOTE: To edit pages in this wiki you must be a member of the TrustedEditorsGroup.