Commit 8a72d061 authored by Damien Naudet's avatar Damien Naudet

WIP

parent 5b4d158c
......@@ -32,55 +32,55 @@ __date__ = "15/09/2016"
import os
import h5py
from silx.gui.hdf5 import Hdf5TreeView
from .model.ModelDef import ModelRoles
from .project.ProjectDef import ProcessId
from .project.HybridItem import HybridItem
from .process.RecipSpaceWidget import (RecipSpaceWidget,
RecipSpaceWidgetEvent)
from .view.RealSpaceViewWidget import (RealSpaceViewWidget,
RealSpaceViewWidgetEvent)
from .view.QspaceViewWidget import (QSpaceViewWidget,
QSpaceViewWidgetEvent)
from .project.QSpaceItem import QSpaceItem
# TODO : something a bit more... flexible
def viewWidgetFromProjectEvent(project, event):
index = event.index
processId = index.data(ModelRoles.XsocsProcessId)
eventData = event.data
widgetCls = None
xsocsType = index.data(ModelRoles.XsocsNodeType)
if xsocsType == h5py.ExternalLink:
widget = Hdf5TreeView()
widget.findHdf5TreeModel().appendFile(event.data)
return widget
if xsocsType == 'HybridItem':
if eventData.evtType == 'scatter':
plotData = HybridItem(project.filename,
eventData.path).getScatter()
elif eventData.evtType == 'image':
plotData = HybridItem(project.filename,
eventData.path).getImage()
else:
plotData = None
if processId == ProcessId.Input:
widgetCls = RealSpaceViewWidget
elif processId == ProcessId.QSpace:
widgetCls = QSpaceViewWidget
if widgetCls is not None and plotData is not None:
widget = widgetCls(index)
widget.setPlotData(*plotData)
return widget
print('Nothing to DO')
return None
# from silx.gui.hdf5 import Hdf5TreeView
#
# from .model.ModelDef import ModelRoles
# from .project.ProjectDef import ProcessId
# # from .project.HybridItem import HybridItem
# from .process.RecipSpaceWidget import (RecipSpaceWidget,
# RecipSpaceWidgetEvent)
# from .view.RealSpaceViewWidget import (RealSpaceViewWidget,
# RealSpaceViewWidgetEvent)
# from .view.QspaceViewWidget import (QSpaceViewWidget,
# QSpaceViewWidgetEvent)
# from .project.QSpaceItem import QSpaceItem
#
#
# # TODO : something a bit more... flexible
# def viewWidgetFromProjectEvent(project, event):
# index = event.index
# processId = index.data(ModelRoles.XsocsProcessId)
# eventData = event.data
#
# widgetCls = None
# xsocsType = index.data(ModelRoles.XsocsNodeType)
# if xsocsType == h5py.ExternalLink:
# widget = Hdf5TreeView()
# widget.findHdf5TreeModel().appendFile(event.data)
# return widget
#
# if xsocsType == 'HybridItem':
# if eventData.evtType == 'scatter':
# plotData = HybridItem(project.filename,
# eventData.path).getScatter()
# elif eventData.evtType == 'image':
# plotData = HybridItem(project.filename,
# eventData.path).getImage()
# else:
# plotData = None
#
# if processId == ProcessId.Input:
# widgetCls = RealSpaceViewWidget
# elif processId == ProcessId.QSpace:
# widgetCls = QSpaceViewWidget
#
# if widgetCls is not None and plotData is not None:
# widget = widgetCls(index)
# widget.setPlotData(*plotData)
# return widget
#
# print('Nothing to DO')
# return None
# TODO : something better!
......@@ -93,37 +93,37 @@ def nextFileName(root, template, cntMax=10000):
else:
raise ValueError('No available file names.')
# TODO : cache the widget to reuse previous parameters?
def processWidgetFromViewEvent(project, event, parent=None):
widget = None
index = event.index
if isinstance(event, RealSpaceViewWidgetEvent):
xsocsPrefix = os.path.basename(project.xsocsFile).rpartition('.')[0]
template = '{0}_qspace_{{0:>04}}.h5'.format(xsocsPrefix)
output_f = nextFileName(project.workdir, template)
widget = RecipSpaceWidget(parent=parent,
index=index,
data_h5f=project.xsocsFile,
output_f=output_f,
qspace_size=None,
image_binning=None,
rect_roi=event.data)
return widget
# TODO : rework this
def processDoneEvent(project, event):
eventData = event.data
if isinstance(event, RecipSpaceWidgetEvent):
qspaceH5 = eventData.qspaceH5
prefix = os.path.basename(qspaceH5).rpartition('.')[0]
processLevel = ProcessId.QSpace
itemPath = 'Qspace/' + prefix
item = QSpaceItem(project.filename,
itemPath,
processLevel=processLevel)
item.qspaceFile = qspaceH5
project.reload()
#
# # TODO : cache the widget to reuse previous parameters?
# def processWidgetFromViewEvent(project, event, parent=None):
# widget = None
# index = event.index
#
# if isinstance(event, RealSpaceViewWidgetEvent):
# xsocsPrefix = os.path.basename(project.xsocsFile).rpartition('.')[0]
# template = '{0}_qspace_{{0:>04}}.h5'.format(xsocsPrefix)
# output_f = nextFileName(project.workdir, template)
# widget = RecipSpaceWidget(parent=parent,
# index=index,
# data_h5f=project.xsocsFile,
# output_f=output_f,
# qspace_size=None,
# image_binning=None,
# rect_roi=event.data)
# return widget
#
#
# # TODO : rework this
# def processDoneEvent(project, event):
# eventData = event.data
#
# if isinstance(event, RecipSpaceWidgetEvent):
# qspaceH5 = eventData.qspaceH5
# prefix = os.path.basename(qspaceH5).rpartition('.')[0]
# processLevel = ProcessId.QSpace
# itemPath = 'Qspace/' + prefix
# item = QSpaceItem(project.filename,
# itemPath,
# processLevel=processLevel)
# item.qspaceFile = qspaceH5
# project.reload()
......@@ -25,6 +25,8 @@
from __future__ import absolute_import
import os
from silx.gui import qt as Qt
from .widgets.Wizard import XsocsWizard
......@@ -33,6 +35,7 @@ from .Widgets import (AcqParamsWidget,
# from .Utils import (viewWidgetFromProjectEvent,
# processWidgetFromViewEvent,
# processDoneEvent)
from .process.RecipSpaceWidget import RecipSpaceWidget
from .view.IntensityView import IntensityView
from .project.XsocsProject import XsocsProject
from .model.TreeView import TreeView
......@@ -40,6 +43,7 @@ from .model.Model import Model
from .project.IntensityGroup import IntensityGroup, IntensityItem
from .project.XsocsH5Factory import XsocsH5Factory, h5NodeToProjectItem
from .project.Hdf5Nodes import setH5NodeFactory, H5File
from .Utils import nextFileName
_COMPANY_NAME = 'ESRF'
......@@ -98,19 +102,41 @@ class XsocsGui(Qt.QMainWindow):
if isinstance(projectItem, (IntensityGroup, IntensityItem))\
and event['event'] == 'scatter':
self.__showIntensity(projectItem)
self.__showIntensity(node)
else:
ValueError('Unknwown event for item {0} : {1}.'
''.format(projectItem, event))
def __showIntensity(self, item):
# intensity, positions = item.getScatterData()
def __showIntensity(self, node):
view = self.__intensityView
if not view:
self.__intensityView = view = IntensityView(item, self)
# view.setPlotData(positions.pos_0, positions.pos_1, intensity)
self.__intensityView = view = IntensityView(self,
model=node.model,
node=node)
view.sigProcessApplied.connect(self.__intensityRoiApplied)
view.show()
def __intensityRoiApplied(self, event):
xsocsFile = os.path.basename(self.__project.xsocsFile)
xsocsPrefix = xsocsFile.rpartition('.')[0]
template = '{0}_qspace_{{0:>04}}.h5'.format(xsocsPrefix)
output_f = nextFileName(self.__project.workdir, template)
widget = RecipSpaceWidget(parent=self,
data_h5f=self.__project.xsocsFile,
output_f=output_f,
qspace_size=None,
image_binning=None,
rect_roi=event)
widget.exec_()
if widget.status == RecipSpaceWidget.StatusCompleted:
qspaceF = widget.qspaceH5
qspaceGroup = self.__project.qspaceGroup()
qspaceGroup.addQSpace(qspaceF)
self.model().refresh()
def model(self):
self.centralWidget().model()
def showEvent(self, event):
super(XsocsGui, self).showEvent(event)
if not self.__widget_setup:
......@@ -195,36 +221,36 @@ class XsocsGui(Qt.QMainWindow):
menus['file'] = fileMenu
menus['help'] = helpMenu
def __projectViewSlot(self, event):
# TODO : store the plot window in a dictionary + weakref w delete
# callback when object is destroyed
mdi = self.centralWidget()
widget = viewWidgetFromProjectEvent(self.__project, event)
if widget is None:
print('UNKNOWN VIEW EVENT')
return
widget.setAttribute(Qt.Qt.WA_DeleteOnClose)
try:
widget.sigProcessApplied.connect(self.__processApplied)
except AttributeError:
pass
mdi.addSubWindow(widget)
widget.show()
def __processApplied(self, event):
widget = processWidgetFromViewEvent(self.__project, event,
parent=self)
if widget is None:
print('UNKNOWN PROCESS EVENT')
return
widget.setWindowFlags(Qt.Qt.Dialog)
widget.setWindowModality(Qt.Qt.WindowModal)
widget.sigProcessDone.connect(self.__processDone)
widget.setAttribute(Qt.Qt.WA_DeleteOnClose)
widget.show()
def __processDone(self, event):
processDoneEvent(self.__project, event)
# def __projectViewSlot(self, event):
# # TODO : store the plot window in a dictionary + weakref w delete
# # callback when object is destroyed
# mdi = self.centralWidget()
# widget = viewWidgetFromProjectEvent(self.__project, event)
# if widget is None:
# print('UNKNOWN VIEW EVENT')
# return
# widget.setAttribute(Qt.Qt.WA_DeleteOnClose)
# try:
# widget.sigProcessApplied.connect(self.__processApplied)
# except AttributeError:
# pass
# mdi.addSubWindow(widget)
# widget.show()
#
# def __processApplied(self, event):
# widget = processWidgetFromViewEvent(self.__project, event,
# parent=self)
# if widget is None:
# print('UNKNOWN PROCESS EVENT')
# return
# widget.setWindowFlags(Qt.Qt.Dialog)
# widget.setWindowModality(Qt.Qt.WindowModal)
# widget.sigProcessDone.connect(self.__processDone)
# widget.setAttribute(Qt.Qt.WA_DeleteOnClose)
# widget.show()
#
# def __processDone(self, event):
# processDoneEvent(self.__project, event)
# def __createToolBars(self):
# self.__toolBars = toolBars = {}
......
......@@ -53,7 +53,7 @@ class Model(Qt.QAbstractItemModel):
def __init__(self, parent=None):
super(Model, self).__init__(parent)
self.__root = RootNode(nodeName='__root__')
self.__root = RootNode(nodeName='__root__', model=self)
self.__root.sigInternalDataChanged.connect(self.__internalDataChanged)
self.__root.sigChildAdded.connect(self.__nodeAdded)
self.__root.sigChildRemoved.connect(self.__nodeRemoved)
......@@ -107,8 +107,9 @@ class Model(Qt.QAbstractItemModel):
self.beginRemoveRows(modelIndex,
indices[0],
indices[0])
child = parent.child(indices[0])
parent._removeChild(child)
if parent:
child = parent.child(indices[0])
parent._removeChild(child)
self.endRemoveRows()
self.sigRowsRemoved.emit(modelIndex, indices[0], indices[0])
......@@ -192,6 +193,18 @@ class Model(Qt.QAbstractItemModel):
self.__root.refresh()
self.endResetModel()
def reset(self):
self.beginResetModel()
children = [self.__root.child(row)
for row in range(self.__root.childCount())]
self.__root.clear()
self.endResetModel()
for child in children:
child.clear()
print 'adding', child
self.__root.appendChild(child)
def rowCount(self, parent=Qt.QModelIndex(), **kwargs):
if not parent.isValid():
node = self.__root
......
......@@ -137,6 +137,7 @@ class Node(object):
subject=None,
nodeName=None,
branchName=None,
model=None,
**kwargs):
super(Node, self).__init__()
......@@ -148,6 +149,8 @@ class Node(object):
self.__started = False
self.__connected = False
self.__model = None
self.setModel(model)
editors = kwargs.get('editors', None)
if editors is not None:
......@@ -272,6 +275,16 @@ class Node(object):
hidden = property(lambda self: self.__hidden)
def setModel(self, model):
if model:
self.__model = weakref.ref(model)
else:
self.__model = None
@property
def model(self):
return (self.__model and self.__model()) or None
@hidden.setter
def hidden(self, hidden):
self.__hidden = hidden
......@@ -339,6 +352,31 @@ class Node(object):
def _setupNode(self):
pass
def _getDepth(self):
parent = self.parent()
row = self.row()
if row < 0:
return []
if parent:
depth = parent._getDepth()
else:
depth = []
depth.append(row)
return depth
def index(self):
model = self.model
index = Qt.QModelIndex()
if model is None:
return index
depth = self._getDepth()
for row in depth:
index = model.index(row, ModelColumns.NameColumn, index)
return index
enabled = property(lambda self: self.__enabled)
def setEnabled(self, enabled, update=True):
......@@ -372,7 +410,7 @@ class Node(object):
def appendChild(self, child):
# TODO : add the node directly if there is no parent!
if self.parent() is None:
if self.model is None:
self._appendChild(child)
else:
self.sigChildAdded.emit([self.childCount()], child)
......@@ -571,6 +609,7 @@ class Node(object):
self.__parent._childDisconnect(self)
self.__parent = parent
self.setModel(parent.model)
if parent is None:
return
......
......@@ -39,7 +39,7 @@ from .NodeEditor import EditorMixin
class TreeView(Qt.QTreeView):
def __init__(self, parent=None):
def __init__(self, parent=None, model=None):
super(TreeView, self).__init__(parent)
delegate = ItemDelegate(self)
self.setItemDelegateForColumn(ModelColumns.NameColumn, delegate)
......@@ -57,8 +57,12 @@ class TreeView(Qt.QTreeView):
self.collapsed.connect(self.__collapsed)
self.header().setResizeMode(Qt.QHeaderView.ResizeToContents)
self.__showUniqueGroup = False
self.__userRoot = False
self.setSelectionBehavior(Qt.QAbstractItemView.SelectRows)
if model:
self.setModel(model)
showUniqueGroup = property(lambda self: self.__showUniqueGroup)
def disableDelegateForColumn(self, column, disable):
......@@ -107,13 +111,18 @@ class TreeView(Qt.QTreeView):
self.__openPersistentEditors(Qt.QModelIndex(), openEditor=True)
def __updateUniqueGroupVisibility(self):
if self.__userRoot:
return
model = self.model()
if model and model.rowCount() == 1 and not self.__showUniqueGroup:
index = model.index(0, 0)
self.setRootIndex(index)
self.__setRootIndex(index)
self.__setHiddenNodes(index)
else:
self.setRootIndex(Qt.QModelIndex())
self.__setRootIndex(Qt.QModelIndex())
def __setRootIndex(self, index):
super(TreeView, self).setRootIndex(index)
def rowsInserted(self, index, start, end):
super(TreeView, self).rowsInserted(index, start, end)
......@@ -161,6 +170,16 @@ class TreeView(Qt.QTreeView):
if persistent or not openEditor:
meth(sibling)
def setRootIndex(self, index):
self.__openPersistentEditors(self.rootIndex(), False)
if index.isValid():
self.__userRoot = True
super(TreeView, self).setRootIndex(index)
else:
self.__userRoot = False
self.__updateUniqueGroupVisibility()
self.__openPersistentEditors(self.rootIndex(), True)
def setModel(self, model):
if self.model():
try:
......
......@@ -34,7 +34,7 @@ from collections import namedtuple
from silx.gui import qt as Qt
from .ProcessWidget import ProcessWidget, ProcessWidgetEvent
# from .ProcessWidget import ProcessWidget, ProcessWidgetEvent
from ..Widgets import (AcqParamsWidget,
AdjustedLineEdit,
AdjustedPushButton)
......@@ -160,11 +160,17 @@ class ConversionParamsWidget(Qt.QWidget):
self.__qsize_z_edit.setText(str(int(qspace_size[2])))
class RecipSpaceWidgetEvent(ProcessWidgetEvent):
RecipSpaceEventData = namedtuple('RecipSpaceEventData', ['qspaceH5'])
# class RecipSpaceWidgetEvent(ProcessWidgetEvent):
# RecipSpaceEventData = namedtuple('RecipSpaceEventData', ['qspaceH5'])
class RecipSpaceWidget(ProcessWidget):
# class RecipSpaceWidget(ProcessWidget):
class RecipSpaceWidget(Qt.QDialog):
sigProcessDone = Qt.Signal(object)
(StatusUnknown, StatusInit,
StatusRunning, StatusCompleted,
StatusAborted, StatusCanceled) = StatusList = range(6)
__sigConvertDone = Qt.Signal()
......@@ -177,9 +183,10 @@ class RecipSpaceWidget(ProcessWidget):
**kwargs):
super(RecipSpaceWidget, self).__init__(**kwargs)
self.__central = Qt.QWidget()
self.setCentralWidget(self.__central)
topLayout = Qt.QGridLayout(self.__central)
self.__status = RecipSpaceWidget.StatusInit
# self.__central = Qt.QWidget()
# self.setCentralWidget(self.__central)
topLayout = Qt.QGridLayout(self)
self.__rectRoi = rect_roi
......@@ -469,29 +476,39 @@ class RecipSpaceWidget(ProcessWidget):
self.__converter = converter
procDialog = _ConversionProcessDialog(converter, parent=self, **kwargs)
procDialog.accepted.connect(partial(
self.__convertDone, status=ProcessWidget.StatusCompleted))
self.__convertDone, status=RecipSpaceWidget.StatusCompleted))
procDialog.rejected.connect(partial(
self.__convertDone, status=ProcessWidget.StatusAborted))
self._setStatus(ProcessWidget.StatusRunning)
self.__convertDone, status=RecipSpaceWidget.StatusAborted))
self._setStatus(self.StatusRunning)
procDialog.exec_()
def __convertDone(self, status=None):
self._setStatus(status)
if status == ProcessWidget.StatusCompleted:
if status == RecipSpaceWidget.StatusCompleted:
self.__qspaceH5 = self.__widgets.output_file_edit.text()
self.hide()
else:
self.__qspaceH5 = None
processedData = self._processData()
evtData = RecipSpaceWidgetEvent.RecipSpaceEventData(
qspaceH5=processedData)
self._emitEvent(RecipSpaceWidgetEvent(self, evtData))
print 'DONE', processedData
self.sigProcessDone.emit(processedData)
# evtData = RecipSpaceWidgetEvent.RecipSpaceEventData(
# qspaceH5=processedData)
# self._emitEvent(RecipSpaceWidgetEvent(self, evtData))
qspaceH5 = property(lambda self: self.__qspaceH5)
def _processData(self):
return self.qspaceH5
status = property(lambda self: self.__status)
def _setStatus(self, status):
if status not in RecipSpaceWidget.StatusList:
raise ValueError('Unknown status value : {0}.'
''.format(status))
self.__status = status
def __acqParamChkBnStateChanged(self, state):
"""
Sets the current acquisition parameters widget shown
......@@ -519,7 +536,7 @@ class RecipSpaceWidget(ProcessWidget):
widgets.output_file_edit.setText('')
self._setStatus(ProcessWidget.StatusInit)
self._setStatus(RecipSpaceWidget.StatusInit)
def __pickOutputFile(self, checked):
"""
......
......@@ -53,22 +53,12 @@ class IntensityItem(ProjectItem):
@property
def entry(self):
return self.path.rpartition('/')[-1]
@property
def name(self):
entry = self.entry
angle = self.xsocsH5.scan_angle(entry)
if angle is not None:
return str(angle)
return entry
return self.path.rsplit('/')[-1]
def getScatterData(self):
entry = self.entry
if entry == 'Total':
entry = self.xsocsH5.entries[0]
intensity = self._get_array_data(self.path)
scanPositions = self.xsocsH5.scan_positions(entry)
scanPositions = self.projectRoot().positions(entry)
return intensity, scanPositions
......@@ -86,7 +76,10 @@ class IntensityGroup(ProjectItem):
for entry in entries[1:]:
intensity += self._get_array_data(path_tpl.format(entry))
itemPath = self.path + '/Total'
item = IntensityItem(self.filename, itemPath, mode=self.mode, data=intensity)
IntensityItem(self.filename,
itemPath,
mode=self.mode,
data=intensity)
def getScatterData(self):
entry = self.xsocsH5.entries()[0]
......
......@@ -62,27 +62,42 @@ class ProjectItem(XsocsH5Base):
self.__gui = weakref.ref(gui) if gui is not None else None
# TODO : improve (maybe check if attributes are already present
if nodePath == '/':
self.__setXsocsAttributes(self.XsocsClass, processLevel)
if self._path_exists(nodePath):
self._loadItem()
else:
with self._get_file() as h5f:
if data is not None:
h5f[nodePath] = data