[ca88b2e] | 1 | """ |
---|
| 2 | Object to manage the state of the application |
---|
| 3 | """ |
---|
| 4 | |
---|
| 5 | import wx |
---|
| 6 | import wx.lib.newevent |
---|
| 7 | |
---|
| 8 | import ModelParameters |
---|
| 9 | import AveragerParameters |
---|
| 10 | import Averager2D |
---|
| 11 | import history |
---|
| 12 | import modelData |
---|
| 13 | from model_thread import Calc2D |
---|
| 14 | from copy import deepcopy |
---|
| 15 | from fitting import ChiUpdateEvent |
---|
| 16 | |
---|
| 17 | (Refresh2DEvent, EVT_2DREFRESH) = wx.lib.newevent.NewEvent() |
---|
| 18 | |
---|
| 19 | # Debug printout |
---|
| 20 | from config import printEVT |
---|
| 21 | import config |
---|
| 22 | from state import State |
---|
| 23 | import Plotter1D |
---|
| 24 | import Plotter2D |
---|
| 25 | |
---|
| 26 | (StateEvent, EVT_STATE) = wx.lib.newevent.NewEvent() |
---|
| 27 | |
---|
| 28 | class DetectorMemento: |
---|
| 29 | def __init__(self, qmax=0.1, npts=10, beam=None, zmin=None, zmax=None, sym4=False): |
---|
| 30 | self.qmax = qmax |
---|
| 31 | self.npts = npts |
---|
| 32 | self.zmin = zmin |
---|
| 33 | self.zmax = zmax |
---|
| 34 | self.beam = beam |
---|
| 35 | self.sym4 = sym4 |
---|
| 36 | |
---|
| 37 | def toString(self, memento): |
---|
| 38 | """ |
---|
| 39 | Return a string that describes the difference |
---|
| 40 | between the given memento and the current one |
---|
| 41 | @param memento: memento to compare to |
---|
| 42 | @return: string |
---|
| 43 | """ |
---|
| 44 | if memento == None: |
---|
| 45 | return "/Qmax = %g/Det npts = %g" % (self.qmax, self.npts) |
---|
| 46 | else: |
---|
| 47 | descr = '' |
---|
| 48 | if not memento.qmax == self.qmax: |
---|
| 49 | descr += "/Qmax = %g" % self.qmax |
---|
| 50 | if not memento.npts == self.npts: |
---|
| 51 | descr += "/Det npts = %g" % self.npts |
---|
| 52 | if not self.zmin==None and not memento.zmin == self.zmin: |
---|
| 53 | descr += "/Zmin = %g" % self.zmin |
---|
| 54 | if self.zmax and not memento.zmax == self.zmax: |
---|
| 55 | descr += "/Zmax = %g" % self.zmax |
---|
| 56 | if not memento.beam == self.beam: |
---|
| 57 | descr += "/beamstop = %g" % self.beam |
---|
| 58 | return descr |
---|
| 59 | |
---|
| 60 | class StateManager: |
---|
| 61 | |
---|
| 62 | def __init__(self, target=None, model=None, slicer=None): |
---|
| 63 | self.target = target |
---|
| 64 | self.state = State(target, model, slicer) |
---|
| 65 | |
---|
| 66 | # Listen to parameter changes to fire history events |
---|
| 67 | |
---|
| 68 | self.target.Bind(ModelParameters.EVT_MODEL_PARS, self._onEVT_MODEL_PARS) |
---|
| 69 | self.target.Bind(AveragerParameters.EVT_AVG_PARS, self._onEVT_AVG_PARS) |
---|
| 70 | self.target.Bind(EVT_STATE, self._onEVT_STATE) |
---|
| 71 | self.target.Bind(Plotter2D.EVT_DETECTOR, self._onEVT_DETECTOR) |
---|
| 72 | self.target.Bind(Plotter1D.EVT_PLOT1D_PARS, self._onEVT_PLOT1D_PARS) |
---|
| 73 | |
---|
| 74 | def setModel(self, target, model, tag=''): |
---|
| 75 | """ |
---|
| 76 | Set the model and send a history event |
---|
| 77 | """ |
---|
| 78 | # Prepare the restore event |
---|
| 79 | self._saveCurrent(tag) |
---|
| 80 | |
---|
| 81 | # Now change the model |
---|
| 82 | self.state.setModel(model) |
---|
| 83 | evt = ModelParameters.ModelEvent(model=model, data=self.state, new=True) |
---|
| 84 | wx.PostEvent(target, evt) |
---|
| 85 | # Posting averager state |
---|
| 86 | wx.PostEvent(target, AveragerParameters.AveragerStateEvent(memento=AveragerParameters.AveragerParameterMemento())) |
---|
| 87 | |
---|
| 88 | def _saveCurrent(self, tag=''): |
---|
| 89 | """ |
---|
| 90 | Send a history event to capture the current state |
---|
| 91 | """ |
---|
| 92 | #TODO: need a way to find the difference between old and new model |
---|
| 93 | if not self.state.model == None: |
---|
| 94 | stored_event = StateEvent(state = self.state.clone()) |
---|
| 95 | |
---|
| 96 | # Send the history event |
---|
| 97 | evt = history.HistoryEvent(name = self.state.model.name+'%s/%s' % (self.state.description, tag), |
---|
| 98 | item = stored_event) |
---|
| 99 | wx.PostEvent(self.target, evt) |
---|
| 100 | |
---|
| 101 | |
---|
| 102 | def setSlicer(self, slicer): |
---|
| 103 | """ |
---|
| 104 | Set the slicer |
---|
| 105 | """ |
---|
| 106 | printEVT("StateManager.setSlicer: Slicer not implmeted") |
---|
| 107 | self.state.slicer = slicer |
---|
| 108 | |
---|
| 109 | def _onEVT_STATE(self, event): |
---|
| 110 | """ |
---|
| 111 | Restor a stored state |
---|
| 112 | @param event: state event |
---|
| 113 | """ |
---|
| 114 | self.state.stop() |
---|
| 115 | self.state = event.state.clone() |
---|
| 116 | |
---|
| 117 | # Posting detector parameter panel state event |
---|
| 118 | if not self.state.detector_memento == None: |
---|
| 119 | det_event = Plotter2D.createDetectorParsEvent(qmax=self.state.detector_memento.qmax, |
---|
| 120 | npts=self.state.detector_memento.npts, |
---|
| 121 | beam=self.state.detector_memento.beam, |
---|
| 122 | zmin=self.state.detector_memento.zmin, |
---|
| 123 | zmax=self.state.detector_memento.zmax, |
---|
| 124 | sym4=self.state.detector_memento.sym4, |
---|
| 125 | info_only=True) |
---|
| 126 | else: |
---|
| 127 | det_event = Plotter2D.createDetectorParsEvent(qmax=None, |
---|
| 128 | npts=None, |
---|
| 129 | info_only=True) |
---|
| 130 | printEVT("StateManager: posting detector memento") |
---|
| 131 | wx.PostEvent(self.target, det_event) |
---|
| 132 | |
---|
| 133 | # Posting 1D plot parameters |
---|
| 134 | if not self.state.pars1D_memento == None: |
---|
| 135 | pars1D_event = Plotter1D.createPlot1DParsEvent(qmax=self.state.pars1D_memento.qmax, |
---|
| 136 | npts=self.state.pars1D_memento.npts, |
---|
| 137 | qmin=self.state.pars1D_memento.qmin, |
---|
| 138 | info_only=True) |
---|
| 139 | else: |
---|
| 140 | pars1D_event = Plotter1D.createPlot1DParsEvent(qmin=None, |
---|
| 141 | qmax=None, |
---|
| 142 | npts=None, |
---|
| 143 | info_only=True) |
---|
| 144 | printEVT("StateManager: posting 1D plot pars memento") |
---|
| 145 | wx.PostEvent(self.target, pars1D_event) |
---|
| 146 | |
---|
| 147 | |
---|
| 148 | # Posting averager state |
---|
| 149 | wx.PostEvent(self.target, AveragerParameters.AveragerStateEvent(memento=event.state.averager_memento)) |
---|
| 150 | |
---|
| 151 | # Posting model event |
---|
| 152 | evt = ModelParameters.ModelEvent(model=self.state.model, data=self.state, new=True) |
---|
| 153 | wx.PostEvent(self.target, evt) |
---|
| 154 | |
---|
| 155 | # Posting parameter panel state event |
---|
| 156 | evt2 = ModelParameters.ModelParameterStateEvent(memento=event.state.model_par_memento) |
---|
| 157 | wx.PostEvent(self.target, evt2) |
---|
| 158 | |
---|
| 159 | printEVT("State.onEVT_STATE: history item restored: %s" % self.state.model.name) |
---|
| 160 | |
---|
| 161 | def _onEVT_DETECTOR(self, event): |
---|
| 162 | config.printEVT("StateManager._onEVT_DETECTOR") |
---|
| 163 | if event.info_only: |
---|
| 164 | config.printEVT(" skip EVT_DETECTOR") |
---|
| 165 | return |
---|
| 166 | |
---|
| 167 | event.Skip() |
---|
| 168 | self._saveCurrent() |
---|
| 169 | |
---|
| 170 | # Clear current data to invoke recalculation |
---|
| 171 | #self.state.clear_data() |
---|
| 172 | self.state.setDetectorMemento(DetectorMemento(event.qmax, event.npts, |
---|
| 173 | zmin = event.zmin, zmax = event.zmax, |
---|
| 174 | beam = event.beam, sym4 = event.sym4)) |
---|
| 175 | |
---|
| 176 | # Send an update event to the plotters |
---|
| 177 | evt = ModelParameters.ModelEvent(model=self.state.model, data=self.state, new=False) |
---|
| 178 | wx.PostEvent(self.target, evt) |
---|
| 179 | |
---|
| 180 | def _onEVT_PLOT1D_PARS(self, event): |
---|
| 181 | config.printEVT("StateManager._onEVT_PLOT1D_PARS") |
---|
| 182 | if event.info_only: |
---|
| 183 | config.printEVT(" skip EVT_PLOT1D_PARS") |
---|
| 184 | return |
---|
| 185 | |
---|
| 186 | event.Skip() |
---|
| 187 | self._saveCurrent() |
---|
| 188 | |
---|
| 189 | # Update the state |
---|
| 190 | self.state.setPars1DMemento(Plotter1D.Plot1DMemento(event.qmin, |
---|
| 191 | event.qmax, |
---|
| 192 | event.npts)) |
---|
| 193 | |
---|
| 194 | # Send an update event to the plotters |
---|
| 195 | evt = ModelParameters.ModelEvent(model=self.state.model, data=self.state, new=False) |
---|
| 196 | wx.PostEvent(self.target, evt) |
---|
| 197 | |
---|
| 198 | |
---|
| 199 | def _onEVT_MODEL_PARS(self, event): |
---|
| 200 | """ |
---|
| 201 | Modify the parameters of the model and |
---|
| 202 | fire a history event |
---|
| 203 | |
---|
| 204 | @param event: parameter change event |
---|
| 205 | """ |
---|
| 206 | printEVT("State.onEVT_MODEL_PARS") |
---|
| 207 | |
---|
| 208 | # Save the state before changing it |
---|
| 209 | stored_event = StateEvent(state = self.state.clone()) |
---|
| 210 | |
---|
| 211 | # Post message to status bar |
---|
| 212 | wx.PostEvent(self.target, config.StatusBarEvent(message=event.status)) |
---|
| 213 | |
---|
| 214 | # Dispersion? |
---|
| 215 | disp_tag = '' |
---|
| 216 | if event.disp_changed and len(event.disp)>0: |
---|
| 217 | if not self.state.model.__class__.__name__ == "Averager2D": |
---|
| 218 | config.printEVT("StateMng: setting up averager") |
---|
| 219 | new_model = Averager2D.Averager2D() |
---|
| 220 | flag = new_model.set_model(self.state.model.clone()) |
---|
| 221 | self.state.setModel(new_model) |
---|
| 222 | else: |
---|
| 223 | disp_list = deepcopy(self.state.model.get_dispersity()) |
---|
| 224 | for item in disp_list: |
---|
| 225 | disp_tag += '/Disp[%s]=%g [%g pts]' % (item[0], item[1], item[2]) |
---|
| 226 | |
---|
| 227 | self.state.model.set_dispersity(event.disp) |
---|
| 228 | |
---|
| 229 | if not event.average and \ |
---|
| 230 | self.state.model.__class__.__name__ == "Averager2D": |
---|
| 231 | disp_list = deepcopy(self.state.model.get_dispersity()) |
---|
| 232 | if self.state.model.set_dispersity([]): |
---|
| 233 | for item in disp_list: |
---|
| 234 | disp_tag += '/Disp[%s]=%g [%g pts]' % (item[0], item[1], item[2]) |
---|
| 235 | |
---|
| 236 | # Push parameters to model |
---|
| 237 | name = self.state.setParams(event.params) |
---|
| 238 | self.state.setModelParMemento(event.memento) |
---|
| 239 | |
---|
| 240 | #print str(self.state.model) |
---|
| 241 | |
---|
| 242 | # Send an update event to the plotters |
---|
| 243 | evt = ModelParameters.ModelEvent(model=self.state.model, data=self.state, new=False) |
---|
| 244 | wx.PostEvent(self.target, evt) |
---|
| 245 | |
---|
| 246 | if len(event.params)>0 or len(event.disp)>0 or event.disp_changed: |
---|
| 247 | # Send the history event |
---|
| 248 | evt = history.HistoryEvent(name = name+disp_tag, |
---|
| 249 | item = stored_event) |
---|
| 250 | wx.PostEvent(self.target, evt) |
---|
| 251 | |
---|
| 252 | # Post chi^2 recalculation event for fit panel |
---|
| 253 | wx.PostEvent(self.target, ChiUpdateEvent()) |
---|
| 254 | |
---|
| 255 | def _onEVT_AVG_PARS(self, event): |
---|
| 256 | printEVT("State._onEVT_AVG_PARS") |
---|
| 257 | |
---|
| 258 | # Save the state before changing it |
---|
| 259 | stored_event = StateEvent(state = self.state.clone()) |
---|
| 260 | |
---|
| 261 | # Inspect the change to give a proper title to the |
---|
| 262 | # history item |
---|
| 263 | info = "Phi = OFF/THETA=ON (filename)" |
---|
| 264 | |
---|
| 265 | # Set averager memento to retreive the same state from history panel |
---|
| 266 | self.state.setAveragerMemento(event.memento) |
---|
| 267 | |
---|
| 268 | # Flags for history event |
---|
| 269 | phi_flag = "OFF" |
---|
| 270 | theta_flag = "OFF" |
---|
| 271 | |
---|
| 272 | #theta_file = os.path.basename(self.theta_file_path) |
---|
| 273 | |
---|
| 274 | # Post message to status bar if needed |
---|
| 275 | if not event.status == None: |
---|
| 276 | wx.PostEvent(self.target, config.StatusBarEvent(message=event.status)) |
---|
| 277 | |
---|
| 278 | # Check if we already have an Averager2D model |
---|
| 279 | # When we reach this points, either we need an Averager2D |
---|
| 280 | # or we already have one. |
---|
| 281 | if not self.state.model.__class__.__name__ == "Averager2D": |
---|
| 282 | model = Averager2D.Averager2D() |
---|
| 283 | flag = model.set_model(self.state.model.clone()) |
---|
| 284 | if flag: |
---|
| 285 | self.state.setModel(model) |
---|
| 286 | else: |
---|
| 287 | message = "Current model doesn't have theta or phi: skipping" |
---|
| 288 | wx.PostEvent(self.target, config.StatusBarEvent(message=message)) |
---|
| 289 | return |
---|
| 290 | else: |
---|
| 291 | if self.state.model.phi_on: |
---|
| 292 | phi_flag = "ON" |
---|
| 293 | if self.state.model.theta_on: |
---|
| 294 | theta_flag = "ON" |
---|
| 295 | |
---|
| 296 | # Set phi info |
---|
| 297 | if event.phi_on: |
---|
| 298 | if not self.state.model.setPhiFile(event.phi_file): |
---|
| 299 | message = "Current model doesn't have phi: skipping" |
---|
| 300 | wx.PostEvent(self.target, config.StatusBarEvent(message=message)) |
---|
| 301 | else: |
---|
| 302 | self.state.model.setPhiFile(None) |
---|
| 303 | |
---|
| 304 | # Set theta info |
---|
| 305 | if event.theta_on: |
---|
| 306 | if not self.state.model.setThetaFile(event.theta_file): |
---|
| 307 | message = "Current model doesn't have phi: skipping" |
---|
| 308 | wx.PostEvent(self.target, config.StatusBarEvent(message=message)) |
---|
| 309 | else: |
---|
| 310 | self.state.model.setThetaFile(None) |
---|
| 311 | |
---|
| 312 | # Send the history event |
---|
| 313 | hist_evt = history.HistoryEvent(name = "%s/Phi %s/Theta %s" % \ |
---|
| 314 | (self.state.model.name, phi_flag, theta_flag), |
---|
| 315 | item = stored_event) |
---|
| 316 | wx.PostEvent(self.target, hist_evt) |
---|
| 317 | |
---|
| 318 | # Send an update event to the plotters |
---|
| 319 | evt = ModelParameters.ModelEvent(model=self.state.model, data=self.state, new=False) |
---|
| 320 | wx.PostEvent(self.target, evt) |
---|
| 321 | |
---|
| 322 | |
---|
| 323 | |
---|