Skip to content

Commit

Permalink
Merge pull request #2145 from SasView/2107-idf-plot-selection
Browse files Browse the repository at this point in the history
All bugs fixed, doable changes made
  • Loading branch information
lucas-wilkins authored Aug 19, 2022
2 parents 940bbd0 + 2108910 commit 8514803
Show file tree
Hide file tree
Showing 18 changed files with 1,182 additions and 986 deletions.
3 changes: 2 additions & 1 deletion installers/sasview.spec
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ PYTHON_LOC = sys.exec_prefix
datas = [
('../src/sas/sasview/images', 'images'),
('../src/sas/sasview/media', 'media'),
('../src/sas/example_data', 'example_data'),
('../src/sas/example_data', 'example_data'),
('../src/sas/qtgui/Utilities/Reports/report_style.css', 'sas/qtgui/Utilities/Reports'),
('../src/sas/sasview/custom_config.py', '.'),
('../src/sas/sasview/local_config.py', '.'),
# ('../src/sas/sasview/wxcruft.py', '.'),
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ def run(self):
'Plotting/UI/*',
'Utilities/UI/*',
'Utilities/Reports/UI/*',
'Utilities/Reports/*.css'
'Utilities/Reports/*.css',
'UI/*',
'UI/res/*',
]
Expand Down
59 changes: 59 additions & 0 deletions src/sas/qtgui/Perspectives/Corfunc/CorfuncCanvas.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
from __future__ import annotations

from abc import ABCMeta, abstractmethod

from typing import Optional, Union, List, Iterable, TYPE_CHECKING

from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure

from sas.sascalc.dataloader.data_info import Data1D


if TYPE_CHECKING:
from sas.qtgui.Perspectives.Corfunc.CorfuncPerspective import CorfuncWindow


class CorfuncCanvasMeta(type(FigureCanvas), ABCMeta):
""" Metaclass for both ABC and matplotlib figure
This is needed to enable the mixin of CorfuncCanvas
"""


class CorfuncCanvas(FigureCanvas, metaclass=CorfuncCanvasMeta):
""" Base class for the canvases in corfunc"""

def __init__(self, parent: CorfuncWindow, width=5, height=4, dpi=100):
self.parent = parent
self.fig = Figure(figsize=(width, height), dpi=dpi)
self.axes = self.fig.add_subplot(111)

FigureCanvas.__init__(self, self.fig)

self._data: Optional[List[Data1D]] = None

def clear(self):
""" Remove data from plots"""
self._data = None

@abstractmethod
def draw_data(self):
pass

@property
def data(self) -> Optional[List[Data1D]]:
""" The data currently shown by the plots """
return self._data

@data.setter
def data(self, target_data: Optional[Union[Data1D, Iterable[Data1D]]]):
# I'm not 100% sure this is good practice, but it will make things cleaner in the short term
if target_data is None:
self._data = None
elif isinstance(target_data, Data1D):
self._data = [target_data]
else:
self._data = list(target_data)

self.draw_data()
102 changes: 50 additions & 52 deletions src/sas/qtgui/Perspectives/Corfunc/CorfuncPerspective.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from numpy.linalg.linalg import LinAlgError


from typing import Optional, List
from typing import Optional, List, Tuple

import logging

Expand All @@ -22,18 +22,20 @@
from sas.qtgui.Perspectives.Corfunc.CorfunSlider import CorfuncSlider
from sas.qtgui.Perspectives.Corfunc.QSpaceCanvas import QSpaceCanvas
from sas.qtgui.Perspectives.Corfunc.RealSpaceCanvas import RealSpaceCanvas
from sas.qtgui.Perspectives.Corfunc.IDFCanvas import IDFCanvas
from sas.sascalc.corfunc.extrapolation_data import ExtrapolationParameters, ExtrapolationInteractionState

import sas.qtgui.Utilities.GuiUtils as GuiUtils
from sas.qtgui.Utilities.Reports.reportdata import ReportData
from sas.qtgui.Utilities.Reports import ReportBase
from sas.qtgui.Plotting.PlotterData import Data1D

from sas.sascalc.corfunc.corfunc_calculator import CorfuncCalculator
# pylint: enable=import-error, no-name-in-module

