source: sasview/guitools/PlotPanel.py @ 057210c

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 057210c was 057210c, checked in by Mathieu Doucet <doucetm@…>, 16 years ago

Added a View class and an example

  • Property mode set to 100644
File size: 8.6 KB
Line 
1import wx
2import matplotlib
3matplotlib.interactive(False)
4#Use the WxAgg back end. The Wx one takes too long to render
5matplotlib.use('WXAgg')
6from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg
7from matplotlib.figure import Figure
8import os
9
10from canvas import FigureCanvas
11#TODO: make the plottables interactive
12from plottables import Graph
13
14def show_tree(obj,d=0):
15    """Handy function for displaying a tree of graph objects"""
16    print "%s%s" % ("-"*d,obj.__class__.__name__)
17    if 'get_children' in dir(obj):
18        for a in obj.get_children(): show_tree(a,d+1)
19
20
21
22class PlotPanel(wx.Panel):
23    """
24    The PlotPanel has a Figure and a Canvas. OnSize events simply set a
25    flag, and the actually redrawing of the
26    figure is triggered by an Idle event.
27    """
28    def __init__(self, parent, id = -1, color = None,\
29        dpi = None, **kwargs):
30        wx.Panel.__init__(self, parent, id = id, **kwargs)
31
32        self.figure = Figure(None, dpi)
33        #self.figure = pylab.Figure(None, dpi)
34        #self.canvas = NoRepaintCanvas(self, -1, self.figure)
35        self.canvas = FigureCanvas(self, -1, self.figure)
36        self.SetColor(color)
37        #self.Bind(wx.EVT_IDLE, self._onIdle)
38        #self.Bind(wx.EVT_SIZE, self._onSize)
39        self._resizeflag = True
40        self._SetSize()
41        self.subplot = self.figure.add_subplot(111)
42        self.figure.subplots_adjust(left=.2, bottom=.2)
43        self.yscale = 'linear'
44
45        sizer = wx.BoxSizer(wx.VERTICAL)
46        sizer.Add(self.canvas,1,wx.EXPAND)
47        self.SetSizer(sizer)
48
49        # Graph object to manage the plottables
50        self.graph = Graph()
51       
52        self.Bind(wx.EVT_CONTEXT_MENU, self.onContextMenu)
53
54        # Define some constants
55        self.colorlist = ['b','g','r','c','m','y']
56        self.symbollist = ['o','x','^','v','<','>','+','s','d','D','h','H','p']
57
58    def set_yscale(self, scale='linear'):
59        self.subplot.set_yscale(scale)
60        self.yscale = scale
61       
62    def get_yscale(self):
63        return self.yscale
64
65    def SetColor(self, rgbtuple):
66        """Set figure and canvas colours to be the same"""
67        if not rgbtuple:
68            rgbtuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get()
69        col = [c/255.0 for c in rgbtuple]
70        self.figure.set_facecolor(col)
71        self.figure.set_edgecolor(col)
72        self.canvas.SetBackgroundColour(wx.Colour(*rgbtuple))
73
74    def _onSize(self, event):
75        self._resizeflag = True
76
77    def _onIdle(self, evt):
78        if self._resizeflag:
79            self._resizeflag = False
80            self._SetSize()
81            self.draw()
82
83    def _SetSize(self, pixels = None):
84        """
85        This method can be called to force the Plot to be a desired size, which defaults to
86        the ClientSize of the panel
87        """
88        if not pixels:
89            pixels = self.GetClientSize()
90        self.canvas.SetSize(pixels)
91        self.figure.set_size_inches(pixels[0]/self.figure.get_dpi(),
92        pixels[1]/self.figure.get_dpi())
93
94    def draw(self):
95        """Where the actual drawing happens"""
96        self.figure.canvas.draw_idle()
97       
98
99
100    def onSaveImage(self, evt):
101        #figure.savefig
102        print "Save image not implemented"
103        path = None
104        dlg = wx.FileDialog(self, "Choose a file", os.getcwd(), "", "*.png", wx.SAVE)
105        if dlg.ShowModal() == wx.ID_OK:
106            path = dlg.GetPath()
107            mypath = os.path.basename(path)
108            print path
109        dlg.Destroy()
110        if not path == None:
111            self.subplot.figure.savefig(path,dpi=300, facecolor='w', edgecolor='w',
112                                        orentation='portrait', papertype=None, format='png')
113       
114    def onContextMenu(self, event):
115        """
116            Default context menu for a plot panel
117        """
118        # Slicer plot popup menu
119        slicerpop = wx.Menu()
120        slicerpop.Append(313,'&Save image', 'Save image as PNG')
121        wx.EVT_MENU(self, 313, self.onSaveImage)
122
123        pos = event.GetPosition()
124        pos = self.ScreenToClient(pos)
125        self.PopupMenu(slicerpop, pos)
126       
127   
128    ## The following is plottable functionality
129
130
131    def properties(self,prop):
132        """Set some properties of the graph.
133       
134        The set of properties is not yet determined.
135        """
136        # The particulars of how they are stored and manipulated (e.g., do
137        # we want an inventory internally) is not settled.  I've used a
138        # property dictionary for now.
139        #
140        # How these properties interact with a user defined style file is
141        # even less clear.
142
143        # Properties defined by plot
144        self.subplot.set_xlabel(r"$%s$" % prop["xlabel"])
145        self.subplot.set_ylabel(r"$%s$" % prop["ylabel"])
146        self.subplot.set_title(prop["title"])
147
148        # Properties defined by user
149        #self.axes.grid(True)
150
151    def clear(self):
152        """Reset the plot"""
153       
154        # TODO: Redraw is brutal.  Render to a backing store and swap in
155        # TODO: rather than redrawing on the fly.
156        self.subplot.clear()
157        self.subplot.hold(True)
158
159
160    def render(self):
161        """Commit the plot after all objects are drawn"""
162        # TODO: this is when the backing store should be swapped in.
163        from matplotlib.font_manager import FontProperties
164        self.subplot.legend(prop=FontProperties(size=10))
165        #self.subplot.legend()
166        pass
167
168    def xaxis(self,label,units):
169        """xaxis label and units.
170       
171        Axis labels know about units.
172       
173        We need to do this so that we can detect when axes are not
174        commesurate.  Currently this is ignored other than for formatting
175        purposes.
176        """
177        if units != "": label = label + " (" + units + ")"
178        self.subplot.set_xlabel(label)
179        pass
180   
181    def yaxis(self,label,units):
182        """yaxis label and units."""
183        if units != "": label = label + " (" + units + ")"
184        self.subplot.set_ylabel(label)
185        pass
186
187    def _connect_to_xlim(self,callback):
188        """Bind the xlim change notification to the callback"""
189        def process_xlim(axes):
190            lo,hi = subplot.get_xlim()
191            callback(lo,hi)
192        self.subplot.callbacks.connect('xlim_changed',process_xlim)
193   
194    #def connect(self,trigger,callback):
195    #    print "PlotPanel.connect???"
196    #    if trigger == 'xlim': self._connect_to_xlim(callback)
197
198    def points(self,x,y,dx=None,dy=None,color=0,symbol=0,label=None):
199        """Draw markers with error bars"""
200        self.subplot.set_yscale('linear')
201        # Convert tuple (lo,hi) to array [(x-lo),(hi-x)]
202        if dx != None and type(dx) == type(()):
203            dx = nx.vstack((x-dx[0],dx[1]-x)).transpose()
204        if dy != None and type(dy) == type(()):
205            dy = nx.vstack((y-dy[0],dy[1]-y)).transpose()
206
207        if dx==None and dy==None:
208            h = self.subplot.plot(x,y,color=self._color(color),
209                                   marker=self._symbol(symbol),linestyle='',label=label)
210        else:
211            self.subplot.errorbar(x, y, yerr=dy, xerr=None,
212             ecolor=self._color(color), capsize=2,linestyle='', barsabove=False,
213             marker=self._symbol(symbol),
214             lolims=False, uplims=False,
215             xlolims=False, xuplims=False,label=label)
216           
217        self.subplot.set_yscale(self.yscale)
218
219    def curve(self,x,y,dy=None,color=0,symbol=0,label=None):
220        """Draw a line on a graph, possibly with confidence intervals."""
221        c = self._color(color)
222        self.subplot.set_yscale('linear')
223       
224        hlist = self.subplot.plot(x,y,color=c,marker='',linestyle='-',label=label)
225       
226        self.subplot.set_yscale(self.yscale)
227
228    def _color(self,c):
229        """Return a particular colour"""
230        return self.colorlist[c%len(self.colorlist)]
231
232    def _symbol(self,s):
233        """Return a particular symbol"""
234        return self.symbollist[s%len(self.symbollist)]
235
236
237   
238
239class NoRepaintCanvas(FigureCanvasWxAgg):
240    """We subclass FigureCanvasWxAgg, overriding the _onPaint method, so that
241    the draw method is only called for the first two paint events. After that,
242    the canvas will only be redrawn when it is resized.
243    """
244    def __init__(self, *args, **kwargs):
245        FigureCanvasWxAgg.__init__(self, *args, **kwargs)
246        self._drawn = 0
247
248    def _onPaint(self, evt):
249        """
250        Called when wxPaintEvt is generated
251        """
252        if not self._isRealized:
253            self.realize()
254        if self._drawn < 2:
255            self.draw(repaint = False)
256            self._drawn += 1
257        self.gui_repaint(drawDC=wx.PaintDC(self))
258           
Note: See TracBrowser for help on using the repository browser.