Ejemplo n.º 1
0
    def __init__(self, parent = None, backend=None, selection=False, aspect=True,
                 colormap=False,
                 imageicons=False, standalonesave=True, standalonezoom=True,
                 profileselection=False, polygon=False):
        qt.QWidget.__init__(self, parent)
        self.mainLayout = qt.QVBoxLayout(self)
        self.mainLayout.setContentsMargins(0, 0, 0, 0)
        self.mainLayout.setSpacing(0)
        self._keepDataAspectRatioFlag = False
        self.graph = PlotWidget(parent=self, backend=backend)
        self.graph.setGraphXLabel("Column")
        self.graph.setGraphYLabel("Row")
        self.graph.setYAxisAutoScale(True)
        self.graph.setXAxisAutoScale(True)
        plotArea = self.graph.getWidgetHandle()
        plotArea.setContextMenuPolicy(qt.Qt.CustomContextMenu)
        plotArea.customContextMenuRequested.connect(self._zoomBack)

        self._buildToolBar(selection, colormap, imageicons,
                           standalonesave,
                           standalonezoom=standalonezoom,
                           profileselection=profileselection,
                           aspect=aspect,
                           polygon=polygon)

        if profileselection:
            if len(self._pickerSelectionButtons):
                self.graph.sigPlotSignal.connect(\
                    self._graphPolygonSignalReceived)
                self._pickerSelectionWidthValue.valueChanged[int].connect( \
                             self.setPickerSelectionWith)

        self.saveDirectory = os.getcwd()
        self.mainLayout.addWidget(self.graph)
Ejemplo n.º 2
0
    def __init__(self, parent=None):
        super(MaskScatterWidget, self).__init__(parent=parent)
        self._activeScatterLegend = "active scatter"
        self._bgImageLegend = "background image"

        # widgets
        centralWidget = qt.QWidget(self)

        self._plot = PlotWidget(parent=centralWidget)

        self._maskToolsWidget = ScatterMaskToolsWidget.ScatterMaskToolsWidget(
            plot=self._plot, parent=centralWidget)

        self._alphaSlider = NamedScatterAlphaSlider(parent=self, plot=self._plot)
        self._alphaSlider.setOrientation(qt.Qt.Horizontal)
        self._alphaSlider.setToolTip("Adjust scatter opacity")

        # layout
        layout = qt.QVBoxLayout(centralWidget)
        layout.addWidget(self._plot)
        layout.addWidget(self._alphaSlider)
        layout.addWidget(self._maskToolsWidget)
        centralWidget.setLayout(layout)

        self.setCentralWidget(centralWidget)
Ejemplo n.º 3
0
 def addItem(self, *var, **kw):
     if len(var) < 2:
         if len(var) == 0:
             return PlotWidget.addItem(self, **kw)
         else:
             return PlotWidget.addItem(self, *var, **kw)
     else:
         return self.__addItem(*var, **kw)
Ejemplo n.º 4
0
 def __init__(self, *var, **kw):
     PlotWidget.__init__(self, *var, **kw)
     # No context menu by default, execute zoomBack on right click
     if "backend" in kw:
         setBackend = kw["backend"]
     else:
         setBackend = None
     _logger.info("SilxBackend called with backend = %s" % setBackend)
     plotArea = self.getWidgetHandle()
     plotArea.setContextMenuPolicy(qt.Qt.CustomContextMenu)
     plotArea.customContextMenuRequested.connect(self._zoomBack)
Ejemplo n.º 5
0
 def __init__(self, *var, **kw):
     PlotWidget.__init__(self, *var, **kw)
     # No context menu by default, execute zoomBack on right click
     if "backend" in kw:
         setBackend = kw["backend"]
     else:
         setBackend = None
     _logger.info("SilxBackend called with backend = %s" % setBackend)
     plotArea = self.getWidgetHandle()
     plotArea.setContextMenuPolicy(qt.Qt.CustomContextMenu)
     plotArea.customContextMenuRequested.connect(self._zoomBack)
Ejemplo n.º 6
0
 def __init__(self, parent=None):
     qt.QWidget.__init__(self, parent)
     self.setWindowTitle("Strip and SNIP Configuration Window")
     self.mainLayout = qt.QVBoxLayout(self)
     self.mainLayout.setContentsMargins(0, 0, 0, 0)
     self.mainLayout.setSpacing(2)
     self.parametersWidget = BackgroundParamWidget(self)
     self.graphWidget = PlotWidget(parent=self)
     self.mainLayout.addWidget(self.parametersWidget)
     self.mainLayout.addWidget(self.graphWidget)
     self._x = None
     self._y = None
     self.parametersWidget.sigBackgroundParamWidgetSignal.connect(self._slot)
    def setUp(self):
        super(TestNamedImageAlphaSlider, self).setUp()
        self.plot = PlotWidget()
        self.aslider = AlphaSlider.NamedImageAlphaSlider(plot=self.plot)
        self.aslider.setOrientation(qt.Qt.Horizontal)

        toolbar = qt.QToolBar("plot", self.plot)
        toolbar.addWidget(self.aslider)
        self.plot.addToolBar(toolbar)

        self.plot.show()
        self.qWaitForWindowExposed(self.plot)

        self.mouseMove(self.plot)  # Move to center
        self.qapp.processEvents()
Ejemplo n.º 8
0
    def __init__(self, parent=None):
        super(MaskScatterWidget, self).__init__(parent=parent)
        self._activeScatterLegend = "active scatter"
        self._bgImageLegend = "background image"

        # widgets
        centralWidget = qt.QWidget(self)

        self._plot = PlotWidget(parent=centralWidget)

        self._maskToolsWidget = ScatterMaskToolsWidget.ScatterMaskToolsWidget(
            plot=self._plot, parent=centralWidget)

        self._alphaSlider = NamedScatterAlphaSlider(parent=self, plot=self._plot)
        self._alphaSlider.setOrientation(qt.Qt.Horizontal)
        self._alphaSlider.setToolTip("Adjust scatter opacity")

        # layout
        layout = qt.QVBoxLayout(centralWidget)
        layout.addWidget(self._plot)
        layout.addWidget(self._alphaSlider)
        layout.addWidget(self._maskToolsWidget)
        centralWidget.setLayout(layout)

        self.setCentralWidget(centralWidget)
Ejemplo n.º 9
0
    def __init__(self, parent = None, backend=None, selection=False, aspect=True,
                 colormap=False,
                 imageicons=False, standalonesave=True, standalonezoom=True,
                 profileselection=False, polygon=False):
        qt.QWidget.__init__(self, parent)
        self.mainLayout = qt.QVBoxLayout(self)
        self.mainLayout.setContentsMargins(0, 0, 0, 0)
        self.mainLayout.setSpacing(0)
        self._keepDataAspectRatioFlag = False
        self.graph = PlotWidget(parent=self, backend=backend)
        self.graph.setGraphXLabel("Column")
        self.graph.setGraphYLabel("Row")
        self.graph.setYAxisAutoScale(True)
        self.graph.setXAxisAutoScale(True)
        plotArea = self.graph.getWidgetHandle()
        plotArea.setContextMenuPolicy(qt.Qt.CustomContextMenu)
        plotArea.customContextMenuRequested.connect(self._zoomBack)

        self._buildToolBar(selection, colormap, imageicons,
                           standalonesave,
                           standalonezoom=standalonezoom,
                           profileselection=profileselection,
                           aspect=aspect,
                           polygon=polygon)

        if profileselection:
            if len(self._pickerSelectionButtons):
                self.graph.sigPlotSignal.connect(\
                    self._graphPolygonSignalReceived)
                self._pickerSelectionWidthValue.valueChanged[int].connect( \
                             self.setPickerSelectionWith)

        self.saveDirectory = os.getcwd()
        self.mainLayout.addWidget(self.graph)
Ejemplo n.º 10
0
    def __init__(self, parent=None):
        super(MyPlotWindow, self).__init__(parent)

        # Create a PlotWidget
        self._plot = PlotWidget(parent=self)

        # Create a colorbar linked with the PlotWidget
        colorBar = ColorBarWidget(parent=self, plot=self._plot)

        # Make ColorBarWidget background white by changing its palette
        colorBar.setAutoFillBackground(True)
        palette = colorBar.palette()
        palette.setColor(qt.QPalette.Background, qt.Qt.white)
        palette.setColor(qt.QPalette.Window, qt.Qt.white)
        colorBar.setPalette(palette)

        # Combine the ColorBarWidget and the PlotWidget as
        # this QMainWindow's central widget
        gridLayout = qt.QGridLayout()
        gridLayout.setSpacing(0)
        gridLayout.setContentsMargins(0, 0, 0, 0)
        gridLayout.addWidget(self._plot, 0, 0)
        gridLayout.addWidget(colorBar, 0, 1)
        gridLayout.setRowStretch(0, 1)
        gridLayout.setColumnStretch(0, 1)
        centralWidget = qt.QWidget(self)
        centralWidget.setLayout(gridLayout)
        self.setCentralWidget(centralWidget)

        # Add ready to use toolbar with zoom and pan interaction mode buttons
        interactionToolBar = tools.InteractiveModeToolBar(parent=self,
                                                          plot=self._plot)
        self.addToolBar(interactionToolBar)
        # Add toolbar actions to activate keyboard shortcuts
        self.addActions(interactionToolBar.actions())

        # Add a new toolbar
        toolBar = qt.QToolBar("Plot Tools", self)
        self.addToolBar(toolBar)

        # Add actions from silx.gui.plot.action to the toolbar
        resetZoomAction = actions.control.ResetZoomAction(parent=self,
                                                          plot=self._plot)
        toolBar.addAction(resetZoomAction)

        # Add tool buttons from silx.gui.plot.PlotToolButtons
        aspectRatioButton = PlotToolButtons.AspectToolButton(parent=self,
                                                             plot=self._plot)
        toolBar.addWidget(aspectRatioButton)

        # Add ready to use toolbar with copy, save and print buttons
        outputToolBar = tools.OutputToolBar(parent=self, plot=self._plot)
        self.addToolBar(outputToolBar)
        # Add toolbar actions to activate keyboard shortcuts
        self.addActions(outputToolBar.actions())

        # Add limits tool bar from silx.gui.plot.PlotTools
        limitsToolBar = tools.LimitsToolBar(parent=self, plot=self._plot)
        self.addToolBar(qt.Qt.BottomToolBarArea, limitsToolBar)
Ejemplo n.º 11
0
 def addCurve(self, *var, **kw):
     if "replot" in kw:
         if kw["replot"]:
             kw["resetzoom"] = True
         del kw["replot"]
     result = PlotWidget.addCurve(self, *var, **kw)
     allCurves = self.getAllCurves(just_legend=True)
     if len(allCurves) == 1:
         self.setActiveCurve(allCurves[0])
     return result
Ejemplo n.º 12
0
 def addCurve(self, *var, **kw):
     if "replot" in kw:
         if kw["replot"]:
             kw["resetzoom"] = True
         del kw["replot"]
     result = PlotWidget.addCurve(self, *var, **kw)
     allCurves = self.getAllCurves(just_legend=True) 
     if len(allCurves) == 1:
         self.setActiveCurve(allCurves[0])
     return result
Ejemplo n.º 13
0
 def __init__(self, parent=None):
     qt.QWidget.__init__(self, parent)
     self.setWindowTitle("Strip and SNIP Configuration Window")
     self.mainLayout = qt.QVBoxLayout(self)
     self.mainLayout.setContentsMargins(0, 0, 0, 0)
     self.mainLayout.setSpacing(2)
     self.parametersWidget = BackgroundParamWidget(self)
     self.graphWidget = PlotWidget(parent=self)
     self.mainLayout.addWidget(self.parametersWidget)
     self.mainLayout.addWidget(self.graphWidget)
     self._x = None
     self._y = None
     self.parametersWidget.sigBackgroundParamWidgetSignal.connect(self._slot)
Ejemplo n.º 14
0
    def setUp(self):
        super(TestNamedImageAlphaSlider, self).setUp()
        self.plot = PlotWidget()
        self.aslider = AlphaSlider.NamedImageAlphaSlider(plot=self.plot)
        self.aslider.setOrientation(qt.Qt.Horizontal)

        toolbar = qt.QToolBar("plot", self.plot)
        toolbar.addWidget(self.aslider)
        self.plot.addToolBar(toolbar)

        self.plot.show()
        self.qWaitForWindowExposed(self.plot)

        self.mouseMove(self.plot)  # Move to center
        self.qapp.processEvents()
Ejemplo n.º 15
0
 def addImage(self, *var, **kw):
     if "replot" in kw:
         del kw["replot"]
     if "xScale" in kw:
         xScale = kw["xScale"]
         del kw["xScale"]
     if "yScale" in kw:
         yScale = kw["yScale"]
         del kw["yScale"]
     if xScale is not None or yScale is not None:
         origin = kw.get("origin", None)
         scale = kw.get("scale", None)
         if origin is None and scale is None:
             kw["origin"] = xScale[0], yScale[0]
             kw["scale"] = xScale[1], yScale[1]
     return PlotWidget.addImage(self, *var, **kw)
Ejemplo n.º 16
0
 def addImage(self, *var, **kw):
     if "replot" in kw:
         del kw["replot"]
     if "xScale" in kw:
         xScale = kw["xScale"]
         del kw["xScale"]
     if "yScale" in kw:
         yScale = kw["yScale"]
         del kw["yScale"]
     if xScale is not None or yScale is not None:
         origin = kw.get("origin", None)
         scale = kw.get("scale", None)
         if origin is None and scale is None:
             kw["origin"] = xScale[0], yScale[0]
             kw["scale"] = xScale[1], yScale[1]
     return PlotWidget.addImage(self, *var, **kw)
Ejemplo n.º 17
0
class _PlotWidgetTest(TestCaseQt):
    """Base class for tests of PlotWidget, not a TestCase in itself.

    plot attribute is the PlotWidget created for the test.
    """
    def setUp(self):
        super(_PlotWidgetTest, self).setUp()
        self.plot = PlotWidget()
        self.plot.show()
        self.qWaitForWindowExposed(self.plot)

    def tearDown(self):
        self.qapp.processEvents()
        self.plot.setAttribute(qt.Qt.WA_DeleteOnClose)
        self.plot.close()
        del self.plot
        super(_PlotWidgetTest, self).tearDown()
Ejemplo n.º 18
0
class _PlotWidgetTest(TestCaseQt):
    """Base class for tests of PlotWidget, not a TestCase in itself.

    plot attribute is the PlotWidget created for the test.
    """

    def setUp(self):
        super(_PlotWidgetTest, self).setUp()
        self.plot = PlotWidget()
        self.plot.show()
        self.qWaitForWindowExposed(self.plot)

    def tearDown(self):
        self.qapp.processEvents()
        self.plot.setAttribute(qt.Qt.WA_DeleteOnClose)
        self.plot.close()
        del self.plot
        super(_PlotWidgetTest, self).tearDown()
Ejemplo n.º 19
0
__authors__ = ["P. Knobel"]
__license__ = "MIT"
__date__ = "25/07/2017"

import numpy

from silx.gui import qt
from silx.gui.plot import PlotWidget
from silx.gui.plot import PrintPreviewToolButton

app = qt.QApplication([])

x = numpy.arange(1000)

# first widget has a standalone print preview action
pw1 = PlotWidget()
pw1.setWindowTitle("Widget 1 with standalone print preview")
toolbar1 = qt.QToolBar(pw1)
toolbutton1 = PrintPreviewToolButton.PrintPreviewToolButton(parent=toolbar1,
                                                            plot=pw1)
pw1.addToolBar(toolbar1)
toolbar1.addWidget(toolbutton1)
pw1.show()
pw1.addCurve(x, numpy.tan(x * 2 * numpy.pi / 1000))

# next two plots share a common print preview
pw2 = PlotWidget()
pw2.setWindowTitle("Widget 2 with shared print preview")
toolbar2 = qt.QToolBar(pw2)
toolbutton2 = PrintPreviewToolButton.SingletonPrintPreviewToolButton(
    parent=toolbar2, plot=pw2)
