Add axis convenience methods and matrix display example (#1726)
* add axis convenient methods and matrix display example * wrestled wayward space back into docstring * color map names are case sensitive on Linux * docstring fix for PlotItem * protect AxisItem.linkToView from being obscured by DateAxisItem override * replaced setOrigin method by promoted setPos and setScale * made tri-state axes switching explicit * reverted setRect behavior, documentation pass for ImageItem * minor text adjustment * implmented some suggested revisions * fix input parsing for setRect and add tests so that I don't break it again * don't try to re-add transform after clearing it * changed example and doc image generators to pg.exec() * removed commented-out code * cleaned up transform eqaulity assertion * restored devoured comment * restored devoured comment
This commit is contained in:
parent
3a3d05b16f
commit
61f067bf7c
@ -1,8 +1,66 @@
|
||||
ImageItem
|
||||
=========
|
||||
|
||||
:class:`~pyqtgraph.ImageItem` displays images inside a :class:`~pyqtgraph.GraphicsView`, or a
|
||||
:class:`~pyqtgraph.ViewBox`, which may itself be part of a :class:`~pyqtgraph.PlotItem`. It is designed
|
||||
for rapid updates as needed for a video display. The supplied data is optionally scaled (see
|
||||
:func:`~pyqtgraph.ImageItem.setLevels`) and/or colored according to a
|
||||
lookup table (see :func:`~pyqtgraph.ImageItem.setLookupTable`.
|
||||
|
||||
Data is provided as a NumPy array with an ordering of either
|
||||
|
||||
* `col-major`, where the shape of the array represents (width, height) or
|
||||
* `row-major`, where the shape of the array represents (height, width).
|
||||
|
||||
While `col-major` is the default, `row-major` ordering typically has the best performance. In either ordering,
|
||||
a third dimension can be added to the array to hold individual
|
||||
``[R,G,B]`` or ``[R,G,B,A]`` components.
|
||||
|
||||
Notes
|
||||
-----
|
||||
|
||||
Data ordering can be set for each ImageItem, or in the :ref:`global configuration options <apiref_config>` by ::
|
||||
|
||||
pyqtgraph.setConfigOption('imageAxisOrder', 'row-major') # best performance
|
||||
|
||||
An image can be placed into a plot area of a given extent directly through the
|
||||
:func:`~pyqtgraph.ImageItem.setRect` method or the ``rect`` keyword. This is internally realized through
|
||||
assigning a ``QtGui.QTransform``. For other translation, scaling or rotations effects that
|
||||
persist for all later image data, the user can also directly define and assign such a
|
||||
transform, as shown in the example below.
|
||||
|
||||
ImageItem is frequently used in conjunction with :class:`~pyqtgraph.ColorBarItem` to provide
|
||||
a color map display and interactive level adjustments, or with
|
||||
:class:`~pyqtgraph.HistogramLUTItem` or :class:`~pyqtgraph.HistogramLUTWidget` for a full GUI
|
||||
to control the levels and lookup table used to display the image.
|
||||
|
||||
If performance is critial, the following points may be worth investigating:
|
||||
|
||||
* Use row-major ordering and C-contiguous image data.
|
||||
* Manually provide ``level`` information to avoid autoLevels sampling of the image.
|
||||
* Prefer `float32` to `float64` for floating point data, avoid NaN values.
|
||||
* Use lookup tables with <= 256 entries for false color images.
|
||||
* Avoid individual level adjustments RGB components.
|
||||
* Use the latest version of NumPy. Notably, SIMD code added in version 1.20 significantly improved performance on Linux platforms.
|
||||
* Enable Numba with ``pyqtgraph.setConfigOption('useNumba', True)``, although the JIT compilation will only accelerate repeated image display.
|
||||
|
||||
.. _ImageItem_examples:
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
.. literalinclude:: ../images/gen_example_imageitem_transform.py
|
||||
:lines: 19-28
|
||||
:dedent: 8
|
||||
|
||||
.. image::
|
||||
../images/example_imageitem_transform.png
|
||||
:width: 49%
|
||||
:alt: Example of transformed image display
|
||||
|
||||
|
||||
|
||||
.. autoclass:: pyqtgraph.ImageItem
|
||||
:members:
|
||||
|
||||
.. automethod:: pyqtgraph.ImageItem.__init__
|
||||
|
||||
|
BIN
doc/source/images/example_imageitem_transform.png
Normal file
BIN
doc/source/images/example_imageitem_transform.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.6 KiB |
@ -42,4 +42,4 @@ main_window = MainWindow()
|
||||
|
||||
## Start Qt event loop
|
||||
if __name__ == '__main__':
|
||||
mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
@ -52,4 +52,4 @@ main_window = MainWindow()
|
||||
|
||||
## Start Qt event loop
|
||||
if __name__ == '__main__':
|
||||
mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
45
doc/source/images/gen_example_imageitem_transform.py
Normal file
45
doc/source/images/gen_example_imageitem_transform.py
Normal file
@ -0,0 +1,45 @@
|
||||
"""
|
||||
generates 'example_false_color_image.png'
|
||||
"""
|
||||
import numpy as np
|
||||
import pyqtgraph as pg
|
||||
import pyqtgraph.exporters as exp
|
||||
from pyqtgraph.Qt import QtGui, mkQApp
|
||||
|
||||
class MainWindow(pg.GraphicsLayoutWidget):
|
||||
""" example application main window """
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.resize(420,400)
|
||||
self.show()
|
||||
|
||||
plot = self.addPlot()
|
||||
# Example: Transformed display of ImageItem
|
||||
|
||||
tr = QtGui.QTransform() # prepare ImageItem transformation:
|
||||
tr.scale(6.0, 3.0) # scale horizontal and vertical axes
|
||||
tr.translate(-1.5, -1.5) # move 3x3 image to locate center at axis origin
|
||||
|
||||
img = pg.ImageItem( image=np.eye(3), levels=(0,1) ) # create example image
|
||||
img.setTransform(tr) # assign transform
|
||||
|
||||
plot.addItem( img ) # add ImageItem to PlotItem
|
||||
plot.showAxes(True) # frame it with a full set of axes
|
||||
plot.invertY(True) # vertical axis counts top to bottom
|
||||
|
||||
self.timer = pg.QtCore.QTimer( singleShot=True )
|
||||
self.timer.timeout.connect(self.export)
|
||||
self.timer.start(100)
|
||||
|
||||
def export(self):
|
||||
print('exporting')
|
||||
exporter = exp.ImageExporter(self.scene())
|
||||
exporter.parameters()['width'] = 420
|
||||
exporter.export('example_imageitem_transform.png')
|
||||
|
||||
mkQApp("ImageItem transform example")
|
||||
main_window = MainWindow()
|
||||
|
||||
## Start Qt event loop
|
||||
if __name__ == '__main__':
|
||||
pg.exec()
|
62
examples/MatrixDisplayExample.py
Normal file
62
examples/MatrixDisplayExample.py
Normal file
@ -0,0 +1,62 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
This example demonstrates ViewBox and AxisItem configuration to plot a correlation matrix.
|
||||
"""
|
||||
## Add path to library (just for examples; you do not need this)
|
||||
import initExample
|
||||
|
||||
import numpy as np
|
||||
import pyqtgraph as pg
|
||||
from pyqtgraph.Qt import QtWidgets, mkQApp, QtGui
|
||||
|
||||
class MainWindow(QtWidgets.QMainWindow):
|
||||
""" example application main window """
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(MainWindow, self).__init__(*args, **kwargs)
|
||||
gr_wid = pg.GraphicsLayoutWidget(show=True)
|
||||
self.setCentralWidget(gr_wid)
|
||||
self.setWindowTitle('pyqtgraph example: Correlation matrix display')
|
||||
self.resize(600,500)
|
||||
self.show()
|
||||
|
||||
corrMatrix = np.array([
|
||||
[ 1. , 0.5184571 , -0.70188642],
|
||||
[ 0.5184571 , 1. , -0.86094096],
|
||||
[-0.70188642, -0.86094096, 1. ]
|
||||
])
|
||||
columns = ["A", "B", "C"]
|
||||
|
||||
pg.setConfigOption('imageAxisOrder', 'row-major') # Switch default order to Row-major
|
||||
|
||||
correlogram = pg.ImageItem()
|
||||
# create transform to center the corner element on the origin, for any assigned image:
|
||||
tr = QtGui.QTransform().translate(-0.5, -0.5)
|
||||
correlogram.setTransform(tr)
|
||||
correlogram.setImage(corrMatrix)
|
||||
|
||||
plotItem = gr_wid.addPlot() # add PlotItem to the main GraphicsLayoutWidget
|
||||
plotItem.invertY(True) # orient y axis to run top-to-bottom
|
||||
plotItem.setDefaultPadding(0.0) # plot without padding data range
|
||||
plotItem.addItem(correlogram) # display correlogram
|
||||
|
||||
# show full frame, label tick marks at top and left sides, with some extra space for labels:
|
||||
plotItem.showAxes( True, showValues=(True, True, False, False), size=20 )
|
||||
|
||||
# define major tick marks and labels:
|
||||
ticks = [ (idx, label) for idx, label in enumerate( columns ) ]
|
||||
for side in ('left','top','right','bottom'):
|
||||
plotItem.getAxis(side).setTicks( (ticks, []) ) # add list of major ticks; no minor ticks
|
||||
plotItem.getAxis('bottom').setHeight(10) # include some additional space at bottom of figure
|
||||
|
||||
colorMap = pg.colormap.get("CET-D1") # choose perceptually uniform, diverging color map
|
||||
# generate an adjustabled color bar, initially spanning -1 to 1:
|
||||
bar = pg.ColorBarItem( values=(-1,1), cmap=colorMap)
|
||||
# link color bar and color map to correlogram, and show it in plotItem:
|
||||
bar.setImageItem(correlogram, insert_in=plotItem)
|
||||
|
||||
mkQApp("Correlation matrix display")
|
||||
main_window = MainWindow()
|
||||
|
||||
## Start Qt event loop
|
||||
if __name__ == '__main__':
|
||||
pg.exec()
|
@ -12,6 +12,7 @@ examples = OrderedDict([
|
||||
('Plot Customization', 'customPlot.py'),
|
||||
('Timestamps on x axis', 'DateAxisItem.py'),
|
||||
('Image Analysis', 'imageAnalysis.py'),
|
||||
('Matrix Display', 'MatrixDisplayExample.py'),
|
||||
('Color Maps', 'colorMaps.py'),
|
||||
('Color Gradient Plots', 'ColorGradientPlots.py'),
|
||||
('ViewBox Features', Namespace(filename='ViewBoxFeatures.py', recommended=True)),
|
||||
|
@ -111,9 +111,10 @@ class AxisItem(GraphicsWidget):
|
||||
|
||||
self._linkedView = None
|
||||
if linkView is not None:
|
||||
self.linkToView(linkView)
|
||||
self._linkToView_internal(linkView)
|
||||
|
||||
self.grid = False
|
||||
|
||||
#self.setCacheMode(self.DeviceCoordinateCache)
|
||||
|
||||
def setStyle(self, **kwds):
|
||||
@ -529,8 +530,9 @@ class AxisItem(GraphicsWidget):
|
||||
else:
|
||||
return self._linkedView()
|
||||
|
||||
def linkToView(self, view):
|
||||
"""Link this axis to a ViewBox, causing its displayed range to match the visible range of the view."""
|
||||
def _linkToView_internal(self, view):
|
||||
# We need this code to be available without override,
|
||||
# even though DateAxisItem overrides the user-side linkToView method
|
||||
self.unlinkFromView()
|
||||
|
||||
self._linkedView = weakref.ref(view)
|
||||
@ -538,8 +540,11 @@ class AxisItem(GraphicsWidget):
|
||||
view.sigYRangeChanged.connect(self.linkedViewChanged)
|
||||
else:
|
||||
view.sigXRangeChanged.connect(self.linkedViewChanged)
|
||||
|
||||
view.sigResized.connect(self.linkedViewChanged)
|
||||
|
||||
def linkToView(self, view):
|
||||
"""Link this axis to a ViewBox, causing its displayed range to match the visible range of the view."""
|
||||
self._linkToView_internal(view)
|
||||
|
||||
def unlinkFromView(self):
|
||||
"""Unlink this axis from a ViewBox."""
|
||||
|
@ -306,7 +306,8 @@ class DateAxisItem(AxisItem):
|
||||
self.minSpacing = density*size
|
||||
|
||||
def linkToView(self, view):
|
||||
super(DateAxisItem, self).linkToView(view)
|
||||
"""Link this axis to a ViewBox, causing its displayed range to match the visible range of the view."""
|
||||
self._linkToView_internal(view) # calls original linkToView code
|
||||
|
||||
# Set default limits
|
||||
_min = MIN_REGULAR_TIMESTAMP
|
||||
|
@ -25,26 +25,20 @@ __all__ = ['ImageItem']
|
||||
class ImageItem(GraphicsObject):
|
||||
"""
|
||||
**Bases:** :class:`GraphicsObject <pyqtgraph.GraphicsObject>`
|
||||
|
||||
GraphicsObject displaying an image. Optimized for rapid update (ie video display).
|
||||
This item displays either a 2D numpy array (height, width) or
|
||||
a 3D array (height, width, RGBa). This array is optionally scaled (see
|
||||
:func:`setLevels <pyqtgraph.ImageItem.setLevels>`) and/or colored
|
||||
with a lookup table (see :func:`setLookupTable <pyqtgraph.ImageItem.setLookupTable>`)
|
||||
before being displayed.
|
||||
|
||||
ImageItem is frequently used in conjunction with
|
||||
:class:`HistogramLUTItem <pyqtgraph.HistogramLUTItem>` or
|
||||
:class:`HistogramLUTWidget <pyqtgraph.HistogramLUTWidget>` to provide a GUI
|
||||
for controlling the levels and lookup table used to display the image.
|
||||
"""
|
||||
|
||||
# Overall description of ImageItem (including examples) moved to documentation text
|
||||
sigImageChanged = QtCore.Signal()
|
||||
sigRemoveRequested = QtCore.Signal(object) # self; emitted when 'remove' is selected from context menu
|
||||
|
||||
def __init__(self, image=None, **kargs):
|
||||
"""
|
||||
See :func:`setImage <pyqtgraph.ImageItem.setImage>` for all allowed initialization arguments.
|
||||
See :func:`~pyqtgraph.ImageItem.setOpts` for further keyword arguments and
|
||||
and :func:`~pyqtgraph.ImageItem.setImage` for information on supported formats.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image: array
|
||||
Image data
|
||||
"""
|
||||
GraphicsObject.__init__(self)
|
||||
self.menu = None
|
||||
@ -64,6 +58,8 @@ class ImageItem(GraphicsObject):
|
||||
self._defferedLevels = None
|
||||
|
||||
self.axisOrder = getConfigOption('imageAxisOrder')
|
||||
self._dataTransform = self._inverseDataTransform = None
|
||||
self._update_data_transforms( self.axisOrder ) # install initial transforms
|
||||
|
||||
# In some cases, we use a modified lookup table to handle both rescaling
|
||||
# and LUT more efficiently
|
||||
@ -79,25 +75,33 @@ class ImageItem(GraphicsObject):
|
||||
self.setOpts(**kargs)
|
||||
|
||||
def setCompositionMode(self, mode):
|
||||
"""Change the composition mode of the item (see QPainter::CompositionMode
|
||||
in the Qt documentation). This is useful when overlaying multiple ImageItems.
|
||||
"""
|
||||
Change the composition mode of the item to `mode`, used when overlaying multiple ImageItems.
|
||||
See ``QPainter::CompositionMode`` in the Qt documentation for details.
|
||||
|
||||
============================================ ============================================================
|
||||
**Most common arguments:**
|
||||
QtGui.QPainter.CompositionMode_SourceOver Default; image replaces the background if it
|
||||
is opaque. Otherwise, it uses the alpha channel to blend
|
||||
the image with the background.
|
||||
QtGui.QPainter.CompositionMode_Overlay The image color is mixed with the background color to
|
||||
reflect the lightness or darkness of the background.
|
||||
QtGui.QPainter.CompositionMode_Plus Both the alpha and color of the image and background pixels
|
||||
are added together.
|
||||
QtGui.QPainter.CompositionMode_Multiply The output is the image color multiplied by the background.
|
||||
============================================ ============================================================
|
||||
Most common arguments:
|
||||
|
||||
- ``QtGui.QPainter.CompositionMode_SourceOver``:
|
||||
(Default) Image replaces the background if it is opaque.
|
||||
Otherwise the alpha channel controls the visibility of the background.
|
||||
|
||||
- ``QtGui.QPainter.CompositionMode_Overlay``:
|
||||
The image color is mixed with the background color to reflect the lightness or darkness of the background.
|
||||
|
||||
- ``QtGui.QPainter.CompositionMode_Plus``:
|
||||
Both the alpha and color of the image and background pixels are added together.
|
||||
|
||||
- ``QtGui.QPainter.CompositionMode_Multiply``:
|
||||
The output is the image color multiplied by the background.
|
||||
"""
|
||||
self.paintMode = mode
|
||||
self.update()
|
||||
|
||||
def setBorder(self, b):
|
||||
"""
|
||||
Defines the border drawn around the image. Accepts all arguments supported by
|
||||
:func:`~pyqtgraph.functions.mkPen`.
|
||||
"""
|
||||
self.border = fn.mkPen(b)
|
||||
self.update()
|
||||
|
||||
@ -125,13 +129,18 @@ class ImageItem(GraphicsObject):
|
||||
|
||||
def setLevels(self, levels, update=True):
|
||||
"""
|
||||
Set image scaling levels. Can be one of:
|
||||
|
||||
* [blackLevel, whiteLevel]
|
||||
* [[minRed, maxRed], [minGreen, maxGreen], [minBlue, maxBlue]]
|
||||
|
||||
Only the first format is compatible with lookup tables. See :func:`makeARGB <pyqtgraph.makeARGB>`
|
||||
for more details on how levels are applied.
|
||||
Sets image scaling levels.
|
||||
See :func:`makeARGB <pyqtgraph.makeARGB>` for more details on how levels are applied.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
levels: list_like
|
||||
- [`blackLevel`, `whiteLevel`]
|
||||
sets black and white levels for monochrome data and can be used with a lookup table.
|
||||
- [[`minR`, `maxR`], [`minG`, `maxG`], [`minB`, `maxB`]]
|
||||
sets individual scaling for RGB values. Not compatible with lookup tables.
|
||||
update: bool, optional
|
||||
Controls if image immediately updates to reflect the new levels.
|
||||
"""
|
||||
if self._xp is None:
|
||||
self.levels = levels
|
||||
@ -145,18 +154,22 @@ class ImageItem(GraphicsObject):
|
||||
self.updateImage()
|
||||
|
||||
def getLevels(self):
|
||||
"""
|
||||
Returns the list representing the current level settings. See :func:`~setLevels`.
|
||||
When ``autoLevels`` is active, the format is [`blackLevel`, `whiteLevel`].
|
||||
"""
|
||||
return self.levels
|
||||
#return self.whiteLevel, self.blackLevel
|
||||
|
||||
def setLookupTable(self, lut, update=True):
|
||||
"""
|
||||
Set the lookup table (numpy array) to use for this image. (see
|
||||
:func:`makeARGB <pyqtgraph.makeARGB>` for more information on how this is used).
|
||||
Optionally, lut can be a callable that accepts the current image as an
|
||||
Sets lookup table `lut` to use for false color display of a monochrome image. See :func:`makeARGB <pyqtgraph.makeARGB>` for more
|
||||
information on how this is used. Optionally, `lut` can be a callable that accepts the current image as an
|
||||
argument and returns the lookup table to use.
|
||||
|
||||
Ordinarily, this table is supplied by a :class:`HistogramLUTItem <pyqtgraph.HistogramLUTItem>`
|
||||
or :class:`GradientEditorItem <pyqtgraph.GradientEditorItem>`.
|
||||
Ordinarily, this table is supplied by a :class:`~pyqtgraph.HistogramLUTItem`,
|
||||
:class:`~pyqtgraph.GradientEditorItem` or :class:`~pyqtgraph.ColorBarItem`.
|
||||
|
||||
Setting `update` to False avoids an immediate image update.
|
||||
"""
|
||||
if lut is not self.lut:
|
||||
if self._xp is not None:
|
||||
@ -180,22 +193,57 @@ class ImageItem(GraphicsObject):
|
||||
data = numpy.asarray(data)
|
||||
return data
|
||||
|
||||
def setAutoDownsample(self, ads):
|
||||
def setAutoDownsample(self, active=True):
|
||||
"""
|
||||
Set the automatic downsampling mode for this ImageItem.
|
||||
Controls automatic downsampling for this ImageItem.
|
||||
|
||||
Added in version 0.9.9
|
||||
If active is `True`, the image is automatically downsampled to match the
|
||||
screen resolution. This improves performance for large images and
|
||||
reduces aliasing. If autoDownsample is not specified, then ImageItem will
|
||||
choose whether to downsample the image based on its size.
|
||||
`False` disables automatic downsampling.
|
||||
"""
|
||||
self.autoDownsample = ads
|
||||
self.autoDownsample = active
|
||||
self._renderRequired = True
|
||||
self.update()
|
||||
|
||||
def setOpts(self, update=True, **kargs):
|
||||
"""
|
||||
Sets display and processing options for this ImageItem. :func:`~pyqtgraph.ImageItem.__init__` and
|
||||
:func:`~pyqtgraph.ImageItem.setImage` support all keyword arguments listed here.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
autoDownsample: bool
|
||||
See :func:`~pyqtgraph.ImageItem.setAutoDownsample`.
|
||||
axisOrder: str
|
||||
| `'col-major'`: The shape of the array represents (width, height) of the image. This is the default.
|
||||
| `'row-major'`: The shape of the array represents (height, width).
|
||||
border: bool
|
||||
Sets a pen to draw to draw an image border. See :func:`~pyqtgraph.ImageItem.setBorder`.
|
||||
compositionMode:
|
||||
See :func:`~pyqtgraph.ImageItem.setCompositionMode`
|
||||
lut: array
|
||||
Sets a color lookup table to use when displaying the image.
|
||||
See :func:`~pyqtgraph.ImageItem.setLookupTable`.
|
||||
levels: list_like, usally [`min`, `max`]
|
||||
Sets minimum and maximum values to use when rescaling the image data. By default, these will be set to
|
||||
the estimated minimum and maximum values in the image. If the image array has dtype uint8, no rescaling
|
||||
is necessary. See :func:`~pyqtgraph.ImageItem.setLevels`.
|
||||
opacity: float, 0.0-1.0
|
||||
Overall opacity for an RGB image.
|
||||
rect: QRectF, QRect or array_like of floats (`x`,`y`,`w`,`h`)
|
||||
Displays the current image within the specified rectangle in plot coordinates.
|
||||
See :func:`~pyqtgraph.ImageItem.setRect`.
|
||||
update : bool, optional
|
||||
Controls if image immediately updates to reflect the new options.
|
||||
"""
|
||||
if 'axisOrder' in kargs:
|
||||
val = kargs['axisOrder']
|
||||
val = kargs['axisOrder']
|
||||
if val not in ('row-major', 'col-major'):
|
||||
raise ValueError('axisOrder must be either "row-major" or "col-major"')
|
||||
raise ValueError("axisOrder must be either 'row-major' or 'col-major'")
|
||||
self.axisOrder = val
|
||||
self._update_data_transforms(self.axisOrder) # update cached transforms
|
||||
if 'lut' in kargs:
|
||||
self.setLookupTable(kargs['lut'], update=update)
|
||||
if 'levels' in kargs:
|
||||
@ -213,17 +261,40 @@ class ImageItem(GraphicsObject):
|
||||
self.menu = None
|
||||
if 'autoDownsample' in kargs:
|
||||
self.setAutoDownsample(kargs['autoDownsample'])
|
||||
if 'rect' in kargs:
|
||||
self.setRect(kargs['rect'])
|
||||
if update:
|
||||
self.update()
|
||||
|
||||
def setRect(self, rect):
|
||||
"""Scale and translate the image to fit within rect (must be a QRect or QRectF)."""
|
||||
def setRect(self, *args):
|
||||
"""
|
||||
setRect(rect) or setRect(x,y,w,h)
|
||||
|
||||
Sets translation and scaling of this ImageItem to display the current image within the rectangle given
|
||||
as ``QtCore.QRect`` or ``QtCore.QRectF`` `rect`, or described by parameters `x, y, w, h`, defining starting
|
||||
position, width and height.
|
||||
|
||||
This method cannot be used before an image is assigned.
|
||||
See the :ref:`examples <ImageItem_examples>` for how to manually set transformations.
|
||||
"""
|
||||
if len(args) == 0:
|
||||
self.resetTransform() # reset scaling and rotation when called without argument
|
||||
return
|
||||
if isinstance(args[0], (QtCore.QRectF, QtCore.QRect)):
|
||||
rect = args[0] # use QRectF or QRect directly
|
||||
else:
|
||||
if hasattr(args[0],'__len__'):
|
||||
args = args[0] # promote tuple or list of values
|
||||
rect = QtCore.QRectF( *args ) # QRectF(x,y,w,h), but also accepts other initializers
|
||||
tr = QtGui.QTransform()
|
||||
tr.translate(rect.left(), rect.top())
|
||||
tr.scale(rect.width() / self.width(), rect.height() / self.height())
|
||||
self.setTransform(tr)
|
||||
|
||||
def clear(self):
|
||||
"""
|
||||
Clears the assigned image.
|
||||
"""
|
||||
self.image = None
|
||||
self.prepareGeometryChange()
|
||||
self.informViewBoundsChanged()
|
||||
@ -239,46 +310,39 @@ class ImageItem(GraphicsObject):
|
||||
|
||||
def setImage(self, image=None, autoLevels=None, **kargs):
|
||||
"""
|
||||
Update the image displayed by this item. For more information on how the image
|
||||
is processed before displaying, see :func:`makeARGB <pyqtgraph.makeARGB>`
|
||||
|
||||
================= =========================================================================
|
||||
**Arguments:**
|
||||
image (numpy array) Specifies the image data. May be 2D (width, height) or
|
||||
3D (width, height, RGBa). The array dtype must be integer or floating
|
||||
point of any bit depth. For 3D arrays, the third dimension must
|
||||
be of length 3 (RGB) or 4 (RGBA). See *notes* below.
|
||||
autoLevels (bool) If True, this forces the image to automatically select
|
||||
levels based on the maximum and minimum values in the data.
|
||||
By default, this argument is true unless the levels argument is
|
||||
given.
|
||||
lut (numpy array) The color lookup table to use when displaying the image.
|
||||
See :func:`setLookupTable <pyqtgraph.ImageItem.setLookupTable>`.
|
||||
levels (min, max) The minimum and maximum values to use when rescaling the image
|
||||
data. By default, this will be set to the minimum and maximum values
|
||||
in the image. If the image array has dtype uint8, no rescaling is necessary.
|
||||
opacity (float 0.0-1.0)
|
||||
compositionMode See :func:`setCompositionMode <pyqtgraph.ImageItem.setCompositionMode>`
|
||||
border Sets the pen used when drawing the image border. Default is None.
|
||||
autoDownsample (bool) If True, the image is automatically downsampled to match the
|
||||
screen resolution. This improves performance for large images and
|
||||
reduces aliasing. If autoDownsample is not specified, then ImageItem will
|
||||
choose whether to downsample the image based on its size.
|
||||
================= =========================================================================
|
||||
|
||||
|
||||
**Notes:**
|
||||
|
||||
For backward compatibility, image data is assumed to be in column-major order (column, row).
|
||||
However, most image data is stored in row-major order (row, column) and will need to be
|
||||
transposed before calling setImage()::
|
||||
Updates the image displayed by this ImageItem. For more information on how the image
|
||||
is processed before displaying, see :func:`~pyqtgraph.makeARGB>`.
|
||||
|
||||
For backward compatibility, image data is assumed to be in column-major order (column, row) by default.
|
||||
However, most data is stored in row-major order (row, column). It can either be transposed before assignment::
|
||||
|
||||
imageitem.setImage(imagedata.T)
|
||||
|
||||
or the interpretation of the data can be changed locally through the ``axisOrder`` keyword or by changing the
|
||||
`imageAxisOrder` :ref:`global configuration option <apiref_config>`.
|
||||
|
||||
All keywords supported by :func:`~pyqtgraph.ImageItem.setOpts` are also allowed here.
|
||||
|
||||
This requirement can be changed by calling ``image.setOpts(axisOrder='row-major')`` or
|
||||
by changing the ``imageAxisOrder`` :ref:`global configuration option <apiref_config>`.
|
||||
Parameters
|
||||
----------
|
||||
image: array
|
||||
Image data given as NumPy array with an integer or floating
|
||||
point dtype of any bit depth. A 2-dimensional array describes single-valued (monochromatic) data.
|
||||
A 3-dimensional array is used to give individual color components. The third dimension must
|
||||
be of length 3 (RGB) or 4 (RGBA).
|
||||
|
||||
rect: QRectF, QRect or list_like of floats (`x, y, w, h`), optional
|
||||
If given, sets translation and scaling to display the image within the specified rectangle. See
|
||||
:func:`~pyqtgraph.ImageItem.setRect`.
|
||||
|
||||
autoLevels: bool, optional
|
||||
If True, ImageItem will automatically select levels based on the maximum and minimum values encountered
|
||||
in the data. For performance reasons, this search subsamples the images and may miss individual bright or
|
||||
or dark points in the data set.
|
||||
|
||||
If False, the search will be omitted.
|
||||
|
||||
The default is `False` if a ``levels`` keyword argument is given, and `True` otherwise.
|
||||
"""
|
||||
profile = debug.Profiler()
|
||||
|
||||
@ -342,45 +406,55 @@ class ImageItem(GraphicsObject):
|
||||
self._defferedLevels = None
|
||||
self.setLevels((levels))
|
||||
|
||||
def _update_data_transforms(self, axisOrder='col-major'):
|
||||
""" Sets up the transforms needed to map between input array and display """
|
||||
self._dataTransform = QtGui.QTransform()
|
||||
self._inverseDataTransform = QtGui.QTransform()
|
||||
if self.axisOrder == 'row-major': # transpose both
|
||||
self._dataTransform.scale(1, -1)
|
||||
self._dataTransform.rotate(-90)
|
||||
self._inverseDataTransform.scale(1, -1)
|
||||
self._inverseDataTransform.rotate(-90)
|
||||
|
||||
|
||||
def dataTransform(self):
|
||||
"""Return the transform that maps from this image's input array to its
|
||||
"""
|
||||
Returns the transform that maps from this image's input array to its
|
||||
local coordinate system.
|
||||
|
||||
This transform corrects for the transposition that occurs when image data
|
||||
is interpreted in row-major order.
|
||||
|
||||
:meta private:
|
||||
"""
|
||||
# Might eventually need to account for downsampling / clipping here
|
||||
tr = QtGui.QTransform()
|
||||
if self.axisOrder == 'row-major':
|
||||
# transpose
|
||||
tr.scale(1, -1)
|
||||
tr.rotate(-90)
|
||||
return tr
|
||||
# transforms are updated in setOpts call.
|
||||
return self._dataTransform
|
||||
|
||||
def inverseDataTransform(self):
|
||||
"""Return the transform that maps from this image's local coordinate
|
||||
system to its input array.
|
||||
|
||||
See dataTransform() for more information.
|
||||
|
||||
:meta private:
|
||||
"""
|
||||
tr = QtGui.QTransform()
|
||||
if self.axisOrder == 'row-major':
|
||||
# transpose
|
||||
tr.scale(1, -1)
|
||||
tr.rotate(-90)
|
||||
return tr
|
||||
# transforms are updated in setOpts call.
|
||||
return self._inverseDataTransform
|
||||
|
||||
def mapToData(self, obj):
|
||||
tr = self.inverseDataTransform()
|
||||
return tr.map(obj)
|
||||
return self._inverseDataTransform.map(obj)
|
||||
|
||||
def mapFromData(self, obj):
|
||||
tr = self.dataTransform()
|
||||
return tr.map(obj)
|
||||
return self._dataTransform.map(obj)
|
||||
|
||||
def quickMinMax(self, targetSize=1e6):
|
||||
"""
|
||||
Estimate the min/max values of the image data by subsampling.
|
||||
Estimates the min/max values of the image data by subsampling.
|
||||
Subsampling is performed at regular strides chosen to evaluate a number of samples
|
||||
equal to or less than `targetSize`.
|
||||
|
||||
Returns (`min`, `max`).
|
||||
"""
|
||||
data = self.image
|
||||
while data.size > targetSize:
|
||||
@ -730,33 +804,36 @@ class ImageItem(GraphicsObject):
|
||||
p.drawRect(self.boundingRect())
|
||||
|
||||
def save(self, fileName, *args):
|
||||
"""Save this image to file. Note that this saves the visible image (after scale/color changes), not the original data."""
|
||||
"""
|
||||
Saves this image to file. Note that this saves the visible image (after scale/color changes), not the
|
||||
original data.
|
||||
"""
|
||||
if self._renderRequired:
|
||||
self.render()
|
||||
self.qimage.save(fileName, *args)
|
||||
|
||||
def getHistogram(self, bins='auto', step='auto', perChannel=False, targetImageSize=200,
|
||||
targetHistogramSize=500, **kwds):
|
||||
"""Returns x and y arrays containing the histogram values for the current image.
|
||||
"""
|
||||
Returns `x` and `y` arrays containing the histogram values for the current image.
|
||||
For an explanation of the return format, see numpy.histogram().
|
||||
|
||||
The *step* argument causes pixels to be skipped when computing the histogram to save time.
|
||||
If *step* is 'auto', then a step is chosen such that the analyzed data has
|
||||
dimensions roughly *targetImageSize* for each axis.
|
||||
The `step` argument causes pixels to be skipped when computing the histogram to save time.
|
||||
If `step` is 'auto', then a step is chosen such that the analyzed data has
|
||||
dimensions approximating `targetImageSize` for each axis.
|
||||
|
||||
The *bins* argument and any extra keyword arguments are passed to
|
||||
self.xp.histogram(). If *bins* is 'auto', then a bin number is automatically
|
||||
The `bins` argument and any extra keyword arguments are passed to
|
||||
``self.xp.histogram()``. If `bins` is `auto`, a bin number is automatically
|
||||
chosen based on the image characteristics:
|
||||
|
||||
* Integer images will have approximately *targetHistogramSize* bins,
|
||||
* Integer images will have approximately `targetHistogramSize` bins,
|
||||
with each bin having an integer width.
|
||||
* All other types will have *targetHistogramSize* bins.
|
||||
* All other types will have `targetHistogramSize` bins.
|
||||
|
||||
If *perChannel* is True, then the histogram is computed once per channel
|
||||
If `perChannel` is `True`, then a histogram is computed for each channel,
|
||||
and the output is a list of the results.
|
||||
|
||||
This method is also used when automatically computing levels.
|
||||
"""
|
||||
# This method is also used when automatically computing levels.
|
||||
if self.image is None or self.image.size == 0:
|
||||
return None, None
|
||||
if step == 'auto':
|
||||
@ -812,10 +889,10 @@ class ImageItem(GraphicsObject):
|
||||
|
||||
def setPxMode(self, b):
|
||||
"""
|
||||
Set whether the item ignores transformations and draws directly to screen pixels.
|
||||
Sets whether the item ignores transformations and draws directly to screen pixels.
|
||||
If True, the item will not inherit any scale or rotation transformations from its
|
||||
parent items, but its position will be transformed as usual.
|
||||
(see GraphicsItem::ItemIgnoresTransformations in the Qt documentation)
|
||||
(see ``GraphicsItem::ItemIgnoresTransformations`` in the Qt documentation)
|
||||
"""
|
||||
self.setFlag(self.ItemIgnoresTransformations, b)
|
||||
|
||||
@ -830,7 +907,9 @@ class ImageItem(GraphicsObject):
|
||||
return QtGui.QPixmap.fromImage(self.qimage)
|
||||
|
||||
def pixelSize(self):
|
||||
"""return scene-size of a single pixel in the image"""
|
||||
"""
|
||||
Returns the scene-size of a single pixel in the image
|
||||
"""
|
||||
br = self.sceneBoundingRect()
|
||||
if self.image is None:
|
||||
return 1,1
|
||||
|
@ -3,6 +3,7 @@ import importlib
|
||||
import os
|
||||
import warnings
|
||||
import weakref
|
||||
import collections.abc
|
||||
|
||||
import numpy as np
|
||||
|
||||
@ -53,6 +54,7 @@ class PlotItem(GraphicsWidget):
|
||||
:func:`setYRange <pyqtgraph.ViewBox.setYRange>`,
|
||||
:func:`setRange <pyqtgraph.ViewBox.setRange>`,
|
||||
:func:`autoRange <pyqtgraph.ViewBox.autoRange>`,
|
||||
:func:`setDefaultPadding <pyqtgraph.ViewBox.setDefaultPadding>`,
|
||||
:func:`setXLink <pyqtgraph.ViewBox.setXLink>`,
|
||||
:func:`setYLink <pyqtgraph.ViewBox.setYLink>`,
|
||||
:func:`setAutoPan <pyqtgraph.ViewBox.setAutoPan>`,
|
||||
@ -269,7 +271,7 @@ class PlotItem(GraphicsWidget):
|
||||
#Important: don't use a settattr(m, getattr(self.vb, m)) as we'd be leaving the viebox alive
|
||||
#because we had a reference to an instance method (creating wrapper methods at runtime instead).
|
||||
for m in ['setXRange', 'setYRange', 'setXLink', 'setYLink', 'setAutoPan', # NOTE:
|
||||
'setAutoVisible', 'setRange', 'autoRange', 'viewRect', 'viewRange', # If you update this list, please
|
||||
'setAutoVisible', 'setDefaultPadding', 'setRange', 'autoRange', 'viewRect', 'viewRange', # If you update this list, please
|
||||
'setMouseEnabled', 'setLimits', 'enableAutoRange', 'disableAutoRange', # update the class docstring
|
||||
'setAspectLocked', 'invertY', 'invertX', 'register', 'unregister']: # as well.
|
||||
|
||||
@ -295,7 +297,6 @@ class PlotItem(GraphicsWidget):
|
||||
============== ==========================================================================================
|
||||
"""
|
||||
|
||||
|
||||
if axisItems is None:
|
||||
axisItems = {}
|
||||
|
||||
@ -332,7 +333,9 @@ class PlotItem(GraphicsWidget):
|
||||
axis.linkToView(self.vb)
|
||||
self.axes[k] = {'item': axis, 'pos': pos}
|
||||
self.layout.addItem(axis, *pos)
|
||||
axis.setZValue(-1000)
|
||||
# axis.setZValue(-1000)
|
||||
# place axis above images at z=0, items that want to draw over the axes should be placed at z>=1:
|
||||
axis.setZValue(0.5)
|
||||
axis.setFlag(axis.ItemNegativeZStacksBehindParent)
|
||||
|
||||
axisVisible = k in visibleAxes
|
||||
@ -1171,7 +1174,63 @@ class PlotItem(GraphicsWidget):
|
||||
def hideAxis(self, axis):
|
||||
"""Hide one of the PlotItem's axes. ('left', 'bottom', 'right', or 'top')"""
|
||||
self.showAxis(axis, False)
|
||||
|
||||
|
||||
def showAxes(self, selection, showValues=True, size=False):
|
||||
"""
|
||||
Convenience method for quickly configuring axis settings.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
selection: boolean or tuple of booleans (left, top, right, bottom)
|
||||
Determines which AxisItems will be displayed.
|
||||
A single boolean value will set all axes,
|
||||
so that ``showAxes(True)`` configures the axes to draw a frame.
|
||||
showValues: optional, boolean or tuple of booleans (left, top, right, bottom)
|
||||
Determines if values will be displayed for the ticks of each axis.
|
||||
True value shows values for left and bottom axis (default).
|
||||
False shows no values.
|
||||
None leaves settings unchanged.
|
||||
If not specified, left and bottom axes will be drawn with values.
|
||||
size: optional, float or tuple of floats (width, height)
|
||||
Reserves as fixed amount of space (width for vertical axis, height for horizontal axis)
|
||||
for each axis where tick values are enabled. If only a single float value is given, it
|
||||
will be applied for both width and height. If `None` is given instead of a float value,
|
||||
the axis reverts to automatic allocation of space.
|
||||
"""
|
||||
if selection is True: # shortcut: enable all axes, creating a frame
|
||||
selection = (True, True, True, True)
|
||||
elif selection is False: # shortcut: disable all axes
|
||||
selection = (False, False, False, False)
|
||||
if showValues is True: # shortcut: defaults arrangement with labels at left and bottom
|
||||
showValues = (True, False, False, True)
|
||||
elif showValues is False: # shortcut: disable all labels
|
||||
showValues = (False, False, False, False)
|
||||
elif showValues is None: # leave labelling untouched
|
||||
showValues = (None, None, None, None)
|
||||
if size is not False and not isinstance(size, collections.abc.Sized):
|
||||
size = (size, size) # make sure that size is either False or a full set of (width, height)
|
||||
|
||||
all_axes = ('left','top','right','bottom')
|
||||
for show_axis, show_value, axis_key in zip(selection, showValues, all_axes):
|
||||
if show_axis is None:
|
||||
pass # leave axis display as it is.
|
||||
else:
|
||||
if show_axis: self.showAxis(axis_key)
|
||||
else : self.hideAxis(axis_key)
|
||||
|
||||
if show_value is None:
|
||||
pass # leave value display as it is.
|
||||
else:
|
||||
ax = self.getAxis(axis_key)
|
||||
ax.setStyle(showValues=show_value)
|
||||
if size is not False: # size adjustment is requested
|
||||
if axis_key in ('left','right'):
|
||||
if show_value: ax.setWidth(size[0])
|
||||
else : ax.setWidth( None )
|
||||
elif axis_key in ('top', 'bottom'):
|
||||
if show_value: ax.setHeight(size[1])
|
||||
else : ax.setHeight( None )
|
||||
|
||||
def showScale(self, *args, **kargs):
|
||||
warnings.warn(
|
||||
'PlotItem.showScale has been deprecated and will be removed in 0.13. '
|
||||
|
@ -106,25 +106,27 @@ class ViewBox(GraphicsWidget):
|
||||
NamedViews = weakref.WeakValueDictionary() # name: ViewBox
|
||||
AllViews = weakref.WeakKeyDictionary() # ViewBox: None
|
||||
|
||||
def __init__(self, parent=None, border=None, lockAspect=False, enableMouse=True, invertY=False, enableMenu=True, name=None, invertX=False):
|
||||
def __init__(self, parent=None, border=None, lockAspect=False, enableMouse=True, invertY=False, enableMenu=True, name=None, invertX=False, defaultPadding=0.02):
|
||||
"""
|
||||
============== =============================================================
|
||||
================= =============================================================
|
||||
**Arguments:**
|
||||
*parent* (QGraphicsWidget) Optional parent widget
|
||||
*border* (QPen) Do draw a border around the view, give any
|
||||
single argument accepted by :func:`mkPen <pyqtgraph.mkPen>`
|
||||
*lockAspect* (False or float) The aspect ratio to lock the view
|
||||
coorinates to. (or False to allow the ratio to change)
|
||||
*enableMouse* (bool) Whether mouse can be used to scale/pan the view
|
||||
*invertY* (bool) See :func:`invertY <pyqtgraph.ViewBox.invertY>`
|
||||
*invertX* (bool) See :func:`invertX <pyqtgraph.ViewBox.invertX>`
|
||||
*enableMenu* (bool) Whether to display a context menu when
|
||||
right-clicking on the ViewBox background.
|
||||
*name* (str) Used to register this ViewBox so that it appears
|
||||
in the "Link axis" dropdown inside other ViewBox
|
||||
context menus. This allows the user to manually link
|
||||
the axes of any other view to this one.
|
||||
============== =============================================================
|
||||
*parent* (QGraphicsWidget) Optional parent widget
|
||||
*border* (QPen) Do draw a border around the view, give any
|
||||
single argument accepted by :func:`mkPen <pyqtgraph.mkPen>`
|
||||
*lockAspect* (False or float) The aspect ratio to lock the view
|
||||
coorinates to. (or False to allow the ratio to change)
|
||||
*enableMouse* (bool) Whether mouse can be used to scale/pan the view
|
||||
*invertY* (bool) See :func:`invertY <pyqtgraph.ViewBox.invertY>`
|
||||
*invertX* (bool) See :func:`invertX <pyqtgraph.ViewBox.invertX>`
|
||||
*enableMenu* (bool) Whether to display a context menu when
|
||||
right-clicking on the ViewBox background.
|
||||
*name* (str) Used to register this ViewBox so that it appears
|
||||
in the "Link axis" dropdown inside other ViewBox
|
||||
context menus. This allows the user to manually link
|
||||
the axes of any other view to this one.
|
||||
*defaultPadding* (float) fraction of the data range that will be added
|
||||
as padding by default
|
||||
================= =============================================================
|
||||
"""
|
||||
|
||||
GraphicsWidget.__init__(self, parent)
|
||||
@ -147,11 +149,12 @@ class ViewBox(GraphicsWidget):
|
||||
'xInverted': invertX,
|
||||
'aspectLocked': False, ## False if aspect is unlocked, otherwise float specifies the locked ratio.
|
||||
'autoRange': [True, True], ## False if auto range is disabled,
|
||||
## otherwise float gives the fraction of data that is visible
|
||||
## otherwise float gives the fraction of data that is visible
|
||||
'autoPan': [False, False], ## whether to only pan (do not change scaling) when auto-range is enabled
|
||||
'autoVisibleOnly': [False, False], ## whether to auto-range only to the visible portion of a plot
|
||||
'linkedViews': [None, None], ## may be None, "viewName", or weakref.ref(view)
|
||||
## a name string indicates that the view *should* link to another, but no view with that name exists yet.
|
||||
'defaultPadding': defaultPadding,
|
||||
|
||||
'mouseEnabled': [enableMouse, enableMouse],
|
||||
'mouseMode': ViewBox.PanMode if getConfigOption('leftButtonPan') else ViewBox.RectMode,
|
||||
@ -497,8 +500,8 @@ class ViewBox(GraphicsWidget):
|
||||
*xRange* (min,max) The range that should be visible along the x-axis.
|
||||
*yRange* (min,max) The range that should be visible along the y-axis.
|
||||
*padding* (float) Expand the view by a fraction of the requested range.
|
||||
By default, this value is set between 0.02 and 0.1 depending on
|
||||
the size of the ViewBox.
|
||||
By default, this value is set between the default padding value
|
||||
and 0.1 depending on the size of the ViewBox.
|
||||
*update* (bool) If True, update the range of the ViewBox immediately.
|
||||
Otherwise, the update is deferred until before the next render.
|
||||
*disableAutoRange* (bool) If True, auto-ranging is diabled. Otherwise, it is left
|
||||
@ -628,7 +631,7 @@ class ViewBox(GraphicsWidget):
|
||||
"""
|
||||
Set the visible Y range of the view to [*min*, *max*].
|
||||
The *padding* argument causes the range to be set larger by the fraction specified.
|
||||
(by default, this value is between 0.02 and 0.1 depending on the size of the ViewBox)
|
||||
(by default, this value is between the default padding and 0.1 depending on the size of the ViewBox)
|
||||
"""
|
||||
self.setRange(yRange=[min, max], update=update, padding=padding)
|
||||
|
||||
@ -636,7 +639,7 @@ class ViewBox(GraphicsWidget):
|
||||
"""
|
||||
Set the visible X range of the view to [*min*, *max*].
|
||||
The *padding* argument causes the range to be set larger by the fraction specified.
|
||||
(by default, this value is between 0.02 and 0.1 depending on the size of the ViewBox)
|
||||
(by default, this value is between the default padding and 0.1 depending on the size of the ViewBox)
|
||||
"""
|
||||
self.setRange(xRange=[min, max], update=update, padding=padding)
|
||||
|
||||
@ -646,14 +649,14 @@ class ViewBox(GraphicsWidget):
|
||||
Note that this is not the same as enableAutoRange, which causes the view to
|
||||
automatically auto-range whenever its contents are changed.
|
||||
|
||||
============== ============================================================
|
||||
============== =============================================================
|
||||
**Arguments:**
|
||||
padding The fraction of the total data range to add on to the final
|
||||
visible range. By default, this value is set between 0.02
|
||||
and 0.1 depending on the size of the ViewBox.
|
||||
visible range. By default, this value is set between the
|
||||
default padding and 0.1 depending on the size of the ViewBox.
|
||||
items If specified, this is a list of items to consider when
|
||||
determining the visible range.
|
||||
============== ============================================================
|
||||
============== =============================================================
|
||||
"""
|
||||
if item is None:
|
||||
bounds = self.childrenBoundingRect(items=items)
|
||||
@ -665,10 +668,14 @@ class ViewBox(GraphicsWidget):
|
||||
|
||||
def suggestPadding(self, axis):
|
||||
l = self.width() if axis==0 else self.height()
|
||||
def_pad = self.state['defaultPadding']
|
||||
if def_pad == 0.:
|
||||
return def_pad # respect requested zero padding
|
||||
max_pad = max(0.1, def_pad) # don't shrink a large default padding
|
||||
if l > 0:
|
||||
padding = fn.clip_scalar(1./(l**0.5), 0.02, 0.1)
|
||||
padding = fn.clip_scalar( 50*def_pad / (l**0.5), def_pad, max_pad)
|
||||
else:
|
||||
padding = 0.02
|
||||
padding = def_pad
|
||||
return padding
|
||||
|
||||
def setLimits(self, **kwds):
|
||||
@ -1110,6 +1117,13 @@ class ViewBox(GraphicsWidget):
|
||||
"""
|
||||
self.border = fn.mkPen(*args, **kwds)
|
||||
self.borderRect.setPen(self.border)
|
||||
|
||||
def setDefaultPadding(self, padding=0.02):
|
||||
"""
|
||||
Sets the fraction of the data range that is used to pad the view range in when auto-ranging.
|
||||
By default, this fraction is 0.02.
|
||||
"""
|
||||
self.state['defaultPadding'] = padding
|
||||
|
||||
def setAspectLocked(self, lock=True, ratio=1):
|
||||
"""
|
||||
|
@ -2,7 +2,7 @@
|
||||
import time
|
||||
import pytest
|
||||
|
||||
from pyqtgraph.Qt import QtGui, QtTest
|
||||
from pyqtgraph.Qt import QtGui, QtTest, QtCore
|
||||
import numpy as np
|
||||
import pyqtgraph as pg
|
||||
from tests.image_testing import assertImageApproved, TransposedImageItem
|
||||
@ -185,6 +185,47 @@ def test_ImageItem_axisorder():
|
||||
test_ImageItem(transpose=True)
|
||||
finally:
|
||||
pg.setConfigOptions(imageAxisOrder=origMode)
|
||||
|
||||
def test_setRect():
|
||||
def assert_equal_transforms(tr1, tr2):
|
||||
dic = { # there seems to be no easy way to get the matrix in one call:
|
||||
'tr11': ( tr1.m11(), tr2.m11() ),
|
||||
'tr12': ( tr1.m12(), tr2.m12() ),
|
||||
'tr13': ( tr1.m13(), tr2.m13() ),
|
||||
'tr21': ( tr1.m21(), tr2.m21() ),
|
||||
'tr22': ( tr1.m22(), tr2.m22() ),
|
||||
'tr23': ( tr1.m23(), tr2.m23() ),
|
||||
'tr31': ( tr1.m31(), tr2.m31() ),
|
||||
'tr32': ( tr1.m32(), tr2.m32() ),
|
||||
'tr33': ( tr1.m33(), tr2.m33() )
|
||||
}
|
||||
log_string = 'Matrix element mismatch\n'
|
||||
good = True
|
||||
for key, values in dic.items():
|
||||
val1, val2 = values
|
||||
if val1 != val2:
|
||||
good = False
|
||||
log_string += f'{key}: {val1} != {val2}\n'
|
||||
assert good, log_string
|
||||
|
||||
tr = QtGui.QTransform() # construct a reference transform
|
||||
tr.scale(2, 4) # scale 2x2 image to 4x8
|
||||
tr.translate(-1, -1) # after shifting by -1, -1
|
||||
# the transformed 2x2 image would cover (-2,-4) to (2,4).
|
||||
# Now have setRect construct the same transform:
|
||||
imgitem = pg.ImageItem(np.eye(2), rect=(-2,-4, 4,8) ) # test tuple of floats
|
||||
assert_equal_transforms(tr, imgitem.transform())
|
||||
|
||||
imgitem = pg.ImageItem(np.eye(2), rect=QtCore.QRectF(-2,-4, 4,8) ) # test QRectF
|
||||
assert_equal_transforms(tr, imgitem.transform())
|
||||
|
||||
imgitem = pg.ImageItem(np.eye(2))
|
||||
imgitem.setRect(-2,-4, 4,8) # test individual parameters
|
||||
assert_equal_transforms(tr, imgitem.transform())
|
||||
|
||||
imgitem = pg.ImageItem(np.eye(2))
|
||||
imgitem.setRect(QtCore.QRect(-2,-4, 4,8)) # test QRect argument
|
||||
assert_equal_transforms(tr, imgitem.transform())
|
||||
|
||||
|
||||
def test_dividebyzero():
|
||||
|
Loading…
x
Reference in New Issue
Block a user