Commit 21afad1b authored by Damien Naudet's avatar Damien Naudet
Browse files

Refactoring : moved point selection to Xsocs2D

parent 1ad03bfb
......@@ -294,16 +294,7 @@ class DropPlotWidget(XsocsPlot2D):
self.setActiveCurveHandling(False)
self.setKeepDataAspectRatio(True)
self.setAcceptDrops(True)
self.sigPlotSignal.connect(self.__plotSignal)
def __plotSignal(self, event):
if event['event'] not in ('mouseClicked',):
return
if self.__legend is None:
return
x, y = event['x'], event['y']
self.sigSelected.emit((x, y))
self.setPointSelectionEnabled(True)
def dropEvent(self, event):
mimeData = event.mimeData()
......@@ -398,7 +389,7 @@ class FitView(Qt.QMainWindow):
yInverted=False)
grpLayout.addWidget(plot)
self.__plots.append(plot)
plot.sigSelected.connect(self.__plotSignal)
plot.sigPointSelected.connect(self.__plotSignal)
plot = DropPlotWidget(grid=False,
curveStyle=False,
......@@ -408,7 +399,7 @@ class FitView(Qt.QMainWindow):
yInverted=False)
grpLayout.addWidget(plot)
self.__plots.append(plot)
plot.sigSelected.connect(self.__plotSignal)
plot.sigPointSelected.connect(self.__plotSignal)
plot = DropPlotWidget(grid=False,
curveStyle=False,
......@@ -418,7 +409,7 @@ class FitView(Qt.QMainWindow):
yInverted=False)
grpLayout.addWidget(plot)
self.__plots.append(plot)
plot.sigSelected.connect(self.__plotSignal)
plot.sigPointSelected.connect(self.__plotSignal)
layout.addWidget(grpBox, 0, 1)
......@@ -474,20 +465,20 @@ class FitView(Qt.QMainWindow):
_initCentroid(self.__plots, fitH5.filename, entry, process)
def __plotSignal(self, point):
x, y = point
self.__plotFitResults(x, y)
# x, y = point.x, point.y
self.__plotFitResults(point.xIdx)
def __plotFitResults(self, x, y):
def __plotFitResults(self, xIdx):
# TODO : the values could/should be loaded when the widget is shown for the
# first time
with self.__fitH5 as fitH5:
sampleX = fitH5.scan_x(self.__entry)
sampleY = fitH5.scan_y(self.__entry)
xIdx = ((sampleX - x)**2 + (sampleY - y)**2).argmin()
x = sampleX[xIdx]
y = sampleY[xIdx]
# sampleX = fitH5.scan_x(self.__entry)
# sampleY = fitH5.scan_y(self.__entry)
#
# xIdx = ((sampleX - x)**2 + (sampleY - y)**2).argmin()
#
# x = sampleX[xIdx]
# y = sampleY[xIdx]
entry = self.__entry
......@@ -530,17 +521,17 @@ class FitView(Qt.QMainWindow):
# TODO : popup
raise ValueError('Unknown process {0}.'.format(process))
self.__setSelectedPosition(x, y)
def __setSelectedPosition(self, x, y):
"""Set the selected position.
:param float x:
:param float y:
"""
for plot in self.__plots:
plot.addXMarker(x, legend='Xselection', color='pink')
plot.addYMarker(y, legend='Yselection', color='pink')
# self.__setSelectedPosition(x, y)
# def __setSelectedPosition(self, x, y):
# """Set the selected position.
#
# :param float x:
# :param float y:
# """
# for plot in self.__plots:
# plot.addXMarker(x, legend='Xselection', color='pink')
# plot.addYMarker(y, legend='Yselection', color='pink')
# TODO : allow users to register plot functions associated with the kind
......
......@@ -98,41 +98,20 @@ class PlotIntensityMap(XsocsPlot2D):
roi=False, mask=False, fit=False)
self.setMinimumSize(150, 150)
# self.setKeepDataAspectRatio(True)
# self.setActiveCurveHandling(False)
self.setDataMargins(0.2, 0.2, 0.2, 0.2)
def setSelectedPosition(self, x, y):
"""Set the selected position.
:param float x:
:param float y:
"""
self.addXMarker(x, legend='Xselection', color='pink')
self.addYMarker(y, legend='Yselection', color='pink')
# def setSelectedPosition(self, x, y):
# """Set the selected position.
#
# :param float x:
# :param float y:
# """
# self.addXMarker(x, legend='Xselection', color='pink')
# self.addYMarker(y, legend='Yselection', color='pink')
def sizeHint(self):
return Qt.QSize(200, 200)
# def setPlotData(self, x, y, data):
# """Set the scatter plot.
#
# This is removing previous scatter plot
#
# :param numpy.ndarray x: X coordinates of the points
# :param numpy.ndarray y: Y coordinates of the points
# :param numpy.ndarray data: Values associated to points
# """
# min_, max_ = data.min(), data.max()
# colormap = cm.jet
# colors = colormap((data.astype(np.float64) - min_) / (max_ - min_))
# self.addCurve(x, y,
# legend='intensities',
# color=colors,
# symbol='s',
# linestyle='',
# resetzoom=False)
class ROIPlotIntensityMap(PlotIntensityMap):
"""Plot ROI intensities with an update button to compute it in a thread"""
......@@ -287,15 +266,18 @@ class QSpaceView(Qt.QMainWindow):
item = h5NodeToProjectItem(node)
self.__qspaceH5 = item.qspaceH5
# plot window displaying the intensity map
self.__plotWindow = plotWindow = PlotIntensityMap(parent=self)
plotWindow.setGraphTitle('Intensity Map')
plotWindow.setToolTip('Intensity Map integrated on whole QSpaces')
plotWindow.sigPlotSignal.connect(self.__plotSignal)
plotWindow.setPointSelectionEnabled(True)
plotWindow.sigPointSelected.connect(self.__pointSelected)
self.__roiPlotWindow = roiPlotWindow = ROIPlotIntensityMap(
parent=self, qspaceH5=item.qspaceH5)
roiPlotWindow.sigPlotSignal.connect(self.__plotSignal)
roiPlotWindow.setPointSelectionEnabled(True)
roiPlotWindow.sigPointSelected.connect(self.__pointSelected)
self.__planePlotWindow = planePlotWindow = CutPlanePlotWindow(self)
......@@ -310,7 +292,7 @@ class QSpaceView(Qt.QMainWindow):
qz = self.__qz = qspaceH5.qz
firstX = sampleX[0]
firstY = sampleX[1]
firstY = sampleY[1]
self.__node = node
......@@ -428,30 +410,35 @@ class QSpaceView(Qt.QMainWindow):
self.sigProcessApplied.emit(self.__node, roi)
def __plotSignal(self, event):
if event['event'] not in ('mouseClicked'):
return
x, y = event['x'], event['y']
def __pointSelected(self, point):
xIdx = point.xIdx
x = point.x
y = point.y
self.__showIsoView(x, y)
self.__showIsoView(x, y, xIdx)
def __showIsoView(self, x, y):
def __showIsoView(self, x, y, idx=None):
isoView = self.__view3d
item = h5NodeToProjectItem(self.__node)
with item.qspaceH5 as qspaceH5:
sampleX = qspaceH5.sample_x
sampleY = qspaceH5.sample_y
if self.sender() == self.__roiPlotWindow:
self.__plotWindow.selectPoint(x, y)
elif self.sender() == self.__roiPlotWindow:
self.__roiPlotWindow.selectPoint(x, y)
else:
self.__plotWindow.selectPoint(x, y)
self.__roiPlotWindow.selectPoint(x, y)
xIdx = ((sampleX - x)**2 + (sampleY - y)**2).argmin()
with self.__qspaceH5 as qspaceH5:
if idx is None:
sampleX = qspaceH5.sample_x
sampleY = qspaceH5.sample_y
x = sampleX[xIdx]
y = sampleY[xIdx]
idx = ((sampleX - x)**2 + (sampleY - y)**2).argmin()
self.__plotWindow.setSelectedPosition(x, y)
self.__roiPlotWindow.setSelectedPosition(x, y)
x = sampleX[idx]
y = sampleY[idx]
qspace = qspaceH5.qspace_slice(xIdx)
qspace = qspaceH5.qspace_slice(idx)
# Set scale and translation
# Do it before setting data as corresponding
......
# 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 .DataViewWidget import DataViewWidget, DataViewEvent
try:
from silx.gui.plot.ImageRois import ImageRoiManager
except ImportError:
# TODO remove this import once the ROIs are added to the silx release.
from ..silx_imports.ImageRois import ImageRoiManager
class RectRoiWidget(Qt.QWidget):
sigRoiApplied = Qt.Signal(object)
def __init__(self, roiManager, parent=None):
# TODO :
# support multiple ROIs then batch them
super(RectRoiWidget, self).__init__(parent)
layout = Qt.QHBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
style = Qt.QApplication.style()
icon = style.standardIcon(Qt.QStyle.SP_DialogCloseButton)
self.__discardBn = discardBn = Qt.QToolButton()
discardBn.setToolTip('Discard ROI')
discardBn.setStatusTip('Discard ROI')
discardBn.setIcon(icon)
discardBn.setEnabled(False)
layout.addWidget(discardBn)
discardBn.clicked.connect(self.__discardRoi)
icon = style.standardIcon(Qt.QStyle.SP_DialogApplyButton)
self.__applyBn = applyBn = Qt.QToolButton()
applyBn.setToolTip('Apply ROI')
applyBn.setStatusTip('Apply ROI')
applyBn.setIcon(icon)
applyBn.setEnabled(False)
layout.addWidget(applyBn)
applyBn.clicked.connect(self.__applyRoi)
layout.addWidget(Qt.QLabel('x='))
self._xEdit = edit = Qt.QLineEdit()
layout.addWidget(edit, stretch=0, alignment=Qt.Qt.AlignLeft)
layout.addWidget(Qt.QLabel('y='))
self._yEdit = edit = Qt.QLineEdit()
layout.addWidget(edit, stretch=0, alignment=Qt.Qt.AlignLeft)
self._wEdit = edit = Qt.QLineEdit()
layout.addWidget(Qt.QLabel('w='))
layout.addWidget(edit, stretch=0, alignment=Qt.Qt.AlignLeft)
self._hEdit = edit = Qt.QLineEdit()
layout.addWidget(Qt.QLabel('h='))
layout.addWidget(edit, stretch=0, alignment=Qt.Qt.AlignLeft)
fm = edit.fontMetrics()
padding = 2 * fm.width('0')
text = '0' * 8
width = fm.width(text) + padding
self._xEdit.setFixedWidth(width)
self._yEdit.setFixedWidth(width)
self._wEdit.setFixedWidth(width)
self._hEdit.setFixedWidth(width)
layout.setSizeConstraint(Qt.QLayout.SetMinimumSize)
# TODO : weakref
self.__roiManager = roiManager
roiManager.sigRoiDrawingFinished.connect(self.__roiDrawingFinished,
Qt.Qt.QueuedConnection)
roiManager.sigRoiRemoved.connect(self.__roiRemoved,
Qt.Qt.QueuedConnection)
roiManager.sigRoiMoved.connect(self.__roiMoved,
Qt.Qt.QueuedConnection)
def __discardRoi(self, checked):
self.__roiManager.clear()
def __applyRoi(self, checked):
# At the moment we only support one roi at a time.
roi = self.__roiManager.rois
roiItem = self.__roiManager.roiItem(roi[0])
xMin = roiItem.pos[0]
xMax = xMin + roiItem.width
yMin = roiItem.pos[1]
yMax = yMin + roiItem.height
self.sigRoiApplied.emit([xMin, xMax, yMin, yMax])
def __roiDrawingFinished(self, event):
self.__display(event['xdata'], event['ydata'])
self.__discardBn.setEnabled(True)
self.__applyBn.setEnabled(True)
def __clear(self):
self._xEdit.clear()
self._yEdit.clear()
self._wEdit.clear()
self._hEdit.clear()
def __display(self, xData, yData):
xMin, xMax = xData[0], xData[2]
if xMax < xMin:
xMin, xMax = xMax, xMin
yMin, yMax = yData[0], yData[1]
if yMax < yMin:
yMin, yMax = yMax, yMin
self._xEdit.setText(str(xMin))
self._yEdit.setText(str(yMin))
self._wEdit.setText(str(xMax - xMin))
self._hEdit.setText(str(yMax - yMin))
def __roiRemoved(self, name):
self.__clear()
self.__discardBn.setEnabled(False)
self.__applyBn.setEnabled(False)
def __roiMoved(self, event):
self.__display(event['xdata'], event['ydata'])
class RealSpaceViewWidgetEvent(DataViewEvent):
pass
class RealSpaceViewWidget(DataViewWidget):
plot = property(lambda self: self.__plotWindow)
def __init__(self, index, parent=None, **kwargs):
super(RealSpaceViewWidget, self).__init__(index, parent=parent)
self.__plotWindow = plotWindow = PlotWindow(aspectRatio=True,
curveStyle=False,
mask=False,
roi=False,
**kwargs)
plotWindow.setKeepDataAspectRatio(True)
plotWindow.setActiveCurveHandling(False)
self.__roiManager = roiManager = ImageRoiManager(plotWindow)
roiToolBar = roiManager.toolBar(rois=['rectangle'],
options=['show'])
roiToolBar.addSeparator()
rectRoiWidget = RectRoiWidget(roiManager)
roiToolBar.addWidget(rectRoiWidget)
rectRoiWidget.sigRoiApplied.connect(self.__roiApplied)
plotWindow.addToolBarBreak()
plotWindow.addToolBar(roiToolBar)
self.setCentralWidget(plotWindow)
def setPlotData(self, x, y, data):
plot = self.__plotWindow
if data.ndim == 1:
# scatter
min_, max_ = data.min(), data.max()
colormap = cm.jet
colors = colormap((data.astype(np.float64) - min_) / (max_ - min_))
plot.addCurve(x, y,
color=colors,
symbol='s',
linestyle='')
elif data.ndim == 2:
# image
min_, max_ = data.min(), data.max()
colormap = {'name': 'temperature',
'normalization': 'linear',
'autoscale': True,
'vmin': min_,
'vmax': max_}
origin = x[0], y[0]
scale = (x[-1] - x[0]) / len(x), (y[-1] - y[0]) / len(y)
plot.addImage(data,
origin=origin,
scale=scale,
colormap=colormap)
else:
raise ValueError('data has {0} dimensions, expected 1 or 2.'
''.format(data.ndim))
def __roiApplied(self, roi):
self._emitEvent(RealSpaceViewWidgetEvent(self, roi))
if __name__ == '__main__':
pass
......@@ -304,18 +304,26 @@ class XsocsPlot2DColorDialog(Qt.QDialog):
colorLabel.setPixmap(pixmap)
XsocsPlot2DPoint = namedtuple('XsocsPlot2DPoint', ['x', 'y', 'xIdx', 'yIdx'])
class XsocsPlot2D(PlotWindow):
"""
Base class for the 2D scatter plot.
The colormap widget only supports one plot at the moment.
"""
sigPointSelected = Qt.Signal(object)
def __init__(self, **kwargs):
super(XsocsPlot2D, self).__init__(**kwargs)
self.setActiveCurveHandling(False)
self.setKeepDataAspectRatio(True)
self.__sigPlotConnected = False
self.__pointSelectionEnabled = False
self.__logScatter = False
self.__colormap = cm.jet
self.__values = {}
......@@ -444,6 +452,69 @@ class XsocsPlot2D(PlotWindow):
optionsLayout.addWidget(optionsBaseB)
optionsLayout.addWidget(optionsBaseA)
def __drawSelectedPosition(self, x, y):
"""Set the selected position.
:param float x:
:param float y:
"""
self.addXMarker(x, legend='Xselection', color='pink')
self.addYMarker(y, legend='Yselection', color='pink')
def __onPlotSignal(self, event):
if self.__pointSelectionEnabled:
if event['event'] in ('mouseClicked'):
x, y = event['x'], event['y']
self.selectPoint(x, y)
curves = self.getAllCurves(just_legend=True)
if not curves:
xIdx = None
yIdx = None
else:
curveX, curveY = self.getCurve(curves[0])[0:2]
xIdx = ((curveX - x) ** 2 + (curveY - y) ** 2).argmin()
yIdx = xIdx
x = curveX[xIdx]
y = curveY[xIdx]
point = XsocsPlot2DPoint(x=x, y=y, xIdx=xIdx, yIdx=yIdx)
self.selectPoint(x, y)
self.sigPointSelected.emit(point)
def selectPoint(self, x, y):
"""
Called when PointSelectionEnabled is True and the mouse is clicked.
The default implementation just draws a crosshair at the position
of the closest point of the 2D scatter plot, if any, or at the
position x,y if an image is plotted. sigPointSelected is not emitted.
:param x:
:param y:
:return:
"""
self.__drawSelectedPosition(x, y)
def setPointSelectionEnabled(self, enabled):
self.__connectPlotSignal(pointSelection=enabled)
def isPointSelectionEnabled(self, enabled):
return self.__pointSelectionEnabled
def __connectPlotSignal(self, pointSelection=None):
currentState = self.__sigPlotConnected
if pointSelection is not None:
self.__pointSelectionEnabled = pointSelection
newState = self.__pointSelectionEnabled
if currentState != newState:
if newState:
self.sigPlotSignal.connect(self.__onPlotSignal)
else:
self.sigPlotSignal.disconnect(self.__onPlotSignal)
self.__sigPlotConnected = newState
def __save2DTriggered(self):
# TODO : support more that one curve
if not self.__values:
......@@ -641,6 +712,9 @@ class XsocsPlot2D(PlotWindow):
colors = None
if values is not None and self.__values:
raise ValueError('XsocsPlot2D only supports one 2D scatter plot.')
if colormap is None:
colormap = XsocsPlot2DColormap(colormap=cm.jet,
minVal=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