Ejemplo n.º 20
0
class RGBCorrelatorGraph(qt.QWidget):
    sigProfileSignal = qt.pyqtSignal(object)

    def __init__(self, parent = None, backend=None, selection=False, aspect=True,
                 colormap=False,
                 imageicons=False, standalonesave=True, standalonezoom=True,
                 profileselection=False, polygon=False):
        qt.QWidget.__init__(self, parent)
        self.mainLayout = qt.QVBoxLayout(self)
        self.mainLayout.setContentsMargins(0, 0, 0, 0)
        self.mainLayout.setSpacing(0)
        self._keepDataAspectRatioFlag = False
        self.graph = PlotWidget(parent=self, backend=backend)
        self.graph.setGraphXLabel("Column")
        self.graph.setGraphYLabel("Row")
        self.graph.setYAxisAutoScale(True)
        self.graph.setXAxisAutoScale(True)
        plotArea = self.graph.getWidgetHandle()
        plotArea.setContextMenuPolicy(qt.Qt.CustomContextMenu)
        plotArea.customContextMenuRequested.connect(self._zoomBack)

        self._buildToolBar(selection, colormap, imageicons,
                           standalonesave,
                           standalonezoom=standalonezoom,
                           profileselection=profileselection,
                           aspect=aspect,
                           polygon=polygon)

        if profileselection:
            if len(self._pickerSelectionButtons):
                self.graph.sigPlotSignal.connect(\
                    self._graphPolygonSignalReceived)
                self._pickerSelectionWidthValue.valueChanged[int].connect( \
                             self.setPickerSelectionWith)

        self.saveDirectory = os.getcwd()
        self.mainLayout.addWidget(self.graph)

    def sizeHint(self):
        return qt.QSize(1.5 * qt.QWidget.sizeHint(self).width(),
                        qt.QWidget.sizeHint(self).height())

    def _buildToolBar(self, selection=False, colormap=False,
                      imageicons=False, standalonesave=True,
                      standalonezoom=True, profileselection=False,
                      aspect=False, polygon=False):
        self.solidCircleIcon = qt.QIcon(qt.QPixmap(IconDict["solidcircle"]))
        self.solidEllipseIcon = qt.QIcon(qt.QPixmap(IconDict["solidellipse"]))
        self.colormapIcon   = qt.QIcon(qt.QPixmap(IconDict["colormap"]))
        self.selectionIcon = qt.QIcon(qt.QPixmap(IconDict["normal"]))
        self.zoomResetIcon = qt.QIcon(qt.QPixmap(IconDict["zoomreset"]))
        self.polygonIcon = qt.QIcon(qt.QPixmap(IconDict["polygon"]))
        self.printIcon	= qt.QIcon(qt.QPixmap(IconDict["fileprint"]))
        self.saveIcon	= qt.QIcon(qt.QPixmap(IconDict["filesave"]))
        self.xAutoIcon	= qt.QIcon(qt.QPixmap(IconDict["xauto"]))
        self.yAutoIcon	= qt.QIcon(qt.QPixmap(IconDict["yauto"]))
        self.hFlipIcon	= qt.QIcon(qt.QPixmap(IconDict["gioconda16mirror"]))
        self.imageIcon     = qt.QIcon(qt.QPixmap(IconDict["image"]))
        self.eraseSelectionIcon = qt.QIcon(qt.QPixmap(IconDict["eraseselect"]))
        self.rectSelectionIcon  = qt.QIcon(qt.QPixmap(IconDict["boxselect"]))
        self.brushSelectionIcon = qt.QIcon(qt.QPixmap(IconDict["brushselect"]))
        self.brushIcon          = qt.QIcon(qt.QPixmap(IconDict["brush"]))
        self.additionalIcon     = qt.QIcon(qt.QPixmap(IconDict["additionalselect"]))
        self.hLineIcon     = qt.QIcon(qt.QPixmap(IconDict["horizontal"]))
        self.vLineIcon     = qt.QIcon(qt.QPixmap(IconDict["vertical"]))
        self.lineIcon     = qt.QIcon(qt.QPixmap(IconDict["diagonal"]))
        self.copyIcon     = silx_icons.getQIcon("edit-copy")

        self.toolBar = qt.QWidget(self)
        self.toolBarLayout = qt.QHBoxLayout(self.toolBar)
        self.toolBarLayout.setContentsMargins(0, 0, 0, 0)
        self.toolBarLayout.setSpacing(0)
        self.mainLayout.addWidget(self.toolBar)
        #Autoscale
        if standalonezoom:
            tb = self._addToolButton(self.zoomResetIcon,
                            self.__zoomReset,
                            'Auto-Scale the Graph')
        else:
            tb = self._addToolButton(self.zoomResetIcon,
                            None,
                            'Auto-Scale the Graph')
        self.zoomResetToolButton = tb
        #y Autoscale
        tb = self._addToolButton(self.yAutoIcon,
                            self._yAutoScaleToggle,
                            'Toggle Autoscale Y Axis (On/Off)',
                            toggle = True, state=True)
        tb.setDown(True)

        self.yAutoScaleToolButton = tb
        tb.setDown(True)

        #x Autoscale
        tb = self._addToolButton(self.xAutoIcon,
                            self._xAutoScaleToggle,
                            'Toggle Autoscale X Axis (On/Off)',
                            toggle = True, state=True)
        self.xAutoScaleToolButton = tb
        tb.setDown(True)

        #Aspect ratio
        if aspect:
            self.aspectButton = self._addToolButton(self.solidCircleIcon,
                                                    self._aspectButtonSignal,
                                                    'Keep data aspect ratio',
                                                    toggle=False)
            self.aspectButton.setChecked(False)

        #colormap
        if colormap:
            tb = self._addToolButton(self.colormapIcon,
                                     None,
                                     'Change Colormap')
            self.colormapToolButton = tb

        #flip
        tb = self._addToolButton(self.hFlipIcon,
                                 None,
                                 'Flip Horizontal')
        self.hFlipToolButton = tb


        #save
        if standalonesave:
            tb = self._addToolButton(self.saveIcon,
                                 self._saveIconSignal,
                                 'Save Graph')
        else:
            tb = self._addToolButton(self.saveIcon,
                                 None,
                                 'Save')
        self.saveToolButton = tb

        self.copyToolButton = self._addToolButton(self.copyIcon,
                                                  self._copyIconSignal,
                                                  "Copy graph to clipboard")

        #Selection
        if selection:
            tb = self._addToolButton(self.selectionIcon,
                                None,
                                'Toggle Selection Mode',
                                toggle = True,
                                state = False)
            tb.setDown(False)
            self.selectionToolButton = tb
        #image selection icons
        if imageicons:
            tb = self._addToolButton(self.imageIcon,
                                     None,
                                     'Reset')
            self.imageToolButton = tb

            tb = self._addToolButton(self.eraseSelectionIcon,
                                     None,
                                     'Erase Selection')
            self.eraseSelectionToolButton = tb

            tb = self._addToolButton(self.rectSelectionIcon,
                                     None,
                                     'Rectangular Selection')
            self.rectSelectionToolButton = tb

            tb = self._addToolButton(self.brushSelectionIcon,
                                     None,
                                     'Brush Selection')
            self.brushSelectionToolButton = tb

            tb = self._addToolButton(self.brushIcon,
                                     None,
                                     'Select Brush')
            self.brushToolButton = tb

            if polygon:
                tb = self._addToolButton(self.polygonIcon,
                                     None,
                        'Polygon selection\nRight click to finish')
                self.polygonSelectionToolButton = tb

            tb = self._addToolButton(self.additionalIcon,
                                     None,
                                     'Additional Selections Menu')
            self.additionalSelectionToolButton = tb
        else:
            if polygon:
                tb = self._addToolButton(self.polygonIcon,
                                     None,
                        'Polygon selection\nRight click to finish')
                self.polygonSelectionToolButton = tb
            self.imageToolButton = None
        #picker selection
        self._pickerSelectionButtons = []
        if profileselection:
            self._profileSelection = True
            self._polygonSelection = False
            self._pickerSelectionButtons = []
            if self._profileSelection:
                tb = self._addToolButton(self.hLineIcon,
                                     self._hLineProfileClicked,
                                     'Horizontal Profile Selection',
                                     toggle=True,
                                     state=False)
                self.hLineProfileButton = tb
                self._pickerSelectionButtons.append(tb)

                tb = self._addToolButton(self.vLineIcon,
                                     self._vLineProfileClicked,
                                     'Vertical Profile Selection',
                                     toggle=True,
                                     state=False)
                self.vLineProfileButton = tb
                self._pickerSelectionButtons.append(tb)

                tb = self._addToolButton(self.lineIcon,
                                     self._lineProfileClicked,
                                     'Line Profile Selection',
                                     toggle=True,
                                     state=False)
                self.lineProfileButton = tb
                self._pickerSelectionButtons.append(tb)

                self._pickerSelectionWidthLabel = qt.QLabel(self.toolBar)
                self._pickerSelectionWidthLabel.setText("W:")
                self.toolBar.layout().addWidget(self._pickerSelectionWidthLabel)
                self._pickerSelectionWidthValue = qt.QSpinBox(self.toolBar)
                self._pickerSelectionWidthValue.setMinimum(0)
                self._pickerSelectionWidthValue.setMaximum(1000)
                self._pickerSelectionWidthValue.setValue(1)
                self.toolBar.layout().addWidget(self._pickerSelectionWidthValue)
                #tb = self._addToolButton(None,
                #                     self._lineProfileClicked,
                #                     'Line Profile Selection',
                #                     toggle=True,
                #                     state=False)
                #tb.setText = "W:"
                #self.lineWidthProfileButton = tb
                #self._pickerSelectionButtons.append(tb)
            if self._polygonSelection:
                _logger.info("Polygon selection not implemented yet")
        #hide profile selection buttons
        if imageicons:
            for button in self._pickerSelectionButtons:
                button.hide()

        self.infoWidget = qt.QWidget(self.toolBar)
        self.infoWidget.mainLayout = qt.QHBoxLayout(self.infoWidget)
        self.infoWidget.mainLayout.setContentsMargins(0, 0, 0, 0)
        self.infoWidget.mainLayout.setSpacing(0)
        self.infoWidget.label = qt.QLabel(self.infoWidget)
        self.infoWidget.label.setText("X = ???? Y = ???? Z = ????")
        self.infoWidget.mainLayout.addWidget(self.infoWidget.label)
        self.toolBarLayout.addWidget(self.infoWidget)
        self.infoWidget.hide()

        self.toolBarLayout.addWidget(qt.HorizontalSpacer(self.toolBar))

        # ---print
        self.printPreview = SingletonPrintPreviewToolButton(parent=self,
                                                            plot=self.graph)
        self.printPreview.setIcon(self.printIcon)
        self.toolBarLayout.addWidget(self.printPreview)

    def _aspectButtonSignal(self):
        _logger.debug("_aspectButtonSignal")
        if self._keepDataAspectRatioFlag:
            self.keepDataAspectRatio(False)
        else:
            self.keepDataAspectRatio(True)

    def keepDataAspectRatio(self, flag=True):
        if flag:
            self._keepDataAspectRatioFlag = True
            self.aspectButton.setIcon(self.solidEllipseIcon)
            self.aspectButton.setToolTip("Set free data aspect ratio")
        else:
            self._keepDataAspectRatioFlag = False
            self.aspectButton.setIcon(self.solidCircleIcon)
            self.aspectButton.setToolTip("Keep data aspect ratio")
        self.graph.setKeepDataAspectRatio(self._keepDataAspectRatioFlag)

    def showInfo(self):
        self.infoWidget.show()

    def hideInfo(self):
        self.infoWidget.hide()

    def setInfoText(self, text):
        self.infoWidget.label.setText(text)

    def setMouseText(self, text=""):
        try:
            if len(text):
                qt.QToolTip.showText(self.cursor().pos(),
                                     text, self, qt.QRect())
            else:
                qt.QToolTip.hideText()
        except:
            _logger.warning("Error trying to show mouse text <%s>" % text)

    def focusOutEvent(self, ev):
        qt.QToolTip.hideText()

    def infoText(self):
        return self.infoWidget.label.text()

    def setXLabel(self, label="Column"):
        return self.graph.setGraphXLabel(label)

    def setYLabel(self, label="Row"):
        return self.graph.setGraphYLabel(label)

    def getXLabel(self):
        return self.graph.getGraphXLabel()

    def getYLabel(self):
        return self.graph.getGraphYLabel()

    def hideImageIcons(self):
        if self.imageToolButton is None:return
        self.imageToolButton.hide()
        self.eraseSelectionToolButton.hide()
        self.rectSelectionToolButton.hide()
        self.brushSelectionToolButton.hide()
        self.brushToolButton.hide()
        if hasattr(self, "polygonSelectionToolButton"):
            self.polygonSelectionToolButton.hide()
        self.additionalSelectionToolButton.hide()

    def showImageIcons(self):
        if self.imageToolButton is None:return
        self.imageToolButton.show()
        self.eraseSelectionToolButton.show()
        self.rectSelectionToolButton.show()
        self.brushSelectionToolButton.show()
        self.brushToolButton.show()
        if hasattr(self, "polygonSelectionToolButton"):
            self.polygonSelectionToolButton.show()
        self.additionalSelectionToolButton.show()

    def _hLineProfileClicked(self):
        for button in self._pickerSelectionButtons:
            if button != self.hLineProfileButton:
                button.setChecked(False)

        if self.hLineProfileButton.isChecked():
            self._setPickerSelectionMode("HORIZONTAL")
        else:
            self._setPickerSelectionMode(None)

    def _vLineProfileClicked(self):
        for button in self._pickerSelectionButtons:
            if button != self.vLineProfileButton:
                button.setChecked(False)
        if self.vLineProfileButton.isChecked():
            self._setPickerSelectionMode("VERTICAL")
        else:
            self._setPickerSelectionMode(None)

    def _lineProfileClicked(self):
        for button in self._pickerSelectionButtons:
            if button != self.lineProfileButton:
                button.setChecked(False)
        if self.lineProfileButton.isChecked():
            self._setPickerSelectionMode("LINE")
        else:
            self._setPickerSelectionMode(None)

    def setPickerSelectionWith(self, intValue):
        self._pickerSelectionWidthValue.setValue(intValue)
        #get the current mode
        mode = "NONE"
        for button in self._pickerSelectionButtons:
            if button.isChecked():
                if button == self.hLineProfileButton:
                    mode = "HORIZONTAL"
                elif button == self.vLineProfileButton:
                    mode = "VERTICAL"
                elif button == self.lineProfileButton:
                    mode = "LINE"
        ddict = {}
        ddict['event'] = "profileWidthChanged"
        ddict['pixelwidth'] = self._pickerSelectionWidthValue.value()
        ddict['mode'] = mode
        self.sigProfileSignal.emit(ddict)

    def hideProfileSelectionIcons(self):
        if not len(self._pickerSelectionButtons):
            return
        for button in self._pickerSelectionButtons:
            button.setChecked(False)
            button.hide()
        self._pickerSelectionWidthLabel.hide()
        self._pickerSelectionWidthValue.hide()
        if self.graph.getInteractiveMode()['mode'] == 'draw':
            self.graph.setInteractiveMode('select')

    def showProfileSelectionIcons(self):
        if not len(self._pickerSelectionButtons):
            return
        for button in self._pickerSelectionButtons:
            button.show()
        self._pickerSelectionWidthLabel.show()
        self._pickerSelectionWidthValue.show()

    def getPickerSelectionMode(self):
        if not len(self._pickerSelectionButtons):
            return None
        if self.hLineProfileButton.isChecked():
            return "HORIZONTAL"
        if self.vLineProfileButton.isChecked():
            return "VERTICAL"
        if self.lineProfileButton.isChecked():
            return "LINE"
        return None

    def _setPickerSelectionMode(self, mode=None):
        if mode is None:
            self.graph.setInteractiveMode('zoom')
        else:
            if mode == "HORIZONTAL":
                shape = "hline"
            elif mode == "VERTICAL":
                shape = "vline"
            else:
                shape = "line"
            self.graph.setInteractiveMode('draw',
                                          shape=shape,
                                          label=mode)
        ddict = {}
        if mode is None:
            mode = "NONE"
        ddict['event'] = "profileModeChanged"
        ddict['mode'] = mode
        self.sigProfileSignal.emit(ddict)

    def _graphPolygonSignalReceived(self, ddict):
        _logger.debug("PolygonSignal Received")
        for key in ddict.keys():
            _logger.debug("%s: %s", key, ddict[key])

        if ddict['event'] not in ['drawingProgress', 'drawingFinished']:
            return
        label = ddict['parameters']['label']
        if label not in ['HORIZONTAL', 'VERTICAL', 'LINE']:
            return
        ddict['mode'] = label
        ddict['pixelwidth'] = self._pickerSelectionWidthValue.value()
        self.sigProfileSignal.emit(ddict)

    def _addToolButton(self, icon, action, tip, toggle=None, state=None, position=None):
        tb      = qt.QToolButton(self.toolBar)
        if icon is not None:
            tb.setIcon(icon)
        tb.setToolTip(tip)
        if toggle is not None:
            if toggle:
                tb.setCheckable(1)
                if state is not None:
                    if state:
                        tb.setChecked(state)
                else:
                    tb.setChecked(False)
        if position is not None:
            self.toolBarLayout.insertWidget(position, tb)
        else:
            self.toolBarLayout.addWidget(tb)
        if action is not None:
            tb.clicked.connect(action)
        return tb

    def __zoomReset(self):
        self._zoomReset()

    def _zoomReset(self, replot=None):
        _logger.debug("_zoomReset")
        if self.graph is not None:
            self.graph.resetZoom()

    def _yAutoScaleToggle(self):
        if self.graph is not None:
            self.yAutoScaleToolButton.setDown(
                    not self.graph.isYAxisAutoScale())
            self.graph.setYAxisAutoScale(
                    not self.graph.isYAxisAutoScale())

    def _xAutoScaleToggle(self):
        if self.graph is not None:
            self.xAutoScaleToolButton.setDown(
                    not self.graph.isXAxisAutoScale())
            self.graph.setXAxisAutoScale(
                    not self.graph.isXAxisAutoScale())

    def _copyIconSignal(self):
        pngFile = BytesIO()
        self.graph.saveGraph(pngFile, fileFormat='png')
        pngFile.flush()
        pngFile.seek(0)
        pngData = pngFile.read()
        pngFile.close()
        image = qt.QImage.fromData(pngData, 'png')
        qt.QApplication.clipboard().setImage(image)

    def _saveIconSignal(self):
        self.saveDirectory = PyMcaDirs.outputDir

        fileTypeList = ["Image *.png",
                        "Image *.jpg",
                        "ZoomedImage *.png",
                        "ZoomedImage *.jpg",
                        "Widget *.png",
                        "Widget *.jpg"]

        outfile = qt.QFileDialog(self)
        outfile.setModal(1)
        outfile.setWindowTitle("Output File Selection")
        if hasattr(qt, "QStringList"):
            strlist = qt.QStringList()
        else:
            strlist = []
        for f in fileTypeList:
            strlist.append(f)
        if hasattr(outfile, "setFilters"):
            outfile.setFilters(strlist)
        else:
            outfile.setNameFilters(strlist)
        outfile.setFileMode(outfile.AnyFile)
        outfile.setAcceptMode(qt.QFileDialog.AcceptSave)
        outfile.setDirectory(self.saveDirectory)
        ret = outfile.exec_()

        if not ret:
            return
        if hasattr(outfile, "selectedFilter"):
            filterused = qt.safe_str(outfile.selectedFilter()).split()
        else:
            filterused = qt.safe_str(outfile.selectedNameFilter()).split()
        filetype = filterused[0]
        extension = filterused[1]

        outstr = qt.safe_str(outfile.selectedFiles()[0])

        try:
            outputFile = os.path.basename(outstr)
        except:
            outputFile = outstr
        outputDir  = os.path.dirname(outstr)
        self.saveDirectory = outputDir
        PyMcaDirs.outputDir = outputDir

        #always overwrite for the time being
        if len(outputFile) < len(extension[1:]):
            outputFile += extension[1:]
        elif outputFile[-4:] != extension[1:]:
            outputFile += extension[1:]
        outputFile = os.path.join(outputDir, outputFile)
        if os.path.exists(outputFile):
            try:
                os.remove(outputFile)
            except:
                qt.QMessageBox.critical(self, "Save Error",
                                        "Cannot overwrite existing file")
                return

        if filetype.upper() == "IMAGE":
            self.saveGraphImage(outputFile, original=True)
        elif filetype.upper() == "ZOOMEDIMAGE":
            self.saveGraphImage(outputFile, original=False)
        else:
            self.saveGraphWidget(outputFile)

    def saveGraphImage(self, filename, original=False):
        format_ = filename[-3:].upper()
        activeImage = self.graph.getActiveImage()
        rgbdata = activeImage.getRgbaImageData()
        # silx to pymca scale convention (a + b x)
        xScale = activeImage.getOrigin()[0], activeImage.getScale()[0]
        yScale = activeImage.getOrigin()[1], activeImage.getScale()[1]
        if original:
            # save whole image
            bgradata = numpy.array(rgbdata, copy=True)
            bgradata[:, :, 0] = rgbdata[:, :, 2]
            bgradata[:, :, 2] = rgbdata[:, :, 0]
        else:
            shape = rgbdata.shape[:2]
            xmin, xmax = self.graph.getGraphXLimits()
            ymin, ymax = self.graph.getGraphYLimits()
            # save zoomed image, for that we have to get the limits
            r0, c0 = convertToRowAndColumn(xmin, ymin, shape, xScale=xScale, yScale=yScale, safe=True)
            r1, c1 = convertToRowAndColumn(xmax, ymax, shape, xScale=xScale, yScale=yScale, safe=True)
            row0 = int(min(r0, r1))
            row1 = int(max(r0, r1))
            col0 = int(min(c0, c1))
            col1 = int(max(c0, c1))
            if row1 < shape[0]:
                row1 += 1
            if col1 < shape[1]:
                col1 += 1
            tmpArray = rgbdata[row0:row1, col0:col1, :]
            bgradata = numpy.array(tmpArray, copy=True, dtype=rgbdata.dtype)
            bgradata[:, :, 0] = tmpArray[:, :, 2]
            bgradata[:, :, 2] = tmpArray[:, :, 0]
        if self.graph.isYAxisInverted():
            qImage = qt.QImage(bgradata, bgradata.shape[1], bgradata.shape[0],
                               qt.QImage.Format_ARGB32)
        else:
            qImage = qt.QImage(bgradata, bgradata.shape[1], bgradata.shape[0],
                               qt.QImage.Format_ARGB32).mirrored(False, True)
        pixmap = qt.QPixmap.fromImage(qImage)
        if pixmap.save(filename, format_):
            return
        else:
            qt.QMessageBox.critical(self, "Save Error",
                                    "%s" % sys.exc_info()[1])
            return
    
    def saveGraphWidget(self, filename):
        format_ = filename[-3:].upper()
        if hasattr(qt.QPixmap, "grabWidget"):
            # Qt4
            pixmap = qt.QPixmap.grabWidget(self.graph.getWidgetHandle())
        else:
            # Qt5
            pixmap = self.graph.getWidgetHandle().grab()
        if pixmap.save(filename, format_):
            return
        else:
            qt.QMessageBox.critical(self, "Save Error", "%s" % sys.exc_info()[1])
            return

    def setSaveDirectory(self, wdir):
        if os.path.exists(wdir):
            self.saveDirectory = wdir
            return True
        else:
            return False

    def selectColormap(self):
        qt.QMessageBox.information(self, "Open", "Not implemented (yet)")

    def _zoomBack(self, pos):
        self.graph.getLimitsHistory().pop()
Ejemplo n.º 21
0
 def setUp(self):
     super(_PlotWidgetTest, self).setUp()
     self.plot = PlotWidget()
     self.plot.show()
     self.qWaitForWindowExposed(self.plot)
Ejemplo n.º 22
0
 def setUp(self):
     TestCaseQt.setUp(self)
     self.plot1 = PlotWidget()
     self.plot2 = PlotWidget()
     self.plot3 = PlotWidget()
Ejemplo n.º 23
0
 def setUp(self):
     TestCaseQt.setUp(self)
     self.plot1 = PlotWidget()
     self.plot2 = PlotWidget()
     self.plot3 = PlotWidget()
