# -*- coding: utf-8 -*- """ Dialog panel to explore the P(r) inversion results for a range of D_max value. User picks a number of points and a range of distances, then can toggle between inversion outputs and see their distribution as a function of D_max. """ # global import sys import os import logging import numpy as np from PyQt5 import QtCore from PyQt5 import QtGui from PyQt5 import QtWidgets from twisted.internet import threads # sas-global from sas.qtgui.Plotting.PlotterData import Data1D from sas.qtgui.Plotting.Plotter import PlotterWidget import sas.qtgui.Utilities.GuiUtils as GuiUtils # local from .UI.DMaxExplorer import Ui_DmaxExplorer logger = logging.getLogger(__name__) from sas.qtgui.Utilities.GuiUtils import enum W = enum( 'NPTS', #0 'DMIN', #1 'DMAX', #2 'VARIABLE', #3 ) class DmaxWindow(QtWidgets.QDialog, Ui_DmaxExplorer): # The controller which is responsible for managing signal slots connections # for the gui and providing an interface to the data model. name = "Dmax Explorer" # For displaying in the combo box def __init__(self, pr_state, nfunc, parent=None): super(DmaxWindow, self).__init__() self.setupUi(self) self.setWindowTitle("Dₘₐₓ Explorer") self.pr_state = pr_state self.nfunc = nfunc self.communicator = GuiUtils.Communicate() self.plot = PlotterWidget(self, self) self.hasPlot = None self.verticalLayout.insertWidget(0, self.plot) # Let's choose the Standard Item Model. self.model = QtGui.QStandardItemModel(self) self.mapper = None # # Connect buttons to slots. # # Needs to be done early so default values propagate properly. self.setupSlots() # Set up the model. self.setupModel() # # Set up the mapper self.setupMapper() def setupSlots(self): self.closeButton.clicked.connect(self.close) self.model.itemChanged.connect(self.modelChanged) def setupModel(self): self.model.setItem(W.NPTS, QtGui.QStandardItem(str(self.nfunc))) self.model.setItem(W.DMIN, QtGui.QStandardItem(str(0.9*self.pr_state.d_max))) self.model.setItem(W.DMAX, QtGui.QStandardItem(str(1.1*self.pr_state.d_max))) self.model.setItem(W.VARIABLE, QtGui.QStandardItem( "χ²/dof")) def setupMapper(self): self.mapper = QtWidgets.QDataWidgetMapper(self) self.mapper.setOrientation(QtCore.Qt.Vertical) self.mapper.setModel(self.model) self.mapper.addMapping(self.Npts, W.NPTS) self.mapper.addMapping(self.minDist, W.DMIN) self.mapper.addMapping(self.maxDist, W.DMAX) self.mapper.addMapping(self.dependentVariable, W.VARIABLE) self.mapper.toFirst() def modelChanged(self, item): if not self.mapper: return iq0 = [] rg = [] pos = [] pos_err = [] osc = [] bck = [] chi2 = [] xs = np.linspace(float(self.model.item(W.DMIN).text()), float(self.model.item(W.DMAX).text()), float(self.model.item(W.NPTS).text())) original = self.pr_state.d_max for x in xs: self.pr_state.d_max = x try: out, cov = self.pr_state.invert(self.pr_state.nfunc) iq0.append(self.pr_state.iq0(out)) rg.append(self.pr_state.rg(out)) pos.append(self.pr_state.get_positive(out)) pos_err.append(self.pr_state.get_pos_err(out, cov)) osc.append(self.pr_state.oscillations(out)) bck.append(self.pr_state.background) chi2.append(self.pr_state.chi2) except: # This inversion failed, skip this D_max value msg = "ExploreDialog: inversion failed " msg += "for D_max=%s\n%s" % (str(x), sys.exc_info()[1]) print(msg) logger.error(msg) #Return the invertor to its original state self.pr_state.d_max = original try: self.pr_state.invert(self.nfunc) except RuntimeError as ex: msg = "ExploreDialog: inversion failed " msg += "for D_max=%s\n%s" % (str(x), sys.exc_info()[1]) print(msg) logger.error(msg) plotter = str(self.model.item(W.VARIABLE).text()) if plotter == "χ²/dof": ys = chi2 elif plotter == "I(Q=0)": ys = iq0 elif plotter == "Rg": ys = rg elif plotter == "Oscillation parameter": ys = osc elif plotter == "Background": ys = bck elif plotter == "Positive Fraction": ys = pos else: ys = pos_err data = Data1D(xs, ys) if self.hasPlot: self.plot.removePlot(None) self.hasPlot = True self.plot.plot(data=data, marker="-")