Commit 63638060 authored by Carsten Richter's avatar Carsten Richter

Merge branch 'qspace-stack-view' into 'master'

Qspace stack view

Closes #49 and #30

See merge request !72
parents 8a7810ca 4138114f
Pipeline #4601 passed with stages
in 3 minutes and 32 seconds
......@@ -30,13 +30,14 @@ __license__ = "MIT"
__date__ = "15/09/2016"
from collections import OrderedDict
import logging
import numpy as np
from matplotlib import cm
from silx.gui import qt as Qt
from ...io.QSpaceH5 import QSpaceH5
from ...util import bin_centers_to_range_step
from ..widgets.Containers import GroupBox
from ..widgets.RoiAxisWidget import RoiAxisWidget
......@@ -45,6 +46,9 @@ from ..widgets.Input import StyledLineEdit
from ...process.fit.peak_fit import PeakFitter, FitTypes
_logger = logging.getLogger(__name__)
class Roi3DSelectorWidget(Qt.QWidget):
"""
Widget displaying three RoiAxisWidgets, one for each axis.
......@@ -72,17 +76,17 @@ class Roi3DSelectorWidget(Qt.QWidget):
grpBox.toggled.connect(self.sigRoiToggled)
grpLayout = Qt.QVBoxLayout(grpBox)
xRoiWid = self.__xRoiWid = RoiAxisWidget('X')
yRoiWid = self.__yRoiWid = RoiAxisWidget('Y')
zRoiWid = self.__zRoiWid = RoiAxisWidget('Z')
self.__xRoiWid = RoiAxisWidget('X')
self.__yRoiWid = RoiAxisWidget('Y')
self.__zRoiWid = RoiAxisWidget('Z')
grpLayout.addWidget(xRoiWid)
grpLayout.addWidget(yRoiWid)
grpLayout.addWidget(zRoiWid)
grpLayout.addWidget(self.__xRoiWid)
grpLayout.addWidget(self.__yRoiWid)
grpLayout.addWidget(self.__zRoiWid)
xRoiWid.sigSliderMoved.connect(self.__slotSliderMoved)
yRoiWid.sigSliderMoved.connect(self.__slotSliderMoved)
zRoiWid.sigSliderMoved.connect(self.__slotSliderMoved)
self.__xRoiWid.sigSliderMoved.connect(self.__slotSliderMoved)
self.__yRoiWid.sigSliderMoved.connect(self.__slotSliderMoved)
self.__zRoiWid.sigSliderMoved.connect(self.__slotSliderMoved)
layout.addWidget(grpBox)
......@@ -96,15 +100,15 @@ class Roi3DSelectorWidget(Qt.QWidget):
sender = self.sender()
if sender == self.__xRoiWid:
xState = sliderEvt
yState = self.__yRoiWid.slider().getSliderState()
zState = self.__zRoiWid.slider().getSliderState()
yState = self.__yRoiWid.getSliderState()
zState = self.__zRoiWid.getSliderState()
elif sender == self.__yRoiWid:
xState = self.__xRoiWid.slider().getSliderState()
xState = self.__xRoiWid.getSliderState()
yState = sliderEvt
zState = self.__zRoiWid.slider().getSliderState()
zState = self.__zRoiWid.getSliderState()
elif sender == self.__zRoiWid:
xState = self.__xRoiWid.slider().getSliderState()
yState = self.__yRoiWid.slider().getSliderState()
xState = self.__xRoiWid.getSliderState()
yState = self.__yRoiWid.getSliderState()
zState = sliderEvt
elif sender == self.__grpBox:
return
......@@ -186,9 +190,9 @@ class FitWidget(Qt.QWidget):
layout = Qt.QGridLayout(self)
self.__roiWidget = roiWidget = Roi3DSelectorWidget()
self.__roiWidget = Roi3DSelectorWidget()
layout.addWidget(roiWidget)
layout.addWidget(self.__roiWidget)
fileLayout = Qt.QHBoxLayout()
self.__fileEdit = fileEdit = StyledLineEdit(nChar=20, readOnly=True)
......@@ -230,14 +234,18 @@ class FitWidget(Qt.QWidget):
statusLabel.setFrameStyle(Qt.QFrame.Panel | Qt.QFrame.Sunken)
layout.addWidget(statusLabel, 4, 0)
# Set sliders range and step
with qspaceH5:
qx = qspaceH5.qx
qy = qspaceH5.qy
qz = qspaceH5.qz
roiWidget.xSlider().setRange([qx[0], qx[-1]])
roiWidget.ySlider().setRange([qy[0], qy[-1]])
roiWidget.zSlider().setRange([qz[0], qz[-1]])
for slider, binCenters in ((self.roiWidget().xSlider(), qx),
(self.roiWidget().ySlider(), qy),
(self.roiWidget().zSlider(), qz)):
slider.setNumberOfSteps(len(binCenters) + 1)
min_, max_ = bin_centers_to_range_step(binCenters)[:2]
slider.setRange(min_, max_)
self.__sigFitDone.connect(self.__slotFitDone)
......@@ -265,13 +273,10 @@ class FitWidget(Qt.QWidget):
y_sum = cube_sum_z.sum(axis=0)
x_sum = cube_sum_z.sum(axis=1)
colors = cm.jet(np.arange(255))
cmap = [Qt.QColor.fromRgbF(*c).rgba() for c in colors]
roiWidget = self.__roiWidget
roiWidget.xSlider().setSliderProfile(x_sum, colormap=cmap)
roiWidget.ySlider().setSliderProfile(y_sum, colormap=cmap)
roiWidget.zSlider().setSliderProfile(z_sum, colormap=cmap)
for profile, slider in ((x_sum, self.roiWidget().xSlider()),
(y_sum, self.roiWidget().ySlider()),
(z_sum, self.roiWidget().zSlider())):
slider.setGroovePixmapFromProfile(profile, colormap='jet')
def setOutputFile(self, outputFile):
self.__outputFile = outputFile
......@@ -322,11 +327,18 @@ class FitWidget(Qt.QWidget):
self.__progBar.setValue(0)
fitType = FitWidget.FitTypes[self.__fitTypeCb.currentText()]
if self.__roiWidget.isActive():
x0, x1 = self.__roiWidget.xSlider().getSliderIndices()
y0, y1 = self.__roiWidget.ySlider().getSliderIndices()
z0, z1 = self.__roiWidget.zSlider().getSliderIndices()
roiIndices = [[x0, x1 + 1], [y0, y1 + 1], [z0, z1 + 1]]
if self.roiWidget().isActive():
x0, x1 = self.roiWidget().xSlider().getPositions()
y0, y1 = self.roiWidget().ySlider().getPositions()
z0, z1 = self.roiWidget().zSlider().getPositions()
if x0 == x1 or y0 == y1 or z0 == z1:
message = "QSpace ROI is void: cannot perform fit"
_logger.error(message)
Qt.QMessageBox.critical(self, "QSpace ROI Error", message)
self.__lock(False)
return
roiIndices = [[x0, x1], [y0, y1], [z0, z1]]
else:
roiIndices = None
......@@ -358,7 +370,7 @@ class FitWidget(Qt.QWidget):
def __lock(self, lock):
enable = not lock
self.__roiWidget.setEnabled(enable)
self.roiWidget().setEnabled(enable)
self.__fitTypeCb.setEnabled(enable)
self.__runButton.setEnabled(enable)
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -30,11 +30,18 @@ __license__ = "MIT"
__date__ = "15/09/2016"
from collections import namedtuple
from silx.gui import qt as Qt
from ..widgets.RangeSlider import RangeSlider
from ..silx_imports.RangeSlider import RangeSlider
from ..widgets.Input import StyledLineEdit, FixedSizeLabel
RangeSliderState = namedtuple('RangeSliderState', ['left', 'right',
'leftIndex', 'rightIndex'])
class RoiAxisWidget(Qt.QWidget):
"""
Widget with a double slider and two line edit
......@@ -42,9 +49,7 @@ class RoiAxisWidget(Qt.QWidget):
"""
sigSliderMoved = Qt.Signal(object)
""" Signal triggered when the slider is moved. Equivalent to connecting
directly to the sliders sigSliderMoved signal.
"""
""" Signal triggered when the slider values has changed."""
def slider(self):
"""
......@@ -65,7 +70,7 @@ class RoiAxisWidget(Qt.QWidget):
qLabel = FixedSizeLabel(nChar=1)
qLabel.setFrameStyle(Qt.QFrame.NoFrame | Qt.QFrame.Plain)
qLabel.setText(label)
slider = self.__slider = RangeSlider()
self.__slider = RangeSlider()
leftEdit = self.__leftEdit = StyledLineEdit(nChar=7)
leftEditValidator = Qt.QDoubleValidator(leftEdit)
......@@ -81,35 +86,43 @@ class RoiAxisWidget(Qt.QWidget):
rightEdit.editingFinished.connect(self.__rightEditingFinished)
layout.addWidget(qLabel, 0, 0)
layout.addWidget(slider, 0, 1, 1, 2)
layout.addWidget(self.__slider, 0, 1, 1, 2)
layout.addWidget(leftEdit, 1, 1)
layout.addWidget(rightEdit, 1, 2)
layout.setColumnStretch(3, 1)
slider.sigSliderMoved.connect(self.__sliderMoved)
slider.sigSliderMoved.connect(self.sigSliderMoved)
self.__slider.sigValueChanged.connect(self.__sliderValueChanged)
def getSliderState(self):
"""Returns current slider state
:rtype: RangeSliderState
"""
firstValue, secondValue = self.slider().getValues()
firstPos, secondPos = self.slider().getPositions()
state = RangeSliderState(left=firstValue,
right=secondValue,
leftIndex=firstPos,
rightIndex=secondPos)
return state
def __leftEditingFinished(self):
"""Handle left line edit editing finished"""
self.__slider.setSliderValue('left', float(self.__leftEdit.text()))
# leftEdit gets updated through sigSliderMoved
self.__slider.setFirstValue(float(self.__leftEdit.text()))
def __rightEditingFinished(self):
"""Handle right line edit editing finished"""
self.__slider.setSliderValue('right', float(self.__rightEdit.text()))
# rightEdit gets updated through sigSliderMoved
self.__slider.setSecondValue(float(self.__rightEdit.text()))
def __sliderMoved(self, event):
"""
Slot triggered when one of the slider is moved. Updates the
line edits.
:param event:
:return:
"""
self.__leftEdit.setText('{0:6g}'.format(event.left))
self.__rightEdit.setText('{0:6g}'.format(event.right))
def __sliderValueChanged(self, first, second):
"""Slot triggered when one of the slider is moved.
Updates the line edits and emits sigSliderMoved.
if __name__ == '__main__':
pass
:param float first:
:param float second:
"""
self.__leftEdit.setText('{0:6g}'.format(first))
self.__rightEdit.setText('{0:6g}'.format(second))
self.sigSliderMoved.emit(self.getSliderState())
......@@ -47,7 +47,7 @@ from silx.math.histogram import Histogramnd
from silx.gui.plot.matplotlib import Colormap
from ..widgets.Containers import GroupBox
from ..widgets.RangeSlider import RangeSlider
from ..silx_imports.RangeSlider import RangeSlider
from ..widgets.PointWidget import PointWidget
from ...gui.icons import getQIcon as getKmapIcon
from ..widgets.Input import StyledLineEdit
......@@ -193,8 +193,8 @@ class XsocsPlot2DColorDialog(Qt.QDialog):
grpBox = GroupBox('Range')
grpBoxLayout = Qt.QGridLayout(grpBox)
self.__rngSlider = rngSlider = RangeSlider()
grpBoxLayout.addWidget(rngSlider, 0, 0, 1, 2)
self.__rngSlider = RangeSlider()
grpBoxLayout.addWidget(self.__rngSlider, 0, 0, 1, 2)
self.__filledProfile = filledProfile = ColorFilledProfile()
filledProfile.setFixedHeight(100)
......@@ -218,7 +218,8 @@ class XsocsPlot2DColorDialog(Qt.QDialog):
self.__setupWidgets()
rngSlider.sigSliderMoved.connect(self.__rngSliderMoved)
self.__rngSlider.sigValueChanged.connect(
self.__rangeSliderValueChanged)
def __updateColorMap(self, colormap, minVal, maxVal, nColors):
"""
......@@ -251,20 +252,20 @@ class XsocsPlot2DColorDialog(Qt.QDialog):
self.__setColormapPixmap()
self.__applyColormap()
def __rngSliderMoved(self, event):
"""
Slot for the sliderMoved signal.
:param event:
:return:
def __rangeSliderValueChanged(self, first, second):
"""Slot for the range slider sigValueChanged signal.
:param float first:
:param float second:
"""
blockedMin = self.__minEdit.blockSignals(True)
blockedMax = self.__maxEdit.blockSignals(True)
self.__minEdit.setText('{0:6g}'.format(event.left))
self.__maxEdit.setText('{0:6g}'.format(event.right))
self.__minEdit.setText('{0:6g}'.format(first))
self.__maxEdit.setText('{0:6g}'.format(second))
colormap = self.__colormap
self.__updateColorMap(colormap.colormap,
event.left,
event.right,
first,
second,
colormap.nColors)
self.__minEdit.blockSignals(blockedMin)
self.__maxEdit.blockSignals(blockedMax)
......@@ -277,9 +278,7 @@ class XsocsPlot2DColorDialog(Qt.QDialog):
"""
minVal = float(self.__minEdit.text())
maxVal = float(self.__maxEdit.text())
blocked = self.__rngSlider.blockSignals(True)
self.__rngSlider.setSliderValues(minVal, maxVal)
self.__rngSlider.blockSignals(blocked)
self.__rngSlider.setValues(minVal, maxVal)
colormap = self.__colormap
self.__updateColorMap(colormap.colormap,
minVal,
......@@ -308,21 +307,6 @@ class XsocsPlot2DColorDialog(Qt.QDialog):
"""
return self.__curve
def __setSliderProfile(self):
"""
Draws the histogram profile on the slider.
:return:
"""
histo = self.__histogram
rngSlider = self.__rngSlider
colormap = self.__colormap
pixmap = _arrayToPixmap(histo.histo,
cm.jet,
256)
rngSlider.setSliderPixmap(pixmap)
def __setupWidgets(self):
"""
Initializes the colorbars, histogram, etc...
......@@ -339,16 +323,15 @@ class XsocsPlot2DColorDialog(Qt.QDialog):
return
histo = self.__histogram
rngSlider = self.__rngSlider
colormap = self.__colormap
self.__minEdit.setText('{0:6g}'.format(colormap.minVal))
self.__maxEdit.setText('{0:6g}'.format(colormap.maxVal))
rngSlider.setRange([histo.edges[0][0], histo.edges[0][-1]])
rngSlider.setSliderValues(colormap.minVal, colormap.maxVal)
self.__setSliderProfile()
self.__rngSlider.setRange(histo.edges[0][0], histo.edges[0][-1])
self.__rngSlider.setNumberOfSteps(256)
self.__rngSlider.setValues(colormap.minVal, colormap.maxVal)
self.__rngSlider.setGroovePixmapFromProfile(histo.histo, 'jet')
self.__drawHistogram()
def __drawHistogram(self):
......
......@@ -296,7 +296,7 @@ class XsocsH5(XsocsH5Base):
for name, node in h5_file[path].items():
if (isinstance(node, _h5py.Dataset) and
node.dtype.kind in 'iuf' and
node.ndim == 1 and
len(node.shape) == 1 and
node.shape[0] == nb_images):
# Only get (u)int and float datasets
# with same number of values as number of images
......
......@@ -23,7 +23,7 @@
#
# ###########################################################################*/
from __future__ import absolute_import
from __future__ import absolute_import, division
__authors__ = ["D. Naudet"]
__license__ = "MIT"
......@@ -41,6 +41,7 @@ import multiprocessing.sharedctypes as mp_sharedctypes
import numpy as np
import xrayutilities as xu
from ...util import bin_centers_to_range_step
from ...util.medianfilter import medfilt2d
from ...util.histogramnd_lut import histogramnd_get_lut, histogramnd_from_lut
# from silx.math import histogramnd
......@@ -777,17 +778,13 @@ class QSpaceConverter(object):
step_y = (qy_max - qy_min) / (ny - 1.)
step_z = (qz_max - qz_min) / (nz - 1.)
bins_rng_x = ([qx_min - step_x / 2., qx_min +
(qx_max - qx_min + step_x) - step_x / 2.])
bins_rng_y = ([qy_min - step_y / 2., qy_min +
(qy_max - qy_min + step_y) - step_y / 2.])
bins_rng_z = ([qz_min - step_z / 2., qz_min +
(qz_max - qz_min + step_z) - step_z / 2.])
bins_rng = [bins_rng_x, bins_rng_y, bins_rng_z]
qx_bin_centers = qx_min + step_x * np.arange(0, nx, dtype=np.float64)
qy_bin_centers = qy_min + step_y * np.arange(0, ny, dtype=np.float64)
qz_bin_centers = qz_min + step_z * np.arange(0, nz, dtype=np.float64)
qx_idx = qx_min + step_x * np.arange(0, nx, dtype=np.float64)
qy_idx = qy_min + step_y * np.arange(0, ny, dtype=np.float64)
qz_idx = qz_min + step_z * np.arange(0, nz, dtype=np.float64)
bins_rng = [bin_centers_to_range_step(qx_bin_centers)[:2],
bin_centers_to_range_step(qy_bin_centers)[:2],
bin_centers_to_range_step(qz_bin_centers)[:2]]
# TODO : on windows we may be forced to use shared memory
# TODO : find why we use more memory when using shared arrays
......@@ -855,9 +852,9 @@ class QSpaceConverter(object):
sample_roi,
sample_x[sample_indices],
sample_y[sample_indices],
qx_idx,
qy_idx,
qz_idx,
qx_bin_centers,
qy_bin_centers,
qz_bin_centers,
histo,
selected_entries=entries,
discarded_entries=discarded_entries,
......
......@@ -22,7 +22,29 @@
# THE SOFTWARE.
#
# ###########################################################################*/
"""This module miscellaneous convenient functions"""
from __future__ import absolute_import, division
__authors__ = ["D. Naudet"]
__license__ = "MIT"
__date__ = "01/03/2016"
import numpy
def bin_centers_to_range_step(centers):
"""Convert histogram bin centers (as stored in hdf5) to bin range and step
This assumes sorted bins of the same size.
:param numpy.ndarray centers: 1D array of bin centers
:return: Bin edges min, max, step
:rtype: List[float]
"""
centers = numpy.array(centers, copy=False)
nbins = centers.shape[0] - 1
min_, max_ = numpy.min(centers), numpy.max(centers)
step = (max_ - min_) / nbins
return min_ - step/2., max_ + step/2., step
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