Ejemplo n.º 24
0
class RGBCorrelatorGraph(qt.QWidget):
    sigProfileSignal = qt.pyqtSignal(object)

    def __init__(self,
                 parent=None,
                 backend=None,
                 selection=False,
                 aspect=True,
                 colormap=False,
                 imageicons=False,
                 standalonesave=True,
                 standalonezoom=True,
                 profileselection=False,
                 polygon=False):
        qt.QWidget.__init__(self, parent)
        self.mainLayout = qt.QVBoxLayout(self)
        self.mainLayout.setContentsMargins(0, 0, 0, 0)
        self.mainLayout.setSpacing(0)
        self._keepDataAspectRatioFlag = False
        self.graph = PlotWidget(parent=self, backend=backend)
        self.graph.setGraphXLabel("Column")
        self.graph.setGraphYLabel("Row")
        self.graph.setYAxisAutoScale(True)
        self.graph.setXAxisAutoScale(True)
        plotArea = self.graph.getWidgetHandle()
        plotArea.setContextMenuPolicy(qt.Qt.CustomContextMenu)
        plotArea.customContextMenuRequested.connect(self._zoomBack)

        self._buildToolBar(selection,
                           colormap,
                           imageicons,
                           standalonesave,
                           standalonezoom=standalonezoom,
                           profileselection=profileselection,
                           aspect=aspect,
                           polygon=polygon)

        if profileselection:
            if len(self._pickerSelectionButtons):
                self.graph.sigPlotSignal.connect(\
                    self._graphPolygonSignalReceived)
                self._pickerSelectionWidthValue.valueChanged[int].connect( \
                             self.setPickerSelectionWith)

        self.saveDirectory = os.getcwd()
        self.mainLayout.addWidget(self.graph)

    def sizeHint(self):
        return qt.QSize(1.5 * qt.QWidget.sizeHint(self).width(),
                        qt.QWidget.sizeHint(self).height())

    def _buildToolBar(self,
                      selection=False,
                      colormap=False,
                      imageicons=False,
                      standalonesave=True,
                      standalonezoom=True,
                      profileselection=False,
                      aspect=False,
                      polygon=False):
        self.solidCircleIcon = qt.QIcon(qt.QPixmap(IconDict["solidcircle"]))
        self.solidEllipseIcon = qt.QIcon(qt.QPixmap(IconDict["solidellipse"]))
        self.colormapIcon = qt.QIcon(qt.QPixmap(IconDict["colormap"]))
        self.selectionIcon = qt.QIcon(qt.QPixmap(IconDict["normal"]))
        self.zoomResetIcon = qt.QIcon(qt.QPixmap(IconDict["zoomreset"]))
        self.polygonIcon = qt.QIcon(qt.QPixmap(IconDict["polygon"]))
        self.printIcon = qt.QIcon(qt.QPixmap(IconDict["fileprint"]))
        self.saveIcon = qt.QIcon(qt.QPixmap(IconDict["filesave"]))
        self.xAutoIcon = qt.QIcon(qt.QPixmap(IconDict["xauto"]))
        self.yAutoIcon = qt.QIcon(qt.QPixmap(IconDict["yauto"]))
        self.hFlipIcon = qt.QIcon(qt.QPixmap(IconDict["gioconda16mirror"]))
        self.imageIcon = qt.QIcon(qt.QPixmap(IconDict["image"]))
        self.eraseSelectionIcon = qt.QIcon(qt.QPixmap(IconDict["eraseselect"]))
        self.rectSelectionIcon = qt.QIcon(qt.QPixmap(IconDict["boxselect"]))
        self.brushSelectionIcon = qt.QIcon(qt.QPixmap(IconDict["brushselect"]))
        self.brushIcon = qt.QIcon(qt.QPixmap(IconDict["brush"]))
        self.additionalIcon = qt.QIcon(qt.QPixmap(
            IconDict["additionalselect"]))
        self.hLineIcon = qt.QIcon(qt.QPixmap(IconDict["horizontal"]))
        self.vLineIcon = qt.QIcon(qt.QPixmap(IconDict["vertical"]))
        self.lineIcon = qt.QIcon(qt.QPixmap(IconDict["diagonal"]))
        self.copyIcon = silx_icons.getQIcon("edit-copy")

        self.toolBar = qt.QWidget(self)
        self.toolBarLayout = qt.QHBoxLayout(self.toolBar)
        self.toolBarLayout.setContentsMargins(0, 0, 0, 0)
        self.toolBarLayout.setSpacing(0)
        self.mainLayout.addWidget(self.toolBar)
        #Autoscale
        if standalonezoom:
            tb = self._addToolButton(self.zoomResetIcon, self.__zoomReset,
                                     'Auto-Scale the Graph')
        else:
            tb = self._addToolButton(self.zoomResetIcon, None,
                                     'Auto-Scale the Graph')
        self.zoomResetToolButton = tb
        #y Autoscale
        tb = self._addToolButton(self.yAutoIcon,
                                 self._yAutoScaleToggle,
                                 'Toggle Autoscale Y Axis (On/Off)',
                                 toggle=True,
                                 state=True)
        tb.setDown(True)

        self.yAutoScaleToolButton = tb
        tb.setDown(True)

        #x Autoscale
        tb = self._addToolButton(self.xAutoIcon,
                                 self._xAutoScaleToggle,
                                 'Toggle Autoscale X Axis (On/Off)',
                                 toggle=True,
                                 state=True)
        self.xAutoScaleToolButton = tb
        tb.setDown(True)

        #Aspect ratio
        if aspect:
            self.aspectButton = self._addToolButton(self.solidCircleIcon,
                                                    self._aspectButtonSignal,
                                                    'Keep data aspect ratio',
                                                    toggle=False)
            self.aspectButton.setChecked(False)

        #colormap
        if colormap:
            tb = self._addToolButton(self.colormapIcon, None,
                                     'Change Colormap')
            self.colormapToolButton = tb

        #flip
        tb = self._addToolButton(self.hFlipIcon, None, 'Flip Horizontal')
        self.hFlipToolButton = tb

        #save
        if standalonesave:
            tb = self._addToolButton(self.saveIcon, self._saveIconSignal,
                                     'Save Graph')
        else:
            tb = self._addToolButton(self.saveIcon, None, 'Save')
        self.saveToolButton = tb

        self.copyToolButton = self._addToolButton(self.copyIcon,
                                                  self._copyIconSignal,
                                                  "Copy graph to clipboard")

        #Selection
        if selection:
            tb = self._addToolButton(self.selectionIcon,
                                     None,
                                     'Toggle Selection Mode',
                                     toggle=True,
                                     state=False)
            tb.setDown(False)
            self.selectionToolButton = tb
        #image selection icons
        if imageicons:
            tb = self._addToolButton(self.imageIcon, None, 'Reset')
            self.imageToolButton = tb

            tb = self._addToolButton(self.eraseSelectionIcon, None,
                                     'Erase Selection')
            self.eraseSelectionToolButton = tb

            tb = self._addToolButton(self.rectSelectionIcon, None,
                                     'Rectangular Selection')
            self.rectSelectionToolButton = tb

            tb = self._addToolButton(self.brushSelectionIcon, None,
                                     'Brush Selection')
            self.brushSelectionToolButton = tb

            tb = self._addToolButton(self.brushIcon, None, 'Select Brush')
            self.brushToolButton = tb

            if polygon:
                tb = self._addToolButton(
                    self.polygonIcon, None,
                    'Polygon selection\nRight click to finish')
                self.polygonSelectionToolButton = tb

            tb = self._addToolButton(self.additionalIcon, None,
                                     'Additional Selections Menu')
            self.additionalSelectionToolButton = tb
        else:
            if polygon:
                tb = self._addToolButton(
                    self.polygonIcon, None,
                    'Polygon selection\nRight click to finish')
                self.polygonSelectionToolButton = tb
            self.imageToolButton = None
        #picker selection
        self._pickerSelectionButtons = []
        if profileselection:
            self._profileSelection = True
            self._polygonSelection = False
            self._pickerSelectionButtons = []
            if self._profileSelection:
                tb = self._addToolButton(self.hLineIcon,
                                         self._hLineProfileClicked,
                                         'Horizontal Profile Selection',
                                         toggle=True,
                                         state=False)
                self.hLineProfileButton = tb
                self._pickerSelectionButtons.append(tb)

                tb = self._addToolButton(self.vLineIcon,
                                         self._vLineProfileClicked,
                                         'Vertical Profile Selection',
                                         toggle=True,
                                         state=False)
                self.vLineProfileButton = tb
                self._pickerSelectionButtons.append(tb)

                tb = self._addToolButton(self.lineIcon,
                                         self._lineProfileClicked,
                                         'Line Profile Selection',
                                         toggle=True,
                                         state=False)
                self.lineProfileButton = tb
                self._pickerSelectionButtons.append(tb)

                self._pickerSelectionWidthLabel = qt.QLabel(self.toolBar)
                self._pickerSelectionWidthLabel.setText("W:")
                self.toolBar.layout().addWidget(
                    self._pickerSelectionWidthLabel)
                self._pickerSelectionWidthValue = qt.QSpinBox(self.toolBar)
                self._pickerSelectionWidthValue.setMinimum(0)
                self._pickerSelectionWidthValue.setMaximum(1000)
                self._pickerSelectionWidthValue.setValue(1)
                self.toolBar.layout().addWidget(
                    self._pickerSelectionWidthValue)
                #tb = self._addToolButton(None,
                #                     self._lineProfileClicked,
                #                     'Line Profile Selection',
                #                     toggle=True,
                #                     state=False)
                #tb.setText = "W:"
                #self.lineWidthProfileButton = tb
                #self._pickerSelectionButtons.append(tb)
            if self._polygonSelection:
                _logger.info("Polygon selection not implemented yet")
        #hide profile selection buttons
        if imageicons:
            for button in self._pickerSelectionButtons:
                button.hide()

        self.infoWidget = qt.QWidget(self.toolBar)
        self.infoWidget.mainLayout = qt.QHBoxLayout(self.infoWidget)
        self.infoWidget.mainLayout.setContentsMargins(0, 0, 0, 0)
        self.infoWidget.mainLayout.setSpacing(0)
        self.infoWidget.label = qt.QLabel(self.infoWidget)
        self.infoWidget.label.setText("X = ???? Y = ???? Z = ????")
        self.infoWidget.mainLayout.addWidget(self.infoWidget.label)
        self.toolBarLayout.addWidget(self.infoWidget)
        self.infoWidget.hide()

        self.toolBarLayout.addWidget(qt.HorizontalSpacer(self.toolBar))

        # ---print
        self.printPreview = SingletonPrintPreviewToolButton(parent=self,
                                                            plot=self.graph)
        self.printPreview.setIcon(self.printIcon)
        self.toolBarLayout.addWidget(self.printPreview)

    def _aspectButtonSignal(self):
        _logger.debug("_aspectButtonSignal")
        if self._keepDataAspectRatioFlag:
            self.keepDataAspectRatio(False)
        else:
            self.keepDataAspectRatio(True)

    def keepDataAspectRatio(self, flag=True):
        if flag:
            self._keepDataAspectRatioFlag = True
            self.aspectButton.setIcon(self.solidEllipseIcon)
            self.aspectButton.setToolTip("Set free data aspect ratio")
        else:
            self._keepDataAspectRatioFlag = False
            self.aspectButton.setIcon(self.solidCircleIcon)
            self.aspectButton.setToolTip("Keep data aspect ratio")
        self.graph.setKeepDataAspectRatio(self._keepDataAspectRatioFlag)

    def showInfo(self):
        self.infoWidget.show()

    def hideInfo(self):
        self.infoWidget.hide()

    def setInfoText(self, text):
        self.infoWidget.label.setText(text)

    def setMouseText(self, text=""):
        try:
            if len(text):
                qt.QToolTip.showText(self.cursor().pos(), text, self,
                                     qt.QRect())
            else:
                qt.QToolTip.hideText()
        except:
            _logger.warning("Error trying to show mouse text <%s>" % text)

    def focusOutEvent(self, ev):
        qt.QToolTip.hideText()

    def infoText(self):
        return self.infoWidget.label.text()

    def setXLabel(self, label="Column"):
        return self.graph.setGraphXLabel(label)

    def setYLabel(self, label="Row"):
        return self.graph.setGraphYLabel(label)

    def getXLabel(self):
        return self.graph.getGraphXLabel()

    def getYLabel(self):
        return self.graph.getGraphYLabel()

    def hideImageIcons(self):
        if self.imageToolButton is None: return
        self.imageToolButton.hide()
        self.eraseSelectionToolButton.hide()
        self.rectSelectionToolButton.hide()
        self.brushSelectionToolButton.hide()
        self.brushToolButton.hide()
        if hasattr(self, "polygonSelectionToolButton"):
            self.polygonSelectionToolButton.hide()
        self.additionalSelectionToolButton.hide()

    def showImageIcons(self):
        if self.imageToolButton is None: return
        self.imageToolButton.show()
        self.eraseSelectionToolButton.show()
        self.rectSelectionToolButton.show()
        self.brushSelectionToolButton.show()
        self.brushToolButton.show()
        if hasattr(self, "polygonSelectionToolButton"):
            self.polygonSelectionToolButton.show()
        self.additionalSelectionToolButton.show()

    def _hLineProfileClicked(self):
        for button in self._pickerSelectionButtons:
            if button != self.hLineProfileButton:
                button.setChecked(False)

        if self.hLineProfileButton.isChecked():
            self._setPickerSelectionMode("HORIZONTAL")
        else:
            self._setPickerSelectionMode(None)

    def _vLineProfileClicked(self):
        for button in self._pickerSelectionButtons:
            if button != self.vLineProfileButton:
                button.setChecked(False)
        if self.vLineProfileButton.isChecked():
            self._setPickerSelectionMode("VERTICAL")
        else:
            self._setPickerSelectionMode(None)

    def _lineProfileClicked(self):
        for button in self._pickerSelectionButtons:
            if button != self.lineProfileButton:
                button.setChecked(False)
        if self.lineProfileButton.isChecked():
            self._setPickerSelectionMode("LINE")
        else:
            self._setPickerSelectionMode(None)

    def setPickerSelectionWith(self, intValue):
        self._pickerSelectionWidthValue.setValue(intValue)
        #get the current mode
        mode = "NONE"
        for button in self._pickerSelectionButtons:
            if button.isChecked():
                if button == self.hLineProfileButton:
                    mode = "HORIZONTAL"
                elif button == self.vLineProfileButton:
                    mode = "VERTICAL"
                elif button == self.lineProfileButton:
                    mode = "LINE"
        ddict = {}
        ddict['event'] = "profileWidthChanged"
        ddict['pixelwidth'] = self._pickerSelectionWidthValue.value()
        ddict['mode'] = mode
        self.sigProfileSignal.emit(ddict)

    def hideProfileSelectionIcons(self):
        if not len(self._pickerSelectionButtons):
            return
        for button in self._pickerSelectionButtons:
            button.setChecked(False)
            button.hide()
        self._pickerSelectionWidthLabel.hide()
        self._pickerSelectionWidthValue.hide()
        if self.graph.getInteractiveMode()['mode'] == 'draw':
            self.graph.setInteractiveMode('select')

    def showProfileSelectionIcons(self):
        if not len(self._pickerSelectionButtons):
            return
        for button in self._pickerSelectionButtons:
            button.show()
        self._pickerSelectionWidthLabel.show()
        self._pickerSelectionWidthValue.show()

    def getPickerSelectionMode(self):
        if not len(self._pickerSelectionButtons):
            return None
        if self.hLineProfileButton.isChecked():
            return "HORIZONTAL"
        if self.vLineProfileButton.isChecked():
            return "VERTICAL"
        if self.lineProfileButton.isChecked():
            return "LINE"
        return None

    def _setPickerSelectionMode(self, mode=None):
        if mode is None:
            self.graph.setInteractiveMode('zoom')
        else:
            if mode == "HORIZONTAL":
                shape = "hline"
            elif mode == "VERTICAL":
                shape = "vline"
            else:
                shape = "line"
            self.graph.setInteractiveMode('draw', shape=shape, label=mode)
        ddict = {}
        if mode is None:
            mode = "NONE"
        ddict['event'] = "profileModeChanged"
        ddict['mode'] = mode
        self.sigProfileSignal.emit(ddict)

    def _graphPolygonSignalReceived(self, ddict):
        _logger.debug("PolygonSignal Received")
        for key in ddict.keys():
            _logger.debug("%s: %s", key, ddict[key])

        if ddict['event'] not in ['drawingProgress', 'drawingFinished']:
            return
        label = ddict['parameters']['label']
        if label not in ['HORIZONTAL', 'VERTICAL', 'LINE']:
            return
        ddict['mode'] = label
        ddict['pixelwidth'] = self._pickerSelectionWidthValue.value()
        self.sigProfileSignal.emit(ddict)

    def _addToolButton(self,
                       icon,
                       action,
                       tip,
                       toggle=None,
                       state=None,
                       position=None):
        tb = qt.QToolButton(self.toolBar)
        if icon is not None:
            tb.setIcon(icon)
        tb.setToolTip(tip)
        if toggle is not None:
            if toggle:
                tb.setCheckable(1)
                if state is not None:
                    if state:
                        tb.setChecked(state)
                else:
                    tb.setChecked(False)
        if position is not None:
            self.toolBarLayout.insertWidget(position, tb)
        else:
            self.toolBarLayout.addWidget(tb)
        if action is not None:
            tb.clicked.connect(action)
        return tb

    def __zoomReset(self):
        self._zoomReset()

    def _zoomReset(self, replot=None):
        _logger.debug("_zoomReset")
        if self.graph is not None:
            self.graph.resetZoom()

    def _yAutoScaleToggle(self):
        if self.graph is not None:
            self.yAutoScaleToolButton.setDown(
                not self.graph.isYAxisAutoScale())
            self.graph.setYAxisAutoScale(not self.graph.isYAxisAutoScale())

    def _xAutoScaleToggle(self):
        if self.graph is not None:
            self.xAutoScaleToolButton.setDown(
                not self.graph.isXAxisAutoScale())
            self.graph.setXAxisAutoScale(not self.graph.isXAxisAutoScale())

    def _copyIconSignal(self):
        pngFile = BytesIO()
        self.graph.saveGraph(pngFile, fileFormat='png')
        pngFile.flush()
        pngFile.seek(0)
        pngData = pngFile.read()
        pngFile.close()
        image = qt.QImage.fromData(pngData, 'png')
        qt.QApplication.clipboard().setImage(image)

    def _saveIconSignal(self):
        self.saveDirectory = PyMcaDirs.outputDir

        fileTypeList = [
            "Image *.png", "Image *.jpg", "ZoomedImage *.png",
            "ZoomedImage *.jpg", "Widget *.png", "Widget *.jpg"
        ]

        outfile = qt.QFileDialog(self)
        outfile.setModal(1)
        outfile.setWindowTitle("Output File Selection")
        if hasattr(qt, "QStringList"):
            strlist = qt.QStringList()
        else:
            strlist = []
        for f in fileTypeList:
            strlist.append(f)
        if hasattr(outfile, "setFilters"):
            outfile.setFilters(strlist)
        else:
            outfile.setNameFilters(strlist)
        outfile.setFileMode(outfile.AnyFile)
        outfile.setAcceptMode(qt.QFileDialog.AcceptSave)
        outfile.setDirectory(self.saveDirectory)
        ret = outfile.exec_()

        if not ret:
            return
        if hasattr(outfile, "selectedFilter"):
            filterused = qt.safe_str(outfile.selectedFilter()).split()
        else:
            filterused = qt.safe_str(outfile.selectedNameFilter()).split()
        filetype = filterused[0]
        extension = filterused[1]

        outstr = qt.safe_str(outfile.selectedFiles()[0])

        try:
            outputFile = os.path.basename(outstr)
        except:
            outputFile = outstr
        outputDir = os.path.dirname(outstr)
        self.saveDirectory = outputDir
        PyMcaDirs.outputDir = outputDir

        #always overwrite for the time being
        if len(outputFile) < len(extension[1:]):
            outputFile += extension[1:]
        elif outputFile[-4:] != extension[1:]:
            outputFile += extension[1:]
        outputFile = os.path.join(outputDir, outputFile)
        if os.path.exists(outputFile):
            try:
                os.remove(outputFile)
            except:
                qt.QMessageBox.critical(self, "Save Error",
                                        "Cannot overwrite existing file")
                return

        if filetype.upper() == "IMAGE":
            self.saveGraphImage(outputFile, original=True)
        elif filetype.upper() == "ZOOMEDIMAGE":
            self.saveGraphImage(outputFile, original=False)
        else:
            self.saveGraphWidget(outputFile)

    def saveGraphImage(self, filename, original=False):
        format_ = filename[-3:].upper()
        activeImage = self.graph.getActiveImage()
        rgbdata = activeImage.getRgbaImageData()
        # silx to pymca scale convention (a + b x)
        xScale = activeImage.getOrigin()[0], activeImage.getScale()[0]
        yScale = activeImage.getOrigin()[1], activeImage.getScale()[1]
        if original:
            # save whole image
            bgradata = numpy.array(rgbdata, copy=True)
            bgradata[:, :, 0] = rgbdata[:, :, 2]
            bgradata[:, :, 2] = rgbdata[:, :, 0]
        else:
            shape = rgbdata.shape[:2]
            xmin, xmax = self.graph.getGraphXLimits()
            ymin, ymax = self.graph.getGraphYLimits()
            # save zoomed image, for that we have to get the limits
            r0, c0 = convertToRowAndColumn(xmin,
                                           ymin,
                                           shape,
                                           xScale=xScale,
                                           yScale=yScale,
                                           safe=True)
            r1, c1 = convertToRowAndColumn(xmax,
                                           ymax,
                                           shape,
                                           xScale=xScale,
                                           yScale=yScale,
                                           safe=True)
            row0 = int(min(r0, r1))
            row1 = int(max(r0, r1))
            col0 = int(min(c0, c1))
            col1 = int(max(c0, c1))
            if row1 < shape[0]:
                row1 += 1
            if col1 < shape[1]:
                col1 += 1
            tmpArray = rgbdata[row0:row1, col0:col1, :]
            bgradata = numpy.array(tmpArray, copy=True, dtype=rgbdata.dtype)
            bgradata[:, :, 0] = tmpArray[:, :, 2]
            bgradata[:, :, 2] = tmpArray[:, :, 0]
        if self.graph.isYAxisInverted():
            qImage = qt.QImage(bgradata, bgradata.shape[1], bgradata.shape[0],
                               qt.QImage.Format_ARGB32)
        else:
            qImage = qt.QImage(bgradata, bgradata.shape[1], bgradata.shape[0],
                               qt.QImage.Format_ARGB32).mirrored(False, True)
        pixmap = qt.QPixmap.fromImage(qImage)
        if pixmap.save(filename, format_):
            return
        else:
            qt.QMessageBox.critical(self, "Save Error",
                                    "%s" % sys.exc_info()[1])
            return

    def saveGraphWidget(self, filename):
        format_ = filename[-3:].upper()
        if hasattr(qt.QPixmap, "grabWidget"):
            # Qt4
            pixmap = qt.QPixmap.grabWidget(self.graph.getWidgetHandle())
        else:
            # Qt5
            pixmap = self.graph.getWidgetHandle().grab()
        if pixmap.save(filename, format_):
            return
        else:
            qt.QMessageBox.critical(self, "Save Error",
                                    "%s" % sys.exc_info()[1])
            return

    def setSaveDirectory(self, wdir):
        if os.path.exists(wdir):
            self.saveDirectory = wdir
            return True
        else:
            return False

    def selectColormap(self):
        qt.QMessageBox.information(self, "Open", "Not implemented (yet)")

    def _zoomBack(self, pos):
        self.graph.getLimitsHistory().pop()
Ejemplo n.º 25
0
    def getCommentAndPosition(self):
        legends = self.getPlot().getAllCurves(just_legend=True)
        comment = "Curves displayed in widget 1:\n\t"
        if legends:
            comment += ", ".join(legends)
        else:
            comment += "none"
        return comment, "CENTER"


app = qt.QApplication([])

x = numpy.arange(1000)

# first widget has a standalone preview action with custom title and comment
pw1 = PlotWidget()
pw1.setWindowTitle("Widget 1 with standalone print preview")
toolbar1 = qt.QToolBar(pw1)
toolbutton1 = MyPrintPreviewButton(parent=toolbar1, plot=pw1)
pw1.addToolBar(toolbar1)
toolbar1.addWidget(toolbutton1)
pw1.show()
pw1.addCurve(x, numpy.tan(x * 2 * numpy.pi / 1000))

# next two plots share a common standard print preview
pw2 = PlotWidget()
pw2.setWindowTitle("Widget 2 with shared print preview")
toolbar2 = qt.QToolBar(pw2)
toolbutton2 = PrintPreviewToolButton.SingletonPrintPreviewToolButton(
        parent=toolbar2, plot=pw2)
pw2.addToolBar(toolbar2)
Ejemplo n.º 26
0
 def setActiveCurve(self, legend, replot=True):
     return PlotWidget.setActiveCurve(self, legend)
Ejemplo n.º 27
0
 def removeCurve(self, *var, **kw):
     if "replot" in kw:
         del kw["replot"]
         # silx schedules replots, explicit replot call
         # should not be needed
     return PlotWidget.removeCurve(self, *var, **kw)
Ejemplo n.º 28
0
    def __init__(self, parent=None):
        qt.QMainWindow.__init__(self, parent=parent)
        if parent is not None:
            # behave as a widget
            self.setWindowFlags(qt.Qt.Widget)
        else:
            self.setWindowTitle("PyMca - Image Selection Tool")

        centralWidget = qt.QWidget(self)
        layout = qt.QVBoxLayout(centralWidget)
        centralWidget.setLayout(layout)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)

        # Plot
        self.plot = PlotWidget(parent=centralWidget)
        self.plot.setWindowFlags(qt.Qt.Widget)
        self.plot.setDefaultColormap({'name': 'temperature',
                                      'normalization': 'linear',
                                      'autoscale': True,
                                      'vmin': 0.,
                                      'vmax': 1.})

        layout.addWidget(self.plot)

        # Mask Widget
        self._maskToolsDockWidget = None

        # Image selection slider
        self.slider = qt.QSlider(self.centralWidget())
        self.slider.setOrientation(qt.Qt.Horizontal)
        self.slider.setMinimum(0)
        self.slider.setMaximum(0)
        layout.addWidget(self.slider)
        self.slider.valueChanged[int].connect(self.showImage)

        # ADD/REMOVE/REPLACE IMAGE buttons
        buttonBox = qt.QWidget(self)
        buttonBoxLayout = qt.QHBoxLayout(buttonBox)
        buttonBoxLayout.setContentsMargins(0, 0, 0, 0)
        buttonBoxLayout.setSpacing(0)
        self.addImageButton = qt.QPushButton(buttonBox)
        icon = qt.QIcon(qt.QPixmap(IconDict["rgb16"]))
        self.addImageButton.setIcon(icon)
        self.addImageButton.setText("ADD IMAGE")
        self.addImageButton.setToolTip("Add image to RGB correlator")
        buttonBoxLayout.addWidget(self.addImageButton)

        self.removeImageButton = qt.QPushButton(buttonBox)
        self.removeImageButton.setIcon(icon)
        self.removeImageButton.setText("REMOVE IMAGE")
        self.removeImageButton.setToolTip("Remove image from RGB correlator")
        buttonBoxLayout.addWidget(self.removeImageButton)

        self.replaceImageButton = qt.QPushButton(buttonBox)
        self.replaceImageButton.setIcon(icon)
        self.replaceImageButton.setText("REPLACE IMAGE")
        self.replaceImageButton.setToolTip(
                "Replace all images in RGB correlator with this one")
        buttonBoxLayout.addWidget(self.replaceImageButton)

        self.addImageButton.clicked.connect(self._addImageClicked)
        self.removeImageButton.clicked.connect(self._removeImageClicked)
        self.replaceImageButton.clicked.connect(self._replaceImageClicked)

        layout.addWidget(buttonBox)

        # median filter widget
        self._medianParameters = {'row_width': 1,
                                  'column_width': 1,
                                  'conditional': 0}
        self._medianParametersWidget = MedianParameters(self,
                                                        use_conditional=True)
        self._medianParametersWidget.widthSpin.setValue(1)
        self._medianParametersWidget.widthSpin.valueChanged[int].connect(
                     self._setMedianKernelWidth)
        self._medianParametersWidget.conditionalSpin.valueChanged[int].connect(
                     self._setMedianConditionalFlag)
        layout.addWidget(self._medianParametersWidget)

        # motor positions (hidden by default)
        self.motorPositionsWidget = MotorInfoWindow.MotorInfoDialog(self,
                                                                    [""],
                                                                    [{}])
        self.motorPositionsWidget.setMaximumHeight(100)
        self.plot.sigPlotSignal.connect(self._updateMotors)
        self.motorPositionsWidget.hide()
        self._motors_first_update = True

        layout.addWidget(self.motorPositionsWidget)

        self.setCentralWidget(centralWidget)

        # Init actions
        self.group = qt.QActionGroup(self)
        self.group.setExclusive(False)

        self.resetZoomAction = self.group.addAction(
                PlotActions.ResetZoomAction(plot=self.plot, parent=self))
        self.addAction(self.resetZoomAction)

        self.zoomInAction = PlotActions.ZoomInAction(plot=self.plot, parent=self)
        self.addAction(self.zoomInAction)

        self.zoomOutAction = PlotActions.ZoomOutAction(plot=self.plot, parent=self)
        self.addAction(self.zoomOutAction)

        self.xAxisAutoScaleAction = self.group.addAction(
            PlotActions.XAxisAutoScaleAction(plot=self.plot, parent=self))
        self.addAction(self.xAxisAutoScaleAction)

        self.yAxisAutoScaleAction = self.group.addAction(
            PlotActions.YAxisAutoScaleAction(plot=self.plot, parent=self))
        self.addAction(self.yAxisAutoScaleAction)

        self.colormapAction = self.group.addAction(
                PlotActions.ColormapAction(plot=self.plot, parent=self))
        self.addAction(self.colormapAction)

        self.copyAction = self.group.addAction(
                PlotActions.CopyAction(plot=self.plot, parent=self))
        self.addAction(self.copyAction)

        self.group.addAction(self.getMaskAction())

        # Init toolbuttons
        self.saveToolbutton = SaveToolButton(parent=self,
                                             maskImageWidget=self)

        self.yAxisInvertedButton = PlotToolButtons.YAxisOriginToolButton(
            parent=self, plot=self.plot)

        self.keepDataAspectRatioButton = PlotToolButtons.AspectToolButton(
            parent=self, plot=self.plot)

        self.backgroundButton = qt.QToolButton(self)
        self.backgroundButton.setCheckable(True)
        self.backgroundButton.setIcon(qt.QIcon(qt.QPixmap(IconDict["subtract"])))
        self.backgroundButton.setToolTip(
            'Toggle background image subtraction from current image\n' +
            'No action if no background image available.')
        self.backgroundButton.clicked.connect(self._subtractBackground)

        # Creating the toolbar also create actions for toolbuttons
        self._toolbar = self._createToolBar(title='Plot', parent=None)
        self.addToolBar(self._toolbar)

        self._profile = ProfileToolBar(plot=self.plot)
        self.addToolBar(self._profile)
        self.setProfileToolbarVisible(False)

        # add a transparency slider for the stack data
        self._alphaSliderToolbar = qt.QToolBar("Alpha slider", parent=self)
        self._alphaSlider = NamedImageAlphaSlider(parent=self._alphaSliderToolbar,
                                                  plot=self.plot,
                                                  legend="current")
        self._alphaSlider.setOrientation(qt.Qt.Vertical)
        self._alphaSlider.setToolTip("Adjust opacity of stack image overlay")
        self._alphaSliderToolbar.addWidget(self._alphaSlider)
        self.addToolBar(qt.Qt.RightToolBarArea, self._alphaSliderToolbar)

        # hide optional tools and actions
        self.setAlphaSliderVisible(False)
        self.setBackgroundActionVisible(False)
        self.setMedianFilterWidgetVisible(False)
        self.setProfileToolbarVisible(False)

        self._images = []
        """List of images, as 2D numpy arrays or 3D numpy arrays (RGB(A)).
        """

        self._labels = []
        """List of image labels.
        """

        self._bg_images = []
        """List of background images, as 2D numpy arrays or 3D numpy arrays
        (RGB(A)).
        These images are not active, their colormap cannot be changed and
        they cannot be the base image used for drawing a mask.
        """

        self._bg_labels = []

        self._deltaXY = (1.0, 1.0)             # TODO: allow different scale and origin for each image
        """Current image scale (Xscale, Yscale) (in axis units per image pixel).
        The scale is adjusted to keep constant width and height for the image
        when a crop operation is applied."""

        self._origin = (0., 0.)
        """Current image origin: coordinate (x, y) of sample located at
        (row, column) = (0, 0)"""

        # scales and origins for background images
        self._bg_deltaXY = []
        self._bg_origins = []
