source: sasview/src/sas/qtgui/Perspectives/Fitting/SmearingWidget.py @ fb3d974

ESS_GUIESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalc
Last change on this file since fb3d974 was 9a7c81c, checked in by Piotr Rozyczko <rozyczko@…>, 6 years ago

Fixed smearing for 1 and 2D

  • Property mode set to 100644
File size: 9.9 KB
Line 
1"""
2Widget/logic for smearing data.
3"""
4import copy
5import numpy as np
6from PyQt5 import QtCore
7from PyQt5 import QtGui
8from PyQt5 import QtWidgets
9
10from sas.sascalc.fit.qsmearing import smear_selection
11from sas.qtgui.Plotting.PlotterData import Data1D
12from sas.qtgui.Plotting.PlotterData import Data2D
13import sas.qtgui.Utilities.GuiUtils as GuiUtils
14
15# Local UI
16from sas.qtgui.Perspectives.Fitting.UI.SmearingWidgetUI import Ui_SmearingWidgetUI
17
18class DataWidgetMapper(QtWidgets.QDataWidgetMapper):
19    """
20    Custom version of the standard QDataWidgetMapper allowing for proper
21    response to index change in comboboxes
22    """
23    def addMapping(self, widget, section, propertyName=None):
24        if propertyName is None:
25            super(DataWidgetMapper, self).addMapping(widget, section)
26        else:
27            super(DataWidgetMapper, self).addMapping(widget, section, propertyName)
28
29        if isinstance(widget, QtWidgets.QComboBox):
30            delegate = self.itemDelegate()
31            widget.currentIndexChanged.connect(lambda: delegate.commitData.emit(widget))
32
33SMEARING_1D = ["Custom Pinhole Smear", "Custom Slit Smear"]
34SMEARING_2D = ["Custom Pinhole Smear"]
35
36MODEL = [
37    'SMEARING',
38    'PINHOLE_MIN',
39    'PINHOLE_MAX',
40    'ACCURACY']
41ACCURACY_DICT={'Low': 'low',
42               'Medium': 'med',
43               'High': 'high',
44               'Extra high': 'xhigh'}
45
46DEFAULT_PINHOLE_UP=0.0
47DEFAULT_PINHOLE_DOWN=0.0
48
49class SmearingWidget(QtWidgets.QWidget, Ui_SmearingWidgetUI):
50    smearingChangedSignal = QtCore.pyqtSignal()
51    def __init__(self, parent=None):
52        super(SmearingWidget, self).__init__()
53
54        self.setupUi(self)
55
56        # Local model for holding data
57        self.model = None
58        # Mapper for model update
59        self.mapper = None
60        # Data from the widget
61        self.data = None
62        self.current_smearer = None
63
64        # Let only floats in the line edits
65        self.txtSmearDown.setValidator(GuiUtils.DoubleValidator())
66        self.txtSmearUp.setValidator(GuiUtils.DoubleValidator())
67
68        # Attach slots
69        self.cbSmearing.currentIndexChanged.connect(self.onIndexChange)
70        self.cbSmearing.setCurrentIndex(0)
71        self.txtSmearUp.setText(str(DEFAULT_PINHOLE_UP))
72        self.txtSmearDown.setText(str(DEFAULT_PINHOLE_DOWN))
73
74        self.initModel()
75        self.initMapper()
76
77    def initModel(self):
78        """
79        Initialize the state
80        """
81        self.model = QtGui.QStandardItemModel()
82        for model_item in range(len(MODEL)):
83            self.model.setItem(model_item, QtGui.QStandardItem())
84        # Attach slot
85        self.model.dataChanged.connect(self.onModelChange)
86
87    def initMapper(self):
88        """
89        Initialize model item <-> UI element mapping
90        """
91        self.mapper = DataWidgetMapper(self)
92
93        self.mapper.setModel(self.model)
94        self.mapper.setOrientation(QtCore.Qt.Vertical)
95
96        self.mapper.addMapping(self.txtSmearUp,   MODEL.index('PINHOLE_MIN'))
97        self.mapper.addMapping(self.txtSmearDown, MODEL.index('PINHOLE_MAX'))
98        self.mapper.addMapping(self.cbAccuracy,   MODEL.index('ACCURACY'))
99
100        self.mapper.toFirst()
101
102    def updateData(self, data=None):
103        """
104        Update control elements based on data and model passed
105        """
106        self.cbSmearing.clear()
107        self.cbSmearing.addItem("None")
108        self.gAccuracy.setVisible(False)
109        self.data = data
110        if data is None:
111            self.setElementsVisibility(False)
112        elif isinstance(data, Data1D):
113            self.cbSmearing.addItems(SMEARING_1D)
114        else:
115            self.cbSmearing.addItems(SMEARING_2D)
116        self.cbSmearing.setCurrentIndex(0)
117
118    def updateKernelModel(self, kernel_model=None):
119        """
120        Update the model
121        """
122        self.kernel_model = kernel_model
123
124    def smearer(self):
125        """ Returns the current smearer """
126        return self.current_smearer
127
128    def onIndexChange(self, index):
129        """
130        Callback for smearing combobox index change
131        """
132        if index == 0:
133            self.setElementsVisibility(False)
134            self.current_smearer = None
135        elif index == 1:
136            self.setElementsVisibility(True)
137            self.setPinholeLabels()
138            self.onPinholeSmear()
139        elif index == 2:
140            self.setElementsVisibility(True)
141            self.setSlitLabels()
142            self.onSlitSmear()
143        self.smearingChangedSignal.emit()
144
145    def onModelChange(self):
146        """
147        Respond to model change by notifying any listeners
148        """
149        # Recalculate the smearing
150        index = self.cbSmearing.currentIndex()
151        self.onIndexChange(index)
152
153    def setElementsVisibility(self, visible):
154        """
155        Labels and linedits visibility control
156        """
157        self.lblSmearDown.setVisible(visible)
158        self.lblSmearUp.setVisible(visible)
159        self.txtSmearDown.setVisible(visible)
160        self.txtSmearUp.setVisible(visible)
161        self.lblUnitUp.setVisible(visible)
162        self.lblUnitDown.setVisible(visible)
163        self.setAccuracyVisibility()
164
165    def setAccuracyVisibility(self):
166        """
167        Accuracy combobox visibility
168        """
169        if isinstance(self.data, Data2D) and self.cbSmearing.currentIndex() == 1:
170            self.gAccuracy.setVisible(True)
171        else:
172            self.gAccuracy.setVisible(False)
173
174    def setPinholeLabels(self):
175        """
176        Use pinhole labels
177        """
178        self.txtSmearDown.setVisible(False)
179        self.lblSmearDown.setText('')
180        self.lblUnitDown.setText('')
181        if isinstance(self.data, Data2D):
182            self.lblUnitUp.setText('<html><head/><body><p>Å<span style=" vertical-align:super;">-1</span></p></body></html>')
183            self.lblSmearUp.setText('<html><head/><body><p>&lt;dQ<span style=" vertical-align:sub;">low</span>&gt;</p></body></html>')
184        else:
185            self.lblSmearUp.setText('<html><head/><body><p>dQ<span style=" vertical-align:sub;">%</span></p></body></html>')
186            self.lblUnitUp.setText('%')
187
188    def setSlitLabels(self):
189        """
190        Use pinhole labels
191        """
192        self.lblSmearUp.setText('Slit height')
193        self.lblSmearDown.setText('Slit width')
194        self.lblUnitUp.setText('<html><head/><body><p>Å<span style=" vertical-align:super;">-1</span></p></body></html>')
195        self.lblUnitDown.setText('<html><head/><body><p>Å<span style=" vertical-align:super;">-1</span></p></body></html>')
196
197    def state(self):
198        """
199        Returns current state of controls
200        """
201        smearing = self.cbSmearing.currentText()
202        accuracy = ""
203        d_down = None
204        d_up = None
205        if smearing != "None":
206            accuracy = str(self.model.item(MODEL.index('ACCURACY')).text())
207            try:
208                d_down = float(self.model.item(MODEL.index('PINHOLE_MIN')).text())
209            except ValueError:
210                d_down = None
211            try:
212                d_up = float(self.model.item(MODEL.index('PINHOLE_MAX')).text())
213            except ValueError:
214                d_up = None
215
216        return (smearing, accuracy, d_down, d_up)
217
218    def setState(self, smearing, accuracy, d_down, d_up):
219        """
220        Sets new values for the controls
221        """
222        # Update the model -> controls update automatically
223        #if smearing is not None:
224            #self.model.item(MODEL.index('SMEARING')).setText(smearing)
225        if accuracy is not None:
226            self.model.item(MODEL.index('ACCURACY')).setText(accuracy)
227        if d_down is not None:
228            self.model.item(MODEL.index('PINHOLE_MIN')).setText(d_down)
229        if d_up is not None:
230            self.model.item(MODEL.index('PINHOLE_MAX')).setText(d_up)
231
232    def onPinholeSmear(self):
233        """
234        Create a custom pinhole smear object that will change the way residuals
235        are compute when fitting
236        """
237        _, accuracy, d_percent, _ = self.state()
238        if d_percent is None or d_percent == 0.0:
239            self.current_smearer=None
240            return
241        percent = d_percent/100.0
242        # copy data
243        data = copy.deepcopy(self.data)
244        if isinstance(self.data, Data2D):
245            len_data = len(data.data)
246            data.dqx_data = np.zeros(len_data)
247            data.dqy_data = np.zeros(len_data)
248            data.dqx_data[data.dqx_data == 0] = percent * data.qx_data
249            data.dqy_data[data.dqy_data == 0] = percent * data.qy_data
250        else:
251            len_data = len(data.x)
252            data.dx = np.zeros(len_data)
253            data.dx = percent * data.x
254            data.dxl = None
255            data.dxw = None
256
257        self.current_smearer = smear_selection(data, self.kernel_model)
258        # need to set accuracy for 2D
259        if isinstance(self.data, Data2D):
260            backend_accuracy = ACCURACY_DICT.get(accuracy)
261            if backend_accuracy:
262                self.current_smearer.set_accuracy(accuracy=backend_accuracy)
263
264    def onSlitSmear(self):
265        """
266        Create a custom slit smear object that will change the way residuals
267        are compute when fitting
268        """
269        _, accuracy, d_height, d_width = self.state()
270        # Check changes in slit width
271        if d_width is None:
272            d_width = 0.0
273        if d_height is None:
274            d_height = 0.0
275
276        if isinstance(self.data, Data2D):
277            return
278        # make sure once more if it is smearer
279        data = copy.deepcopy(self.data)
280        data_len = len(data.x)
281        data.dx = None
282        data.dxl = None
283        data.dxw = None
284
285        try:
286            self.dxl = d_height
287            data.dxl = self.dxl * np.ones(data_len)
288        except:
289            self.dxl = None
290            data.dxl = np.zeros(data_len)
291        try:
292            self.dxw = d_width
293            data.dxw = self.dxw * np.ones(data_len)
294        except:
295            self.dxw = None
296            data.dxw = np.zeros(data_len)
297
298        self.current_smearer = smear_selection(data, self.kernel_model)
Note: See TracBrowser for help on using the repository browser.