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 | |
---|