Ejemplo n.º 29
0
class TestLimitConstaints(unittest.TestCase):
    """Tests setLimitConstaints class"""

    def setUp(self):
        self.plot = PlotWidget()

    def tearDown(self):
        self.plot = None

    def testApi(self):
        """Test availability of the API"""
        self.plot.getXAxis().setLimitsConstraints(minPos=1, maxPos=10)
        self.plot.getXAxis().setRangeConstraints(minRange=1, maxRange=1)
        self.plot.getYAxis().setLimitsConstraints(minPos=1, maxPos=10)
        self.plot.getYAxis().setRangeConstraints(minRange=1, maxRange=1)

    def testXMinMax(self):
        """Test limit constains on x-axis"""
        self.plot.getXAxis().setLimitsConstraints(minPos=0, maxPos=100)
        self.plot.setLimits(xmin=-1, xmax=101, ymin=-1, ymax=101)
        self.assertEqual(self.plot.getXAxis().getLimits(), (0, 100))
        self.assertEqual(self.plot.getYAxis().getLimits(), (-1, 101))

    def testYMinMax(self):
        """Test limit constains on y-axis"""
        self.plot.getYAxis().setLimitsConstraints(minPos=0, maxPos=100)
        self.plot.setLimits(xmin=-1, xmax=101, ymin=-1, ymax=101)
        self.assertEqual(self.plot.getXAxis().getLimits(), (-1, 101))
        self.assertEqual(self.plot.getYAxis().getLimits(), (0, 100))

    def testMinXRange(self):
        """Test min range constains on x-axis"""
        self.plot.getXAxis().setRangeConstraints(minRange=100)
        self.plot.setLimits(xmin=1, xmax=99, ymin=1, ymax=99)
        limits = self.plot.getXAxis().getLimits()
        self.assertEqual(limits[1] - limits[0], 100)
        limits = self.plot.getYAxis().getLimits()
        self.assertNotEqual(limits[1] - limits[0], 100)

    def testMaxXRange(self):
        """Test max range constains on x-axis"""
        self.plot.getXAxis().setRangeConstraints(maxRange=100)
        self.plot.setLimits(xmin=-1, xmax=101, ymin=-1, ymax=101)
        limits = self.plot.getXAxis().getLimits()
        self.assertEqual(limits[1] - limits[0], 100)
        limits = self.plot.getYAxis().getLimits()
        self.assertNotEqual(limits[1] - limits[0], 100)

    def testMinYRange(self):
        """Test min range constains on y-axis"""
        self.plot.getYAxis().setRangeConstraints(minRange=100)
        self.plot.setLimits(xmin=1, xmax=99, ymin=1, ymax=99)
        limits = self.plot.getXAxis().getLimits()
        self.assertNotEqual(limits[1] - limits[0], 100)
        limits = self.plot.getYAxis().getLimits()
        self.assertEqual(limits[1] - limits[0], 100)

    def testMaxYRange(self):
        """Test max range constains on y-axis"""
        self.plot.getYAxis().setRangeConstraints(maxRange=100)
        self.plot.setLimits(xmin=-1, xmax=101, ymin=-1, ymax=101)
        limits = self.plot.getXAxis().getLimits()
        self.assertNotEqual(limits[1] - limits[0], 100)
        limits = self.plot.getYAxis().getLimits()
        self.assertEqual(limits[1] - limits[0], 100)

    def testChangeOfConstraints(self):
        """Test changing of the constraints"""
        self.plot.getXAxis().setRangeConstraints(minRange=10, maxRange=10)
        # There is no more constraints on the range
        self.plot.getXAxis().setRangeConstraints(minRange=None, maxRange=None)
        self.plot.setLimits(xmin=-1, xmax=101, ymin=-1, ymax=101)
        self.assertEqual(self.plot.getXAxis().getLimits(), (-1, 101))

    def testSettingConstraints(self):
        """Test setting a constaint (setLimits first then the constaint)"""
        self.plot.setLimits(xmin=-1, xmax=101, ymin=-1, ymax=101)
        self.plot.getXAxis().setLimitsConstraints(minPos=0, maxPos=100)
        self.assertEqual(self.plot.getXAxis().getLimits(), (0, 100))
Ejemplo n.º 30
0
class SilxMaskImageWidget(qt.QMainWindow):
    """Main window with a plot widget, a toolbar and a slider.

    A list of images can be set with :meth:`setImages`.
    The mask can be accessed through getter and setter methods:
    :meth:`setSelectionMask` and :meth:`getSelectionMask`.

    The plot widget can be accessed as :attr:`plot`. It is a silx
    plot widget.

    The toolbar offers some basic interaction tools:
    zoom control, colormap, aspect ratio, y axis orientation,
    "save image" menu and a mask widget.
    """
    sigMaskImageWidget = qt.pyqtSignal(object)

    def __init__(self, parent=None):
        qt.QMainWindow.__init__(self, parent=parent)
        if parent is not None:
            # behave as a widget
            self.setWindowFlags(qt.Qt.Widget)
        else:
            self.setWindowTitle("PyMca - Image Selection Tool")

        centralWidget = qt.QWidget(self)
        layout = qt.QVBoxLayout(centralWidget)
        centralWidget.setLayout(layout)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)

        # Plot
        self.plot = PlotWidget(parent=centralWidget)
        self.plot.setWindowFlags(qt.Qt.Widget)
        self.plot.setDefaultColormap({'name': 'temperature',
                                      'normalization': 'linear',
                                      'autoscale': True,
                                      'vmin': 0.,
                                      'vmax': 1.})

        layout.addWidget(self.plot)

        # Mask Widget
        self._maskToolsDockWidget = None

        # Image selection slider
        self.slider = qt.QSlider(self.centralWidget())
        self.slider.setOrientation(qt.Qt.Horizontal)
        self.slider.setMinimum(0)
        self.slider.setMaximum(0)
        layout.addWidget(self.slider)
        self.slider.valueChanged[int].connect(self.showImage)

        # ADD/REMOVE/REPLACE IMAGE buttons
        buttonBox = qt.QWidget(self)
        buttonBoxLayout = qt.QHBoxLayout(buttonBox)
        buttonBoxLayout.setContentsMargins(0, 0, 0, 0)
        buttonBoxLayout.setSpacing(0)
        self.addImageButton = qt.QPushButton(buttonBox)
        icon = qt.QIcon(qt.QPixmap(IconDict["rgb16"]))
        self.addImageButton.setIcon(icon)
        self.addImageButton.setText("ADD IMAGE")
        self.addImageButton.setToolTip("Add image to RGB correlator")
        buttonBoxLayout.addWidget(self.addImageButton)

        self.removeImageButton = qt.QPushButton(buttonBox)
        self.removeImageButton.setIcon(icon)
        self.removeImageButton.setText("REMOVE IMAGE")
        self.removeImageButton.setToolTip("Remove image from RGB correlator")
        buttonBoxLayout.addWidget(self.removeImageButton)

        self.replaceImageButton = qt.QPushButton(buttonBox)
        self.replaceImageButton.setIcon(icon)
        self.replaceImageButton.setText("REPLACE IMAGE")
        self.replaceImageButton.setToolTip(
                "Replace all images in RGB correlator with this one")
        buttonBoxLayout.addWidget(self.replaceImageButton)

        self.addImageButton.clicked.connect(self._addImageClicked)
        self.removeImageButton.clicked.connect(self._removeImageClicked)
        self.replaceImageButton.clicked.connect(self._replaceImageClicked)

        layout.addWidget(buttonBox)

        # median filter widget
        self._medianParameters = {'row_width': 1,
                                  'column_width': 1,
                                  'conditional': 0}
        self._medianParametersWidget = MedianParameters(self,
                                                        use_conditional=True)
        self._medianParametersWidget.widthSpin.setValue(1)
        self._medianParametersWidget.widthSpin.valueChanged[int].connect(
                     self._setMedianKernelWidth)
        self._medianParametersWidget.conditionalSpin.valueChanged[int].connect(
                     self._setMedianConditionalFlag)
        layout.addWidget(self._medianParametersWidget)

        # motor positions (hidden by default)
        self.motorPositionsWidget = MotorInfoWindow.MotorInfoDialog(self,
                                                                    [""],
                                                                    [{}])
        self.motorPositionsWidget.setMaximumHeight(100)
        self.plot.sigPlotSignal.connect(self._updateMotors)
        self.motorPositionsWidget.hide()
        self._motors_first_update = True

        layout.addWidget(self.motorPositionsWidget)

        self.setCentralWidget(centralWidget)

        # Init actions
        self.group = qt.QActionGroup(self)
        self.group.setExclusive(False)

        self.resetZoomAction = self.group.addAction(
                PlotActions.ResetZoomAction(plot=self.plot, parent=self))
        self.addAction(self.resetZoomAction)

        self.zoomInAction = PlotActions.ZoomInAction(plot=self.plot, parent=self)
        self.addAction(self.zoomInAction)

        self.zoomOutAction = PlotActions.ZoomOutAction(plot=self.plot, parent=self)
        self.addAction(self.zoomOutAction)

        self.xAxisAutoScaleAction = self.group.addAction(
            PlotActions.XAxisAutoScaleAction(plot=self.plot, parent=self))
        self.addAction(self.xAxisAutoScaleAction)

        self.yAxisAutoScaleAction = self.group.addAction(
            PlotActions.YAxisAutoScaleAction(plot=self.plot, parent=self))
        self.addAction(self.yAxisAutoScaleAction)

        self.colormapAction = self.group.addAction(
                PlotActions.ColormapAction(plot=self.plot, parent=self))
        self.addAction(self.colormapAction)

        self.copyAction = self.group.addAction(
                PlotActions.CopyAction(plot=self.plot, parent=self))
        self.addAction(self.copyAction)

        self.group.addAction(self.getMaskAction())

        # Init toolbuttons
        self.saveToolbutton = SaveToolButton(parent=self,
                                             maskImageWidget=self)

        self.yAxisInvertedButton = PlotToolButtons.YAxisOriginToolButton(
            parent=self, plot=self.plot)

        self.keepDataAspectRatioButton = PlotToolButtons.AspectToolButton(
            parent=self, plot=self.plot)

        self.backgroundButton = qt.QToolButton(self)
        self.backgroundButton.setCheckable(True)
        self.backgroundButton.setIcon(qt.QIcon(qt.QPixmap(IconDict["subtract"])))
        self.backgroundButton.setToolTip(
            'Toggle background image subtraction from current image\n' +
            'No action if no background image available.')
        self.backgroundButton.clicked.connect(self._subtractBackground)

        # Creating the toolbar also create actions for toolbuttons
        self._toolbar = self._createToolBar(title='Plot', parent=None)
        self.addToolBar(self._toolbar)

        self._profile = ProfileToolBar(plot=self.plot)
        self.addToolBar(self._profile)
        self.setProfileToolbarVisible(False)

        # add a transparency slider for the stack data
        self._alphaSliderToolbar = qt.QToolBar("Alpha slider", parent=self)
        self._alphaSlider = NamedImageAlphaSlider(parent=self._alphaSliderToolbar,
                                                  plot=self.plot,
                                                  legend="current")
        self._alphaSlider.setOrientation(qt.Qt.Vertical)
        self._alphaSlider.setToolTip("Adjust opacity of stack image overlay")
        self._alphaSliderToolbar.addWidget(self._alphaSlider)
        self.addToolBar(qt.Qt.RightToolBarArea, self._alphaSliderToolbar)

        # hide optional tools and actions
        self.setAlphaSliderVisible(False)
        self.setBackgroundActionVisible(False)
        self.setMedianFilterWidgetVisible(False)
        self.setProfileToolbarVisible(False)

        self._images = []
        """List of images, as 2D numpy arrays or 3D numpy arrays (RGB(A)).
        """

        self._labels = []
        """List of image labels.
        """

        self._bg_images = []
        """List of background images, as 2D numpy arrays or 3D numpy arrays
        (RGB(A)).
        These images are not active, their colormap cannot be changed and
        they cannot be the base image used for drawing a mask.
        """

        self._bg_labels = []

        self._deltaXY = (1.0, 1.0)             # TODO: allow different scale and origin for each image
        """Current image scale (Xscale, Yscale) (in axis units per image pixel).
        The scale is adjusted to keep constant width and height for the image
        when a crop operation is applied."""

        self._origin = (0., 0.)
        """Current image origin: coordinate (x, y) of sample located at
        (row, column) = (0, 0)"""

        # scales and origins for background images
        self._bg_deltaXY = []
        self._bg_origins = []

    def sizeHint(self):
        return qt.QSize(500, 400)

    def _createToolBar(self, title, parent):
        """Create a QToolBar with crop, rotate and flip operations

        :param str title: The title of the QMenu
        :param qt.QWidget parent: See :class:`QToolBar`
        """
        toolbar = qt.QToolBar(title, parent)

        # Order widgets with actions
        objects = self.group.actions()

        # Add standard push buttons to list
        index = objects.index(self.colormapAction)
        objects.insert(index + 1, self.keepDataAspectRatioButton)
        objects.insert(index + 2, self.yAxisInvertedButton)
        objects.insert(index + 3, self.saveToolbutton)
        objects.insert(index + 4, self.backgroundButton)
        for obj in objects:
            if isinstance(obj, qt.QAction):
                toolbar.addAction(obj)
            else:
                # keep reference to toolbutton's action for changing visibility
                if obj is self.keepDataAspectRatioButton:
                    self.keepDataAspectRatioAction = toolbar.addWidget(obj)
                elif obj is self.yAxisInvertedButton:
                    self.yAxisInvertedAction = toolbar.addWidget(obj)
                elif obj is self.saveToolbutton:
                    self.saveAction = toolbar.addWidget(obj)
                elif obj is self.backgroundButton:
                    self.bgAction = toolbar.addWidget(obj)
                else:
                    raise RuntimeError()

        return toolbar

    def _getMaskToolsDockWidget(self):
        """DockWidget with image mask panel (lazy-loaded)."""
        if self._maskToolsDockWidget is None:
            self._maskToolsDockWidget = MyMaskToolsDockWidget(
                plot=self.plot, name='Mask')
            self._maskToolsDockWidget.hide()
            self.addDockWidget(qt.Qt.RightDockWidgetArea,
                               self._maskToolsDockWidget)
            # self._maskToolsDockWidget.setFloating(True)
            self._maskToolsDockWidget.sigMaskChanged.connect(
                    self._emitMaskImageWidgetSignal)
        return self._maskToolsDockWidget

    def _setMedianKernelWidth(self, value):
        kernelSize = numpy.asarray(value)
        if len(kernelSize.shape) == 0:
            kernelSize = [kernelSize.item()] * 2
        self._medianParameters['row_width'] = kernelSize[0]
        self._medianParameters['column_width'] = kernelSize[1]
        self._medianParametersWidget.widthSpin.setValue(int(kernelSize[0]))
        self.showImage(self.slider.value())

    def _setMedianConditionalFlag(self, value):
        self._medianParameters['conditional'] = int(value)
        self._medianParametersWidget.conditionalSpin.setValue(int(value))
        self.showImage(self.slider.value())

    def _subtractBackground(self):
        """When background button is clicked, this causes showImage to
        display the data after subtracting the stack background image.

        This background image is unrelated to the background images set
        with :meth:`setBackgroundImages`, it is simply the first data image
        whose label ends with 'background'."""
        current = self.getCurrentIndex()
        self.showImage(current)

    def _updateMotors(self, ddict):
        if not ddict["event"] == "mouseMoved":
            return
        if not self.motorPositionsWidget.isVisible():
            return

        motorsValuesAtCursor = self._getPositionersFromXY(ddict["x"],
                                                          ddict["y"])
        if motorsValuesAtCursor is None:
            return

        self.motorPositionsWidget.table.updateTable(
                legList=[self.plot.getActiveImage().getLegend()],
                motList=[motorsValuesAtCursor])

        if self._motors_first_update:
            self._select_motors()
            self._motors_first_update = False

    def _select_motors(self):
        """This methods sets the motors in the comboboxes when the widget
        is first initialized."""
        for i, combobox in enumerate(self.motorPositionsWidget.table.header.boxes):
            # First item (index 0) in combobox is "", so first motor name is at index 1.
            # First combobox in header.boxes is at index 1 (boxes[0] is None).
            if i == 0:
                continue
            if i < combobox.count():
                combobox.setCurrentIndex(i)

    def _getPositionersFromXY(self, x, y):
        """Return positioner values for a stack pixel identified
        by it's (x, y) coordinates.
        """
        activeImage = self.plot.getActiveImage()
        if activeImage is None:
            return None
        info = activeImage.getInfo()
        if not info or not isinstance(info, dict):
            return None
        positioners = info.get("positioners", {})

        nRows, nCols = activeImage.getData().shape
        xScale, yScale = activeImage.getScale()
        xOrigin, yOrigin = activeImage.getOrigin()
        r, c = convertToRowAndColumn(
                x, y,
                shape=(nRows, nCols),
                xScale=(xOrigin, xScale),
                yScale=(yOrigin, yScale),
                safe=True)

        idx1d = r * nCols + c
        positionersAtIdx = {}

        for motorName, motorValues in positioners.items():
            if numpy.isscalar(motorValues):
                positionersAtIdx[motorName] = motorValues
            elif len(motorValues.shape) == 1:
                positionersAtIdx[motorName] = motorValues[idx1d]
            else:
                positionersAtIdx[motorName] = motorValues.reshape((-1,))[idx1d]

        return positionersAtIdx

    # widgets visibility toggling
    def setBackgroundActionVisible(self, visible):
        """Set visibility of the background toolbar button.

        :param visible: True to show tool button, False to hide it.
        """
        self.bgAction.setVisible(visible)

    def setProfileToolbarVisible(self, visible):
        """Set visibility of the profile toolbar

        :param visible: True to show toolbar, False to hide it.
        """
        self._profile.setVisible(visible)

    def setMedianFilterWidgetVisible(self, visible):
        """Set visibility of the median filter parametrs widget.

        :param visible: True to show widget, False to hide it.
        """
        self._medianParametersWidget.setVisible(visible)

    def setMotorPositionsVisible(self, flag):
        """Show or hide motor positions widget"""
        self.motorPositionsWidget.setVisible(flag)

    def setAlphaSliderVisible(self, visible):
        """Set visibility of the transparency slider widget in the right
        toolbar area.

        :param visible: True to show widget, False to hide it.
        """
        self._alphaSliderToolbar.setVisible(visible)

    def setImagesAlpha(self, alpha):
        """Set the opacity of the images layer.

        Full opacity means that the background images layer will not
        be visible.

        :param float alpha: Opacity of images layer, in [0., 1.]
        """
        self._alphaSlider.setValue(round(alpha * 255))

    def getMaskAction(self):
        """QAction toggling image mask dock widget

        :rtype: QAction
        """
        return self._getMaskToolsDockWidget().toggleViewAction()

    def _emitMaskImageWidgetSignal(self):
        mask = self.getSelectionMask()
        if not mask.size:
            # workaround to ignore the empty mask emitted when the mask widget
            # is initialized
            return
        self.sigMaskImageWidget.emit(
            {"event": "selectionMaskChanged",
             "current": self.getSelectionMask(),
             "id": id(self)})

    def setSelectionMask(self, mask, copy=True):
        """Set the mask to a new array.

        :param numpy.ndarray mask: The array to use for the mask.
                    Mask type: array of uint8 of dimension 2,
                    Array of other types are converted.
        :param bool copy: True (the default) to copy the array,
                          False to use it as is if possible.
        :return: None if failed, shape of mask as 2-tuple if successful.
                 The mask can be cropped or padded to fit active image,
                 the returned shape is that of the active image.
        """
        # disconnect temporarily to avoid infinite loop
        self._getMaskToolsDockWidget().sigMaskChanged.disconnect(
                    self._emitMaskImageWidgetSignal)
        if mask is None and silx.version_info <= (0, 7, 0):
            self._getMaskToolsDockWidget().resetSelectionMask()
            ret = None
        else:
            # from silx 0.8 onwards, setSelectionMask(None) is supported
            ret = self._getMaskToolsDockWidget().setSelectionMask(mask,
                                                                  copy=copy)
        self._getMaskToolsDockWidget().sigMaskChanged.connect(
                    self._emitMaskImageWidgetSignal)
        return ret

    def getSelectionMask(self, copy=True):
        """Get the current mask as a 2D array.

        :param bool copy: True (default) to get a copy of the mask.
                          If False, the returned array MUST not be modified.
        :return: The array of the mask with dimension of the 'active' image.
                 If there is no active image, None is returned.
        :rtype: 2D numpy.ndarray of uint8
        """
        return self._getMaskToolsDockWidget().getSelectionMask(copy=copy)

    @staticmethod
    def _RgbaToGrayscale(image):
        """Convert RGBA image to 2D array of grayscale values
        (Luma coding)

        :param image: RGBA image, as a numpy array of shapes
             (nrows, ncols, 3/4)
        :return: Image as a 2D array
        """
        if len(image.shape) == 2:
            return image
        assert len(image.shape) == 3

        imageData = image[:, :, 0] * 0.299 +\
                    image[:, :, 1] * 0.587 +\
                    image[:, :, 2] * 0.114
        return imageData

    def getImageData(self):
        """Return current image data to be sent to RGB correlator

        :return: Image as a 2D array
        """
        index = self.slider.value()
        image = self._images[index]
        return self._RgbaToGrayscale(image)

    def getFirstBgImageData(self):
        """Return first bg image data to be sent to RGB correlator
        :return: Image as a 2D array
        """
        image = self._bg_images[0]
        return self._RgbaToGrayscale(image)

    def getBgImagesDict(self):
        """Return a dict containing the data for all background images."""
        bgimages = {}
        for i, label in enumerate(self._bg_labels):
            data = self._bg_images[i]
            origin = self._bg_origins[i]
            delta_w, delta_h = self._bg_deltaXY[i]
            w, h = delta_w * data.shape[1], delta_h * data.shape[0]
            bgimages[label] = {"data": data,
                               "origin": origin,
                               "width": w,
                               "height": h}
        return bgimages

    def _addImageClicked(self):
        imageData = self.getImageData()
        ddict = {
            'event': "addImageClicked",
            'image': imageData,
            'title': self.plot.getGraphTitle(),
            'id': id(self)}
        self.sigMaskImageWidget.emit(ddict)

    def _replaceImageClicked(self):
        imageData = self.getImageData()
        ddict = {
            'event': "replaceImageClicked",
            'image': imageData,
            'title': self.plot.getGraphTitle(),
            'id': id(self)}
        self.sigMaskImageWidget.emit(ddict)

    def _removeImageClicked(self):
        imageData = self.getImageData()
        ddict = {
            'event': "removeImageClicked",
            'image': imageData,
            'title': self.plot.getGraphTitle(),
            'id': id(self)}
        self.sigMaskImageWidget.emit(ddict)

    def showImage(self, index=0):
        """Show data image corresponding to index. Update slider to index.
        """
        if not self._images:
            return
        assert index < len(self._images)

        bg_index = None
        if self.backgroundButton.isChecked():
            for i, imageName in enumerate(self._labels):
                if imageName.lower().endswith('background'):
                    bg_index = i
                    break

        mf_text = ""
        a = self._medianParameters['row_width']
        b = self._medianParameters['column_width']
        if max(a, b) > 1:
            mf_text = "MF(%d,%d) " % (a, b)

        imdata = self._getMedianData(self._images[index])
        if bg_index is None:
            self.plot.setGraphTitle(mf_text + self._labels[index])
        else:
            self.plot.setGraphTitle(mf_text + self._labels[index] + " Net")
            imdata -= self._images[bg_index]

        self.plot.addImage(imdata,
                           legend="current",
                           origin=self._origin,
                           scale=self._deltaXY,
                           replace=False,
                           z=0,
                           info=self._infos[index])
        self.plot.setActiveImage("current")
        self.slider.setValue(index)

    def _getMedianData(self, data):
        data = copy.copy(data)
        if max(self._medianParameters['row_width'],
               self._medianParameters['column_width']) > 1:
            data = medfilt2d(data,
                             [self._medianParameters['row_width'],
                              self._medianParameters['column_width']],
                             conditional=self._medianParameters['conditional'])
        return data

    def setImages(self, images, labels=None,
                  origin=None, height=None, width=None,
                  infos=None):
        """Set the list of data images.

        All images share the same origin, width and height.

        :param images: List of 2D or 3D (for RGBA data) numpy arrays
            of image data. All images must have the same shape.
        :type images: List of ndarrays
        :param labels: list of image names
        :param origin: Image origin: coordinate (x, y) of sample located at
            (row, column) = (0, 0). If None, use (0., 0.)
        :param height: Image height in Y axis units. If None, use the
            image height in number of pixels.
        :param width: Image width in X axis units. If None, use the
            image width in number of pixels.
        :param infos: List of info dicts, one per image, or None.
        """
        self._images = images
        if labels is None:
            labels = ["Image %d" % (i + 1) for i in range(len(images))]
        if infos is None:
            infos = [{} for _img in images]

        self._labels = labels
        self._infos = infos

        height_pixels, width_pixels = images[0].shape[0:2]
        height = height or height_pixels
        width = width or width_pixels

        self._deltaXY = (float(width) / width_pixels,
                         float(height) / height_pixels)

        self._origin = origin or (0., 0.)

        current = self.slider.value()
        self.slider.setMaximum(len(self._images) - 1)
        if current < len(self._images):
            self.showImage(current)
        else:
            self.showImage(0)

        # _maskParamsCache = width, height, self._origin, self._deltaXY
        # if _maskParamsCache != self._maskParamsCache:
        #     self._maskParamsCache = _maskParamsCache
        #     self.resetMask(width, height, self._origin, self._deltaXY)

    def _updateBgScales(self, heights, widths):
        """Recalculate BG scales
        (e.g after a crop operation on :attr:`_bg_images`)"""
        self._bg_deltaXY = []
        for w, h, img in zip(widths,
                             heights,
                             self._bg_images):
            self._bg_deltaXY.append(
                (float(w) / img.shape[1], float(h) / img.shape[0])
            )

    def setBackgroundImages(self, images, labels=None,
                            origins=None, heights=None, widths=None):
        """Set the list of background images.

        Each image should be a tile and have an origin (x, y) tuple,
        a height and a width defined, so that all images can be plotted
        on the same background layer.

        :param images: List of 2D or 3D (for RGBA data) numpy arrays
            of image data. All images must have the same shape.
        :type images: List of ndarrays
        :param labels: list of image names
        :param origins: Images origins: list of coordinate tuples (x, y)
            of sample located at (row, column) = (0, 0).
            If None, use (0., 0.) for all images.
        :param heights: Image height in Y axis units. If None, use the
            image height in number of pixels.
        :param widths: Image width in X axis units. If None, use the
            image width in number of pixels.
        """
        self._bg_images = images
        if labels is None:
            labels = ["Background image %d" % (i + 1) for i in range(len(images))]

        # delete existing images
        for label in self._bg_labels:
            self.plot.removeImage(label)

        self._bg_labels = labels

        if heights is None:
            heights = [image.shape[0] for image in images]
        else:
            assert len(heights) == len(images)
        if widths is None:
            widths = [image.shape[1] for image in images]
        else:
            assert len(widths) == len(images)

        if origins is None:
            origins = [(0, 0) for _img in images]
        else:
            assert len(origins) == len(images)

        self._bg_origins = origins
        self._updateBgScales(heights, widths)

        for bg_deltaXY, bg_orig, label, img in zip(self._bg_deltaXY,
                                                  self._bg_origins,
                                                  labels,
                                                  images):
            # FIXME: we use z=-1 because the mask is always on z=1,
            # so the data must be on z=0. To be fixed after the silx mask
            # is improved
            self.plot.addImage(img,
                               origin=bg_orig,
                               scale=bg_deltaXY,
                               legend=label,
                               replace=False,
                               z=-1)  # TODO: z=0

    def getCurrentIndex(self):
        """
        :return: Index of slider widget used for image selection.
        """
        return self.slider.value()

    def getCurrentColormap(self):
        """Return colormap dict associated with the current image.
        If the current image is a RGBA Image, return None.

        See doc of silx.gui.plot.Plot for an explanation about the colormap
        dictionary.
        """
        image = self.plot.getImage(legend="current")
        if not hasattr(image, "getColormap"):    # isinstance(image, silx.gui.plot.items.ImageRgba):
            return None
        return self.plot.getImage(legend="current").getColormap()

    def showAndRaise(self):
        self.show()
        self.raise_()
