# global
import sys
import os
import logging
import copy
import webbrowser
from PyQt5 import QtCore
from PyQt5 import QtGui, QtWidgets
from twisted.internet import threads
from twisted.internet import reactor
# sas-global
from sas.sascalc.invariant import invariant
from sas.qtgui.Plotting.PlotterData import Data1D
import sas.qtgui.Utilities.GuiUtils as GuiUtils
# import sas.qtgui.Plotting.PlotHelper as PlotHelper
# local
from .UI.TabbedInvariantUI import Ui_tabbedInvariantUI
from .InvariantDetails import DetailsDialog
from .InvariantUtils import WIDGETS
# The minimum q-value to be used when extrapolating
Q_MINIMUM = 1e-5
# The maximum q-value to be used when extrapolating
Q_MAXIMUM = 10
# the ratio of maximum q value/(qmax of data) to plot the theory data
Q_MAXIMUM_PLOT = 3
# Default number of points of interpolation: high and low range
NPOINTS_Q_INTERP = 10
# Default power law for interpolation
DEFAULT_POWER_LOW = 4
# Background of line edits if settings OK or wrong
BG_WHITE = "background-color: rgb(255, 255, 255);"
BG_RED = "background-color: rgb(244, 170, 164);"
[docs]class InvariantWindow(QtWidgets.QDialog, Ui_tabbedInvariantUI):
# The controller which is responsible for managing signal slots connections
# for the gui and providing an interface to the data model.
name = "Invariant" # For displaying in the combo box in DataExplorer
def __init__(self, parent=None):
super(InvariantWindow, self).__init__()
self.setupUi(self)
self.setWindowTitle("Invariant Perspective")
# initial input params
self._background = 0.0
self._scale = 1.0
self._contrast = 1.0
self._porod = None
self.parent = parent
self._manager = parent
self._reactor = reactor
self._model_item = QtGui.QStandardItem()
self.detailsDialog = DetailsDialog(self)
self.detailsDialog.cmdOK.clicked.connect(self.enabling)
self._low_extrapolate = False
self._low_guinier = True
self._low_fit = False
self._low_power_value = False
self._low_points = NPOINTS_Q_INTERP
self._low_power_value = DEFAULT_POWER_LOW
self._high_extrapolate = False
self._high_power_value = False
self._high_fit = False
self._high_points = NPOINTS_Q_INTERP
self._high_power_value = DEFAULT_POWER_LOW
# no reason to have this widget resizable
self.resize(self.minimumSizeHint())
self.communicate = self._manager.communicator()
self._data = None
self._path = ""
self._allow_close = False
# Modify font in order to display Angstrom symbol correctly
new_font = 'font-family: -apple-system, "Helvetica Neue", "Ubuntu";'
self.lblTotalQUnits.setStyleSheet(new_font)
self.lblSpecificSurfaceUnits.setStyleSheet(new_font)
self.lblInvariantTotalQUnits.setStyleSheet(new_font)
self.lblContrastUnits.setStyleSheet(new_font)
self.lblPorodCstUnits.setStyleSheet(new_font)
self.lblExtrapolQUnits.setStyleSheet(new_font)
# To remove blue square around line edits
self.txtBackgd.setAttribute(QtCore.Qt.WA_MacShowFocusRect, False)
self.txtContrast.setAttribute(QtCore.Qt.WA_MacShowFocusRect, False)
self.txtScale.setAttribute(QtCore.Qt.WA_MacShowFocusRect, False)
self.txtPorodCst.setAttribute(QtCore.Qt.WA_MacShowFocusRect, False)
self.txtNptsHighQ.setAttribute(QtCore.Qt.WA_MacShowFocusRect, False)
self.txtNptsLowQ.setAttribute(QtCore.Qt.WA_MacShowFocusRect, False)
self.txtPowerLowQ.setAttribute(QtCore.Qt.WA_MacShowFocusRect, False)
self.txtPowerHighQ.setAttribute(QtCore.Qt.WA_MacShowFocusRect, False)
self.txtExtrapolQMin.setText(str(Q_MINIMUM))
self.txtExtrapolQMax.setText(str(Q_MAXIMUM))
# Let's choose the Standard Item Model.
self.model = QtGui.QStandardItemModel(self)
# 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()
# Default enablement
self.cmdCalculate.setEnabled(False)
# validator: double
self.txtBackgd.setValidator(GuiUtils.DoubleValidator())
self.txtContrast.setValidator(GuiUtils.DoubleValidator())
self.txtScale.setValidator(GuiUtils.DoubleValidator())
self.txtPorodCst.setValidator(GuiUtils.DoubleValidator())
# validator: integer number
self.txtNptsLowQ.setValidator(QtGui.QIntValidator())
self.txtNptsHighQ.setValidator(QtGui.QIntValidator())
self.txtPowerLowQ.setValidator(GuiUtils.DoubleValidator())
self.txtPowerHighQ.setValidator(GuiUtils.DoubleValidator())
[docs] def enabling(self):
""" """
self.cmdStatus.setEnabled(True)
[docs] def setClosable(self, value=True):
""" Allow outsiders close this widget """
assert isinstance(value, bool)
self._allow_close = value
[docs] def closeEvent(self, event):
"""
Overwrite QDialog close method to allow for custom widget close
"""
if self._allow_close:
# reset the closability flag
self.setClosable(value=False)
# Tell the MdiArea to close the container
self.parentWidget().close()
event.accept()
else:
event.ignore()
# Maybe we should just minimize
self.setWindowState(QtCore.Qt.WindowMinimized)
[docs] def updateFromModel(self):
""" Update the globals based on the data in the model """
self._background = float(self.model.item(WIDGETS.W_BACKGROUND).text())
self._contrast = float(self.model.item(WIDGETS.W_CONTRAST).text())
self._scale = float(self.model.item(WIDGETS.W_SCALE).text())
if self.model.item(WIDGETS.W_POROD_CST).text() != 'None' \
and self.model.item(WIDGETS.W_POROD_CST).text() != '':
self._porod = float(self.model.item(WIDGETS.W_POROD_CST).text())
# Low extrapolating
self._low_extrapolate = str(self.model.item(WIDGETS.W_ENABLE_LOWQ).text()) == 'true'
self._low_points = float(self.model.item(WIDGETS.W_NPTS_LOWQ).text())
self._low_guinier = str(self.model.item(WIDGETS.W_LOWQ_GUINIER).text()) == 'true'
self._low_fit = str(self.model.item(WIDGETS.W_LOWQ_FIT).text()) == 'true'
self._low_power_value = float(self.model.item(WIDGETS.W_LOWQ_POWER_VALUE).text())
# High extrapolating
self._high_extrapolate = str(self.model.item(WIDGETS.W_ENABLE_HIGHQ).text()) == 'true'
self._high_points = float(self.model.item(WIDGETS.W_NPTS_HIGHQ).text())
self._high_fit = str(self.model.item(WIDGETS.W_HIGHQ_FIT).text()) == 'true'
self._high_power_value = float(self.model.item(WIDGETS.W_HIGHQ_POWER_VALUE).text())
[docs] def calculateInvariant(self):
""" Use twisted to thread the calculations away """
# Find out if extrapolation needs to be used.
extrapolation = None
if self._low_extrapolate and not self._high_extrapolate:
extrapolation = "low"
elif not self._low_extrapolate and self._high_extrapolate:
extrapolation = "high"
elif self._low_extrapolate and self._high_extrapolate:
extrapolation = "both"
# modify the Calculate button to indicate background process
self.cmdCalculate.setText("Calculating...")
self.cmdCalculate.setEnabled(False)
# Send the calculations to separate thread.
d = threads.deferToThread(self.calculateThread, extrapolation)
# Add deferred callback for call return
d.addCallback(self.deferredPlot)
d.addErrback(self.calculationFailed)
[docs] def calculationFailed(self, reason):
print("calculation failed: ", reason)
pass
[docs] def deferredPlot(self, model):
"""
Run the GUI/model update in the main thread
"""
reactor.callFromThread(lambda: self.plotResult(model))
[docs] def plotResult(self, model):
""" Plot result of calculation """
# Set the button back to available
self.cmdCalculate.setEnabled(True)
self.cmdCalculate.setText("Calculate")
self.cmdStatus.setEnabled(True)
self.model = model
self.mapper.toFirst()
self._data = GuiUtils.dataFromItem(self._model_item)
filename = self._data.filename
# Send the modified model item to DE for keeping in the model
# Currently -unused
# self.communicate.updateModelFromPerspectiveSignal.emit(self._model_item)
plot_data = GuiUtils.plotsFromFilename(filename, self._manager.filesWidget.model)
# only the Low-Q/High-Q data for plotting
data_to_plot = [(self._model_item, p) for p in plot_data.values() if p.plot_role == p.ROLE_DATA]
self._manager.filesWidget.plotData(data_to_plot)
# Update the details dialog in case it is open
self.updateDetailsWidget(model)
[docs] def calculateThread(self, extrapolation):
"""
Perform Invariant calculations.
TODO: Create a dictionary of results to be sent to DE on completion.
"""
self.updateFromModel()
msg = ''
qstar_low = 0.0
qstar_low_err = 0.0
qstar_high = 0.0
qstar_high_err = 0.0
qstar_total = 0.0
qstar_total_error = 0.0
temp_data = copy.deepcopy(self._data)
# Prepare the invariant object
inv = invariant.InvariantCalculator(data=temp_data,
background=self._background,
scale=self._scale)
if self._low_extrapolate:
function_low = "power_law"
if self._low_guinier:
function_low = "guinier"
if self._low_fit:
self._low_power_value = None
inv.set_extrapolation(range="low",
npts=int(self._low_points),
function=function_low,
power=self._low_power_value)
if self._high_extrapolate:
function_low = "power_law"
inv.set_extrapolation(range="high",
npts=int(self._high_points),
function=function_low,
power=self._high_power_value)
# Compute invariant
calculation_failed = False
try:
qstar_total, qstar_total_error = inv.get_qstar_with_error()
except Exception as ex:
msg += str(ex)
calculation_failed = True
# Display relevant information
item = QtGui.QStandardItem("ERROR")
self.model.setItem(WIDGETS.W_INVARIANT, item)
item = QtGui.QStandardItem("ERROR")
self.model.setItem(WIDGETS.W_INVARIANT_ERR, item)
try:
volume_fraction, volume_fraction_error = \
inv.get_volume_fraction_with_error(self._contrast)
except Exception as ex:
calculation_failed = True
msg += str(ex)
# Display relevant information
item = QtGui.QStandardItem("ERROR")
self.model.setItem(WIDGETS.W_VOLUME_FRACTION, item)
item = QtGui.QStandardItem("ERROR")
self.model.setItem(WIDGETS.W_VOLUME_FRACTION_ERR, item)
if self._porod:
try:
surface, surface_error = \
inv.get_surface_with_error(self._contrast, self._porod)
except Exception as ex:
calculation_failed = True
msg += str(ex)
# Display relevant information
item = QtGui.QStandardItem("ERROR")
self.model.setItem(WIDGETS.W_SPECIFIC_SURFACE, item)
item = QtGui.QStandardItem("ERROR")
self.model.setItem(WIDGETS.W_SPECIFIC_SURFACE_ERR, item)
else:
surface = None
if (calculation_failed):
logging.warning('Calculation failed: {}'.format(msg))
return self.model
low_calculation_pass = True
high_calculation_pass = True
if self._low_extrapolate:
try:
qstar_low, qstar_low_err = inv.get_qstar_low()
except Exception as ex:
low_calculation_pass = False
msg += str(ex)
logging.warning('Low-q calculation failed: {}'.format(msg))
if self._high_extrapolate:
try:
qstar_high, qstar_high_err = inv.get_qstar_high()
except Exception as ex:
high_calculation_pass = False
msg += str(ex)
logging.warning('High-q calculation failed: {}'.format(msg))
if self._low_extrapolate and low_calculation_pass:
extrapolated_data = inv.get_extra_data_low(self._low_points)
power_low = inv.get_extrapolation_power(range='low')
# Plot the chart
title = "Low-Q extrapolation"
# Convert the data into plottable
extrapolated_data = self._manager.createGuiData(extrapolated_data)
extrapolated_data.name = title
extrapolated_data.title = title
extrapolated_data.symbol = "Line"
extrapolated_data.has_errors = False
# copy labels and units of axes for plotting
extrapolated_data._xaxis = temp_data._xaxis
extrapolated_data._xunit = temp_data._xunit
extrapolated_data._yaxis = temp_data._yaxis
extrapolated_data._yunit = temp_data._yunit
# Add the plot to the model item
# This needs to run in the main thread
reactor.callFromThread(GuiUtils.updateModelItemWithPlot,
self._model_item,
extrapolated_data,
title)
if self._high_extrapolate and high_calculation_pass:
# for presentation in InvariantDetails
qmax_plot = Q_MAXIMUM_PLOT * max(temp_data.x)
if qmax_plot > Q_MAXIMUM:
qmax_plot = Q_MAXIMUM
power_high = inv.get_extrapolation_power(range='high')
high_out_data = inv.get_extra_data_high(q_end=qmax_plot, npts=500)
# Plot the chart
title = "High-Q extrapolation"
# Convert the data into plottable
high_out_data = self._manager.createGuiData(high_out_data)
high_out_data.name = title
high_out_data.title = title
high_out_data.symbol = "Line"
high_out_data.has_errors = False
# copy labels and units of axes for plotting
high_out_data._xaxis = temp_data._xaxis
high_out_data._xunit = temp_data._xunit
high_out_data._yaxis = temp_data._yaxis
high_out_data._yunit = temp_data._yunit
# Add the plot to the model item
# This needs to run in the main thread
reactor.callFromThread(GuiUtils.updateModelItemWithPlot,
self._model_item, high_out_data, title)
reactor.callFromThread(self.updateModelFromThread, WIDGETS.W_VOLUME_FRACTION, volume_fraction)
reactor.callFromThread(self.updateModelFromThread, WIDGETS.W_VOLUME_FRACTION_ERR, volume_fraction_error)
if surface:
reactor.callFromThread(self.updateModelFromThread, WIDGETS.W_SPECIFIC_SURFACE, surface)
reactor.callFromThread(self.updateModelFromThread, WIDGETS.W_SPECIFIC_SURFACE_ERR,
surface_error)
reactor.callFromThread(self.updateModelFromThread, WIDGETS.W_INVARIANT, qstar_total)
reactor.callFromThread(self.updateModelFromThread, WIDGETS.W_INVARIANT_ERR, qstar_total_error)
reactor.callFromThread(self.updateModelFromThread, WIDGETS.D_LOW_QSTAR, qstar_low)
reactor.callFromThread(self.updateModelFromThread, WIDGETS.D_LOW_QSTAR_ERR, qstar_low_err)
reactor.callFromThread(self.updateModelFromThread, WIDGETS.D_HIGH_QSTAR, qstar_high)
reactor.callFromThread(self.updateModelFromThread, WIDGETS.D_HIGH_QSTAR_ERR, qstar_high_err)
return self.model
[docs] def updateModelFromThread(self, widget, value):
"""
Update the model in the main thread
"""
item = QtGui.QStandardItem(str(float('%.3g' % value)))
self.model.setItem(widget, item)
[docs] def title(self):
""" Perspective name """
return "Invariant panel"
[docs] def onStatus(self):
"""
Display Invariant Details panel when clicking on Status button
"""
self.detailsDialog.setModel(self.model)
self.detailsDialog.showDialog()
self.cmdStatus.setEnabled(False)
[docs] def onHelp(self):
""" Display help when clicking on Help button """
treeLocation = "/user/qtgui/Perspectives/Invariant/invariant_help.html"
self.parent.showHelp(treeLocation)
[docs] def setupSlots(self):
""" """
self.cmdCalculate.clicked.connect(self.calculateInvariant)
self.cmdStatus.clicked.connect(self.onStatus)
self.cmdHelp.clicked.connect(self.onHelp)
self.chkLowQ.stateChanged.connect(self.stateChanged)
self.chkLowQ.stateChanged.connect(self.checkQExtrapolatedData)
self.chkHighQ.stateChanged.connect(self.stateChanged)
self.chkHighQ.stateChanged.connect(self.checkQExtrapolatedData)
# slots for the Guinier and PowerLaw radio buttons at low Q
# since they are not auto-exclusive
self.rbGuinier.toggled.connect(self.lowGuinierAndPowerToggle)
self.rbPowerLawLowQ.toggled.connect(self.lowGuinierAndPowerToggle)
self.rbFitHighQ.toggled.connect(self.hiFitAndFixToggle)
self.rbFitLowQ.toggled.connect(self.lowFitAndFixToggle)
self.model.itemChanged.connect(self.modelChanged)
# update model from gui editing by users
self.txtBackgd.textChanged.connect(self.updateFromGui)
self.txtScale.textChanged.connect(self.updateFromGui)
self.txtContrast.textChanged.connect(self.updateFromGui)
self.txtPorodCst.textChanged.connect(self.updateFromGui)
self.txtPowerLowQ.textChanged.connect(self.updateFromGui)
self.txtPowerHighQ.textChanged.connect(self.updateFromGui)
self.txtNptsLowQ.textChanged.connect(self.updateFromGui)
self.txtNptsHighQ.textChanged.connect(self.updateFromGui)
# check values of n_points compared to distribution length
if self.txtNptsLowQ.isEnabled():
self.txtNptsLowQ.textChanged.connect(self.checkLength)
if self.txtNptsHighQ.isEnabled():
self.txtNptsHighQ.textChanged.connect(self.checkLength)
[docs] def stateChanged(self):
"""
Catch modifications from low- and high-Q extrapolation check boxes
"""
sender = self.sender()
itemf = QtGui.QStandardItem(str(sender.isChecked()).lower())
if sender.text() == 'Enable Low-Q extrapolation':
self.model.setItem(WIDGETS.W_ENABLE_LOWQ, itemf)
if sender.text() == 'Enable High-Q extrapolation':
self.model.setItem(WIDGETS.W_ENABLE_HIGHQ, itemf)
[docs] def checkLength(self):
"""
Validators of number of points for extrapolation.
Error if it is larger than the distribution length
"""
try:
int_value = int(self.sender().text())
except ValueError:
self.sender().setStyleSheet(BG_RED)
self.cmdCalculate.setEnabled(False)
return
if self._data:
if len(self._data.x) < int_value:
self.sender().setStyleSheet(BG_RED)
logging.warning('The number of points must be smaller than {}'.format(len(self._data.x)))
self.cmdCalculate.setEnabled(False)
else:
self.sender().setStyleSheet(BG_WHITE)
self.cmdCalculate.setEnabled(True)
else:
# logging.info('no data is loaded')
self.cmdCalculate.setEnabled(False)
[docs] def modelChanged(self, item):
""" Update when model changed """
if item.row() == WIDGETS.W_ENABLE_LOWQ:
toggle = (str(item.text()) == 'true')
self._low_extrapolate = toggle
self.lowQToggle(toggle)
elif item.row() == WIDGETS.W_ENABLE_HIGHQ:
toggle = (str(item.text()) == 'true')
self._high_extrapolate = toggle
self.highQToggle(toggle)
[docs] def updateFromGui(self):
""" Update model when new user inputs """
possible_senders = ['txtBackgd', 'txtContrast', 'txtPorodCst',
'txtScale', 'txtPowerLowQ', 'txtPowerHighQ',
'txtNptsLowQ', 'txtNptsHighQ']
related_widgets = [WIDGETS.W_BACKGROUND, WIDGETS.W_CONTRAST,
WIDGETS.W_POROD_CST, WIDGETS.W_SCALE,
WIDGETS.W_LOWQ_POWER_VALUE, WIDGETS.W_HIGHQ_POWER_VALUE,
WIDGETS.W_NPTS_LOWQ, WIDGETS.W_NPTS_HIGHQ]
related_internal_values = [self._background, self._contrast,
self._porod, self._scale,
self._low_power_value,
self._high_power_value,
self._low_points, self._high_points]
item = QtGui.QStandardItem(self.sender().text())
index_elt = possible_senders.index(self.sender().objectName())
self.model.setItem(related_widgets[index_elt], item)
try:
related_internal_values[index_elt] = float(self.sender().text())
self.sender().setStyleSheet(BG_WHITE)
self.cmdCalculate.setEnabled(True)
except ValueError:
# empty field, just skip
self.sender().setStyleSheet(BG_RED)
self.cmdCalculate.setEnabled(False)
[docs] def lowGuinierAndPowerToggle(self, toggle):
"""
Guinier and Power radio buttons cannot be selected at the same time
If Power is selected, Fit and Fix radio buttons are visible and
Power line edit can be edited if Fix is selected
"""
if self.sender().text() == 'Guinier':
self._low_guinier = toggle
toggle = not toggle
self.rbPowerLawLowQ.setChecked(toggle)
self.rbFitLowQ.toggled.connect(self.lowFitAndFixToggle)
self.rbFitLowQ.setVisible(toggle)
self.rbFixLowQ.setVisible(toggle)
self.txtPowerLowQ.setEnabled(toggle and (not self._low_fit))
else:
self._low_guinier = not toggle
self.rbGuinier.setChecked(not toggle)
self.rbFitLowQ.toggled.connect(self.lowFitAndFixToggle)
self.rbFitLowQ.setVisible(toggle)
self.rbFixLowQ.setVisible(toggle)
self.txtPowerLowQ.setEnabled(toggle and (not self._low_fit))
[docs] def lowFitAndFixToggle(self, toggle):
""" Fit and Fix radiobuttons cannot be selected at the same time """
self._low_fit = toggle
toggle = not toggle
self.txtPowerLowQ.setEnabled(toggle)
[docs] def hiFitAndFixToggle(self, toggle):
"""
Enable editing of power exponent if Fix for high Q is checked
Disable otherwise
"""
self.txtPowerHighQ.setEnabled(not toggle)
[docs] def highQToggle(self, clicked):
""" Disable/enable High Q extrapolation """
self.rbFitHighQ.setEnabled(clicked)
self.rbFixHighQ.setEnabled(clicked)
self.txtNptsHighQ.setEnabled(clicked)
self.txtPowerHighQ.setEnabled(clicked)
[docs] def lowQToggle(self, clicked):
""" Disable / enable Low Q extrapolation """
self.rbGuinier.setEnabled(clicked)
self.rbPowerLawLowQ.setEnabled(clicked)
self.txtNptsLowQ.setEnabled(clicked)
# Enable subelements
self.rbFitLowQ.setVisible(self.rbPowerLawLowQ.isChecked())
self.rbFixLowQ.setVisible(self.rbPowerLawLowQ.isChecked())
self.rbFitLowQ.setEnabled(clicked) # and not self._low_guinier)
self.rbFixLowQ.setEnabled(clicked) # and not self._low_guinier)
self.txtPowerLowQ.setEnabled(clicked
and not self._low_guinier
and not self._low_fit)
[docs] def setupModel(self):
""" """
# filename
item = QtGui.QStandardItem(self._path)
self.model.setItem(WIDGETS.W_FILENAME, item)
# add Q parameters to the model
qmin = 0.0
item = QtGui.QStandardItem(str(qmin))
self.model.setItem(WIDGETS.W_QMIN, item)
qmax = 0.0
item = QtGui.QStandardItem(str(qmax))
self.model.setItem(WIDGETS.W_QMAX, item)
# add custom input params
item = QtGui.QStandardItem(str(self._background))
self.model.setItem(WIDGETS.W_BACKGROUND, item)
item = QtGui.QStandardItem(str(self._contrast))
self.model.setItem(WIDGETS.W_CONTRAST, item)
item = QtGui.QStandardItem(str(self._scale))
self.model.setItem(WIDGETS.W_SCALE, item)
# leave line edit empty if Porod constant not defined
if self._porod != None:
item = QtGui.QStandardItem(str(self._porod))
else:
item = QtGui.QStandardItem(str(''))
self.model.setItem(WIDGETS.W_POROD_CST, item)
# Dialog elements
itemf = QtGui.QStandardItem("false")
self.model.setItem(WIDGETS.W_ENABLE_HIGHQ, itemf)
itemf = QtGui.QStandardItem("false")
self.model.setItem(WIDGETS.W_ENABLE_LOWQ, itemf)
item = QtGui.QStandardItem(str(NPOINTS_Q_INTERP))
self.model.setItem(WIDGETS.W_NPTS_LOWQ, item)
item = QtGui.QStandardItem(str(NPOINTS_Q_INTERP))
self.model.setItem(WIDGETS.W_NPTS_HIGHQ, item)
itemt = QtGui.QStandardItem("true")
self.model.setItem(WIDGETS.W_LOWQ_GUINIER, itemt)
itemt = QtGui.QStandardItem("true")
self.model.setItem(WIDGETS.W_LOWQ_FIT, itemt)
item = QtGui.QStandardItem(str(DEFAULT_POWER_LOW))
self.model.setItem(WIDGETS.W_LOWQ_POWER_VALUE, item)
itemt = QtGui.QStandardItem("true")
self.model.setItem(WIDGETS.W_HIGHQ_FIT, itemt)
item = QtGui.QStandardItem(str(DEFAULT_POWER_LOW))
self.model.setItem(WIDGETS.W_HIGHQ_POWER_VALUE, item)
[docs] def setupMapper(self):
# Set up the mapper.
self.mapper = QtWidgets.QDataWidgetMapper(self)
self.mapper.setOrientation(QtCore.Qt.Vertical)
self.mapper.setModel(self.model)
# Filename
self.mapper.addMapping(self.txtName, WIDGETS.W_FILENAME)
# Qmin/Qmax
self.mapper.addMapping(self.txtTotalQMin, WIDGETS.W_QMIN)
self.mapper.addMapping(self.txtTotalQMax, WIDGETS.W_QMAX)
# Background
self.mapper.addMapping(self.txtBackgd, WIDGETS.W_BACKGROUND)
# Scale
self.mapper.addMapping(self.txtScale, WIDGETS.W_SCALE)
# Contrast
self.mapper.addMapping(self.txtContrast, WIDGETS.W_CONTRAST)
# Porod constant
self.mapper.addMapping(self.txtPorodCst, WIDGETS.W_POROD_CST)
# Lowq/Highq items
self.mapper.addMapping(self.chkLowQ, WIDGETS.W_ENABLE_LOWQ)
self.mapper.addMapping(self.chkHighQ, WIDGETS.W_ENABLE_HIGHQ)
self.mapper.addMapping(self.txtNptsLowQ, WIDGETS.W_NPTS_LOWQ)
self.mapper.addMapping(self.rbGuinier, WIDGETS.W_LOWQ_GUINIER)
self.mapper.addMapping(self.rbFitLowQ, WIDGETS.W_LOWQ_FIT)
self.mapper.addMapping(self.txtPowerLowQ, WIDGETS.W_LOWQ_POWER_VALUE)
self.mapper.addMapping(self.txtNptsHighQ, WIDGETS.W_NPTS_HIGHQ)
self.mapper.addMapping(self.rbFitHighQ, WIDGETS.W_HIGHQ_FIT)
self.mapper.addMapping(self.txtPowerHighQ, WIDGETS.W_HIGHQ_POWER_VALUE)
# Output
self.mapper.addMapping(self.txtVolFract, WIDGETS.W_VOLUME_FRACTION)
self.mapper.addMapping(self.txtVolFractErr, WIDGETS.W_VOLUME_FRACTION_ERR)
self.mapper.addMapping(self.txtSpecSurf, WIDGETS.W_SPECIFIC_SURFACE)
self.mapper.addMapping(self.txtSpecSurfErr, WIDGETS.W_SPECIFIC_SURFACE_ERR)
self.mapper.addMapping(self.txtInvariantTot, WIDGETS.W_INVARIANT)
self.mapper.addMapping(self.txtInvariantTotErr, WIDGETS.W_INVARIANT_ERR)
self.mapper.toFirst()
[docs] def setData(self, data_item=None, is_batch=False):
"""
Obtain a QStandardItem object and dissect it to get Data1D/2D
Pass it over to the calculator
"""
assert data_item is not None
if self.txtName.text() == data_item[0].text():
logging.info('This file is already loaded in Invariant panel.')
return
if not isinstance(data_item, list):
msg = "Incorrect type passed to the Invariant Perspective"
raise AttributeError(msg)
if not isinstance(data_item[0], QtGui.QStandardItem):
msg = "Incorrect type passed to the Invariant Perspective"
raise AttributeError(msg)
# only 1 file can be loaded
self._model_item = data_item[0]
# Extract data on 1st child - this is the Data1D/2D component
data = GuiUtils.dataFromItem(self._model_item)
self.model.item(WIDGETS.W_FILENAME).setData(self._model_item.text())
# update GUI and model with info from loaded data
self.updateGuiFromFile(data=data)
[docs] def updateGuiFromFile(self, data=None):
"""
update display in GUI and plot
"""
self._data = data
# plot loaded file
if not isinstance(self._data, Data1D):
msg = "Invariant cannot be computed with 2D data."
raise ValueError(msg)
try:
filename = data.filename
except:
msg = 'No filename chosen.'
raise ValueError(msg)
try:
qmin = min(self._data.x)
qmax = max(self._data.x)
except:
msg = "Unable to find q min/max of \n data named %s" % \
data.filename
raise ValueError(msg)
# update model with input form files: filename, qmin, qmax
self.model.item(WIDGETS.W_FILENAME).setText(filename)
self.model.item(WIDGETS.W_QMIN).setText(str(qmin))
self.model.item(WIDGETS.W_QMAX).setText(str(qmax))
self._path = filename
# Calculate and add to GUI: volume fraction, invariant total,
# and specific surface if porod checked
self.calculateInvariant()
[docs] def allowBatch(self):
"""
Tell the caller that we don't accept multiple data instances
"""
return False