Commit 11dc1cf3 authored by Damien Naudet's avatar Damien Naudet

More work on the view

parent 8a95fe2b
......@@ -29,7 +29,7 @@ __authors__ = ["D. Naudet"]
__license__ = "MIT"
__date__ = "15/09/2016"
import os
from functools import partial
import h5py
......@@ -141,9 +141,17 @@ class ExternalLinkNode(XsocsNode):
super(ExternalLinkNode, self).__init__(*args, **kwargs)
with h5py.File(self.projectFile) as h5f:
item = h5f[self.path]
filename = item.file.filename
followLink = item.attrs.get('XsocsExpand')
del item
self.__followLink = followLink if followLink is not None else False
basename = os.path.basename(filename).rpartition('.')[0]
self.setData(ModelColumns.NameColumn,
basename,
role=Qt.Qt.DisplayRole)
self.setData(ModelColumns.NameColumn,
filename,
role=Qt.Qt.ToolTipRole)
def childCount(self):
if not self.__followLink:
......@@ -174,7 +182,7 @@ class DatasetNode(ProjectNode):
self.setData(ModelColumns.NameColumn, icon, Qt.Qt.DecorationRole)
self.setData(ModelColumns.NameColumn,
self.setData(ModelColumns.ValueColumn,
text,
role=Qt.Qt.DisplayRole)
......
......@@ -44,6 +44,7 @@ class ProjectView(Qt.QTreeView):
self.setItemDelegateForColumn(1, delegate)
delegate.sigDelegateEvent.connect(self.sigItemEvent)
self.expanded.connect(self.__expanded)
self.header().setResizeMode(Qt.QHeaderView.ResizeToContents)
# self.collapsed.connect(self.__collapsed)
def __expanded(self, index):
......
......@@ -32,14 +32,11 @@ __date__ = "15/09/2016"
import h5py
from .ProjectItem import ProjectItem
from .ProjectDef import ItemClassDef
@ItemClassDef('HybridItem')
class HybridItem(ProjectItem):
viewShowChildren = False
icon = 'item-2dim'
def __init__(self, *args, **kwargs):
super(HybridItem, self).__init__(*args, **kwargs)
def setScatter(self, x, y, data=None):
self._commit(scatter=(x, y, data))
......@@ -51,7 +48,7 @@ class HybridItem(ProjectItem):
# self._commit(imageSlice=(xSlice, ySlice))
def getScatter(self):
with h5py.File(self.file, 'r') as h5f:
with self._get_file() as h5f:
grp = h5f.get(self.path)
if grp is None:
return None
......@@ -72,7 +69,7 @@ class HybridItem(ProjectItem):
return x, y, data
def getImage(self):
with h5py.File(self.file, 'r') as h5f:
with self._get_file() as h5f:
grp = h5f.get(self.path)
if grp is None:
return None
......@@ -114,7 +111,7 @@ class HybridItem(ProjectItem):
imageSlice=None):
super(HybridItem, self)._commit()
# TODO : check if data already exists in file.
with h5py.File(self.file, 'a') as h5f:
with self._get_file(mode='r+') as h5f:
grp = h5f.require_group(self.path)
if scatter:
x, y, data = scatter
......
# 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"
class ProcessId(object):
Input, QSpace, Fit = range(3)
_registeredItems = {}
def getItemClass(itemName):
return _registeredItems.get(itemName)
def registerItemClass(klass):
global _registeredItems
itemName = klass.itemName
if itemName in _registeredItems:
raise AttributeError('Failed to register item class {0}.'
'attribute is already registered.'
''.format(klass.__name__))
# TODO : some kind of checks on the klass
_registeredItems[itemName] = klass
def ItemClassDef(itemName):
def inner(cls):
cls.itemName = itemName
registerItemClass(cls)
return cls
return inner
......@@ -30,59 +30,29 @@ __license__ = "MIT"
__date__ = "15/09/2016"
import h5py
import numpy as np
_registeredItems = {}
from .ProjectDef import getItemClass
from ...io.XsocsH5Base import XsocsH5Base
def getItemClass(itemName):
return _registeredItems.get(itemName)
def registerItemClass(klass):
global _registeredItems
itemName = klass.itemName
if itemName in _registeredItems:
raise AttributeError('Failed to register item class {0}.'
'attribute is already registered.'
''.format(klass.__name__))
# TODO : some kind of checks on the klass
_registeredItems[itemName] = klass
def ItemClassDef(itemName, editor=None):
def inner(cls):
cls.itemName = itemName
cls.editor = editor
registerItemClass(cls)
return cls
return inner
class ProjectItem(object):
class ProjectItem(XsocsH5Base):
itemName = None
editor = None
viewShowChildren = True
icon = None
def __init__(self, h5File, nodePath, processLevel=None):
def __init__(self, h5File, nodePath, mode='r+', processLevel=None):
# TODO : check if parent already has a child with the same name
super(ProjectItem, self).__init__()
super(ProjectItem, self).__init__(h5File, mode=mode)
self.__nodePath = nodePath
self.__h5File = h5File
self.__processLevel = None
self.__processLevelIn = processLevel
path = property(lambda self: self.__nodePath)
file = property(lambda self: self.__h5File)
@property
def processLevel(self):
if self.__processLevel is None:
if self.__processLevelIn is None:
with h5py.File(self.__h5File, 'r') as h5f:
with self._get_file() as h5f:
processLevel = h5f[self.__nodePath].attrs.get('XsocsLevel')
self.__processLevel = processLevel
else:
......@@ -90,9 +60,9 @@ class ProjectItem(object):
return self.__processLevel
def _commit(self):
with h5py.File(self.file, 'a') as h5f:
with self._get_file() as h5f:
grp = h5f.require_group(self.path)
grp.attrs['XsocsType'] = self.itemName
grp.attrs['XsocsType'] = np.string_(self.itemName)
if self.__processLevelIn is not None:
grp.attrs['XsocsLevel'] = self.__processLevelIn
del grp
......@@ -105,25 +75,8 @@ class ProjectItem(object):
del grp
if xsocsType is None:
return None
klass = _registeredItems.get(xsocsType)
klass = getItemClass(xsocsType)
if klass is None:
return None
instance = klass(h5File, groupPath)
return instance
class ItemEvent(object):
def __init__(self, item, eventType, index):
super(ItemEvent, self).__init__()
self.__item = item
self.__type = eventType
self.__index = index
def plotData(self):
raise NotImplementedError('')
type = property(lambda self: self.__type)
item = property(lambda self: self.__item)
index = property(lambda self: self.__index)
\ No newline at end of file
......@@ -27,4 +27,4 @@ from __future__ import absolute_import
__authors__ = ["D. Naudet"]
__license__ = "MIT"
__date__ = "15/09/2016"
\ No newline at end of file
__date__ = "15/09/2016"
# 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.XsocsH5 import XsocsH5
from .ProjectItem import ProjectItem
from .ProjectDef import ItemClassDef
@ItemClassDef('SourceItem')
class SourceItem(ProjectItem):
XSocsFilePath = 'input'
AcqParamsPath = 'AcqParams'
DataPath = 'Data'
EntriesPath = 'Entries'
def __init__(self, *args, **kwargs):
super(SourceItem, self).__init__(*args, **kwargs)
self.__xsocsFile = None
@property
def xsocsFile(self):
""" The name of the input data file. """
if self.__xsocsFile is None:
with self._get_file() as h5f:
path = self.path + SourceItem.XSocsFilePath
group = h5f.get(path)
if group:
self.__xsocsFile = group.file.filename
del group
return self.__xsocsFile
@xsocsFile.setter
def xsocsFile(self, xsocs_f):
""" Set the input data file for this Xsocs workspace. The input data
file can only be set once. To use a different data file you have to
create a new workspace. """
# TODO : make sure file exists and is readable
if self.xsocsFile is not None:
raise ValueError('Xsocs input file is already set.')
# adding a link to the source file
xsocsH5 = h5f = XsocsH5(xsocs_f)
self.__xsocsFile = xsocs_f
path = '/'.join([self.path, SourceItem.XSocsFilePath])
self.add_file_link(path, xsocs_f, '/')
# adding parameter values to the source folder
entries = h5f.entries()
# TODO : make sure that all parameters are consistent
scan_params = h5f.scan_params(entries[0])
path_tpl = '{0}/{1}/{{0}}'.format(self.path, SourceItem.AcqParamsPath)
for key, value in scan_params.items():
self._set_scalar_data(path_tpl.format(key), value)
path_tpl = '{0}/{1}/{{0}}'.format(self.path, SourceItem.DataPath)
globalIntensityGrp = HybridItem(self.filename,
path_tpl.format('intensity'),
processLevel=ProcessId.Input)
gIntensity = None
gPos_0 = None
gPos_1 = None
gParams = None
gSteps_0 = None
gSteps_1 = None
xsocs_f_prefix = os.path.basename(xsocs_f).rsplit('.')[0]
# adding misc. data
path_tpl = '{0}/{1}//{2}/{{0}}/{{1}}'.format(self.path,
SourceItem.DataPath,
SourceItem.EntriesPath)
for entry in entries:
entry_stripped = entry.lstrip(xsocs_f_prefix)
dataGrp = HybridItem(self.filename,
path_tpl.format(entry_stripped,
'intensity'),
processLevel=ProcessId.Input)
data = h5f.image_cumul(entry)
pos_0, pos_1 = h5f.scan_positions(entry)
# intensity as a scatter plot
dataGrp.setScatter(pos_0, pos_1, data)
# intensity as an image
scan_params = h5f.scan_params(entry)
# xSlice = np.s_[0:scan_params['motor_0_steps']:1]
# ySlice = np.s_[0::scan_params['motor_0_steps']]
# dataGrp.setImageFromScatter(xSlice, ySlice)
steps_0 = scan_params['motor_0_steps']
steps_1 = scan_params['motor_1_steps']
x = np.linspace(scan_params['motor_0_start'],
scan_params['motor_0_end'], steps_0, endpoint=False)
y = np.linspace(scan_params['motor_1_start'],
scan_params['motor_1_end'], steps_1, endpoint=False)
# TODO : check overflow
if gIntensity is None:
# TODO : see if we want to keep the first entry positions,
# or an avg of all of them or ...
gIntensity = data.copy()
gPos_0 = pos_0
gPos_1 = pos_1
gParams = scan_params
gSteps_0 = steps_0
gSteps_1 = steps_1
else:
gIntensity += data
data = data.reshape(steps_1, steps_0)
dataGrp.setImage(x, y, data)
del dataGrp
globalIntensityGrp.setScatter(gPos_0, gPos_1, gIntensity)
x = np.linspace(gParams['motor_0_start'],
gParams['motor_0_end'], gSteps_0, endpoint=False)
y = np.linspace(gParams['motor_1_start'],
gParams['motor_1_end'], gSteps_1, endpoint=False)
gIntensity = gIntensity.reshape(gSteps_1, gSteps_0)
globalIntensityGrp.setImage(x, y, gIntensity)
del globalIntensityGrp
......@@ -30,18 +30,14 @@ __license__ = "MIT"
__date__ = "15/09/2016"
import os
import numpy as np
from ...io import XsocsH5 as _XsocsH5
from ..model.ProjectModel import ProjectModel
from ..model.ProjectView import ProjectView
from .HybridItem import HybridItem
from .SourceItem import SourceItem
class XsocsProject(_XsocsH5.XsocsH5Base):
H5_SOURCE_F = '/source/file'
H5_SCAN_PARAMS = '/source/params'
H5_INPUT_DATA = '/input/'
GLOBAL_ENTRY = '_global'
InputItemPath = '/Source'
XsocsNone, XsocsInput, XsocsQSpace, XsocsFit = range(4)
......@@ -73,96 +69,12 @@ class XsocsProject(_XsocsH5.XsocsH5Base):
@property
def xsocsFile(self):
""" The name of the input data file. """
if self.__xsocsFile is None:
with self._get_file() as h5f:
group = h5f.get(XsocsProject.H5_SOURCE_F)
if group:
self.__xsocsFile = group.file.filename
return self.__xsocsFile
with self._get_file() as h5f:
if h5f.get(self.InputItemPath) is None:
return None
return SourceItem(self.filename, self.InputItemPath).xsocsFile
@xsocsFile.setter
def xsocsFile(self, xsocs_f):
""" Set the input data file for this Xsocs workspace. The input data
file can only be set once. To use a different data file you have to
create a new workspace. """
# TODO : make sure file exists and is readable
if self.xsocsFile is not None:
raise ValueError('Xsocs input file is already set.')
# adding a link to the source file
self.__xsocsH5 = h5f = _XsocsH5.XsocsH5(xsocs_f)
self.__xsocsFile = xsocs_f
self.add_file_link(XsocsProject.H5_SOURCE_F, xsocs_f, '/')
# adding parameter values to the source folder
entries = h5f.entries()
# TODO : make sure that all parameters are consistent
scan_params = h5f.scan_params(entries[0])
path_tpl = '{0}/{{0}}'.format(XsocsProject.H5_SCAN_PARAMS)
for key, value in scan_params.items():
self._set_scalar_data(path_tpl.format(key), value)
path_tpl = '{0}/{{0}}'.format(XsocsProject.H5_INPUT_DATA)
globalIntensityGrp = HybridItem(self.filename,
path_tpl.format('intensity'),
processLevel=XsocsProject.XsocsInput)
gIntensity = None
gPos_0 = None
gPos_1 = None
gParams = None
gSteps_0 = None
gSteps_1 = None
# adding misc. data
path_tpl = '{0}/entries/{{0}}/{{1}}'.format(XsocsProject.H5_INPUT_DATA)
for entry in entries:
dataGrp = HybridItem(self.filename,
path_tpl.format(entry,
'intensity'),
processLevel=XsocsProject.XsocsInput)
data = h5f.image_cumul(entry)
pos_0, pos_1 = h5f.scan_positions(entry)
# intensity as a scatter plot
dataGrp.setScatter(pos_0, pos_1, data)
# intensity as an image
scan_params = h5f.scan_params(entry)
# xSlice = np.s_[0:scan_params['motor_0_steps']:1]
# ySlice = np.s_[0::scan_params['motor_0_steps']]
# dataGrp.setImageFromScatter(xSlice, ySlice)
steps_0 = scan_params['motor_0_steps']
steps_1 = scan_params['motor_1_steps']
x = np.linspace(scan_params['motor_0_start'],
scan_params['motor_0_end'], steps_0, endpoint=False)
y = np.linspace(scan_params['motor_1_start'],
scan_params['motor_1_end'], steps_1, endpoint=False)
# TODO : check overflow
if gIntensity is None:
# TODO : see if we want to keep the first entry positions,
# or an avg of all of them or ...
gIntensity = data.copy()
gPos_0 = pos_0
gPos_1 = pos_1
gParams = scan_params
gSteps_0 = steps_0
gSteps_1 = steps_1
else:
gIntensity += data
data = data.reshape(steps_1, steps_0)
dataGrp.setImage(x, y, data)
del dataGrp
globalIntensityGrp.setScatter(gPos_0, gPos_1, gIntensity)
x = np.linspace(gParams['motor_0_start'],
gParams['motor_0_end'], gSteps_0, endpoint=False)
y = np.linspace(gParams['motor_1_start'],
gParams['motor_1_end'], gSteps_1, endpoint=False)
gIntensity = gIntensity.reshape(gSteps_1, gSteps_0)
globalIntensityGrp.setImage(x, y, gIntensity)
del globalIntensityGrp
\ No newline at end of file
item = SourceItem(self.filename, self.InputItemPath)
item.xsocsFile = xsocs_f
......@@ -59,14 +59,31 @@ class XsocsH5Base(object):
filename = property(lambda self: self.__h5_f)
@contextmanager
def _get_file(self):
def _get_file(self, mode=None):
"""
This protected context manager opens the hdf5 file if it isn't already
opened (i.e : if the XsocsH5Base isn't already used as a context
manager).
"""
# TODO : lots of tests...
prev_mode = None
if mode is not None:
if self.__file is not None:
if self.__file.mode != mode:
raise ValueError('File is already opened with '
'a different mode.\n'
'File : {0}, current mode : {1}, '
'requested mode : {2}'
''.format(self.filename,
self.__file.mode,
mode))
else:
prev_mode = self.mode
self.mode = mode
with self:
yield self.__file
if prev_mode is not None:
self.mode = prev_mode
def _open(self):
if self.__file is None:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment