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 :