Ejemplo n.º 31
0
        if maxValue == 1:
            startIndex = 1
            colors = numpy.zeros((2, 4), dtype=numpy.uint8)
            colors[1, 3] = 255
        else:
            raise ValueError("Different mask levels require color list input")
    oldShape = pixmap.shape
    pixmap.shape = -1, 4
    maskView = mask[:]
    maskView.shape = -1,
    blendFactor = 0.8
    for i in range(startIndex, maxValue + 1):
        idx = (maskView==i)
        pixmap[idx, :] = pixmap[idx, :] * blendFactor + \
                         colors[i] * (1.0 - blendFactor)
    pixmap.shape = oldShape
    return pixmap

if __name__ == "__main__":
    from PyMca5.PyMcaGui import PyMcaQt as qt
    from silx.gui.plot import PlotWidget
    app = qt.QApplication([])
    w = PlotWidget()
    data = numpy.arange(10000.).reshape(100, 100)
    mask = numpy.zeros(data.shape, dtype=numpy.uint8)
    mask[25:75, 25:75] = 1
    image = getPixmapFromData(data, mask=mask)
    w.addImage(image, mask=mask)
    w.show()
    app.exec_()
Ejemplo n.º 32
0
class TestNamedImageAlphaSlider(TestCaseQt):
    def setUp(self):
        super(TestNamedImageAlphaSlider, self).setUp()
        self.plot = PlotWidget()
        self.aslider = AlphaSlider.NamedImageAlphaSlider(plot=self.plot)
        self.aslider.setOrientation(qt.Qt.Horizontal)

        toolbar = qt.QToolBar("plot", self.plot)
        toolbar.addWidget(self.aslider)
        self.plot.addToolBar(toolbar)

        self.plot.show()
        self.qWaitForWindowExposed(self.plot)

        self.mouseMove(self.plot)  # Move to center
        self.qapp.processEvents()

    def tearDown(self):
        self.qapp.processEvents()
        self.plot.setAttribute(qt.Qt.WA_DeleteOnClose)
        self.plot.close()
        del self.plot
        del self.aslider

        super(TestNamedImageAlphaSlider, self).tearDown()

    def testWidgetEnabled(self):
        # no image set initially, slider must be deactivate
        self.assertFalse(self.aslider.isEnabled())

        self.plot.addImage(numpy.array([[0, 1, 2], [3, 4, 5]]), legend="1")
        self.aslider.setLegend("1")
        # now we have an image set
        self.assertTrue(self.aslider.isEnabled())

    def testGetImage(self):
        self.plot.addImage(numpy.array([[0, 1, 2], [3, 4, 5]]), legend="1")
        self.plot.addImage(numpy.array([[0, 1, 3], [2, 4, 6]]), legend="2")
        self.aslider.setLegend("1")
        self.assertEqual(self.plot.getImage("1"),
                         self.aslider.getItem())

        self.aslider.setLegend("2")
        self.assertEqual(self.plot.getImage("2"),
                         self.aslider.getItem())

    def testGetAlpha(self):
        self.plot.addImage(numpy.array([[0, 1, 2], [3, 4, 5]]), legend="1")
        self.aslider.setLegend("1")
        self.aslider.setValue(128)
        self.assertAlmostEqual(self.aslider.getAlpha(),
                               128. / 255)
class TestNamedImageAlphaSlider(TestCaseQt):
    def setUp(self):
        super(TestNamedImageAlphaSlider, self).setUp()
        self.plot = PlotWidget()
        self.aslider = AlphaSlider.NamedImageAlphaSlider(plot=self.plot)
        self.aslider.setOrientation(qt.Qt.Horizontal)

        toolbar = qt.QToolBar("plot", self.plot)
        toolbar.addWidget(self.aslider)
        self.plot.addToolBar(toolbar)

        self.plot.show()
        self.qWaitForWindowExposed(self.plot)

        self.mouseMove(self.plot)  # Move to center
        self.qapp.processEvents()

    def tearDown(self):
        self.qapp.processEvents()
        self.plot.setAttribute(qt.Qt.WA_DeleteOnClose)
        self.plot.close()
        del self.plot
        del self.aslider

        super(TestNamedImageAlphaSlider, self).tearDown()

    def testWidgetEnabled(self):
        # no image set initially, slider must be deactivate
        self.assertFalse(self.aslider.isEnabled())

        self.plot.addImage(numpy.array([[0, 1, 2], [3, 4, 5]]), legend="1")
        self.aslider.setLegend("1")
        # now we have an image set
        self.assertTrue(self.aslider.isEnabled())

    def testGetImage(self):
        self.plot.addImage(numpy.array([[0, 1, 2], [3, 4, 5]]), legend="1")
        self.plot.addImage(numpy.array([[0, 1, 3], [2, 4, 6]]), legend="2")
        self.aslider.setLegend("1")
        self.assertEqual(self.plot.getImage("1"), self.aslider.getItem())

        self.aslider.setLegend("2")
        self.assertEqual(self.plot.getImage("2"), self.aslider.getItem())

    def testGetAlpha(self):
        self.plot.addImage(numpy.array([[0, 1, 2], [3, 4, 5]]), legend="1")
        self.aslider.setLegend("1")
        self.aslider.setValue(128)
        self.assertAlmostEqual(self.aslider.getAlpha(), 128. / 255)
Ejemplo n.º 34
0
 def setGraphYLimits(self, *var, **kw):
     if "replot" in kw:
         del kw["replot"]
     return PlotWidget.setGraphYLimits(self, *var, **kw)
Ejemplo n.º 35
0
class MaskScatterWidget(qt.QMainWindow):
    """Simple plot widget designed to display a scatter plot on top
    of a background image.

    A transparency slider is provided to adjust the transparency of the
    scatter points.

    A mask tools widget is provided to select/mask points of the scatter
    plot.
    """
    def __init__(self, parent=None):
        super(MaskScatterWidget, self).__init__(parent=parent)
        self._activeScatterLegend = "active scatter"
        self._bgImageLegend = "background image"

        # widgets
        centralWidget = qt.QWidget(self)

        self._plot = PlotWidget(parent=centralWidget)

        self._maskToolsWidget = ScatterMaskToolsWidget.ScatterMaskToolsWidget(
            plot=self._plot, parent=centralWidget)

        self._alphaSlider = NamedScatterAlphaSlider(parent=self, plot=self._plot)
        self._alphaSlider.setOrientation(qt.Qt.Horizontal)
        self._alphaSlider.setToolTip("Adjust scatter opacity")

        # layout
        layout = qt.QVBoxLayout(centralWidget)
        layout.addWidget(self._plot)
        layout.addWidget(self._alphaSlider)
        layout.addWidget(self._maskToolsWidget)
        centralWidget.setLayout(layout)

        self.setCentralWidget(centralWidget)

    def setSelectionMask(self, mask, copy=True):
        """Set the mask to a new array.

        :param numpy.ndarray mask: The array to use for the mask.
                    Mask type: array of uint8 of dimension 1,
                    Array of other types are converted.
        :param bool copy: True (the default) to copy the array,
                          False to use it as is if possible.
        :return: None if failed, shape of mask as 1-tuple if successful.
        """
        return self._maskToolsWidget.setSelectionMask(mask,
                                                      copy=copy)

    def getSelectionMask(self, copy=True):
        """Get the current mask as a 1D array.

        :param bool copy: True (default) to get a copy of the mask.
                          If False, the returned array MUST not be modified.
        :return: The array of the mask with dimension of the scatter data.
                 If there is no scatter data, None is returned.
        :rtype: 1D numpy.ndarray of uint8
        """
        return self._maskToolsWidget.getSelectionMask(copy=copy)

    def setBackgroundImage(self, image, xscale=(0, 1.), yscale=(0, 1.),
                           colormap=None):
        """Set a background image

        :param image: 2D image, array of shape (nrows, ncolumns)
            or (nrows, ncolumns, 3) or (nrows, ncolumns, 4) RGB(A) pixmap
        :param xscale: Factors for polynomial scaling  for x-axis,
            *(a, b)* such as :math:`x \mapsto a + bx`
        :param yscale: Factors for polynomial scaling  for y-axis
        """
        self._plot.addImage(image, legend=self._bgImageLegend,
                            origin=(xscale[0], yscale[0]),
                            scale=(xscale[1], yscale[1]),
                            z=0,
                            colormap=colormap)

    def setScatter(self, x, y, v=None, info=None, colormap=None):
        """Set the scatter data, by providing its data as a 1D
        array or as a pixmap.

        The scatter plot set through this method is associated
        with the transparency slider.

        :param x: 1D array of x coordinates
        :param y: 1D array of y coordinates
        :param v: Array of values for each point, represented as the color
             of the point on the plot.
        """
        self._plot.addScatter(x, y, v, legend=self._activeScatterLegend,
                              info=info, colormap=colormap)
        # the mask is associated with the active scatter
        self._plot._setActiveItem(kind="scatter",
                                  legend=self._activeScatterLegend)

        self._alphaSlider.setLegend(self._activeScatterLegend)
Ejemplo n.º 36
0
 def setActiveImage(self, *var, **kw):
     if "replot" in kw:
         del kw["replot"]
     return PlotWidget.setActiveImage(self, *var, **kw)
