Commit e71dc3fe authored by Damien Naudet's avatar Damien Naudet

Status is now at entry level.

Displaying status in the FitView.
parent 62cbb74b
...@@ -368,10 +368,6 @@ class FitWidget(Qt.QWidget): ...@@ -368,10 +368,6 @@ class FitWidget(Qt.QWidget):
zresult = results.results(process, param, zresult = results.results(process, param,
results.QZ_AXIS) results.QZ_AXIS)
xstatus = results.qx_status(process)
ystatus = results.qy_status(process)
zstatus = results.qz_status(process)
fitH5.set_qx_result(entry, fitH5.set_qx_result(entry,
process, process,
param, param,
...@@ -387,15 +383,19 @@ class FitWidget(Qt.QWidget): ...@@ -387,15 +383,19 @@ class FitWidget(Qt.QWidget):
param, param,
zresult) zresult)
fitH5.set_status(entry, xstatus = results.qx_status()
process, ystatus = results.qy_status()
FitH5QAxis.qx_axis, zstatus = results.qz_status()
xstatus)
fitH5.set_status(entry, fitH5.set_status(entry,
process, FitH5QAxis.qx_axis,
FitH5QAxis.qy_axis, xstatus)
ystatus) fitH5.set_status(entry,
fitH5.set_status(entry, FitH5QAxis.qy_axis,
process, ystatus)
FitH5QAxis.qz_axis, fitH5.set_status(entry,
zstatus) FitH5QAxis.qz_axis,
zstatus)
if __name__ == '__main__':
pass
\ No newline at end of file
...@@ -86,7 +86,7 @@ class FitView(Qt.QMainWindow): ...@@ -86,7 +86,7 @@ class FitView(Qt.QMainWindow):
tree = self.__tree = TreeView() tree = self.__tree = TreeView()
tree.setModel(self.__model) tree.setModel(self.__model)
tree.setRootIndex(self.__model.index(0, 0, tree.rootIndex())) # tree.setRootIndex(self.__model.index(0, 0, tree.rootIndex()))
tree.setSelectionBehavior(Qt.QAbstractItemView.SelectItems) tree.setSelectionBehavior(Qt.QAbstractItemView.SelectItems)
tree.header().setStretchLastSection(False) tree.header().setStretchLastSection(False)
tree.setShowUniqueGroup(True) tree.setShowUniqueGroup(True)
...@@ -193,6 +193,10 @@ class FitView(Qt.QMainWindow): ...@@ -193,6 +193,10 @@ class FitView(Qt.QMainWindow):
""" """
self.__initPlots() self.__initPlots()
self.__startModel() self.__startModel()
tree = self.__tree
root = self.__model.index(0, 0, tree.rootIndex())
tree.setRootIndex(self.__model.index(0, 0, root))
tree.expandAll()
def __startModel(self): def __startModel(self):
""" """
...@@ -270,11 +274,11 @@ class FitView(Qt.QMainWindow): ...@@ -270,11 +274,11 @@ class FitView(Qt.QMainWindow):
# TODO : refactor # TODO : refactor
process = self.__process process = self.__process
if process == 'gaussian': if process == 'gaussian':
_plotLeastSq(self.__fitPlots, xIdx, _plotGaussian(self.__fitPlots, xIdx,
fitH5, fitH5,
entry, process, entry, process,
xAcqQX, xAcqQY, xAcqQZ, xAcqQX, xAcqQY, xAcqQZ,
yAcqQX, yAcqQY, yAcqQZ) yAcqQX, yAcqQY, yAcqQZ)
elif process == 'centroid': elif process == 'centroid':
_plotCentroid(self.__fitPlots, xIdx, _plotCentroid(self.__fitPlots, xIdx,
...@@ -289,12 +293,12 @@ class FitView(Qt.QMainWindow): ...@@ -289,12 +293,12 @@ class FitView(Qt.QMainWindow):
# TODO : allow users to register plot functions associated with the kind # TODO : allow users to register plot functions associated with the kind
# of process results that are being displayed # of process results that are being displayed
def _plotLeastSq(plots, index, fitH5, def _plotGaussian(plots, index, fitH5,
entry, process, entry, process,
xAcqQX, xAcqQY, xAcqQZ, xAcqQX, xAcqQY, xAcqQZ,
yAcqQX, yAcqQY, yAcqQZ): yAcqQX, yAcqQY, yAcqQZ):
""" """
Plots the "leastsq" fit results Plots the "Gaussian" fit results
:param plots: plot widgets :param plots: plot widgets
:param index: index of the selected point (in the results array) :param index: index of the selected point (in the results array)
:param fitH5: instance of FitH5. This instance may be already opened by :param fitH5: instance of FitH5. This instance may be already opened by
...@@ -360,17 +364,17 @@ def _plotCentroid(plots, index, fitH5, ...@@ -360,17 +364,17 @@ def _plotCentroid(plots, index, fitH5,
yAcqQX, yAcqQY, yAcqQZ): yAcqQX, yAcqQY, yAcqQZ):
""" """
Plot the results from a "centroid" fit. Plot the results from a "centroid" fit.
:param plots: :param plots: the plot widgets
:param index: :param index: index of the sample point
:param fitH5: :param fitH5: fitH5 file
:param entry: :param entry: name of the entry in the fitH5
:param process: :param process: name of the process in the fitH5
:param xAcqQX: :param xAcqQX: measured Qx data, x axis
:param xAcqQY: :param xAcqQY: measured Qy data, x axis
:param xAcqQZ: :param xAcqQZ: measured Qz data, x axis
:param yAcqQX: :param yAcqQX: measured Qx data, y axis
:param yAcqQY: :param yAcqQY:measured Qy data, y axis
:param yAcqQZ: :param yAcqQZ:measured Qz data, y axis
:return: :return:
""" """
...@@ -395,10 +399,10 @@ def _plotCentroid(plots, index, fitH5, ...@@ -395,10 +399,10 @@ def _plotCentroid(plots, index, fitH5,
def _initLeastSq(plots, fitH5Name, entry, process): def _initLeastSq(plots, fitH5Name, entry, process):
""" """
Sets up the plots when the interface is shown for the first time. Sets up the plots when the interface is shown for the first time.
:param plots: :param plots: the plot widgets
:param fitH5Name: :param fitH5Name: fitH5 file name
:param entry: :param entry: name of the entry in the fitH5
:param process: :param process: name of the process in the fitH5
:return: :return:
""" """
# hard coded result name, this isn't satisfactory but I can't think # hard coded result name, this isn't satisfactory but I can't think
...@@ -424,10 +428,10 @@ def _initLeastSq(plots, fitH5Name, entry, process): ...@@ -424,10 +428,10 @@ def _initLeastSq(plots, fitH5Name, entry, process):
def _initCentroid(plots, fitH5Name, entry, process): def _initCentroid(plots, fitH5Name, entry, process):
""" """
Sets up the plots when the interface is shown for the first time. Sets up the plots when the interface is shown for the first time.
:param plots: :param plots: the plot widgets
:param fitH5Name: :param fitH5Name: fitH5 file name
:param entry: :param entry: name of the entry in the fitH5
:param process: :param process: name of the process in the fitH5
:return: :return:
""" """
# hard coded result name, this isn't satisfactory but I can't think # hard coded result name, this isn't satisfactory but I can't think
......
...@@ -134,7 +134,8 @@ class ROIPlotIntensityMap(PlotIntensityMap): ...@@ -134,7 +134,8 @@ class ROIPlotIntensityMap(PlotIntensityMap):
qapp = Qt.QApplication.instance() qapp = Qt.QApplication.instance()
with self.__qspaceH5 as qspaceH5: with self.__qspaceH5 as qspaceH5:
intensities = np.zeros((qspaceH5.qspace_sum.size,), dtype=np.float64) intensities = np.zeros((qspaceH5.qspace_sum.size,),
dtype=np.float64)
progress.setRange(0, qspaceH5.qspace_sum.size - 1) progress.setRange(0, qspaceH5.qspace_sum.size - 1)
zslice, yslice, xslice = self.__roiSlices zslice, yslice, xslice = self.__roiSlices
......
...@@ -29,9 +29,12 @@ __authors__ = ["D. Naudet"] ...@@ -29,9 +29,12 @@ __authors__ = ["D. Naudet"]
__license__ = "MIT" __license__ = "MIT"
__date__ = "15/09/2016" __date__ = "15/09/2016"
import numpy as np
from silx.gui import qt as Qt from silx.gui import qt as Qt
from kmap.io.FitH5 import FitH5, FitH5QAxis from kmap.io.FitH5 import FitH5, FitH5QAxis
from ....process.fitresults import FitStatus
from ...widgets.XsocsPlot2D import XsocsPlot2D from ...widgets.XsocsPlot2D import XsocsPlot2D
...@@ -58,10 +61,16 @@ class DropPlotWidget(XsocsPlot2D): ...@@ -58,10 +61,16 @@ class DropPlotWidget(XsocsPlot2D):
stream = Qt.QDataStream(qByteArray, Qt.QIODevice.ReadOnly) stream = Qt.QDataStream(qByteArray, Qt.QIODevice.ReadOnly)
h5File = stream.readString() h5File = stream.readString()
entry = stream.readString() entry = stream.readString()
process = stream.readString()
result = stream.readString()
q_axis = stream.readInt() q_axis = stream.readInt()
self.plotFitResult(h5File, entry, process, result, q_axis)
type = stream.readString()
if type == 'result':
process = stream.readString()
result = stream.readString()
self.plotFitResult(h5File, entry, process, result, q_axis)
elif type == 'status':
self.plotFitStatus(h5File, entry, q_axis)
def dragEnterEvent(self, event): def dragEnterEvent(self, event):
# super(DropWidget, self).dragEnterEvent(event) # super(DropWidget, self).dragEnterEvent(event)
...@@ -81,7 +90,20 @@ class DropPlotWidget(XsocsPlot2D): ...@@ -81,7 +90,20 @@ class DropPlotWidget(XsocsPlot2D):
scan_y = h5f.scan_y(entry) scan_y = h5f.scan_y(entry)
self.__legend = self.setPlotData(scan_x, scan_y, data) self.__legend = self.setPlotData(scan_x, scan_y, data)
self.setGraphTitle(result + '/' + FitH5QAxis.axis_names[q_axis]) self.setGraphTitle(result + '[' + FitH5QAxis.axis_names[q_axis] + ']')
def plotFitStatus(self, fitH5Name, entry, q_axis):
with FitH5(fitH5Name) as h5f:
data = h5f.get_status(entry, q_axis)
errorPts = np.where(data != FitStatus.OK)[0]
if len(errorPts) == 0:
return
scan_x = h5f.scan_x(entry)[errorPts]
scan_y = h5f.scan_y(entry)[errorPts]
data = data[errorPts]
self.__legend = self.setPlotData(scan_x, scan_y, data)
self.setGraphTitle('Errors[qx]' + FitH5QAxis.axis_names[q_axis])
if __name__ == '__main__': if __name__ == '__main__':
......
...@@ -29,15 +29,16 @@ __authors__ = ["D. Naudet"] ...@@ -29,15 +29,16 @@ __authors__ = ["D. Naudet"]
__license__ = "MIT" __license__ = "MIT"
__date__ = "01/01/2017" __date__ = "01/01/2017"
import numpy as np
from silx.gui import qt as Qt from silx.gui import qt as Qt
import numpy as np
from kmap.gui.model.Model import Model, RootNode from kmap.gui.model.Model import Model, RootNode
from kmap.gui.project.Hdf5Nodes import H5File from kmap.gui.project.Hdf5Nodes import H5File
from kmap.gui.model.ModelDef import ModelRoles from kmap.gui.model.ModelDef import ModelRoles
from kmap.io.FitH5 import FitH5, FitH5QAxis from kmap.io.FitH5 import FitH5, FitH5QAxis
from ....process.peak_fit import FitStatus
from ...widgets.XsocsPlot2D import XsocsPlot2D from ...widgets.XsocsPlot2D import XsocsPlot2D
from ...project.Hdf5Nodes import H5Base, H5NodeClassDef from ...project.Hdf5Nodes import H5Base, H5NodeClassDef
...@@ -103,8 +104,31 @@ class FitEntryNode(H5Base): ...@@ -103,8 +104,31 @@ class FitEntryNode(H5Base):
child = FitProcessNode(self.h5File, base + '/' + process) child = FitProcessNode(self.h5File, base + '/' + process)
children.append(child) children.append(child)
statusNode = FitStatusNode(self.h5File, base)
children.append(statusNode)
return children return children
def mimeData(self, column, stream):
# TODO : put column value in enum
if column == 1:
q_axis = FitH5QAxis.qx_axis
elif column == 2:
q_axis = FitH5QAxis.qy_axis
elif column == 3:
q_axis = FitH5QAxis.qz_axis
else:
raise ValueError('Unexpected column.')
h5file = self.h5File
entry = self.entry
stream.writeString(h5file)
stream.writeString(entry)
stream.writeInt(q_axis)
return True
class FitProcessNode(FitEntryNode): class FitProcessNode(FitEntryNode):
""" """
...@@ -117,16 +141,104 @@ class FitProcessNode(FitEntryNode): ...@@ -117,16 +141,104 @@ class FitProcessNode(FitEntryNode):
entry = self.entry entry = self.entry
process = self.process process = self.process
children = [] children = []
print 'PATH', self.h5Path, self.entry, self.process
with FitH5(self.h5File, mode='r') as h5f: with FitH5(self.h5File, mode='r') as h5f:
results = h5f.get_result_names(entry, process) results = h5f.get_result_names(entry, process)
for result in results: for result in results:
child = FitResultNode(self.h5File, base + '/' + result) child = FitResultNode(self.h5File,
base + '/' + result)
children.append(child) children.append(child)
return children return children
class FitStatusNode(FitEntryNode):
"""
Preview of the points where the fit has failed.
"""
def __init__(self, *args, **kwargs):
self.dragEnabledColumns = [False, True, True, True]
super(FitStatusNode, self).__init__(*args, **kwargs)
self.nodeName = 'Status'
self.__nErrors = [0, 0, 0]
def _setupNode(self):
width = 100
plot = PlotGrabber()
plot.setFixedSize(Qt.QSize(width, 100))
plot.toPixmap()
qApp = Qt.qApp
qApp.processEvents()
with FitH5(self.h5File) as fitH5:
x = fitH5.scan_x(self.entry)
y = fitH5.scan_y(self.entry)
status = fitH5.get_qx_status(self.entry)
errorPts = np.where(status != FitStatus.OK)[0]
self.__nErrors[0] = len(errorPts)
if len(errorPts) != 0:
plot.setPlotData(x[errorPts], y[errorPts], status[errorPts])
pixmap = plot.toPixmap()
else:
label = Qt.QLabel('No errors')
label.setFixedWidth(width)
label.setAlignment(Qt.Qt.AlignCenter)
label.setAttribute(Qt.Qt.WA_TranslucentBackground)
pixmap = Qt.QPixmap.grabWidget(label)
self.setData(1, pixmap, Qt.Qt.DecorationRole)
qApp.processEvents()
status = fitH5.get_qy_status(self.entry)
errorPts = np.where(status != FitStatus.OK)[0]
self.__nErrors[1] = len(errorPts)
if len(errorPts) != 0:
plot.setPlotData(x[errorPts], y[errorPts], status[errorPts])
pixmap = plot.toPixmap()
else:
label = Qt.QLabel('No errors')
label.setFixedWidth(width)
label.setAlignment(Qt.Qt.AlignCenter)
label.setAttribute(Qt.Qt.WA_TranslucentBackground)
pixmap = Qt.QPixmap.grabWidget(label)
self.setData(2, pixmap, Qt.Qt.DecorationRole)
qApp.processEvents()
status = fitH5.get_qz_status(self.entry)
errorPts = np.where(status != FitStatus.OK)[0]
self.__nErrors[2] = len(errorPts)
if len(errorPts) != 0:
plot.setPlotData(x[errorPts], y[errorPts], status[errorPts])
pixmap = plot.toPixmap()
else:
label = Qt.QLabel('No errors')
label.setFixedWidth(width)
label.setAlignment(Qt.Qt.AlignCenter)
label.setAttribute(Qt.Qt.WA_TranslucentBackground)
pixmap = Qt.QPixmap.grabWidget(label)
self.setData(3, pixmap, Qt.Qt.DecorationRole)
qApp.processEvents()
def _loadChildren(self):
return []
def mimeData(self, column, stream):
if column < 1 or column > 3:
return False
if self.__nErrors[column - 1] == 0:
return False
if not FitEntryNode.mimeData(self, column, stream):
return False
stream.writeString('status')
return True
class FitResultNode(FitProcessNode): class FitResultNode(FitProcessNode):
""" """
Node linked to a result group in a FitH5 file. Node linked to a result group in a FitH5 file.
...@@ -176,6 +288,18 @@ class FitResultNode(FitProcessNode): ...@@ -176,6 +288,18 @@ class FitResultNode(FitProcessNode):
def _loadChildren(self): def _loadChildren(self):
return [] return []
def mimeData(self, column, stream):
if not FitProcessNode.mimeData(self, column, stream):
return False
process = self.process
result = self.result
stream.writeString('result')
stream.writeString(process)
stream.writeString(result)
return True
class FitRootNode(RootNode): class FitRootNode(RootNode):
""" """
...@@ -195,36 +319,19 @@ class FitModel(Model): ...@@ -195,36 +319,19 @@ class FitModel(Model):
if len(indexes) > 1: if len(indexes) > 1:
raise ValueError('Drag&Drop of more than one item is not' raise ValueError('Drag&Drop of more than one item is not'
'supported yet.') 'supported yet.')
mimeData = Qt.QMimeData()
index = indexes[0] index = indexes[0]
node = index.data(ModelRoles.InternalDataRole) node = index.data(ModelRoles.InternalDataRole)
if not isinstance(node, FitResultNode): if not isinstance(node, (FitResultNode, FitStatusNode)):
return super(Model, self).mimeData(indexes) return super(Model, self).mimeData(indexes)
if index.column() == 1:
q_axis = FitH5QAxis.qx_axis
elif index.column() == 2:
q_axis = FitH5QAxis.qy_axis
elif index.column() == 3:
q_axis = FitH5QAxis.qz_axis
else:
raise ValueError('Unexpected column.')
h5file = node.h5File
entry = node.entry
process = node.process
result = node.result
data = Qt.QByteArray() data = Qt.QByteArray()
stream = Qt.QDataStream(data, Qt.QIODevice.WriteOnly) stream = Qt.QDataStream(data, Qt.QIODevice.WriteOnly)
stream.writeString(h5file) if node.mimeData(index.column(), stream):
stream.writeString(entry) mimeData.setData('application/FitModel', data)
stream.writeString(process)
stream.writeString(result)
stream.writeInt(q_axis)
mimeData = Qt.QMimeData()
mimeData.setData('application/FitModel', data)
return mimeData return mimeData
......
...@@ -59,16 +59,13 @@ class FitH5(XsocsH5Base): ...@@ -59,16 +59,13 @@ class FitH5(XsocsH5Base):
for that entry) for that entry)
- all arrays are 1D. - all arrays are 1D.
""" """
# _axis_values = range(3)
# qx_axis, qy_axis, qz_axis = _axis_values
# axis_names = ('qx', 'qy', 'qz')
title_path = '{entry}/title' title_path = '{entry}/title'
start_time_path = '{entry}/start_time' start_time_path = '{entry}/start_time'
end_time_path = '{entry}/end_time' end_time_path = '{entry}/end_time'
date_path = '{entry}/{process}/date' date_path = '{entry}/{process}/date'
qspace_axis_path = '{entry}/qspace_axis/{axis}' qspace_axis_path = '{entry}/qspace_axis/{axis}'
status_path = '{entry}/{process}/status/{axis}' status_path = '{entry}/status/{axis}'
configuration_path = '{entry}/{process}/configuration' configuration_path = '{entry}/{process}/configuration'
result_grp_path = '{entry}/{process}/results' result_grp_path = '{entry}/{process}/results'
result_path = '{entry}/{process}/results/{result}/{axis}' result_path = '{entry}/{process}/results/{result}/{axis}'
...@@ -126,7 +123,34 @@ class FitH5(XsocsH5Base): ...@@ -126,7 +123,34 @@ class FitH5(XsocsH5Base):
with self._get_file() as h5_file: with self._get_file() as h5_file:
return sorted(h5_file[results_path].keys()) return sorted(h5_file[results_path].keys())
def get_status(self, entry, process, axis): def get_qx_status(self, entry):
"""
Returns the Qx fit status for the given entry/process.
:param entry:
:param process:
:return:
"""
return self.get_status(entry, FitH5QAxis.qx_axis)
def get_qy_status(self, entry):
"""
Returns the Qy fit status for the given entry/process.
:param entry:
:param process:
:return:
"""
return self.get_status(entry, FitH5QAxis.qy_axis)
def get_qz_status(self, entry):
"""
Returns the Qz fit status for the given entry/process.
:param entry:
:param process:
:return:
"""
return self.get_status(entry, FitH5QAxis.qz_axis)
def get_status(self, entry, axis):
""" """
Returns the fit status for the given entry/process/axis Returns the fit status for the given entry/process/axis
:param entry: :param entry:
...@@ -137,9 +161,25 @@ class FitH5(XsocsH5Base): ...@@ -137,9 +161,25 @@ class FitH5(XsocsH5Base):
""" """
axis_name = FitH5QAxis.axis_name(axis) axis_name = FitH5QAxis.axis_name(axis)
status_path = FitH5.status_path.format(entry=entry, status_path = FitH5.status_path.format(entry=entry,
process=process,
axis=axis_name) axis=axis_name)
return self._get_array_data(status_path) status = self._get_array_data(status_path)
# 30 Jan 2017.
# This is kept for compatibility with previous versions
# TODO : remove this sometime...
if status is None:
# only one process was supported at the time
processes = self.processes(entry)
if len(processes) == 0:
return None
process = processes[0]
status_path = '{entry}/{process}/status/{axis}'
status = self._get_array_data(status_path.format(entry=entry,
process=process,
axis=axis_name))
return status
def scan_x(self, entry): def scan_x(self, entry):
""" """
...@@ -317,7 +357,6 @@ class FitH5(XsocsH5Base): ...@@ -317,7 +357,6 @@ class FitH5(XsocsH5Base):
results[:, col_idx] = result results[:, col_idx] = result