source: sasview/src/sas/sasgui/guiframe/local_perspectives/plotting/parameters_panel_slicer.py @ 7a80072

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.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 7a80072 was 7a80072, checked in by krzywon, 7 years ago

Fitting partially works. Problem creating new pages.

  • Property mode set to 100644
File size: 20.3 KB
Line 
1
2
3import wx
4import wx.lib.newevent
5from sas.sascalc.dataloader.readers.cansas_reader import Reader
6from sas.sasgui.guiframe.events import EVT_SLICER_PARS
7from sas.sasgui.guiframe.utils import format_number
8from sas.sasgui.guiframe.events import EVT_SLICER
9from sas.sasgui.guiframe.events import SlicerParameterEvent, SlicerEvent
10from Plotter2D import ModelPanel2D
11apply_params, EVT_APPLY_PARAMS = wx.lib.newevent.NewEvent()
12auto_save, EVT_AUTO_SAVE = wx.lib.newevent.NewEvent()
13
14FIT_OPTIONS = ["No fitting", "Fitting"]
15CONVERT_DICT = {"SectorInteractor": "SectorQ",
16                "AnnulusInteractor": "AnnulusPhi",
17                "BoxInteractorX": "SlabX",
18                "BoxInteractorY": "SlabY"}
19
20class SlicerParameterPanel(wx.Dialog):
21    """
22    Panel class to show the slicer parameters
23    """
24    # TODO: show units
25    # TODO: order parameters properly
26
27    def __init__(self, parent, *args, **kwargs):
28        """
29        Dialog window that allow to edit parameters slicer
30        by entering new values
31        """
32        wx.Dialog.__init__(self, parent, *args, **kwargs)
33        self.params = {}
34        self.iter = 0
35        self.parent = parent
36        self.type = None
37        self.listeners = []
38        self.parameters = []
39        self.bck = wx.GridBagSizer(5, 5)
40        self.SetSizer(self.bck)
41        self.auto_save = None
42        self.path = None
43        self.fitting_options = None
44        self.type_list = []
45        self.type_select = None
46        self.append_name = None
47        self.data_list = None
48        label = "Right-click on 2D plot for slicer options"
49        title = wx.StaticText(self, -1, label, style=wx.ALIGN_LEFT)
50        self.bck.Add(title, (0, 0), (1, 2),
51                     flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL, border=15)
52        # Bindings
53        self.parent.Bind(EVT_SLICER, self.onEVT_SLICER)
54        self.Bind(EVT_SLICER_PARS, self.onParamChange)
55        self.Bind(EVT_APPLY_PARAMS, self.apply_params_list_and_process)
56        self.Bind(EVT_AUTO_SAVE, self.save_files)
57
58    def onEVT_SLICER(self, event):
59        """
60        Process EVT_SLICER events
61        When the slicer changes, update the panel
62
63        :param event: EVT_SLICER event
64        """
65        event.Skip()
66        if event.obj_class is None:
67            self.set_slicer(None, None)
68        else:
69            self.set_slicer(event.type, event.params)
70
71    def set_slicer(self, type, params):
72        """
73        Rebuild the panel
74        """
75        self.bck.Clear(True)
76        self.bck.Add((5, 5), (0, 0), (1, 1),
77                     wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 5)
78        self.type = type
79        if type is None:
80            label = "Right-click on 2D plot for slicer options"
81            title = wx.StaticText(self, -1, label, style=wx.ALIGN_LEFT)
82            self.bck.Add(title, (1, 0), (1, 2),
83                         flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL, border=15)
84        else:
85            title = wx.StaticText(self, -1,
86                                  "Slicer Parameters:", style=wx.ALIGN_LEFT)
87            self.bck.Add(title, (1, 0), (1, 2),
88                         flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL, border=15)
89            iy = 1
90            self.parameters = []
91            keys = params.keys()
92            keys.sort()
93            for item in keys:
94                iy += 1
95                ix = 0
96                if not item in ["count", "errors"]:
97                    text = wx.StaticText(self, -1, item, style=wx.ALIGN_LEFT)
98                    self.bck.Add(text, (iy, ix), (1, 1),
99                                 wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
100                    ctl = wx.TextCtrl(self, -1, size=(80, 20),
101                                      style=wx.TE_PROCESS_ENTER)
102                    hint_msg = "Modify the value of %s to change" % item
103                    hint_msg += " the 2D slicer"
104                    ctl.SetToolTipString(hint_msg)
105                    ix = 1
106                    ctl.SetValue(format_number(str(params[item])))
107                    self.Bind(wx.EVT_TEXT_ENTER, self.onTextEnter)
108                    self.parameters.append([item, ctl])
109                    self.bck.Add(ctl, (iy, ix), (1, 1),
110                                 wx.EXPAND | wx.ADJUST_MINSIZE, 0)
111                    ix = 3
112                    self.bck.Add((20, 20), (iy, ix), (1, 1),
113                                 wx.EXPAND | wx.ADJUST_MINSIZE, 0)
114                else:
115                    text = wx.StaticText(self, -1, item + " : ",
116                                         style=wx.ALIGN_LEFT)
117                    self.bck.Add(text, (iy, ix), (1, 1),
118                                 wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
119                    ctl = wx.StaticText(self, -1,
120                                        format_number(str(params[item])),
121                                        style=wx.ALIGN_LEFT)
122                    ix = 1
123                    self.bck.Add(ctl, (iy, ix), (1, 1),
124                                 wx.EXPAND | wx.ADJUST_MINSIZE, 0)
125
126            # Change slicer within the window
127            ix = 0
128            iy += 1
129            txt = "Slicer type:"
130            text = wx.StaticText(self, -1, txt, style=wx.ALIGN_LEFT)
131            self.bck.Add(text, (iy, ix), (1, 1),
132                         wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
133            self.type_list = ["SectorInteractor", "AnnulusInteractor",
134                              "BoxInteractorX", "BoxInteractorY"]
135            self.type_select = wx.ComboBox(parent=self, choices=self.type_list)
136            self.type_select.Bind(wx.EVT_COMBOBOX, self.onChangeSlicer)
137            index = self.type_select.FindString(type)
138            self.type_select.SetSelection(index)
139            self.bck.Add(self.type_select, (iy, 1), (1, 1),
140                         wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
141
142            # batch slicing parameters
143            title_text = "Batch Slicing Options:"
144            title = wx.StaticText(self, -1, title_text, style=wx.ALIGN_LEFT)
145            iy += 1
146            line = wx.StaticLine(self, -1, style=wx.LI_VERTICAL)
147            line.SetSize((60, 60))
148            self.bck.Add(line, (iy, ix), (1, 2),
149                         wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
150            iy += 1
151            self.bck.Add(title, (iy, ix), (1, 1),
152                         wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
153
154            # Create a list box with all of the 2D plots
155            iy += 1
156            self.process_list()
157            self.bck.Add(self.data_list, (iy, ix), (1, 1),
158                         wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
159
160            # Checkbox for autosaving data
161            iy += 1
162            self.auto_save = wx.CheckBox(parent=self, id=wx.NewId(),
163                                         label="Auto save generated 1D:")
164            self.Bind(wx.EVT_CHECKBOX, self.on_auto_save_checked)
165            self.bck.Add(self.auto_save, (iy, ix), (1, 1),
166                         wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
167            iy += 1
168            # File browser
169            save_to = "Save files to:"
170            save = wx.StaticText(self, -1, save_to, style=wx.ALIGN_LEFT)
171            self.path = wx.DirPickerCtrl(self, id=wx.NewId(), path="",
172                                         message=save_to)
173            self.path.Enable(False)
174            self.bck.Add(save, (iy, ix), (1, 1),
175                         wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
176            self.bck.Add(self.path, (iy, 1), (1, 1),
177                         wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
178            # Append to file
179            iy += 1
180            default_value = ""
181            for key in params:
182                default_value += "_{0}".format(key).split(" [")[0]
183                default_value += "-{:.5f}".format(params[key])
184            append_text = "Append to file name:"
185            append = wx.StaticText(self, -1, append_text, style=wx.ALIGN_LEFT)
186            self.append_name = wx.TextCtrl(parent=self, id=wx.NewId(),
187                                           name="Append to file name:")
188            self.append_name.SetValue(default_value)
189            self.append_name.Enable(False)
190            self.bck.Add(append, (iy, ix), (1, 1),
191                         wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
192            self.bck.Add(self.append_name, (iy, 1), (1, 1),
193                         wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
194
195            # Combobox for selecting fitting options
196            iy += 1
197            fit_text = "Fitting Options:"
198            fit_text_item = wx.StaticText(self, -1, fit_text, style=wx.ALIGN_LEFT)
199            self.bck.Add(fit_text_item, (iy, ix), (1, 1),
200                         wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
201            self.fitting_options = wx.ComboBox(parent=self, choices=FIT_OPTIONS)
202            self.fitting_options.SetSelection(0)
203            self.bck.Add(self.fitting_options, (iy, 1), (1, 1),
204                         wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
205            self.fitting_options.Enable(False)
206            self.fitting_options.Bind(wx.EVT_COMBOBOX, None)
207
208            # Button to start batch slicing
209            iy += 1
210            button_label = "Apply Slicer to Selected Plots"
211            self.batch_slicer_button = wx.Button(parent=self,
212                                                 label=button_label)
213            self.Bind(wx.EVT_BUTTON, self.on_batch_slicer)
214            self.bck.Add(self.batch_slicer_button, (iy, ix), (1, 1),
215                         wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
216            iy += 1
217            self.bck.Add((5, 5), (iy, ix), (1, 1),
218                         wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 5)
219        self.bck.Layout()
220        self.bck.Fit(self)
221        self.parent.GetSizer().Layout()
222
223    def onParamChange(self, evt):
224        """
225        receive an event end reset value text fields
226        inside self.parameters
227        """
228        evt.Skip()
229        if evt.type == "UPDATE":
230            for item in self.parameters:
231                if item[0] in evt.params:
232                    item[1].SetValue("%-5.3g" % evt.params[item[0]])
233                    item[1].Refresh()
234
235    def onTextEnter(self, evt):
236        """
237        Parameters have changed
238        """
239        params = {}
240        has_error = False
241        for item in self.parameters:
242            try:
243                params[item[0]] = float(item[1].GetValue())
244                item[1].SetBackgroundColour(
245                    wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
246                item[1].Refresh()
247            except:
248                has_error = True
249                item[1].SetBackgroundColour("pink")
250                item[1].Refresh()
251
252        if not has_error:
253            # Post parameter event
254            # parent here is plotter2D
255            event = SlicerParameterEvent(type=self.type, params=params)
256            wx.PostEvent(self.parent, event)
257
258    def on_batch_slicer(self, evt=None):
259        """
260        Method invoked when batch slicing button is pressed
261        :param evt: Event triggering hide/show of the batch slicer parameters
262        """
263        apply_to_list = []
264        spp = self.parent.parent
265        params = self.parent.slicer.get_params()
266        type = self.type_select.GetStringSelection()
267        save = self.auto_save.IsChecked()
268        append = self.append_name.GetValue()
269        path = self.path.GetPath()
270        fit = self.fitting_options.GetValue()
271
272        # Find desired 2D data panels
273        for key, mgr in spp.plot_panels.iteritems():
274            if mgr.graph.prop['title'] in self.data_list.CheckedStrings:
275                apply_to_list.append(mgr)
276
277        # Apply slicer type to selected panels
278        for item in apply_to_list:
279            self._apply_slicer_to_plot(item, type)
280
281        # Post an event to apply appropriate slicer params to each slicer
282        event_params = apply_params(params=params, apply_to_list=apply_to_list,
283                             auto_save=save, append=append, fit=fit,
284                             path=path, type=type)
285        wx.PostEvent(self, event_params)
286
287    def onChangeSlicer(self, evt):
288        """
289        Event driven slicer change when self.type_select changes
290        :param evt: Event triggering this change
291        """
292        self._apply_slicer_to_plot(self.parent)
293
294    def _apply_slicer_to_plot(self, plot, type=None):
295        """
296        Apply a slicer to *any* plot window, not just parent window
297        :param plot: 2D plot panel to apply a slicer to
298        :param type: The type of slicer to apply to the panel
299        """
300        # Skip redrawing the current plot if no change
301        if self.parent == plot and self.type == type:
302            return
303        # Do not draw a slicer on a 1D plot
304        if not isinstance(plot, ModelPanel2D):
305            return
306        if type is None:
307            type = self.type_select.GetStringSelection()
308        if type == "SectorInteractor":
309            plot.onSectorQ(None)
310        elif type == "AnnulusInteractor":
311            plot.onSectorPhi(None)
312        elif type == "BoxInteractorX":
313            plot.onBoxavgX(None)
314        elif type == "BoxInteractorY":
315            plot.onBoxavgY(None)
316
317    def process_list(self):
318        """
319        Populate the check list from the currently plotted 2D data
320        """
321        self.checkme = None
322        main_window = self.parent.parent
323        self.loaded_data = []
324        id = wx.NewId()
325        # Iterate over the loaded plots and find all 2D panels
326        for key, value in main_window.plot_panels.iteritems():
327            if isinstance(value, ModelPanel2D):
328                self.loaded_data.append(value.data2D.name)
329                if value.data2D.id == self.parent.data2D.id:
330                    # Set current plot panel as uncheckable
331                    self.checkme = self.loaded_data.index(value.data2D.name)
332        self.data_list = wx.CheckListBox(parent=self, id=id,
333                                         choices=self.loaded_data,
334                                         name="Apply Slicer to 2D Plots:")
335        # Check all items by default
336        for item in range(len(self.data_list.Items)):
337            self.data_list.Check(item)
338        self.data_list.Bind(wx.EVT_CHECKLISTBOX, self.on_check_box_list)
339
340    def on_check_box_list(self, evt=None):
341        """
342        Prevent a checkbox item from being unchecked
343        :param e: Event triggered when a checkbox list item is checked
344        """
345        if evt is None:
346            return
347        index = evt.GetSelection()
348        if index == self.checkme:
349            self.data_list.Check(index)
350
351    def apply_params_list_and_process(self, evt=None):
352        """
353        Event based parameter setting.
354        :param evt: Event triggered to apply parameters to a list of plots
355                    evt should have attrs plot_list and params
356        """
357        # Apply parameter list to each plot as desired
358        for item in evt.apply_to_list:
359            event = SlicerParameterEvent(type=evt.type, params=evt.params)
360            wx.PostEvent(item, event)
361        # Post an event to save each data set to file
362        if evt.auto_save:
363            event = auto_save(append_to_name=evt.append, path=evt.path,
364                              type=evt.type, file_list=evt.apply_to_list,
365                              fit=evt.fit)
366            wx.PostEvent(self, event)
367
368    def save_files(self, evt=None):
369        """
370        Automatically save the sliced data to file.
371        :param evt: Event that triggered the call to the method
372        """
373
374        # Send the event to the end of the wx event queue
375        if self.iter == 0:
376            clone = evt.Clone()
377            wx.PostEvent(self, clone)
378            self.iter += 1
379            return
380        if evt is None:
381            return
382
383        # Start definitions
384        writer = Reader()
385        main_window = self.parent.parent
386        data_dic = {}
387        append = evt.append_to_name
388        names = []
389        f_name_list = []
390        f_path_list = []
391
392        # Get list of 2D data names for saving
393        for f_name in evt.file_list:
394            names.append(f_name.data2D.label)
395
396        # Find the correct plots to save
397        for key, plot in main_window.plot_panels.iteritems():
398            if not hasattr(plot, "data2D"):
399                for item in plot.plots:
400                    base = item.replace(CONVERT_DICT[evt.type], "")
401                    if base in names:
402                        data_dic[item] = plot.plots[item]
403
404        # Save files as XML
405        for item, data1d in data_dic.iteritems():
406            base = ('.').join(item.split('.')[:-1])
407            file_name = base + append + ".xml"
408            save_to = evt.path + "\\" + file_name
409            writer.write(save_to, data1d)
410            f_path_list.append(save_to)
411            f_name_list.append(file_name)
412
413        # Load files into GUI
414        for item in f_path_list:
415            main_window.load_data(item)
416
417        # Send to fitting
418        self.send_to_fitting(evt.fit, f_name_list)
419
420    def send_to_fitting(self, fit=FIT_OPTIONS[0], file_list=None):
421        """
422        Send a list of data to the fitting perspective
423        :param fit: fit type desired
424        :param file_list: list of loaded file names to send to fit
425        """
426        if fit != FIT_OPTIONS[0] and file_list is not None:
427            # Method variable definitions
428            main_window = self.parent.parent
429            datapanel = main_window._data_panel
430            # Set perspective to fitting
431            int = datapanel.perspective_cbox.FindString("Fitting")
432            datapanel.perspective_cbox.SetSelection(int)
433            datapanel._on_perspective_selection(None)
434            # Unselect all loaded data
435            datapanel.selection_cbox.SetValue('Unselect all Data')
436            datapanel._on_selection_type(None)
437            # Click each sliced data file
438            for f_name in file_list:
439                num = len(f_name)
440                data_list = datapanel.list_cb_data
441                for key in data_list:
442                    loaded_key = (key[:num]) if len(key) > num else key
443                    if loaded_key == f_name:
444                        selection = key
445                        data_ctrl = data_list[selection][0]
446                        self.check_item_and_children(data_ctrl=data_ctrl,
447                                                     check_value=True)
448            # TODO: Batch fitting
449            # # Switch to batch mode if selected
450            # if fit == FIT_OPTIONS[2]:
451            #     datapanel.rb_single_mode.SetValue(False)
452            #     datapanel.rb_batch_mode.SetValue(True)
453            #     evt = wx.PyCommandEvent(wx.EVT_RADIOBUTTON.typeId,
454            #                             datapanel.rb_batch_mode.GetId())
455            #     wx.PostEvent(datapanel, evt)
456            # else:
457            #     datapanel.rb_single_mode.SetValue(True)
458            #     datapanel.rb_batch_mode.SetValue(False)
459            #     evt = wx.PyCommandEvent(wx.EVT_RADIOBUTTON.typeId,
460            #                             datapanel.rb_single_mode.GetId())
461            #     wx.PostEvent(datapanel, evt)
462
463            # Post button click event to send data to fitting
464            evt = wx.PyCommandEvent(wx.EVT_BUTTON.typeId,
465                                    datapanel.bt_import.GetId())
466            wx.PostEvent(datapanel, evt)
467
468            # TODO: Simultaneous/Constrained fitting
469            # # Create event to open simfitpage if selected
470            # if fit == FIT_OPTIONS[3]:
471            #     fit_pers = main_window._current_perspective
472            #     evt = wx.PyCommandEvent(wx.EVT_MENU.typeId,
473            #                         fit_pers.id_simfit)
474            #     wx.PostEvent(datapanel, evt)
475
476    def on_auto_save_checked(self, evt=None):
477        """
478        Enable/Disable auto append when checkbox is checked
479        :param evt: Event
480        """
481        self.append_name.Enable(self.auto_save.IsChecked())
482        self.path.Enable(self.auto_save.IsChecked())
483        self.fitting_options.Enable(self.auto_save.IsChecked())
484
485    def check_item_and_children(self, data_ctrl, check_value=True):
486        self.parent.parent._data_panel.tree_ctrl.CheckItem(data_ctrl,
487                                                           check_value)
488        if data_ctrl.HasChildren():
489            if check_value and not data_ctrl.IsExpanded():
490                # Only select children if control is expanded
491                # Always deselect children, regardless (see ticket #259)
492                return
493            for child_ctrl in data_ctrl.GetChildren():
494                self.tree_ctrl.CheckItem(child_ctrl, check_value)
Note: See TracBrowser for help on using the repository browser.