Commit ca36b45b authored by Damien Naudet's avatar Damien Naudet

WIP

parent 5e45ab12
......@@ -36,13 +36,16 @@ from .Widgets import (AcqParamsWidget,
# processWidgetFromViewEvent,
# processDoneEvent)
from .process.RecipSpaceWidget import RecipSpaceWidget
from .process.FitWidget import FitWidget
from .view.IntensityView import IntensityView
from .view.QspaceView import QSpaceView
from .view.FitView import FitView
from .project.XsocsProject import XsocsProject
from .model.TreeView import TreeView
from .model.Model import Model
from .project.IntensityGroup import IntensityGroup
from .project.QSpaceGroup import QSpaceItem
from .project.FitGroup import FitItem
from .project.XsocsH5Factory import XsocsH5Factory, h5NodeToProjectItem
from .project.Hdf5Nodes import setH5NodeFactory, H5File
from .Utils import nextFileName
......@@ -73,6 +76,7 @@ class XsocsGui(Qt.QMainWindow):
self.__intensityView = None
self.__qspaceViews = {}
self.__fitViews = {}
mdiArea = Qt.QMdiArea()
self.setCentralWidget(mdiArea)
......@@ -108,6 +112,9 @@ class XsocsGui(Qt.QMainWindow):
elif isinstance(projectItem, QSpaceItem)\
and event['event'] == 'qspace':
self.__showQSpace(node)
elif isinstance(projectItem, FitItem)\
and event['event'] == 'fit':
self.__showFit(node)
else:
ValueError('Unknwown event for item {0} : {1}.'
''.format(projectItem, event))
......@@ -138,6 +145,7 @@ class XsocsGui(Qt.QMainWindow):
qspaceGroup = self.__project.qspaceGroup()
qspaceGroup.addQSpace(qspaceF)
self.model().reset()
widget.deleteLater()
def __showQSpace(self, node):
view = self.__qspaceViews.get(node)
......@@ -145,12 +153,29 @@ class XsocsGui(Qt.QMainWindow):
view = QSpaceView(self, model=node.model, node=node)
self.__qspaceViews[node] = view
view.sigProcessApplied.connect(self.__qspaceRoiApplied)
# view.sigProcessApplied.connect(self.__intensityRoiApplied)
view.show()
def __qspaceRoiApplied(self, node):
item = h5NodeToProjectItem(node)
print item
xsocsFile = os.path.basename(self.__project.xsocsFile)
xsocsPrefix = xsocsFile.rpartition('.')[0]
template = '{0}_fit_{{0:>04}}.h5'.format(xsocsPrefix)
output_f = nextFileName(self.__project.workdir, template)
fitWidget = FitWidget(item.qspaceFile, output_f)
fitWidget.exec_()
if fitWidget.status == FitWidget.StatusCompleted:
fitFile = fitWidget.fitFile
fitGroup = item.fitGroup()
fitGroup.addFitFile(fitFile)
self.model().reset()
fitWidget.deleteLater()
def __showFit(self, node):
view = self.__fitViews.get(node)
if not view:
view = FitView(self, model=node.model, node=node)
self.__fitViews[node] = view
view.show()
def model(self):
return self.centralWidget().model()
......
# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2015-2016 European Synchrotron Radiation Facility
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# ###########################################################################*/
from __future__ import absolute_import
__authors__ = ["D. Naudet"]
__license__ = "MIT"
__date__ = "15/09/2016"
import os
from collections import OrderedDict
from silx.gui import qt as Qt
from ...io.FitH5 import FitH5Writer
from ..widgets.FileChooser import FileChooser
from ...process.peak_fit import peak_fit, FitTypes
class FitWidget(Qt.QDialog):
sigProcessDone = Qt.Signal(object)
(StatusUnknown, StatusInit, StatusRunning, StatusCompleted,
StatusAborted,
StatusCanceled) = StatusList = range(6)
FitTypes = OrderedDict([('LEASTSQ', FitTypes.LEASTSQ),
('CENTROID', FitTypes.CENTROID)])
__sigConvertDone = Qt.Signal()
def __init__(self,
qspaceFile,
outputFile=None,
**kwargs):
super(FitWidget, self).__init__(**kwargs)
self.__status = FitWidget.StatusInit
self.__qspaceFile = qspaceFile
self.__fitType = None
self.__selectedFile = None
self.__fitFile = None
layout = Qt.QGridLayout(self)
fitTypeLayout = Qt.QHBoxLayout()
fitTypeCb = Qt.QComboBox()
fitTypeCb.currentIndexChanged[str].connect(self.__fitTypeChanged)
fitTypeCb.addItems(FitWidget.FitTypes.keys())
fitTypeCb.setCurrentIndex(0)
self.__fitTypeChanged(fitTypeCb.currentText())
fitTypeLayout.addWidget(Qt.QLabel('Fit :'))
fitTypeLayout.addWidget(fitTypeCb)
layout.addLayout(fitTypeLayout, 0, 0, alignment=Qt.Qt.AlignLeft)
fChooser = FileChooser(fileMode=Qt.QFileDialog.AnyFile)
fChooser.sigSelectionChanged.connect(self.__fileChanged)
fChooser.label.setText('Output')
if outputFile:
fChooser.lineEdit.setText(outputFile)
layout.addWidget(fChooser, 1, 0)
bn_box = Qt.QDialogButtonBox(Qt.QDialogButtonBox.Ok |
Qt.QDialogButtonBox.Cancel)
bn_box.button(Qt.QDialogButtonBox.Ok).setText('Apply')
bn_box.accepted.connect(self.__onAccept)
bn_box.rejected.connect(self.reject)
layout.addWidget(bn_box, 3, 0)
layout.setRowStretch(2, 1)
self.__bn_box = bn_box
self.__fileChanged(outputFile)
def __fileChanged(self, filePath):
if filePath:
enab = True
else:
enab = False
self.__selectedFile = filePath
bn = self.__bn_box.button(Qt.QDialogButtonBox.Ok)
bn.setEnabled(enab)
def __fitTypeChanged(self, fitName):
self.__fitType = FitWidget.FitTypes[fitName]
def __onAccept(self):
self.__fitFile = None
results = peak_fit(self.__qspaceFile,
fit_type=self.__fitType,
n_proc=1)
with FitH5Writer(self.__selectedFile, mode='w') as fitH5:
fitH5.set_x_fit(results.x_height,
results.x_center,
results.x_width)
fitH5.set_y_fit(results.y_height,
results.y_center,
results.y_width)
fitH5.set_z_fit(results.z_height,
results.z_center,
results.z_width)
fitH5.set_scan_positions(results.sample_x, results.sample_y)
fitH5.set_status(results.status)
self.__fitFile = self.__selectedFile
self._setStatus(FitWidget.StatusCompleted)
self.accept()
fitFile = property(lambda self: self.__fitFile)
status = property(lambda self: self.__status)
def _setStatus(self, status):
if status not in FitWidget.StatusList:
raise ValueError('Unknown status value : {0}.'
''.format(status))
self.__status = status
......@@ -160,11 +160,6 @@ class ConversionParamsWidget(Qt.QWidget):
self.__qsize_z_edit.setText(str(int(qspace_size[2])))
# class RecipSpaceWidgetEvent(ProcessWidgetEvent):
# RecipSpaceEventData = namedtuple('RecipSpaceEventData', ['qspaceH5'])
# class RecipSpaceWidget(ProcessWidget):
class RecipSpaceWidget(Qt.QDialog):
sigProcessDone = Qt.Signal(object)
......@@ -490,11 +485,7 @@ class RecipSpaceWidget(Qt.QDialog):
else:
self.__qspaceH5 = None
processedData = self._processData()
print 'DONE', processedData
self.sigProcessDone.emit(processedData)
# evtData = RecipSpaceWidgetEvent.RecipSpaceEventData(
# qspaceH5=processedData)
# self._emitEvent(RecipSpaceWidgetEvent(self, evtData))
qspaceH5 = property(lambda self: self.__qspaceH5)
......
# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2015-2016 European Synchrotron Radiation Facility
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# ###########################################################################*/
from __future__ import absolute_import
__authors__ = ["D. Naudet"]
__license__ = "MIT"
__date__ = "15/09/2016"
import os
import numpy as np
# from .ProjectDef import ProcessId
# from .HybridItem import HybridItem
from ...io.FitH5 import FitH5
from .ProjectItem import ProjectItem
from .ProjectDef import ItemClassDef
@ItemClassDef('FitGroup')
class FitGroup(ProjectItem):
def addFitFile(self, fitFile):
itemName = os.path.basename(fitFile).rsplit('.')[0]
itemPath = self.path + '/' + itemName
item = FitItem(self.filename, itemPath, mode='a')
item.fitFile = fitFile
return item
@ItemClassDef('FitItem')
class FitItem(ProjectItem):
FitH5FilePath = 'input'
def __init__(self, *args, **kwargs):
self.__fitFile = None
super(FitItem, self).__init__(*args, **kwargs)
fitH5 = property(lambda self: FitH5(self.fitFile)
if self.fitFile else None)
@property
def fitFile(self):
""" The name of the input data file. """
if self.__fitFile is None:
with self._get_file() as h5f:
path = self.path + '/' + FitItem.FitH5FilePath
if path in h5f:
group = h5f.get(path)
self.__fitFile = group.file.filename
del group
return self.__fitFile
@fitFile.setter
def fitFile(self, fitFile):
""" Set the fit data file for this item. The fit data
file can only be set once. To use a different data file you have to
create a new project. """
# TODO : make sure file exists and is readable
if self.fitFile is not None:
raise ValueError('Fit input file is already set.')
# Adding the external link to the file
self.__fitFile = fitFile
path = self.path + '/' + FitItem.FitH5FilePath
self.add_file_link(path, fitFile, '/')
self.setHidden(True, path)
self._createItem()
def _createItem(self):
fitFile = self.fitFile
if fitFile is None:
return
# self.add_file_link(self.path + '/width')
......@@ -36,6 +36,7 @@ from ..model.ModelDef import ModelColumns
from .IntensityGroup import IntensityGroup, IntensityItem
from ..model.NodeEditor import EditorMixin
from .Hdf5Nodes import H5GroupNode, H5NodeClassDef, H5DatasetNode
from ..model.Node import Node
class ScatterPlotButton(EditorMixin, Qt.QWidget):
......@@ -106,3 +107,47 @@ class IntensityNode(H5DatasetNode):
attribute=('XsocsClass', 'QSpaceItem'))
class QSpaceItemNode(H5GroupNode):
editors = QSpaceButton
class FitButton(EditorMixin, Qt.QWidget):
persistent = True
sigValueChanged = Qt.Signal()
def __init__(self, parent, option, index):
super(FitButton, self).__init__(parent, option, index)
layout = Qt.QHBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
icon = icons.getQIcon('item-1dim')
button = Qt.QToolButton()
button.setIcon(icon)
layout.addWidget(button)
layout.addStretch(1)
button.clicked.connect(self.__clicked)
def __clicked(self):
# node = self.node
event = {'event': 'fit'}
self.notifyView(event)
class FitHeightNode(Node):
className = 'height'
class FitCenterNode(Node):
className = 'center'
class FitWidthNode(Node):
className = 'width'
@H5NodeClassDef('FitItem',
attribute=('XsocsClass', 'FitItem'))
class QSpaceItemNode(H5GroupNode):
editors = FitButton
groupClasses = [(None, FitHeightNode),
(None, FitCenterNode),
(None, FitWidthNode)]
......@@ -38,6 +38,7 @@ import numpy as np
from ...io.QSpaceH5 import QSpaceH5
from .ProjectItem import ProjectItem
from .ProjectDef import ItemClassDef
from .FitGroup import FitGroup
@ItemClassDef('QSpaceGroup')
......@@ -53,6 +54,7 @@ class QSpaceGroup(ProjectItem):
@ItemClassDef('QSpaceItem')
class QSpaceItem(ProjectItem):
QSpaceH5FilePath = 'input'
FitGroupPath = 'fits'
def __init__(self, *args, **kwargs):
self.__qspaceFile = None
......@@ -90,6 +92,10 @@ class QSpaceItem(ProjectItem):
self._createItem()
def fitGroup(self):
return FitGroup(self.filename,
self.path + '/' + QSpaceItem.FitGroupPath)
def _createItem(self):
qspaceFile = self.qspaceFile
if qspaceFile is None:
......@@ -114,56 +120,6 @@ class QSpaceItem(ProjectItem):
itemPath = pathTpl.format('qz range')
self._set_array_data(itemPath, np.array([qz[0], qz[-1]]))
# @ItemClassDef('QSpaceItem')
# class QSpaceItem(ProjectItem):
# QSpaceFilePath = 'File'
# AcqParamsPath = 'AcqParams'
# SumPath = 'Sum'
#
# def __init__(self, *args, **kwargs):
# super(QSpaceItem, self).__init__(*args, **kwargs)
# self.__qspaceFile = None
#
# @property
# def qspaceFile(self):
# """ The name of the input data file. """
# if self.__qspaceFile is None:
# with self._get_file() as h5f:
# path = self.path + '/' + QSpaceItem.QSpaceFilePath
# group = h5f.get(path)
# if group:
# self.__qspaceFile = group.file.filename
# del group
# return self.__qspaceFile
#
# @qspaceFile.setter
# def qspaceFile(self, qspace_f):
# # TODO : make sure file exists and is readable
# if self.qspaceFile is not None:
# raise ValueError('Xsocs input file is already set.')
#
# # adding a link to the source file
# qspaceH5 = QSpaceH5(qspace_f)
# self.__qspaceFile = qspace_f
# qspaceRoot = '/' + '/'.join([self.path, QSpaceItem.QSpaceFilePath])
# self.add_file_link(qspaceRoot, qspace_f, '/')
#
# sumItemPath = '/'.join([self.path, QSpaceItem.SumPath])
# intensityGrp = HybridItem(self.filename,
# sumItemPath,
# processLevel=ProcessId.QSpace)
# sumPath = qspaceRoot + '/' + QSpaceH5.qspace_sum_path
# xPath = qspaceRoot + '/' + QSpaceH5.sample_x_path
# yPath = qspaceRoot + '/' + QSpaceH5.sample_y_path
#
# sumLink = h5py.SoftLink(sumPath)
# xLink = h5py.SoftLink(xPath)
# yLink = h5py.SoftLink(yPath)
# with qspaceH5:
# intensityGrp.setScatter(xLink,
# yLink,
# sumLink)
FitGroup(self.filename,
self.path + '/' + QSpaceItem.FitGroupPath,
mode=self.mode)
\ No newline at end of file
# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2015-2016 European Synchrotron Radiation Facility
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# ###########################################################################*/
from __future__ import absolute_import
__authors__ = ["D. Naudet"]
__license__ = "MIT"
__date__ = "15/09/2016"
import numpy as np
from matplotlib import cm
from silx.gui import qt as Qt
from silx.gui.plot import PlotWindow
from ..model.TreeView import TreeView
from ..model.Model import Model
from ..model.ModelDef import ModelColumns, ModelRoles
from ..project.XsocsH5Factory import h5NodeToProjectItem
from ..project.FitGroup import FitItem
from ...io.FitH5 import FitH5
class FitTree(TreeView):
sigCurrentChanged = Qt.Signal(object)
def __init__(self, *args, **kwargs):
super(FitTree, self).__init__(*args, **kwargs)
self.disableDelegateForColumn(1, True)
for col in range(ModelColumns.ColumnMax):
if col != ModelColumns.NameColumn:
self.setColumnHidden(col, True)
def currentChanged(self, current, previous):
node = current.data(ModelRoles.InternalDataRole)
dataType = node.nodeName
fitItem = h5NodeToProjectItem(node.parent())
if not isinstance(fitItem, FitItem):
return
self.sigCurrentChanged.emit({'event': dataType, 'item': fitItem})
class FitView(Qt.QMainWindow):
sigProcessApplied = Qt.Signal(object)
plot = property(lambda self: self.__plotWindow)
def __init__(self,
parent,
model,
node,
**kwargs):
super(FitView, self).__init__(parent=parent)
treeDock = Qt.QDockWidget(self)
features = treeDock.features() ^ Qt.QDockWidget.DockWidgetClosable
treeDock.setFeatures(features)
self.addDockWidget(Qt.Qt.LeftDockWidgetArea, treeDock)
self.__xPlotWindow = plotWindow = PlotWindow(aspectRatio=True,
curveStyle=False,
mask=False,
roi=False,
**kwargs)
plotWindow.setKeepDataAspectRatio(True)
plotWindow.setActiveCurveHandling(False)
xDock = Qt.QDockWidget()
xDock.setWidget(plotWindow)
features = xDock.features() ^ Qt.QDockWidget.DockWidgetClosable
xDock.setFeatures(features)
self.splitDockWidget(treeDock, xDock, Qt.Qt.Vertical)
self.__yPlotWindow = plotWindow = PlotWindow(aspectRatio=True,
curveStyle=False,
mask=False,
roi=False,
**kwargs)
plotWindow.setKeepDataAspectRatio(True)
plotWindow.setActiveCurveHandling(False)
yDock = Qt.QDockWidget()
yDock.setWidget(plotWindow)
features = yDock.features() ^ Qt.QDockWidget.DockWidgetClosable
yDock.setFeatures(features)
self.addDockWidget(Qt.Qt.RightDockWidgetArea, yDock)
self.__zPlotWindow = plotWindow = PlotWindow(aspectRatio=True,
curveStyle=False,
mask=False,
roi=False,
**kwargs)
plotWindow.setKeepDataAspectRatio(True)
plotWindow.setActiveCurveHandling(False)
zDock = Qt.QDockWidget()
zDock.setWidget(plotWindow)
features = zDock.features() ^ Qt.QDockWidget.DockWidgetClosable
zDock.setFeatures(features)
self.splitDockWidget(yDock, zDock, Qt.Qt.Vertical)
tree = FitTree(self, model=model)
index = node.index()
tree.setRootIndex(index)
tree.sigCurrentChanged.connect(self.__itemSelected)
treeDock.setWidget(tree)
def __itemSelected(self, event):
dtype = event['event']
item = event['item']
with item.fitH5 as fitH5:
sampleX, sampleY = fitH5.scan_positions()
if dtype == 'height':
xData = fitH5.x_height()
yData = fitH5.y_height()
zData = fitH5.z_height()
elif dtype == 'center':
xData = fitH5.x_center()
yData = fitH5.y_center()
zData = fitH5.z_center()
elif dtype == 'width':
xData = fitH5.x_width()
yData = fitH5.y_width()
zData = fitH5.z_width()
else:
raise ValueError('Unknown event {0}.'.format(dtype))
self.__setPlotData(self.__xPlotWindow, sampleX, sampleY, xData)
self.__setPlotData(self.__yPlotWindow, sampleX, sampleY, yData)
self.__setPlotData(self.__zPlotWindow, sampleX, sampleY, zData)
def __setPlotData(self, plot, x, y, data):
if data.ndim == 1:
# scatter
min_, max_ = data.min(), data.max()
colormap = cm.jet