source: sasview/src/sas/qtgui/Perspectives/Corfunc/CorfuncPerspective.py @ 6930b91

Last change on this file since 6930b91 was 6930b91, checked in by Adam Washington <adam.washington@…>, 6 years ago

Move to combobox to pick transformation model

  • Property mode set to 100644
File size: 10.9 KB
Line 
1"""
2This module provides the intelligence behind the gui interface for Corfunc.
3"""
4# pylint: disable=E1101
5
6# global
7from PyQt4 import QtCore
8from PyQt4 import QtGui
9
10# sas-global
11# pylint: disable=import-error, no-name-in-module
12import sas.qtgui.Utilities.GuiUtils as GuiUtils
13from sas.sascalc.corfunc.corfunc_calculator import CorfuncCalculator
14# pylint: enable=import-error, no-name-in-module
15
16# local
17from UI.CorfuncPanel import Ui_CorfuncDialog
18# from InvariantDetails import DetailsDialog
19from CorfuncUtils import WIDGETS as W
20
21from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg \
22    as FigureCanvas
23from matplotlib.figure import Figure
24
25
26class MyMplCanvas(FigureCanvas):
27    """Ultimately, this is a QWidget (as well as a FigureCanvasAgg, etc.)."""
28    def __init__(self, model, width=5, height=4, dpi=100):
29        self.model = model
30        self.fig = Figure(figsize=(width, height), dpi=dpi)
31        self.axes = self.fig.add_subplot(111)
32
33        FigureCanvas.__init__(self, self.fig)
34
35        self.data = None
36        self.extrap = None
37
38    def draw_q_space(self):
39        """Draw the Q space data in the plot window
40
41        This draws the q space data in self.data, as well
42        as the bounds set by self.qmin, self.qmax1, and self.qmax2.
43        It will also plot the extrpolation in self.extrap, if it exists."""
44
45        self.fig.clf()
46
47        self.axes = self.fig.add_subplot(111)
48        self.axes.set_xscale("log")
49        self.axes.set_yscale("log")
50
51        qmin = float(self.model.item(W.W_QMIN).text())
52        qmax1 = float(self.model.item(W.W_QMAX).text())
53        qmax2 = float(self.model.item(W.W_QCUTOFF).text())
54
55        if self.data:
56            self.axes.plot(self.data.x, self.data.y)
57            self.axes.axvline(qmin)
58            self.axes.axvline(qmax1)
59            self.axes.axvline(qmax2)
60            self.axes.set_xlim(min(self.data.x) / 2,
61                               max(self.data.x) * 1.5 - 0.5 * min(self.data.x))
62        if self.extrap:
63            self.axes.plot(self.extrap.x, self.extrap.y)
64
65        self.draw()
66
67    def draw_real_space(self):
68        """
69        This function draws the real space data onto the plot
70
71        The 1d correlation function in self.data, the 3d correlation function
72        in self.data3, and the interface distribution function in self.data_idf
73        are all draw in on the plot in linear cooredinates."""
74        self.fig.clf()
75
76        self.axes = self.fig.add_subplot(111)
77        self.axes.set_xscale("linear")
78        self.axes.set_yscale("linear")
79
80        if self.data:
81            data1, data3, data_idf = self.data
82            self.axes.plot(data1.x, data1.y, label="1D Correlation")
83            self.axes.plot(data3.x, data3.y, label="3D Correlation")
84            self.axes.plot(data_idf.x, data_idf.y,
85                           label="Interface Distribution Function")
86            self.axes.set_xlim(min(data1.x), max(data1.x) / 4)
87            self.axes.legend()
88
89        self.draw()
90
91
92class CorfuncWindow(QtGui.QDialog, Ui_CorfuncDialog):
93    """Displays the correlation function analysis of sas data."""
94    name = "Corfunc"  # For displaying in the combo box
95
96# pylint: disable=unused-argument
97    def __init__(self, parent=None):
98        super(CorfuncWindow, self).__init__()
99        self.setupUi(self)
100
101        self.setWindowTitle("Corfunc Perspective")
102
103        self.mapper = None
104        self.model = QtGui.QStandardItemModel(self)
105        self.communicate = GuiUtils.Communicate()
106        self._calculator = CorfuncCalculator()
107        self._allow_close = True
108
109        self._canvas = MyMplCanvas(self.model)
110        self._realplot = MyMplCanvas(self.model)
111        self.verticalLayout_7.insertWidget(0, self._canvas)
112        self.verticalLayout_7.insertWidget(1, self._realplot)
113
114        # Connect buttons to slots.
115        # Needs to be done early so default values propagate properly.
116        self.setup_slots()
117
118        # Set up the model.
119        self.setup_model()
120
121        # Set up the mapper
122        self.setup_mapper()
123
124    def setup_slots(self):
125        """Connect the buttons to their appropriate slots."""
126        self.extrapolateBtn.clicked.connect(self.extrapolate)
127        self.transformBtn.clicked.connect(self.transform)
128
129        self.calculateBgBtn.clicked.connect(self.calculate_background)
130
131        self.model.itemChanged.connect(self.model_changed)
132
133    def setup_model(self):
134        """Populate the model with default data."""
135        self.model.setItem(W.W_QMIN,
136                           QtGui.QStandardItem("0.01"))
137        self.model.setItem(W.W_QMAX,
138                           QtGui.QStandardItem("0.20"))
139        self.model.setItem(W.W_QCUTOFF,
140                           QtGui.QStandardItem("0.22"))
141        self.model.setItem(W.W_BACKGROUND,
142                           QtGui.QStandardItem("0"))
143        self.model.setItem(W.W_TRANSFORM,
144                           QtGui.QStandardItem("Fourier"))
145        self.model.setItem(W.W_GUINIERA,
146                           QtGui.QStandardItem("0.0"))
147        self.model.setItem(W.W_GUINIERB,
148                           QtGui.QStandardItem("0.0"))
149        self.model.setItem(W.W_PORODK,
150                           QtGui.QStandardItem("0.0"))
151        self.model.setItem(W.W_PORODSIGMA,
152                           QtGui.QStandardItem("0.0"))
153        self.model.setItem(W.W_CORETHICK, QtGui.QStandardItem(str(0)))
154        self.model.setItem(W.W_INTTHICK, QtGui.QStandardItem(str(0)))
155        self.model.setItem(W.W_HARDBLOCK, QtGui.QStandardItem(str(0)))
156        self.model.setItem(W.W_CRYSTAL, QtGui.QStandardItem(str(0)))
157        self.model.setItem(W.W_POLY, QtGui.QStandardItem(str(0)))
158        self.model.setItem(W.W_PERIOD, QtGui.QStandardItem(str(0)))
159
160    def model_changed(self, _):
161        """Actions to perform when the data is updated"""
162        if not self.mapper:
163            return
164        self.mapper.toFirst()
165        self._canvas.draw_q_space()
166
167    def _update_calculator(self):
168        self._calculator.lowerq = float(self.model.item(W.W_QMIN).text())
169        qmax1 = float(self.model.item(W.W_QMAX).text())
170        qmax2 = float(self.model.item(W.W_QCUTOFF).text())
171        self._calculator.upperq = (qmax1, qmax2)
172        self._calculator.background = \
173            float(self.model.item(W.W_BACKGROUND).text())
174
175    def extrapolate(self):
176        """Extend the experiemntal data with guinier and porod curves."""
177        self._update_calculator()
178        params, extrapolation, _ = self._calculator.compute_extrapolation()
179
180        self.model.setItem(W.W_GUINIERA, QtGui.QStandardItem(str(params['A'])))
181        self.model.setItem(W.W_GUINIERB, QtGui.QStandardItem(str(params['B'])))
182        self.model.setItem(W.W_PORODK, QtGui.QStandardItem(str(params['K'])))
183        self.model.setItem(W.W_PORODSIGMA,
184                           QtGui.QStandardItem(str(params['sigma'])))
185
186        self._canvas.extrap = extrapolation
187        self._canvas.draw_q_space()
188
189    def transform(self):
190        """Calculate the real space version of the extrapolation."""
191        method = str(self.model.item(W.W_TRANSFORM).text()).lower()
192
193        extrap = self._canvas.extrap
194        background = float(self.model.item(W.W_BACKGROUND).text())
195
196        def updatefn(msg):
197            """Report progress of transformation."""
198            self.communicate.statusBarUpdateSignal.emit(msg)
199
200        def completefn(transforms):
201            """Extract the values from the transforms and plot"""
202            self._realplot.data = transforms
203            self._realplot.draw_real_space()
204            params = self._calculator.extract_parameters(transforms[0])
205            self.model.setItem(W.W_CORETHICK,
206                               QtGui.QStandardItem(str(params['d0'])))
207            self.model.setItem(W.W_INTTHICK,
208                               QtGui.QStandardItem(str(params['dtr'])))
209            self.model.setItem(W.W_HARDBLOCK,
210                               QtGui.QStandardItem(str(params['Lc'])))
211            self.model.setItem(W.W_CRYSTAL,
212                               QtGui.QStandardItem(str(params['fill'])))
213            self.model.setItem(W.W_POLY,
214                               QtGui.QStandardItem(str(params['A'])))
215            self.model.setItem(W.W_PERIOD,
216                               QtGui.QStandardItem(str(params['max'])))
217
218        self._update_calculator()
219        self._calculator.compute_transform(extrap, method, background,
220                                           completefn, updatefn)
221
222    def setup_mapper(self):
223        """Creating mapping between model and gui elements."""
224        self.mapper = QtGui.QDataWidgetMapper(self)
225        self.mapper.setOrientation(QtCore.Qt.Vertical)
226        self.mapper.setModel(self.model)
227
228        self.mapper.addMapping(self.qMin, W.W_QMIN)
229        self.mapper.addMapping(self.qMax1, W.W_QMAX)
230        self.mapper.addMapping(self.qMax2, W.W_QCUTOFF)
231        self.mapper.addMapping(self.bg, W.W_BACKGROUND)
232        self.mapper.addMapping(self.transformCombo, W.W_TRANSFORM)
233
234        self.mapper.addMapping(self.guinierA, W.W_GUINIERA)
235        self.mapper.addMapping(self.guinierB, W.W_GUINIERB)
236        self.mapper.addMapping(self.porodK, W.W_PORODK)
237        self.mapper.addMapping(self.porodSigma, W.W_PORODSIGMA)
238
239        self.mapper.addMapping(self.avgCoreThick, W.W_CORETHICK)
240        self.mapper.addMapping(self.avgIntThick, W.W_INTTHICK)
241        self.mapper.addMapping(self.avgHardBlock, W.W_HARDBLOCK)
242        self.mapper.addMapping(self.polydisp, W.W_POLY)
243        self.mapper.addMapping(self.longPeriod, W.W_PERIOD)
244        self.mapper.addMapping(self.localCrystal, W.W_CRYSTAL)
245
246        self.mapper.toFirst()
247
248    def calculate_background(self):
249        """Find a good estimate of the background value."""
250        self._update_calculator()
251        background = self._calculator.compute_background()
252        temp = QtGui.QStandardItem(str(background))
253        self.model.setItem(W.W_BACKGROUND, temp)
254
255    # pylint: disable=invalid-name
256    @staticmethod
257    def allowBatch():
258        """
259        We cannot perform corfunc analysis in batch at this time.
260        """
261        return False
262
263    def setData(self, data_item, is_batch=False):
264        """
265        Obtain a QStandardItem object and dissect it to get Data1D/2D
266        Pass it over to the calculator
267        """
268        if not isinstance(data_item, list):
269            msg = "Incorrect type passed to the Corfunc Perpsective"
270            raise AttributeError(msg)
271
272        if not isinstance(data_item[0], QtGui.QStandardItem):
273            msg = "Incorrect type passed to the Corfunc Perspective"
274            raise AttributeError(msg)
275
276        model_item = data_item[0]
277        data = GuiUtils.dataFromItem(model_item)
278        self._calculator.set_data(data)
279
280        self._canvas.data = data
281        self._canvas.draw_q_space()
282
283        # self.model.item(WIDGETS.W_FILENAME).setData(QtCoreQVariant(self._model_item.text()))
284
285    def setClosable(self, value=True):
286        """
287        Allow outsiders close this widget
288        """
289        assert isinstance(value, bool)
290
291        self._allow_close = value
292    # pylint: enable=invalid-name
Note: See TracBrowser for help on using the repository browser.