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

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

Lint free

  • 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        if self.fourierBtn.isChecked():
192            method = "fourier"
193        elif self.hilbertBtn.isChecked():
194            method = "hilbert"
195
196        extrap = self._canvas.extrap
197        background = float(self.model.item(W.W_BACKGROUND).text())
198
199        def updatefn(msg):
200            """Report progress of transformation."""
201            self.communicate.statusBarUpdateSignal.emit(msg)
202
203        def completefn(transforms):
204            """Extract the values from the transforms and plot"""
205            self._realplot.data = transforms
206            self._realplot.draw_real_space()
207            params = self._calculator.extract_parameters(transforms[0])
208            self.model.setItem(W.W_CORETHICK,
209                               QtGui.QStandardItem(str(params['d0'])))
210            self.model.setItem(W.W_INTTHICK,
211                               QtGui.QStandardItem(str(params['dtr'])))
212            self.model.setItem(W.W_HARDBLOCK,
213                               QtGui.QStandardItem(str(params['Lc'])))
214            self.model.setItem(W.W_CRYSTAL,
215                               QtGui.QStandardItem(str(params['fill'])))
216            self.model.setItem(W.W_POLY,
217                               QtGui.QStandardItem(str(params['A'])))
218            self.model.setItem(W.W_PERIOD,
219                               QtGui.QStandardItem(str(params['max'])))
220
221        self._update_calculator()
222        self._calculator.compute_transform(extrap, method, background,
223                                           completefn, updatefn)
224
225    def setup_mapper(self):
226        """Creating mapping between model and gui elements."""
227        self.mapper = QtGui.QDataWidgetMapper(self)
228        self.mapper.setOrientation(QtCore.Qt.Vertical)
229        self.mapper.setModel(self.model)
230
231        self.mapper.addMapping(self.qMin, W.W_QMIN)
232        self.mapper.addMapping(self.qMax1, W.W_QMAX)
233        self.mapper.addMapping(self.qMax2, W.W_QCUTOFF)
234        self.mapper.addMapping(self.bg, W.W_BACKGROUND)
235
236        self.mapper.addMapping(self.guinierA, W.W_GUINIERA)
237        self.mapper.addMapping(self.guinierB, W.W_GUINIERB)
238        self.mapper.addMapping(self.porodK, W.W_PORODK)
239        self.mapper.addMapping(self.porodSigma, W.W_PORODSIGMA)
240
241        self.mapper.addMapping(self.avgCoreThick, W.W_CORETHICK)
242        self.mapper.addMapping(self.avgIntThick, W.W_INTTHICK)
243        self.mapper.addMapping(self.avgHardBlock, W.W_HARDBLOCK)
244        self.mapper.addMapping(self.polydisp, W.W_POLY)
245        self.mapper.addMapping(self.longPeriod, W.W_PERIOD)
246        self.mapper.addMapping(self.localCrystal, W.W_CRYSTAL)
247
248        self.mapper.toFirst()
249
250    def calculate_background(self):
251        """Find a good estimate of the background value."""
252        self._update_calculator()
253        background = self._calculator.compute_background()
254        temp = QtGui.QStandardItem(str(background))
255        self.model.setItem(W.W_BACKGROUND, temp)
256
257    # pylint: disable=invalid-name
258    @staticmethod
259    def allowBatch():
260        """
261        We cannot perform corfunc analysis in batch at this time.
262        """
263        return False
264
265    def setData(self, data_item, is_batch=False):
266        """
267        Obtain a QStandardItem object and dissect it to get Data1D/2D
268        Pass it over to the calculator
269        """
270        if not isinstance(data_item, list):
271            msg = "Incorrect type passed to the Corfunc Perpsective"
272            raise AttributeError(msg)
273
274        if not isinstance(data_item[0], QtGui.QStandardItem):
275            msg = "Incorrect type passed to the Corfunc Perspective"
276            raise AttributeError(msg)
277
278        model_item = data_item[0]
279        data = GuiUtils.dataFromItem(model_item)
280        self._calculator.set_data(data)
281
282        self._canvas.data = data
283        self._canvas.draw_q_space()
284
285        # self.model.item(WIDGETS.W_FILENAME).setData(QtCoreQVariant(self._model_item.text()))
286
287    def setClosable(self, value=True):
288        """
289        Allow outsiders close this widget
290        """
291        assert isinstance(value, bool)
292
293        self._allow_close = value
294    # pylint: enable=invalid-name
Note: See TracBrowser for help on using the repository browser.