Ejemplo n.º 37
0
    """
    ob = Medfilt2DPlugin(plotWindow)
    return ob


if __name__ == "__main__":
    # python -m PyMca5.PyMcaPlugins.Medfilt2DPlugin
    from PyMca5.PyMcaGui.PluginsToolButton import PluginsToolButton
    from PyMca5 import PyMcaPlugins
    import os
    from silx.test.utils import add_relative_noise
    from silx.gui.plot import PlotWidget

    # build a plot widget with a plugin toolbar button
    app = qt.QApplication([])
    master_plot = PlotWidget()
    toolb = qt.QToolBar(master_plot)
    plugins_tb_2d = PluginsToolButton(plot=master_plot,
                                      parent=toolb,
                                      method="getPlugin2DInstance")
    plugins_tb_2d.getPlugins(
                    method="getPlugin2DInstance",
                    directoryList=[os.path.dirname(PyMcaPlugins.__file__)])
    toolb.addWidget(plugins_tb_2d)
    master_plot.addToolBar(toolb)
    master_plot.show()

    # add a noisy image
    a, b = numpy.meshgrid(numpy.linspace(-10, 10, 500),
                          numpy.linspace(-10, 5, 400),
                          indexing="ij")
Ejemplo n.º 38
0
    def __init__(self, parent=None):
        qt.QMainWindow.__init__(self, parent=parent)
        if parent is not None:
            # behave as a widget
            self.setWindowFlags(qt.Qt.Widget)
        else:
            self.setWindowTitle("PyMca - Image Selection Tool")

        centralWidget = qt.QWidget(self)
        layout = qt.QVBoxLayout(centralWidget)
        centralWidget.setLayout(layout)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)

        # Plot
        self.plot = PlotWidget(parent=centralWidget)
        self.plot.setWindowFlags(qt.Qt.Widget)
        self.plot.setDefaultColormap({
            'name': 'temperature',
            'normalization': 'linear',
            'autoscale': True,
            'vmin': 0.,
            'vmax': 1.
        })

        layout.addWidget(self.plot)

        # Mask Widget
        self._maskToolsDockWidget = None

        # Image selection slider
        self.slider = qt.QSlider(self.centralWidget())
        self.slider.setOrientation(qt.Qt.Horizontal)
        self.slider.setMinimum(0)
        self.slider.setMaximum(0)
        layout.addWidget(self.slider)
        self.slider.valueChanged[int].connect(self.showImage)

        # ADD/REMOVE/REPLACE IMAGE buttons
        buttonBox = qt.QWidget(self)
        buttonBoxLayout = qt.QHBoxLayout(buttonBox)
        buttonBoxLayout.setContentsMargins(0, 0, 0, 0)
        buttonBoxLayout.setSpacing(0)
        self.addImageButton = qt.QPushButton(buttonBox)
        icon = qt.QIcon(qt.QPixmap(IconDict["rgb16"]))
        self.addImageButton.setIcon(icon)
        self.addImageButton.setText("ADD IMAGE")
        self.addImageButton.setToolTip("Add image to RGB correlator")
        buttonBoxLayout.addWidget(self.addImageButton)

        self.removeImageButton = qt.QPushButton(buttonBox)
        self.removeImageButton.setIcon(icon)
        self.removeImageButton.setText("REMOVE IMAGE")
        self.removeImageButton.setToolTip("Remove image from RGB correlator")
        buttonBoxLayout.addWidget(self.removeImageButton)

        self.replaceImageButton = qt.QPushButton(buttonBox)
        self.replaceImageButton.setIcon(icon)
        self.replaceImageButton.setText("REPLACE IMAGE")
        self.replaceImageButton.setToolTip(
            "Replace all images in RGB correlator with this one")
        buttonBoxLayout.addWidget(self.replaceImageButton)

        self.addImageButton.clicked.connect(self._addImageClicked)
        self.removeImageButton.clicked.connect(self._removeImageClicked)
        self.replaceImageButton.clicked.connect(self._replaceImageClicked)

        layout.addWidget(buttonBox)

        # median filter widget
        self._medianParameters = {'row_width': 1, 'column_width': 1}
        self._medianParametersWidget = MedianParameters(self)
        self._medianParametersWidget.widthSpin.setValue(1)
        self._medianParametersWidget.widthSpin.valueChanged[int].connect(
            self._setMedianKernelWidth)
        layout.addWidget(self._medianParametersWidget)

        self.setCentralWidget(centralWidget)

        # Init actions
        self.group = qt.QActionGroup(self)
        self.group.setExclusive(False)

        self.resetZoomAction = self.group.addAction(
            PlotActions.ResetZoomAction(plot=self.plot, parent=self))
        self.addAction(self.resetZoomAction)

        self.zoomInAction = PlotActions.ZoomInAction(plot=self.plot,
                                                     parent=self)
        self.addAction(self.zoomInAction)

        self.zoomOutAction = PlotActions.ZoomOutAction(plot=self.plot,
                                                       parent=self)
        self.addAction(self.zoomOutAction)

        self.xAxisAutoScaleAction = self.group.addAction(
            PlotActions.XAxisAutoScaleAction(plot=self.plot, parent=self))
        self.addAction(self.xAxisAutoScaleAction)

        self.yAxisAutoScaleAction = self.group.addAction(
            PlotActions.YAxisAutoScaleAction(plot=self.plot, parent=self))
        self.addAction(self.yAxisAutoScaleAction)

        self.colormapAction = self.group.addAction(
            PlotActions.ColormapAction(plot=self.plot, parent=self))
        self.addAction(self.colormapAction)

        self.copyAction = self.group.addAction(
            PlotActions.CopyAction(plot=self.plot, parent=self))
        self.addAction(self.copyAction)

        self.group.addAction(self.getMaskAction())

        # Init toolbuttons
        self.saveToolbutton = SaveToolButton(parent=self, maskImageWidget=self)

        self.yAxisInvertedButton = PlotToolButtons.YAxisOriginToolButton(
            parent=self, plot=self.plot)

        self.keepDataAspectRatioButton = PlotToolButtons.AspectToolButton(
            parent=self, plot=self.plot)

        self.backgroundButton = qt.QToolButton(self)
        self.backgroundButton.setCheckable(True)
        self.backgroundButton.setIcon(
            qt.QIcon(qt.QPixmap(IconDict["subtract"])))
        self.backgroundButton.setToolTip(
            'Toggle background image subtraction from current image\n' +
            'No action if no background image available.')
        self.backgroundButton.clicked.connect(self._subtractBackground)

        # Creating the toolbar also create actions for toolbuttons
        self._toolbar = self._createToolBar(title='Plot', parent=None)
        self.addToolBar(self._toolbar)

        self._profile = ProfileToolBar(plot=self.plot)
        self.addToolBar(self._profile)
        self.setProfileToolbarVisible(False)

        # add a transparency slider for the stack data
        self._alphaSliderToolbar = qt.QToolBar("Alpha slider", parent=self)
        self._alphaSlider = NamedImageAlphaSlider(
            parent=self._alphaSliderToolbar, plot=self.plot, legend="current")
        self._alphaSlider.setOrientation(qt.Qt.Vertical)
        self._alphaSlider.setToolTip("Adjust opacity of stack image overlay")
        self._alphaSliderToolbar.addWidget(self._alphaSlider)
        self.addToolBar(qt.Qt.RightToolBarArea, self._alphaSliderToolbar)

        # hide optional tools and actions
        self.setAlphaSliderVisible(False)
        self.setBackgroundActionVisible(False)
        self.setMedianFilterWidgetVisible(False)
        self.setProfileToolbarVisible(False)

        self._images = []
        """List of images, as 2D numpy arrays or 3D numpy arrays (RGB(A)).
        """

        self._labels = []
        """List of image labels.
        """

        self._bg_images = []
        """List of background images, as 2D numpy arrays or 3D numpy arrays
        (RGB(A)).
        These images are not active, their colormap cannot be changed and
        they cannot be the base image used for drawing a mask.
        """

        self._bg_labels = []

        self._deltaXY = (
            1.0, 1.0)  # TODO: allow different scale and origin for each image
        """Current image scale (Xscale, Yscale) (in axis units per image pixel).
        The scale is adjusted to keep constant width and height for the image
        when a crop operation is applied."""

        self._origin = (0., 0.)
        """Current image origin: coordinate (x, y) of sample located at
        (row, column) = (0, 0)"""

        # scales and origins for background images
        self._bg_deltaXY = []
        self._bg_origins = []
Ejemplo n.º 39
0
 def setUp(self):
     self.plot = PlotWidget()
Ejemplo n.º 40
0
class TestAxisSync(TestCaseQt):
    """Tests AxisSync class"""

    def setUp(self):
        TestCaseQt.setUp(self)
        self.plot1 = PlotWidget()
        self.plot2 = PlotWidget()
        self.plot3 = PlotWidget()

    def tearDown(self):
        self.plot1 = None
        self.plot2 = None
        self.plot3 = None
        TestCaseQt.tearDown(self)

    def testMoveFirstAxis(self):
        """Test synchronization after construction"""
        _sync = SyncAxes([self.plot1.getXAxis(), self.plot2.getXAxis(), self.plot3.getXAxis()])

        self.plot1.getXAxis().setLimits(10, 500)
        self.assertEqual(self.plot1.getXAxis().getLimits(), (10, 500))
        self.assertEqual(self.plot2.getXAxis().getLimits(), (10, 500))
        self.assertEqual(self.plot3.getXAxis().getLimits(), (10, 500))

    def testMoveSecondAxis(self):
        """Test synchronization after construction"""
        _sync = SyncAxes([self.plot1.getXAxis(), self.plot2.getXAxis(), self.plot3.getXAxis()])

        self.plot2.getXAxis().setLimits(10, 500)
        self.assertEqual(self.plot1.getXAxis().getLimits(), (10, 500))
        self.assertEqual(self.plot2.getXAxis().getLimits(), (10, 500))
        self.assertEqual(self.plot3.getXAxis().getLimits(), (10, 500))

    def testMoveTwoAxes(self):
        """Test synchronization after construction"""
        _sync = SyncAxes([self.plot1.getXAxis(), self.plot2.getXAxis(), self.plot3.getXAxis()])

        self.plot1.getXAxis().setLimits(1, 50)
        self.plot2.getXAxis().setLimits(10, 500)
        self.assertEqual(self.plot1.getXAxis().getLimits(), (10, 500))
        self.assertEqual(self.plot2.getXAxis().getLimits(), (10, 500))
        self.assertEqual(self.plot3.getXAxis().getLimits(), (10, 500))

    def testDestruction(self):
        """Test synchronization when sync object is destroyed"""
        sync = SyncAxes([self.plot1.getXAxis(), self.plot2.getXAxis(), self.plot3.getXAxis()])
        del sync

        self.plot1.getXAxis().setLimits(10, 500)
        self.assertEqual(self.plot1.getXAxis().getLimits(), (10, 500))
        self.assertNotEqual(self.plot2.getXAxis().getLimits(), (10, 500))
        self.assertNotEqual(self.plot3.getXAxis().getLimits(), (10, 500))

    def testAxisDestruction(self):
        """Test synchronization when an axis disappear"""
        _sync = SyncAxes([self.plot1.getXAxis(), self.plot2.getXAxis(), self.plot3.getXAxis()])

        # Destroy the plot is possible
        import weakref
        plot = weakref.ref(self.plot2)
        self.plot2 = None
        result = self.qWaitForDestroy(plot)
        if not result:
            # We can't test
            self.skipTest("Object not destroyed")

        self.plot1.getXAxis().setLimits(10, 500)
        self.assertEqual(self.plot3.getXAxis().getLimits(), (10, 500))

    def testStop(self):
        """Test synchronization after calling stop"""
        sync = SyncAxes([self.plot1.getXAxis(), self.plot2.getXAxis(), self.plot3.getXAxis()])
        sync.stop()

        self.plot1.getXAxis().setLimits(10, 500)
        self.assertEqual(self.plot1.getXAxis().getLimits(), (10, 500))
        self.assertNotEqual(self.plot2.getXAxis().getLimits(), (10, 500))
        self.assertNotEqual(self.plot3.getXAxis().getLimits(), (10, 500))

    def testStopMovingStart(self):
        """Test synchronization after calling stop, moving an axis, then start again"""
        sync = SyncAxes([self.plot1.getXAxis(), self.plot2.getXAxis(), self.plot3.getXAxis()])
        sync.stop()
        self.plot1.getXAxis().setLimits(10, 500)
        self.plot2.getXAxis().setLimits(1, 50)
        self.assertEqual(self.plot1.getXAxis().getLimits(), (10, 500))
        sync.start()

        # The first axis is the reference
        self.assertEqual(self.plot1.getXAxis().getLimits(), (10, 500))
        self.assertEqual(self.plot2.getXAxis().getLimits(), (10, 500))
        self.assertEqual(self.plot3.getXAxis().getLimits(), (10, 500))

    def testDoubleStop(self):
        """Test double stop"""
        sync = SyncAxes([self.plot1.getXAxis(), self.plot2.getXAxis(), self.plot3.getXAxis()])
        sync.stop()
        self.assertRaises(RuntimeError, sync.stop)

    def testDoubleStart(self):
        """Test double stop"""
        sync = SyncAxes([self.plot1.getXAxis(), self.plot2.getXAxis(), self.plot3.getXAxis()])
        self.assertRaises(RuntimeError, sync.start)

    def testScale(self):
        """Test scale change"""
        _sync = SyncAxes([self.plot1.getXAxis(), self.plot2.getXAxis(), self.plot3.getXAxis()])
        self.plot1.getXAxis().setScale(self.plot1.getXAxis().LOGARITHMIC)
        self.assertEqual(self.plot1.getXAxis().getScale(), self.plot1.getXAxis().LOGARITHMIC)
        self.assertEqual(self.plot2.getXAxis().getScale(), self.plot1.getXAxis().LOGARITHMIC)
        self.assertEqual(self.plot3.getXAxis().getScale(), self.plot1.getXAxis().LOGARITHMIC)

    def testDirection(self):
        """Test direction change"""
        _sync = SyncAxes([self.plot1.getYAxis(), self.plot2.getYAxis(), self.plot3.getYAxis()])
        self.plot1.getYAxis().setInverted(True)
        self.assertEqual(self.plot1.getYAxis().isInverted(), True)
        self.assertEqual(self.plot2.getYAxis().isInverted(), True)
        self.assertEqual(self.plot3.getYAxis().isInverted(), True)
Ejemplo n.º 41
0
 def setActiveImage(self, *var, **kw):
     if "replot" in kw:
         del kw["replot"]
     return PlotWidget.setActiveImage(self, *var, **kw)
Ejemplo n.º 42
0
class TestSaveAction(unittest.TestCase):
    def setUp(self):
        self.plot = PlotWidget(backend='none')
        self.saveAction = SaveAction(plot=self.plot)

        self.tempdir = tempfile.mkdtemp()
        self.out_fname = os.path.join(self.tempdir, "out.dat")

    def tearDown(self):
        os.unlink(self.out_fname)
        os.rmdir(self.tempdir)

    def testSaveMultipleCurvesAsSpec(self):
        """Test that labels are properly used."""
        self.plot.setGraphXLabel("graph x label")
        self.plot.setGraphYLabel("graph y label")

        self.plot.addCurve([0, 1], [1, 2],
                           "curve with labels",
                           xlabel="curve0 X",
                           ylabel="curve0 Y")
        self.plot.addCurve([-1, 3], [-6, 2],
                           "curve with X label",
                           xlabel="curve1 X")
        self.plot.addCurve([-2, 0], [8, 12],
                           "curve with Y label",
                           ylabel="curve2 Y")
        self.plot.addCurve([3, 1], [7, 6], "curve with no labels")

        self.saveAction._saveCurves(self.out_fname,
                                    SaveAction.ALL_CURVES_FILTERS[0]
                                    )  # "All curves as SpecFile (*.dat)"

        with open(self.out_fname, "rb") as f:
            file_content = f.read()
            if hasattr(file_content, "decode"):
                file_content = file_content.decode()

            # case with all curve labels specified
            self.assertIn("#S 1 curve0 Y", file_content)
            self.assertIn("#L curve0 X  curve0 Y", file_content)

            # graph X&Y labels are used when no curve label is specified
            self.assertIn("#S 2 graph y label", file_content)
            self.assertIn("#L curve1 X  graph y label", file_content)

            self.assertIn("#S 3 curve2 Y", file_content)
            self.assertIn("#L graph x label  curve2 Y", file_content)

            self.assertIn("#S 4 graph y label", file_content)
            self.assertIn("#L graph x label  graph y label", file_content)
Ejemplo n.º 43
0
 def hideCurve(self, *var, **kw):
     if "replot" in kw:
         del kw["replot"]
     return PlotWidget.hideCurve(self, *var, **kw)
class TestLimitConstaints(unittest.TestCase):
    """Tests setLimitConstaints class"""
    def setUp(self):
        self.plot = PlotWidget()

    def tearDown(self):
        self.plot = None

    def testApi(self):
        """Test availability of the API"""
        self.plot.getXAxis().setLimitsConstraints(minPos=1, maxPos=10)
        self.plot.getXAxis().setRangeConstraints(minRange=1, maxRange=1)
        self.plot.getYAxis().setLimitsConstraints(minPos=1, maxPos=10)
        self.plot.getYAxis().setRangeConstraints(minRange=1, maxRange=1)

    def testXMinMax(self):
        """Test limit constains on x-axis"""
        self.plot.getXAxis().setLimitsConstraints(minPos=0, maxPos=100)
        self.plot.setLimits(xmin=-1, xmax=101, ymin=-1, ymax=101)
        self.assertEqual(self.plot.getXAxis().getLimits(), (0, 100))
        self.assertEqual(self.plot.getYAxis().getLimits(), (-1, 101))

    def testYMinMax(self):
        """Test limit constains on y-axis"""
        self.plot.getYAxis().setLimitsConstraints(minPos=0, maxPos=100)
        self.plot.setLimits(xmin=-1, xmax=101, ymin=-1, ymax=101)
        self.assertEqual(self.plot.getXAxis().getLimits(), (-1, 101))
        self.assertEqual(self.plot.getYAxis().getLimits(), (0, 100))

    def testMinXRange(self):
        """Test min range constains on x-axis"""
        self.plot.getXAxis().setRangeConstraints(minRange=100)
        self.plot.setLimits(xmin=1, xmax=99, ymin=1, ymax=99)
        limits = self.plot.getXAxis().getLimits()
        self.assertEqual(limits[1] - limits[0], 100)
        limits = self.plot.getYAxis().getLimits()
        self.assertNotEqual(limits[1] - limits[0], 100)

    def testMaxXRange(self):
        """Test max range constains on x-axis"""
        self.plot.getXAxis().setRangeConstraints(maxRange=100)
        self.plot.setLimits(xmin=-1, xmax=101, ymin=-1, ymax=101)
        limits = self.plot.getXAxis().getLimits()
        self.assertEqual(limits[1] - limits[0], 100)
        limits = self.plot.getYAxis().getLimits()
        self.assertNotEqual(limits[1] - limits[0], 100)

    def testMinYRange(self):
        """Test min range constains on y-axis"""
        self.plot.getYAxis().setRangeConstraints(minRange=100)
        self.plot.setLimits(xmin=1, xmax=99, ymin=1, ymax=99)
        limits = self.plot.getXAxis().getLimits()
        self.assertNotEqual(limits[1] - limits[0], 100)
        limits = self.plot.getYAxis().getLimits()
        self.assertEqual(limits[1] - limits[0], 100)

    def testMaxYRange(self):
        """Test max range constains on y-axis"""
        self.plot.getYAxis().setRangeConstraints(maxRange=100)
        self.plot.setLimits(xmin=-1, xmax=101, ymin=-1, ymax=101)
        limits = self.plot.getXAxis().getLimits()
        self.assertNotEqual(limits[1] - limits[0], 100)
        limits = self.plot.getYAxis().getLimits()
        self.assertEqual(limits[1] - limits[0], 100)

    def testChangeOfConstraints(self):
        """Test changing of the constraints"""
        self.plot.getXAxis().setRangeConstraints(minRange=10, maxRange=10)
        # There is no more constraints on the range
        self.plot.getXAxis().setRangeConstraints(minRange=None, maxRange=None)
        self.plot.setLimits(xmin=-1, xmax=101, ymin=-1, ymax=101)
        self.assertEqual(self.plot.getXAxis().getLimits(), (-1, 101))

    def testSettingConstraints(self):
        """Test setting a constaint (setLimits first then the constaint)"""
        self.plot.setLimits(xmin=-1, xmax=101, ymin=-1, ymax=101)
        self.plot.getXAxis().setLimitsConstraints(minPos=0, maxPos=100)
        self.assertEqual(self.plot.getXAxis().getLimits(), (0, 100))
Ejemplo n.º 45
0
 def setActiveCurveColor(self, *var, **kw):
     return PlotWidget.setActiveCurveStyle(self, *var, **kw)
Ejemplo n.º 46
0
class ColormapDialog(qt.QDialog):
    sigColormapChanged = qt.pyqtSignal(object)
    def __init__(self, parent=None, name="Colormap Dialog"):
        qt.QDialog.__init__(self, parent)
        self.setWindowTitle(name)
        self.title = name


        self.colormapList = ["Greyscale", "Reverse Grey", "Temperature",
                             "Red", "Green", "Blue", "Many"]

        # histogramData is tupel(bins, counts)
        self.histogramData = None

        # default values
        self.dataMin   = -10
        self.dataMax   = 10
        self.minValue  = 0
        self.maxValue  = 1

        self.colormapIndex  = 2
        self.colormapType   = 0

        self.autoscale   = False
        self.autoscale90 = False
        # main layout
        vlayout = qt.QVBoxLayout(self)
        vlayout.setContentsMargins(10, 10, 10, 10)
        vlayout.setSpacing(0)

        # layout 1 : -combo to choose colormap
        #            -autoscale button
        #            -autoscale 90% button
        hbox1    = qt.QWidget(self)
        hlayout1 = qt.QHBoxLayout(hbox1)
        vlayout.addWidget(hbox1)
        hlayout1.setContentsMargins(0, 0, 0, 0)
        hlayout1.setSpacing(10)

        # combo
        self.combo = qt.QComboBox(hbox1)
        for colormap in self.colormapList:
            self.combo.addItem(colormap)
        self.combo.activated[int].connect(self.colormapChange)
        hlayout1.addWidget(self.combo)

        # autoscale
        self.autoScaleButton = qt.QPushButton("Autoscale", hbox1)
        self.autoScaleButton.setCheckable(True)
        self.autoScaleButton.setAutoDefault(False)
        self.autoScaleButton.toggled[bool].connect(self.autoscaleChange)
        hlayout1.addWidget(self.autoScaleButton)

        # autoscale 90%
        self.autoScale90Button = qt.QPushButton("Autoscale 90%", hbox1)
        self.autoScale90Button.setCheckable(True)
        self.autoScale90Button.setAutoDefault(False)

        self.autoScale90Button.toggled[bool].connect(self.autoscale90Change)
        hlayout1.addWidget(self.autoScale90Button)

        # hlayout
        hbox0    = qt.QWidget(self)
        self.__hbox0 = hbox0
        hlayout0 = qt.QHBoxLayout(hbox0)
        hlayout0.setContentsMargins(0, 0, 0, 0)
        hlayout0.setSpacing(0)
        vlayout.addWidget(hbox0)
        #hlayout0.addStretch(10)

        self.buttonGroup = qt.QButtonGroup()
        g1 = qt.QCheckBox(hbox0)
        g1.setText("Linear")
        g2 = qt.QCheckBox(hbox0)
        g2.setText("Logarithmic")
        g3 = qt.QCheckBox(hbox0)
        g3.setText("Gamma")
        self.buttonGroup.addButton(g1, 0)
        self.buttonGroup.addButton(g2, 1)
        self.buttonGroup.addButton(g3, 2)
        self.buttonGroup.setExclusive(True)
        if self.colormapType == 1:
            self.buttonGroup.button(1).setChecked(True)
        elif self.colormapType == 2:
            self.buttonGroup.button(2).setChecked(True)
        else:
            self.buttonGroup.button(0).setChecked(True)
        hlayout0.addWidget(g1)
        hlayout0.addWidget(g2)
        hlayout0.addWidget(g3)
        vlayout.addWidget(hbox0)
        self.buttonGroup.buttonClicked[int].connect(self.buttonGroupChange)
        vlayout.addSpacing(20)

        hboxlimits = qt.QWidget(self)
        hboxlimitslayout = qt.QHBoxLayout(hboxlimits)
        hboxlimitslayout.setContentsMargins(0, 0, 0, 0)
        hboxlimitslayout.setSpacing(0)

        self.slider = None

        vlayout.addWidget(hboxlimits)

        vboxlimits = qt.QWidget(hboxlimits)
        vboxlimitslayout = qt.QVBoxLayout(vboxlimits)
        vboxlimitslayout.setContentsMargins(0, 0, 0, 0)
        vboxlimitslayout.setSpacing(0)
        hboxlimitslayout.addWidget(vboxlimits)

        # hlayout 2 : - min label
        #             - min texte
        hbox2    = qt.QWidget(vboxlimits)
        self.__hbox2 = hbox2
        hlayout2 = qt.QHBoxLayout(hbox2)
        hlayout2.setContentsMargins(0, 0, 0, 0)
        hlayout2.setSpacing(0)
        #vlayout.addWidget(hbox2)
        vboxlimitslayout.addWidget(hbox2)
        hlayout2.addStretch(10)

        self.minLabel  = qt.QLabel(hbox2)
        self.minLabel.setText("Minimum")
        hlayout2.addWidget(self.minLabel)

        hlayout2.addSpacing(5)
        hlayout2.addStretch(1)
        self.minText  = MyQLineEdit(hbox2)
        self.minText.setFixedWidth(150)
        self.minText.setAlignment(qt.Qt.AlignRight)
        self.minText.returnPressed[()].connect(self.minTextChanged)
        hlayout2.addWidget(self.minText)

        # hlayout 3 : - min label
        #             - min text
        hbox3    = qt.QWidget(vboxlimits)
        self.__hbox3 = hbox3
        hlayout3 = qt.QHBoxLayout(hbox3)
        hlayout3.setContentsMargins(0, 0, 0, 0)
        hlayout3.setSpacing(0)
        #vlayout.addWidget(hbox3)
        vboxlimitslayout.addWidget(hbox3)

        hlayout3.addStretch(10)
        self.maxLabel = qt.QLabel(hbox3)
        self.maxLabel.setText("Maximum")
        hlayout3.addWidget(self.maxLabel)

        hlayout3.addSpacing(5)
        hlayout3.addStretch(1)

        self.maxText = MyQLineEdit(hbox3)
        self.maxText.setFixedWidth(150)
        self.maxText.setAlignment(qt.Qt.AlignRight)

        self.maxText.returnPressed[()].connect(self.maxTextChanged)
        hlayout3.addWidget(self.maxText)


        # Graph widget for color curve...
        self.c = PlotWidget(self, backend=None)
        self.c.setGraphXLabel("Data Values")
        self.c.setInteractiveMode('select')

        self.marge = (abs(self.dataMax) + abs(self.dataMin)) / 6.0
        self.minmd = self.dataMin - self.marge
        self.maxpd = self.dataMax + self.marge

        self.c.setGraphXLimits(self.minmd, self.maxpd)
        self.c.setGraphYLimits(-11.5, 11.5)

        x = [self.minmd, self.dataMin, self.dataMax, self.maxpd]
        y = [-10, -10, 10, 10 ]
        self.c.addCurve(x, y,
                        legend="ConstrainedCurve",
                        color='black',
                        symbol='o',
                        linestyle='-')
        self.markers = []
        self.__x = x
        self.__y = y
        labelList = ["","Min", "Max", ""]
        for i in range(4):
            if i in [1, 2]:
                draggable = True
                color = "blue"
            else:
                draggable = False
                color = "black"
            #TODO symbol
            legend = "%d" % i
            self.c.addXMarker(x[i],
                              legend=legend,
                              text=labelList[i],
                              draggable=draggable,
                              color=color)
            self.markers.append((legend, ""))

        self.c.setMinimumSize(qt.QSize(250,200))
        vlayout.addWidget(self.c)

        self.c.sigPlotSignal.connect(self.chval)

        # colormap window can not be resized
        self.setFixedSize(vlayout.minimumSize())

    def plotHistogram(self, data=None):
        if data is not None:
            self.histogramData = data
        if self.histogramData is None:
            return False
        bins, counts = self.histogramData
        self.c.addCurve(bins, counts,
                        "Histogram",
                        color='darkYellow',
                        histogram='center',
                        yaxis='right',
                        fill=True)

    def _update(self):
        _logger.debug("colormap _update called")
        self.marge = (abs(self.dataMax) + abs(self.dataMin)) / 6.0
        self.minmd = self.dataMin - self.marge
        self.maxpd = self.dataMax + self.marge
        self.c.setGraphXLimits(self.minmd, self.maxpd)
        self.c.setGraphYLimits( -11.5, 11.5)

        self.__x = [self.minmd, self.dataMin, self.dataMax, self.maxpd]
        self.__y = [-10, -10, 10, 10]
        self.c.addCurve(self.__x, self.__y,
                        legend="ConstrainedCurve",
                        color='black',
                        symbol='o',
                        linestyle='-')
        self.c.clearMarkers()
        for i in range(4):
            if i in [1, 2]:
                draggable = True
                color = "blue"
            else:
                draggable = False
                color = "black"
            key = self.markers[i][0]
            label = self.markers[i][1]
            self.c.addXMarker(self.__x[i],
                              legend=key,
                              text=label,
                              draggable=draggable,
                              color=color)
        self.sendColormap()

    def buttonGroupChange(self, val):
        _logger.debug("buttonGroup asking to update colormap")
        self.setColormapType(val, update=True)
        self._update()

    def setColormapType(self, val, update=False):
        self.colormapType = val
        if self.colormapType == 1:
            self.buttonGroup.button(1).setChecked(True)
        elif self.colormapType == 2:
            self.buttonGroup.button(2).setChecked(True)
        else:
            self.colormapType = 0
            self.buttonGroup.button(0).setChecked(True)
        if update:
            self._update()

    def chval(self, ddict):
        _logger.debug("Received %s", ddict)
        if ddict['event'] == 'markerMoving':
            diam = int(ddict['label'])
            x = ddict['x']
            if diam == 1:
                self.setDisplayedMinValue(x)
            elif diam == 2:
                self.setDisplayedMaxValue(x)
        elif ddict['event'] == 'markerMoved':
            diam = int(ddict['label'])
            x = ddict['x']
            if diam == 1:
                self.setMinValue(x)
            if diam == 2:
                self.setMaxValue(x)

    """
    Colormap
    """
    def setColormap(self, colormap):
        self.colormapIndex = colormap
        if QTVERSION < '4.0.0':
            self.combo.setCurrentItem(colormap)
        else:
            self.combo.setCurrentIndex(colormap)

    def colormapChange(self, colormap):
        self.colormapIndex = colormap
        self.sendColormap()

    # AUTOSCALE
    """
    Autoscale
    """
    def autoscaleChange(self, val):
        self.autoscale = val
        self.setAutoscale(val)
        self.sendColormap()

    def setAutoscale(self, val):
        _logger.debug("setAutoscale called %s", val)
        if val:
            self.autoScaleButton.setChecked(True)
            self.autoScale90Button.setChecked(False)
            #self.autoScale90Button.setDown(False)
            self.setMinValue(self.dataMin)
            self.setMaxValue(self.dataMax)
            self.maxText.setEnabled(0)
            self.minText.setEnabled(0)
            self.c.setEnabled(False)
            #self.c.disablemarkermode()
        else:
            self.autoScaleButton.setChecked(False)
            self.autoScale90Button.setChecked(False)
            self.minText.setEnabled(1)
            self.maxText.setEnabled(1)
            self.c.setEnabled(True)
            #self.c.enablemarkermode()

    """
    set rangeValues to dataMin ; dataMax-10%
    """
    def autoscale90Change(self, val):
        self.autoscale90 = val
        self.setAutoscale90(val)
        self.sendColormap()

    def setAutoscale90(self, val):
        if val:
            self.autoScaleButton.setChecked(False)
            self.setMinValue(self.dataMin)
            self.setMaxValue(self.dataMax - abs(self.dataMax/10))
            self.minText.setEnabled(0)
            self.maxText.setEnabled(0)
            self.c.setEnabled(False)
        else:
            self.autoScale90Button.setChecked(False)
            self.minText.setEnabled(1)
            self.maxText.setEnabled(1)
            self.c.setEnabled(True)
            self.c.setFocus()



    # MINIMUM
    """
    change min value and update colormap
    """
    def setMinValue(self, val):
        v = float(str(val))
        self.minValue = v
        self.minText.setText("%g" % v)
        self.__x[1] = v
        key = self.markers[1][0]
        label = self.markers[1][1]
        self.c.addXMarker(v, legend=key, text=label, color="blue", draggable=True)
        self.c.addCurve(self.__x,
                        self.__y,
                        legend="ConstrainedCurve",
                        color='black',
                        symbol='o',
                        linestyle='-')
        self.sendColormap()

    """
    min value changed by text
    """
    def minTextChanged(self):
        text = str(self.minText.text())
        if not len(text):
            return
        val = float(text)
        self.setMinValue(val)
        if self.minText.hasFocus():
            self.c.setFocus()

    """
    change only the displayed min value
    """
    def setDisplayedMinValue(self, val):
        val = float(val)
        self.minValue = val
        self.minText.setText("%g"%val)
        self.__x[1] = val
        key = self.markers[1][0]
        label = self.markers[1][1]
        self.c.addXMarker(val, legend=key, text=label, color="blue", draggable=True)
        self.c.addCurve(self.__x, self.__y,
                        legend="ConstrainedCurve",
                        color='black',
                        symbol='o',
                        linestyle='-')
    # MAXIMUM
    """
    change max value and update colormap
    """
    def setMaxValue(self, val):
        v = float(str(val))
        self.maxValue = v
        self.maxText.setText("%g"%v)
        self.__x[2] = v
        key = self.markers[2][0]
        label = self.markers[2][1]
        self.c.addXMarker(v, legend=key, text=label, color="blue", draggable=True)
        self.c.addCurve(self.__x, self.__y,
                        legend="ConstrainedCurve",
                        color='black',
                        symbol='o',
                        linestyle='-')
        self.sendColormap()

    """
    max value changed by text
    """
    def maxTextChanged(self):
        text = str(self.maxText.text())
        if not len(text):return
        val = float(text)
        self.setMaxValue(val)
        if self.maxText.hasFocus():
            self.c.setFocus()

    """
    change only the displayed max value
    """
    def setDisplayedMaxValue(self, val):
        val = float(val)
        self.maxValue = val
        self.maxText.setText("%g"%val)
        self.__x[2] = val
        key = self.markers[2][0]
        label = self.markers[2][1]
        self.c.addXMarker(val, legend=key, text=label, color="blue", draggable=True)
        self.c.addCurve(self.__x, self.__y,
                        legend="ConstrainedCurve",
                        color='black',
                        symbol='o',
                        linestyle='-')


    # DATA values
    """
    set min/max value of data source
    """
    def setDataMinMax(self, minVal, maxVal, update=True):
        if minVal is not None:
            vmin = float(str(minVal))
            self.dataMin = vmin
        if maxVal is not None:
            vmax = float(str(maxVal))
            self.dataMax = vmax

        if update:
            # are current values in the good range ?
            self._update()

    def getColormap(self):
        if self.minValue > self.maxValue:
            vmax = self.minValue
            vmin = self.maxValue
        else:
            vmax = self.maxValue
            vmin = self.minValue
        cmap = [self.colormapIndex, self.autoscale,
                vmin, vmax,
                self.dataMin, self.dataMax,
                self.colormapType]
        return cmap

    """
    send 'ColormapChanged' signal
    """
    def sendColormap(self):
        _logger.debug("sending colormap")
        try:
            cmap = self.getColormap()
            self.sigColormapChanged.emit(cmap)
        except:
            sys.excepthook(sys.exc_info()[0],
                           sys.exc_info()[1],
                           sys.exc_info()[2])
Ejemplo n.º 47
0
class SilxMaskImageWidget(qt.QMainWindow):
    """Main window with a plot widget, a toolbar and a slider.

    A list of images can be set with :meth:`setImages`.
    The mask can be accessed through getter and setter methods:
    :meth:`setSelectionMask` and :meth:`getSelectionMask`.

    The plot widget can be accessed as :attr:`plot`. It is a silx
    plot widget.

    The toolbar offers some basic interaction tools:
    zoom control, colormap, aspect ratio, y axis orientation,
    "save image" menu and a mask widget.
    """
    sigMaskImageWidget = qt.pyqtSignal(object)

    def __init__(self, parent=None):
        qt.QMainWindow.__init__(self, parent=parent)
        if parent is not None:
            # behave as a widget
            self.setWindowFlags(qt.Qt.Widget)
        else:
            self.setWindowTitle("PyMca - Image Selection Tool")

        centralWidget = qt.QWidget(self)
        layout = qt.QVBoxLayout(centralWidget)
        centralWidget.setLayout(layout)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)

        # Plot
        self.plot = PlotWidget(parent=centralWidget)
        self.plot.setWindowFlags(qt.Qt.Widget)
        self.plot.setDefaultColormap({
            'name': 'temperature',
            'normalization': 'linear',
            'autoscale': True,
            'vmin': 0.,
            'vmax': 1.
        })

        layout.addWidget(self.plot)

        # Mask Widget
        self._maskToolsDockWidget = None

        # Image selection slider
        self.slider = qt.QSlider(self.centralWidget())
        self.slider.setOrientation(qt.Qt.Horizontal)
        self.slider.setMinimum(0)
        self.slider.setMaximum(0)
        layout.addWidget(self.slider)
        self.slider.valueChanged[int].connect(self.showImage)

        # ADD/REMOVE/REPLACE IMAGE buttons
        buttonBox = qt.QWidget(self)
        buttonBoxLayout = qt.QHBoxLayout(buttonBox)
        buttonBoxLayout.setContentsMargins(0, 0, 0, 0)
        buttonBoxLayout.setSpacing(0)
        self.addImageButton = qt.QPushButton(buttonBox)
        icon = qt.QIcon(qt.QPixmap(IconDict["rgb16"]))
        self.addImageButton.setIcon(icon)
        self.addImageButton.setText("ADD IMAGE")
        self.addImageButton.setToolTip("Add image to RGB correlator")
        buttonBoxLayout.addWidget(self.addImageButton)

        self.removeImageButton = qt.QPushButton(buttonBox)
        self.removeImageButton.setIcon(icon)
        self.removeImageButton.setText("REMOVE IMAGE")
        self.removeImageButton.setToolTip("Remove image from RGB correlator")
        buttonBoxLayout.addWidget(self.removeImageButton)

        self.replaceImageButton = qt.QPushButton(buttonBox)
        self.replaceImageButton.setIcon(icon)
        self.replaceImageButton.setText("REPLACE IMAGE")
        self.replaceImageButton.setToolTip(
            "Replace all images in RGB correlator with this one")
        buttonBoxLayout.addWidget(self.replaceImageButton)

        self.addImageButton.clicked.connect(self._addImageClicked)
        self.removeImageButton.clicked.connect(self._removeImageClicked)
        self.replaceImageButton.clicked.connect(self._replaceImageClicked)

        layout.addWidget(buttonBox)

        # median filter widget
        self._medianParameters = {'row_width': 1, 'column_width': 1}
        self._medianParametersWidget = MedianParameters(self)
        self._medianParametersWidget.widthSpin.setValue(1)
        self._medianParametersWidget.widthSpin.valueChanged[int].connect(
            self._setMedianKernelWidth)
        layout.addWidget(self._medianParametersWidget)

        self.setCentralWidget(centralWidget)

        # Init actions
        self.group = qt.QActionGroup(self)
        self.group.setExclusive(False)

        self.resetZoomAction = self.group.addAction(
            PlotActions.ResetZoomAction(plot=self.plot, parent=self))
        self.addAction(self.resetZoomAction)

        self.zoomInAction = PlotActions.ZoomInAction(plot=self.plot,
                                                     parent=self)
        self.addAction(self.zoomInAction)

        self.zoomOutAction = PlotActions.ZoomOutAction(plot=self.plot,
                                                       parent=self)
        self.addAction(self.zoomOutAction)

        self.xAxisAutoScaleAction = self.group.addAction(
            PlotActions.XAxisAutoScaleAction(plot=self.plot, parent=self))
        self.addAction(self.xAxisAutoScaleAction)

        self.yAxisAutoScaleAction = self.group.addAction(
            PlotActions.YAxisAutoScaleAction(plot=self.plot, parent=self))
        self.addAction(self.yAxisAutoScaleAction)

        self.colormapAction = self.group.addAction(
            PlotActions.ColormapAction(plot=self.plot, parent=self))
        self.addAction(self.colormapAction)

        self.copyAction = self.group.addAction(
            PlotActions.CopyAction(plot=self.plot, parent=self))
        self.addAction(self.copyAction)

        self.group.addAction(self.getMaskAction())

        # Init toolbuttons
        self.saveToolbutton = SaveToolButton(parent=self, maskImageWidget=self)

        self.yAxisInvertedButton = PlotToolButtons.YAxisOriginToolButton(
            parent=self, plot=self.plot)

        self.keepDataAspectRatioButton = PlotToolButtons.AspectToolButton(
            parent=self, plot=self.plot)

        self.backgroundButton = qt.QToolButton(self)
        self.backgroundButton.setCheckable(True)
        self.backgroundButton.setIcon(
            qt.QIcon(qt.QPixmap(IconDict["subtract"])))
        self.backgroundButton.setToolTip(
            'Toggle background image subtraction from current image\n' +
            'No action if no background image available.')
        self.backgroundButton.clicked.connect(self._subtractBackground)

        # Creating the toolbar also create actions for toolbuttons
        self._toolbar = self._createToolBar(title='Plot', parent=None)
        self.addToolBar(self._toolbar)

        self._profile = ProfileToolBar(plot=self.plot)
        self.addToolBar(self._profile)
        self.setProfileToolbarVisible(False)

        # add a transparency slider for the stack data
        self._alphaSliderToolbar = qt.QToolBar("Alpha slider", parent=self)
        self._alphaSlider = NamedImageAlphaSlider(
            parent=self._alphaSliderToolbar, plot=self.plot, legend="current")
        self._alphaSlider.setOrientation(qt.Qt.Vertical)
        self._alphaSlider.setToolTip("Adjust opacity of stack image overlay")
        self._alphaSliderToolbar.addWidget(self._alphaSlider)
        self.addToolBar(qt.Qt.RightToolBarArea, self._alphaSliderToolbar)

        # hide optional tools and actions
        self.setAlphaSliderVisible(False)
        self.setBackgroundActionVisible(False)
        self.setMedianFilterWidgetVisible(False)
        self.setProfileToolbarVisible(False)

        self._images = []
        """List of images, as 2D numpy arrays or 3D numpy arrays (RGB(A)).
        """

        self._labels = []
        """List of image labels.
        """

        self._bg_images = []
        """List of background images, as 2D numpy arrays or 3D numpy arrays
        (RGB(A)).
        These images are not active, their colormap cannot be changed and
        they cannot be the base image used for drawing a mask.
        """

        self._bg_labels = []

        self._deltaXY = (
            1.0, 1.0)  # TODO: allow different scale and origin for each image
        """Current image scale (Xscale, Yscale) (in axis units per image pixel).
        The scale is adjusted to keep constant width and height for the image
        when a crop operation is applied."""

        self._origin = (0., 0.)
        """Current image origin: coordinate (x, y) of sample located at
        (row, column) = (0, 0)"""

        # scales and origins for background images
        self._bg_deltaXY = []
        self._bg_origins = []

    def sizeHint(self):
        return qt.QSize(500, 400)

    def _createToolBar(self, title, parent):
        """Create a QToolBar with crop, rotate and flip operations

        :param str title: The title of the QMenu
        :param qt.QWidget parent: See :class:`QToolBar`
        """
        toolbar = qt.QToolBar(title, parent)

        # Order widgets with actions
        objects = self.group.actions()

        # Add standard push buttons to list
        index = objects.index(self.colormapAction)
        objects.insert(index + 1, self.keepDataAspectRatioButton)
        objects.insert(index + 2, self.yAxisInvertedButton)
        objects.insert(index + 3, self.saveToolbutton)
        objects.insert(index + 4, self.backgroundButton)
        for obj in objects:
            if isinstance(obj, qt.QAction):
                toolbar.addAction(obj)
            else:
                # keep reference to toolbutton's action for changing visibility
                if obj is self.keepDataAspectRatioButton:
                    self.keepDataAspectRatioAction = toolbar.addWidget(obj)
                elif obj is self.yAxisInvertedButton:
                    self.yAxisInvertedAction = toolbar.addWidget(obj)
                elif obj is self.saveToolbutton:
                    self.saveAction = toolbar.addWidget(obj)
                elif obj is self.backgroundButton:
                    self.bgAction = toolbar.addWidget(obj)
                else:
                    raise RuntimeError()

        return toolbar

    def _getMaskToolsDockWidget(self):
        """DockWidget with image mask panel (lazy-loaded)."""
        if self._maskToolsDockWidget is None:
            self._maskToolsDockWidget = MyMaskToolsDockWidget(plot=self.plot,
                                                              name='Mask')
            self._maskToolsDockWidget.hide()
            self.addDockWidget(qt.Qt.RightDockWidgetArea,
                               self._maskToolsDockWidget)
            # self._maskToolsDockWidget.setFloating(True)
            self._maskToolsDockWidget.sigMaskChanged.connect(
                self._emitMaskImageWidgetSignal)
        return self._maskToolsDockWidget

    def _setMedianKernelWidth(self, value):
        kernelSize = numpy.asarray(value)
        if len(kernelSize.shape) == 0:
            kernelSize = [kernelSize.item()] * 2
        self._medianParameters['row_width'] = kernelSize[0]
        self._medianParameters['column_width'] = kernelSize[1]
        self._medianParametersWidget.widthSpin.setValue(int(kernelSize[0]))
        current = self.slider.value()
        self.showImage(current)

    def _subtractBackground(self):
        """When background button is clicked, this causes showImage to
        display the data after subtracting the stack background image.

        This background image is unrelated to the background images set
        with :meth:`setBackgroundImages`, it is simply the first data image
        whose label ends with 'background'."""
        current = self.getCurrentIndex()
        self.showImage(current)

    def setBackgroundActionVisible(self, visible):
        """Set visibility of the background toolbar button.

        :param visible: True to show tool button, False to hide it.
        """
        self.bgAction.setVisible(visible)

    def setProfileToolbarVisible(self, visible):
        """Set visibility of the profile toolbar

        :param visible: True to show toolbar, False to hide it.
        """
        self._profile.setVisible(visible)

    def setMedianFilterWidgetVisible(self, visible):
        """Set visibility of the median filter parametrs widget.

        :param visible: True to show widget, False to hide it.
        """
        self._medianParametersWidget.setVisible(visible)

    def setAlphaSliderVisible(self, visible):
        """Set visibility of the transparency slider widget in the right
        toolbar area.

        :param visible: True to show widget, False to hide it.
        """
        self._alphaSliderToolbar.setVisible(visible)

    def setImagesAlpha(self, alpha):
        """Set the opacity of the images layer.

        Full opacity means that the background images layer will not
        be visible.

        :param float alpha: Opacity of images layer, in [0., 1.]
        """
        self._alphaSlider.setValue(round(alpha * 255))

    def getMaskAction(self):
        """QAction toggling image mask dock widget

        :rtype: QAction
        """
        return self._getMaskToolsDockWidget().toggleViewAction()

    def _emitMaskImageWidgetSignal(self):
        mask = self.getSelectionMask()
        if not mask.size:
            # workaround to ignore the empty mask emitted when the mask widget
            # is initialized
            return
        self.sigMaskImageWidget.emit({
            "event": "selectionMaskChanged",
            "current": self.getSelectionMask(),
            "id": id(self)
        })

    def setSelectionMask(self, mask, copy=True):
        """Set the mask to a new array.

        :param numpy.ndarray mask: The array to use for the mask.
                    Mask type: array of uint8 of dimension 2,
                    Array of other types are converted.
        :param bool copy: True (the default) to copy the array,
                          False to use it as is if possible.
        :return: None if failed, shape of mask as 2-tuple if successful.
                 The mask can be cropped or padded to fit active image,
                 the returned shape is that of the active image.
        """
        if mask is None:
            mask = numpy.zeros_like(
                self._getMaskToolsDockWidget().getSelectionMask())
        if not len(mask):
            return
        # disconnect temporarily to avoid infinite loop
        self._getMaskToolsDockWidget().sigMaskChanged.disconnect(
            self._emitMaskImageWidgetSignal)
        ret = self._getMaskToolsDockWidget().setSelectionMask(mask, copy=copy)
        self._getMaskToolsDockWidget().sigMaskChanged.connect(
            self._emitMaskImageWidgetSignal)
        return ret

    def getSelectionMask(self, copy=True):
        """Get the current mask as a 2D array.

        :param bool copy: True (default) to get a copy of the mask.
                          If False, the returned array MUST not be modified.
        :return: The array of the mask with dimension of the 'active' image.
                 If there is no active image, an empty array is returned.
        :rtype: 2D numpy.ndarray of uint8
        """
        return self._getMaskToolsDockWidget().getSelectionMask(copy=copy)

    @staticmethod
    def _RgbaToGrayscale(image):
        """Convert RGBA image to 2D array of grayscale values
        (Luma coding)

        :param image: RGBA image, as a numpy array of shapes
             (nrows, ncols, 3/4)
        :return: Image as a 2D array
        """
        if len(image.shape) == 2:
            return image
        assert len(image.shape) == 3

        imageData = image[:, :, 0] * 0.299 +\
                    image[:, :, 1] * 0.587 +\
                    image[:, :, 2] * 0.114
        return imageData

    def getImageData(self):
        """Return current image data to be sent to RGB correlator

        :return: Image as a 2D array
        """
        index = self.slider.value()
        image = self._images[index]
        return self._RgbaToGrayscale(image)

    def getFirstBgImageData(self):
        """Return first bg image data to be sent to RGB correlator
        :return: Image as a 2D array
        """
        image = self._bg_images[0]
        return self._RgbaToGrayscale(image)

    def _addImageClicked(self):
        imageData = self.getImageData()
        ddict = {
            'event': "addImageClicked",
            'image': imageData,
            'title': self.plot.getGraphTitle(),
            'id': id(self)
        }
        self.sigMaskImageWidget.emit(ddict)

    def _replaceImageClicked(self):
        imageData = self.getImageData()
        ddict = {
            'event': "replaceImageClicked",
            'image': imageData,
            'title': self.plot.getGraphTitle(),
            'id': id(self)
        }
        self.sigMaskImageWidget.emit(ddict)

    def _removeImageClicked(self):
        imageData = self.getImageData()
        ddict = {
            'event': "removeImageClicked",
            'image': imageData,
            'title': self.plot.getGraphTitle(),
            'id': id(self)
        }
        self.sigMaskImageWidget.emit(ddict)

    def showImage(self, index=0):
        """Show data image corresponding to index. Update slider to index.
        """
        if not self._images:
            return
        assert index < len(self._images)

        bg_index = None
        if self.backgroundButton.isChecked():
            for i, imageName in enumerate(self._labels):
                if imageName.lower().endswith('background'):
                    bg_index = i
                    break

        mf_text = ""
        a = self._medianParameters['row_width']
        b = self._medianParameters['column_width']
        if max(a, b) > 1:
            mf_text = "MF(%d,%d) " % (a, b)

        imdata = self._getMedianData(self._images[index])
        if bg_index is None:
            self.plot.setGraphTitle(mf_text + self._labels[index])
        else:
            self.plot.setGraphTitle(mf_text + self._labels[index] + " Net")
            imdata -= self._images[bg_index]

        self.plot.addImage(imdata,
                           legend="current",
                           origin=self._origin,
                           scale=self._deltaXY,
                           replace=False,
                           z=0)
        self.plot.setActiveImage("current")
        self.slider.setValue(index)

    def _getMedianData(self, data):
        data = copy.copy(data)
        if max(self._medianParameters['row_width'],
               self._medianParameters['column_width']) > 1:
            data = medfilt2d(data, [
                self._medianParameters['row_width'],
                self._medianParameters['column_width']
            ])
        return data

    def setImages(self,
                  images,
                  labels=None,
                  origin=None,
                  height=None,
                  width=None):
        """Set the list of data images.

        All images share the same origin, width and height.

        :param images: List of 2D or 3D (for RGBA data) numpy arrays
            of image data. All images must have the same shape.
        :type images: List of ndarrays
        :param labels: list of image names
        :param origin: Image origin: coordinate (x, y) of sample located at
            (row, column) = (0, 0). If None, use (0., 0.)
        :param height: Image height in Y axis units. If None, use the
            image height in number of pixels.
        :param width: Image width in X axis units. If None, use the
            image width in number of pixels.
        """
        self._images = images
        if labels is None:
            labels = ["Image %d" % (i + 1) for i in range(len(images))]

        self._labels = labels

        height_pixels, width_pixels = images[0].shape[0:2]
        height = height or height_pixels
        width = width or width_pixels

        self._deltaXY = (float(width) / width_pixels,
                         float(height) / height_pixels)

        self._origin = origin or (0., 0.)

        current = self.slider.value()
        self.slider.setMaximum(len(self._images) - 1)
        if current < len(self._images):
            self.showImage(current)
        else:
            self.showImage(0)

        # _maskParamsCache = width, height, self._origin, self._deltaXY
        # if _maskParamsCache != self._maskParamsCache:
        #     self._maskParamsCache = _maskParamsCache
        #     self.resetMask(width, height, self._origin, self._deltaXY)

    def _updateBgScales(self, heights, widths):
        """Recalculate BG scales
        (e.g after a crop operation on :attr:`_bg_images`)"""
        self._bg_deltaXY = []
        for w, h, img in zip(widths, heights, self._bg_images):
            self._bg_deltaXY.append(
                (float(w) / img.shape[1], float(h) / img.shape[0]))

    def setBackgroundImages(self,
                            images,
                            labels=None,
                            origins=None,
                            heights=None,
                            widths=None):
        """Set the list of background images.

        Each image should be a tile and have an origin (x, y) tuple,
        a height and a width defined, so that all images can be plotted
        on the same background layer.

        :param images: List of 2D or 3D (for RGBA data) numpy arrays
            of image data. All images must have the same shape.
        :type images: List of ndarrays
        :param labels: list of image names
        :param origins: Images origins: list of coordinate tuples (x, y)
            of sample located at (row, column) = (0, 0).
            If None, use (0., 0.) for all images.
        :param height: Image height in Y axis units. If None, use the
            image height in number of pixels.
        :param width: Image width in X axis units. If None, use the
            image width in number of pixels.
        """
        self._bg_images = images
        if labels is None:
            labels = [
                "Background image %d" % (i + 1) for i in range(len(images))
            ]

        # delete existing images
        for label in self._bg_labels:
            self.plot.removeImage(label)

        self._bg_labels = labels

        if heights is None:
            heights = [image.shape[0] for image in images]
        else:
            assert len(heights) == len(images)
        if widths is None:
            widths = [image.shape[1] for image in images]
        else:
            assert len(widths) == len(images)

        if origins is None:
            origins = [(0, 0) for _img in images]
        else:
            assert len(origins) == len(images)

        self._bg_origins = origins
        self._updateBgScales(heights, widths)

        for bg_deltaXY, bg_orig, label, img in zip(self._bg_deltaXY,
                                                   self._bg_origins, labels,
                                                   images):
            # FIXME: we use z=-1 because the mask is always on z=1,
            # so the data must be on z=0. To be fixed after the silx mask
            # is improved
            self.plot.addImage(img,
                               origin=bg_orig,
                               scale=bg_deltaXY,
                               legend=label,
                               replace=False,
                               z=-1)  # TODO: z=0

    def getCurrentIndex(self):
        """
        :return: Index of slider widget used for image selection.
        """
        return self.slider.value()

    def getCurrentColormap(self):
        """Return colormap dict associated with the current image.
        If the current image is a RGBA Image, return None.

        See doc of silx.gui.plot.Plot for an explanation about the colormap
        dictionary.
        """
        image = self.plot.getImage(legend="current")
        if not hasattr(image, "getColormap"
                       ):  # isinstance(image, silx.gui.plot.items.ImageRgba):
            return None
        return self.plot.getImage(legend="current").getColormap()

    def showAndRaise(self):
        self.show()
        self.raise_()
Ejemplo n.º 48
0
 def setActiveCurve(self, legend, replot=True):
     return PlotWidget.setActiveCurve(self, legend)
Ejemplo n.º 49
0
class TestAxisSync(TestCaseQt):
    """Tests AxisSync class"""
    def setUp(self):
        TestCaseQt.setUp(self)
        self.plot1 = PlotWidget()
        self.plot2 = PlotWidget()
        self.plot3 = PlotWidget()

    def tearDown(self):
        self.plot1 = None
        self.plot2 = None
        self.plot3 = None
        TestCaseQt.tearDown(self)

    def testMoveFirstAxis(self):
        """Test synchronization after construction"""
        _sync = SyncAxes([
            self.plot1.getXAxis(),
            self.plot2.getXAxis(),
            self.plot3.getXAxis()
        ])

        self.plot1.getXAxis().setLimits(10, 500)
        self.assertEqual(self.plot1.getXAxis().getLimits(), (10, 500))
        self.assertEqual(self.plot2.getXAxis().getLimits(), (10, 500))
        self.assertEqual(self.plot3.getXAxis().getLimits(), (10, 500))

    def testMoveSecondAxis(self):
        """Test synchronization after construction"""
        _sync = SyncAxes([
            self.plot1.getXAxis(),
            self.plot2.getXAxis(),
            self.plot3.getXAxis()
        ])

        self.plot2.getXAxis().setLimits(10, 500)
        self.assertEqual(self.plot1.getXAxis().getLimits(), (10, 500))
        self.assertEqual(self.plot2.getXAxis().getLimits(), (10, 500))
        self.assertEqual(self.plot3.getXAxis().getLimits(), (10, 500))

    def testMoveTwoAxes(self):
        """Test synchronization after construction"""
        _sync = SyncAxes([
            self.plot1.getXAxis(),
            self.plot2.getXAxis(),
            self.plot3.getXAxis()
        ])

        self.plot1.getXAxis().setLimits(1, 50)
        self.plot2.getXAxis().setLimits(10, 500)
        self.assertEqual(self.plot1.getXAxis().getLimits(), (10, 500))
        self.assertEqual(self.plot2.getXAxis().getLimits(), (10, 500))
        self.assertEqual(self.plot3.getXAxis().getLimits(), (10, 500))

    def testDestruction(self):
        """Test synchronization when sync object is destroyed"""
        sync = SyncAxes([
            self.plot1.getXAxis(),
            self.plot2.getXAxis(),
            self.plot3.getXAxis()
        ])
        del sync

        self.plot1.getXAxis().setLimits(10, 500)
        self.assertEqual(self.plot1.getXAxis().getLimits(), (10, 500))
        self.assertNotEqual(self.plot2.getXAxis().getLimits(), (10, 500))
        self.assertNotEqual(self.plot3.getXAxis().getLimits(), (10, 500))

    def testAxisDestruction(self):
        """Test synchronization when an axis disappear"""
        _sync = SyncAxes([
            self.plot1.getXAxis(),
            self.plot2.getXAxis(),
            self.plot3.getXAxis()
        ])

        # Destroy the plot is possible
        import weakref
        plot = weakref.ref(self.plot2)
        self.plot2 = None
        result = self.qWaitForDestroy(plot)
        if not result:
            # We can't test
            self.skipTest("Object not destroyed")

        self.plot1.getXAxis().setLimits(10, 500)
        self.assertEqual(self.plot3.getXAxis().getLimits(), (10, 500))

    def testStop(self):
        """Test synchronization after calling stop"""
        sync = SyncAxes([
            self.plot1.getXAxis(),
            self.plot2.getXAxis(),
            self.plot3.getXAxis()
        ])
        sync.stop()

        self.plot1.getXAxis().setLimits(10, 500)
        self.assertEqual(self.plot1.getXAxis().getLimits(), (10, 500))
        self.assertNotEqual(self.plot2.getXAxis().getLimits(), (10, 500))
        self.assertNotEqual(self.plot3.getXAxis().getLimits(), (10, 500))

    def testStopMovingStart(self):
        """Test synchronization after calling stop, moving an axis, then start again"""
        sync = SyncAxes([
            self.plot1.getXAxis(),
            self.plot2.getXAxis(),
            self.plot3.getXAxis()
        ])
        sync.stop()
        self.plot1.getXAxis().setLimits(10, 500)
        self.plot2.getXAxis().setLimits(1, 50)
        self.assertEqual(self.plot1.getXAxis().getLimits(), (10, 500))
        sync.start()

        # The first axis is the reference
        self.assertEqual(self.plot1.getXAxis().getLimits(), (10, 500))
        self.assertEqual(self.plot2.getXAxis().getLimits(), (10, 500))
        self.assertEqual(self.plot3.getXAxis().getLimits(), (10, 500))

    def testDoubleStop(self):
        """Test double stop"""
        sync = SyncAxes([
            self.plot1.getXAxis(),
            self.plot2.getXAxis(),
            self.plot3.getXAxis()
        ])
        sync.stop()
        self.assertRaises(RuntimeError, sync.stop)

    def testDoubleStart(self):
        """Test double stop"""
        sync = SyncAxes([
            self.plot1.getXAxis(),
            self.plot2.getXAxis(),
            self.plot3.getXAxis()
        ])
        self.assertRaises(RuntimeError, sync.start)

    def testScale(self):
        """Test scale change"""
        _sync = SyncAxes([
            self.plot1.getXAxis(),
            self.plot2.getXAxis(),
            self.plot3.getXAxis()
        ])
        self.plot1.getXAxis().setScale(self.plot1.getXAxis().LOGARITHMIC)
        self.assertEqual(self.plot1.getXAxis().getScale(),
                         self.plot1.getXAxis().LOGARITHMIC)
        self.assertEqual(self.plot2.getXAxis().getScale(),
                         self.plot1.getXAxis().LOGARITHMIC)
        self.assertEqual(self.plot3.getXAxis().getScale(),
                         self.plot1.getXAxis().LOGARITHMIC)

    def testDirection(self):
        """Test direction change"""
        _sync = SyncAxes([
            self.plot1.getYAxis(),
            self.plot2.getYAxis(),
            self.plot3.getYAxis()
        ])
        self.plot1.getYAxis().setInverted(True)
        self.assertEqual(self.plot1.getYAxis().isInverted(), True)
        self.assertEqual(self.plot2.getYAxis().isInverted(), True)
        self.assertEqual(self.plot3.getYAxis().isInverted(), True)
Ejemplo n.º 50
0
 def createWidget(self, parent):
     plot = PlotWidget(parent)
     plot.setAutoReplot(False)
     return plot
Ejemplo n.º 51
0
Archivo: view.py Proyecto: fangohr/silx
def main(argv):
    """
    Main function to launch the viewer as an application

    :param argv: Command line arguments
    :returns: exit status
    """
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument(
        'files',
        nargs=argparse.ZERO_OR_MORE,
        help='Data file to show (h5 file, edf files, spec files)')
    parser.add_argument(
        '--debug',
        dest="debug",
        action="store_true",
        default=False,
        help='Set logging system in debug mode')
    parser.add_argument(
        '--use-opengl-plot',
        dest="use_opengl_plot",
        action="store_true",
        default=False,
        help='Use OpenGL for plots (instead of matplotlib)')

    options = parser.parse_args(argv[1:])

    if options.debug:
        logging.root.setLevel(logging.DEBUG)

    #
    # Import most of the things here to be sure to use the right logging level
    #

    try:
        # it should be loaded before h5py
        import hdf5plugin  # noqa
    except ImportError:
        _logger.debug("Backtrace", exc_info=True)
        hdf5plugin = None

    try:
        import h5py
    except ImportError:
        _logger.debug("Backtrace", exc_info=True)
        h5py = None

    if h5py is None:
        message = "Module 'h5py' is not installed but is mandatory."\
            + " You can install it using \"pip install h5py\"."
        _logger.error(message)
        return -1

    if hdf5plugin is None:
        message = "Module 'hdf5plugin' is not installed. It supports some hdf5"\
            + " compressions. You can install it using \"pip install hdf5plugin\"."
        _logger.warning(message)

    #
    # Run the application
    #

    if options.use_opengl_plot:
        from silx.gui.plot import PlotWidget
        PlotWidget.setDefaultBackend("opengl")

    app = qt.QApplication([])
    qt.QLocale.setDefault(qt.QLocale.c())

    sys.excepthook = qt.exceptionHandler
    window = Viewer()
    window.setAttribute(qt.Qt.WA_DeleteOnClose, True)
    window.resize(qt.QSize(640, 480))

    for filename in options.files:
        try:
            window.appendFile(filename)
        except IOError as e:
            _logger.error(e.args[0])
            _logger.debug("Backtrace", exc_info=True)

    window.show()
    result = app.exec_()
    # remove ending warnings relative to QTimer
    app.deleteLater()
    return result
Ejemplo n.º 52
0
class BackgroundWidget(qt.QWidget):
    """Background configuration widget, with a plot to preview the results.

    Strip and snip filters parameters can be adjusted using input widgets,
    and the computed backgrounds are plotted next to the original data to
    show the result."""
    def __init__(self, parent=None):
        qt.QWidget.__init__(self, parent)
        self.setWindowTitle("Strip and SNIP Configuration Window")
        self.mainLayout = qt.QVBoxLayout(self)
        self.mainLayout.setContentsMargins(0, 0, 0, 0)
        self.mainLayout.setSpacing(2)
        self.parametersWidget = BackgroundParamWidget(self)
        self.graphWidget = PlotWidget(parent=self)
        self.mainLayout.addWidget(self.parametersWidget)
        self.mainLayout.addWidget(self.graphWidget)
        self._x = None
        self._y = None
        self.parametersWidget.sigBackgroundParamWidgetSignal.connect(self._slot)

    def getParameters(self):
        """Return dictionary of parameters defined in the GUI

        The returned dictionary contains following values:

            - *algorithm*: *"strip"* or *"snip"*
            - *StripWidth*: width of strip iterator
            - *StripIterations*: number of iterations
            - *StripThreshold*: strip curvature (currently fixed to 1.0)
            - *SnipWidth*: width of snip algorithm
            - *SmoothingFlag*: flag to enable/disable smoothing
            - *SmoothingWidth*: width of Savitsky-Golay smoothing filter
            - *AnchorsFlag*: flag to enable/disable anchors
            - *AnchorsList*: list of anchors (X coordinates of fixed values)
        """
        return self.parametersWidget.getParameters()

    def setParameters(self, ddict):
        """Set values for all input widgets.

        :param dict ddict: Input dictionary, must have the same
            keys as the dictionary output by :meth:`getParameters`
        """
        return self.parametersWidget.setParameters(ddict)

    def setData(self, x, y, xmin=None, xmax=None):
        """Set data for the original curve, and _update strip and snip
        curves accordingly.

        :param x: Array or sequence of curve abscissa values
        :param y: Array or sequence of curve ordinate values
        :param xmin: Min value to be displayed on the X axis
        :param xmax: Max value to be displayed on the X axis
        """
        self._x = x
        self._y = y
        self._xmin = xmin
        self._xmax = xmax
        self._update(resetzoom=True)

    def _slot(self, ddict):
        self._update()

    def _update(self, resetzoom=False):
        """Compute strip and snip backgrounds, update the curves
        """
        if self._y is None:
            return

        pars = self.getParameters()

        # smoothed data
        y = numpy.ravel(numpy.array(self._y)).astype(numpy.float)
        if pars["SmoothingFlag"]:
            ysmooth = filters.savitsky_golay(y, pars['SmoothingWidth'])
            f = [0.25, 0.5, 0.25]
            ysmooth[1:-1] = numpy.convolve(ysmooth, f, mode=0)
            ysmooth[0] = 0.5 * (ysmooth[0] + ysmooth[1])
            ysmooth[-1] = 0.5 * (ysmooth[-1] + ysmooth[-2])
        else:
            ysmooth = y


        # loop for anchors
        x = self._x
        niter = pars['StripIterations']
        anchors_indices = []
        if pars['AnchorsFlag'] and pars['AnchorsList'] is not None:
            ravelled = x
            for channel in pars['AnchorsList']:
                if channel <= ravelled[0]:
                    continue
                index = numpy.nonzero(ravelled >= channel)[0]
                if len(index):
                    index = min(index)
                    if index > 0:
                        anchors_indices.append(index)

        stripBackground = filters.strip(ysmooth,
                                        w=pars['StripWidth'],
                                        niterations=niter,
                                        factor=pars['StripThreshold'],
                                        anchors=anchors_indices)

        if niter >= 1000:
            # final smoothing
            stripBackground = filters.strip(stripBackground,
                                            w=1,
                                            niterations=50*pars['StripWidth'],
                                            factor=pars['StripThreshold'],
                                            anchors=anchors_indices)

        if len(anchors_indices) == 0:
            anchors_indices = [0, len(ysmooth)-1]
        anchors_indices.sort()
        snipBackground = 0.0 * ysmooth
        lastAnchor = 0
        for anchor in anchors_indices:
            if (anchor > lastAnchor) and (anchor < len(ysmooth)):
                snipBackground[lastAnchor:anchor] =\
                            filters.snip1d(ysmooth[lastAnchor:anchor],
                                           pars['SnipWidth'])
                lastAnchor = anchor
        if lastAnchor < len(ysmooth):
            snipBackground[lastAnchor:] =\
                            filters.snip1d(ysmooth[lastAnchor:],
                                           pars['SnipWidth'])

        self.graphWidget.addCurve(x, y,
                                  legend='Input Data',
                                  replace=True,
                                  resetzoom=resetzoom)
        self.graphWidget.addCurve(x, stripBackground,
                                  legend='Strip Background',
                                  resetzoom=False)
        self.graphWidget.addCurve(x, snipBackground,
                                  legend='SNIP Background',
                                  resetzoom=False)
        if self._xmin is not None and self._xmax is not None:
            self.graphWidget.getXAxis().setLimits(self._xmin, self._xmax)
Ejemplo n.º 53
0
    def setUp(self):
        self.plot = PlotWidget(backend='none')
        self.saveAction = SaveAction(plot=self.plot)

        self.tempdir = tempfile.mkdtemp()
        self.out_fname = os.path.join(self.tempdir, "out.dat")
Ejemplo n.º 54
0
class TestSaveActionSaveCurvesAsSpec(unittest.TestCase):

    def setUp(self):
        self.plot = PlotWidget(backend='none')
        self.saveAction = SaveAction(plot=self.plot)

        self.tempdir = tempfile.mkdtemp()
        self.out_fname = os.path.join(self.tempdir, "out.dat")

    def tearDown(self):
        os.unlink(self.out_fname)
        os.rmdir(self.tempdir)

    def testSaveMultipleCurvesAsSpec(self):
        """Test that labels are properly used."""
        self.plot.setGraphXLabel("graph x label")
        self.plot.setGraphYLabel("graph y label")

        self.plot.addCurve([0, 1], [1, 2], "curve with labels",
                           xlabel="curve0 X", ylabel="curve0 Y")
        self.plot.addCurve([-1, 3], [-6, 2], "curve with X label",
                           xlabel="curve1 X")
        self.plot.addCurve([-2, 0], [8, 12], "curve with Y label",
                           ylabel="curve2 Y")
        self.plot.addCurve([3, 1], [7, 6], "curve with no labels")

        self.saveAction._saveCurves(self.plot,
                                    self.out_fname,
                                    SaveAction.DEFAULT_ALL_CURVES_FILTERS[0])  # "All curves as SpecFile (*.dat)"

        with open(self.out_fname, "rb") as f:
            file_content = f.read()
            if hasattr(file_content, "decode"):
                file_content = file_content.decode()

            # case with all curve labels specified
            self.assertIn("#S 1 curve0 Y", file_content)
            self.assertIn("#L curve0 X  curve0 Y", file_content)

            # graph X&Y labels are used when no curve label is specified
            self.assertIn("#S 2 graph y label", file_content)
            self.assertIn("#L curve1 X  graph y label", file_content)

            self.assertIn("#S 3 curve2 Y", file_content)
            self.assertIn("#L graph x label  curve2 Y", file_content)

            self.assertIn("#S 4 graph y label", file_content)
            self.assertIn("#L graph x label  graph y label", file_content)
 def setUp(self):
     self.plot = PlotWidget()
Ejemplo n.º 56
0
    def setUp(self):
        self.plot = PlotWidget(backend='none')
        self.saveAction = SaveAction(plot=self.plot)

        self.tempdir = tempfile.mkdtemp()
        self.out_fname = os.path.join(self.tempdir, "out.dat")
Ejemplo n.º 57
0
    def __init__(self, parent=None, name="Colormap Dialog"):
        qt.QDialog.__init__(self, parent)
        self.setWindowTitle(name)
        self.title = name


        self.colormapList = ["Greyscale", "Reverse Grey", "Temperature",
                             "Red", "Green", "Blue", "Many"]

        # histogramData is tupel(bins, counts)
        self.histogramData = None

        # default values
        self.dataMin   = -10
        self.dataMax   = 10
        self.minValue  = 0
        self.maxValue  = 1

        self.colormapIndex  = 2
        self.colormapType   = 0

        self.autoscale   = False
        self.autoscale90 = False
        # main layout
        vlayout = qt.QVBoxLayout(self)
        vlayout.setContentsMargins(10, 10, 10, 10)
        vlayout.setSpacing(0)

        # layout 1 : -combo to choose colormap
        #            -autoscale button
        #            -autoscale 90% button
        hbox1    = qt.QWidget(self)
        hlayout1 = qt.QHBoxLayout(hbox1)
        vlayout.addWidget(hbox1)
        hlayout1.setContentsMargins(0, 0, 0, 0)
        hlayout1.setSpacing(10)

        # combo
        self.combo = qt.QComboBox(hbox1)
        for colormap in self.colormapList:
            self.combo.addItem(colormap)
        self.combo.activated[int].connect(self.colormapChange)
        hlayout1.addWidget(self.combo)

        # autoscale
        self.autoScaleButton = qt.QPushButton("Autoscale", hbox1)
        self.autoScaleButton.setCheckable(True)
        self.autoScaleButton.setAutoDefault(False)
        self.autoScaleButton.toggled[bool].connect(self.autoscaleChange)
        hlayout1.addWidget(self.autoScaleButton)

        # autoscale 90%
        self.autoScale90Button = qt.QPushButton("Autoscale 90%", hbox1)
        self.autoScale90Button.setCheckable(True)
        self.autoScale90Button.setAutoDefault(False)

        self.autoScale90Button.toggled[bool].connect(self.autoscale90Change)
        hlayout1.addWidget(self.autoScale90Button)

        # hlayout
        hbox0    = qt.QWidget(self)
        self.__hbox0 = hbox0
        hlayout0 = qt.QHBoxLayout(hbox0)
        hlayout0.setContentsMargins(0, 0, 0, 0)
        hlayout0.setSpacing(0)
        vlayout.addWidget(hbox0)
        #hlayout0.addStretch(10)

        self.buttonGroup = qt.QButtonGroup()
        g1 = qt.QCheckBox(hbox0)
        g1.setText("Linear")
        g2 = qt.QCheckBox(hbox0)
        g2.setText("Logarithmic")
        g3 = qt.QCheckBox(hbox0)
        g3.setText("Gamma")
        self.buttonGroup.addButton(g1, 0)
        self.buttonGroup.addButton(g2, 1)
        self.buttonGroup.addButton(g3, 2)
        self.buttonGroup.setExclusive(True)
        if self.colormapType == 1:
            self.buttonGroup.button(1).setChecked(True)
        elif self.colormapType == 2:
            self.buttonGroup.button(2).setChecked(True)
        else:
            self.buttonGroup.button(0).setChecked(True)
        hlayout0.addWidget(g1)
        hlayout0.addWidget(g2)
        hlayout0.addWidget(g3)
        vlayout.addWidget(hbox0)
        self.buttonGroup.buttonClicked[int].connect(self.buttonGroupChange)
        vlayout.addSpacing(20)

        hboxlimits = qt.QWidget(self)
        hboxlimitslayout = qt.QHBoxLayout(hboxlimits)
        hboxlimitslayout.setContentsMargins(0, 0, 0, 0)
        hboxlimitslayout.setSpacing(0)

        self.slider = None

        vlayout.addWidget(hboxlimits)

        vboxlimits = qt.QWidget(hboxlimits)
        vboxlimitslayout = qt.QVBoxLayout(vboxlimits)
        vboxlimitslayout.setContentsMargins(0, 0, 0, 0)
        vboxlimitslayout.setSpacing(0)
        hboxlimitslayout.addWidget(vboxlimits)

        # hlayout 2 : - min label
        #             - min texte
        hbox2    = qt.QWidget(vboxlimits)
        self.__hbox2 = hbox2
        hlayout2 = qt.QHBoxLayout(hbox2)
        hlayout2.setContentsMargins(0, 0, 0, 0)
        hlayout2.setSpacing(0)
        #vlayout.addWidget(hbox2)
        vboxlimitslayout.addWidget(hbox2)
        hlayout2.addStretch(10)

        self.minLabel  = qt.QLabel(hbox2)
        self.minLabel.setText("Minimum")
        hlayout2.addWidget(self.minLabel)

        hlayout2.addSpacing(5)
        hlayout2.addStretch(1)
        self.minText  = MyQLineEdit(hbox2)
        self.minText.setFixedWidth(150)
        self.minText.setAlignment(qt.Qt.AlignRight)
        self.minText.returnPressed[()].connect(self.minTextChanged)
        hlayout2.addWidget(self.minText)

        # hlayout 3 : - min label
        #             - min text
        hbox3    = qt.QWidget(vboxlimits)
        self.__hbox3 = hbox3
        hlayout3 = qt.QHBoxLayout(hbox3)
        hlayout3.setContentsMargins(0, 0, 0, 0)
        hlayout3.setSpacing(0)
        #vlayout.addWidget(hbox3)
        vboxlimitslayout.addWidget(hbox3)

        hlayout3.addStretch(10)
        self.maxLabel = qt.QLabel(hbox3)
        self.maxLabel.setText("Maximum")
        hlayout3.addWidget(self.maxLabel)

        hlayout3.addSpacing(5)
        hlayout3.addStretch(1)

        self.maxText = MyQLineEdit(hbox3)
        self.maxText.setFixedWidth(150)
        self.maxText.setAlignment(qt.Qt.AlignRight)

        self.maxText.returnPressed[()].connect(self.maxTextChanged)
        hlayout3.addWidget(self.maxText)


        # Graph widget for color curve...
        self.c = PlotWidget(self, backend=None)
        self.c.setGraphXLabel("Data Values")
        self.c.setInteractiveMode('select')

        self.marge = (abs(self.dataMax) + abs(self.dataMin)) / 6.0
        self.minmd = self.dataMin - self.marge
        self.maxpd = self.dataMax + self.marge

        self.c.setGraphXLimits(self.minmd, self.maxpd)
        self.c.setGraphYLimits(-11.5, 11.5)

        x = [self.minmd, self.dataMin, self.dataMax, self.maxpd]
        y = [-10, -10, 10, 10 ]
        self.c.addCurve(x, y,
                        legend="ConstrainedCurve",
                        color='black',
                        symbol='o',
                        linestyle='-')
        self.markers = []
        self.__x = x
        self.__y = y
        labelList = ["","Min", "Max", ""]
        for i in range(4):
            if i in [1, 2]:
                draggable = True
                color = "blue"
            else:
                draggable = False
                color = "black"
            #TODO symbol
            legend = "%d" % i
            self.c.addXMarker(x[i],
                              legend=legend,
                              text=labelList[i],
                              draggable=draggable,
                              color=color)
            self.markers.append((legend, ""))

        self.c.setMinimumSize(qt.QSize(250,200))
        vlayout.addWidget(self.c)

        self.c.sigPlotSignal.connect(self.chval)

        # colormap window can not be resized
        self.setFixedSize(vlayout.minimumSize())
Ejemplo n.º 58
0
 def setActiveCurveColor(self, *var, **kw):
     return PlotWidget.setActiveCurveStyle(self, *var, **kw)