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

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 cac1828 was cac1828, checked in by krzywon, 7 years ago

Refactor to fix window redraw issues.

  • Property mode set to 100644
File size: 16.3 KB
RevLine 
[d955bf19]1
[b06ef8c]2
3import wx
4import wx.lib.newevent
[54557b5]5from sas.sascalc.dataloader.readers.cansas_reader import Reader
[d85c194]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
[39f0bf4]9from sas.sasgui.guiframe.events import SlicerParameterEvent, SlicerEvent
[df27aa8]10from Plotter2D import ModelPanel2D
[dfa1579]11from sas.sascalc.dataloader.data_info import Data1D, Data2D
[54557b5]12apply_params, EVT_APPLY_PARAMS = wx.lib.newevent.NewEvent()
13auto_save, EVT_AUTO_SAVE = wx.lib.newevent.NewEvent()
[32c0841]14
[0d9dae8]15
[ef0c170]16class SlicerParameterPanel(wx.Dialog):
[d955bf19]17    """
[b40ad40]18    Panel class to show the slicer parameters
[d955bf19]19    """
[f15da54]20    # TODO: show units
21    # TODO: order parameters properly
[b40ad40]22
[cd84dca]23    def __init__(self, parent, *args, **kwargs):
[12aa9b5]24        """
[b40ad40]25        Dialog window that allow to edit parameters slicer
[d955bf19]26        by entering new values
[12aa9b5]27        """
[b40ad40]28        wx.Dialog.__init__(self, parent, *args, **kwargs)
[b06ef8c]29        self.params = {}
30        self.parent = parent
31        self.type = None
32        self.listeners = []
33        self.parameters = []
[32c0841]34        self.bck = wx.GridBagSizer(5, 5)
[b06ef8c]35        self.SetSizer(self.bck)
[54557b5]36        self.auto_save = None
37        self.path = None
[cac1828]38        self.type_list = []
39        self.type_select = None
40        self.append_name = None
[54557b5]41        self.data_list = None
[32c0841]42        label = "Right-click on 2D plot for slicer options"
43        title = wx.StaticText(self, -1, label, style=wx.ALIGN_LEFT)
44        self.bck.Add(title, (0, 0), (1, 2),
[b40ad40]45                     flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL, border=15)
[b06ef8c]46        # Bindings
47        self.parent.Bind(EVT_SLICER, self.onEVT_SLICER)
[cac1828]48        self.Bind(EVT_SLICER_PARS, self.onParamChange)
[54557b5]49        self.Bind(EVT_APPLY_PARAMS, self.apply_params_list_and_process)
50        self.Bind(EVT_AUTO_SAVE, self.save_files)
[b06ef8c]51
52    def onEVT_SLICER(self, event):
53        """
[d955bf19]54        Process EVT_SLICER events
55        When the slicer changes, update the panel
[b40ad40]56
[d955bf19]57        :param event: EVT_SLICER event
[b06ef8c]58        """
59        event.Skip()
[e075203]60        if event.obj_class is None:
[b06ef8c]61            self.set_slicer(None, None)
62        else:
63            self.set_slicer(event.type, event.params)
[b40ad40]64
[b06ef8c]65    def set_slicer(self, type, params):
66        """
[d955bf19]67        Rebuild the panel
[b06ef8c]68        """
[b40ad40]69        self.bck.Clear(True)
[39f0bf4]70        self.bck.Add((5, 5), (0, 0), (1, 1),
71                     wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 5)
[b40ad40]72        self.type = type
[e075203]73        if type is None:
[32c0841]74            label = "Right-click on 2D plot for slicer options"
75            title = wx.StaticText(self, -1, label, style=wx.ALIGN_LEFT)
[39f0bf4]76            self.bck.Add(title, (1, 0), (1, 2),
[b40ad40]77                         flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL, border=15)
[b06ef8c]78        else:
[b40ad40]79            title = wx.StaticText(self, -1,
[f6bb24d]80                                  "Slicer Parameters:", style=wx.ALIGN_LEFT)
[39f0bf4]81            self.bck.Add(title, (1, 0), (1, 2),
[b40ad40]82                         flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL, border=15)
[39f0bf4]83            iy = 1
[b06ef8c]84            self.parameters = []
85            keys = params.keys()
86            keys.sort()
87            for item in keys:
[ef0c170]88                iy += 1
89                ix = 0
[32c0841]90                if not item in ["count", "errors"]:
[0f6d05f8]91                    text = wx.StaticText(self, -1, item, style=wx.ALIGN_LEFT)
[b40ad40]92                    self.bck.Add(text, (iy, ix), (1, 1),
93                                 wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
[32c0841]94                    ctl = wx.TextCtrl(self, -1, size=(80, 20),
95                                      style=wx.TE_PROCESS_ENTER)
[88989768]96                    hint_msg = "Modify the value of %s to change" % item
97                    hint_msg += " the 2D slicer"
[32c0841]98                    ctl.SetToolTipString(hint_msg)
[0f6d05f8]99                    ix = 1
100                    ctl.SetValue(format_number(str(params[item])))
101                    self.Bind(wx.EVT_TEXT_ENTER, self.onTextEnter)
102                    self.parameters.append([item, ctl])
[b40ad40]103                    self.bck.Add(ctl, (iy, ix), (1, 1),
104                                 wx.EXPAND | wx.ADJUST_MINSIZE, 0)
[32c0841]105                    ix = 3
[b40ad40]106                    self.bck.Add((20, 20), (iy, ix), (1, 1),
107                                 wx.EXPAND | wx.ADJUST_MINSIZE, 0)
[0f6d05f8]108                else:
[b40ad40]109                    text = wx.StaticText(self, -1, item + " : ",
[32c0841]110                                         style=wx.ALIGN_LEFT)
[b40ad40]111                    self.bck.Add(text, (iy, ix), (1, 1),
112                                 wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
113                    ctl = wx.StaticText(self, -1,
114                                        format_number(str(params[item])),
115                                        style=wx.ALIGN_LEFT)
[32c0841]116                    ix = 1
[b40ad40]117                    self.bck.Add(ctl, (iy, ix), (1, 1),
118                                 wx.EXPAND | wx.ADJUST_MINSIZE, 0)
[39f0bf4]119
120            # Change slicer within the window
[54557b5]121            ix = 0
122            iy += 1
123            txt = "Slicer type:"
[39f0bf4]124            text = wx.StaticText(self, -1, txt, style=wx.ALIGN_LEFT)
125            self.bck.Add(text, (iy, ix), (1, 1),
[b40ad40]126                         wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
[cac1828]127            self.type_list = ["SectorInteractor", "AnnulusInteractor",
128                              "BoxInteractorX", "BoxInteractorY"]
129            self.type_select = wx.ComboBox(parent=self, choices=self.type_list)
[39f0bf4]130            self.Bind(wx.EVT_COMBOBOX, self.onChangeSlicer)
[54557b5]131            index = self.type_select.FindString(type)
[39f0bf4]132            self.type_select.SetSelection(index)
133            self.bck.Add(self.type_select, (iy, 1), (1, 1),
134                         wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
135
136            # batch slicing parameters
[f6bb24d]137            title_text = "Batch Slicing Options:"
138            title = wx.StaticText(self, -1, title_text, style=wx.ALIGN_LEFT)
[39f0bf4]139            iy += 1
[54557b5]140            line = wx.StaticLine(self, -1, style=wx.LI_VERTICAL)
141            line.SetSize((60, 60))
142            self.bck.Add(line, (iy, ix), (1, 2),
[f6bb24d]143                         wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
144            iy += 1
145            self.bck.Add(title, (iy, ix), (1, 1),
146                         wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
[54557b5]147
[b697396b]148            # Create a list box with all of the 2D plots
[54557b5]149            iy += 1
[dfa1579]150            self.process_list()
[f6bb24d]151            self.bck.Add(self.data_list, (iy, ix), (1, 1),
[39f0bf4]152                         wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
[54557b5]153
154            # Checkbox for autosaving data
155            iy += 1
156            self.auto_save = wx.CheckBox(parent=self, id=wx.NewId(),
157                                         label="Auto save generated 1D:")
158            self.Bind(wx.EVT_CHECKBOX, self.on_auto_save_checked)
159            self.bck.Add(self.auto_save, (iy, ix), (1, 1),
160                         wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
161            iy += 1
162            # File browser
163            save_to = "Save files to:"
164            save = wx.StaticText(self, -1, save_to, style=wx.ALIGN_LEFT)
165            self.path = wx.DirPickerCtrl(self, id=wx.NewId(), path="",
166                                         message=save_to)
167            self.path.Enable(False)
168            self.bck.Add(save, (iy, ix), (1, 1),
169                         wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
170            self.bck.Add(self.path, (iy, 1), (1, 1),
171                         wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
172            # Append to file
[39f0bf4]173            iy += 1
[71b25b2]174            default_value = ""
[54557b5]175            for key in params:
[cac1828]176                default_value += "_{0}".format(key).split(" [")[0]
177                default_value += "-{:.5f}".format(params[key])
[54557b5]178            append_text = "Append to file name:"
179            append = wx.StaticText(self, -1, append_text, style=wx.ALIGN_LEFT)
[cac1828]180            self.append_name = wx.TextCtrl(parent=self, id=wx.NewId(),
181                                           name="Append to file name:")
[54557b5]182            self.append_name.SetValue(default_value)
183            self.append_name.Enable(False)
184            self.bck.Add(append, (iy, ix), (1, 1),
185                         wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
186            self.bck.Add(self.append_name, (iy, 1), (1, 1),
187                         wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
188
189            # TODO: Fix fitting options combobox/radiobox
190            # Combobox for selecting fitting options
191            # iy += 1
192            # self.fitting_radio = wx.RadioBox(parent=self, id=wx.NewId(),
193            #                                  size=(4,1))
194            # self.fitting_radio.SetString(0, "No fitting")
195            # self.fitting_radio.SetString(1, "Batch Fitting")
196            # self.fitting_radio.SetString(2, "Fitting")
197            # self.fitting_radio.SetString(3, "Simultaneous and Constrained Fit")
198            # self.fitting_radio.SetValue(0)
199            # self.bck.Add(self.fitting_radio, (iy, ix), (1, 1),
200            #              wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
201
[b697396b]202            # Button to start batch slicing
[54557b5]203            iy += 1
[f15da54]204            button_label = "Apply Slicer to Selected Plots"
[f6bb24d]205            self.batch_slicer_button = wx.Button(parent=self,
206                                                 label=button_label)
[54557b5]207            self.Bind(wx.EVT_BUTTON, self.on_batch_slicer)
[f6bb24d]208            self.bck.Add(self.batch_slicer_button, (iy, ix), (1, 1),
[54557b5]209                         wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
[f6bb24d]210            iy += 1
[39f0bf4]211            self.bck.Add((5, 5), (iy, ix), (1, 1),
212                         wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 5)
[b06ef8c]213        self.bck.Layout()
214        self.bck.Fit(self)
215        self.parent.GetSizer().Layout()
216
217    def onParamChange(self, evt):
[12aa9b5]218        """
[d955bf19]219        receive an event end reset value text fields
220        inside self.parameters
[12aa9b5]221        """
[b06ef8c]222        evt.Skip()
223        if evt.type == "UPDATE":
[b40ad40]224            for item in self.parameters:
[b06ef8c]225                if item[0] in evt.params:
[32c0841]226                    item[1].SetValue("%-5.3g" % evt.params[item[0]])
[b06ef8c]227                    item[1].Refresh()
[b40ad40]228
229    def onTextEnter(self, evt):
[b06ef8c]230        """
[d955bf19]231        Parameters have changed
[b40ad40]232        """
[b06ef8c]233        params = {}
234        has_error = False
235        for item in self.parameters:
236            try:
237                params[item[0]] = float(item[1].GetValue())
[e075203]238                item[1].SetBackgroundColour(
239                    wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
[b06ef8c]240                item[1].Refresh()
241            except:
242                has_error = True
243                item[1].SetBackgroundColour("pink")
244                item[1].Refresh()
245
[e075203]246        if not has_error:
[b06ef8c]247            # Post parameter event
[e075203]248            # parent here is plotter2D
[b06ef8c]249            event = SlicerParameterEvent(type=self.type, params=params)
[cac1828]250            wx.PostEvent(self.parent, event)
[39f0bf4]251
[54557b5]252    def on_batch_slicer(self, evt=None):
[39f0bf4]253        """
[cac1828]254        Method invoked when batch slicing button is pressed
[39f0bf4]255        :param evt: Event triggering hide/show of the batch slicer parameters
256        """
[a099131]257        apply_to_list = []
258        spp = self.parent.parent
259        params = self.parent.slicer.get_params()
260        type = self.type_select.GetStringSelection()
[54557b5]261        save = self.auto_save.IsChecked()
262        append = self.append_name.GetValue()
263        path = self.path.GetPath()
[a099131]264
[b697396b]265        # Find desired 2D data panels
[a099131]266        for key, mgr in spp.plot_panels.iteritems():
[f15da54]267            if mgr.graph.prop['title'] in self.data_list.CheckedStrings:
[a099131]268                apply_to_list.append(mgr)
269
[b697396b]270        # Apply slicer type to selected panels
[a099131]271        for item in apply_to_list:
272            self._apply_slicer_to_plot(item, type)
[f15da54]273
[b697396b]274        # Post an event to apply appropriate slicer params to each slicer
[cac1828]275        event_params = apply_params(params=params, apply_to_list=apply_to_list,
[54557b5]276                             auto_save=save, append=append,
[71b25b2]277                             path=path, type=type)
[cac1828]278        wx.PostEvent(self, event_params)
[39f0bf4]279
280    def onChangeSlicer(self, evt):
281        """
[df27aa8]282        Event driven slicer change when self.type_select changes
[39f0bf4]283        :param evt: Event triggering this change
284        """
[df27aa8]285        self._apply_slicer_to_plot(self.parent)
286
[a099131]287    def _apply_slicer_to_plot(self, plot, type=None):
[df27aa8]288        """
289        Apply a slicer to *any* plot window, not just parent window
[f15da54]290        :param plot: 2D plot panel to apply a slicer to
291        :param type: The type of slicer to apply to the panel
[df27aa8]292        """
[71b25b2]293        # Skip redrawing the current plot if no change
294        if self.parent == plot and self.type == type:
295            return
[cac1828]296        # Do not draw a slicer on a 1D plot
297        if not isinstance(plot, ModelPanel2D):
298            return
[a099131]299        if type is None:
300            type = self.type_select.GetStringSelection()
301        if type == "SectorInteractor":
302            plot.onSectorQ(None)
303        elif type == "AnnulusInteractor":
304            plot.onSectorPhi(None)
305        elif type == "BoxInteractorX":
306            plot.onBoxavgX(None)
307        elif type == "BoxInteractorY":
308            plot.onBoxavgY(None)
[df27aa8]309
[dfa1579]310    def process_list(self):
[b697396b]311        """
312        Populate the check list from the currently plotted 2D data
313        """
[f15da54]314        self.checkme = None
[dfa1579]315        main_window = self.parent.parent
316        self.loaded_data = []
317        id = wx.NewId()
[b697396b]318        # Iterate over the loaded plots and find all 2D panels
[f15da54]319        for key, value in main_window.plot_panels.iteritems():
320            if isinstance(value, ModelPanel2D):
321                self.loaded_data.append(value.data2D.name)
322                if value.data2D.id == self.parent.data2D.id:
[b697396b]323                    # Set current plot panel as uncheckable
[f15da54]324                    self.checkme = self.loaded_data.index(value.data2D.name)
[dfa1579]325        self.data_list = wx.CheckListBox(parent=self, id=id,
326                                         choices=self.loaded_data,
[f15da54]327                                         name="Apply Slicer to 2D Plots:")
[54557b5]328        # Check all items by default
[b697396b]329        for item in range(len(self.data_list.Items)):
330            self.data_list.Check(item)
[54557b5]331        self.data_list.Bind(wx.EVT_CHECKLISTBOX, self.on_check_box_list)
[dfa1579]332
[54557b5]333    def on_check_box_list(self, evt=None):
[f15da54]334        """
[b697396b]335        Prevent a checkbox item from being unchecked
[f15da54]336        :param e: Event triggered when a checkbox list item is checked
337        """
[54557b5]338        if evt is None:
339            return
340        index = evt.GetSelection()
[dfa1579]341        if index == self.checkme:
342            self.data_list.Check(index)
[f15da54]343
[54557b5]344    def apply_params_list_and_process(self, evt=None):
[b697396b]345        """
346        Event based parameter setting.
347        :param evt: Event triggered to apply parameters to a list of plots
348                    evt should have attrs plot_list and params
349        """
350        # Apply parameter list to each plot as desired
[cac1828]351        for item in evt.apply_to_list:
[71b25b2]352            event = SlicerParameterEvent(type=evt.type, params=evt.params)
353            wx.PostEvent(item, event)
[54557b5]354        # Post an event to save each data set to file
355        if evt.auto_save:
356            event = auto_save(append_to_name=evt.append,
[cac1828]357                              file_list=evt.apply_to_list,
[71b25b2]358                              path=evt.path, type=evt.type)
[54557b5]359            wx.PostEvent(self, event)
360
361    def save_files(self, evt=None):
362        """
363        Automatically save the sliced data to file.
364        :param evt: Event that triggered the call to the method
365        """
366        if evt is None:
367            return
368        writer = Reader()
369        main_window = self.parent.parent
[982577b]370        data_dic = {}
[54557b5]371        append = evt.append_to_name
[71b25b2]372        names = []
373        convert_dict = {"SectorInteractor": "SectorQ",
374                        "AnnulusInteractor": "AnnulusPhi",
375                        "BoxInteractorX": "SlabX",
376                        "BoxInteractorY": "SlabY"}
377        for f_name in evt.file_list:
378            names.append(f_name.data2D.label)
[54557b5]379        for key, plot in main_window.plot_panels.iteritems():
380            if not hasattr(plot, "data2D"):
381                for item in plot.plots:
[71b25b2]382                    base = item.replace(convert_dict[evt.type], "")
383                    if base in names:
384                        data_dic[item] = plot.plots[item]
[982577b]385        for item, data1d in data_dic.iteritems():
[cac1828]386            base = ('.').join(item.split('.')[:-1])
[982577b]387            save_to = evt.path + "\\" + base + append + ".xml"
388            writer.write(save_to, data1d)
[54557b5]389
390    def on_auto_save_checked(self, evt=None):
391        """
392        Enable/Disable auto append when checkbox is checked
393        :param evt: Event
394        """
395        self.append_name.Enable(self.auto_save.IsChecked())
396        self.path.Enable(self.auto_save.IsChecked())
Note: See TracBrowser for help on using the repository browser.