source: sasview/guitools/PlotPanel.py @ e40b651

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 e40b651 was 15f0e94, checked in by Mathieu Doucet <doucetm@…>, 16 years ago

Fixed it again…

  • 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            col = self._color(color)
212            self.subplot.errorbar(x, y, yerr=dy, xerr=None,
213             ecolor=col, capsize=2,linestyle='', barsabove=False,
214             marker=self._symbol(symbol),
215             lolims=False, uplims=False,
216             xlolims=False, xuplims=False,label=label,
217             mec = col, mfc = col)
218           
219        self.subplot.set_yscale(self.yscale)
220
221    def curve(self,x,y,dy=None,color=0,symbol=0,label=None):
222        """Draw a line on a graph, possibly with confidence intervals."""
223        c = self._color(color)
224        self.subplot.set_yscale('linear')
225       
226        hlist = self.subplot.plot(x,y,color=c,marker='',linestyle='-',label=label)
227       
228        self.subplot.set_yscale(self.yscale)
229
230    def _color(self,c):
231        """Return a particular colour"""
232        return self.colorlist[c%len(self.colorlist)]
233
234    def _symbol(self,s):
235        """Return a particular symbol"""
236        return self.symbollist[s%len(self.symbollist)]
237
238
239   
240
241class NoRepaintCanvas(FigureCanvasWxAgg):
242    """We subclass FigureCanvasWxAgg, overriding the _onPaint method, so that
243    the draw method is only called for the first two paint events. After that,
244    the canvas will only be redrawn when it is resized.
245    """
246    def __init__(self, *args, **kwargs):
247        FigureCanvasWxAgg.__init__(self, *args, **kwargs)
248        self._drawn = 0
249
250    def _onPaint(self, evt):
251        """
252        Called when wxPaintEvt is generated
253        """
254        if not self._isRealized:
255            self.realize()
256        if self._drawn < 2:
257            self.draw(repaint = False)
258            self._drawn += 1
259        self.gui_repaint(drawDC=wx.PaintDC(self))
260           
Note: See TracBrowser for help on using the repository browser.