# local
from .UI.CorfuncPanel import Ui_CorfuncDialog
from .util import WIDGETS, safe_float
from .util import WIDGETS, safe_float, TransformedData
from .SaveExtrapolatedPopup import SaveExtrapolatedPopup
from ..perspective import Perspective

Expand All @@ -49,7 +51,7 @@ def title(self):
""" Window title """
return "Corfunc Perspective"

trigger = QtCore.pyqtSignal(tuple)
trigger = QtCore.pyqtSignal(TransformedData)

# pylint: disable=unused-argument
def __init__(self, parent=None):
Expand All @@ -67,7 +69,8 @@ def __init__(self, parent=None):
self._calculator = CorfuncCalculator()
self._allow_close = False
self._model_item: Optional[QStandardItem] = None
self.data = None
self.data: Optional[Data1D] = None
self.extrap: Optional[Data1D] = None
self.has_data = False
self.txtLowerQMin.setText("0.0")
self.txtLowerQMin.setEnabled(False)
Expand All @@ -86,10 +89,15 @@ def __init__(self, parent=None):
self.realSpaceLayout.insertWidget(0, self._real_space_plot)
self.realSpaceLayout.insertWidget(1, NavigationToolbar2QT(self._real_space_plot, self))

self._idf_plot = IDFCanvas(self)
self.idfLayout.insertWidget(0, self._idf_plot)
self.idfLayout.insertWidget(1, NavigationToolbar2QT(self._idf_plot, self))

# Things to make the corfunc panel behave
self.mainLayout.setStretch(0, 0)
self.mainLayout.setStretch(1, 1)
self.controlFrame.setFixedWidth(583)
self.horizontalLayout_3.setStretch(0, 0)
self.horizontalLayout_3.setStretch(1, 1)
self.scrollArea.setFixedWidth(600)
self.adjustSize()

# Connect buttons to slots.
# Needs to be done early so default values propagate properly.
Expand Down Expand Up @@ -193,10 +201,10 @@ def removeData(self, data_list=None):
# Clear data plots
self._q_space_plot.data = None
self._q_space_plot.extrap = None
self._q_space_plot.draw_data()

self._real_space_plot.data = None
self._real_space_plot.extrap = None
self._real_space_plot.draw_data()
self._idf_plot.data = None

self.slider.setEnabled(False)
# Clear calculator, model, and data path
self._calculator = CorfuncCalculator()
Expand Down Expand Up @@ -249,6 +257,7 @@ def extrapolate(self):
self.model.setItem(WIDGETS.W_PORODSIGMA,
QtGui.QStandardItem("{:.4g}".format(params['sigma'])))

self.extrap = extrapolation
self._q_space_plot.extrap = extrapolation
self.model_changed(None)
self.cmdTransform.setEnabled(True)
Expand All @@ -263,16 +272,16 @@ def transform(self):

method = "fourier"

extrap = self._q_space_plot.extrap
extrap = self.extrap
background = float(self.model.item(WIDGETS.W_BACKGROUND).text())

def updatefn(msg):
"""Report progress of transformation."""
self.communicate.statusBarUpdateSignal.emit(msg)

def completefn(transforms):
def completefn(transformed_data: Tuple[Data1D, Data1D, Data1D]):
"""Extract the values from the transforms and plot"""
self.trigger.emit(transforms)
self.trigger.emit(TransformedData(*transformed_data)) # TODO: Make this return more structured data earlier

