diff options
Diffstat (limited to 'silx/gui/widgets')
-rw-r--r-- | silx/gui/widgets/FloatEdit.py | 65 | ||||
-rw-r--r-- | silx/gui/widgets/FrameBrowser.py | 4 | ||||
-rw-r--r-- | silx/gui/widgets/PeriodicTable.py | 6 | ||||
-rw-r--r-- | silx/gui/widgets/PrintGeometryDialog.py | 222 | ||||
-rw-r--r-- | silx/gui/widgets/PrintPreview.py | 704 | ||||
-rw-r--r-- | silx/gui/widgets/TableWidget.py | 162 | ||||
-rw-r--r-- | silx/gui/widgets/ThreadPoolPushButton.py | 2 | ||||
-rw-r--r-- | silx/gui/widgets/WaitingPushButton.py | 2 | ||||
-rw-r--r-- | silx/gui/widgets/test/__init__.py | 4 | ||||
-rw-r--r-- | silx/gui/widgets/test/test_printpreview.py | 74 |
10 files changed, 1232 insertions, 13 deletions
diff --git a/silx/gui/widgets/FloatEdit.py b/silx/gui/widgets/FloatEdit.py new file mode 100644 index 0000000..fd6d8a7 --- /dev/null +++ b/silx/gui/widgets/FloatEdit.py @@ -0,0 +1,65 @@ +# 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. +# +# ###########################################################################*/ +"""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(value) + self.setText(text) diff --git a/silx/gui/widgets/FrameBrowser.py b/silx/gui/widgets/FrameBrowser.py index 783a70a..6737e9c 100644 --- a/silx/gui/widgets/FrameBrowser.py +++ b/silx/gui/widgets/FrameBrowser.py @@ -43,6 +43,8 @@ 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 @@ -215,6 +217,8 @@ 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), diff --git a/silx/gui/widgets/PeriodicTable.py b/silx/gui/widgets/PeriodicTable.py index 2f1ca78..db71483 100644 --- a/silx/gui/widgets/PeriodicTable.py +++ b/silx/gui/widgets/PeriodicTable.py @@ -499,6 +499,8 @@ class _ElementButton(qt.QPushButton): 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 @@ -686,6 +688,8 @@ 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 @@ -741,6 +745,8 @@ class PeriodicCombo(qt.QComboBox): 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. diff --git a/silx/gui/widgets/PrintGeometryDialog.py b/silx/gui/widgets/PrintGeometryDialog.py new file mode 100644 index 0000000..0613ce0 --- /dev/null +++ b/silx/gui/widgets/PrintGeometryDialog.py @@ -0,0 +1,222 @@ +# 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() + 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 new file mode 100644 index 0000000..158d6b7 --- /dev/null +++ b/silx/gui/widgets/PrintPreview.py @@ -0,0 +1,704 @@ +# 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 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 + + +__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): + """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. + """ + 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) + rectItemResizeRect.setZValue(2) + + self._svgItems.append(item) + + if qt.qVersion() < '5.0': + textItem = qt.QGraphicsTextItem(title, svgItem, self.scene) + else: + textItem = qt.QGraphicsTextItem(title, svgItem) + textItem.setTextInteractionFlags(qt.Qt.TextEditorInteraction) + title_offset = 0.5 * textItem.boundingRect().width() + textItem.setZValue(1) + textItem.setFlag(qt.QGraphicsItem.ItemIsMovable, True) + + 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) + scaleCalculationRect = qt.QRectF(commentItem.boundingRect()) + scale = svgItem.boundingRect().width() / scaleCalculationRect.width() + comment_offset = 0.5 * commentItem.boundingRect().width() + if commentPosition.upper() == "LEFT": + x = 1 + else: + x = 0.5 * svgItem.boundingRect().width() - comment_offset * scale # fixme: centering + commentItem.moveBy(svgItem.boundingRect().x() + x, + svgItem.boundingRect().y() + svgItem.boundingRect().height()) + commentItem.setPlainText(comment) + commentItem.setZValue(1) + + commentItem.setFlag(qt.QGraphicsItem.ItemIsMovable, True) + if qt.qVersion() < "5.0": + commentItem.scale(scale, scale) + else: + # the correct equivalent would be: + # rectItem.setTransform(qt.QTransform.fromScale(scalex, scaley)) + commentItem.setScale(scale) + 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: + # the correct equivalent would be: + # rectItem.setTransform(qt.QTransform.fromScale(scalex, scaley)) + 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 = qt.QPrinter() + 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. + """ + 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.") + + 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.rect() + 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: + # the correct equivalent would be: + # rectItem.setTransform(qt.QTransform.fromScale(scalex, scaley)) + parent.setScale(scalex) + + self.scene().removeItem(self._newRect) + self._newRect = None + qt.QGraphicsRectItem.mouseReleaseEvent(self, event) + + +def main(): + """ + """ + import sys + + 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/TableWidget.py b/silx/gui/widgets/TableWidget.py index fad80ee..8167fec 100644 --- a/silx/gui/widgets/TableWidget.py +++ b/silx/gui/widgets/TableWidget.py @@ -1,7 +1,7 @@ # coding: utf-8 # /*########################################################################## # -# Copyright (c) 2004-2016 European Synchrotron Radiation Facility +# 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 @@ -50,7 +50,7 @@ creating the widgets, or later by calling their :meth:`enableCut` and __authors__ = ["P. Knobel"] __license__ = "MIT" -__date__ = "26/01/2017" +__date__ = "03/07/2017" import sys @@ -104,6 +104,8 @@ class CopySelectedCellsAction(qt.QAction): 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] @@ -334,6 +336,41 @@ class PasteCellsAction(qt.QAction): 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: @@ -350,14 +387,25 @@ class TableWidget(qt.QTableWidget): 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.addAction(CopySelectedCellsAction(self)) - self.addAction(CopyAllCellsAction(self)) + 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: @@ -365,6 +413,12 @@ class TableWidget(qt.QTableWidget): 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. @@ -374,7 +428,8 @@ class TableWidget(qt.QTableWidget): This action can cause data to be overwritten. There is currently no *Undo* action to retrieve lost data. """ - self.addAction(PasteCellsAction(self)) + self.pasteCellsAction = PasteCellsAction(self) + self.addAction(self.pasteCellsAction) def enableCut(self): """Enable cut action. @@ -383,8 +438,40 @@ class TableWidget(qt.QTableWidget): This action can cause data to be deleted. There is currently no *Undo* action to retrieve lost data.""" - self.addAction(CutSelectedCellsAction(self)) - self.addAction(CutAllCellsAction(self)) + 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): @@ -414,9 +501,24 @@ class TableView(qt.QTableView): """ 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. @@ -425,8 +527,10 @@ class TableView(qt.QTableView): """ super(TableView, self).setModel(model) - self.addAction(CopySelectedCellsAction(self)) - self.addAction(CopyAllCellsAction(self)) + self.copySelectedCellsAction = CopySelectedCellsAction(self) + self.copyAllCellsAction = CopyAllCellsAction(self) + self.addAction(self.copySelectedCellsAction) + self.addAction(self.copyAllCellsAction) if self.cut: self.enableCut() if self.paste: @@ -443,7 +547,8 @@ class TableView(qt.QTableView): This action can cause data to be overwritten. There is currently no *Undo* action to retrieve lost data. """ - self.addAction(PasteCellsAction(self)) + self.pasteCellsAction = PasteCellsAction(self) + self.addAction(self.pasteCellsAction) def enableCut(self): """Enable cut action. @@ -453,8 +558,10 @@ class TableView(qt.QTableView): This action can cause data to be deleted. There is currently no *Undo* action to retrieve lost data. """ - self.addAction(CutSelectedCellsAction(self)) - self.addAction(CutAllCellsAction(self)) + 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: @@ -466,6 +573,37 @@ class TableView(qt.QTableView): 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([]) diff --git a/silx/gui/widgets/ThreadPoolPushButton.py b/silx/gui/widgets/ThreadPoolPushButton.py index 29e831d..4dba488 100644 --- a/silx/gui/widgets/ThreadPoolPushButton.py +++ b/silx/gui/widgets/ThreadPoolPushButton.py @@ -102,6 +102,8 @@ class ThreadPoolPushButton(WaitingPushButton): >>> 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)") diff --git a/silx/gui/widgets/WaitingPushButton.py b/silx/gui/widgets/WaitingPushButton.py index 49ab9b9..499de1a 100644 --- a/silx/gui/widgets/WaitingPushButton.py +++ b/silx/gui/widgets/WaitingPushButton.py @@ -40,6 +40,8 @@ class WaitingPushButton(qt.QPushButton): 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): diff --git a/silx/gui/widgets/test/__init__.py b/silx/gui/widgets/test/__init__.py index afa0f78..7affc20 100644 --- a/silx/gui/widgets/test/__init__.py +++ b/silx/gui/widgets/test/__init__.py @@ -28,10 +28,11 @@ from . import test_periodictable from . import test_tablewidget from . import test_threadpoolpushbutton from . import test_hierarchicaltableview +from . import test_printpreview __authors__ = ["V. Valls", "P. Knobel"] __license__ = "MIT" -__date__ = "07/04/2017" +__date__ = "19/07/2017" def suite(): @@ -40,6 +41,7 @@ def suite(): [test_threadpoolpushbutton.suite(), test_tablewidget.suite(), test_periodictable.suite(), + test_printpreview.suite(), test_hierarchicaltableview.suite(), ]) return test_suite diff --git a/silx/gui/widgets/test/test_printpreview.py b/silx/gui/widgets/test/test_printpreview.py new file mode 100644 index 0000000..ecb165a --- /dev/null +++ b/silx/gui/widgets/test/test_printpreview.py @@ -0,0 +1,74 @@ +# 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.test.utils 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') |