summaryrefslogtreecommitdiff
path: root/silx/gui/widgets
diff options
context:
space:
mode:
Diffstat (limited to 'silx/gui/widgets')
-rw-r--r--silx/gui/widgets/BoxLayoutDockWidget.py90
-rw-r--r--silx/gui/widgets/ColormapNameComboBox.py166
-rw-r--r--silx/gui/widgets/ElidedLabel.py137
-rw-r--r--silx/gui/widgets/FloatEdit.py65
-rw-r--r--silx/gui/widgets/FlowLayout.py177
-rw-r--r--silx/gui/widgets/FrameBrowser.py324
-rw-r--r--silx/gui/widgets/HierarchicalTableView.py172
-rwxr-xr-xsilx/gui/widgets/LegendIconWidget.py514
-rw-r--r--silx/gui/widgets/MedianFilterDialog.py80
-rw-r--r--silx/gui/widgets/MultiModeAction.py83
-rw-r--r--silx/gui/widgets/PeriodicTable.py831
-rw-r--r--silx/gui/widgets/PrintGeometryDialog.py222
-rw-r--r--silx/gui/widgets/PrintPreview.py728
-rw-r--r--silx/gui/widgets/RangeSlider.py765
-rw-r--r--silx/gui/widgets/TableWidget.py626
-rw-r--r--silx/gui/widgets/ThreadPoolPushButton.py238
-rw-r--r--silx/gui/widgets/UrlSelectionTable.py172
-rw-r--r--silx/gui/widgets/WaitingPushButton.py245
-rw-r--r--silx/gui/widgets/__init__.py27
-rw-r--r--silx/gui/widgets/setup.py41
-rw-r--r--silx/gui/widgets/test/__init__.py59
-rw-r--r--silx/gui/widgets/test/test_boxlayoutdockwidget.py83
-rw-r--r--silx/gui/widgets/test/test_elidedlabel.py111
-rw-r--r--silx/gui/widgets/test/test_flowlayout.py77
-rw-r--r--silx/gui/widgets/test/test_framebrowser.py73
-rw-r--r--silx/gui/widgets/test/test_hierarchicaltableview.py117
-rw-r--r--silx/gui/widgets/test/test_legendiconwidget.py74
-rw-r--r--silx/gui/widgets/test/test_periodictable.py163
-rw-r--r--silx/gui/widgets/test/test_printpreview.py74
-rw-r--r--silx/gui/widgets/test/test_rangeslider.py114
-rw-r--r--silx/gui/widgets/test/test_tablewidget.py61
-rw-r--r--silx/gui/widgets/test/test_threadpoolpushbutton.py135
32 files changed, 0 insertions, 6844 deletions
diff --git a/silx/gui/widgets/BoxLayoutDockWidget.py b/silx/gui/widgets/BoxLayoutDockWidget.py
deleted file mode 100644
index 3d2b853..0000000
--- a/silx/gui/widgets/BoxLayoutDockWidget.py
+++ /dev/null
@@ -1,90 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2018 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-"""A QDockWidget that update the layout direction of its widget
-"""
-
-__authors__ = ["T. Vincent"]
-__license__ = "MIT"
-__date__ = "06/03/2018"
-
-
-from .. import qt
-
-
-class BoxLayoutDockWidget(qt.QDockWidget):
- """QDockWidget adjusting its child widget QBoxLayout direction.
-
- The child widget layout direction is set according to dock widget area.
- The child widget MUST use a QBoxLayout
-
- :param parent: See :class:`QDockWidget`
- :param flags: See :class:`QDockWidget`
- """
-
- def __init__(self, parent=None, flags=qt.Qt.Widget):
- super(BoxLayoutDockWidget, self).__init__(parent, flags)
- self._currentArea = qt.Qt.NoDockWidgetArea
- self.dockLocationChanged.connect(self._dockLocationChanged)
- self.topLevelChanged.connect(self._topLevelChanged)
-
- def setWidget(self, widget):
- """Set the widget of this QDockWidget
-
- See :meth:`QDockWidget.setWidget`
- """
- super(BoxLayoutDockWidget, self).setWidget(widget)
- # Update widget's layout direction
- self._dockLocationChanged(self._currentArea)
-
- def _dockLocationChanged(self, area):
- self._currentArea = area
-
- widget = self.widget()
- if widget is not None:
- layout = widget.layout()
- if isinstance(layout, qt.QBoxLayout):
- if area in (qt.Qt.LeftDockWidgetArea, qt.Qt.RightDockWidgetArea):
- direction = qt.QBoxLayout.TopToBottom
- else:
- direction = qt.QBoxLayout.LeftToRight
- layout.setDirection(direction)
- self.resize(widget.minimumSize())
- self.adjustSize()
-
- def _topLevelChanged(self, topLevel):
- widget = self.widget()
- if widget is not None and topLevel:
- layout = widget.layout()
- if isinstance(layout, qt.QBoxLayout):
- layout.setDirection(qt.QBoxLayout.LeftToRight)
- self.resize(widget.minimumSize())
- self.adjustSize()
-
- def showEvent(self, event):
- """Make sure this dock widget is raised when it is shown.
-
- This is useful for tabbed dock widgets.
- """
- self.raise_()
diff --git a/silx/gui/widgets/ColormapNameComboBox.py b/silx/gui/widgets/ColormapNameComboBox.py
deleted file mode 100644
index fa8faf1..0000000
--- a/silx/gui/widgets/ColormapNameComboBox.py
+++ /dev/null
@@ -1,166 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2004-2018 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-"""A QComboBox to display prefered colormaps
-"""
-
-from __future__ import division
-
-__authors__ = ["V.A. Sole", "T. Vincent", "H. Payno"]
-__license__ = "MIT"
-__date__ = "27/11/2018"
-
-
-import logging
-import numpy
-
-from .. import qt
-from .. import colors as colors_mdl
-
-_logger = logging.getLogger(__name__)
-
-
-_colormapIconPreview = {}
-
-
-class ColormapNameComboBox(qt.QComboBox):
- def __init__(self, parent=None):
- qt.QComboBox.__init__(self, parent)
- self.__initItems()
-
- LUT_NAME = qt.Qt.UserRole + 1
- LUT_COLORS = qt.Qt.UserRole + 2
-
- def __initItems(self):
- for colormapName in colors_mdl.preferredColormaps():
- index = self.count()
- self.addItem(str.title(colormapName))
- self.setItemIcon(index, self.getIconPreview(name=colormapName))
- self.setItemData(index, colormapName, role=self.LUT_NAME)
-
- def getIconPreview(self, name=None, colors=None):
- """Return an icon preview from a LUT name.
-
- This icons are cached into a global structure.
-
- :param str name: Name of the LUT
- :param numpy.ndarray colors: Colors identify the LUT
- :rtype: qt.QIcon
- """
- if name is not None:
- iconKey = name
- else:
- iconKey = tuple(colors)
- icon = _colormapIconPreview.get(iconKey, None)
- if icon is None:
- icon = self.createIconPreview(name, colors)
- _colormapIconPreview[iconKey] = icon
- return icon
-
- def createIconPreview(self, name=None, colors=None):
- """Create and return an icon preview from a LUT name.
-
- This icons are cached into a global structure.
-
- :param str name: Name of the LUT
- :param numpy.ndarray colors: Colors identify the LUT
- :rtype: qt.QIcon
- """
- colormap = colors_mdl.Colormap(name)
- size = 32
- if name is not None:
- lut = colormap.getNColors(size)
- else:
- lut = colors
- if len(lut) > size:
- # Down sample
- step = int(len(lut) / size)
- lut = lut[::step]
- elif len(lut) < size:
- # Over sample
- indexes = numpy.arange(size) / float(size) * (len(lut) - 1)
- indexes = indexes.astype("int")
- lut = lut[indexes]
- if lut is None or len(lut) == 0:
- return qt.QIcon()
-
- pixmap = qt.QPixmap(size, size)
- painter = qt.QPainter(pixmap)
- for i in range(size):
- rgb = lut[i]
- r, g, b = rgb[0], rgb[1], rgb[2]
- painter.setPen(qt.QColor(r, g, b))
- painter.drawPoint(qt.QPoint(i, 0))
-
- painter.drawPixmap(0, 1, size, size - 1, pixmap, 0, 0, size, 1)
- painter.end()
-
- return qt.QIcon(pixmap)
-
- def getCurrentName(self):
- return self.itemData(self.currentIndex(), self.LUT_NAME)
-
- def getCurrentColors(self):
- return self.itemData(self.currentIndex(), self.LUT_COLORS)
-
- def findLutName(self, name):
- return self.findData(name, role=self.LUT_NAME)
-
- def findLutColors(self, lut):
- for index in range(self.count()):
- if self.itemData(index, role=self.LUT_NAME) is not None:
- continue
- colors = self.itemData(index, role=self.LUT_COLORS)
- if colors is None:
- continue
- if numpy.array_equal(colors, lut):
- return index
- return -1
-
- def setCurrentLut(self, colormap):
- name = colormap.getName()
- if name is not None:
- self._setCurrentName(name)
- else:
- lut = colormap.getColormapLUT()
- self._setCurrentLut(lut)
-
- def _setCurrentLut(self, lut):
- index = self.findLutColors(lut)
- if index == -1:
- index = self.count()
- self.addItem("Custom")
- self.setItemIcon(index, self.getIconPreview(colors=lut))
- self.setItemData(index, None, role=self.LUT_NAME)
- self.setItemData(index, lut, role=self.LUT_COLORS)
- self.setCurrentIndex(index)
-
- def _setCurrentName(self, name):
- index = self.findLutName(name)
- if index < 0:
- index = self.count()
- self.addItem(str.title(name))
- self.setItemIcon(index, self.getIconPreview(name=name))
- self.setItemData(index, name, role=self.LUT_NAME)
- self.setCurrentIndex(index)
diff --git a/silx/gui/widgets/ElidedLabel.py b/silx/gui/widgets/ElidedLabel.py
deleted file mode 100644
index fe53bb9..0000000
--- a/silx/gui/widgets/ElidedLabel.py
+++ /dev/null
@@ -1,137 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2004-2020 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-"""Module contains an elidable label
-"""
-
-__license__ = "MIT"
-__date__ = "07/12/2018"
-
-from silx.gui import qt
-
-
-class ElidedLabel(qt.QLabel):
- """QLabel with an edile property.
-
- By default if the text is too big, it is elided on the right.
-
- This mode can be changed with :func:`setElideMode`.
-
- In case the text is elided, the full content is displayed as part of the
- tool tip. This behavior can be disabled with :func:`setTextAsToolTip`.
- """
-
- def __init__(self, parent=None):
- super(ElidedLabel, self).__init__(parent)
- self.__text = ""
- self.__toolTip = ""
- self.__textAsToolTip = True
- self.__textIsElided = False
- self.__elideMode = qt.Qt.ElideRight
- self.__updateMinimumSize()
-
- def resizeEvent(self, event):
- self.__updateText()
- return qt.QLabel.resizeEvent(self, event)
-
- def setFont(self, font):
- qt.QLabel.setFont(self, font)
- self.__updateMinimumSize()
- self.__updateText()
-
- def __updateMinimumSize(self):
- metrics = self.fontMetrics()
- width = metrics.width("...")
- self.setMinimumWidth(width)
-
- def __updateText(self):
- metrics = self.fontMetrics()
- elidedText = metrics.elidedText(self.__text, self.__elideMode, self.width())
- qt.QLabel.setText(self, elidedText)
- wasElided = self.__textIsElided
- self.__textIsElided = elidedText != self.__text
- if self.__textIsElided or wasElided != self.__textIsElided:
- self.__updateToolTip()
-
- def __updateToolTip(self):
- if self.__textIsElided and self.__textAsToolTip:
- qt.QLabel.setToolTip(self, self.__text + "<br/>" + self.__toolTip)
- else:
- qt.QLabel.setToolTip(self, self.__toolTip)
-
- # Properties
-
- def setText(self, text):
- self.__text = text
- self.__updateText()
-
- def getText(self):
- return self.__text
-
- text = qt.Property(str, getText, setText)
-
- def setToolTip(self, toolTip):
- self.__toolTip = toolTip
- self.__updateToolTip()
-
- def getToolTip(self):
- return self.__toolTip
-
- toolTip = qt.Property(str, getToolTip, setToolTip)
-
- def setElideMode(self, elideMode):
- """Set the elide mode.
-
- :param qt.Qt.TextElideMode elidMode: Elide mode to use
- """
- self.__elideMode = elideMode
- self.__updateText()
-
- def getElideMode(self):
- """Returns the used elide mode.
-
- :rtype: qt.Qt.TextElideMode
- """
- return self.__elideMode
-
- elideMode = qt.Property(qt.Qt.TextElideMode, getToolTip, setToolTip)
-
- def setTextAsToolTip(self, enabled):
- """Enable displaying text as part of the tooltip if it is elided.
-
- :param bool enabled: Enable the behavior
- """
- if self.__textAsToolTip == enabled:
- return
- self.__textAsToolTip = enabled
- self.__updateToolTip()
-
- def getTextAsToolTip(self):
- """True if an elided text is displayed as part of the tooltip.
-
- :rtype: bool
- """
- return self.__textAsToolTip
-
- textAsToolTip = qt.Property(bool, getTextAsToolTip, setTextAsToolTip)
diff --git a/silx/gui/widgets/FloatEdit.py b/silx/gui/widgets/FloatEdit.py
deleted file mode 100644
index 36a39a7..0000000
--- a/silx/gui/widgets/FloatEdit.py
+++ /dev/null
@@ -1,65 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2004-2018 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-"""Module contains a float editor
-"""
-
-from __future__ import division
-
-__authors__ = ["V.A. Sole", "T. Vincent"]
-__license__ = "MIT"
-__date__ = "02/10/2017"
-
-from .. import qt
-
-
-class FloatEdit(qt.QLineEdit):
- """Field to edit a float value.
-
- :param parent: See :class:`QLineEdit`
- :param float value: The value to set the QLineEdit to.
- """
- def __init__(self, parent=None, value=None):
- qt.QLineEdit.__init__(self, parent)
- validator = qt.QDoubleValidator(self)
- self.setValidator(validator)
- self.setAlignment(qt.Qt.AlignRight)
- if value is not None:
- self.setValue(value)
-
- def value(self):
- """Return the QLineEdit current value as a float."""
- text = self.text()
- value, validated = self.validator().locale().toDouble(text)
- if not validated:
- self.setValue(value)
- return value
-
- def setValue(self, value):
- """Set the current value of the LineEdit
-
- :param float value: The value to set the QLineEdit to.
- """
- text = self.validator().locale().toString(float(value))
- self.setText(text)
diff --git a/silx/gui/widgets/FlowLayout.py b/silx/gui/widgets/FlowLayout.py
deleted file mode 100644
index 3c4c9dd..0000000
--- a/silx/gui/widgets/FlowLayout.py
+++ /dev/null
@@ -1,177 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2018 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-"""This module provides a flow layout for QWidget: :class:`FlowLayout`.
-"""
-
-from __future__ import division
-
-__authors__ = ["T. Vincent"]
-__license__ = "MIT"
-__date__ = "20/07/2018"
-
-
-from .. import qt
-
-
-class FlowLayout(qt.QLayout):
- """Layout widgets on (possibly) multiple lines in the available width.
-
- See Qt :class:`QLayout` for API documentation.
-
- Adapted from C++ `Qt FlowLayout example
- <http://doc.qt.io/qt-5/qtwidgets-layouts-flowlayout-example.html>`_
-
- :param QWidget parent: See :class:`QLayout`
- """
-
- def __init__(self, parent=None):
- super(FlowLayout, self).__init__(parent)
- self._items = []
- self._horizontalSpacing = -1
- self._verticalSpacing = -1
-
- def addItem(self, item):
- self._items.append(item)
-
- def count(self):
- return len(self._items)
-
- def itemAt(self, index):
- if 0 <= index < len(self._items):
- return self._items[index]
- else:
- return None
-
- def takeAt(self, index):
- if 0 <= index < len(self._items):
- return self._items.pop(index)
- else:
- return None
-
- def expandingDirections(self):
- return qt.Qt.Orientations()
-
- def hasHeightForWidth(self):
- return True
-
- def heightForWidth(self, width):
- return self._layout(qt.QRect(0, 0, width, 0), test=True)
-
- def setGeometry(self, rect):
- super(FlowLayout, self).setGeometry(rect)
- self._layout(rect)
-
- def sizeHint(self):
- return self.minimumSize()
-
- def minimumSize(self):
- size = qt.QSize()
- for item in self._items:
- size = size.expandedTo(item.minimumSize())
-
- left, top, right, bottom = self.getContentsMargins()
- size += qt.QSize(left + right, top + bottom)
- return size
-
- def _layout(self, rect, test=False):
- left, top, right, bottom = self.getContentsMargins()
- effectiveRect = rect.adjusted(left, top, -right, -bottom)
- x, y = effectiveRect.x(), effectiveRect.y()
- lineHeight = 0
-
- for item in self._items:
- widget = item.widget()
- spaceX = self.horizontalSpacing()
- if spaceX == -1:
- spaceX = widget.style().layoutSpacing(
- qt.QSizePolicy.PushButton,
- qt.QSizePolicy.PushButton,
- qt.Qt.Horizontal)
- spaceY = self.verticalSpacing()
- if spaceY == -1:
- spaceY = widget.style().layoutSpacing(
- qt.QSizePolicy.PushButton,
- qt.QSizePolicy.PushButton,
- qt.Qt.Vertical)
-
- nextX = x + item.sizeHint().width() + spaceX
- if (nextX - spaceX) > effectiveRect.right() and lineHeight > 0:
- x = effectiveRect.x()
- y += lineHeight + spaceY
- nextX = x + item.sizeHint().width() + spaceX
- lineHeight = 0
-
- if not test:
- item.setGeometry(qt.QRect(qt.QPoint(x, y), item.sizeHint()))
-
- x = nextX
- lineHeight = max(lineHeight, item.sizeHint().height())
-
- return y + lineHeight - rect.y() + bottom
-
- def setHorizontalSpacing(self, spacing):
- """Set the horizontal spacing between widgets laid out side by side
-
- :param int spacing:
- """
- self._horizontalSpacing = spacing
- self.update()
-
- def horizontalSpacing(self):
- """Returns the horizontal spacing between widgets laid out side by side
-
- :rtype: int
- """
- if self._horizontalSpacing >= 0:
- return self._horizontalSpacing
- else:
- return self._smartSpacing(qt.QStyle.PM_LayoutHorizontalSpacing)
-
- def setVerticalSpacing(self, spacing):
- """Set the vertical spacing between lines
-
- :param int spacing:
- """
- self._verticalSpacing = spacing
- self.update()
-
- def verticalSpacing(self):
- """Returns the vertical spacing between lines
-
- :rtype: int
- """
- if self._verticalSpacing >= 0:
- return self._verticalSpacing
- else:
- return self._smartSpacing(qt.QStyle.PM_LayoutVerticalSpacing)
-
- def _smartSpacing(self, pm):
- parent = self.parent()
- if parent is None:
- return -1
- if parent.isWidgetType():
- return parent.style().pixelMetric(pm, None, parent)
- else:
- return parent.spacing()
diff --git a/silx/gui/widgets/FrameBrowser.py b/silx/gui/widgets/FrameBrowser.py
deleted file mode 100644
index 671991f..0000000
--- a/silx/gui/widgets/FrameBrowser.py
+++ /dev/null
@@ -1,324 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2016-2018 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-"""This module defines two main classes:
-
- - :class:`FrameBrowser`: a widget with 4 buttons (first, previous, next,
- last) to browse between frames and a text entry to access a specific frame
- by typing it's number)
- - :class:`HorizontalSliderWithBrowser`: a FrameBrowser with an additional
- slider. This class inherits :class:`qt.QAbstractSlider`.
-
-"""
-from silx.gui import qt
-from silx.gui import icons
-from silx.utils import deprecation
-
-__authors__ = ["V.A. Sole", "P. Knobel"]
-__license__ = "MIT"
-__date__ = "16/01/2017"
-
-
-class FrameBrowser(qt.QWidget):
- """Frame browser widget, with 4 buttons/icons and a line edit to provide
- a way of selecting a frame index in a stack of images.
-
- .. image:: img/FrameBrowser.png
-
- It can be used in more generic case to select an integer within a range.
-
- :param QWidget parent: Parent widget
- :param int n: Number of frames. This will set the range
- of frame indices to 0--n-1.
- If None, the range is initialized to the default QSlider range (0--99).
- """
-
- sigIndexChanged = qt.pyqtSignal(object)
-
- def __init__(self, parent=None, n=None):
- qt.QWidget.__init__(self, parent)
-
- # Use the font size as the icon size to avoid to create bigger buttons
- fontMetric = self.fontMetrics()
- iconSize = qt.QSize(fontMetric.height(), fontMetric.height())
-
- self.mainLayout = qt.QHBoxLayout(self)
- self.mainLayout.setContentsMargins(0, 0, 0, 0)
- self.mainLayout.setSpacing(0)
- self.firstButton = qt.QPushButton(self)
- self.firstButton.setIcon(icons.getQIcon("first"))
- self.firstButton.setIconSize(iconSize)
- self.previousButton = qt.QPushButton(self)
- self.previousButton.setIcon(icons.getQIcon("previous"))
- self.previousButton.setIconSize(iconSize)
- self._lineEdit = qt.QLineEdit(self)
-
- self._label = qt.QLabel(self)
- self.nextButton = qt.QPushButton(self)
- self.nextButton.setIcon(icons.getQIcon("next"))
- self.nextButton.setIconSize(iconSize)
- self.lastButton = qt.QPushButton(self)
- self.lastButton.setIcon(icons.getQIcon("last"))
- self.lastButton.setIconSize(iconSize)
-
- self.mainLayout.addWidget(self.firstButton)
- self.mainLayout.addWidget(self.previousButton)
- self.mainLayout.addWidget(self._lineEdit)
- self.mainLayout.addWidget(self._label)
- self.mainLayout.addWidget(self.nextButton)
- self.mainLayout.addWidget(self.lastButton)
-
- if n is None:
- first = qt.QSlider().minimum()
- last = qt.QSlider().maximum()
- else:
- first, last = 0, n
-
- self._lineEdit.setFixedWidth(self._lineEdit.fontMetrics().boundingRect('%05d' % last).width())
- validator = qt.QIntValidator(first, last, self._lineEdit)
- self._lineEdit.setValidator(validator)
- self._lineEdit.setText("%d" % first)
- self._label.setText("of %d" % last)
-
- self._index = first
- """0-based index"""
-
- self.firstButton.clicked.connect(self._firstClicked)
- self.previousButton.clicked.connect(self._previousClicked)
- self.nextButton.clicked.connect(self._nextClicked)
- self.lastButton.clicked.connect(self._lastClicked)
- self._lineEdit.editingFinished.connect(self._textChangedSlot)
-
- def lineEdit(self):
- """Returns the line edit provided by this widget.
-
- :rtype: qt.QLineEdit
- """
- return self._lineEdit
-
- def limitWidget(self):
- """Returns the widget displaying axes limits.
-
- :rtype: qt.QLabel
- """
- return self._label
-
- def _firstClicked(self):
- """Select first/lowest frame number"""
- self.setValue(self.getRange()[0])
-
- def _previousClicked(self):
- """Select previous frame number"""
- self.setValue(self.getValue() - 1)
-
- def _nextClicked(self):
- """Select next frame number"""
- self.setValue(self.getValue() + 1)
-
- def _lastClicked(self):
- """Select last/highest frame number"""
- self.setValue(self.getRange()[1])
-
- def _textChangedSlot(self):
- """Select frame number typed in the line edit widget"""
- txt = self._lineEdit.text()
- if not len(txt):
- self._lineEdit.setText("%d" % self._index)
- return
- new_value = int(txt)
- if new_value == self._index:
- return
- ddict = {
- "event": "indexChanged",
- "old": self._index,
- "new": new_value,
- "id": id(self)
- }
- self._index = new_value
- self.sigIndexChanged.emit(ddict)
-
- def getRange(self):
- """Returns frame range
-
- :return: (first_index, last_index)
- """
- validator = self.lineEdit().validator()
- return validator.bottom(), validator.top()
-
- def setRange(self, first, last):
- """Set minimum and maximum frame indices.
-
- Initialize the frame index to *first*.
- Update the label text to *" limits: first, last"*
-
- :param int first: Minimum frame index
- :param int last: Maximum frame index"""
- bottom = min(first, last)
- top = max(first, last)
- self._lineEdit.validator().setTop(top)
- self._lineEdit.validator().setBottom(bottom)
- self.setValue(bottom)
-
- # Update limits
- self._label.setText(" limits: %d, %d " % (bottom, top))
-
- @deprecation.deprecated(replacement="FrameBrowser.setRange",
- since_version="0.8")
- def setLimits(self, first, last):
- return self.setRange(first, last)
-
- def setNFrames(self, nframes):
- """Set minimum=0 and maximum=nframes-1 frame numbers.
-
- Initialize the frame index to 0.
- Update the label text to *"1 of nframes"*
-
- :param int nframes: Number of frames"""
- top = nframes - 1
- self.setRange(0, top)
- # display 1-based index in label
- self._label.setText(" of %d " % top)
-
- @deprecation.deprecated(replacement="FrameBrowser.getValue",
- since_version="0.8")
- def getCurrentIndex(self):
- return self._index
-
- def getValue(self):
- """Return current frame index"""
- return self._index
-
- def setValue(self, value):
- """Set 0-based frame index
-
- Value is clipped to current range.
-
- :param int value: Frame number"""
- bottom = self.lineEdit().validator().bottom()
- top = self.lineEdit().validator().top()
- value = int(value)
-
- if value < bottom:
- value = bottom
- elif value > top:
- value = top
-
- self._lineEdit.setText("%d" % value)
- self._textChangedSlot()
-
-
-class HorizontalSliderWithBrowser(qt.QAbstractSlider):
- """
- Slider widget combining a :class:`QSlider` and a :class:`FrameBrowser`.
-
- .. image:: img/HorizontalSliderWithBrowser.png
-
- The data model is an integer within a range.
-
- The default value is the default :class:`QSlider` value (0),
- and the default range is the default QSlider range (0 -- 99)
-
- The signal emitted when the value is changed is the usual QAbstractSlider
- signal :attr:`valueChanged`. The signal carries the value (as an integer).
-
- :param QWidget parent: Optional parent widget
- """
- def __init__(self, parent=None):
- qt.QAbstractSlider.__init__(self, parent)
- self.setOrientation(qt.Qt.Horizontal)
-
- self.mainLayout = qt.QHBoxLayout(self)
- self.mainLayout.setContentsMargins(0, 0, 0, 0)
- self.mainLayout.setSpacing(2)
-
- self._slider = qt.QSlider(self)
- self._slider.setOrientation(qt.Qt.Horizontal)
-
- self._browser = FrameBrowser(self)
-
- self.mainLayout.addWidget(self._slider, 1)
- self.mainLayout.addWidget(self._browser)
-
- self._slider.valueChanged[int].connect(self._sliderSlot)
- self._browser.sigIndexChanged.connect(self._browserSlot)
-
- def lineEdit(self):
- """Returns the line edit provided by this widget.
-
- :rtype: qt.QLineEdit
- """
- return self._browser.lineEdit()
-
- def limitWidget(self):
- """Returns the widget displaying axes limits.
-
- :rtype: qt.QLabel
- """
- return self._browser.limitWidget()
-
- def setMinimum(self, value):
- """Set minimum value
-
- :param int value: Minimum value"""
- self._slider.setMinimum(value)
- maximum = self._slider.maximum()
- self._browser.setRange(value, maximum)
-
- def setMaximum(self, value):
- """Set maximum value
-
- :param int value: Maximum value
- """
- self._slider.setMaximum(value)
- minimum = self._slider.minimum()
- self._browser.setRange(minimum, value)
-
- def setRange(self, first, last):
- """Set minimum/maximum values
-
- :param int first: Minimum value
- :param int last: Maximum value"""
- self._slider.setRange(first, last)
- self._browser.setRange(first, last)
-
- def _sliderSlot(self, value):
- """Emit selected value when slider is activated
- """
- self._browser.setValue(value)
- self.valueChanged.emit(value)
-
- def _browserSlot(self, ddict):
- """Emit selected value when browser state is changed"""
- self._slider.setValue(ddict['new'])
-
- def setValue(self, value):
- """Set value
-
- :param int value: value"""
- self._slider.setValue(value)
- self._browser.setValue(value)
-
- def value(self):
- """Get selected value"""
- return self._slider.value()
diff --git a/silx/gui/widgets/HierarchicalTableView.py b/silx/gui/widgets/HierarchicalTableView.py
deleted file mode 100644
index 3ccf4c7..0000000
--- a/silx/gui/widgets/HierarchicalTableView.py
+++ /dev/null
@@ -1,172 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2016-2017 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-"""
-This module define a hierarchical table view and model.
-
-It allows to define many headers in the middle of a table.
-
-The implementation hide the default header and allows to custom each cells
-to became a header.
-
-Row and column span is a concept of the view in a QTableView.
-This implementation also provide a span property as part of the model of the
-cell. A role is define to custom this information.
-The view is updated everytime the model is reset to take care of the
-changes of this information.
-
-A default item delegate is used to redefine the paint of the cells.
-"""
-__authors__ = ["V. Valls"]
-__license__ = "MIT"
-__date__ = "07/04/2017"
-
-from silx.gui import qt
-
-
-class HierarchicalTableModel(qt.QAbstractTableModel):
- """
- Abstract table model to provide more custom on row and column span and
- headers.
-
- Default headers are ignored and each cells can define IsHeaderRole and
- SpanRole using the `data` function.
- """
-
- SpanRole = qt.Qt.UserRole + 0
- """Role returning a tuple for number of row span then column span.
-
- None and (1, 1) are neutral for the rendering.
- """
-
- IsHeaderRole = qt.Qt.UserRole + 1
- """Role returning True is the identified cell is a header."""
-
- UserRole = qt.Qt.UserRole + 2
- """First index of user defined roles"""
-
- def headerData(self, section, orientation, role=qt.Qt.DisplayRole):
- """Returns the 0-based row or column index, for display in the
- horizontal and vertical headers
-
- In this case the headers are just ignored. Header information is part
- of each cells.
- """
- return None
-
-
-class HierarchicalItemDelegate(qt.QStyledItemDelegate):
- """
- Delegate item to take care of the rendering of the default table cells and
- also the header cells.
- """
-
- def __init__(self, parent=None):
- """
- Constructor
-
- :param qt.QObject parent: Parent of the widget
- """
- qt.QStyledItemDelegate.__init__(self, parent)
-
- def paint(self, painter, option, index):
- """Override the paint function to inject the style of the header.
-
- :param qt.QPainter painter: Painter context used to displayed the cell
- :param qt.QStyleOptionViewItem option: Control how the editor is shown
- :param qt.QIndex index: Index of the data to display
- """
- isHeader = index.data(role=HierarchicalTableModel.IsHeaderRole)
- if isHeader:
- span = index.data(role=HierarchicalTableModel.SpanRole)
- span = 1 if span is None else span[1]
- columnCount = index.model().columnCount()
- if span == columnCount:
- mainTitle = True
- position = qt.QStyleOptionHeader.OnlyOneSection
- else:
- mainTitle = False
- col = index.column()
- if col == 0:
- position = qt.QStyleOptionHeader.Beginning
- elif col < columnCount - 1:
- position = qt.QStyleOptionHeader.Middle
- else:
- position = qt.QStyleOptionHeader.End
- opt = qt.QStyleOptionHeader()
- opt.direction = option.direction
- opt.text = index.data()
- opt.textAlignment = qt.Qt.AlignCenter if mainTitle else qt.Qt.AlignVCenter
- opt.direction = option.direction
- opt.fontMetrics = option.fontMetrics
- opt.palette = option.palette
- opt.rect = option.rect
- opt.state = option.state
- opt.position = position
- margin = -1
- style = qt.QApplication.instance().style()
- opt.rect = opt.rect.adjusted(margin, margin, -margin, -margin)
- style.drawControl(qt.QStyle.CE_HeaderSection, opt, painter, None)
- margin = 3
- opt.rect = opt.rect.adjusted(margin, margin, -margin, -margin)
- style.drawControl(qt.QStyle.CE_HeaderLabel, opt, painter, None)
- else:
- qt.QStyledItemDelegate.paint(self, painter, option, index)
-
-
-class HierarchicalTableView(qt.QTableView):
- """A TableView which allow to display a `HierarchicalTableModel`."""
-
- def __init__(self, parent=None):
- """
- Constructor
-
- :param qt.QWidget parent: Parent of the widget
- """
- super(HierarchicalTableView, self).__init__(parent)
- self.setItemDelegate(HierarchicalItemDelegate(self))
- self.verticalHeader().setVisible(False)
- self.horizontalHeader().setVisible(False)
-
- def setModel(self, model):
- """Override the default function to connect the model to update
- function"""
- if self.model() is not None:
- model.modelReset.disconnect(self.__modelReset)
- super(HierarchicalTableView, self).setModel(model)
- if self.model() is not None:
- model.modelReset.connect(self.__modelReset)
- self.__modelReset()
-
- def __modelReset(self):
- """Update the model to take care of the changes of the span
- information"""
- self.clearSpans()
- model = self.model()
- for row in range(model.rowCount()):
- for column in range(model.columnCount()):
- index = model.index(row, column, qt.QModelIndex())
- span = model.data(index, HierarchicalTableModel.SpanRole)
- if span is not None and span != (1, 1):
- self.setSpan(row, column, span[0], span[1])
diff --git a/silx/gui/widgets/LegendIconWidget.py b/silx/gui/widgets/LegendIconWidget.py
deleted file mode 100755
index 1c95e41..0000000
--- a/silx/gui/widgets/LegendIconWidget.py
+++ /dev/null
@@ -1,514 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2004-2018 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-"""Widget displaying a symbol (marker symbol, line style and color) to identify
-an item displayed by a plot.
-"""
-
-__authors__ = ["V.A. Sole", "T. Rueter", "T. Vincent"]
-__license__ = "MIT"
-__data__ = "11/11/2019"
-
-
-import logging
-
-import numpy
-
-from .. import qt, colors
-
-
-_logger = logging.getLogger(__name__)
-
-
-# Build all symbols
-# Courtesy of the pyqtgraph project
-
-_Symbols = None
-""""Cache supported symbols as Qt paths"""
-
-
-_NoSymbols = (None, 'None', 'none', '', ' ')
-"""List of values resulting in no symbol being displayed for a curve"""
-
-
-_LineStyles = {
- None: qt.Qt.NoPen,
- 'None': qt.Qt.NoPen,
- 'none': qt.Qt.NoPen,
- '': qt.Qt.NoPen,
- ' ': qt.Qt.NoPen,
- '-': qt.Qt.SolidLine,
- '--': qt.Qt.DashLine,
- ':': qt.Qt.DotLine,
- '-.': qt.Qt.DashDotLine
-}
-"""Conversion from matplotlib-like linestyle to Qt"""
-
-_NoLineStyle = (None, 'None', 'none', '', ' ')
-"""List of style values resulting in no line being displayed for a curve"""
-
-
-_colormapImage = {}
-"""Store cached pixmap"""
-# FIXME: Could be better to use a LRU dictionary
-
-_COLORMAP_PIXMAP_SIZE = 32
-"""Size of the cached pixmaps for the colormaps"""
-
-
-def _initSymbols():
- """Init the cached symbol structure if not yet done."""
- global _Symbols
- if _Symbols is not None:
- return
-
- symbols = dict([(name, qt.QPainterPath())
- for name in ['o', 's', 't', 'd', '+', 'x', '.', ',']])
- symbols['o'].addEllipse(qt.QRectF(.1, .1, .8, .8))
- symbols['.'].addEllipse(qt.QRectF(.3, .3, .4, .4))
- symbols[','].addEllipse(qt.QRectF(.4, .4, .2, .2))
- symbols['s'].addRect(qt.QRectF(.1, .1, .8, .8))
-
- coords = {
- 't': [(0.5, 0.), (.1, .8), (.9, .8)],
- 'd': [(0.1, 0.5), (0.5, 0.), (0.9, 0.5), (0.5, 1.)],
- '+': [(0.0, 0.40), (0.40, 0.40), (0.40, 0.), (0.60, 0.),
- (0.60, 0.40), (1., 0.40), (1., 0.60), (0.60, 0.60),
- (0.60, 1.), (0.40, 1.), (0.40, 0.60), (0., 0.60)],
- 'x': [(0.0, 0.40), (0.40, 0.40), (0.40, 0.), (0.60, 0.),
- (0.60, 0.40), (1., 0.40), (1., 0.60), (0.60, 0.60),
- (0.60, 1.), (0.40, 1.), (0.40, 0.60), (0., 0.60)]
- }
- for s, c in coords.items():
- symbols[s].moveTo(*c[0])
- for x, y in c[1:]:
- symbols[s].lineTo(x, y)
- symbols[s].closeSubpath()
- tr = qt.QTransform()
- tr.rotate(45)
- symbols['x'].translate(qt.QPointF(-0.5, -0.5))
- symbols['x'] = tr.map(symbols['x'])
- symbols['x'].translate(qt.QPointF(0.5, 0.5))
-
- _Symbols = symbols
-
-
-class LegendIconWidget(qt.QWidget):
- """Object displaying linestyle and symbol of plots.
-
- :param QWidget parent: See :class:`QWidget`
- """
-
- def __init__(self, parent=None):
- super(LegendIconWidget, self).__init__(parent)
- _initSymbols()
-
- # Visibilities
- self.showLine = True
- self.showSymbol = True
- self.showColormap = True
-
- # Line attributes
- self.lineStyle = qt.Qt.NoPen
- self.lineWidth = 1.
- self.lineColor = qt.Qt.green
-
- self.symbol = ''
- # Symbol attributes
- self.symbolStyle = qt.Qt.SolidPattern
- self.symbolColor = qt.Qt.green
- self.symbolOutlineBrush = qt.QBrush(qt.Qt.white)
- self.symbolColormap = None
- """Name or array of colors"""
-
- self.colormap = None
- """Name or array of colors"""
-
- # Control widget size: sizeHint "is the only acceptable
- # alternative, so the widget can never grow or shrink"
- # (c.f. Qt Doc, enum QSizePolicy::Policy)
- self.setSizePolicy(qt.QSizePolicy.Fixed,
- qt.QSizePolicy.Fixed)
-
- def sizeHint(self):
- return qt.QSize(50, 15)
-
- def setSymbol(self, symbol):
- """Set the symbol"""
- symbol = str(symbol)
- if symbol not in _NoSymbols:
- if symbol not in _Symbols:
- raise ValueError("Unknown symbol: <%s>" % symbol)
- self.symbol = symbol
- self.update()
-
- def setSymbolColor(self, color):
- """
- :param color: determines the symbol color
- :type style: qt.QColor
- """
- self.symbolColor = qt.QColor(color)
- self.update()
-
- # Modify Line
-
- def setLineColor(self, color):
- self.lineColor = qt.QColor(color)
- self.update()
-
- def setLineWidth(self, width):
- self.lineWidth = float(width)
- self.update()
-
- def setLineStyle(self, style):
- """Set the linestyle.
-
- Possible line styles:
-
- - '', ' ', 'None': No line
- - '-': solid
- - '--': dashed
- - ':': dotted
- - '-.': dash and dot
-
- :param str style: The linestyle to use
- """
- if style not in _LineStyles:
- raise ValueError('Unknown style: %s', style)
- self.lineStyle = _LineStyles[style]
- self.update()
-
- def _toLut(self, colormap):
- """Returns an internal LUT object used by this widget to manage
- a colormap LUT.
-
- If the argument is a `Colormap` object, only the current state will be
- displayed. The object itself will not be stored, and further changes
- of this `Colormap` will not update this widget.
-
- :param Union[str,numpy.ndarray,Colormap] colormap: The colormap to
- display
- :rtype: Union[None,str,numpy.ndarray]
- """
- if isinstance(colormap, colors.Colormap):
- # Helper to allow to support Colormap objects
- c = colormap.getName()
- if c is None:
- c = colormap.getNColors()
- colormap = c
-
- return colormap
-
- def setColormap(self, colormap):
- """Set the colormap to display
-
- If the argument is a `Colormap` object, only the current state will be
- displayed. The object itself will not be stored, and further changes
- of this `Colormap` will not update this widget.
-
- :param Union[str,numpy.ndarray,Colormap] colormap: The colormap to
- display
- """
- colormap = self._toLut(colormap)
-
- if colormap is None:
- if self.colormap is None:
- return
- self.colormap = None
- self.update()
- return
-
- if numpy.array_equal(self.colormap, colormap):
- # This also works with strings
- return
-
- self.colormap = colormap
- self.update()
-
- def getColormap(self):
- """Returns the used colormap.
-
- If the argument was set with a `Colormap` object, this function will
- returns the LUT, represented by a string name or by an array or colors.
-
- :returns: Union[None,str,numpy.ndarray,Colormap]
- """
- return self.colormap
-
- def setSymbolColormap(self, colormap):
- """Set the colormap to display a symbol
-
- If the argument is a `Colormap` object, only the current state will be
- displayed. The object itself will not be stored, and further changes
- of this `Colormap` will not update this widget.
-
- :param Union[str,numpy.ndarray,Colormap] colormap: The colormap to
- display
- """
- colormap = self._toLut(colormap)
-
- if colormap is None:
- if self.colormap is None:
- return
- self.symbolColormap = None
- self.update()
- return
-
- if numpy.array_equal(self.symbolColormap, colormap):
- # This also works with strings
- return
-
- self.symbolColormap = colormap
- self.update()
-
- def getSymbolColormap(self):
- """Returns the used symbol colormap.
-
- If the argument was set with a `Colormap` object, this function will
- returns the LUT, represented by a string name or by an array or colors.
-
- :returns: Union[None,str,numpy.ndarray,Colormap]
- """
- return self.colormap
-
- # Paint
-
- def paintEvent(self, event):
- """
- :param event: event
- :type event: QPaintEvent
- """
- painter = qt.QPainter(self)
- self.paint(painter, event.rect(), self.palette())
-
- def paint(self, painter, rect, palette):
- painter.save()
- painter.setRenderHint(qt.QPainter.Antialiasing)
- # Scale painter to the icon height
- # current -> width = 2.5, height = 1.0
- scale = float(self.height())
- ratio = float(self.width()) / scale
- symbolOffset = qt.QPointF(.5 * (ratio - 1.), 0.)
- # Determine and scale offset
- offset = qt.QPointF(float(rect.left()) / scale, float(rect.top()) / scale)
-
- # Override color when disabled
- if self.isEnabled():
- overrideColor = None
- else:
- overrideColor = palette.color(qt.QPalette.Disabled,
- qt.QPalette.WindowText)
-
- # Draw BG rectangle (for debugging)
- # bottomRight = qt.QPointF(
- # float(rect.right())/scale,
- # float(rect.bottom())/scale)
- # painter.fillRect(qt.QRectF(offset, bottomRight),
- # qt.QBrush(qt.Qt.green))
-
- if self.showColormap:
- if self.colormap is not None:
- if self.isEnabled():
- image = self.getColormapImage(self.colormap)
- else:
- image = self.getGrayedColormapImage(self.colormap)
- pixmapRect = qt.QRect(0, 0, _COLORMAP_PIXMAP_SIZE, 1)
- widthMargin = 0
- halfHeight = 4
- widgetRect = self.rect()
- dest = qt.QRect(
- widgetRect.left() + widthMargin,
- widgetRect.center().y() - halfHeight + 1,
- widgetRect.width() - widthMargin * 2,
- halfHeight * 2,
- )
- painter.drawImage(dest, image, pixmapRect)
-
- painter.scale(scale, scale)
-
- llist = []
- if self.showLine:
- linePath = qt.QPainterPath()
- linePath.moveTo(0., 0.5)
- linePath.lineTo(ratio, 0.5)
- # linePath.lineTo(2.5, 0.5)
- lineBrush = qt.QBrush(
- self.lineColor if overrideColor is None else overrideColor)
- linePen = qt.QPen(
- lineBrush,
- (self.lineWidth / self.height()),
- self.lineStyle,
- qt.Qt.FlatCap
- )
- llist.append((linePath, linePen, lineBrush))
-
- isValidSymbol = (len(self.symbol) and
- self.symbol not in _NoSymbols)
- if self.showSymbol and isValidSymbol:
- if self.symbolColormap is None:
- # PITFALL ahead: Let this be a warning to others
- # symbolPath = Symbols[self.symbol]
- # Copy before translate! Dict is a mutable type
- symbolPath = qt.QPainterPath(_Symbols[self.symbol])
- symbolPath.translate(symbolOffset)
- symbolBrush = qt.QBrush(
- self.symbolColor if overrideColor is None else overrideColor,
- self.symbolStyle)
- symbolPen = qt.QPen(
- self.symbolOutlineBrush, # Brush
- 1. / self.height(), # Width
- qt.Qt.SolidLine # Style
- )
- llist.append((symbolPath,
- symbolPen,
- symbolBrush))
- else:
- nbSymbols = int(ratio + 2)
- for i in range(nbSymbols):
- if self.isEnabled():
- image = self.getColormapImage(self.symbolColormap)
- else:
- image = self.getGrayedColormapImage(self.symbolColormap)
- pos = int((_COLORMAP_PIXMAP_SIZE / nbSymbols) * i)
- pos = numpy.clip(pos, 0, _COLORMAP_PIXMAP_SIZE-1)
- color = image.pixelColor(pos, 0)
- delta = qt.QPointF(ratio * ((i - (nbSymbols-1)/2) / nbSymbols), 0)
-
- symbolPath = qt.QPainterPath(_Symbols[self.symbol])
- symbolPath.translate(symbolOffset + delta)
- symbolBrush = qt.QBrush(color, self.symbolStyle)
- symbolPen = qt.QPen(
- self.symbolOutlineBrush, # Brush
- 1. / self.height(), # Width
- qt.Qt.SolidLine # Style
- )
- llist.append((symbolPath,
- symbolPen,
- symbolBrush))
-
- # Draw
- for path, pen, brush in llist:
- path.translate(offset)
- painter.setPen(pen)
- painter.setBrush(brush)
- painter.drawPath(path)
-
- painter.restore()
-
- # Helpers
-
- @staticmethod
- def isEmptySymbol(symbol):
- """Returns True if this symbol description will result in an empty
- symbol."""
- return symbol in _NoSymbols
-
- @staticmethod
- def isEmptyLineStyle(lineStyle):
- """Returns True if this line style description will result in an empty
- line."""
- return lineStyle in _NoLineStyle
-
- @staticmethod
- def _getColormapKey(colormap):
- """
- Returns the key used to store the image in the data storage
- """
- if isinstance(colormap, numpy.ndarray):
- key = tuple(colormap)
- else:
- key = colormap
- return key
-
- @staticmethod
- def getGrayedColormapImage(colormap):
- """Return a grayed version image preview from a LUT name.
-
- This images are cached into a global structure.
-
- :param Union[str,numpy.ndarray] colormap: Description of the LUT
- :rtype: qt.QImage
- """
- key = LegendIconWidget._getColormapKey(colormap)
- grayKey = (key, "gray")
- image = _colormapImage.get(grayKey, None)
- if image is None:
- image = LegendIconWidget.getColormapImage(colormap)
- image = image.convertToFormat(qt.QImage.Format_Grayscale8)
- _colormapImage[grayKey] = image
- return image
-
- @staticmethod
- def getColormapImage(colormap):
- """Return an image preview from a LUT name.
-
- This images are cached into a global structure.
-
- :param Union[str,numpy.ndarray] colormap: Description of the LUT
- :rtype: qt.QImage
- """
- key = LegendIconWidget._getColormapKey(colormap)
- image = _colormapImage.get(key, None)
- if image is None:
- image = LegendIconWidget.createColormapImage(colormap)
- _colormapImage[key] = image
- return image
-
- @staticmethod
- def createColormapImage(colormap):
- """Create and return an icon preview from a LUT name.
-
- This icons are cached into a global structure.
-
- :param Union[str,numpy.ndarray] colormap: Description of the LUT
- :rtype: qt.QImage
- """
- size = _COLORMAP_PIXMAP_SIZE
- if isinstance(colormap, numpy.ndarray):
- lut = colormap
- if len(lut) > size:
- # Down sample
- step = int(len(lut) / size)
- lut = lut[::step]
- elif len(lut) < size:
- # Over sample
- indexes = numpy.arange(size) / float(size) * (len(lut) - 1)
- indexes = indexes.astype("int")
- lut = lut[indexes]
- else:
- colormap = colors.Colormap(colormap)
- lut = colormap.getNColors(size)
-
- if lut is None or len(lut) == 0:
- return qt.QIcon()
-
- pixmap = qt.QPixmap(size, 1)
- painter = qt.QPainter(pixmap)
- for i in range(size):
- rgb = lut[i]
- r, g, b = rgb[0], rgb[1], rgb[2]
- painter.setPen(qt.QColor(r, g, b))
- painter.drawPoint(qt.QPoint(i, 0))
- painter.end()
- return pixmap.toImage()
diff --git a/silx/gui/widgets/MedianFilterDialog.py b/silx/gui/widgets/MedianFilterDialog.py
deleted file mode 100644
index dd4a00d..0000000
--- a/silx/gui/widgets/MedianFilterDialog.py
+++ /dev/null
@@ -1,80 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2017-2018 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-""" MedianFilterDialog
-Classes
--------
-
-Widgets:
-
- - :class:`MedianFilterDialog`
-"""
-
-__authors__ = ["H. Payno"]
-__license__ = "MIT"
-__date__ = "14/02/2017"
-
-
-import logging
-
-from silx.gui import qt
-
-
-_logger = logging.getLogger(__name__)
-
-class MedianFilterDialog(qt.QDialog):
- """QDialog window featuring a :class:`BackgroundWidget`"""
- sigFilterOptChanged = qt.Signal(int, bool)
-
- def __init__(self, parent=None):
- qt.QDialog.__init__(self, parent)
-
- self.setWindowTitle("Median filter options")
- self.mainLayout = qt.QHBoxLayout(self)
- self.setLayout(self.mainLayout)
-
- # filter width GUI
- self.mainLayout.addWidget(qt.QLabel('filter width:', parent = self))
- self._filterWidth = qt.QSpinBox(parent=self)
- self._filterWidth.setMinimum(1)
- self._filterWidth.setValue(1)
- self._filterWidth.setSingleStep(2);
- widthTooltip = """radius width of the pixel including in the filter
- for each pixel"""
- self._filterWidth.setToolTip(widthTooltip)
- self._filterWidth.valueChanged.connect(self._filterOptionChanged)
- self.mainLayout.addWidget(self._filterWidth)
-
- # filter option GUI
- self._filterOption = qt.QCheckBox('conditional', parent=self)
- conditionalTooltip = """if check, implement a conditional filter"""
- self._filterOption.stateChanged.connect(self._filterOptionChanged)
- self.mainLayout.addWidget(self._filterOption)
-
- def _filterOptionChanged(self):
- """Call back used when the filter values are changed"""
- if self._filterWidth.value()%2 == 0:
- _logger.warning('median filter only accept odd values')
- else:
- self.sigFilterOptChanged.emit(self._filterWidth.value(), self._filterOption.isChecked()) \ No newline at end of file
diff --git a/silx/gui/widgets/MultiModeAction.py b/silx/gui/widgets/MultiModeAction.py
deleted file mode 100644
index 502275d..0000000
--- a/silx/gui/widgets/MultiModeAction.py
+++ /dev/null
@@ -1,83 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2004-2018 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-"""Action to hold many mode actions, usually for a tool bar.
-"""
-
-__authors__ = ["V. Valls"]
-__license__ = "MIT"
-__data__ = "22/04/2020"
-
-
-from silx.gui import qt
-
-
-class MultiModeAction(qt.QWidgetAction):
- """This action provides a default checkable action from a list of checkable
- actions.
-
- The default action can be selected from a drop down list. The last one used
- became the default one.
-
- The default action is directly usable without using the drop down list.
- """
-
- def __init__(self, parent=None):
- assert isinstance(parent, qt.QWidget)
- qt.QWidgetAction.__init__(self, parent)
- button = qt.QToolButton(parent)
- button.setPopupMode(qt.QToolButton.MenuButtonPopup)
- self.setDefaultWidget(button)
- self.__button = button
-
- def getMenu(self):
- """Returns the menu.
-
- :rtype: qt.QMenu
- """
- button = self.__button
- menu = button.menu()
- if menu is None:
- menu = qt.QMenu(button)
- button.setMenu(menu)
- return menu
-
- def addAction(self, action):
- """Add a new action to the list.
-
- :param qt.QAction action: New action
- """
- menu = self.getMenu()
- button = self.__button
- menu.addAction(action)
- if button.defaultAction() is None:
- button.setDefaultAction(action)
- if action.isCheckable():
- action.toggled.connect(self._toggled)
-
- def _toggled(self, checked):
- if checked:
- action = self.sender()
- button = self.__button
- button.setDefaultAction(action)
diff --git a/silx/gui/widgets/PeriodicTable.py b/silx/gui/widgets/PeriodicTable.py
deleted file mode 100644
index 0233e8c..0000000
--- a/silx/gui/widgets/PeriodicTable.py
+++ /dev/null
@@ -1,831 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2004-2018 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-"""Periodic table widgets
-
-Classes
--------
-
-Widgets:
-
- - :class:`PeriodicTable`
- - :class:`PeriodicList`
- - :class:`PeriodicCombo`
-
-Data model:
-
- - :class:`PeriodicTableItem`
- - :class:`ColoredPeriodicTableItem`
-
-
-Example of usage
-----------------
-
-This example uses the widgets with the standard builtin elements list.
-
-.. code-block:: python
-
- from silx.gui import qt
- from silx.gui.widgets.PeriodicTable import PeriodicTable, \
- PeriodicCombo, PeriodicList
-
- a = qt.QApplication([])
-
- w = qt.QTabWidget()
-
- ptable = PeriodicTable(w, selectable=True)
- pcombo = PeriodicCombo(w)
- plist = PeriodicList(w)
-
- w.addTab(ptable, "PeriodicTable")
- w.addTab(plist, "PeriodicList")
- w.addTab(pcombo, "PeriodicCombo")
-
- ptable.setSelection(['H', 'Fe', 'Si'])
- plist.setSelectedElements(['H', 'Be', 'F'])
- pcombo.setSelection("Li")
-
- def change_list(items):
- print("New list selection:", [item.symbol for item in items])
-
- def change_combo(item):
- print("New combo selection:", item.symbol)
-
- def click_table(item):
- print("New table click:", item.symbol)
-
- def change_table(items):
- print("New table selection:", [item.symbol for item in items])
-
- ptable.sigElementClicked.connect(click_table)
- ptable.sigSelectionChanged.connect(change_table)
- plist.sigSelectionChanged.connect(change_list)
- pcombo.sigSelectionChanged.connect(change_combo)
-
- w.show()
- a.exec_()
-
-
-The second example explains how to define custom elements.
-
-.. code-block:: python
-
- from silx.gui import qt
- from silx.gui.widgets.PeriodicTable import PeriodicTable, \
- PeriodicCombo, PeriodicList
- from silx.gui.widgets.PeriodicTable import PeriodicTableItem
-
- # subclass PeriodicTableItem
- class MyPeriodicTableItem(PeriodicTableItem):
- "New item with added mass number and number of protons"
- def __init__(self, symbol, Z, A, col, row, name, mass,
- subcategory=""):
- PeriodicTableItem.__init__(
- self, symbol, Z, col, row, name, mass,
- subcategory)
-
- self.A = A
- "Mass number (neutrons + protons)"
-
- self.num_neutrons = A - Z
- "Number of neutrons"
-
- # build your list of elements
- my_elements = [MyPeriodicTableItem("H", 1, 1, 1, 1, "hydrogen",
- 1.00800, "diatomic nonmetal"),
- MyPeriodicTableItem("He", 2, 4, 18, 1, "helium",
- 4.0030, "noble gas"),
- # etc ...
- ]
-
- app = qt.QApplication([])
-
- ptable = PeriodicTable(elements=my_elements, selectable=True)
- ptable.show()
-
- def click_table(item):
- "Callback function printing the mass number of clicked element"
- print("New table click, mass number:", item.A)
-
- ptable.sigElementClicked.connect(click_table)
- app.exec_()
-
-"""
-
-__authors__ = ["E. Papillon", "V.A. Sole", "P. Knobel"]
-__license__ = "MIT"
-__date__ = "26/01/2017"
-
-from collections import OrderedDict
-import logging
-from silx.gui import qt
-
-_logger = logging.getLogger(__name__)
-
-# Symbol Atomic Number col row name mass subcategory
-_elements = [("H", 1, 1, 1, "hydrogen", 1.00800, "diatomic nonmetal"),
- ("He", 2, 18, 1, "helium", 4.0030, "noble gas"),
- ("Li", 3, 1, 2, "lithium", 6.94000, "alkali metal"),
- ("Be", 4, 2, 2, "beryllium", 9.01200, "alkaline earth metal"),
- ("B", 5, 13, 2, "boron", 10.8110, "metalloid"),
- ("C", 6, 14, 2, "carbon", 12.0100, "polyatomic nonmetal"),
- ("N", 7, 15, 2, "nitrogen", 14.0080, "diatomic nonmetal"),
- ("O", 8, 16, 2, "oxygen", 16.0000, "diatomic nonmetal"),
- ("F", 9, 17, 2, "fluorine", 19.0000, "diatomic nonmetal"),
- ("Ne", 10, 18, 2, "neon", 20.1830, "noble gas"),
- ("Na", 11, 1, 3, "sodium", 22.9970, "alkali metal"),
- ("Mg", 12, 2, 3, "magnesium", 24.3200, "alkaline earth metal"),
- ("Al", 13, 13, 3, "aluminium", 26.9700, "post transition metal"),
- ("Si", 14, 14, 3, "silicon", 28.0860, "metalloid"),
- ("P", 15, 15, 3, "phosphorus", 30.9750, "polyatomic nonmetal"),
- ("S", 16, 16, 3, "sulphur", 32.0660, "polyatomic nonmetal"),
- ("Cl", 17, 17, 3, "chlorine", 35.4570, "diatomic nonmetal"),
- ("Ar", 18, 18, 3, "argon", 39.9440, "noble gas"),
- ("K", 19, 1, 4, "potassium", 39.1020, "alkali metal"),
- ("Ca", 20, 2, 4, "calcium", 40.0800, "alkaline earth metal"),
- ("Sc", 21, 3, 4, "scandium", 44.9600, "transition metal"),
- ("Ti", 22, 4, 4, "titanium", 47.9000, "transition metal"),
- ("V", 23, 5, 4, "vanadium", 50.9420, "transition metal"),
- ("Cr", 24, 6, 4, "chromium", 51.9960, "transition metal"),
- ("Mn", 25, 7, 4, "manganese", 54.9400, "transition metal"),
- ("Fe", 26, 8, 4, "iron", 55.8500, "transition metal"),
- ("Co", 27, 9, 4, "cobalt", 58.9330, "transition metal"),
- ("Ni", 28, 10, 4, "nickel", 58.6900, "transition metal"),
- ("Cu", 29, 11, 4, "copper", 63.5400, "transition metal"),
- ("Zn", 30, 12, 4, "zinc", 65.3800, "transition metal"),
- ("Ga", 31, 13, 4, "gallium", 69.7200, "post transition metal"),
- ("Ge", 32, 14, 4, "germanium", 72.5900, "metalloid"),
- ("As", 33, 15, 4, "arsenic", 74.9200, "metalloid"),
- ("Se", 34, 16, 4, "selenium", 78.9600, "polyatomic nonmetal"),
- ("Br", 35, 17, 4, "bromine", 79.9200, "diatomic nonmetal"),
- ("Kr", 36, 18, 4, "krypton", 83.8000, "noble gas"),
- ("Rb", 37, 1, 5, "rubidium", 85.4800, "alkali metal"),
- ("Sr", 38, 2, 5, "strontium", 87.6200, "alkaline earth metal"),
- ("Y", 39, 3, 5, "yttrium", 88.9050, "transition metal"),
- ("Zr", 40, 4, 5, "zirconium", 91.2200, "transition metal"),
- ("Nb", 41, 5, 5, "niobium", 92.9060, "transition metal"),
- ("Mo", 42, 6, 5, "molybdenum", 95.9500, "transition metal"),
- ("Tc", 43, 7, 5, "technetium", 99.0000, "transition metal"),
- ("Ru", 44, 8, 5, "ruthenium", 101.0700, "transition metal"),
- ("Rh", 45, 9, 5, "rhodium", 102.9100, "transition metal"),
- ("Pd", 46, 10, 5, "palladium", 106.400, "transition metal"),
- ("Ag", 47, 11, 5, "silver", 107.880, "transition metal"),
- ("Cd", 48, 12, 5, "cadmium", 112.410, "transition metal"),
- ("In", 49, 13, 5, "indium", 114.820, "post transition metal"),
- ("Sn", 50, 14, 5, "tin", 118.690, "post transition metal"),
- ("Sb", 51, 15, 5, "antimony", 121.760, "metalloid"),
- ("Te", 52, 16, 5, "tellurium", 127.600, "metalloid"),
- ("I", 53, 17, 5, "iodine", 126.910, "diatomic nonmetal"),
- ("Xe", 54, 18, 5, "xenon", 131.300, "noble gas"),
- ("Cs", 55, 1, 6, "caesium", 132.910, "alkali metal"),
- ("Ba", 56, 2, 6, "barium", 137.360, "alkaline earth metal"),
- ("La", 57, 3, 6, "lanthanum", 138.920, "lanthanide"),
- ("Ce", 58, 4, 9, "cerium", 140.130, "lanthanide"),
- ("Pr", 59, 5, 9, "praseodymium", 140.920, "lanthanide"),
- ("Nd", 60, 6, 9, "neodymium", 144.270, "lanthanide"),
- ("Pm", 61, 7, 9, "promethium", 147.000, "lanthanide"),
- ("Sm", 62, 8, 9, "samarium", 150.350, "lanthanide"),
- ("Eu", 63, 9, 9, "europium", 152.000, "lanthanide"),
- ("Gd", 64, 10, 9, "gadolinium", 157.260, "lanthanide"),
- ("Tb", 65, 11, 9, "terbium", 158.930, "lanthanide"),
- ("Dy", 66, 12, 9, "dysprosium", 162.510, "lanthanide"),
- ("Ho", 67, 13, 9, "holmium", 164.940, "lanthanide"),
- ("Er", 68, 14, 9, "erbium", 167.270, "lanthanide"),
- ("Tm", 69, 15, 9, "thulium", 168.940, "lanthanide"),
- ("Yb", 70, 16, 9, "ytterbium", 173.040, "lanthanide"),
- ("Lu", 71, 17, 9, "lutetium", 174.990, "lanthanide"),
- ("Hf", 72, 4, 6, "hafnium", 178.500, "transition metal"),
- ("Ta", 73, 5, 6, "tantalum", 180.950, "transition metal"),
- ("W", 74, 6, 6, "tungsten", 183.920, "transition metal"),
- ("Re", 75, 7, 6, "rhenium", 186.200, "transition metal"),
- ("Os", 76, 8, 6, "osmium", 190.200, "transition metal"),
- ("Ir", 77, 9, 6, "iridium", 192.200, "transition metal"),
- ("Pt", 78, 10, 6, "platinum", 195.090, "transition metal"),
- ("Au", 79, 11, 6, "gold", 197.200, "transition metal"),
- ("Hg", 80, 12, 6, "mercury", 200.610, "transition metal"),
- ("Tl", 81, 13, 6, "thallium", 204.390, "post transition metal"),
- ("Pb", 82, 14, 6, "lead", 207.210, "post transition metal"),
- ("Bi", 83, 15, 6, "bismuth", 209.000, "post transition metal"),
- ("Po", 84, 16, 6, "polonium", 209.000, "post transition metal"),
- ("At", 85, 17, 6, "astatine", 210.000, "metalloid"),
- ("Rn", 86, 18, 6, "radon", 222.000, "noble gas"),
- ("Fr", 87, 1, 7, "francium", 223.000, "alkali metal"),
- ("Ra", 88, 2, 7, "radium", 226.000, "alkaline earth metal"),
- ("Ac", 89, 3, 7, "actinium", 227.000, "actinide"),
- ("Th", 90, 4, 10, "thorium", 232.000, "actinide"),
- ("Pa", 91, 5, 10, "proactinium", 231.03588, "actinide"),
- ("U", 92, 6, 10, "uranium", 238.070, "actinide"),
- ("Np", 93, 7, 10, "neptunium", 237.000, "actinide"),
- ("Pu", 94, 8, 10, "plutonium", 239.100, "actinide"),
- ("Am", 95, 9, 10, "americium", 243, "actinide"),
- ("Cm", 96, 10, 10, "curium", 247, "actinide"),
- ("Bk", 97, 11, 10, "berkelium", 247, "actinide"),
- ("Cf", 98, 12, 10, "californium", 251, "actinide"),
- ("Es", 99, 13, 10, "einsteinium", 252, "actinide"),
- ("Fm", 100, 14, 10, "fermium", 257, "actinide"),
- ("Md", 101, 15, 10, "mendelevium", 258, "actinide"),
- ("No", 102, 16, 10, "nobelium", 259, "actinide"),
- ("Lr", 103, 17, 10, "lawrencium", 262, "actinide"),
- ("Rf", 104, 4, 7, "rutherfordium", 261, "transition metal"),
- ("Db", 105, 5, 7, "dubnium", 262, "transition metal"),
- ("Sg", 106, 6, 7, "seaborgium", 266, "transition metal"),
- ("Bh", 107, 7, 7, "bohrium", 264, "transition metal"),
- ("Hs", 108, 8, 7, "hassium", 269, "transition metal"),
- ("Mt", 109, 9, 7, "meitnerium", 268)]
-
-
-class PeriodicTableItem(object):
- """Periodic table item, used as generic item in :class:`PeriodicTable`,
- :class:`PeriodicCombo` and :class:`PeriodicList`.
-
- This implementation stores the minimal amount of information needed by the
- widgets:
-
- - atomic symbol
- - atomic number
- - element name
- - atomic mass
- - column of element in periodic table
- - row of element in periodic table
-
- You can subclass this class to add additional information.
-
- :param str symbol: Atomic symbol (e.g. H, He, Li...)
- :param int Z: Proton number
- :param int col: 1-based column index of element in periodic table
- :param int row: 1-based row index of element in periodic table
- :param str name: PeriodicTableItem name ("hydrogen", ...)
- :param float mass: Atomic mass (gram per mol)
- :param str subcategory: Subcategory, based on physical properties
- (e.g. "alkali metal", "noble gas"...)
- """
- def __init__(self, symbol, Z, col, row, name, mass,
- subcategory=""):
- self.symbol = symbol
- """Atomic symbol (e.g. H, He, Li...)"""
- self.Z = Z
- """Atomic number (Proton number)"""
- self.col = col
- """1-based column index of element in periodic table"""
- self.row = row
- """1-based row index of element in periodic table"""
- self.name = name
- """PeriodicTableItem name ("hydrogen", ...)"""
- self.mass = mass
- """Atomic mass (gram per mol)"""
- self.subcategory = subcategory
- """Subcategory, based on physical properties
- (e.g. "alkali metal", "noble gas"...)"""
-
- # pymca compatibility (elements used to be stored as a list of lists)
- def __getitem__(self, idx):
- if idx == 6:
- _logger.warning("density not implemented in silx, returning 0.")
-
- ret = [self.symbol, self.Z,
- self.col, self.row,
- self.name, self.mass,
- 0.]
- return ret[idx]
-
- def __len__(self):
- return 6
-
-
-class ColoredPeriodicTableItem(PeriodicTableItem):
- """:class:`PeriodicTableItem` with an added :attr:`bgcolor`.
- The background color can be passed as a parameter to the constructor.
- If it is not specified, it will be defined based on
- :attr:`subcategory`.
-
- :param str bgcolor: Custom background color for element in
- periodic table, as a RGB string *#RRGGBB*"""
- COLORS = {
- "diatomic nonmetal": "#7FFF00", # chartreuse
- "noble gas": "#00FFFF", # cyan
- "alkali metal": "#FFE4B5", # Moccasin
- "alkaline earth metal": "#FFA500", # orange
- "polyatomic nonmetal": "#7FFFD4", # aquamarine
- "transition metal": "#FFA07A", # light salmon
- "metalloid": "#8FBC8F", # Dark Sea Green
- "post transition metal": "#D3D3D3", # light gray
- "lanthanide": "#FFB6C1", # light pink
- "actinide": "#F08080", # Light Coral
- "": "#FFFFFF" # white
- }
- """Dictionary defining RGB colors for each subcategory."""
-
- def __init__(self, symbol, Z, col, row, name, mass,
- subcategory="", bgcolor=None):
- PeriodicTableItem.__init__(self, symbol, Z, col, row, name, mass,
- subcategory)
-
- self.bgcolor = self.COLORS.get(subcategory, "#FFFFFF")
- """Background color of element in the periodic table,
- based on its subcategory. This should be a string of a hexadecimal
- RGB code, with the format *#RRGGBB*.
- If the subcategory is unknown, use white (*#FFFFFF*)
- """
-
- # possible custom color
- if bgcolor is not None:
- self.bgcolor = bgcolor
-
-
-_defaultTableItems = [ColoredPeriodicTableItem(*info) for info in _elements]
-
-
-class _ElementButton(qt.QPushButton):
- """Atomic element button, used as a cell in the periodic table
- """
- sigElementEnter = qt.pyqtSignal(object)
- """Signal emitted as the cursor enters the widget"""
- sigElementLeave = qt.pyqtSignal(object)
- """Signal emitted as the cursor leaves the widget"""
- sigElementClicked = qt.pyqtSignal(object)
- """Signal emitted when the widget is clicked"""
-
- def __init__(self, item, parent=None):
- """
-
- :param parent: Parent widget
- :param PeriodicTableItem item: :class:`PeriodicTableItem` object
- """
- qt.QPushButton.__init__(self, parent)
-
- self.item = item
- """:class:`PeriodicTableItem` object represented by this button"""
-
- self.setText(item.symbol)
- self.setFlat(1)
- self.setCheckable(0)
-
- self.setSizePolicy(qt.QSizePolicy(qt.QSizePolicy.Expanding,
- qt.QSizePolicy.Expanding))
-
- self.selected = False
- self.current = False
-
- # selection colors
- self.selected_color = qt.QColor(qt.Qt.yellow)
- self.current_color = qt.QColor(qt.Qt.gray)
- self.selected_current_color = qt.QColor(qt.Qt.darkYellow)
-
- # element colors
-
- if hasattr(item, "bgcolor"):
- self.bgcolor = qt.QColor(item.bgcolor)
- else:
- self.bgcolor = qt.QColor("#FFFFFF")
-
- self.brush = qt.QBrush()
- self.__setBrush()
-
- self.clicked.connect(self.clickedSlot)
-
- def sizeHint(self):
- return qt.QSize(40, 40)
-
- def setCurrent(self, b):
- """Set this element button as current.
- Multiple buttons can be selected.
-
- :param b: boolean
- """
- self.current = b
- self.__setBrush()
-
- def isCurrent(self):
- """
- :return: True if element button is current
- """
- return self.current
-
- def isSelected(self):
- """
- :return: True if element button is selected
- """
- return self.selected
-
- def setSelected(self, b):
- """Set this element button as selected.
- Only a single button can be selected.
-
- :param b: boolean
- """
- self.selected = b
- self.__setBrush()
-
- def __setBrush(self):
- """Selected cells are yellow when not current.
- The current cell is dark yellow when selected or grey when not
- selected.
- Other cells have no bg color by default, unless specified at
- instantiation (:attr:`bgcolor`)"""
- palette = self.palette()
- # if self.current and self.selected:
- # self.brush = qt.QBrush(self.selected_current_color)
- # el
- if self.selected:
- self.brush = qt.QBrush(self.selected_color)
- # elif self.current:
- # self.brush = qt.QBrush(self.current_color)
- elif self.bgcolor is not None:
- self.brush = qt.QBrush(self.bgcolor)
- else:
- self.brush = qt.QBrush()
- palette.setBrush(self.backgroundRole(),
- self.brush)
- self.setPalette(palette)
- self.update()
-
- def paintEvent(self, pEvent):
- # get button geometry
- widgGeom = self.rect()
- paintGeom = qt.QRect(widgGeom.left() + 1,
- widgGeom.top() + 1,
- widgGeom.width() - 2,
- widgGeom.height() - 2)
-
- # paint background color
- painter = qt.QPainter(self)
- if self.brush is not None:
- painter.fillRect(paintGeom, self.brush)
- # paint frame
- pen = qt.QPen(qt.Qt.black)
- pen.setWidth(1 if not self.isCurrent() else 5)
- painter.setPen(pen)
- painter.drawRect(paintGeom)
- painter.end()
- qt.QPushButton.paintEvent(self, pEvent)
-
- def enterEvent(self, e):
- """Emit a :attr:`sigElementEnter` signal and send a
- :class:`PeriodicTableItem` object"""
- self.sigElementEnter.emit(self.item)
-
- def leaveEvent(self, e):
- """Emit a :attr:`sigElementLeave` signal and send a
- :class:`PeriodicTableItem` object"""
- self.sigElementLeave.emit(self.item)
-
- def clickedSlot(self):
- """Emit a :attr:`sigElementClicked` signal and send a
- :class:`PeriodicTableItem` object"""
- self.sigElementClicked.emit(self.item)
-
-
-class PeriodicTable(qt.QWidget):
- """Periodic Table widget
-
- .. image:: img/PeriodicTable.png
-
- The following example shows how to connect clicking to selection::
-
- from silx.gui import qt
- from silx.gui.widgets.PeriodicTable import PeriodicTable
- app = qt.QApplication([])
- pt = PeriodicTable()
- pt.sigElementClicked.connect(pt.elementToggle)
- pt.show()
- app.exec_()
-
- To print all selected elements each time a new element is selected::
-
- def my_slot(item):
- pt.elementToggle(item)
- selected_elements = pt.getSelection()
- for e in selected_elements:
- print(e.symbol)
-
- pt.sigElementClicked.connect(my_slot)
-
- """
- sigElementClicked = qt.pyqtSignal(object)
- """When any element is clicked in the table, the widget emits
- this signal and sends a :class:`PeriodicTableItem` object.
- """
-
- sigSelectionChanged = qt.pyqtSignal(object)
- """When any element is selected/unselected in the table, the widget emits
- this signal and sends a list of :class:`PeriodicTableItem` objects.
-
- .. note::
-
- To enable selection of elements, you must set *selectable=True*
- when you instantiate the widget. Alternatively, you can also connect
- :attr:`sigElementClicked` to :meth:`elementToggle` manually::
-
- pt = PeriodicTable()
- pt.sigElementClicked.connect(pt.elementToggle)
-
-
- :param parent: parent QWidget
- :param str name: Widget window title
- :param elements: List of items (:class:`PeriodicTableItem` objects) to
- be represented in the table. By default, take elements from
- a predefined list with minimal information (symbol, atomic number,
- name, mass).
- :param bool selectable: If *True*, multiple elements can be
- selected by clicking with the mouse. If *False* (default),
- selection is only possible with method :meth:`setSelection`.
- """
-
- def __init__(self, parent=None, name="PeriodicTable", elements=None,
- selectable=False):
- self.selectable = selectable
- qt.QWidget.__init__(self, parent)
- self.setWindowTitle(name)
- self.gridLayout = qt.QGridLayout(self)
- self.gridLayout.setContentsMargins(0, 0, 0, 0)
- self.gridLayout.addItem(qt.QSpacerItem(0, 5), 7, 0)
-
- for idx in range(10):
- self.gridLayout.setRowStretch(idx, 3)
- # row 8 (above lanthanoids is empty)
- self.gridLayout.setRowStretch(7, 2)
-
- # Element information displayed when cursor enters a cell
- self.eltLabel = qt.QLabel(self)
- f = self.eltLabel.font()
- f.setBold(1)
- self.eltLabel.setFont(f)
- self.eltLabel.setAlignment(qt.Qt.AlignHCenter)
- self.gridLayout.addWidget(self.eltLabel, 1, 1, 3, 10)
-
- self._eltCurrent = None
- """Current :class:`_ElementButton` (last clicked)"""
-
- self._eltButtons = OrderedDict()
- """Dictionary of all :class:`_ElementButton`. Keys are the symbols
- ("H", "He", "Li"...)"""
-
- if elements is None:
- elements = _defaultTableItems
- # fill cells with elements
- for elmt in elements:
- self.__addElement(elmt)
-
- def __addElement(self, elmt):
- """Add one :class:`_ElementButton` widget into the grid,
- connect its signals to interact with the cursor"""
- b = _ElementButton(elmt, self)
- b.setAutoDefault(False)
-
- self._eltButtons[elmt.symbol] = b
- self.gridLayout.addWidget(b, elmt.row, elmt.col)
-
- b.sigElementEnter.connect(self.elementEnter)
- b.sigElementLeave.connect(self._elementLeave)
- b.sigElementClicked.connect(self._elementClicked)
-
- def elementEnter(self, item):
- """Update label with element info (e.g. "Nb(41) - niobium")
- when mouse cursor hovers an element.
-
- :param PeriodicTableItem item: Element entered by cursor
- """
- self.eltLabel.setText("%s(%d) - %s" % (item.symbol, item.Z, item.name))
-
- def _elementLeave(self, item):
- """Clear label when the cursor leaves the cell
-
- :param PeriodicTableItem item: Element left
- """
- self.eltLabel.setText("")
-
- def _elementClicked(self, item):
- """Emit :attr:`sigElementClicked`,
- toggle selected state of element
-
- :param PeriodicTableItem item: Element clicked
- """
- if self._eltCurrent is not None:
- self._eltCurrent.setCurrent(False)
- self._eltButtons[item.symbol].setCurrent(True)
- self._eltCurrent = self._eltButtons[item.symbol]
- if self.selectable:
- self.elementToggle(item)
- self.sigElementClicked.emit(item)
-
- def getSelection(self):
- """Return a list of selected elements, as a list of :class:`PeriodicTableItem`
- objects.
-
- :return: Selected items
- :rtype: List[PeriodicTableItem]
- """
- return [b.item for b in self._eltButtons.values() if b.isSelected()]
-
- def setSelection(self, symbols):
- """Set selected elements.
-
- This causes the sigSelectionChanged signal
- to be emitted, even if the selection didn't actually change.
-
- :param List[str] symbols: List of symbols of elements to be selected
- (e.g. *["Fe", "Hg", "Li"]*)
- """
- # accept list of PeriodicTableItems as input, because getSelection
- # returns these objects and it makes sense to have getter and setter
- # use same type of data
- if isinstance(symbols[0], PeriodicTableItem):
- symbols = [elmt.symbol for elmt in symbols]
-
- for (e, b) in self._eltButtons.items():
- b.setSelected(e in symbols)
- self.sigSelectionChanged.emit(self.getSelection())
-
- def setElementSelected(self, symbol, state):
- """Modify *selected* status of a single element (select or unselect)
-
- :param str symbol: PeriodicTableItem symbol to be selected
- :param bool state: *True* to select, *False* to unselect
- """
- self._eltButtons[symbol].setSelected(state)
- self.sigSelectionChanged.emit(self.getSelection())
-
- def isElementSelected(self, symbol):
- """Return *True* if element is selected, else *False*
-
- :param str symbol: PeriodicTableItem symbol
- :return: *True* if element is selected, else *False*
- """
- return self._eltButtons[symbol].isSelected()
-
- def elementToggle(self, item):
- """Toggle selected/unselected state for element
-
- :param item: PeriodicTableItem object
- """
- b = self._eltButtons[item.symbol]
- b.setSelected(not b.isSelected())
- self.sigSelectionChanged.emit(self.getSelection())
-
-
-class PeriodicCombo(qt.QComboBox):
- """
- Combo list with all atomic elements of the periodic table
-
- .. image:: img/PeriodicCombo.png
-
- :param bool detailed: True (default) display element symbol, Z and name.
- False display only element symbol and Z.
- :param elements: List of items (:class:`PeriodicTableItem` objects) to
- be represented in the table. By default, take elements from
- a predefined list with minimal information (symbol, atomic number,
- name, mass).
- """
- sigSelectionChanged = qt.pyqtSignal(object)
- """Signal emitted when the selection changes. Send
- :class:`PeriodicTableItem` object representing selected
- element
- """
-
- def __init__(self, parent=None, detailed=True, elements=None):
- qt.QComboBox.__init__(self, parent)
-
- # add all elements from global list
- if elements is None:
- elements = _defaultTableItems
- for i, elmt in enumerate(elements):
- if detailed:
- txt = "%2s (%d) - %s" % (elmt.symbol, elmt.Z, elmt.name)
- else:
- txt = "%2s (%d)" % (elmt.symbol, elmt.Z)
- self.insertItem(i, txt)
-
- self.currentIndexChanged[int].connect(self.__selectionChanged)
-
- def __selectionChanged(self, idx):
- """Emit :attr:`sigSelectionChanged`"""
- self.sigSelectionChanged.emit(_defaultTableItems[idx])
-
- def getSelection(self):
- """Get selected element
-
- :return: Selected element
- :rtype: PeriodicTableItem
- """
- return _defaultTableItems[self.currentIndex()]
-
- def setSelection(self, symbol):
- """Set selected item in combobox by giving the atomic symbol
-
- :param symbol: Symbol of element to be selected
- """
- # accept PeriodicTableItem for getter/setter consistency
- if isinstance(symbol, PeriodicTableItem):
- symbol = symbol.symbol
- symblist = [elmt.symbol for elmt in _defaultTableItems]
- self.setCurrentIndex(symblist.index(symbol))
-
-
-class PeriodicList(qt.QTreeWidget):
- """List of atomic elements in a :class:`QTreeView`
-
- .. image:: img/PeriodicList.png
-
- :param QWidget parent: Parent widget
- :param bool detailed: True (default) display element symbol, Z and name.
- False display only element symbol and Z.
- :param single: *True* for single element selection with mouse click,
- *False* for multiple element selection mode.
- """
- sigSelectionChanged = qt.pyqtSignal(object)
- """When any element is selected/unselected in the widget, it emits
- this signal and sends a list of currently selected
- :class:`PeriodicTableItem` objects.
- """
-
- def __init__(self, parent=None, detailed=True, single=False, elements=None):
- qt.QTreeWidget.__init__(self, parent)
-
- self.detailed = detailed
-
- headers = ["Z", "Symbol"]
- if detailed:
- headers.append("Name")
- self.setColumnCount(3)
- else:
- self.setColumnCount(2)
- self.setHeaderLabels(headers)
- self.header().setStretchLastSection(False)
-
- self.setRootIsDecorated(0)
- self.itemClicked.connect(self.__selectionChanged)
- self.setSelectionMode(qt.QAbstractItemView.SingleSelection if single
- else qt.QAbstractItemView.ExtendedSelection)
- self.__fill_widget(elements)
- self.resizeColumnToContents(0)
- self.resizeColumnToContents(1)
- if detailed:
- self.resizeColumnToContents(2)
-
- def __fill_widget(self, elements):
- """Fill tree widget with elements """
- if elements is None:
- elements = _defaultTableItems
-
- self.tree_items = []
-
- previous_item = None
- for elmt in elements:
- if previous_item is None:
- item = qt.QTreeWidgetItem(self)
- else:
- item = qt.QTreeWidgetItem(self, previous_item)
- item.setText(0, str(elmt.Z))
- item.setText(1, elmt.symbol)
- if self.detailed:
- item.setText(2, elmt.name)
- self.tree_items.append(item)
- previous_item = item
-
- def __selectionChanged(self, treeItem, column):
- """Emit a :attr:`sigSelectionChanged` and send a list of
- :class:`PeriodicTableItem` objects."""
- self.sigSelectionChanged.emit(self.getSelection())
-
- def getSelection(self):
- """Get a list of selected elements, as a list of :class:`PeriodicTableItem`
- objects.
-
- :return: Selected elements
- :rtype: List[PeriodicTableItem]"""
- return [_defaultTableItems[idx] for idx in range(len(self.tree_items))
- if self.tree_items[idx].isSelected()]
-
- # setSelection is a bad name (name of a QTreeWidget method)
- def setSelectedElements(self, symbolList):
- """
-
- :param symbolList: List of atomic symbols ["H", "He", "Li"...]
- to be selected in the widget
- """
- # accept PeriodicTableItem for getter/setter consistency
- if isinstance(symbolList[0], PeriodicTableItem):
- symbolList = [elmt.symbol for elmt in symbolList]
- for idx in range(len(self.tree_items)):
- self.tree_items[idx].setSelected(_defaultTableItems[idx].symbol in symbolList)
diff --git a/silx/gui/widgets/PrintGeometryDialog.py b/silx/gui/widgets/PrintGeometryDialog.py
deleted file mode 100644
index db0f3b3..0000000
--- a/silx/gui/widgets/PrintGeometryDialog.py
+++ /dev/null
@@ -1,222 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2017 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-
-
-from silx.gui import qt
-from silx.gui.widgets.FloatEdit import FloatEdit
-
-
-class PrintGeometryWidget(qt.QWidget):
- """Widget to specify the size and aspect ratio of an item
- before sending it to the print preview dialog.
-
- Use methods :meth:`setPrintGeometry` and :meth:`getPrintGeometry`
- to interact with the widget.
- """
- def __init__(self, parent=None):
- super(PrintGeometryWidget, self).__init__(parent)
- self.mainLayout = qt.QGridLayout(self)
- self.mainLayout.setContentsMargins(0, 0, 0, 0)
- self.mainLayout.setSpacing(2)
- hbox = qt.QWidget(self)
- hboxLayout = qt.QHBoxLayout(hbox)
- hboxLayout.setContentsMargins(0, 0, 0, 0)
- hboxLayout.setSpacing(2)
- label = qt.QLabel(self)
- label.setText("Units")
- label.setAlignment(qt.Qt.AlignCenter)
- self._pageButton = qt.QRadioButton()
- self._pageButton.setText("Page")
- self._inchButton = qt.QRadioButton()
- self._inchButton.setText("Inches")
- self._cmButton = qt.QRadioButton()
- self._cmButton.setText("Centimeters")
- self._buttonGroup = qt.QButtonGroup(self)
- self._buttonGroup.addButton(self._pageButton)
- self._buttonGroup.addButton(self._inchButton)
- self._buttonGroup.addButton(self._cmButton)
- self._buttonGroup.setExclusive(True)
-
- # units
- self.mainLayout.addWidget(label, 0, 0, 1, 4)
- hboxLayout.addWidget(self._pageButton)
- hboxLayout.addWidget(self._inchButton)
- hboxLayout.addWidget(self._cmButton)
- self.mainLayout.addWidget(hbox, 1, 0, 1, 4)
- self._pageButton.setChecked(True)
-
- # xOffset
- label = qt.QLabel(self)
- label.setText("X Offset:")
- self.mainLayout.addWidget(label, 2, 0)
- self._xOffset = FloatEdit(self, 0.1)
- self.mainLayout.addWidget(self._xOffset, 2, 1)
-
- # yOffset
- label = qt.QLabel(self)
- label.setText("Y Offset:")
- self.mainLayout.addWidget(label, 2, 2)
- self._yOffset = FloatEdit(self, 0.1)
- self.mainLayout.addWidget(self._yOffset, 2, 3)
-
- # width
- label = qt.QLabel(self)
- label.setText("Width:")
- self.mainLayout.addWidget(label, 3, 0)
- self._width = FloatEdit(self, 0.9)
- self.mainLayout.addWidget(self._width, 3, 1)
-
- # height
- label = qt.QLabel(self)
- label.setText("Height:")
- self.mainLayout.addWidget(label, 3, 2)
- self._height = FloatEdit(self, 0.9)
- self.mainLayout.addWidget(self._height, 3, 3)
-
- # aspect ratio
- self._aspect = qt.QCheckBox(self)
- self._aspect.setText("Keep screen aspect ratio")
- self._aspect.setChecked(True)
- self.mainLayout.addWidget(self._aspect, 4, 1, 1, 2)
-
- def getPrintGeometry(self):
- """Return the print geometry dictionary.
-
- See :meth:`setPrintGeometry` for documentation about the
- print geometry dictionary."""
- ddict = {}
- if self._inchButton.isChecked():
- ddict['units'] = "inches"
- elif self._cmButton.isChecked():
- ddict['units'] = "centimeters"
- else:
- ddict['units'] = "page"
-
- ddict['xOffset'] = self._xOffset.value()
- ddict['yOffset'] = self._yOffset.value()
- ddict['width'] = self._width.value()
- ddict['height'] = self._height.value()
-
- if self._aspect.isChecked():
- ddict['keepAspectRatio'] = True
- else:
- ddict['keepAspectRatio'] = False
- return ddict
-
- def setPrintGeometry(self, geometry=None):
- """Set the print geometry.
-
- The geometry parameters must be provided as a dictionary with
- the following keys:
-
- - *"xOffset"* (float)
- - *"yOffset"* (float)
- - *"width"* (float)
- - *"height"* (float)
- - *"units"*: possible values *"page", "inch", "cm"*
- - *"keepAspectRatio"*: *True* or *False*
-
- If *units* is *"page"*, the values should be floats in [0, 1.]
- and are interpreted as a fraction of the page width or height.
-
- :param dict geometry: Geometry parameters, as a dictionary."""
- if geometry is None:
- geometry = {}
- oldDict = self.getPrintGeometry()
- for key in ["units", "xOffset", "yOffset",
- "width", "height", "keepAspectRatio"]:
- geometry[key] = geometry.get(key, oldDict[key])
-
- if geometry['units'].lower().startswith("inc"):
- self._inchButton.setChecked(True)
- elif geometry['units'].lower().startswith("c"):
- self._cmButton.setChecked(True)
- else:
- self._pageButton.setChecked(True)
-
- self._xOffset.setText("%s" % float(geometry['xOffset']))
- self._yOffset.setText("%s" % float(geometry['yOffset']))
- self._width.setText("%s" % float(geometry['width']))
- self._height.setText("%s" % float(geometry['height']))
- if geometry['keepAspectRatio']:
- self._aspect.setChecked(True)
- else:
- self._aspect.setChecked(False)
-
-
-class PrintGeometryDialog(qt.QDialog):
- """Dialog embedding a :class:`PrintGeometryWidget`.
-
- Use methods :meth:`setPrintGeometry` and :meth:`getPrintGeometry`
- to interact with the widget.
-
- Execute method :meth:`exec_` to run the dialog.
- The return value of that method is *True* if the geometry was set
- (*Ok* button clicked) or *False* if the user clicked the *Cancel*
- button.
- """
-
- def __init__(self, parent=None):
- qt.QDialog.__init__(self, parent)
- self.setWindowTitle("Set print size preferences")
- layout = qt.QVBoxLayout(self)
- layout.setContentsMargins(0, 0, 0, 0)
- layout.setSpacing(0)
- self.configurationWidget = PrintGeometryWidget(self)
- hbox = qt.QWidget(self)
- hboxLayout = qt.QHBoxLayout(hbox)
- self.okButton = qt.QPushButton(hbox)
- self.okButton.setText("Accept")
- self.okButton.setAutoDefault(False)
- self.rejectButton = qt.QPushButton(hbox)
- self.rejectButton.setText("Dismiss")
- self.rejectButton.setAutoDefault(False)
- self.okButton.clicked.connect(self.accept)
- self.rejectButton.clicked.connect(self.reject)
- hboxLayout.setContentsMargins(0, 0, 0, 0)
- hboxLayout.setSpacing(2)
- # hboxLayout.addWidget(qt.HorizontalSpacer(hbox))
- hboxLayout.addWidget(self.okButton)
- hboxLayout.addWidget(self.rejectButton)
- # hboxLayout.addWidget(qt.HorizontalSpacer(hbox))
- layout.addWidget(self.configurationWidget)
- layout.addWidget(hbox)
-
- def setPrintGeometry(self, geometry):
- """Return the print geometry dictionary.
-
- See :meth:`PrintGeometryWidget.setPrintGeometry` for documentation on
- print geometry dictionary.
-
- :param dict geometry: Print geometry parameters dictionary.
- """
- self.configurationWidget.setPrintGeometry(geometry)
-
- def getPrintGeometry(self):
- """Return the print geometry dictionary.
-
- See :meth:`PrintGeometryWidget.setPrintGeometry` for documentation on
- print geometry dictionary."""
- return self.configurationWidget.getPrintGeometry()
diff --git a/silx/gui/widgets/PrintPreview.py b/silx/gui/widgets/PrintPreview.py
deleted file mode 100644
index 96af34b..0000000
--- a/silx/gui/widgets/PrintPreview.py
+++ /dev/null
@@ -1,728 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2004-2018 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-"""This module implements a print preview dialog.
-
-The dialog provides methods to send images, pixmaps and SVG
-items to the page to be printed.
-
-The user can interactively move and resize the items.
-"""
-import sys
-import logging
-from silx.gui import qt, printer
-
-
-__authors__ = ["V.A. Sole", "P. Knobel"]
-__license__ = "MIT"
-__date__ = "11/07/2017"
-
-
-_logger = logging.getLogger(__name__)
-
-
-class PrintPreviewDialog(qt.QDialog):
- """Print preview dialog widget.
- """
- def __init__(self, parent=None, printer=None):
-
- qt.QDialog.__init__(self, parent)
- self.setWindowTitle("Print Preview")
- self.setModal(False)
- self.resize(400, 500)
-
- self.mainLayout = qt.QVBoxLayout(self)
- self.mainLayout.setContentsMargins(0, 0, 0, 0)
- self.mainLayout.setSpacing(0)
-
- self._buildToolbar()
-
- self.printer = printer
- # :class:`QPrinter` (paint device that paints on a printer).
- # :meth:`showEvent` has been reimplemented to enforce printer
- # setup.
-
- self.printDialog = None
- # :class:`QPrintDialog` (dialog for specifying the printer's
- # configuration)
-
- self.scene = None
- # :class:`QGraphicsScene` (surface for managing
- # 2D graphical items)
-
- self.page = None
- # :class:`QGraphicsRectItem` used as white background page on which
- # to display the print preview.
-
- self.view = None
- # :class:`QGraphicsView` widget for displaying :attr:`scene`
-
- self._svgItems = []
- # List storing :class:`QSvgRenderer` items to be printed, added in
- # :meth:`addSvgItem`, cleared in :meth:`_clearAll`.
- # This ensures that there is a reference pointing to the items,
- # which ensures they are not destroyed before being printed.
-
- self._viewScale = 1.0
- # Zoom level (1.0 is 100%)
-
- self._toBeCleared = False
- # Flag indicating that all items must be removed from :attr:`scene`
- # and from :attr:`_svgItems`.
- # Set to True after a successful printing. The widget is then hidden,
- # and it will be cleared the next time it is shown.
- # Reset to False after :meth:`_clearAll` has done its job.
-
- def _buildToolbar(self):
- toolBar = qt.QWidget(self)
- # a layout for the toolbar
- toolsLayout = qt.QHBoxLayout(toolBar)
- toolsLayout.setContentsMargins(0, 0, 0, 0)
- toolsLayout.setSpacing(0)
-
- hideBut = qt.QPushButton("Hide", toolBar)
- hideBut.setToolTip("Hide print preview dialog")
- hideBut.clicked.connect(self.hide)
-
- cancelBut = qt.QPushButton("Clear All", toolBar)
- cancelBut.setToolTip("Remove all items")
- cancelBut.clicked.connect(self._clearAll)
-
- removeBut = qt.QPushButton("Remove",
- toolBar)
- removeBut.setToolTip("Remove selected item (use left click to select)")
- removeBut.clicked.connect(self._remove)
-
- setupBut = qt.QPushButton("Setup", toolBar)
- setupBut.setToolTip("Select and configure a printer")
- setupBut.clicked.connect(self.setup)
-
- printBut = qt.QPushButton("Print", toolBar)
- printBut.setToolTip("Print page and close print preview")
- printBut.clicked.connect(self._print)
-
- zoomPlusBut = qt.QPushButton("Zoom +", toolBar)
- zoomPlusBut.clicked.connect(self._zoomPlus)
-
- zoomMinusBut = qt.QPushButton("Zoom -", toolBar)
- zoomMinusBut.clicked.connect(self._zoomMinus)
-
- toolsLayout.addWidget(hideBut)
- toolsLayout.addWidget(printBut)
- toolsLayout.addWidget(cancelBut)
- toolsLayout.addWidget(removeBut)
- toolsLayout.addWidget(setupBut)
- # toolsLayout.addStretch()
- # toolsLayout.addWidget(marginLabel)
- # toolsLayout.addWidget(self.marginSpin)
- toolsLayout.addStretch()
- # toolsLayout.addWidget(scaleLabel)
- # toolsLayout.addWidget(self.scaleCombo)
- toolsLayout.addWidget(zoomPlusBut)
- toolsLayout.addWidget(zoomMinusBut)
- # toolsLayout.addStretch()
- self.toolBar = toolBar
- self.mainLayout.addWidget(self.toolBar)
-
- def _buildStatusBar(self):
- """Create the status bar used to display the printer name
- or output file name."""
- # status bar
- statusBar = qt.QStatusBar(self)
- self.targetLabel = qt.QLabel(statusBar)
- self._updateTargetLabel()
- statusBar.addWidget(self.targetLabel)
- self.mainLayout.addWidget(statusBar)
-
- def _updateTargetLabel(self):
- """Update printer name or file name shown in the status bar."""
- if self.printer is None:
- self.targetLabel.setText("Undefined printer")
- return
- if self.printer.outputFileName():
- self.targetLabel.setText("File:" +
- self.printer.outputFileName())
- else:
- self.targetLabel.setText("Printer:" +
- self.printer.printerName())
-
- def _updatePrinter(self):
- """Resize :attr:`page`, :attr:`scene` and :attr:`view` to :attr:`printer`
- width and height."""
- printer = self.printer
- assert printer is not None, \
- "_updatePrinter should not be called unless a printer is defined"
- if self.scene is None:
- self.scene = qt.QGraphicsScene()
- self.scene.setBackgroundBrush(qt.QColor(qt.Qt.lightGray))
- self.scene.setSceneRect(qt.QRectF(0, 0, printer.width(), printer.height()))
-
- if self.page is None:
- self.page = qt.QGraphicsRectItem(0, 0, printer.width(), printer.height())
- self.page.setBrush(qt.QColor(qt.Qt.white))
- self.scene.addItem(self.page)
-
- self.scene.setSceneRect(qt.QRectF(0, 0, printer.width(), printer.height()))
- self.page.setPos(qt.QPointF(0.0, 0.0))
- self.page.setRect(qt.QRectF(0, 0, printer.width(), printer.height()))
-
- if self.view is None:
- self.view = qt.QGraphicsView(self.scene)
- self.mainLayout.addWidget(self.view)
- self._buildStatusBar()
- # self.view.scale(1./self._viewScale, 1./self._viewScale)
- self.view.fitInView(self.page.rect(), qt.Qt.KeepAspectRatio)
- self._viewScale = 1.00
- self._updateTargetLabel()
-
- # Public methods
- def addImage(self, image, title=None, comment=None, commentPosition=None):
- """Add an image to the print preview scene.
-
- :param QImage image: Image to be added to the scene
- :param str title: Title shown above (centered) the image
- :param str comment: Comment displayed below the image
- :param commentPosition: "CENTER" or "LEFT"
- """
- self.addPixmap(qt.QPixmap.fromImage(image),
- title=title, comment=comment,
- commentPosition=commentPosition)
-
- def addPixmap(self, pixmap, title=None, comment=None, commentPosition=None):
- """Add a pixmap to the print preview scene
-
- :param QPixmap pixmap: Pixmap to be added to the scene
- :param str title: Title shown above (centered) the pixmap
- :param str comment: Comment displayed below the pixmap
- :param commentPosition: "CENTER" or "LEFT"
- """
- if self._toBeCleared:
- self._clearAll()
- self.ensurePrinterIsSet()
- if self.printer is None:
- _logger.error("printer is not set, cannot add pixmap to page")
- return
- if title is None:
- title = ' ' * 88
- if comment is None:
- comment = ' ' * 88
- if commentPosition is None:
- commentPosition = "CENTER"
- if qt.qVersion() < "5.0":
- rectItem = qt.QGraphicsRectItem(self.page, self.scene)
- else:
- rectItem = qt.QGraphicsRectItem(self.page)
-
- rectItem.setRect(qt.QRectF(1, 1,
- pixmap.width(), pixmap.height()))
-
- pen = rectItem.pen()
- color = qt.QColor(qt.Qt.red)
- color.setAlpha(1)
- pen.setColor(color)
- rectItem.setPen(pen)
- rectItem.setZValue(1)
- rectItem.setFlag(qt.QGraphicsItem.ItemIsSelectable, True)
- rectItem.setFlag(qt.QGraphicsItem.ItemIsMovable, True)
- rectItem.setFlag(qt.QGraphicsItem.ItemIsFocusable, False)
-
- rectItemResizeRect = _GraphicsResizeRectItem(rectItem, self.scene)
- rectItemResizeRect.setZValue(2)
-
- if qt.qVersion() < "5.0":
- pixmapItem = qt.QGraphicsPixmapItem(rectItem, self.scene)
- else:
- pixmapItem = qt.QGraphicsPixmapItem(rectItem)
- pixmapItem.setPixmap(pixmap)
- pixmapItem.setZValue(0)
-
- # I add the title
- if qt.qVersion() < "5.0":
- textItem = qt.QGraphicsTextItem(title, rectItem, self.scene)
- else:
- textItem = qt.QGraphicsTextItem(title, rectItem)
- textItem.setTextInteractionFlags(qt.Qt.TextEditorInteraction)
- offset = 0.5 * textItem.boundingRect().width()
- textItem.moveBy(0.5 * pixmap.width() - offset, -20)
- textItem.setZValue(2)
-
- # I add the comment
- if qt.qVersion() < "5.0":
- commentItem = qt.QGraphicsTextItem(comment, rectItem, self.scene)
- else:
- commentItem = qt.QGraphicsTextItem(comment, rectItem)
- commentItem.setTextInteractionFlags(qt.Qt.TextEditorInteraction)
- offset = 0.5 * commentItem.boundingRect().width()
- if commentPosition.upper() == "LEFT":
- x = 1
- else:
- x = 0.5 * pixmap.width() - offset
- commentItem.moveBy(x, pixmap.height() + 20)
- commentItem.setZValue(2)
-
- rectItem.moveBy(20, 40)
-
- def addSvgItem(self, item, title=None,
- comment=None, commentPosition=None,
- viewBox=None, keepRatio=True):
- """Add a SVG item to the scene.
-
- :param QSvgRenderer item: SVG item to be added to the scene.
- :param str title: Title shown above (centered) the SVG item.
- :param str comment: Comment displayed below the SVG item.
- :param str commentPosition: "CENTER" or "LEFT"
- :param QRectF viewBox: Bounding box for the item on the print page
- (xOffset, yOffset, width, height). If None, use original
- item size.
- :param bool keepRatio: If True, resizing the item will preserve its
- original aspect ratio.
- """
- if not qt.HAS_SVG:
- raise RuntimeError("Missing QtSvg library.")
- if not isinstance(item, qt.QSvgRenderer):
- raise TypeError("addSvgItem: QSvgRenderer expected")
- if self._toBeCleared:
- self._clearAll()
- self.ensurePrinterIsSet()
- if self.printer is None:
- _logger.error("printer is not set, cannot add SvgItem to page")
- return
-
- if title is None:
- title = 50 * ' '
- if comment is None:
- comment = 80 * ' '
- if commentPosition is None:
- commentPosition = "CENTER"
-
- if viewBox is None:
- if hasattr(item, "_viewBox"):
- # PyMca compatibility: viewbox attached to item
- viewBox = item._viewBox
- else:
- # try the original item viewbox
- viewBox = item.viewBoxF()
-
- svgItem = _GraphicsSvgRectItem(viewBox, self.page)
- svgItem.setSvgRenderer(item)
-
- svgItem.setCacheMode(qt.QGraphicsItem.NoCache)
- svgItem.setZValue(0)
- svgItem.setFlag(qt.QGraphicsItem.ItemIsSelectable, True)
- svgItem.setFlag(qt.QGraphicsItem.ItemIsMovable, True)
- svgItem.setFlag(qt.QGraphicsItem.ItemIsFocusable, False)
-
- rectItemResizeRect = _GraphicsResizeRectItem(svgItem, self.scene,
- keepratio=keepRatio)
- rectItemResizeRect.setZValue(2)
-
- self._svgItems.append(item)
-
- # Comment / legend
- dummyComment = 80 * "1"
- if qt.qVersion() < '5.0':
- commentItem = qt.QGraphicsTextItem(dummyComment, svgItem, self.scene)
- else:
- commentItem = qt.QGraphicsTextItem(dummyComment, svgItem)
- commentItem.setTextInteractionFlags(qt.Qt.TextEditorInteraction)
- # we scale the text to have the legend box have the same width as the graph
- scaleCalculationRect = qt.QRectF(commentItem.boundingRect())
- scale = svgItem.boundingRect().width() / scaleCalculationRect.width()
-
- commentItem.setPlainText(comment)
- commentItem.setZValue(1)
-
- commentItem.setFlag(qt.QGraphicsItem.ItemIsMovable, True)
- if qt.qVersion() < "5.0":
- commentItem.scale(scale, scale)
- else:
- commentItem.setScale(scale)
-
- # align
- if commentPosition.upper() == "CENTER":
- alignment = qt.Qt.AlignCenter
- elif commentPosition.upper() == "RIGHT":
- alignment = qt.Qt.AlignRight
- else:
- alignment = qt.Qt.AlignLeft
- commentItem.setTextWidth(commentItem.boundingRect().width())
- center_format = qt.QTextBlockFormat()
- center_format.setAlignment(alignment)
- cursor = commentItem.textCursor()
- cursor.select(qt.QTextCursor.Document)
- cursor.mergeBlockFormat(center_format)
- cursor.clearSelection()
- commentItem.setTextCursor(cursor)
- if alignment == qt.Qt.AlignLeft:
- deltax = 0
- else:
- deltax = (svgItem.boundingRect().width() - commentItem.boundingRect().width()) / 2.
- commentItem.moveBy(svgItem.boundingRect().x() + deltax,
- svgItem.boundingRect().y() + svgItem.boundingRect().height())
-
- # Title
- if qt.qVersion() < '5.0':
- textItem = qt.QGraphicsTextItem(title, svgItem, self.scene)
- else:
- textItem = qt.QGraphicsTextItem(title, svgItem)
- textItem.setTextInteractionFlags(qt.Qt.TextEditorInteraction)
- textItem.setZValue(1)
- textItem.setFlag(qt.QGraphicsItem.ItemIsMovable, True)
-
- title_offset = 0.5 * textItem.boundingRect().width()
- textItem.moveBy(svgItem.boundingRect().x() +
- 0.5 * svgItem.boundingRect().width() - title_offset * scale,
- svgItem.boundingRect().y())
- if qt.qVersion() < "5.0":
- textItem.scale(scale, scale)
- else:
- textItem.setScale(scale)
-
- def setup(self):
- """Open a print dialog to ensure the :attr:`printer` is set.
-
- If the setting fails or is cancelled, :attr:`printer` is reset to
- *None*.
- """
- if self.printer is None:
- self.printer = printer.getDefaultPrinter()
- if self.printDialog is None:
- self.printDialog = qt.QPrintDialog(self.printer, self)
- if self.printDialog.exec_():
- if self.printer.width() <= 0 or self.printer.height() <= 0:
- self.message = qt.QMessageBox(self)
- self.message.setIcon(qt.QMessageBox.Critical)
- self.message.setText("Unknown library error \non printer initialization")
- self.message.setWindowTitle("Library Error")
- self.message.setModal(0)
- self.printer = None
- return
- self.printer.setFullPage(True)
- self._updatePrinter()
- else:
- # printer setup cancelled, check for a possible previous configuration
- if self.page is None:
- # not initialized
- self.printer = None
-
- def ensurePrinterIsSet(self):
- """If the printer is not already set, try to interactively
- setup the printer using a QPrintDialog.
- In case of failure, hide widget and log a warning.
-
- :return: True if printer was set. False if it failed or if the
- selection dialog was canceled.
- """
- if self.printer is None:
- self.setup()
- if self.printer is None:
- self.hide()
- _logger.warning("Printer setup failed or was cancelled, " +
- "but printer is required.")
- return self.printer is not None
-
- def setOutputFileName(self, name):
- """Set output filename.
-
- Setting a non-empty name enables printing to file.
-
- :param str name: File name (path)"""
- self.printer.setOutputFileName(name)
-
- # overloaded methods
- def exec_(self):
- if self._toBeCleared:
- self._clearAll()
- return qt.QDialog.exec_(self)
-
- def raise_(self):
- if self._toBeCleared:
- self._clearAll()
- return qt.QDialog.raise_(self)
-
- def showEvent(self, event):
- """Reimplemented to force printer setup.
- In case of failure, hide the widget."""
- if self._toBeCleared:
- self._clearAll()
- self.ensurePrinterIsSet()
-
- return super(PrintPreviewDialog, self).showEvent(event)
-
- # button callbacks
- def _print(self):
- """Do the printing, hide the print preview dialog,
- set :attr:`_toBeCleared` flag to True to trigger clearing the
- next time the dialog is shown.
-
- If the printer is not setup, do it first."""
- printer = self.printer
-
- painter = qt.QPainter()
- if not painter.begin(printer) or printer is None:
- _logger.error("Cannot initialize printer")
- return
- try:
- self.scene.render(painter, qt.QRectF(0, 0, printer.width(), printer.height()),
- qt.QRectF(self.page.rect().x(), self.page.rect().y(),
- self.page.rect().width(), self.page.rect().height()),
- qt.Qt.KeepAspectRatio)
- painter.end()
- self.hide()
- self.accept()
- self._toBeCleared = True
- except: # FIXME
- painter.end()
- qt.QMessageBox.critical(self, "ERROR",
- 'Printing problem:\n %s' % sys.exc_info()[1])
- _logger.error('printing problem:\n %s' % sys.exc_info()[1])
- return
-
- def _zoomPlus(self):
- self._viewScale *= 1.20
- self.view.scale(1.20, 1.20)
-
- def _zoomMinus(self):
- self._viewScale *= 0.80
- self.view.scale(0.80, 0.80)
-
- def _clearAll(self):
- """
- Clear the print preview window, remove all items
- but keep the page.
- """
- itemlist = self.scene.items()
- keep = self.page
- while len(itemlist) != 1:
- if itemlist.index(keep) == 0:
- self.scene.removeItem(itemlist[1])
- else:
- self.scene.removeItem(itemlist[0])
- itemlist = self.scene.items()
- self._svgItems = []
- self._toBeCleared = False
-
- def _remove(self):
- """Remove selected item in :attr:`scene`.
- """
- itemlist = self.scene.items()
-
- # this loop is not efficient if there are many items ...
- for item in itemlist:
- if item.isSelected():
- self.scene.removeItem(item)
-
-
-class SingletonPrintPreviewDialog(PrintPreviewDialog):
- """Singleton print preview dialog.
-
- All widgets in a program that instantiate this class will share
- a single print preview dialog. This enables sending
- multiple images to a single page to be printed.
- """
- _instance = None
-
- def __new__(self, *var, **kw):
- if self._instance is None:
- self._instance = PrintPreviewDialog(*var, **kw)
- return self._instance
-
-
-class _GraphicsSvgRectItem(qt.QGraphicsRectItem):
- """:class:`qt.QGraphicsRectItem` with an attached
- :class:`qt.QSvgRenderer`, and with a painter redefined to render
- the SVG item."""
- def setSvgRenderer(self, renderer):
- """
-
- :param QSvgRenderer renderer: svg renderer
- """
- self._renderer = renderer
-
- def paint(self, painter, *var, **kw):
- self._renderer.render(painter, self.boundingRect())
-
-
-class _GraphicsResizeRectItem(qt.QGraphicsRectItem):
- """Resizable QGraphicsRectItem."""
- def __init__(self, parent=None, scene=None, keepratio=True):
- if qt.qVersion() < '5.0':
- qt.QGraphicsRectItem.__init__(self, parent, scene)
- else:
- qt.QGraphicsRectItem.__init__(self, parent)
- rect = parent.boundingRect()
- x = rect.x()
- y = rect.y()
- w = rect.width()
- h = rect.height()
- self._newRect = None
- self.keepRatio = keepratio
- self.setRect(qt.QRectF(x + w - 40, y + h - 40, 40, 40))
- self.setAcceptHoverEvents(True)
- pen = qt.QPen()
- color = qt.QColor(qt.Qt.white)
- color.setAlpha(0)
- pen.setColor(color)
- pen.setStyle(qt.Qt.NoPen)
- self.setPen(pen)
- self.setBrush(color)
- self.setFlag(self.ItemIsMovable, True)
- self.show()
-
- def hoverEnterEvent(self, event):
- if self.parentItem().isSelected():
- self.parentItem().setSelected(False)
- if self.keepRatio:
- self.setCursor(qt.QCursor(qt.Qt.SizeFDiagCursor))
- else:
- self.setCursor(qt.QCursor(qt.Qt.SizeAllCursor))
- self.setBrush(qt.QBrush(qt.Qt.yellow, qt.Qt.SolidPattern))
- return qt.QGraphicsRectItem.hoverEnterEvent(self, event)
-
- def hoverLeaveEvent(self, event):
- self.setCursor(qt.QCursor(qt.Qt.ArrowCursor))
- pen = qt.QPen()
- color = qt.QColor(qt.Qt.white)
- color.setAlpha(0)
- pen.setColor(color)
- pen.setStyle(qt.Qt.NoPen)
- self.setPen(pen)
- self.setBrush(color)
- return qt.QGraphicsRectItem.hoverLeaveEvent(self, event)
-
- def mousePressEvent(self, event):
- if self._newRect is not None:
- self._newRect = None
- self._point0 = self.pos()
- parent = self.parentItem()
- scene = self.scene()
- # following line prevents dragging along the previously selected
- # item when resizing another one
- scene.clearSelection()
-
- rect = parent.boundingRect()
- self._x = rect.x()
- self._y = rect.y()
- self._w = rect.width()
- self._h = rect.height()
- self._ratio = self._w / self._h
- if qt.qVersion() < "5.0":
- self._newRect = qt.QGraphicsRectItem(parent, scene)
- else:
- self._newRect = qt.QGraphicsRectItem(parent)
- self._newRect.setRect(qt.QRectF(self._x,
- self._y,
- self._w,
- self._h))
- qt.QGraphicsRectItem.mousePressEvent(self, event)
-
- def mouseMoveEvent(self, event):
- point1 = self.pos()
- deltax = point1.x() - self._point0.x()
- deltay = point1.y() - self._point0.y()
- if self.keepRatio:
- r1 = (self._w + deltax) / self._w
- r2 = (self._h + deltay) / self._h
- if r1 < r2:
- self._newRect.setRect(qt.QRectF(self._x,
- self._y,
- self._w + deltax,
- (self._w + deltax) / self._ratio))
- else:
- self._newRect.setRect(qt.QRectF(self._x,
- self._y,
- (self._h + deltay) * self._ratio,
- self._h + deltay))
- else:
- self._newRect.setRect(qt.QRectF(self._x,
- self._y,
- self._w + deltax,
- self._h + deltay))
- qt.QGraphicsRectItem.mouseMoveEvent(self, event)
-
- def mouseReleaseEvent(self, event):
- point1 = self.pos()
- deltax = point1.x() - self._point0.x()
- deltay = point1.y() - self._point0.y()
- self.moveBy(-deltax, -deltay)
- parent = self.parentItem()
-
- # deduce scale from rectangle
- if (qt.qVersion() < "5.0") or self.keepRatio:
- scalex = self._newRect.rect().width() / self._w
- scaley = scalex
- else:
- scalex = self._newRect.rect().width() / self._w
- scaley = self._newRect.rect().height() / self._h
-
- if qt.qVersion() < "5.0":
- parent.scale(scalex, scaley)
- else:
- # apply the scale to the previous transformation matrix
- previousTransform = parent.transform()
- parent.setTransform(
- previousTransform.scale(scalex, scaley))
-
- self.scene().removeItem(self._newRect)
- self._newRect = None
- qt.QGraphicsRectItem.mouseReleaseEvent(self, event)
-
-
-def main():
- """
- """
- if len(sys.argv) < 2:
- print("give an image file as parameter please.")
- sys.exit(1)
-
- if len(sys.argv) > 2:
- print("only one parameter please.")
- sys.exit(1)
-
- filename = sys.argv[1]
- w = PrintPreviewDialog()
- w.resize(400, 500)
-
- comment = ""
- for i in range(20):
- comment += "Line number %d: En un lugar de La Mancha de cuyo nombre ...\n" % i
-
- if filename[-3:] == "svg":
- item = qt.QSvgRenderer(filename, w.page)
- w.addSvgItem(item, title=filename,
- comment=comment, commentPosition="CENTER")
- else:
- w.addPixmap(qt.QPixmap.fromImage(qt.QImage(filename)),
- title=filename,
- comment=comment,
- commentPosition="CENTER")
- w.addImage(qt.QImage(filename), comment=comment, commentPosition="LEFT")
-
- sys.exit(w.exec_())
-
-
-if __name__ == '__main__':
- a = qt.QApplication(sys.argv)
- main()
- a.exec_()
diff --git a/silx/gui/widgets/RangeSlider.py b/silx/gui/widgets/RangeSlider.py
deleted file mode 100644
index 31dbd4e..0000000
--- a/silx/gui/widgets/RangeSlider.py
+++ /dev/null
@@ -1,765 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2015-2020 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-"""This module provides a :class:`RangeSlider` widget.
-
-.. image:: img/RangeSlider.png
- :align: center
-"""
-from __future__ import absolute_import, division
-
-__authors__ = ["D. Naudet", "T. Vincent"]
-__license__ = "MIT"
-__date__ = "26/11/2018"
-
-
-import numpy as numpy
-
-from silx.gui import qt, icons, colors
-from silx.gui.utils.image import convertArrayToQImage
-
-
-class StyleOptionRangeSlider(qt.QStyleOption):
- def __init__(self):
- super(StyleOptionRangeSlider, self).__init__()
- self.minimum = None
- self.maximum = None
- self.sliderPosition1 = None
- self.sliderPosition2 = None
- self.handlerRect1 = None
- self.handlerRect2 = None
-
-
-class RangeSlider(qt.QWidget):
- """Range slider with 2 thumbs and an optional colored groove.
-
- The position of the slider thumbs can be retrieved either as values
- in the slider range or as a number of steps or pixels.
-
- :param QWidget parent: See QWidget
- """
-
- _SLIDER_WIDTH = 10
- """Width of the slider rectangle"""
-
- _PIXMAP_VOFFSET = 7
- """Vertical groove pixmap offset"""
-
- sigRangeChanged = qt.Signal(float, float)
- """Signal emitted when the value range has changed.
-
- It provides the new range (min, max).
- """
-
- sigValueChanged = qt.Signal(float, float)
- """Signal emitted when the value of the sliders has changed.
-
- It provides the slider values (first, second).
- """
-
- sigPositionCountChanged = qt.Signal(object)
- """This signal is emitted when the number of steps has changed.
-
- It provides the new position count.
- """
-
- sigPositionChanged = qt.Signal(int, int)
- """Signal emitted when the position of the sliders has changed.
-
- It provides the slider positions in steps or pixels (first, second).
- """
-
- def __init__(self, parent=None):
- self.__pixmap = None
- self.__positionCount = None
- self.__firstValue = 0.
- self.__secondValue = 1.
- self.__minValue = 0.
- self.__maxValue = 1.
- self.__hoverRect = qt.QRect()
- self.__hoverControl = None
-
- self.__focus = None
- self.__moving = None
-
- self.__icons = {
- 'first': icons.getQIcon('previous'),
- 'second': icons.getQIcon('next')
- }
-
- # call the super constructor AFTER defining all members that
- # are used in the "paint" method
- super(RangeSlider, self).__init__(parent)
-
- self.setFocusPolicy(qt.Qt.ClickFocus)
- self.setAttribute(qt.Qt.WA_Hover)
-
- self.setMinimumSize(qt.QSize(50, 20))
- self.setMaximumHeight(20)
-
- # Broadcast value changed signal
- self.sigValueChanged.connect(self.__emitPositionChanged)
-
- def event(self, event):
- t = event.type()
- if t == qt.QEvent.HoverEnter or t == qt.QEvent.HoverLeave or t == qt.QEvent.HoverMove:
- return self.__updateHoverControl(event.pos())
- else:
- return super(RangeSlider, self).event(event)
-
- def __updateHoverControl(self, pos):
- hoverControl, hoverRect = self.__findHoverControl(pos)
- if hoverControl != self.__hoverControl:
- self.update(self.__hoverRect)
- self.update(hoverRect)
- self.__hoverControl = hoverControl
- self.__hoverRect = hoverRect
- return True
- return hoverControl is not None
-
- def __findHoverControl(self, pos):
- """Returns the control at the position and it's rect location"""
- for name in ["first", "second"]:
- rect = self.__sliderRect(name)
- if rect.contains(pos):
- return name, rect
- rect = self.__drawArea()
- if rect.contains(pos):
- return "groove", rect
- return None, qt.QRect()
-
- # Position <-> Value conversion
-
- def __positionToValue(self, position):
- """Returns value corresponding to position
-
- :param int position:
- :rtype: float
- """
- min_, max_ = self.getMinimum(), self.getMaximum()
- maxPos = self.__getCurrentPositionCount() - 1
- return min_ + (max_ - min_) * int(position) / maxPos
-
- def __valueToPosition(self, value):
- """Returns closest position corresponding to value
-
- :param float value:
- :rtype: int
- """
- min_, max_ = self.getMinimum(), self.getMaximum()
- maxPos = self.__getCurrentPositionCount() - 1
- return int(0.5 + maxPos * (float(value) - min_) / (max_ - min_))
-
- # Position (int) API
-
- def __getCurrentPositionCount(self):
- """Return current count (either position count or widget width
-
- :rtype: int
- """
- count = self.getPositionCount()
- if count is not None:
- return count
- else:
- return max(2, self.width() - self._SLIDER_WIDTH)
-
- def getPositionCount(self):
- """Returns the number of positions.
-
- :rtype: Union[int,None]"""
- return self.__positionCount
-
- def setPositionCount(self, count):
- """Set the number of positions.
-
- Slider values are eventually adjusted.
-
- :param Union[int,None] count:
- Either the number of possible positions or
- None to allow any values.
- :raise ValueError: If count <= 1
- """
- count = None if count is None else int(count)
- if count != self.getPositionCount():
- if count is not None and count <= 1:
- raise ValueError("Position count must be higher than 1")
- self.__positionCount = count
- emit = self.__setValues(*self.getValues())
- self.sigPositionCountChanged.emit(count)
- if emit:
- self.sigValueChanged.emit(*self.getValues())
-
- def getFirstPosition(self):
- """Returns first slider position
-
- :rtype: int
- """
- return self.__valueToPosition(self.getFirstValue())
-
- def setFirstPosition(self, position):
- """Set the position of the first slider
-
- The position is adjusted to valid values
-
- :param int position:
- """
- self.setFirstValue(self.__positionToValue(position))
-
- def getSecondPosition(self):
- """Returns second slider position
-
- :rtype: int
- """
- return self.__valueToPosition(self.getSecondValue())
-
- def setSecondPosition(self, position):
- """Set the position of the second slider
-
- The position is adjusted to valid values
-
- :param int position:
- """
- self.setSecondValue(self.__positionToValue(position))
-
- def getPositions(self):
- """Returns slider positions (first, second)
-
- :rtype: List[int]
- """
- return self.getFirstPosition(), self.getSecondPosition()
-
- def setPositions(self, first, second):
- """Set the position of both sliders at once
-
- First is clipped to the slider range: [0, max].
- Second is clipped to valid values: [first, max]
-
- :param int first:
- :param int second:
- """
- self.setValues(self.__positionToValue(first),
- self.__positionToValue(second))
-
- # Value (float) API
-
- def __emitPositionChanged(self, *args, **kwargs):
- self.sigPositionChanged.emit(*self.getPositions())
-
- def __rangeChanged(self):
- """Handle change of value range"""
- emit = self.__setValues(*self.getValues())
- self.sigRangeChanged.emit(*self.getRange())
- if emit:
- self.sigValueChanged.emit(*self.getValues())
-
- def getMinimum(self):
- """Returns the minimum value of the slider range
-
- :rtype: float
- """
- return self.__minValue
-
- def setMinimum(self, minimum):
- """Set the minimum value of the slider range.
-
- It eventually adjusts maximum.
- Slider positions remains unchanged and slider values are modified.
-
- :param float minimum:
- """
- minimum = float(minimum)
- if minimum != self.getMinimum():
- if minimum > self.getMaximum():
- self.__maxValue = minimum
- self.__minValue = minimum
- self.__rangeChanged()
-
- def getMaximum(self):
- """Returns the maximum value of the slider range
-
- :rtype: float
- """
- return self.__maxValue
-
- def setMaximum(self, maximum):
- """Set the maximum value of the slider range
-
- It eventually adjusts minimum.
- Slider positions remains unchanged and slider values are modified.
-
- :param float maximum:
- """
- maximum = float(maximum)
- if maximum != self.getMaximum():
- if maximum < self.getMinimum():
- self.__minValue = maximum
- self.__maxValue = maximum
- self.__rangeChanged()
-
- def getRange(self):
- """Returns the range of values (min, max)
-
- :rtype: List[float]
- """
- return self.getMinimum(), self.getMaximum()
-
- def setRange(self, minimum, maximum):
- """Set the range of values.
-
- If maximum is lower than minimum, minimum is the only valid value.
- Slider positions remains unchanged and slider values are modified.
-
- :param float minimum:
- :param float maximum:
- """
- minimum, maximum = float(minimum), float(maximum)
- if minimum != self.getMinimum() or maximum != self.getMaximum():
- self.__minValue = minimum
- self.__maxValue = max(maximum, minimum)
- self.__rangeChanged()
-
- def getFirstValue(self):
- """Returns the value of the first slider
-
- :rtype: float
- """
- return self.__firstValue
-
- def __clipFirstValue(self, value, max_=None):
- """Clip first value to range and steps
-
- :param float value:
- :param float max_: Alternative maximum to use
- """
- if max_ is None:
- max_ = self.getSecondValue()
- value = min(max(self.getMinimum(), float(value)), max_)
- if self.getPositionCount() is not None: # Clip to steps
- value = self.__positionToValue(self.__valueToPosition(value))
- return value
-
- def setFirstValue(self, value):
- """Set the value of the first slider
-
- Value is clipped to valid values.
-
- :param float value:
- """
- value = self.__clipFirstValue(value)
- if value != self.getFirstValue():
- self.__firstValue = value
- self.update()
- self.sigValueChanged.emit(*self.getValues())
-
- def getSecondValue(self):
- """Returns the value of the second slider
-
- :rtype: float
- """
- return self.__secondValue
-
- def __clipSecondValue(self, value):
- """Clip second value to range and steps
-
- :param float value:
- """
- value = min(max(self.getFirstValue(), float(value)), self.getMaximum())
- if self.getPositionCount() is not None: # Clip to steps
- value = self.__positionToValue(self.__valueToPosition(value))
- return value
-
- def setSecondValue(self, value):
- """Set the value of the second slider
-
- Value is clipped to valid values.
-
- :param float value:
- """
- value = self.__clipSecondValue(value)
- if value != self.getSecondValue():
- self.__secondValue = value
- self.update()
- self.sigValueChanged.emit(*self.getValues())
-
- def getValues(self):
- """Returns value of both sliders at once
-
- :return: (first value, second value)
- :rtype: List[float]
- """
- return self.getFirstValue(), self.getSecondValue()
-
- def setValues(self, first, second):
- """Set values for both sliders at once
-
- First is clipped to the slider range: [minimum, maximum].
- Second is clipped to valid values: [first, maximum]
-
- :param float first:
- :param float second:
- """
- if self.__setValues(first, second):
- self.sigValueChanged.emit(*self.getValues())
-
- def __setValues(self, first, second):
- """Set values for both sliders at once
-
- First is clipped to the slider range: [minimum, maximum].
- Second is clipped to valid values: [first, maximum]
-
- :param float first:
- :param float second:
- :return: True if values has changed, False otherwise
- :rtype: bool
- """
- first = self.__clipFirstValue(first, self.getMaximum())
- second = self.__clipSecondValue(second)
- values = first, second
-
- if self.getValues() != values:
- self.__firstValue, self.__secondValue = values
- self.update()
- return True
- return False
-
- # Groove API
-
- def getGroovePixmap(self):
- """Returns the pixmap displayed in the slider groove if any.
-
- :rtype: Union[QPixmap,None]
- """
- return self.__pixmap
-
- def setGroovePixmap(self, pixmap):
- """Set the pixmap displayed in the slider groove.
-
- :param Union[QPixmap,None] pixmap: The QPixmap to use or None to unset.
- """
- assert pixmap is None or isinstance(pixmap, qt.QPixmap)
- self.__pixmap = pixmap
- self.update()
-
- def setGroovePixmapFromProfile(self, profile, colormap=None):
- """Set the pixmap displayed in the slider groove from histogram values.
-
- :param Union[numpy.ndarray,None] profile:
- 1D array of values to display
- :param Union[~silx.gui.colors.Colormap,str] colormap:
- The colormap name or object to convert profile values to colors
- """
- if profile is None:
- self.setSliderPixmap(None)
- return
-
- profile = numpy.array(profile, copy=False)
-
- if profile.size == 0:
- self.setSliderPixmap(None)
- return
-
- if colormap is None:
- colormap = colors.Colormap()
- elif isinstance(colormap, str):
- colormap = colors.Colormap(name=colormap)
- assert isinstance(colormap, colors.Colormap)
-
- rgbImage = colormap.applyToData(profile.reshape(1, -1))[:, :, :3]
- qimage = convertArrayToQImage(rgbImage)
- qpixmap = qt.QPixmap.fromImage(qimage)
- self.setGroovePixmap(qpixmap)
-
- # Handle interaction
-
- def mousePressEvent(self, event):
- super(RangeSlider, self).mousePressEvent(event)
-
- if event.buttons() == qt.Qt.LeftButton:
- picked = None
- for name in ('first', 'second'):
- area = self.__sliderRect(name)
- if area.contains(event.pos()):
- picked = name
- break
-
- self.__moving = picked
- self.__focus = picked
- self.update()
-
- def mouseMoveEvent(self, event):
- super(RangeSlider, self).mouseMoveEvent(event)
-
- if self.__moving is not None:
- delta = self._SLIDER_WIDTH // 2
- if self.__moving == 'first':
- position = self.__xPixelToPosition(event.pos().x() + delta)
- self.setFirstPosition(position)
- else:
- position = self.__xPixelToPosition(event.pos().x() - delta)
- self.setSecondPosition(position)
-
- def mouseReleaseEvent(self, event):
- super(RangeSlider, self).mouseReleaseEvent(event)
-
- if event.button() == qt.Qt.LeftButton and self.__moving is not None:
- self.__moving = None
- self.update()
-
- def focusOutEvent(self, event):
- if self.__focus is not None:
- self.__focus = None
- self.update()
- super(RangeSlider, self).focusOutEvent(event)
-
- def keyPressEvent(self, event):
- key = event.key()
- if event.modifiers() == qt.Qt.NoModifier and self.__focus is not None:
- if key in (qt.Qt.Key_Left, qt.Qt.Key_Down):
- if self.__focus == 'first':
- self.setFirstPosition(self.getFirstPosition() - 1)
- else:
- self.setSecondPosition(self.getSecondPosition() - 1)
- return # accept event
- elif key in (qt.Qt.Key_Right, qt.Qt.Key_Up):
- if self.__focus == 'first':
- self.setFirstPosition(self.getFirstPosition() + 1)
- else:
- self.setSecondPosition(self.getSecondPosition() + 1)
- return # accept event
-
- super(RangeSlider, self).keyPressEvent(event)
-
- # Handle resize
-
- def resizeEvent(self, event):
- super(RangeSlider, self).resizeEvent(event)
-
- # If no step, signal position update when width change
- if (self.getPositionCount() is None and
- event.size().width() != event.oldSize().width()):
- self.sigPositionChanged.emit(*self.getPositions())
-
- # Handle repaint
-
- def __xPixelToPosition(self, x):
- """Convert position in pixel to slider position
-
- :param int x: X in pixel coordinates
- :rtype: int
- """
- sliderArea = self.__sliderAreaRect()
- maxPos = self.__getCurrentPositionCount() - 1
- position = maxPos * (x - sliderArea.left()) / (sliderArea.width() - 1)
- return int(position + 0.5)
-
- def __sliderRect(self, name):
- """Returns rectangle corresponding to slider in pixels
-
- :param str name: 'first' or 'second'
- :rtype: QRect
- :raise ValueError: If wrong name
- """
- assert name in ('first', 'second')
- if name == 'first':
- offset = - self._SLIDER_WIDTH
- position = self.getFirstPosition()
- elif name == 'second':
- offset = 0
- position = self.getSecondPosition()
- else:
- raise ValueError('Unknown name')
-
- sliderArea = self.__sliderAreaRect()
-
- maxPos = self.__getCurrentPositionCount() - 1
- xOffset = int((sliderArea.width() - 1) * position / maxPos)
- xPos = sliderArea.left() + xOffset + offset
-
- return qt.QRect(xPos,
- sliderArea.top(),
- self._SLIDER_WIDTH,
- sliderArea.height())
-
- def __drawArea(self):
- return self.rect().adjusted(self._SLIDER_WIDTH, 0,
- -self._SLIDER_WIDTH, 0)
-
- def __sliderAreaRect(self):
- return self.__drawArea().adjusted(self._SLIDER_WIDTH // 2,
- 0,
- -self._SLIDER_WIDTH // 2 + 1,
- 0)
-
- def __pixMapRect(self):
- return self.__sliderAreaRect().adjusted(0,
- self._PIXMAP_VOFFSET,
- -1,
- -self._PIXMAP_VOFFSET)
-
- def paintEvent(self, event):
- painter = qt.QPainter(self)
-
- style = qt.QApplication.style()
-
- area = self.__drawArea()
- if self.__pixmap is not None:
- pixmapRect = self.__pixMapRect()
-
- option = qt.QStyleOptionProgressBar()
- option.initFrom(self)
- option.rect = area
- option.state = (qt.QStyle.State_Enabled if self.isEnabled()
- else qt.QStyle.State_None)
- style.drawControl(qt.QStyle.CE_ProgressBarGroove,
- option,
- painter,
- self)
-
- painter.save()
- pen = painter.pen()
- pen.setWidth(1)
- pen.setColor(qt.Qt.black if self.isEnabled() else qt.Qt.gray)
- painter.setPen(pen)
- painter.drawRect(pixmapRect.adjusted(-1, -1, 0, 1))
- painter.restore()
-
- if self.isEnabled():
- rect = area.adjusted(self._SLIDER_WIDTH // 2,
- self._PIXMAP_VOFFSET,
- -self._SLIDER_WIDTH // 2,
- -self._PIXMAP_VOFFSET + 1)
- painter.drawPixmap(rect,
- self.__pixmap,
- self.__pixmap.rect())
- else:
- option = StyleOptionRangeSlider()
- option.initFrom(self)
- option.rect = area
- option.sliderPosition1 = self.__firstValue
- option.sliderPosition2 = self.__secondValue
- option.handlerRect1 = self.__sliderRect("first")
- option.handlerRect2 = self.__sliderRect("second")
- option.minimum = self.__minValue
- option.maximum = self.__maxValue
- option.state = (qt.QStyle.State_Enabled if self.isEnabled()
- else qt.QStyle.State_None)
- if self.__hoverControl == "groove":
- option.state |= qt.QStyle.State_MouseOver
- elif option.state & qt.QStyle.State_MouseOver:
- option.state ^= qt.QStyle.State_MouseOver
- self.drawRangeSliderBackground(painter, option, self)
-
- # Avoid glitch when moving handles
- hoverControl = self.__moving or self.__hoverControl
-
- for name in ('first', 'second'):
- rect = self.__sliderRect(name)
- option = qt.QStyleOptionButton()
- option.initFrom(self)
- option.icon = self.__icons[name]
- option.iconSize = rect.size() * 0.7
- if hoverControl == name:
- option.state |= qt.QStyle.State_MouseOver
- elif option.state & qt.QStyle.State_MouseOver:
- option.state ^= qt.QStyle.State_MouseOver
- if self.__focus == name:
- option.state |= qt.QStyle.State_HasFocus
- elif option.state & qt.QStyle.State_HasFocus:
- option.state ^= qt.QStyle.State_HasFocus
- option.rect = rect
- style.drawControl(
- qt.QStyle.CE_PushButton, option, painter, self)
-
- def sizeHint(self):
- return qt.QSize(200, self.minimumHeight())
-
- @classmethod
- def drawRangeSliderBackground(cls, painter, option, widget):
- """Draw the background of the RangeSlider widget into the painter.
-
- :param qt.QPainter painter: A painter
- :param StyleOptionRangeSlider option: Options to draw the widget
- :param qt.QWidget: The widget which have to be drawn
- """
- painter.save()
- painter.translate(0.5, 0.5)
-
- backgroundRect = qt.QRect(option.rect)
- if backgroundRect.height() > 8:
- center = backgroundRect.center()
- backgroundRect.setHeight(8)
- backgroundRect.moveCenter(center)
-
- selectedRangeRect = qt.QRect(backgroundRect)
- selectedRangeRect.setLeft(option.handlerRect1.center().x())
- selectedRangeRect.setRight(option.handlerRect2.center().x())
-
- highlight = option.palette.color(qt.QPalette.Highlight)
- activeHighlight = highlight
- selectedOutline = option.palette.color(qt.QPalette.Highlight)
-
- buttonColor = option.palette.button().color()
- val = qt.qGray(buttonColor.rgb())
- buttonColor = buttonColor.lighter(100 + max(1, (180 - val) // 6))
- buttonColor.setHsv(buttonColor.hue(), (buttonColor.saturation() * 3) // 4, buttonColor.value())
-
- grooveColor = qt.QColor()
- grooveColor.setHsv(buttonColor.hue(),
- min(255, (int)(buttonColor.saturation())),
- min(255, (int)(buttonColor.value() * 0.9)))
-
- selectedInnerContrastLine = qt.QColor(255, 255, 255, 30)
-
- outline = option.palette.color(qt.QPalette.Background).darker(140)
- if (option.state & qt.QStyle.State_HasFocus and option.state & qt.QStyle.State_KeyboardFocusChange):
- outline = highlight.darker(125)
- if outline.value() > 160:
- outline.setHsl(highlight.hue(), highlight.saturation(), 160)
-
- # Draw background groove
- painter.setRenderHint(qt.QPainter.Antialiasing, True)
- gradient = qt.QLinearGradient()
- gradient.setStart(backgroundRect.center().x(), backgroundRect.top())
- gradient.setFinalStop(backgroundRect.center().x(), backgroundRect.bottom())
- painter.setPen(qt.QPen(outline))
- gradient.setColorAt(0, grooveColor.darker(110))
- gradient.setColorAt(1, grooveColor.lighter(110))
- painter.setBrush(gradient)
- painter.drawRoundedRect(backgroundRect.adjusted(1, 1, -2, -2), 1, 1)
-
- # Draw slider background for the value
- gradient = qt.QLinearGradient()
- gradient.setStart(selectedRangeRect.center().x(), selectedRangeRect.top())
- gradient.setFinalStop(selectedRangeRect.center().x(), selectedRangeRect.bottom())
- painter.setRenderHint(qt.QPainter.Antialiasing, True)
- painter.setPen(qt.QPen(selectedOutline))
- gradient.setColorAt(0, activeHighlight)
- gradient.setColorAt(1, activeHighlight.lighter(130))
- painter.setBrush(gradient)
- painter.drawRoundedRect(selectedRangeRect.adjusted(1, 1, -2, -2), 1, 1)
- painter.setPen(selectedInnerContrastLine)
- painter.setBrush(qt.Qt.NoBrush)
- painter.drawRoundedRect(selectedRangeRect.adjusted(2, 2, -3, -3), 1, 1)
-
- painter.restore()
diff --git a/silx/gui/widgets/TableWidget.py b/silx/gui/widgets/TableWidget.py
deleted file mode 100644
index 8167fec..0000000
--- a/silx/gui/widgets/TableWidget.py
+++ /dev/null
@@ -1,626 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2004-2017 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-"""This module provides table widgets handling cut, copy and paste for
-multiple cell selections. These actions can be triggered using keyboard
-shortcuts or through a context menu (right-click).
-
-:class:`TableView` is a subclass of :class:`QTableView`. The added features
-are made available to users after a model is added to the widget, using
-:meth:`TableView.setModel`.
-
-:class:`TableWidget` is a subclass of :class:`qt.QTableWidget`, a table view
-with a built-in standard data model. The added features are available as soon as
-the widget is initialized.
-
-The cut, copy and paste actions are implemented as QActions:
-
- - :class:`CopySelectedCellsAction` (*Ctrl+C*)
- - :class:`CopyAllCellsAction`
- - :class:`CutSelectedCellsAction` (*Ctrl+X*)
- - :class:`CutAllCellsAction`
- - :class:`PasteCellsAction` (*Ctrl+V*)
-
-The copy actions are enabled by default. The cut and paste actions must be
-explicitly enabled, by passing parameters ``cut=True, paste=True`` when
-creating the widgets, or later by calling their :meth:`enableCut` and
-:meth:`enablePaste` methods.
-"""
-
-__authors__ = ["P. Knobel"]
-__license__ = "MIT"
-__date__ = "03/07/2017"
-
-
-import sys
-from .. import qt
-
-
-if sys.platform.startswith("win"):
- row_separator = "\r\n"
-else:
- row_separator = "\n"
-
-col_separator = "\t"
-
-
-class CopySelectedCellsAction(qt.QAction):
- """QAction to copy text from selected cells in a :class:`QTableWidget`
- into the clipboard.
-
- If multiple cells are selected, the copied text will be a concatenation
- of the texts in all selected cells, tabulated with tabulation and
- newline characters.
-
- If the cells are sparsely selected, the structure is preserved by
- representing the unselected cells as empty strings in between two
- tabulation characters.
- Beware of pasting this data in another table widget, because depending
- on how the paste is implemented, the empty cells may cause data in the
- target table to be deleted, even though you didn't necessarily select the
- corresponding cell in the origin table.
-
- :param table: :class:`QTableView` to which this action belongs.
- """
- def __init__(self, table):
- if not isinstance(table, qt.QTableView):
- raise ValueError('CopySelectedCellsAction must be initialised ' +
- 'with a QTableWidget.')
- super(CopySelectedCellsAction, self).__init__(table)
- self.setText("Copy selection")
- self.setToolTip("Copy selected cells into the clipboard.")
- self.setShortcut(qt.QKeySequence.Copy)
- self.setShortcutContext(qt.Qt.WidgetShortcut)
- self.triggered.connect(self.copyCellsToClipboard)
- self.table = table
- self.cut = False
- """:attr:`cut` can be set to True by classes inheriting this action,
- to do a cut action."""
-
- def copyCellsToClipboard(self):
- """Concatenate the text content of all selected cells into a string
- using tabulations and newlines to keep the table structure.
- Put this text into the clipboard.
- """
- selected_idx = self.table.selectedIndexes()
- if not selected_idx:
- return
- selected_idx_tuples = [(idx.row(), idx.column()) for idx in selected_idx]
-
- selected_rows = [idx[0] for idx in selected_idx_tuples]
- selected_columns = [idx[1] for idx in selected_idx_tuples]
-
- data_model = self.table.model()
-
- copied_text = ""
- for row in range(min(selected_rows), max(selected_rows) + 1):
- for col in range(min(selected_columns), max(selected_columns) + 1):
- index = data_model.index(row, col)
- cell_text = data_model.data(index)
- flags = data_model.flags(index)
-
- if (row, col) in selected_idx_tuples and cell_text is not None:
- copied_text += cell_text
- if self.cut and (flags & qt.Qt.ItemIsEditable):
- data_model.setData(index, "")
- copied_text += col_separator
- # remove the right-most tabulation
- copied_text = copied_text[:-len(col_separator)]
- # add a newline
- copied_text += row_separator
- # remove final newline
- copied_text = copied_text[:-len(row_separator)]
-
- # put this text into clipboard
- qapp = qt.QApplication.instance()
- qapp.clipboard().setText(copied_text)
-
-
-class CopyAllCellsAction(qt.QAction):
- """QAction to copy text from all cells in a :class:`QTableWidget`
- into the clipboard.
-
- The copied text will be a concatenation
- of the texts in all cells, tabulated with tabulation and
- newline characters.
-
- :param table: :class:`QTableView` to which this action belongs.
- """
- def __init__(self, table):
- if not isinstance(table, qt.QTableView):
- raise ValueError('CopyAllCellsAction must be initialised ' +
- 'with a QTableWidget.')
- super(CopyAllCellsAction, self).__init__(table)
- self.setText("Copy all")
- self.setToolTip("Copy all cells into the clipboard.")
- self.triggered.connect(self.copyCellsToClipboard)
- self.table = table
- self.cut = False
-
- def copyCellsToClipboard(self):
- """Concatenate the text content of all cells into a string
- using tabulations and newlines to keep the table structure.
- Put this text into the clipboard.
- """
- data_model = self.table.model()
- copied_text = ""
- for row in range(data_model.rowCount()):
- for col in range(data_model.columnCount()):
- index = data_model.index(row, col)
- cell_text = data_model.data(index)
- flags = data_model.flags(index)
- if cell_text is not None:
- copied_text += cell_text
- if self.cut and (flags & qt.Qt.ItemIsEditable):
- data_model.setData(index, "")
- copied_text += col_separator
- # remove the right-most tabulation
- copied_text = copied_text[:-len(col_separator)]
- # add a newline
- copied_text += row_separator
- # remove final newline
- copied_text = copied_text[:-len(row_separator)]
-
- # put this text into clipboard
- qapp = qt.QApplication.instance()
- qapp.clipboard().setText(copied_text)
-
-
-class CutSelectedCellsAction(CopySelectedCellsAction):
- """QAction to cut text from selected cells in a :class:`QTableWidget`
- into the clipboard.
-
- The text is deleted from the original table widget
- (use :class:`CopySelectedCellsAction` to preserve the original data).
-
- If multiple cells are selected, the cut text will be a concatenation
- of the texts in all selected cells, tabulated with tabulation and
- newline characters.
-
- If the cells are sparsely selected, the structure is preserved by
- representing the unselected cells as empty strings in between two
- tabulation characters.
- Beware of pasting this data in another table widget, because depending
- on how the paste is implemented, the empty cells may cause data in the
- target table to be deleted, even though you didn't necessarily select the
- corresponding cell in the origin table.
-
- :param table: :class:`QTableView` to which this action belongs."""
- def __init__(self, table):
- super(CutSelectedCellsAction, self).__init__(table)
- self.setText("Cut selection")
- self.setShortcut(qt.QKeySequence.Cut)
- self.setShortcutContext(qt.Qt.WidgetShortcut)
- # cutting is already implemented in CopySelectedCellsAction (but
- # it is disabled), we just need to enable it
- self.cut = True
-
-
-class CutAllCellsAction(CopyAllCellsAction):
- """QAction to cut text from all cells in a :class:`QTableWidget`
- into the clipboard.
-
- The text is deleted from the original table widget
- (use :class:`CopyAllCellsAction` to preserve the original data).
-
- The cut text will be a concatenation
- of the texts in all cells, tabulated with tabulation and
- newline characters.
-
- :param table: :class:`QTableView` to which this action belongs."""
- def __init__(self, table):
- super(CutAllCellsAction, self).__init__(table)
- self.setText("Cut all")
- self.setToolTip("Cut all cells into the clipboard.")
- self.cut = True
-
-
-def _parseTextAsTable(text, row_separator=row_separator, col_separator=col_separator):
- """Parse text into list of lists (2D sequence).
-
- The input text must be tabulated using tabulation characters and
- newlines to separate columns and rows.
-
- :param text: text to be parsed
- :param record_separator: String, or single character, to be interpreted
- as a record/row separator.
- :param field_separator: String, or single character, to be interpreted
- as a field/column separator.
- :return: 2D sequence of strings
- """
- rows = text.split(row_separator)
- table_data = [row.split(col_separator) for row in rows]
- return table_data
-
-
-class PasteCellsAction(qt.QAction):
- """QAction to paste text from the clipboard into the table.
-
- If the text contains tabulations and
- newlines, they are interpreted as column and row separators.
- In such a case, the text is split into multiple texts to be pasted
- into multiple cells.
-
- If a cell content is an empty string in the original text, it is
- ignored: the destination cell's text will not be deleted.
-
- :param table: :class:`QTableView` to which this action belongs.
- """
- def __init__(self, table):
- if not isinstance(table, qt.QTableView):
- raise ValueError('PasteCellsAction must be initialised ' +
- 'with a QTableWidget.')
- super(PasteCellsAction, self).__init__(table)
- self.table = table
- self.setText("Paste")
- self.setShortcut(qt.QKeySequence.Paste)
- self.setShortcutContext(qt.Qt.WidgetShortcut)
- self.setToolTip("Paste data. The selected cell is the top-left" +
- "corner of the paste area.")
- self.triggered.connect(self.pasteCellFromClipboard)
-
- def pasteCellFromClipboard(self):
- """Paste text from clipboard into the table.
-
- :return: *True* in case of success, *False* if pasting data failed.
- """
- selected_idx = self.table.selectedIndexes()
- if len(selected_idx) != 1:
- msgBox = qt.QMessageBox(parent=self.table)
- msgBox.setText("A single cell must be selected to paste data")
- msgBox.exec_()
- return False
-
- data_model = self.table.model()
-
- selected_row = selected_idx[0].row()
- selected_col = selected_idx[0].column()
-
- qapp = qt.QApplication.instance()
- clipboard_text = qapp.clipboard().text()
- table_data = _parseTextAsTable(clipboard_text)
-
- protected_cells = 0
- out_of_range_cells = 0
-
- # paste table data into cells, using selected cell as origin
- for row_offset in range(len(table_data)):
- for col_offset in range(len(table_data[row_offset])):
- target_row = selected_row + row_offset
- target_col = selected_col + col_offset
-
- if target_row >= data_model.rowCount() or\
- target_col >= data_model.columnCount():
- out_of_range_cells += 1
- continue
-
- index = data_model.index(target_row, target_col)
- flags = data_model.flags(index)
-
- # ignore empty strings
- if table_data[row_offset][col_offset] != "":
- if not flags & qt.Qt.ItemIsEditable:
- protected_cells += 1
- continue
- data_model.setData(index, table_data[row_offset][col_offset])
- # item.setText(table_data[row_offset][col_offset])
-
- if protected_cells or out_of_range_cells:
- msgBox = qt.QMessageBox(parent=self.table)
- msg = "Some data could not be inserted, "
- msg += "due to out-of-range or write-protected cells."
- msgBox.setText(msg)
- msgBox.exec_()
- return False
- return True
-
-
-class CopySingleCellAction(qt.QAction):
- """QAction to copy text from a single cell in a modified
- :class:`QTableWidget`.
-
- This action relies on the fact that the text in the last clicked cell
- are stored in :attr:`_last_cell_clicked` of the modified widget.
-
- In most cases, :class:`CopySelectedCellsAction` handles single cells,
- but if the selection mode of the widget has been set to NoSelection
- it is necessary to use this class instead.
-
- :param table: :class:`QTableView` to which this action belongs.
- """
- def __init__(self, table):
- if not isinstance(table, qt.QTableView):
- raise ValueError('CopySingleCellAction must be initialised ' +
- 'with a QTableWidget.')
- super(CopySingleCellAction, self).__init__(table)
- self.setText("Copy cell")
- self.setToolTip("Copy cell content into the clipboard.")
- self.triggered.connect(self.copyCellToClipboard)
- self.table = table
-
- def copyCellToClipboard(self):
- """
- """
- cell_text = self.table._text_last_cell_clicked
- if cell_text is None:
- return
-
- # put this text into clipboard
- qapp = qt.QApplication.instance()
- qapp.clipboard().setText(cell_text)
-
-
-class TableWidget(qt.QTableWidget):
- """:class:`QTableWidget` with a context menu displaying up to 5 actions:
-
- - :class:`CopySelectedCellsAction`
- - :class:`CopyAllCellsAction`
- - :class:`CutSelectedCellsAction`
- - :class:`CutAllCellsAction`
- - :class:`PasteCellsAction`
-
- These actions interact with the clipboard and can be used to copy data
- to or from an external application, or another widget.
-
- The cut and paste actions are disabled by default, due to the risk of
- overwriting data (no *Undo* action is available). Use :meth:`enablePaste`
- and :meth:`enableCut` to activate them.
-
- .. image:: img/TableWidget.png
-
- :param parent: Parent QWidget
- :param bool cut: Enable cut action
- :param bool paste: Enable paste action
- """
- def __init__(self, parent=None, cut=False, paste=False):
- super(TableWidget, self).__init__(parent)
- self._text_last_cell_clicked = None
-
- self.copySelectedCellsAction = CopySelectedCellsAction(self)
- self.copyAllCellsAction = CopyAllCellsAction(self)
- self.copySingleCellAction = None
- self.pasteCellsAction = None
- self.cutSelectedCellsAction = None
- self.cutAllCellsAction = None
-
- self.addAction(self.copySelectedCellsAction)
- self.addAction(self.copyAllCellsAction)
- if cut:
- self.enableCut()
- if paste:
- self.enablePaste()
-
- self.setContextMenuPolicy(qt.Qt.ActionsContextMenu)
-
- def mousePressEvent(self, event):
- item = self.itemAt(event.pos())
- if item is not None:
- self._text_last_cell_clicked = item.text()
- super(TableWidget, self).mousePressEvent(event)
-
- def enablePaste(self):
- """Enable paste action, to paste data from the clipboard into the
- table.
-
- .. warning::
-
- This action can cause data to be overwritten.
- There is currently no *Undo* action to retrieve lost data.
- """
- self.pasteCellsAction = PasteCellsAction(self)
- self.addAction(self.pasteCellsAction)
-
- def enableCut(self):
- """Enable cut action.
-
- .. warning::
-
- This action can cause data to be deleted.
- There is currently no *Undo* action to retrieve lost data."""
- self.cutSelectedCellsAction = CutSelectedCellsAction(self)
- self.cutAllCellsAction = CutAllCellsAction(self)
- self.addAction(self.cutSelectedCellsAction)
- self.addAction(self.cutAllCellsAction)
-
- def setSelectionMode(self, mode):
- """Overloaded from QTableWidget to disable cut/copy selection
- actions in case mode is NoSelection
-
- :param mode:
- :return:
- """
- if mode == qt.QTableView.NoSelection:
- self.copySelectedCellsAction.setVisible(False)
- self.copySelectedCellsAction.setEnabled(False)
- if self.cutSelectedCellsAction is not None:
- self.cutSelectedCellsAction.setVisible(False)
- self.cutSelectedCellsAction.setEnabled(False)
- if self.copySingleCellAction is None:
- self.copySingleCellAction = CopySingleCellAction(self)
- self.insertAction(self.copySelectedCellsAction, # before first action
- self.copySingleCellAction)
- self.copySingleCellAction.setVisible(True)
- self.copySingleCellAction.setEnabled(True)
- else:
- self.copySelectedCellsAction.setVisible(True)
- self.copySelectedCellsAction.setEnabled(True)
- if self.cutSelectedCellsAction is not None:
- self.cutSelectedCellsAction.setVisible(True)
- self.cutSelectedCellsAction.setEnabled(True)
- if self.copySingleCellAction is not None:
- self.copySingleCellAction.setVisible(False)
- self.copySingleCellAction.setEnabled(False)
- super(TableWidget, self).setSelectionMode(mode)
-
-
-class TableView(qt.QTableView):
- """:class:`QTableView` with a context menu displaying up to 5 actions:
-
- - :class:`CopySelectedCellsAction`
- - :class:`CopyAllCellsAction`
- - :class:`CutSelectedCellsAction`
- - :class:`CutAllCellsAction`
- - :class:`PasteCellsAction`
-
- These actions interact with the clipboard and can be used to copy data
- to or from an external application, or another widget.
-
- The cut and paste actions are disabled by default, due to the risk of
- overwriting data (no *Undo* action is available). Use :meth:`enablePaste`
- and :meth:`enableCut` to activate them.
-
- .. note::
-
- These actions will be available only after a model is associated
- with this view, using :meth:`setModel`.
-
- :param parent: Parent QWidget
- :param bool cut: Enable cut action
- :param bool paste: Enable paste action
- """
- def __init__(self, parent=None, cut=False, paste=False):
- super(TableView, self).__init__(parent)
- self._text_last_cell_clicked = None
-
- self.cut = cut
- self.paste = paste
-
- self.copySelectedCellsAction = None
- self.copyAllCellsAction = None
- self.copySingleCellAction = None
- self.pasteCellsAction = None
- self.cutSelectedCellsAction = None
- self.cutAllCellsAction = None
-
- def mousePressEvent(self, event):
- qindex = self.indexAt(event.pos())
- if self.copyAllCellsAction is not None: # model was set
- self._text_last_cell_clicked = self.model().data(qindex)
- super(TableView, self).mousePressEvent(event)
-
- def setModel(self, model):
- """Set the data model for the table view, activate the actions
- and the context menu.
-
- :param model: :class:`qt.QAbstractItemModel` object
- """
- super(TableView, self).setModel(model)
-
- self.copySelectedCellsAction = CopySelectedCellsAction(self)
- self.copyAllCellsAction = CopyAllCellsAction(self)
- self.addAction(self.copySelectedCellsAction)
- self.addAction(self.copyAllCellsAction)
- if self.cut:
- self.enableCut()
- if self.paste:
- self.enablePaste()
-
- self.setContextMenuPolicy(qt.Qt.ActionsContextMenu)
-
- def enablePaste(self):
- """Enable paste action, to paste data from the clipboard into the
- table.
-
- .. warning::
-
- This action can cause data to be overwritten.
- There is currently no *Undo* action to retrieve lost data.
- """
- self.pasteCellsAction = PasteCellsAction(self)
- self.addAction(self.pasteCellsAction)
-
- def enableCut(self):
- """Enable cut action.
-
- .. warning::
-
- This action can cause data to be deleted.
- There is currently no *Undo* action to retrieve lost data.
- """
- self.cutSelectedCellsAction = CutSelectedCellsAction(self)
- self.cutAllCellsAction = CutAllCellsAction(self)
- self.addAction(self.cutSelectedCellsAction)
- self.addAction(self.cutAllCellsAction)
-
- def addAction(self, action):
- # ensure the actions are not added multiple times:
- # compare action type and parent widget with those of existing actions
- for existing_action in self.actions():
- if type(action) == type(existing_action):
- if hasattr(action, "table") and\
- action.table is existing_action.table:
- return None
- super(TableView, self).addAction(action)
-
- def setSelectionMode(self, mode):
- """Overloaded from QTableView to disable cut/copy selection
- actions in case mode is NoSelection
-
- :param mode:
- :return:
- """
- if mode == qt.QTableView.NoSelection:
- self.copySelectedCellsAction.setVisible(False)
- self.copySelectedCellsAction.setEnabled(False)
- if self.cutSelectedCellsAction is not None:
- self.cutSelectedCellsAction.setVisible(False)
- self.cutSelectedCellsAction.setEnabled(False)
- if self.copySingleCellAction is None:
- self.copySingleCellAction = CopySingleCellAction(self)
- self.insertAction(self.copySelectedCellsAction, # before first action
- self.copySingleCellAction)
- self.copySingleCellAction.setVisible(True)
- self.copySingleCellAction.setEnabled(True)
- else:
- self.copySelectedCellsAction.setVisible(True)
- self.copySelectedCellsAction.setEnabled(True)
- if self.cutSelectedCellsAction is not None:
- self.cutSelectedCellsAction.setVisible(True)
- self.cutSelectedCellsAction.setEnabled(True)
- if self.copySingleCellAction is not None:
- self.copySingleCellAction.setVisible(False)
- self.copySingleCellAction.setEnabled(False)
- super(TableView, self).setSelectionMode(mode)
-
-
-if __name__ == "__main__":
- app = qt.QApplication([])
-
- tablewidget = TableWidget()
- tablewidget.setWindowTitle("TableWidget")
- tablewidget.setColumnCount(10)
- tablewidget.setRowCount(7)
- tablewidget.enableCut()
- tablewidget.enablePaste()
- tablewidget.show()
-
- tableview = TableView(cut=True, paste=True)
- tableview.setWindowTitle("TableView")
- model = qt.QStandardItemModel()
- model.setColumnCount(10)
- model.setRowCount(7)
- tableview.setModel(model)
- tableview.show()
-
- app.exec_()
diff --git a/silx/gui/widgets/ThreadPoolPushButton.py b/silx/gui/widgets/ThreadPoolPushButton.py
deleted file mode 100644
index 949b6ef..0000000
--- a/silx/gui/widgets/ThreadPoolPushButton.py
+++ /dev/null
@@ -1,238 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2016-2018 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-"""ThreadPoolPushButton module
-"""
-
-__authors__ = ["V. Valls"]
-__license__ = "MIT"
-__date__ = "13/10/2016"
-
-import logging
-from .. import qt
-from .WaitingPushButton import WaitingPushButton
-
-
-_logger = logging.getLogger(__name__)
-
-
-class _Wrapper(qt.QRunnable):
- """Wrapper to allow to call a function into a `QThreadPool` and
- sending signals during the life cycle of the object"""
-
- def __init__(self, signalHolder, function, args, kwargs):
- """Constructor"""
- super(_Wrapper, self).__init__()
- self.__signalHolder = signalHolder
- self.__callable = function
- self.__args = args
- self.__kwargs = kwargs
-
- def run(self):
- holder = self.__signalHolder
- holder.started.emit()
- try:
- result = self.__callable(*self.__args, **self.__kwargs)
- holder.succeeded.emit(result)
- except Exception as e:
- module = self.__callable.__module__
- name = self.__callable.__name__
- _logger.error("Error while executing callable %s.%s.", module, name, exc_info=True)
- holder.failed.emit(e)
- finally:
- holder.finished.emit()
- holder._sigReleaseRunner.emit(self)
-
- def autoDelete(self):
- """Returns true to ask the QThreadPool to manage the life cycle of
- this QRunner."""
- return True
-
-
-class ThreadPoolPushButton(WaitingPushButton):
- """
- ThreadPoolPushButton provides a simple push button to execute
- a threaded task with user feedback when the task is running.
-
- The task can be defined with the method `setCallable`. It takes a python
- function and arguments as parameters.
-
- WARNING: This task is run in a separate thread.
-
- Everytime the button is pushed a new runner is created to execute the
- function with defined arguments. An animated waiting icon is displayed
- to show the activity. By default the button is disabled when an execution
- is requested. This behaviour can be disabled by using
- `setDisabledWhenWaiting`.
-
- When the button is clicked a `beforeExecuting` signal is sent from the
- Qt main thread. Then the task is started in a thread pool and the following
- signals are emitted from the thread pool. Right before calling the
- registered callable, the widget emits a `started` signal.
- When the task ends, its result is emitted by the `succeeded` signal, but
- if it fails the signal `failed` is emitted with the resulting exception.
- At the end, the `finished` signal is emitted.
-
- The task can be programatically executed by using `executeCallable`.
-
- >>> # Compute a value
- >>> import math
- >>> button = ThreadPoolPushButton(text="Compute 2^16")
- >>> button.setCallable(math.pow, 2, 16)
- >>> button.succeeded.connect(print) # python3
-
- .. image:: img/ThreadPoolPushButton.png
-
- >>> # Compute a wrong value
- >>> import math
- >>> button = ThreadPoolPushButton(text="Compute sqrt(-1)")
- >>> button.setCallable(math.sqrt, -1)
- >>> button.failed.connect(print) # python3
- """
-
- def __init__(self, parent=None, text=None, icon=None):
- """Constructor
-
- :param str text: Text displayed on the button
- :param qt.QIcon icon: Icon displayed on the button
- :param qt.QWidget parent: Parent of the widget
- """
- WaitingPushButton.__init__(self, parent=parent, text=text, icon=icon)
- self.__callable = None
- self.__args = None
- self.__kwargs = None
- self.__runnerCount = 0
- self.__runnerSet = set([])
- self.clicked.connect(self.executeCallable)
- self.finished.connect(self.__runnerFinished)
- self._sigReleaseRunner.connect(self.__releaseRunner)
-
- beforeExecuting = qt.Signal()
- """Signal emitted just before execution of the callable by the main Qt
- thread. In synchronous mode (direct mode), it can be used to define
- dynamically `setCallable`, or to execute something in the Qt thread before
- the execution, or both."""
-
- started = qt.Signal()
- """Signal emitted from the thread pool when the defined callable is
- started.
-
- WARNING: This signal is emitted from the thread performing the task, and
- might be received after the registered callable has been called. If you
- want to perform some initialisation or set the callable to run, use the
- `beforeExecuting` signal instead.
- """
-
- finished = qt.Signal()
- """Signal emitted from the thread pool when the defined callable is
- finished"""
-
- succeeded = qt.Signal(object)
- """Signal emitted from the thread pool when the callable exit with a
- success.
-
- The parameter of the signal is the result returned by the callable.
- """
-
- failed = qt.Signal(object)
- """Signal emitted emitted from the thread pool when the callable raises an
- exception.
-
- The parameter of the signal is the raised exception.
- """
-
- _sigReleaseRunner = qt.Signal(object)
- """Callback to release runners"""
-
- def __runnerStarted(self):
- """Called when a runner is started.
-
- Count the number of executed tasks to change the state of the widget.
- """
- self.__runnerCount += 1
- if self.__runnerCount > 0:
- self.wait()
-
- def __runnerFinished(self):
- """Called when a runner is finished.
-
- Count the number of executed tasks to change the state of the widget.
- """
- self.__runnerCount -= 1
- if self.__runnerCount <= 0:
- self.stopWaiting()
-
- @qt.Slot()
- def executeCallable(self):
- """Execute the defined callable in QThreadPool.
-
- First emit a `beforeExecuting` signal.
- If callable is not defined, nothing append.
- If a callable is defined, it will be started
- as a new thread using the `QThreadPool` system. At start of the thread
- the `started` will be emitted. When the callable returns a result it
- is emitted by the `succeeded` signal. If the callable fail, the signal
- `failed` is emitted with the resulting exception. Then the `finished`
- signal is emitted.
- """
- self.beforeExecuting.emit()
- if self.__callable is None:
- return
- self.__runnerStarted()
- runner = self._createRunner(self.__callable, self.__args, self.__kwargs)
- qt.silxGlobalThreadPool().start(runner)
- self.__runnerSet.add(runner)
-
- def __releaseRunner(self, runner):
- self.__runnerSet.remove(runner)
-
- def hasPendingOperations(self):
- return len(self.__runnerSet) > 0
-
- def _createRunner(self, function, args, kwargs):
- """Create a QRunnable from a callable object.
-
- :param callable function: A callable Python object.
- :param List args: List of arguments to call the function.
- :param dict kwargs: Dictionary of arguments used to call the function.
- :rtpye: qt.QRunnable
- """
- runnable = _Wrapper(self, function, args, kwargs)
- return runnable
-
- def setCallable(self, function, *args, **kwargs):
- """Define a callable which will be executed on QThreadPool everytime
- the button is clicked.
-
- To retrieve the results, connect to the `succeeded` signal.
-
- WARNING: The callable will be called in a separate thread.
-
- :param callable function: A callable Python object
- :param List args: List of arguments to call the function.
- :param dict kwargs: Dictionary of arguments used to call the function.
- """
- self.__callable = function
- self.__args = args
- self.__kwargs = kwargs
diff --git a/silx/gui/widgets/UrlSelectionTable.py b/silx/gui/widgets/UrlSelectionTable.py
deleted file mode 100644
index fb15edd..0000000
--- a/silx/gui/widgets/UrlSelectionTable.py
+++ /dev/null
@@ -1,172 +0,0 @@
-# /*##########################################################################
-# Copyright (C) 2017-2021 European Synchrotron Radiation Facility
-#
-# This file is part of the PyMca X-ray Fluorescence Toolkit developed at
-# the ESRF by the Software group.
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-#############################################################################*/
-"""Some widget construction to check if a sample moved"""
-
-__author__ = ["H. Payno"]
-__license__ = "MIT"
-__date__ = "19/03/2018"
-
-from silx.gui import qt
-from collections import OrderedDict
-from silx.gui.widgets.TableWidget import TableWidget
-from silx.io.url import DataUrl
-import functools
-import logging
-import os
-
-logger = logging.getLogger(__name__)
-
-
-class UrlSelectionTable(TableWidget):
- """Table used to select the color channel to be displayed for each"""
-
- COLUMS_INDEX = OrderedDict([
- ('url', 0),
- ('img A', 1),
- ('img B', 2),
- ])
-
- sigImageAChanged = qt.Signal(str)
- """Signal emitted when the image A change. Param is the image url path"""
-
- sigImageBChanged = qt.Signal(str)
- """Signal emitted when the image B change. Param is the image url path"""
-
- def __init__(self, parent=None):
- TableWidget.__init__(self, parent)
- self.clear()
-
- def clear(self):
- qt.QTableWidget.clear(self)
- self.setRowCount(0)
- self.setColumnCount(len(self.COLUMS_INDEX))
- self.setHorizontalHeaderLabels(list(self.COLUMS_INDEX.keys()))
- self.verticalHeader().hide()
- if hasattr(self.horizontalHeader(), 'setSectionResizeMode'): # Qt5
- self.horizontalHeader().setSectionResizeMode(0,
- qt.QHeaderView.Stretch)
- else: # Qt4
- self.horizontalHeader().setResizeMode(0, qt.QHeaderView.Stretch)
-
- self.setSortingEnabled(True)
- self._checkBoxes = {}
-
- def setUrls(self, urls: list) -> None:
- """
-
- :param urls: urls to be displayed
- """
- for url in urls:
- self.addUrl(url=url)
-
- def addUrl(self, url, **kwargs):
- """
-
- :param url:
- :param args:
- :return: index of the created items row
- :rtype int
- """
- assert isinstance(url, DataUrl)
- row = self.rowCount()
- self.setRowCount(row + 1)
-
- _item = qt.QTableWidgetItem()
- _item.setText(os.path.basename(url.path()))
- _item.setFlags(qt.Qt.ItemIsEnabled | qt.Qt.ItemIsSelectable)
- self.setItem(row, self.COLUMS_INDEX['url'], _item)
-
- widgetImgA = qt.QRadioButton(parent=self)
- widgetImgA.setAutoExclusive(False)
- self.setCellWidget(row, self.COLUMS_INDEX['img A'], widgetImgA)
- callbackImgA = functools.partial(self._activeImgAChanged, url.path())
- widgetImgA.toggled.connect(callbackImgA)
-
- widgetImgB = qt.QRadioButton(parent=self)
- widgetImgA.setAutoExclusive(False)
- self.setCellWidget(row, self.COLUMS_INDEX['img B'], widgetImgB)
- callbackImgB = functools.partial(self._activeImgBChanged, url.path())
- widgetImgB.toggled.connect(callbackImgB)
-
- self._checkBoxes[url.path()] = {'img A': widgetImgA,
- 'img B': widgetImgB}
- self.resizeColumnsToContents()
- return row
-
- def _activeImgAChanged(self, name):
- self._updatecheckBoxes('img A', name)
- self.sigImageAChanged.emit(name)
-
- def _activeImgBChanged(self, name):
- self._updatecheckBoxes('img B', name)
- self.sigImageBChanged.emit(name)
-
- def _updatecheckBoxes(self, whichImg, name):
- assert name in self._checkBoxes
- assert whichImg in self._checkBoxes[name]
- if self._checkBoxes[name][whichImg].isChecked():
- for radioUrl in self._checkBoxes:
- if radioUrl != name:
- self._checkBoxes[radioUrl][whichImg].blockSignals(True)
- self._checkBoxes[radioUrl][whichImg].setChecked(False)
- self._checkBoxes[radioUrl][whichImg].blockSignals(False)
-
- def getSelection(self):
- """
-
- :return: url selected for img A and img B.
- """
- imgA = imgB = None
- for radioUrl in self._checkBoxes:
- if self._checkBoxes[radioUrl]['img A'].isChecked():
- imgA = radioUrl
- if self._checkBoxes[radioUrl]['img B'].isChecked():
- imgB = radioUrl
- return imgA, imgB
-
- def setSelection(self, url_img_a, url_img_b):
- """
-
- :param ddict: key: image url, values: list of active channels
- """
- for radioUrl in self._checkBoxes:
- for img in ('img A', 'img B'):
- self._checkBoxes[radioUrl][img].blockSignals(True)
- self._checkBoxes[radioUrl][img].setChecked(False)
- self._checkBoxes[radioUrl][img].blockSignals(False)
-
- self._checkBoxes[radioUrl][img].blockSignals(True)
- self._checkBoxes[url_img_a]['img A'].setChecked(True)
- self._checkBoxes[radioUrl][img].blockSignals(False)
-
- self._checkBoxes[radioUrl][img].blockSignals(True)
- self._checkBoxes[url_img_b]['img B'].setChecked(True)
- self._checkBoxes[radioUrl][img].blockSignals(False)
- self.sigImageAChanged.emit(url_img_a)
- self.sigImageBChanged.emit(url_img_b)
-
- def removeUrl(self, url):
- raise NotImplementedError("")
diff --git a/silx/gui/widgets/WaitingPushButton.py b/silx/gui/widgets/WaitingPushButton.py
deleted file mode 100644
index 499de1a..0000000
--- a/silx/gui/widgets/WaitingPushButton.py
+++ /dev/null
@@ -1,245 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2004-2017 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-"""WaitingPushButton module
-"""
-
-__authors__ = ["V. Valls"]
-__license__ = "MIT"
-__date__ = "26/04/2017"
-
-from .. import qt
-from .. import icons
-
-
-class WaitingPushButton(qt.QPushButton):
- """Button which allows to display a waiting status when, for example,
- something is still computing.
-
- The component is graphically disabled when it is in waiting. Then we
- overwrite the enabled method to dissociate the 2 concepts:
- graphically enabled/disabled, and enabled/disabled
-
- .. image:: img/WaitingPushButton.png
- """
-
- def __init__(self, parent=None, text=None, icon=None):
- """Constructor
-
- :param str text: Text displayed on the button
- :param qt.QIcon icon: Icon displayed on the button
- :param qt.QWidget parent: Parent of the widget
- """
- if icon is not None:
- qt.QPushButton.__init__(self, icon, text, parent)
- elif text is not None:
- qt.QPushButton.__init__(self, text, parent)
- else:
- qt.QPushButton.__init__(self, parent)
-
- self.__waiting = False
- self.__enabled = True
- self.__icon = icon
- self.__disabled_when_waiting = True
- self.__waitingIcon = icons.getWaitIcon()
-
- def sizeHint(self):
- """Returns the recommended size for the widget.
-
- This implementation of the recommended size always consider there is an
- icon. In this way it avoid to update the layout when the waiting icon
- is displayed.
- """
- self.ensurePolished()
-
- w = 0
- h = 0
-
- opt = qt.QStyleOptionButton()
- self.initStyleOption(opt)
-
- # Content with icon
- # no condition, assume that there is an icon to avoid blinking
- # when the widget switch to waiting state
- ih = opt.iconSize.height()
- iw = opt.iconSize.width() + 4
- w += iw
- h = max(h, ih)
-
- # Content with text
- text = self.text()
- isEmpty = text == ""
- if isEmpty:
- text = "XXXX"
- fm = self.fontMetrics()
- textSize = fm.size(qt.Qt.TextShowMnemonic, text)
- if not isEmpty or w == 0:
- w += textSize.width()
- if not isEmpty or h == 0:
- h = max(h, textSize.height())
-
- # Content with menu indicator
- opt.rect.setSize(qt.QSize(w, h)) # PM_MenuButtonIndicator depends on the height
- if self.menu() is not None:
- w += self.style().pixelMetric(qt.QStyle.PM_MenuButtonIndicator, opt, self)
-
- contentSize = qt.QSize(w, h)
- if qt.qVersion().startswith("4.8."):
- # On PyQt4/PySide the method QCommonStyle sizeFromContents returns
- # different size when the widget provides an icon or not.
- # In Qt5 there is not this problem.
- opt.icon = qt.QIcon()
- sizeHint = self.style().sizeFromContents(qt.QStyle.CT_PushButton, opt, contentSize, self)
- sizeHint = sizeHint.expandedTo(qt.QApplication.globalStrut())
- return sizeHint
-
- def setDisabledWhenWaiting(self, isDisabled):
- """Enable or disable the auto disable behaviour when the button is waiting.
-
- :param bool isDisabled: Enable the auto-disable behaviour
- """
- if self.__disabled_when_waiting == isDisabled:
- return
- self.__disabled_when_waiting = isDisabled
- self.__updateVisibleEnabled()
-
- def isDisabledWhenWaiting(self):
- """Returns true if the button is auto disabled when it is waiting.
-
- :rtype: bool
- """
- return self.__disabled_when_waiting
-
- disabledWhenWaiting = qt.Property(bool, isDisabledWhenWaiting, setDisabledWhenWaiting)
- """Property to enable/disable the auto disabled state when the button is waiting."""
-
- def __setWaitingIcon(self, icon):
- """Called when the waiting icon is updated. It is called every frames
- of the animation.
-
- :param qt.QIcon icon: The new waiting icon
- """
- qt.QPushButton.setIcon(self, icon)
-
- def setIcon(self, icon):
- """Set the button icon. If the button is waiting, the icon is not
- visible directly, but will be visible when the waiting state will be
- removed.
-
- :param qt.QIcon icon: An icon
- """
- self.__icon = icon
- self.__updateVisibleIcon()
-
- def getIcon(self):
- """Returns the icon set to the button. If the widget is waiting
- it is not returning the visible icon, but the one requested by
- the application (the one displayed when the widget is not in
- waiting state).
-
- :rtype: qt.QIcon
- """
- return self.__icon
-
- icon = qt.Property(qt.QIcon, getIcon, setIcon)
- """Property providing access to the icon."""
-
- def __updateVisibleIcon(self):
- """Update the visible icon according to the state of the widget."""
- if not self.isWaiting():
- icon = self.__icon
- else:
- icon = self.__waitingIcon.currentIcon()
- if icon is None:
- icon = qt.QIcon()
- qt.QPushButton.setIcon(self, icon)
-
- def setEnabled(self, enabled):
- """Set the enabled state of the widget.
-
- :param bool enabled: The enabled state
- """
- if self.__enabled == enabled:
- return
- self.__enabled = enabled
- self.__updateVisibleEnabled()
-
- def isEnabled(self):
- """Returns the enabled state of the widget.
-
- :rtype: bool
- """
- return self.__enabled
-
- enabled = qt.Property(bool, isEnabled, setEnabled)
- """Property providing access to the enabled state of the widget"""
-
- def __updateVisibleEnabled(self):
- """Update the visible enabled state according to the state of the
- widget."""
- if self.__disabled_when_waiting:
- enabled = not self.isWaiting() and self.__enabled
- else:
- enabled = self.__enabled
- qt.QPushButton.setEnabled(self, enabled)
-
- def setWaiting(self, waiting):
- """Set the waiting state of the widget.
-
- :param bool waiting: Requested state"""
- if self.__waiting == waiting:
- return
- self.__waiting = waiting
-
- if self.__waiting:
- self.__waitingIcon.register(self)
- self.__waitingIcon.iconChanged.connect(self.__setWaitingIcon)
- else:
- # unregister only if the object is registred
- self.__waitingIcon.unregister(self)
- self.__waitingIcon.iconChanged.disconnect(self.__setWaitingIcon)
-
- self.__updateVisibleEnabled()
- self.__updateVisibleIcon()
-
- def isWaiting(self):
- """Returns true if the widget is in waiting state.
-
- :rtype: bool"""
- return self.__waiting
-
- @qt.Slot()
- def wait(self):
- """Enable the waiting state."""
- self.setWaiting(True)
-
- @qt.Slot()
- def stopWaiting(self):
- """Disable the waiting state."""
- self.setWaiting(False)
-
- @qt.Slot()
- def swapWaiting(self):
- """Swap the waiting state."""
- self.setWaiting(not self.isWaiting())
diff --git a/silx/gui/widgets/__init__.py b/silx/gui/widgets/__init__.py
deleted file mode 100644
index 9d0299d..0000000
--- a/silx/gui/widgets/__init__.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2016-2018 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-"""This package provides a few simple Qt widgets that rely only on a Qt binding for Python.
-
-No other optional dependencies of *silx* should be required."""
diff --git a/silx/gui/widgets/setup.py b/silx/gui/widgets/setup.py
deleted file mode 100644
index e96ac8d..0000000
--- a/silx/gui/widgets/setup.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2016 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-__authors__ = ["V. Valls"]
-__license__ = "MIT"
-__date__ = "11/10/2016"
-
-
-from numpy.distutils.misc_util import Configuration
-
-
-def configuration(parent_package='', top_path=None):
- config = Configuration('widgets', parent_package, top_path)
- config.add_subpackage('test')
- return config
-
-
-if __name__ == "__main__":
- from numpy.distutils.core import setup
- setup(configuration=configuration)
diff --git a/silx/gui/widgets/test/__init__.py b/silx/gui/widgets/test/__init__.py
deleted file mode 100644
index 9aaec76..0000000
--- a/silx/gui/widgets/test/__init__.py
+++ /dev/null
@@ -1,59 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2020 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-import unittest
-
-from . import test_periodictable
-from . import test_tablewidget
-from . import test_threadpoolpushbutton
-from . import test_hierarchicaltableview
-from . import test_printpreview
-from . import test_framebrowser
-from . import test_boxlayoutdockwidget
-from . import test_rangeslider
-from . import test_flowlayout
-from . import test_elidedlabel
-from . import test_legendiconwidget
-
-__authors__ = ["V. Valls", "P. Knobel"]
-__license__ = "MIT"
-__date__ = "19/07/2017"
-
-
-def suite():
- test_suite = unittest.TestSuite()
- test_suite.addTests(
- [test_threadpoolpushbutton.suite(),
- test_tablewidget.suite(),
- test_periodictable.suite(),
- test_printpreview.suite(),
- test_hierarchicaltableview.suite(),
- test_framebrowser.suite(),
- test_boxlayoutdockwidget.suite(),
- test_rangeslider.suite(),
- test_flowlayout.suite(),
- test_elidedlabel.suite(),
- test_legendiconwidget.suite(),
- ])
- return test_suite
diff --git a/silx/gui/widgets/test/test_boxlayoutdockwidget.py b/silx/gui/widgets/test/test_boxlayoutdockwidget.py
deleted file mode 100644
index 9a93ca1..0000000
--- a/silx/gui/widgets/test/test_boxlayoutdockwidget.py
+++ /dev/null
@@ -1,83 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2018 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-"""Tests for BoxLayoutDockWidget"""
-
-__authors__ = ["T. Vincent"]
-__license__ = "MIT"
-__date__ = "06/03/2018"
-
-import unittest
-
-from silx.gui.widgets.BoxLayoutDockWidget import BoxLayoutDockWidget
-from silx.gui import qt
-from silx.gui.utils.testutils import TestCaseQt
-
-
-class TestBoxLayoutDockWidget(TestCaseQt):
- """Tests for BoxLayoutDockWidget"""
-
- def setUp(self):
- """Create and show a main window"""
- self.window = qt.QMainWindow()
- self.qWaitForWindowExposed(self.window)
-
- def tearDown(self):
- """Delete main window"""
- self.window.setAttribute(qt.Qt.WA_DeleteOnClose)
- self.window.close()
- del self.window
- self.qapp.processEvents()
-
- def test(self):
- """Test update of layout direction according to dock area"""
- # Create a widget with a QBoxLayout
- layout = qt.QBoxLayout(qt.QBoxLayout.LeftToRight)
- layout.addWidget(qt.QLabel('First'))
- layout.addWidget(qt.QLabel('Second'))
- widget = qt.QWidget()
- widget.setLayout(layout)
-
- # Add it to a BoxLayoutDockWidget
- dock = BoxLayoutDockWidget()
- dock.setWidget(widget)
-
- self.window.addDockWidget(qt.Qt.BottomDockWidgetArea, dock)
- self.qapp.processEvents()
- self.assertEqual(layout.direction(), qt.QBoxLayout.LeftToRight)
-
- self.window.addDockWidget(qt.Qt.LeftDockWidgetArea, dock)
- self.qapp.processEvents()
- self.assertEqual(layout.direction(), qt.QBoxLayout.TopToBottom)
-
-
-def suite():
- loader = unittest.defaultTestLoader.loadTestsFromTestCase
- test_suite = unittest.TestSuite()
- test_suite.addTest(loader(TestBoxLayoutDockWidget))
- return test_suite
-
-
-if __name__ == '__main__':
- unittest.main(defaultTest='suite')
diff --git a/silx/gui/widgets/test/test_elidedlabel.py b/silx/gui/widgets/test/test_elidedlabel.py
deleted file mode 100644
index 2856733..0000000
--- a/silx/gui/widgets/test/test_elidedlabel.py
+++ /dev/null
@@ -1,111 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2020 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-"""Tests for ElidedLabel"""
-
-__license__ = "MIT"
-__date__ = "08/06/2020"
-
-import unittest
-
-from silx.gui import qt
-from silx.gui.widgets.ElidedLabel import ElidedLabel
-from silx.gui.utils import testutils
-
-
-class TestElidedLabel(testutils.TestCaseQt):
-
- def setUp(self):
- self.label = ElidedLabel()
- self.label.show()
- self.qWaitForWindowExposed(self.label)
-
- def tearDown(self):
- self.label.setAttribute(qt.Qt.WA_DeleteOnClose)
- self.label.close()
- del self.label
- self.qapp.processEvents()
-
- def testElidedValue(self):
- """Test elided text"""
- raw = "mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm"
- self.label.setText(raw)
- self.label.setFixedWidth(30)
- displayedText = qt.QLabel.text(self.label)
- self.assertNotEqual(raw, displayedText)
- self.assertIn("…", displayedText)
- self.assertIn("m", displayedText)
-
- def testNotElidedValue(self):
- """Test elided text"""
- raw = "mmmmmmm"
- self.label.setText(raw)
- self.label.setFixedWidth(200)
- displayedText = qt.QLabel.text(self.label)
- self.assertNotIn("…", displayedText)
- self.assertEqual(raw, displayedText)
-
- def testUpdateFromElidedToNotElided(self):
- """Test tooltip when not elided"""
- raw1 = "mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm"
- raw2 = "nn"
- self.label.setText(raw1)
- self.label.setFixedWidth(30)
- self.label.setText(raw2)
- displayedTooltip = qt.QLabel.toolTip(self.label)
- self.assertNotIn(raw1, displayedTooltip)
- self.assertNotIn(raw2, displayedTooltip)
-
- def testUpdateFromNotElidedToElided(self):
- """Test tooltip when elided"""
- raw1 = "nn"
- raw2 = "mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm"
- self.label.setText(raw1)
- self.label.setFixedWidth(30)
- self.label.setText(raw2)
- displayedTooltip = qt.QLabel.toolTip(self.label)
- self.assertNotIn(raw1, displayedTooltip)
- self.assertIn(raw2, displayedTooltip)
-
- def testUpdateFromElidedToElided(self):
- """Test tooltip when elided"""
- raw1 = "nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn"
- raw2 = "mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm"
- self.label.setText(raw1)
- self.label.setFixedWidth(30)
- self.label.setText(raw2)
- displayedTooltip = qt.QLabel.toolTip(self.label)
- self.assertNotIn(raw1, displayedTooltip)
- self.assertIn(raw2, displayedTooltip)
-
-
-def suite():
- loader = unittest.defaultTestLoader.loadTestsFromTestCase
- test_suite = unittest.TestSuite()
- test_suite.addTest(loader(TestElidedLabel))
- return test_suite
-
-
-if __name__ == '__main__':
- unittest.main(defaultTest='suite')
diff --git a/silx/gui/widgets/test/test_flowlayout.py b/silx/gui/widgets/test/test_flowlayout.py
deleted file mode 100644
index 1497945..0000000
--- a/silx/gui/widgets/test/test_flowlayout.py
+++ /dev/null
@@ -1,77 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2018 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-"""Tests for FlowLayout"""
-
-__authors__ = ["T. Vincent"]
-__license__ = "MIT"
-__date__ = "02/08/2018"
-
-import unittest
-
-from silx.gui.widgets.FlowLayout import FlowLayout
-from silx.gui import qt
-from silx.gui.utils.testutils import TestCaseQt
-
-
-class TestFlowLayout(TestCaseQt):
- """Tests for FlowLayout"""
-
- def setUp(self):
- """Create and show a widget"""
- self.widget = qt.QWidget()
- self.widget.show()
- self.qWaitForWindowExposed(self.widget)
-
- def tearDown(self):
- """Delete widget"""
- self.widget.setAttribute(qt.Qt.WA_DeleteOnClose)
- self.widget.close()
- del self.widget
- self.qapp.processEvents()
-
- def test(self):
- """Basic tests"""
- layout = FlowLayout()
- self.widget.setLayout(layout)
-
- layout.addWidget(qt.QLabel('first'))
- layout.addWidget(qt.QLabel('second'))
- self.assertEqual(layout.count(), 2)
-
- layout.setHorizontalSpacing(10)
- self.assertEqual(layout.horizontalSpacing(), 10)
- layout.setVerticalSpacing(5)
- self.assertEqual(layout.verticalSpacing(), 5)
-
-
-def suite():
- loader = unittest.defaultTestLoader.loadTestsFromTestCase
- test_suite = unittest.TestSuite()
- test_suite.addTest(loader(TestFlowLayout))
- return test_suite
-
-
-if __name__ == '__main__':
- unittest.main(defaultTest='suite')
diff --git a/silx/gui/widgets/test/test_framebrowser.py b/silx/gui/widgets/test/test_framebrowser.py
deleted file mode 100644
index 2dfd302..0000000
--- a/silx/gui/widgets/test/test_framebrowser.py
+++ /dev/null
@@ -1,73 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2018 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-__authors__ = ["T. Vincent"]
-__license__ = "MIT"
-__date__ = "23/03/2018"
-
-
-import unittest
-
-from silx.gui.utils.testutils import TestCaseQt
-from silx.gui.widgets.FrameBrowser import FrameBrowser
-
-
-class TestFrameBrowser(TestCaseQt):
- """Test for FrameBrowser"""
-
- def test(self):
- """Test FrameBrowser"""
- widget = FrameBrowser()
- widget.show()
- self.qWaitForWindowExposed(widget)
-
- nFrames = 20
- widget.setNFrames(nFrames)
- self.assertEqual(widget.getRange(), (0, nFrames - 1))
- self.assertEqual(widget.getValue(), 0)
-
- range_ = -100, 100
- widget.setRange(*range_)
- self.assertEqual(widget.getRange(), range_)
- self.assertEqual(widget.getValue(), range_[0])
-
- widget.setValue(0)
- self.assertEqual(widget.getValue(), 0)
-
- widget.setValue(range_[1] + 100)
- self.assertEqual(widget.getValue(), range_[1])
-
- widget.setValue(range_[0] - 100)
- self.assertEqual(widget.getValue(), range_[0])
-
-
-def suite():
- loader = unittest.defaultTestLoader.loadTestsFromTestCase
- test_suite = unittest.TestSuite()
- test_suite.addTest(loader(TestFrameBrowser))
- return test_suite
-
-
-if __name__ == '__main__':
- unittest.main(defaultTest='suite')
diff --git a/silx/gui/widgets/test/test_hierarchicaltableview.py b/silx/gui/widgets/test/test_hierarchicaltableview.py
deleted file mode 100644
index 9fad54d..0000000
--- a/silx/gui/widgets/test/test_hierarchicaltableview.py
+++ /dev/null
@@ -1,117 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2016-2017 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-__authors__ = ["V. Valls"]
-__license__ = "MIT"
-__date__ = "07/04/2017"
-
-import unittest
-
-from .. import HierarchicalTableView
-from silx.gui.utils.testutils import TestCaseQt
-from silx.gui import qt
-
-
-class TableModel(HierarchicalTableView.HierarchicalTableModel):
-
- def __init__(self, parent):
- HierarchicalTableView.HierarchicalTableModel.__init__(self, parent)
- self.__content = {}
-
- def rowCount(self, parent=qt.QModelIndex()):
- return 3
-
- def columnCount(self, parent=qt.QModelIndex()):
- return 3
-
- def setData1(self):
- if qt.qVersion() > "4.6":
- self.beginResetModel()
- else:
- self.reset()
-
- content = {}
- content[0, 0] = ("title", True, (1, 3))
- content[0, 1] = ("a", True, (2, 1))
- content[1, 1] = ("b", False, (1, 2))
- content[1, 2] = ("c", False, (1, 1))
- content[2, 2] = ("d", False, (1, 1))
- self.__content = content
- if qt.qVersion() > "4.6":
- self.endResetModel()
-
- def data(self, index, role=qt.Qt.DisplayRole):
- if not index.isValid():
- return None
- cell = self.__content.get((index.column(), index.row()), None)
- if cell is None:
- return None
-
- if role == self.SpanRole:
- return cell[2]
- elif role == self.IsHeaderRole:
- return cell[1]
- elif role == qt.Qt.DisplayRole:
- return cell[0]
- return None
-
-
-class TestHierarchicalTableView(TestCaseQt):
- """Test for HierarchicalTableView"""
-
- def testEmpty(self):
- widget = HierarchicalTableView.HierarchicalTableView()
- widget.show()
- self.qWaitForWindowExposed(widget)
-
- def testModel(self):
- widget = HierarchicalTableView.HierarchicalTableView()
- model = TableModel(widget)
- # set the data before using the model into the widget
- model.setData1()
- widget.setModel(model)
- span = widget.rowSpan(0, 0), widget.columnSpan(0, 0)
- self.assertEqual(span, (1, 3))
- widget.show()
- self.qWaitForWindowExposed(widget)
-
- def testModelUpdate(self):
- widget = HierarchicalTableView.HierarchicalTableView()
- model = TableModel(widget)
- widget.setModel(model)
- # set the data after using the model into the widget
- model.setData1()
- span = widget.rowSpan(0, 0), widget.columnSpan(0, 0)
- self.assertEqual(span, (1, 3))
-
-
-def suite():
- loader = unittest.defaultTestLoader.loadTestsFromTestCase
- test_suite = unittest.TestSuite()
- test_suite.addTest(loader(TestHierarchicalTableView))
- return test_suite
-
-
-if __name__ == '__main__':
- unittest.main(defaultTest='suite')
diff --git a/silx/gui/widgets/test/test_legendiconwidget.py b/silx/gui/widgets/test/test_legendiconwidget.py
deleted file mode 100644
index f845f75..0000000
--- a/silx/gui/widgets/test/test_legendiconwidget.py
+++ /dev/null
@@ -1,74 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2020 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-"""Tests for LegendIconWidget"""
-
-__authors__ = ["V. Valls"]
-__license__ = "MIT"
-__date__ = "23/10/2020"
-
-import unittest
-
-from silx.gui import qt
-from silx.gui.widgets.LegendIconWidget import LegendIconWidget
-from silx.gui.utils.testutils import TestCaseQt
-from silx.utils.testutils import ParametricTestCase
-
-
-class TestLegendIconWidget(TestCaseQt, ParametricTestCase):
- """Tests for TestRangeSlider"""
-
- def setUp(self):
- self.widget = LegendIconWidget()
- self.widget.show()
- self.qWaitForWindowExposed(self.widget)
-
- def tearDown(self):
- self.widget.setAttribute(qt.Qt.WA_DeleteOnClose)
- self.widget.close()
- del self.widget
- self.qapp.processEvents()
-
- def testCreate(self):
- self.qapp.processEvents()
-
- def testColormap(self):
- self.widget.setColormap("viridis")
- self.qapp.processEvents()
-
- def testSymbol(self):
- self.widget.setSymbol("o")
- self.widget.setSymbolColormap("viridis")
- self.qapp.processEvents()
-
-
-def suite():
- loader = unittest.defaultTestLoader.loadTestsFromTestCase
- test_suite = unittest.TestSuite()
- test_suite.addTest(loader(TestLegendIconWidget))
- return test_suite
-
-
-if __name__ == '__main__':
- unittest.main(defaultTest='suite')
diff --git a/silx/gui/widgets/test/test_periodictable.py b/silx/gui/widgets/test/test_periodictable.py
deleted file mode 100644
index 3e7eb16..0000000
--- a/silx/gui/widgets/test/test_periodictable.py
+++ /dev/null
@@ -1,163 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2016-2017 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-__authors__ = ["P. Knobel"]
-__license__ = "MIT"
-__date__ = "05/12/2016"
-
-import unittest
-
-from .. import PeriodicTable
-from silx.gui.utils.testutils import TestCaseQt
-from silx.gui import qt
-
-
-class TestPeriodicTable(TestCaseQt):
- """Basic test for ArrayTableWidget with a numpy array"""
-
- def testShow(self):
- """basic test (instantiation done in setUp)"""
- pt = PeriodicTable.PeriodicTable()
- pt.show()
- self.qWaitForWindowExposed(pt)
-
- def testSelectable(self):
- """basic test (instantiation done in setUp)"""
- pt = PeriodicTable.PeriodicTable(selectable=True)
- self.assertTrue(pt.selectable)
-
- def testCustomElements(self):
- PTI = PeriodicTable.ColoredPeriodicTableItem
- my_items = [
- PTI("Xx", 42, 43, 44, "xaxatorium", 1002.2,
- bgcolor="#FF0000"),
- PTI("Yy", 25, 22, 44, "yoyotrium", 8.8)
- ]
-
- pt = PeriodicTable.PeriodicTable(elements=my_items)
-
- pt.setSelection(["He", "Xx"])
- selection = pt.getSelection()
- self.assertEqual(len(selection), 1) # "He" not found
- self.assertEqual(selection[0].symbol, "Xx")
- self.assertEqual(selection[0].Z, 42)
- self.assertEqual(selection[0].col, 43)
- self.assertAlmostEqual(selection[0].mass, 1002.2)
- self.assertEqual(qt.QColor(selection[0].bgcolor),
- qt.QColor(qt.Qt.red))
-
- self.assertTrue(pt.isElementSelected("Xx"))
- self.assertFalse(pt.isElementSelected("Yy"))
- self.assertRaises(KeyError, pt.isElementSelected, "Yx")
-
- def testVeryCustomElements(self):
- class MyPTI(PeriodicTable.PeriodicTableItem):
- def __init__(self, *args):
- PeriodicTable.PeriodicTableItem.__init__(self, *args[:6])
- self.my_feature = args[6]
-
- my_items = [
- MyPTI("Xx", 42, 43, 44, "xaxatorium", 1002.2, "spam"),
- MyPTI("Yy", 25, 22, 44, "yoyotrium", 8.8, "eggs")
- ]
-
- pt = PeriodicTable.PeriodicTable(elements=my_items)
-
- pt.setSelection(["Xx", "Yy"])
- selection = pt.getSelection()
- self.assertEqual(len(selection), 2)
- self.assertEqual(selection[1].symbol, "Yy")
- self.assertEqual(selection[1].Z, 25)
- self.assertEqual(selection[1].col, 22)
- self.assertEqual(selection[1].row, 44)
- self.assertAlmostEqual(selection[0].mass, 1002.2)
- self.assertAlmostEqual(selection[0].my_feature, "spam")
-
-
-class TestPeriodicCombo(TestCaseQt):
- """Basic test for ArrayTableWidget with a numpy array"""
- def setUp(self):
- super(TestPeriodicCombo, self).setUp()
- self.pc = PeriodicTable.PeriodicCombo()
-
- def tearDown(self):
- del self.pc
- super(TestPeriodicCombo, self).tearDown()
-
- def testShow(self):
- """basic test (instantiation done in setUp)"""
- self.pc.show()
- self.qWaitForWindowExposed(self.pc)
-
- def testSelect(self):
- self.pc.setSelection("Sb")
- selection = self.pc.getSelection()
- self.assertIsInstance(selection,
- PeriodicTable.PeriodicTableItem)
- self.assertEqual(selection.symbol, "Sb")
- self.assertEqual(selection.Z, 51)
- self.assertEqual(selection.name, "antimony")
-
-
-class TestPeriodicList(TestCaseQt):
- """Basic test for ArrayTableWidget with a numpy array"""
- def setUp(self):
- super(TestPeriodicList, self).setUp()
- self.pl = PeriodicTable.PeriodicList()
-
- def tearDown(self):
- del self.pl
- super(TestPeriodicList, self).tearDown()
-
- def testShow(self):
- """basic test (instantiation done in setUp)"""
- self.pl.show()
- self.qWaitForWindowExposed(self.pl)
-
- def testSelect(self):
- self.pl.setSelectedElements(["Li", "He", "Au"])
- sel_elmts = self.pl.getSelection()
-
- self.assertEqual(len(sel_elmts), 3,
- "Wrong number of elements selected")
- for e in sel_elmts:
- self.assertIsInstance(e, PeriodicTable.PeriodicTableItem)
- self.assertIn(e.symbol, ["Li", "He", "Au"])
- self.assertIn(e.Z, [2, 3, 79])
- self.assertIn(e.name, ["lithium", "helium", "gold"])
-
-
-def suite():
- test_suite = unittest.TestSuite()
- test_suite.addTest(
- unittest.defaultTestLoader.loadTestsFromTestCase(TestPeriodicTable))
- test_suite.addTest(
- unittest.defaultTestLoader.loadTestsFromTestCase(TestPeriodicList))
- test_suite.addTest(
- unittest.defaultTestLoader.loadTestsFromTestCase(TestPeriodicCombo))
- return test_suite
-
-
-if __name__ == '__main__':
- unittest.main(defaultTest='suite')
diff --git a/silx/gui/widgets/test/test_printpreview.py b/silx/gui/widgets/test/test_printpreview.py
deleted file mode 100644
index 3c29171..0000000
--- a/silx/gui/widgets/test/test_printpreview.py
+++ /dev/null
@@ -1,74 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2017 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-"""Test PrintPreview"""
-
-__authors__ = ["P. Knobel"]
-__license__ = "MIT"
-__date__ = "19/07/2017"
-
-
-import unittest
-from silx.gui.utils.testutils import TestCaseQt
-from silx.gui.widgets.PrintPreview import PrintPreviewDialog
-from silx.gui import qt
-
-from silx.resources import resource_filename
-
-
-class TestPrintPreview(TestCaseQt):
- def testShow(self):
- p = qt.QPrinter()
- d = PrintPreviewDialog(printer=p)
- d.show()
- self.qapp.processEvents()
-
- def testAddImage(self):
- p = qt.QPrinter()
- d = PrintPreviewDialog(printer=p)
- d.addImage(qt.QImage(resource_filename("gui/icons/clipboard.png")))
- self.qapp.processEvents()
-
- def testAddSvg(self):
- p = qt.QPrinter()
- d = PrintPreviewDialog(printer=p)
- d.addSvgItem(qt.QSvgRenderer(resource_filename("gui/icons/clipboard.svg"), d.page))
- self.qapp.processEvents()
-
- def testAddPixmap(self):
- p = qt.QPrinter()
- d = PrintPreviewDialog(printer=p)
- d.addPixmap(qt.QPixmap.fromImage(qt.QImage(resource_filename("gui/icons/clipboard.png"))))
- self.qapp.processEvents()
-
-
-def suite():
- test_suite = unittest.TestSuite()
- test_suite.addTest(
- unittest.defaultTestLoader.loadTestsFromTestCase(TestPrintPreview))
- return test_suite
-
-
-if __name__ == '__main__':
- unittest.main(defaultTest='suite')
diff --git a/silx/gui/widgets/test/test_rangeslider.py b/silx/gui/widgets/test/test_rangeslider.py
deleted file mode 100644
index 2829050..0000000
--- a/silx/gui/widgets/test/test_rangeslider.py
+++ /dev/null
@@ -1,114 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2018 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-"""Tests for RangeSlider"""
-
-__authors__ = ["T. Vincent"]
-__license__ = "MIT"
-__date__ = "01/08/2018"
-
-import unittest
-
-from silx.gui import qt, colors
-from silx.gui.widgets.RangeSlider import RangeSlider
-from silx.gui.utils.testutils import TestCaseQt
-from silx.utils.testutils import ParametricTestCase
-
-
-class TestRangeSlider(TestCaseQt, ParametricTestCase):
- """Tests for TestRangeSlider"""
-
- def setUp(self):
- self.slider = RangeSlider()
- self.slider.show()
- self.qWaitForWindowExposed(self.slider)
-
- def tearDown(self):
- self.slider.setAttribute(qt.Qt.WA_DeleteOnClose)
- self.slider.close()
- del self.slider
- self.qapp.processEvents()
-
- def testRangeValue(self):
- """Test slider range and values"""
-
- # Play with range
- self.slider.setRange(1, 2)
- self.assertEqual(self.slider.getRange(), (1., 2.))
- self.assertEqual(self.slider.getValues(), (1., 1.))
-
- self.slider.setMinimum(-1)
- self.assertEqual(self.slider.getRange(), (-1., 2.))
- self.assertEqual(self.slider.getValues(), (1., 1.))
-
- self.slider.setMaximum(0)
- self.assertEqual(self.slider.getRange(), (-1., 0.))
- self.assertEqual(self.slider.getValues(), (0., 0.))
-
- # Play with values
- self.slider.setFirstValue(-2.)
- self.assertEqual(self.slider.getValues(), (-1., 0.))
-
- self.slider.setFirstValue(-0.5)
- self.assertEqual(self.slider.getValues(), (-0.5, 0.))
-
- self.slider.setSecondValue(2.)
- self.assertEqual(self.slider.getValues(), (-0.5, 0.))
-
- self.slider.setSecondValue(-0.1)
- self.assertEqual(self.slider.getValues(), (-0.5, -0.1))
-
- def testStepCount(self):
- """Test related to step count"""
- self.slider.setPositionCount(11)
- self.assertEqual(self.slider.getPositionCount(), 11)
- self.slider.setFirstValue(0.32)
- self.assertEqual(self.slider.getFirstValue(), 0.3)
- self.assertEqual(self.slider.getFirstPosition(), 3)
-
- self.slider.setPositionCount(3) # Value is adjusted
- self.assertEqual(self.slider.getValues(), (0.5, 1.))
- self.assertEqual(self.slider.getPositions(), (1, 2))
-
- def testGroove(self):
- """Test Groove pixmap"""
- profile = list(range(100))
-
- for cmap in ('jet', colors.Colormap('viridis')):
- with self.subTest(str(cmap)):
- self.slider.setGroovePixmapFromProfile(profile, cmap)
- pixmap = self.slider.getGroovePixmap()
- self.assertIsInstance(pixmap, qt.QPixmap)
- self.assertEqual(pixmap.width(), len(profile))
-
-
-def suite():
- loader = unittest.defaultTestLoader.loadTestsFromTestCase
- test_suite = unittest.TestSuite()
- test_suite.addTest(loader(TestRangeSlider))
- return test_suite
-
-
-if __name__ == '__main__':
- unittest.main(defaultTest='suite')
diff --git a/silx/gui/widgets/test/test_tablewidget.py b/silx/gui/widgets/test/test_tablewidget.py
deleted file mode 100644
index 6822aef..0000000
--- a/silx/gui/widgets/test/test_tablewidget.py
+++ /dev/null
@@ -1,61 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2016 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-"""Test TableWidget"""
-
-__authors__ = ["P. Knobel"]
-__license__ = "MIT"
-__date__ = "05/12/2016"
-
-
-import unittest
-from silx.gui.utils.testutils import TestCaseQt
-from silx.gui.widgets.TableWidget import TableWidget
-
-
-class TestTableWidget(TestCaseQt):
- def setUp(self):
- super(TestTableWidget, self).setUp()
- self._result = []
-
- def testShow(self):
- table = TableWidget()
- table.setColumnCount(10)
- table.setRowCount(7)
- table.enableCut()
- table.enablePaste()
- table.show()
- table.hide()
- self.qapp.processEvents()
-
-
-def suite():
- test_suite = unittest.TestSuite()
- test_suite.addTest(
- unittest.defaultTestLoader.loadTestsFromTestCase(TestTableWidget))
- return test_suite
-
-
-if __name__ == '__main__':
- unittest.main(defaultTest='suite')
diff --git a/silx/gui/widgets/test/test_threadpoolpushbutton.py b/silx/gui/widgets/test/test_threadpoolpushbutton.py
deleted file mode 100644
index e92eb02..0000000
--- a/silx/gui/widgets/test/test_threadpoolpushbutton.py
+++ /dev/null
@@ -1,135 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2016 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-"""Test for silx.gui.hdf5 module"""
-
-__authors__ = ["V. Valls"]
-__license__ = "MIT"
-__date__ = "17/01/2018"
-
-
-import unittest
-import time
-from silx.gui import qt
-from silx.gui.utils.testutils import TestCaseQt
-from silx.gui.utils.testutils import SignalListener
-from silx.gui.widgets.ThreadPoolPushButton import ThreadPoolPushButton
-from silx.utils.testutils import TestLogging
-
-
-class TestThreadPoolPushButton(TestCaseQt):
-
- def setUp(self):
- super(TestThreadPoolPushButton, self).setUp()
- self._result = []
-
- def waitForPendingOperations(self, object):
- for i in range(50):
- if not object.hasPendingOperations():
- break
- self.qWait(10)
- else:
- raise RuntimeError("Still waiting for a pending operation")
-
- def _trace(self, name, delay=0):
- self._result.append(name)
- if delay != 0:
- time.sleep(delay / 1000.0)
-
- def _compute(self):
- return "result"
-
- def _computeFail(self):
- raise Exception("exception")
-
- def testExecute(self):
- button = ThreadPoolPushButton()
- button.setCallable(self._trace, "a", 0)
- button.executeCallable()
- time.sleep(0.1)
- self.assertListEqual(self._result, ["a"])
- self.waitForPendingOperations(button)
-
- def testMultiExecution(self):
- button = ThreadPoolPushButton()
- button.setCallable(self._trace, "a", 0)
- number = qt.silxGlobalThreadPool().maxThreadCount()
- for _ in range(number):
- button.executeCallable()
- self.waitForPendingOperations(button)
- self.assertListEqual(self._result, ["a"] * number)
-
- def testSaturateThreadPool(self):
- button = ThreadPoolPushButton()
- button.setCallable(self._trace, "a", 100)
- number = qt.silxGlobalThreadPool().maxThreadCount() * 2
- for _ in range(number):
- button.executeCallable()
- self.waitForPendingOperations(button)
- self.assertListEqual(self._result, ["a"] * number)
-
- def testSuccess(self):
- listener = SignalListener()
- button = ThreadPoolPushButton()
- button.setCallable(self._compute)
- button.beforeExecuting.connect(listener.partial(test="be"))
- button.started.connect(listener.partial(test="s"))
- button.succeeded.connect(listener.partial(test="result"))
- button.failed.connect(listener.partial(test="Unexpected exception"))
- button.finished.connect(listener.partial(test="f"))
- button.executeCallable()
- self.qapp.processEvents()
- time.sleep(0.1)
- self.qapp.processEvents()
- result = listener.karguments(argumentName="test")
- self.assertListEqual(result, ["be", "s", "result", "f"])
-
- def testFail(self):
- listener = SignalListener()
- button = ThreadPoolPushButton()
- button.setCallable(self._computeFail)
- button.beforeExecuting.connect(listener.partial(test="be"))
- button.started.connect(listener.partial(test="s"))
- button.succeeded.connect(listener.partial(test="Unexpected success"))
- button.failed.connect(listener.partial(test="exception"))
- button.finished.connect(listener.partial(test="f"))
- with TestLogging('silx.gui.widgets.ThreadPoolPushButton', error=1):
- button.executeCallable()
- self.qapp.processEvents()
- time.sleep(0.1)
- self.qapp.processEvents()
- result = listener.karguments(argumentName="test")
- self.assertListEqual(result, ["be", "s", "exception", "f"])
- listener.clear()
-
-
-def suite():
- test_suite = unittest.TestSuite()
- test_suite.addTest(
- unittest.defaultTestLoader.loadTestsFromTestCase(TestThreadPoolPushButton))
- return test_suite
-
-
-if __name__ == '__main__':
- unittest.main(defaultTest='suite')