self._update_calculator()
self._calculator.compute_transform(extrap, method, background,
Expand All @@ -281,20 +290,27 @@ def completefn(transforms):



def finish_transform(self, transforms):
self._real_space_plot.data = transforms
def finish_transform(self, data: TransformedData):

self.transformed_data = data

self._real_space_plot.data = data.gamma_1, data.gamma_3

self.update_real_space_plot(transforms)
# self.update_real_space_plot(transforms)

self._idf_plot.data = data.idf

self._real_space_plot.draw_data()
self.cmdExtract.setEnabled(True)
self.cmdSave.setEnabled(True)

self.tabWidget.setCurrentIndex(1)

def extract(self):
transforms = self._real_space_plot.data

params = self._calculator.extract_parameters(transforms[0])
if self.transformed_data is None:
return

params = self._calculator.extract_parameters(self.transformed_data[0])

if params is not None:

Expand All @@ -313,29 +329,6 @@ def extract(self):
self.model_changed(None)


def update_real_space_plot(self, datas):
"""take the datas tuple and create a plot in DE"""

assert isinstance(datas, tuple)
plot_id = id(self)
titles = [f"1D Correlation [{self._path}]", f"3D Correlation [{self._path}]",
'Interface Distribution Function']
for i, plot in enumerate(datas):
plot_to_add = self.parent.createGuiData(plot)
# set plot properties
title = plot_to_add.title
plot_to_add.scale = 'linear'
plot_to_add.symbol = 'Line'
plot_to_add._xaxis = "x"
plot_to_add._xunit = "A"
plot_to_add._yaxis = "\Gamma"
if i < len(titles):
title = titles[i]
plot_to_add.name = titles[i]
GuiUtils.updateModelItemWithPlot(self._model_item, plot_to_add, title)
#self.axes.set_xlim(min(data1.x), max(data1.x) / 4)
pass

def setup_mapper(self):
"""Creating mapping between model and gui elements."""
self.mapper = QtWidgets.QDataWidgetMapper(self)
Expand Down Expand Up @@ -480,11 +473,15 @@ def fractional_position(f):
self.cmdTransform.setEnabled(False)
self._path = data.name
self.model.setItem(WIDGETS.W_FILENAME, QtGui.QStandardItem(self._path))

self._real_space_plot.data = None
self._real_space_plot.draw_data()
self._idf_plot.data = None

self.set_text_enable(True)
self.has_data = True

self.tabWidget.setCurrentIndex(0)


def setClosable(self, value=True):
"""
Expand Down Expand Up @@ -609,12 +606,15 @@ def on_save(self):
if "." not in f_name:
f_name += ".csv"

data1, data3, data_idf = self._real_space_plot.data

with open(f_name, "w") as outfile:
outfile.write("X,1D,3D,IDF\n")
np.savetxt(outfile,
np.vstack([(data1.x, data1.y, data3.y, data_idf.y)]).T,
np.vstack([(
self.transformed_data.gamma_1.x,
self.transformed_data.gamma_1.y,
self.transformed_data.gamma_3.y,
self.transformed_data.idf.y)]).T,
delimiter=",")
# pylint: enable=invalid-name

Expand Down Expand Up @@ -765,13 +765,6 @@ def updateFromParameters(self, params):
if params.get('long_period', '0') != '0':
self.transform()

def get_figures(self):
"""
Get plots for the report
"""

return [self._real_space_plot.fig]

@property
def real_space_figure(self):
return self._real_space_plot.fig
Expand All @@ -780,6 +773,10 @@ def real_space_figure(self):
def q_space_figure(self):
return self._q_space_plot.fig

@property
def idf_figure(self):
return self._idf_plot.fig

@property
def supports_reports(self) -> bool:
return True
Expand All @@ -805,5 +802,6 @@ def getReport(self) -> Optional[ReportData]:
report.add_table_dict(fancy_parameters, ("Parameter", "Value"))
report.add_plot(self.q_space_figure)
report.add_plot(self.real_space_figure)
report.add_plot(self.idf_figure)

return report.report_data
32 changes: 32 additions & 0 deletions src/sas/qtgui/Perspectives/Corfunc/IDFCanvas.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@

from sas.qtgui.Perspectives.Corfunc.CorfuncCanvas import CorfuncCanvas


class IDFCanvas(CorfuncCanvas):
""" Canvas for displaying real space representation"""

def draw_data(self):
"""
This function draws the real space data onto the plot
The 1d correlation function in self.data, the 3d correlation function
in self.data3, and the interface distribution function in self.data_idf
are all draw in on the plot in linear cooredinates."""


self.fig.clf()

self.axes = self.fig.add_subplot(111)
self.axes.set_xscale("linear")
self.axes.set_yscale("linear")
self.axes.set_xlabel("Z [$\AA$]")
self.axes.set_ylabel("IDF")
self.axes.set_title("Interface Distribution Function")
self.fig.tight_layout()

if self.data is not None and len(self.data) > 0:
self.axes.plot(self.data[0].x, self.data[0].y)
self.axes.set_xlim(0, max(self.data[0].x) / 4)

self.draw()

Loading

0 comments on commit 8514803

Please sign in to comment.