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

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

Lock unusable buttons

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