[c510183] | 1 | """ |
---|
| 2 | Class that manages what is being displayed on the 1D plot |
---|
| 3 | of a SANS modeling application |
---|
| 4 | |
---|
| 5 | TODO: Eliminate the code to re-read the data in menu_Simple and Menu_Guinier. |
---|
| 6 | Modify the way you store and handle the data in Data1D |
---|
| 7 | |
---|
| 8 | TODO: use only one menu entry for "Toggle between Q and Q**2 scale" instead |
---|
| 9 | of have one menu entry for Q and one menu entry for Q**2. |
---|
| 10 | You want to be consistent in the way you present options for the x-axis |
---|
| 11 | and y-axis. |
---|
| 12 | |
---|
| 13 | """ |
---|
| 14 | |
---|
| 15 | import wx |
---|
| 16 | import wx.lib.newevent |
---|
| 17 | |
---|
| 18 | (FunctionParamEvent, EVT_FUNC_PARS) = wx.lib.newevent.NewEvent() |
---|
| 19 | |
---|
| 20 | from PlotPanel import PlotPanel |
---|
| 21 | import numpy, pylab |
---|
| 22 | from plottables import Plottable, Graph |
---|
| 23 | import os |
---|
| 24 | import math |
---|
| 25 | from scipy import optimize |
---|
| 26 | |
---|
| 27 | |
---|
| 28 | |
---|
| 29 | DEFAULT_QMIN = 0 |
---|
| 30 | DEFAULT_QMAX = 0.05 |
---|
| 31 | DEFAULT_QSTEP = 0.001 |
---|
| 32 | |
---|
| 33 | |
---|
| 34 | class Data1D(Plottable): |
---|
| 35 | """Data plottable: scatter plot of x,y with errors in x and y. |
---|
| 36 | """ |
---|
| 37 | |
---|
| 38 | def __init__(self,x,y,dx=None,dy=None): |
---|
| 39 | """Draw points specified by x[i],y[i] in the current color/symbol. |
---|
| 40 | Uncertainty in x is given by dx[i], or by (xlo[i],xhi[i]) if the |
---|
| 41 | uncertainty is asymmetric. Similarly for y uncertainty. |
---|
| 42 | |
---|
| 43 | The title appears on the legend. |
---|
| 44 | The label, if it is different, appears on the status bar. |
---|
| 45 | """ |
---|
| 46 | self.name = "DATA" |
---|
| 47 | self.x = x |
---|
| 48 | self.y = y |
---|
| 49 | self.dx = dx |
---|
| 50 | self.dy = dy |
---|
| 51 | self.hidden = False |
---|
| 52 | |
---|
| 53 | |
---|
| 54 | |
---|
| 55 | def render(self,plot,**kw): |
---|
| 56 | plot.curve(self.x,self.y,dy=self.dy,**kw) |
---|
| 57 | |
---|
| 58 | def changed(self): |
---|
| 59 | return False |
---|
| 60 | |
---|
| 61 | @classmethod |
---|
| 62 | def labels(cls, collection): |
---|
| 63 | """Build a label mostly unique within a collection""" |
---|
| 64 | map = {} |
---|
| 65 | for item in collection: |
---|
| 66 | map[item] = r"$\rm{%s}$" % item.name |
---|
| 67 | return map |
---|
| 68 | |
---|
| 69 | |
---|
| 70 | class ModelPanel1D(PlotPanel): |
---|
| 71 | def __init__(self, parent, id = -1, color = None,\ |
---|
| 72 | dpi = None, style = wx.NO_FULL_REPAINT_ON_RESIZE, **kwargs): |
---|
| 73 | PlotPanel.__init__(self, parent, id = id, style = style, **kwargs) |
---|
| 74 | |
---|
| 75 | self.parent = parent |
---|
| 76 | self.qmin = DEFAULT_QMIN |
---|
| 77 | self.qmax = DEFAULT_QMAX |
---|
| 78 | self.qstep = DEFAULT_QSTEP |
---|
| 79 | |
---|
| 80 | self.figure.subplots_adjust(bottom=.25) |
---|
| 81 | |
---|
| 82 | self.set_yscale('log') |
---|
| 83 | self.set_xscale('linear') |
---|
| 84 | self.x = pylab.arange(self.qmin, self.qmax+self.qstep*0.01, self.qstep) |
---|
| 85 | # Error on x |
---|
| 86 | self.dx = numpy.zeros(len(self.x)) |
---|
| 87 | # Intensity values |
---|
| 88 | y = numpy.ones(len(self.x)) |
---|
| 89 | # Error on y |
---|
| 90 | self.dy = numpy.zeros(len(self.x)) |
---|
| 91 | |
---|
| 92 | # Plottables |
---|
| 93 | self.file_data = Data1D(x=[], y=[], dx=[], dy=[]) |
---|
| 94 | self.file_data.name = "Loaded 1D data" |
---|
| 95 | |
---|
| 96 | self.t = pylab.arange(0.0, 10, 1) |
---|
| 97 | self.r = [0,3,2,4.5,6,1,2,4,7,9] |
---|
| 98 | self.file_data1 = Data1D(x=self.t, y=[], dx=self.r, dy=self.r) |
---|
| 99 | |
---|
| 100 | # Graph |
---|
| 101 | self.graph = Graph() |
---|
| 102 | self.graph.xaxis('\\rm{q} ', 'A^{-1}') |
---|
| 103 | self.graph.yaxis("\\rm{Intensity} ","cm^{-1}") |
---|
| 104 | self.graph.add(self.file_data) |
---|
| 105 | #self.graph.add(self.file_data1) |
---|
| 106 | self.graph.render(self) |
---|
| 107 | |
---|
| 108 | |
---|
| 109 | # Bind events |
---|
| 110 | self.parent.Bind(EVT_FUNC_PARS, self._onEVT_FUNC_PARS) |
---|
| 111 | |
---|
| 112 | def onSave1DData(self, evt): |
---|
| 113 | """ |
---|
| 114 | Saves the date in a file |
---|
| 115 | @param evt: wx event |
---|
| 116 | """ |
---|
| 117 | path = None |
---|
| 118 | dlg = wx.FileDialog(self, "Choose a file", os.getcwd(), "", "*.txt", wx.SAVE) |
---|
| 119 | if dlg.ShowModal() == wx.ID_OK: |
---|
| 120 | path = dlg.GetPath() |
---|
| 121 | mypath = os.path.basename(path) |
---|
| 122 | print path |
---|
| 123 | dlg.Destroy() |
---|
| 124 | if not path == None: |
---|
| 125 | |
---|
| 126 | out = open(path, 'w') |
---|
| 127 | out.write("<q> <I_1D>\n") |
---|
| 128 | for i in range(len(self.file_data.x)): |
---|
| 129 | out.write("%g %g\n" % (self.file_data.x[i], self.file_data.y[i])) |
---|
| 130 | out.close() |
---|
| 131 | |
---|
| 132 | def onContextMenu(self, event): |
---|
| 133 | """ |
---|
| 134 | Pop up a context menu |
---|
| 135 | """ |
---|
| 136 | # Slicer plot popup menu |
---|
| 137 | slicerpop = wx.Menu() |
---|
| 138 | slicerpop.Append(314, "&Save 1D model points (%s)" % self.file_data.name, |
---|
| 139 | 'Save randomly oriented data currently displayed') |
---|
| 140 | |
---|
| 141 | slicerpop.Append(316, '&Load 1D data file') |
---|
| 142 | slicerpop.Append(321, '&Display Error') |
---|
| 143 | slicerpop.AppendSeparator() |
---|
| 144 | # Those labels were wrong. The way you coded it, it's note necessarily log(I)! |
---|
| 145 | slicerpop.Append(315, '&Toggle Linear/Log intensity scale Y-axis') |
---|
| 146 | slicerpop.Append(319, '&Plot Q') |
---|
| 147 | slicerpop.Append(317, '&Plot Q**2') |
---|
| 148 | slicerpop.Append(320, '&Plot log(Q)') |
---|
| 149 | |
---|
| 150 | wx.EVT_MENU(self, 314, self.onSave1DData) |
---|
| 151 | wx.EVT_MENU(self, 315, self._onToggleScale) |
---|
| 152 | wx.EVT_MENU(self, 316, self._onLoad1DData) |
---|
| 153 | |
---|
| 154 | wx.EVT_MENU(self, 319, self._onLinearQ) |
---|
| 155 | wx.EVT_MENU(self, 317, self._onSquaredQ) |
---|
| 156 | wx.EVT_MENU(self, 320, self._onLogQ) |
---|
| 157 | wx.EVT_MENU(self, 321, self._onError) |
---|
| 158 | pos = event.GetPosition() |
---|
| 159 | pos = self.ScreenToClient(pos) |
---|
| 160 | self.PopupMenu(slicerpop, pos) |
---|
| 161 | |
---|
| 162 | def _onError(self, event): |
---|
| 163 | print self.file_data1.dx |
---|
| 164 | a,b,c=self.points(x=self.file_data1.x, y=self.file_data1.y, \ |
---|
| 165 | dx=self.file_data1.dx , dy=self.file_data1.dy) |
---|
| 166 | |
---|
| 167 | self.graph.add(b) |
---|
| 168 | #self.graph.add(c) |
---|
| 169 | self.graph.render(self) |
---|
| 170 | self.subplot.figure.canvas.draw_idle() |
---|
| 171 | def _onFit(self,event): |
---|
| 172 | print "the SansFit goes here" |
---|
| 173 | |
---|
| 174 | def _onEVT_FUNC_PARS(self, event): |
---|
| 175 | """ |
---|
| 176 | Plot x * cstA + cstB |
---|
| 177 | """ |
---|
| 178 | a=event.cstA |
---|
| 179 | b=event.cstB |
---|
| 180 | temp=[] |
---|
| 181 | |
---|
| 182 | if self.file_data.x: |
---|
| 183 | for x_i in self.file_data.x: |
---|
| 184 | temp.append(Line_function(x_i,a,b)) |
---|
| 185 | self.file_data1.y =temp |
---|
| 186 | self.file_data1.x= self.file_data.x |
---|
| 187 | else: |
---|
| 188 | |
---|
| 189 | for x_i in self.file_data1.x: |
---|
| 190 | temp.append(Line_function(x_i,a,b)) |
---|
| 191 | self.file_data1.y =temp |
---|
| 192 | |
---|
| 193 | self.file_data1.name = "Loaded 1D data" |
---|
| 194 | self.graph.xaxis('\\rm{q} ', 'A^{-1}') |
---|
| 195 | self.graph.yaxis("\\rm{Intensity} ","cm^{-1}") |
---|
| 196 | self.graph.add(self.file_data1) |
---|
| 197 | self.graph.render(self) |
---|
| 198 | self.subplot.figure.canvas.draw_idle() |
---|
| 199 | |
---|
| 200 | def _onLoad1DData(self, event): |
---|
| 201 | """ |
---|
| 202 | Load a data file |
---|
| 203 | """ |
---|
| 204 | path = None |
---|
| 205 | dlg = wx.FileDialog(self, "Choose a file", os.getcwd(), "", "*.txt", wx.OPEN) |
---|
| 206 | if dlg.ShowModal() == wx.ID_OK: |
---|
| 207 | path = dlg.GetPath() |
---|
| 208 | mypath = os.path.basename(path) |
---|
| 209 | dlg.Destroy() |
---|
| 210 | |
---|
| 211 | file_x = [] |
---|
| 212 | file_y = [] |
---|
| 213 | if not path == None: |
---|
| 214 | self.path =path |
---|
| 215 | input_f = open(path,'r') |
---|
| 216 | buff = input_f.read() |
---|
| 217 | lines = buff.split('\n') |
---|
| 218 | for line in lines: |
---|
| 219 | try: |
---|
| 220 | toks = line.split() |
---|
| 221 | x = float(toks[0]) |
---|
| 222 | y = float(toks[1]) |
---|
| 223 | file_x.append(x) |
---|
| 224 | file_y.append(y) |
---|
| 225 | except: |
---|
| 226 | print "READ ERROR", line |
---|
| 227 | |
---|
| 228 | self.file_data.x = file_x |
---|
| 229 | self.file_data.y = file_y |
---|
| 230 | |
---|
| 231 | self.file_data.name = "Loaded 1D data" |
---|
| 232 | self.graph.xaxis('\\rm{q} ', 'A^{-1}') |
---|
| 233 | self.graph.yaxis("\\rm{Intensity} ","cm^{-1}") |
---|
| 234 | |
---|
| 235 | self.graph.add(self.file_data) |
---|
| 236 | self.graph.render(self) |
---|
| 237 | self.subplot.figure.canvas.draw_idle() |
---|
| 238 | |
---|
| 239 | def onMenuGuinier(self, event): |
---|
| 240 | """ |
---|
| 241 | This method plot I vs Q**2 |
---|
| 242 | """ |
---|
| 243 | file_x =[] |
---|
| 244 | for entry in self.file_data.x: |
---|
| 245 | file_x.append(entry * entry) |
---|
| 246 | self.file_data.x = file_x |
---|
| 247 | self.graph.xaxis('\\rm{q**2 } ', 'A^{-2}') |
---|
| 248 | |
---|
| 249 | def _onSquaredQ(self, event): |
---|
| 250 | """ |
---|
| 251 | This method plots Q**2 |
---|
| 252 | """ |
---|
| 253 | self.graph.xaxis('\\rm{q}^2 ', 'A^{-2}') |
---|
| 254 | self.set_xscale('squared') |
---|
| 255 | |
---|
| 256 | self.graph.render(self) |
---|
| 257 | self.subplot.figure.canvas.draw_idle() |
---|
| 258 | |
---|
| 259 | def _onLinearQ(self, event): |
---|
| 260 | """ |
---|
| 261 | This method plots Q |
---|
| 262 | """ |
---|
| 263 | self.graph.xaxis('\\rm{q} ', 'A^{-1}') |
---|
| 264 | self.set_xscale('linear') |
---|
| 265 | |
---|
| 266 | self.graph.render(self) |
---|
| 267 | self.subplot.figure.canvas.draw_idle() |
---|
| 268 | |
---|
| 269 | def _onToggleScale(self, event): |
---|
| 270 | """ |
---|
| 271 | Toggle the scale of the y-axis |
---|
| 272 | """ |
---|
| 273 | if self.get_yscale() == 'log': |
---|
| 274 | self.set_yscale('linear') |
---|
| 275 | else: |
---|
| 276 | self.set_yscale('log') |
---|
| 277 | self.subplot.figure.canvas.draw_idle() |
---|
| 278 | |
---|
| 279 | def _onLogQ(self, event): |
---|
| 280 | """ |
---|
| 281 | Plot log(q) |
---|
| 282 | """ |
---|
| 283 | self.set_xscale('log') |
---|
| 284 | self.graph.xaxis('\\rm{q} ', 'A^{-1}') |
---|
| 285 | |
---|
| 286 | self.graph.render(self) |
---|
| 287 | self.subplot.figure.canvas.draw_idle() |
---|
| 288 | |
---|
| 289 | |
---|
| 290 | def Line_function(x, paramA, paramB): |
---|
| 291 | """ |
---|
| 292 | linear function |
---|
| 293 | """ |
---|
| 294 | return x * paramA + paramB |
---|