Commit 22b2b6a0 authored by Damien Naudet's avatar Damien Naudet
Browse files

Lots of refactoring.

parent e1e4b4f1
#!/users/naudet/bin/python_id01
# 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
import os
import time
import h5py
import numpy as np
from silx.gui import qt as Qt
from silx.gui import plot
from matplotlib import cm
from silx.gui import qt as Qt
from plot3d.IsosurfaceView import IsosurfaceView
from kmap.io import XsocsH5
from kmap.gui.MergeWidget import MergeWidget
from kmap.gui.RecipSpaceWidget import RecipSpaceWidget
from kmap.gui.Widgets import (AcqParamsWidget,
_AdjustedLabel,
_AdjustedLineEdit,
_AdjustedPushButton)
from kmap.gui.XsocsWorkspace import XsocsWorkspace, XsocsWorkspaceView
from ..io import XsocsH5
from .MergeWidget import MergeWidget
from .RecipSpaceWidget import RecipSpaceWidget
from .Widgets import (AcqParamsWidget,
_AdjustedLineEdit,
_AdjustedPushButton)
from .project.XsocsProject import XsocsProject
from kmap.gui.PlotWidgets import IntensityWindow
from .PlotWidgets import IntensityWindow
_COMPANY_NAME = 'ESRF'
_APP_NAME = 'XSOCS'
class XsocsMainWindow(Qt.QMainWindow):
class XsocsGui(Qt.QMainWindow):
__firstInitSig = Qt.Signal()
def __init__(self,
parent=None,
workspaceH5File=None):
super(XsocsMainWindow, self).__init__(parent)
super(XsocsGui, self).__init__(parent)
self.__pw = None
mdiArea = Qt.QMdiArea()
......@@ -50,18 +71,18 @@ class XsocsMainWindow(Qt.QMainWindow):
self.__greeterDiag = None
self.__readSettings()
def __plotSlot(self, plotType, index):
def __dataDockSlot(self, event):
# TODO : store the plot window in a dictionary + weakref w delete
# callback when object is destroyed
plotData = self.__workspace.model().plotData(plotType, index)
if plotData['type'] == 'scatter':
plotWin = self.showScatterPlot(*plotData['data'])
elif plotData['type'] == 'image':
plotWin = self.showImage(*plotData['data'])
else:
print 'Unknown plotType : {0}'.format(plotType)
def showScatterPlot(self, x, y, data, title=''):
# callback when object is destroyed
type = event.type
plotData = event.plotData()
x, y, data = plotData
if type == 'scatter':
self.showScatterPlot(x, y, data)
elif type == 'image':
self.showImage(x, y, data)
def showScatterPlot(self, x, y, data):
mdi = self.centralWidget()
min_, max_ = data.min(), data.max()
colormap = cm.jet
......@@ -74,7 +95,7 @@ class XsocsMainWindow(Qt.QMainWindow):
plotWin.show()
return plotWin
def showImage(self, x, y, data, title=''):
def showImage(self, x, y, data):
mdi = self.centralWidget()
plotWin = IntensityWindow(aspectRatio=True)
plotWin.setAttribute(Qt.Qt.WA_DeleteOnClose)
......@@ -104,14 +125,14 @@ class XsocsMainWindow(Qt.QMainWindow):
self.__workspace.addQSpaceH5(widget.qspaceH5)
def showEvent(self, event):
super(XsocsMainWindow, self).showEvent(event)
super(XsocsGui, self).showEvent(event)
if not self.__widget_setup:
self.__firstInitSig.emit()
self.__widget_setup = True
def closeEvent(self, event):
self.__writeSettings()
super(XsocsMainWindow, self).closeEvent(event)
super(XsocsGui, self).closeEvent(event)
def __showGreeter(self):
if self.__startupWorkspaceH5File is None:
......@@ -120,8 +141,8 @@ class XsocsMainWindow(Qt.QMainWindow):
self.__actions['new'],
self.__actions['import'])
greeterDiag.setAttribute(Qt.Qt.WA_DeleteOnClose)
#greeterDiag.setWindowFlags(Qt.Qt.WindowStaysOnTopHint |
#greeterDiag.windowFlags())
# greeterDiag.setWindowFlags(Qt.Qt.WindowStaysOnTopHint |
# greeterDiag.windowFlags())
greeterDiag.rejected.connect(self.close)
greeterDiag.show()
self.__greeterDiag = greeterDiag
......@@ -161,7 +182,7 @@ class XsocsMainWindow(Qt.QMainWindow):
if ans == Qt.QMessageBox.No:
return False
wkSpace = XsocsWorkspace(ws_file, mode=mode)
wkSpace = XsocsProject(ws_file, mode=mode)
if xsocsH5 is not None:
wkSpace.xsocsFile = xsocsH5
self.__sessionDock.widget().setXsocsWorkspace(wkSpace)
......@@ -204,7 +225,6 @@ class XsocsMainWindow(Qt.QMainWindow):
quitAct.setShortcuts(Qt.QKeySequence.Quit)
quitAct.setStatusTip('Exit the application')
quitAct.triggered.connect(Qt.qApp.closeAllWindows)
#connect(exitAct, SIGNAL(triggered()), qApp, SLOT(closeAllWindows()))
actions['quit'] = quitAct
# "about" action
......@@ -254,11 +274,13 @@ class XsocsMainWindow(Qt.QMainWindow):
fileToolBar.addAction(actions['open'])
fileToolBar.addAction(actions['new'])
fileToolBar.addAction(actions['import'])
toolBars[fileToolBar.windowTitle()] = fileToolBar
viewToolBar = self.addToolBar('View')
viewToolBar.setObjectName('viewToolBar')
viewToolBar.addAction(actions['sessionDock'])
viewToolBar.addAction(actions['dataDock'])
toolBars[viewToolBar.windowTitle()] = viewToolBar
def __createDocks(self):
self.__sessionDock = sessionDock = Qt.QDockWidget('Infos')
......@@ -270,8 +292,8 @@ class XsocsMainWindow(Qt.QMainWindow):
plotDataDock.setWidget(PlotDataWidget())
plotDataDock.setObjectName('DataDock')
self.addDockWidget(Qt.Qt.LeftDockWidgetArea, plotDataDock)
plotDataDock.widget().plotSig.connect(self.__plotSlot,
Qt.Qt.QueuedConnection)
plotDataDock.widget().sigItemEvent.connect(self.__dataDockSlot,
Qt.Qt.QueuedConnection)
def __writeSettings(self):
settings = Qt.QSettings(_COMPANY_NAME, _APP_NAME)
......@@ -325,25 +347,31 @@ class XsocsMainWindow(Qt.QMainWindow):
class PlotDataWidget(Qt.QWidget):
plotSig = Qt.Signal(str, object)
sigItemEvent = Qt.Signal(object)
def __init__(self, parent=None):
super(PlotDataWidget, self).__init__(parent=parent)
super(PlotDataWidget, self).__init__(parent)
layout = Qt.QGridLayout(self)
self.__xsocsProject = None
self.__treeView = treeView = XsocsWorkspaceView()
self.__treeView = treeView = Qt.QTreeView()
layout.addWidget(treeView, 0, 0)
treeView.plotSig.connect(self.plotSig)
def setXsocsWorkspace(self, xsocsWorkspace):
self.__xsocsWorkspace = xsocsWorkspace
self.__xsocsProject = xsocsWorkspace
self.__setupWidget()
def __setupWidget(self):
if self.__xsocsWorkspace is not None:
treeView = self.__treeView
treeView.setModel(self.__xsocsWorkspace.model())
treeView.expand(treeView.model().index(0, 0))
# TODO : better
if self.__xsocsProject is not None:
if self.__treeView:
self.layout().takeAt(0)
self.__treeView.deleteLater()
self.__treeView = None
view = self.__xsocsProject.view(parent=self)
view.sigItemEvent.connect(self.sigItemEvent)
self.layout().addWidget(view)
self.__treeView = view
# ####################################################################
......@@ -353,10 +381,11 @@ class PlotDataWidget(Qt.QWidget):
class SessionWidget(Qt.QWidget):
def __init__(self, parent=None, h5_f=None):
super(SessionWidget, self).__init__(parent=parent)
super(SessionWidget, self).__init__(parent)
layout = Qt.QGridLayout(self)
self.__xsocsH5 = None
self.__xsocsProject = None
# #########
# file name
......@@ -378,15 +407,19 @@ class SessionWidget(Qt.QWidget):
# number of angles
rowIdx = 0
self.__anglesText = anglesText = _AdjustedLineEdit(3, read_only=True)
gridLayout.addWidget(Qt.QLabel('# angles :'), rowIdx, 0, alignment=Qt.Qt.AlignLeft)
gridLayout.addWidget(anglesText, rowIdx, 1, alignment=Qt.Qt.AlignLeft)
gridLayout.addWidget(Qt.QLabel('# angles :'),
rowIdx, 0, alignment=Qt.Qt.AlignLeft)
gridLayout.addWidget(anglesText,
rowIdx, 1, alignment=Qt.Qt.AlignLeft)
rowIdx += 1
rowIdx += 1
self.__nImgText = nImgText = _AdjustedLineEdit(5, read_only=True)
gridLayout.addWidget(Qt.QLabel('Images :'), rowIdx, 0, alignment=Qt.Qt.AlignLeft)
gridLayout.addWidget(Qt.QLabel('Images :'),
rowIdx, 0, alignment=Qt.Qt.AlignLeft)
gridLayout.addWidget(nImgText, rowIdx, 1, alignment=Qt.Qt.AlignLeft)
self.__imgSizeText = imgSizeText = _AdjustedLineEdit(10, read_only=True)
gridLayout.addWidget(Qt.QLabel('Size :'), rowIdx, 2, alignment=Qt.Qt.AlignLeft)
gridLayout.addWidget(Qt.QLabel('Size :'),
rowIdx, 2, alignment=Qt.Qt.AlignLeft)
gridLayout.addWidget(imgSizeText, rowIdx, 3, alignment=Qt.Qt.AlignLeft)
gridLayout.setColumnStretch(gridLayout.columnCount(), 1)
......@@ -408,13 +441,13 @@ class SessionWidget(Qt.QWidget):
layout.setRowStretch(layout.rowCount(), 1)
def setXsocsWorkspace(self, xsocsWorkspace):
self.__xsocsWorkspace = xsocsWorkspace
self.__xsocsProject = xsocsWorkspace
self.__setupWidget()
def __setupWidget(self):
acqParamsWid = self.__acqParamsWid
xsocsH5 = self.__xsocsWorkspace.xsocsH5
xsocsH5 = self.__xsocsProject.xsocsH5
acqParamsWid.clear()
filename = ''
......@@ -461,7 +494,7 @@ class _GreeterDialog(Qt.QDialog):
openAction,
loadAction,
importAction):
super(_GreeterDialog, self).__init__(parent=parent)
super(_GreeterDialog, self).__init__(parent)
self.setModal(True)
layout = Qt.QGridLayout(self)
......@@ -490,7 +523,7 @@ class _WorkspaceDirDialog(Qt.QDialog):
parent=None,
nameHint=None,
**kwargs):
super(_WorkspaceDirDialog, self).__init__(parent=parent, **kwargs)
super(_WorkspaceDirDialog, self).__init__(parent, **kwargs)
self.setModal(True)
layout = Qt.QGridLayout(self)
......@@ -545,3 +578,20 @@ class _WorkspaceDirDialog(Qt.QDialog):
if __name__ == '__main__':
pass
# f = os.path.expanduser(
# '~/data/xsocs/results/kmap/psic_nano_20150314_fast_00007/qspace/gepoly200_004_qspace.h5')
# with h5py.File(f) as h5f:
# data = h5f['/data/qspace'][0]
# x = h5f['/bins_edges/x'][:]
# y = h5f['/bins_edges/y'][:]
# z = h5f['/bins_edges/z'][:]
# wid = Qt.QWidget(self)
# layout = Qt.QHBoxLayout(wid)
# threedwin = IsosurfaceView()
# layout.addWidget(threedwin)
# threedwin.setIsoLevel(80.)
# threedwin.setData(data, copy=True)
# self.centralWidget().addSubWindow(wid)
# wid.show()
# print 'shown', wid.isVisible()
This diff is collapsed.
......@@ -3,7 +3,7 @@ import sys
from silx.gui import qt as Qt
print('Using Qt {0}'.format(Qt.qVersion()))
from XsocsMainWindow import XsocsMainWindow
from XsocsGui import XsocsGui
from .MergeWidget import MergeWidget
from .RecipSpaceWidget import RecipSpaceWidget
......@@ -21,6 +21,6 @@ def conversion_window(*args, **kwargs):
def xsocs_main(*args, **kwargs):
app = Qt.QApplication(sys.argv)
mw = XsocsMainWindow(*args, **kwargs)
mw = XsocsGui(*args, **kwargs)
mw.show()
app.exec_()
# 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"
from functools import partial
from silx.gui import qt as Qt
import h5py
from silx.gui.hdf5 import Hdf5TreeModel
from silx.gui import icons
from . import ItemClassDef
from .ProjectItem import ProjectItem, ItemEvent
class HybridItemEvent(ItemEvent):
def plotData(self):
eventType = self.type
if eventType == 'scatter':
return self.item.getScatter()
if eventType == 'image':
return self.item.getImage()
return None
class HybridItemDelegate(Qt.QWidget):
sigEditorEvent = Qt.Signal(object)
def __init__(self, parent, option, index):
super(HybridItemDelegate, self).__init__(parent)
self.__index = Qt.QPersistentModelIndex(index)
layout = Qt.QHBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
icon = icons.getQIcon('item-1dim')
bn = Qt.QToolButton()
bn.setIcon(icon)
bn.clicked.connect(partial(self.__onClicked, type='scatter'))
layout.addWidget(bn, Qt.Qt.AlignLeft)
icon = icons.getQIcon('item-2dim')
bn = Qt.QToolButton()
bn.setIcon(icon)
bn.clicked.connect(partial(self.__onClicked, type='image'))
layout.addWidget(bn, Qt.Qt.AlignLeft)
layout.addStretch(1)
# self.setAutoFillBackground(True)
# layout.setSizeConstraint(Qt.QLayout.SetMinimumSize)
def __onClicked(self, checked, type=None):
obj = self.__index.data(Hdf5TreeModel.H5PY_OBJECT_ROLE)
instance = ProjectItem.load(obj.file.filename, obj.name)
event = HybridItemEvent(instance, type)
self.sigEditorEvent.emit(event)
def sizeHint(self):
print super(HybridItem, self).sizeHint()
return Qt.QSize(0, 0)
# def sizeHint(self):
# # TODO connect to cell resize
# if self.__sizeHint:
# return self.parent().sizeHint()
# # return Qt.QSize(self.__sizeHint)
# return super(XsocsProjectDataGroupDelegate, self).sizeHint()
@ItemClassDef('HybridItem', editor=HybridItemDelegate)
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))
def setImage(self, x, y, data):
self._commit(image=(x, y, data))
# def setImageFromScatter(self, xSlice, ySlice):
# self._commit(imageSlice=(xSlice, ySlice))
def getScatter(self):
with h5py.File(self.file, 'r') as h5f:
grp = h5f.get(self.path)
if grp is None:
return None
scatterPath = grp.attrs.get('XsocsScatter')
if scatterPath is None:
return None
scatterGrp = grp[scatterPath]
# TODO : checks
x = scatterGrp.get('x')
y = scatterGrp.get('y')
data = scatterGrp.get('data')
x = x and x[:]
y = y and y[:]
data = data and data[:]
return x, y, data
def getImage(self):
with h5py.File(self.file, 'r') as h5f:
grp = h5f.get(self.path)
if grp is None:
return None
imagePath = grp.attrs.get('XsocsImage')
if imagePath is None:
return None
imageGrp = grp[imagePath]
# TODO : checks
x = imageGrp.get('x')
y = imageGrp.get('y')
data = imageGrp.get('data')
if x:
if x.shape == ():
x = x[()]
if isinstance(x, h5py.RegionReference):
x = h5f[x][x]
else:
x = x[:]
if y.shape == ():
y = y[()]
if isinstance(y, h5py.RegionReference):
y = h5f[y][y]
else:
y = y[:]
reshape = imageGrp.attrs.get('XsocsShape')
data = data and data[:]
if reshape is not None:
data.shape = reshape
return x, y, data
def _commit(self,
scatter=None,
image=None,
cube=None,
imageSlice=None):
super(HybridItem, self)._commit()
# TODO : check if data already exists in file.
with h5py.File(self.file, 'a') as h5f:
grp = h5f.require_group(self.path)
if scatter:
x, y, data = scatter
scatterGrp = grp.require_group('scatter')
scatterGrp['x'] = x
scatterGrp['y'] = y
if data is not None:
grp['scatter/data'] = data
grp.attrs.update({'XsocsScatter': 'scatter'})
if image:
x, y, data = image
grp['image/data'] = data
grp['image/x'] = x
grp['image/y'] = y
grp.attrs.update({'XsocsImage': 'image'})
elif imageSlice:
xSlice, ySlice = imageSlice
dataSet = grp.get('scatter/data')
if dataSet is None:
raise ValueError('Cant convert scatter to image : '
'no data.')
xSet = grp.get('scatter/x')
ySet = grp.get('scatter/y')
x = xSet.regionref[xSlice]
y = ySet.regionref[ySlice]
xShape = xSet[x].shape
yShape = ySet[y].shape
if len(xShape) != 1 or len(yShape) != 1:
raise ValueError('Invalid slice shapes x:{0}, y:{1}.'
''.format(xShape, yShape))
imageGrp = grp.require_group('image')
imageGrp['x'] = x
imageGrp['y'] = y
imageGrp['data'] = dataSet
print imageGrp['x'], x
imageGrp.attrs.update({'XsocsShape': [yShape[0],
xShape[0]]})
grp.attrs.update({'XsocsImage': 'image'})
if cube:
grp.attrs.update({'XsocsCube': 'cube'})
# 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 h5py
_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, editor=None):
def inner(cls):
cls.itemName = itemName
cls.editor = editor