2020-02-29 22:38:19 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
2021-06-06 01:22:10 +00:00
|
|
|
from ..Qt import QtGui, QtCore
|
2012-03-02 02:55:32 +00:00
|
|
|
import numpy as np
|
2013-12-22 07:08:39 +00:00
|
|
|
from ..Point import Point
|
|
|
|
from .. import debug as debug
|
2021-04-21 04:42:01 +00:00
|
|
|
from math import ceil, floor, log, log10, isfinite
|
2020-05-30 20:53:38 +00:00
|
|
|
import sys
|
2012-03-02 02:55:32 +00:00
|
|
|
import weakref
|
2013-12-22 07:08:39 +00:00
|
|
|
from .. import functions as fn
|
|
|
|
from .. import getConfigOption
|
2012-05-11 22:05:41 +00:00
|
|
|
from .GraphicsWidget import GraphicsWidget
|
2021-02-12 05:34:02 +00:00
|
|
|
import warnings
|
2012-03-02 02:55:32 +00:00
|
|
|
|
|
|
|
__all__ = ['AxisItem']
|
|
|
|
class AxisItem(GraphicsWidget):
|
2012-04-28 20:00:42 +00:00
|
|
|
"""
|
|
|
|
GraphicsItem showing a single plot axis with ticks, values, and label.
|
2020-10-15 15:43:23 +00:00
|
|
|
Can be configured to fit on any side of a plot,
|
|
|
|
Can automatically synchronize its displayed scale with ViewBox items.
|
2012-04-28 20:00:42 +00:00
|
|
|
Ticks can be extended to draw a grid.
|
2019-01-16 14:43:04 +00:00
|
|
|
If maxTickLength is negative, ticks point into the plot.
|
2012-04-28 20:00:42 +00:00
|
|
|
"""
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2019-11-20 04:43:27 +00:00
|
|
|
def __init__(self, orientation, pen=None, textPen=None, linkView=None, parent=None, maxTickLength=-5, showValues=True, text='', units='', unitPrefix='', **args):
|
2012-03-02 02:55:32 +00:00
|
|
|
"""
|
2020-10-15 15:43:23 +00:00
|
|
|
=============== ===============================================================
|
2012-04-28 20:00:42 +00:00
|
|
|
**Arguments:**
|
|
|
|
orientation one of 'left', 'right', 'top', or 'bottom'
|
|
|
|
maxTickLength (px) maximum length of ticks to draw. Negative values draw
|
|
|
|
into the plot, positive values draw outward.
|
|
|
|
linkView (ViewBox) causes the range of values displayed in the axis
|
|
|
|
to be linked to the visible range of a ViewBox.
|
2019-01-16 14:43:04 +00:00
|
|
|
showValues (bool) Whether to display values adjacent to ticks
|
2012-04-28 20:00:42 +00:00
|
|
|
pen (QPen) Pen used when drawing ticks.
|
2019-11-20 04:43:27 +00:00
|
|
|
textPen (QPen) Pen used when drawing tick labels.
|
2019-01-16 14:43:04 +00:00
|
|
|
text The text (excluding units) to display on the label for this
|
|
|
|
axis.
|
|
|
|
units The units for this axis. Units should generally be given
|
|
|
|
without any scaling prefix (eg, 'V' instead of 'mV'). The
|
|
|
|
scaling prefix will be automatically prepended based on the
|
|
|
|
range of data displayed.
|
2020-04-03 17:06:25 +00:00
|
|
|
args All extra keyword arguments become CSS style options for
|
2019-01-16 14:43:04 +00:00
|
|
|
the <span> tag which will surround the axis label and units.
|
2020-10-15 15:43:23 +00:00
|
|
|
=============== ===============================================================
|
2012-03-02 02:55:32 +00:00
|
|
|
"""
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-03-02 02:55:32 +00:00
|
|
|
GraphicsWidget.__init__(self, parent)
|
|
|
|
self.label = QtGui.QGraphicsTextItem(self)
|
2012-03-12 14:04:59 +00:00
|
|
|
self.picture = None
|
2020-06-08 03:29:28 +00:00
|
|
|
self.orientation = orientation
|
|
|
|
if orientation not in ['left', 'right', 'top', 'bottom']:
|
|
|
|
raise Exception("Orientation argument must be one of 'left', 'right', 'top', or 'bottom'.")
|
|
|
|
if orientation in ['left', 'right']:
|
2021-01-31 15:13:54 +00:00
|
|
|
self.label.setRotation(-90)
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2013-03-28 00:24:01 +00:00
|
|
|
self.style = {
|
2019-01-16 14:43:04 +00:00
|
|
|
'tickTextOffset': [5, 2], ## (horizontal, vertical) spacing between text and axis
|
2013-03-28 00:24:01 +00:00
|
|
|
'tickTextWidth': 30, ## space reserved for tick text
|
2019-01-16 14:43:04 +00:00
|
|
|
'tickTextHeight': 18,
|
2013-03-28 00:24:01 +00:00
|
|
|
'autoExpandTextSpace': True, ## automatically expand text space if needed
|
2020-06-23 06:02:48 +00:00
|
|
|
'autoReduceTextSpace': True,
|
2013-03-28 00:24:01 +00:00
|
|
|
'tickFont': None,
|
2019-01-16 14:43:04 +00:00
|
|
|
'stopAxisAtTick': (False, False), ## whether axis is drawn to edge of box or to last tick
|
|
|
|
'textFillLimits': [ ## how much of the axis to fill up with tick text, maximally.
|
2013-07-03 15:20:49 +00:00
|
|
|
(0, 0.8), ## never fill more than 80% of the axis
|
|
|
|
(2, 0.6), ## If we already have 2 ticks with text, fill no more than 60% of the axis
|
|
|
|
(4, 0.4), ## If we already have 4 ticks with text, fill no more than 40% of the axis
|
|
|
|
(6, 0.2), ## If we already have 6 ticks with text, fill no more than 20% of the axis
|
2014-03-29 15:31:23 +00:00
|
|
|
],
|
|
|
|
'showValues': showValues,
|
2014-04-08 19:29:31 +00:00
|
|
|
'tickLength': maxTickLength,
|
2014-07-25 11:52:58 +00:00
|
|
|
'maxTickLevel': 2,
|
|
|
|
'maxTextLevel': 2,
|
2020-06-25 00:42:28 +00:00
|
|
|
'tickAlpha': None, ## If not none, use this alpha for all ticks.
|
2013-03-28 00:24:01 +00:00
|
|
|
}
|
2019-01-16 14:43:04 +00:00
|
|
|
|
|
|
|
self.textWidth = 30 ## Keeps track of maximum width / height of tick text
|
2013-03-28 00:24:01 +00:00
|
|
|
self.textHeight = 18
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2014-08-30 02:50:14 +00:00
|
|
|
# If the user specifies a width / height, remember that setting
|
|
|
|
# indefinitely.
|
|
|
|
self.fixedWidth = None
|
|
|
|
self.fixedHeight = None
|
2019-01-16 14:43:04 +00:00
|
|
|
|
|
|
|
self.labelText = text
|
|
|
|
self.labelUnits = units
|
|
|
|
self.labelUnitPrefix = unitPrefix
|
|
|
|
self.labelStyle = args
|
2012-04-21 19:57:47 +00:00
|
|
|
self.logMode = False
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-05-21 21:31:09 +00:00
|
|
|
self._tickLevels = None ## used to override the automatic ticking system with explicit ticks
|
2014-07-25 11:52:58 +00:00
|
|
|
self._tickSpacing = None # used to override default tickSpacing method
|
2012-03-02 02:55:32 +00:00
|
|
|
self.scale = 1.0
|
2013-10-18 18:47:49 +00:00
|
|
|
self.autoSIPrefix = True
|
|
|
|
self.autoSIPrefixScale = 1.0
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2019-01-18 08:31:37 +00:00
|
|
|
self.showLabel(False)
|
|
|
|
|
2012-03-02 02:55:32 +00:00
|
|
|
self.setRange(0, 1)
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2014-03-01 14:37:59 +00:00
|
|
|
if pen is None:
|
|
|
|
self.setPen()
|
|
|
|
else:
|
|
|
|
self.setPen(pen)
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2019-11-20 04:43:27 +00:00
|
|
|
if textPen is None:
|
|
|
|
self.setTextPen()
|
|
|
|
else:
|
|
|
|
self.setTextPen(pen)
|
|
|
|
|
2012-03-12 14:04:59 +00:00
|
|
|
self._linkedView = None
|
2012-03-02 02:55:32 +00:00
|
|
|
if linkView is not None:
|
2021-06-07 14:44:19 +00:00
|
|
|
self._linkToView_internal(linkView)
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-03-02 02:55:32 +00:00
|
|
|
self.grid = False
|
2021-06-07 14:44:19 +00:00
|
|
|
|
2012-03-02 02:55:32 +00:00
|
|
|
#self.setCacheMode(self.DeviceCoordinateCache)
|
2014-04-08 19:29:31 +00:00
|
|
|
|
|
|
|
def setStyle(self, **kwds):
|
|
|
|
"""
|
|
|
|
Set various style options.
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2014-04-08 19:29:31 +00:00
|
|
|
=================== =======================================================
|
|
|
|
Keyword Arguments:
|
2019-01-16 14:43:04 +00:00
|
|
|
tickLength (int) The maximum length of ticks in pixels.
|
|
|
|
Positive values point toward the text; negative
|
2014-04-08 19:29:31 +00:00
|
|
|
values point away.
|
|
|
|
tickTextOffset (int) reserved spacing between text and axis in px
|
|
|
|
tickTextWidth (int) Horizontal space reserved for tick text in px
|
|
|
|
tickTextHeight (int) Vertical space reserved for tick text in px
|
|
|
|
autoExpandTextSpace (bool) Automatically expand text space if the tick
|
|
|
|
strings become too long.
|
2020-06-23 06:02:48 +00:00
|
|
|
autoReduceTextSpace (bool) Automatically shrink the axis if necessary
|
2019-01-16 14:43:04 +00:00
|
|
|
tickFont (QFont or None) Determines the font used for tick
|
2014-04-08 19:29:31 +00:00
|
|
|
values. Use None for the default font.
|
2019-01-16 14:43:04 +00:00
|
|
|
stopAxisAtTick (tuple: (bool min, bool max)) If True, the axis
|
|
|
|
line is drawn only as far as the last tick.
|
|
|
|
Otherwise, the line is drawn to the edge of the
|
2014-04-08 19:29:31 +00:00
|
|
|
AxisItem boundary.
|
|
|
|
textFillLimits (list of (tick #, % fill) tuples). This structure
|
2019-01-16 14:43:04 +00:00
|
|
|
determines how the AxisItem decides how many ticks
|
2014-04-08 19:29:31 +00:00
|
|
|
should have text appear next to them. Each tuple in
|
|
|
|
the list specifies what fraction of the axis length
|
|
|
|
may be occupied by text, given the number of ticks
|
|
|
|
that already have text displayed. For example::
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2014-04-08 19:29:31 +00:00
|
|
|
[(0, 0.8), # Never fill more than 80% of the axis
|
2019-01-16 14:43:04 +00:00
|
|
|
(2, 0.6), # If we already have 2 ticks with text,
|
2014-04-08 19:29:31 +00:00
|
|
|
# fill no more than 60% of the axis
|
2019-01-16 14:43:04 +00:00
|
|
|
(4, 0.4), # If we already have 4 ticks with text,
|
2014-04-08 19:29:31 +00:00
|
|
|
# fill no more than 40% of the axis
|
2019-01-16 14:43:04 +00:00
|
|
|
(6, 0.2)] # If we already have 6 ticks with text,
|
2014-04-08 19:29:31 +00:00
|
|
|
# fill no more than 20% of the axis
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2014-04-11 14:54:21 +00:00
|
|
|
showValues (bool) indicates whether text is displayed adjacent
|
2014-04-08 19:29:31 +00:00
|
|
|
to ticks.
|
2020-06-25 00:42:28 +00:00
|
|
|
tickAlpha (float or int or None) If None, pyqtgraph will draw the
|
|
|
|
ticks with the alpha it deems appropriate. Otherwise,
|
|
|
|
the alpha will be fixed at the value passed. With int,
|
|
|
|
accepted values are [0..255]. With vaule of type
|
|
|
|
float, accepted values are from [0..1].
|
2014-04-08 19:29:31 +00:00
|
|
|
=================== =======================================================
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2014-04-11 14:54:21 +00:00
|
|
|
Added in version 0.9.9
|
2014-04-08 19:29:31 +00:00
|
|
|
"""
|
|
|
|
for kwd,value in kwds.items():
|
|
|
|
if kwd not in self.style:
|
|
|
|
raise NameError("%s is not a valid style argument." % kwd)
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2014-04-08 19:29:31 +00:00
|
|
|
if kwd in ('tickLength', 'tickTextOffset', 'tickTextWidth', 'tickTextHeight'):
|
|
|
|
if not isinstance(value, int):
|
|
|
|
raise ValueError("Argument '%s' must be int" % kwd)
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2014-04-08 19:29:31 +00:00
|
|
|
if kwd == 'tickTextOffset':
|
|
|
|
if self.orientation in ('left', 'right'):
|
|
|
|
self.style['tickTextOffset'][0] = value
|
|
|
|
else:
|
|
|
|
self.style['tickTextOffset'][1] = value
|
|
|
|
elif kwd == 'stopAxisAtTick':
|
|
|
|
try:
|
|
|
|
assert len(value) == 2 and isinstance(value[0], bool) and isinstance(value[1], bool)
|
|
|
|
except:
|
|
|
|
raise ValueError("Argument 'stopAxisAtTick' must have type (bool, bool)")
|
|
|
|
self.style[kwd] = value
|
|
|
|
else:
|
|
|
|
self.style[kwd] = value
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2014-04-08 19:29:31 +00:00
|
|
|
self.picture = None
|
|
|
|
self._adjustSize()
|
|
|
|
self.update()
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-03-02 02:55:32 +00:00
|
|
|
def close(self):
|
|
|
|
self.scene().removeItem(self.label)
|
|
|
|
self.label = None
|
|
|
|
self.scene().removeItem(self)
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-03-02 02:55:32 +00:00
|
|
|
def setGrid(self, grid):
|
2014-04-28 12:19:53 +00:00
|
|
|
"""Set the alpha value (0-255) for the grid, or False to disable.
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2014-04-28 12:19:53 +00:00
|
|
|
When grid lines are enabled, the axis tick lines are extended to cover
|
|
|
|
the extent of the linked ViewBox, if any.
|
|
|
|
"""
|
2012-03-02 02:55:32 +00:00
|
|
|
self.grid = grid
|
2012-03-12 14:04:59 +00:00
|
|
|
self.picture = None
|
|
|
|
self.prepareGeometryChange()
|
2012-03-02 02:55:32 +00:00
|
|
|
self.update()
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-04-21 19:57:47 +00:00
|
|
|
def setLogMode(self, log):
|
2012-04-28 20:00:42 +00:00
|
|
|
"""
|
|
|
|
If *log* is True, then ticks are displayed on a logarithmic scale and values
|
2019-01-16 14:43:04 +00:00
|
|
|
are adjusted accordingly. (This is usually accessed by changing the log mode
|
2012-04-28 20:00:42 +00:00
|
|
|
of a :func:`PlotItem <pyqtgraph.PlotItem.setLogMode>`)
|
|
|
|
"""
|
2012-04-21 19:57:47 +00:00
|
|
|
self.logMode = log
|
|
|
|
self.picture = None
|
|
|
|
self.update()
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2013-01-24 18:47:05 +00:00
|
|
|
def setTickFont(self, font):
|
2020-05-04 21:58:29 +00:00
|
|
|
"""
|
|
|
|
(QFont or None) Determines the font used for tick values.
|
|
|
|
Use None for the default font.
|
|
|
|
"""
|
|
|
|
self.style['tickFont'] = font
|
2013-01-24 18:47:05 +00:00
|
|
|
self.picture = None
|
|
|
|
self.prepareGeometryChange()
|
|
|
|
## Need to re-allocate space depending on font size?
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2013-01-24 18:47:05 +00:00
|
|
|
self.update()
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-03-02 02:55:32 +00:00
|
|
|
def resizeEvent(self, ev=None):
|
|
|
|
#s = self.size()
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-03-02 02:55:32 +00:00
|
|
|
## Set the position of the label
|
|
|
|
nudge = 5
|
2021-07-18 08:24:55 +00:00
|
|
|
if self.label is None: # self.label is set to None on close, but resize events can still occur.
|
|
|
|
self.picture = None
|
|
|
|
return
|
|
|
|
|
2012-03-02 02:55:32 +00:00
|
|
|
br = self.label.boundingRect()
|
|
|
|
p = QtCore.QPointF(0, 0)
|
|
|
|
if self.orientation == 'left':
|
|
|
|
p.setY(int(self.size().height()/2 + br.width()/2))
|
|
|
|
p.setX(-nudge)
|
|
|
|
elif self.orientation == 'right':
|
|
|
|
p.setY(int(self.size().height()/2 + br.width()/2))
|
|
|
|
p.setX(int(self.size().width()-br.height()+nudge))
|
|
|
|
elif self.orientation == 'top':
|
|
|
|
p.setY(-nudge)
|
|
|
|
p.setX(int(self.size().width()/2. - br.width()/2.))
|
|
|
|
elif self.orientation == 'bottom':
|
|
|
|
p.setX(int(self.size().width()/2. - br.width()/2.))
|
|
|
|
p.setY(int(self.size().height()-br.height()+nudge))
|
|
|
|
self.label.setPos(p)
|
2012-03-12 14:04:59 +00:00
|
|
|
self.picture = None
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-03-02 02:55:32 +00:00
|
|
|
def showLabel(self, show=True):
|
2012-04-28 20:00:42 +00:00
|
|
|
"""Show/hide the label text for this axis."""
|
2012-03-02 02:55:32 +00:00
|
|
|
#self.drawLabel = show
|
|
|
|
self.label.setVisible(show)
|
|
|
|
if self.orientation in ['left', 'right']:
|
2014-08-30 02:50:14 +00:00
|
|
|
self._updateWidth()
|
2012-03-02 02:55:32 +00:00
|
|
|
else:
|
2014-08-30 02:50:14 +00:00
|
|
|
self._updateHeight()
|
2013-10-18 18:47:49 +00:00
|
|
|
if self.autoSIPrefix:
|
|
|
|
self.updateAutoSIPrefix()
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-03-02 02:55:32 +00:00
|
|
|
def setLabel(self, text=None, units=None, unitPrefix=None, **args):
|
2013-01-19 12:48:31 +00:00
|
|
|
"""Set the text displayed adjacent to the axis.
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2014-02-05 20:04:33 +00:00
|
|
|
============== =============================================================
|
|
|
|
**Arguments:**
|
|
|
|
text The text (excluding units) to display on the label for this
|
|
|
|
axis.
|
|
|
|
units The units for this axis. Units should generally be given
|
|
|
|
without any scaling prefix (eg, 'V' instead of 'mV'). The
|
|
|
|
scaling prefix will be automatically prepended based on the
|
|
|
|
range of data displayed.
|
2020-04-03 17:06:25 +00:00
|
|
|
args All extra keyword arguments become CSS style options for
|
2014-02-05 20:04:33 +00:00
|
|
|
the <span> tag which will surround the axis label and units.
|
|
|
|
============== =============================================================
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2013-01-19 12:48:31 +00:00
|
|
|
The final text generated for the label will look like::
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2013-01-19 12:48:31 +00:00
|
|
|
<span style="...options...">{text} (prefix{units})</span>
|
2019-01-16 14:43:04 +00:00
|
|
|
|
|
|
|
Each extra keyword argument will become a CSS option in the above template.
|
2013-01-19 12:48:31 +00:00
|
|
|
For example, you can set the font size and color of the label::
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2013-01-19 12:48:31 +00:00
|
|
|
labelStyle = {'color': '#FFF', 'font-size': '14pt'}
|
|
|
|
axis.setLabel('label text', units='V', **labelStyle)
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2013-01-19 12:48:31 +00:00
|
|
|
"""
|
2020-10-15 03:40:54 +00:00
|
|
|
# `None` input is kept for backward compatibility!
|
|
|
|
self.labelText = text or ""
|
|
|
|
self.labelUnits = units or ""
|
|
|
|
self.labelUnitPrefix = unitPrefix or ""
|
2012-03-02 02:55:32 +00:00
|
|
|
if len(args) > 0:
|
|
|
|
self.labelStyle = args
|
2020-10-15 03:40:54 +00:00
|
|
|
# Account empty string and `None` for units and text
|
|
|
|
visible = True if (text or units) else False
|
|
|
|
self.showLabel(visible)
|
|
|
|
self._updateLabel()
|
|
|
|
|
|
|
|
def _updateLabel(self):
|
|
|
|
"""Internal method to update the label according to the text"""
|
2012-03-02 02:55:32 +00:00
|
|
|
self.label.setHtml(self.labelString())
|
2013-03-28 00:24:01 +00:00
|
|
|
self._adjustSize()
|
2012-03-12 14:04:59 +00:00
|
|
|
self.picture = None
|
2012-03-02 02:55:32 +00:00
|
|
|
self.update()
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-03-02 02:55:32 +00:00
|
|
|
def labelString(self):
|
|
|
|
if self.labelUnits == '':
|
2013-10-18 18:47:49 +00:00
|
|
|
if not self.autoSIPrefix or self.autoSIPrefixScale == 1.0:
|
2012-03-02 02:55:32 +00:00
|
|
|
units = ''
|
|
|
|
else:
|
2021-08-02 04:43:32 +00:00
|
|
|
units = '(x%g)' % (1.0/self.autoSIPrefixScale)
|
2012-03-02 02:55:32 +00:00
|
|
|
else:
|
2021-08-02 04:43:32 +00:00
|
|
|
units = '(%s%s)' % (self.labelUnitPrefix, self.labelUnits)
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2021-08-02 04:43:32 +00:00
|
|
|
s = '%s %s' % (self.labelText, units)
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-08-31 21:18:06 +00:00
|
|
|
style = ';'.join(['%s: %s' % (k, self.labelStyle[k]) for k in self.labelStyle])
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2021-08-02 04:43:32 +00:00
|
|
|
return "<span style='%s'>%s</span>" % (style, s)
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2013-03-28 00:24:01 +00:00
|
|
|
def _updateMaxTextSize(self, x):
|
|
|
|
## Informs that the maximum tick size orthogonal to the axis has
|
|
|
|
## changed; we use this to decide whether the item needs to be resized
|
2019-06-24 04:41:20 +00:00
|
|
|
## to accomodate.
|
2013-03-28 00:24:01 +00:00
|
|
|
if self.orientation in ['left', 'right']:
|
2020-06-23 06:02:48 +00:00
|
|
|
if self.style["autoReduceTextSpace"]:
|
|
|
|
if x > self.textWidth or x < self.textWidth - 10:
|
|
|
|
self.textWidth = x
|
|
|
|
else:
|
|
|
|
mx = max(self.textWidth, x)
|
|
|
|
if mx > self.textWidth or mx < self.textWidth - 10:
|
|
|
|
self.textWidth = mx
|
|
|
|
if self.style['autoExpandTextSpace']:
|
|
|
|
self._updateWidth()
|
|
|
|
|
2013-03-28 00:24:01 +00:00
|
|
|
else:
|
2020-06-23 06:02:48 +00:00
|
|
|
if self.style['autoReduceTextSpace']:
|
|
|
|
if x > self.textHeight or x < self.textHeight - 10:
|
|
|
|
self.textHeight = x
|
|
|
|
else:
|
|
|
|
mx = max(self.textHeight, x)
|
|
|
|
if mx > self.textHeight or mx < self.textHeight - 10:
|
|
|
|
self.textHeight = mx
|
|
|
|
if self.style['autoExpandTextSpace']:
|
|
|
|
self._updateHeight()
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2013-03-28 00:24:01 +00:00
|
|
|
def _adjustSize(self):
|
|
|
|
if self.orientation in ['left', 'right']:
|
2014-08-30 02:50:14 +00:00
|
|
|
self._updateWidth()
|
2013-03-28 00:24:01 +00:00
|
|
|
else:
|
2014-08-30 02:50:14 +00:00
|
|
|
self._updateHeight()
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-03-02 02:55:32 +00:00
|
|
|
def setHeight(self, h=None):
|
2012-10-19 02:51:46 +00:00
|
|
|
"""Set the height of this axis reserved for ticks and tick labels.
|
2014-08-30 02:50:14 +00:00
|
|
|
The height of the axis label is automatically added.
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2014-08-30 02:50:14 +00:00
|
|
|
If *height* is None, then the value will be determined automatically
|
|
|
|
based on the size of the tick text."""
|
|
|
|
self.fixedHeight = h
|
|
|
|
self._updateHeight()
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2014-08-30 02:50:14 +00:00
|
|
|
def _updateHeight(self):
|
|
|
|
if not self.isVisible():
|
|
|
|
h = 0
|
|
|
|
else:
|
|
|
|
if self.fixedHeight is None:
|
|
|
|
if not self.style['showValues']:
|
|
|
|
h = 0
|
2020-06-24 05:22:51 +00:00
|
|
|
elif self.style['autoExpandTextSpace']:
|
2014-08-30 02:50:14 +00:00
|
|
|
h = self.textHeight
|
|
|
|
else:
|
|
|
|
h = self.style['tickTextHeight']
|
|
|
|
h += self.style['tickTextOffset'][1] if self.style['showValues'] else 0
|
|
|
|
h += max(0, self.style['tickLength'])
|
|
|
|
if self.label.isVisible():
|
|
|
|
h += self.label.boundingRect().height() * 0.8
|
2014-03-29 15:31:23 +00:00
|
|
|
else:
|
2014-08-30 02:50:14 +00:00
|
|
|
h = self.fixedHeight
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-03-02 02:55:32 +00:00
|
|
|
self.setMaximumHeight(h)
|
|
|
|
self.setMinimumHeight(h)
|
2012-03-12 14:04:59 +00:00
|
|
|
self.picture = None
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-03-02 02:55:32 +00:00
|
|
|
def setWidth(self, w=None):
|
2012-10-19 02:51:46 +00:00
|
|
|
"""Set the width of this axis reserved for ticks and tick labels.
|
2014-08-30 02:50:14 +00:00
|
|
|
The width of the axis label is automatically added.
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2014-08-30 02:50:14 +00:00
|
|
|
If *width* is None, then the value will be determined automatically
|
|
|
|
based on the size of the tick text."""
|
|
|
|
self.fixedWidth = w
|
|
|
|
self._updateWidth()
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2014-08-30 02:50:14 +00:00
|
|
|
def _updateWidth(self):
|
|
|
|
if not self.isVisible():
|
|
|
|
w = 0
|
|
|
|
else:
|
|
|
|
if self.fixedWidth is None:
|
|
|
|
if not self.style['showValues']:
|
|
|
|
w = 0
|
2020-06-24 05:22:51 +00:00
|
|
|
elif self.style['autoExpandTextSpace']:
|
2014-08-30 02:50:14 +00:00
|
|
|
w = self.textWidth
|
|
|
|
else:
|
|
|
|
w = self.style['tickTextWidth']
|
|
|
|
w += self.style['tickTextOffset'][0] if self.style['showValues'] else 0
|
|
|
|
w += max(0, self.style['tickLength'])
|
|
|
|
if self.label.isVisible():
|
|
|
|
w += self.label.boundingRect().height() * 0.8 ## bounding rect is usually an overestimate
|
2014-03-29 15:31:23 +00:00
|
|
|
else:
|
2014-08-30 02:50:14 +00:00
|
|
|
w = self.fixedWidth
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-03-02 02:55:32 +00:00
|
|
|
self.setMaximumWidth(w)
|
|
|
|
self.setMinimumWidth(w)
|
2013-03-28 00:24:01 +00:00
|
|
|
self.picture = None
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-06-29 18:39:27 +00:00
|
|
|
def pen(self):
|
|
|
|
if self._pen is None:
|
2013-12-22 07:08:39 +00:00
|
|
|
return fn.mkPen(getConfigOption('foreground'))
|
|
|
|
return fn.mkPen(self._pen)
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2014-02-28 21:16:13 +00:00
|
|
|
def setPen(self, *args, **kwargs):
|
2012-06-29 18:39:27 +00:00
|
|
|
"""
|
|
|
|
Set the pen used for drawing text, axes, ticks, and grid lines.
|
2019-01-16 14:43:04 +00:00
|
|
|
If no arguments are given, the default foreground color will be used
|
2014-03-01 14:37:59 +00:00
|
|
|
(see :func:`setConfigOption <pyqtgraph.setConfigOption>`).
|
2012-06-29 18:39:27 +00:00
|
|
|
"""
|
2012-03-12 14:04:59 +00:00
|
|
|
self.picture = None
|
2014-03-01 14:37:59 +00:00
|
|
|
if args or kwargs:
|
|
|
|
self._pen = fn.mkPen(*args, **kwargs)
|
|
|
|
else:
|
|
|
|
self._pen = fn.mkPen(getConfigOption('foreground'))
|
2014-01-15 05:11:05 +00:00
|
|
|
self.labelStyle['color'] = '#' + fn.colorStr(self._pen.color())[:6]
|
2020-10-15 03:40:54 +00:00
|
|
|
self._updateLabel()
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2019-11-20 04:43:27 +00:00
|
|
|
def textPen(self):
|
|
|
|
if self._textPen is None:
|
|
|
|
return fn.mkPen(getConfigOption('foreground'))
|
|
|
|
return fn.mkPen(self._textPen)
|
|
|
|
|
|
|
|
def setTextPen(self, *args, **kwargs):
|
|
|
|
"""
|
|
|
|
Set the pen used for drawing text.
|
|
|
|
If no arguments are given, the default foreground color will be used.
|
|
|
|
"""
|
|
|
|
self.picture = None
|
|
|
|
if args or kwargs:
|
|
|
|
self._textPen = fn.mkPen(*args, **kwargs)
|
|
|
|
else:
|
|
|
|
self._textPen = fn.mkPen(getConfigOption('foreground'))
|
|
|
|
self.labelStyle['color'] = '#' + fn.colorStr(self._textPen.color())[:6]
|
2020-10-15 03:40:54 +00:00
|
|
|
self._updateLabel()
|
2019-11-20 04:43:27 +00:00
|
|
|
|
2012-03-02 02:55:32 +00:00
|
|
|
def setScale(self, scale=None):
|
|
|
|
"""
|
2019-01-16 14:43:04 +00:00
|
|
|
Set the value scaling for this axis.
|
|
|
|
|
2013-10-18 18:59:17 +00:00
|
|
|
Setting this value causes the axis to draw ticks and tick labels as if
|
2019-01-16 14:43:04 +00:00
|
|
|
the view coordinate system were scaled. By default, the axis scaling is
|
2013-10-18 18:59:17 +00:00
|
|
|
1.0.
|
2012-03-02 02:55:32 +00:00
|
|
|
"""
|
2013-10-18 19:02:17 +00:00
|
|
|
# Deprecated usage, kept for backward compatibility
|
2019-01-16 14:43:04 +00:00
|
|
|
if scale is None:
|
2021-02-12 05:34:02 +00:00
|
|
|
warnings.warn(
|
|
|
|
'AxisItem.setScale(None) is deprecated, will be removed in 0.13.0'
|
|
|
|
'instead use AxisItem.enableAutoSIPrefix(bool) to enable/disable'
|
|
|
|
'SI prefix scaling',
|
|
|
|
DeprecationWarning, stacklevel=2
|
|
|
|
)
|
2013-10-18 19:02:17 +00:00
|
|
|
scale = 1.0
|
|
|
|
self.enableAutoSIPrefix(True)
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-03-02 02:55:32 +00:00
|
|
|
if scale != self.scale:
|
|
|
|
self.scale = scale
|
2020-10-15 03:40:54 +00:00
|
|
|
self._updateLabel()
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2013-10-18 18:47:49 +00:00
|
|
|
def enableAutoSIPrefix(self, enable=True):
|
2013-10-18 18:59:17 +00:00
|
|
|
"""
|
2019-01-16 14:43:04 +00:00
|
|
|
Enable (or disable) automatic SI prefix scaling on this axis.
|
|
|
|
|
|
|
|
When enabled, this feature automatically determines the best SI prefix
|
2013-10-18 18:59:17 +00:00
|
|
|
to prepend to the label units, while ensuring that axis values are scaled
|
2019-01-16 14:43:04 +00:00
|
|
|
accordingly.
|
|
|
|
|
|
|
|
For example, if the axis spans values from -0.1 to 0.1 and has units set
|
2013-10-18 18:59:17 +00:00
|
|
|
to 'V' then the axis would display values -100 to 100
|
|
|
|
and the units would appear as 'mV'
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2013-10-18 18:59:17 +00:00
|
|
|
This feature is enabled by default, and is only available when a suffix
|
|
|
|
(unit string) is provided to display on the label.
|
|
|
|
"""
|
2013-10-18 18:47:49 +00:00
|
|
|
self.autoSIPrefix = enable
|
2013-10-18 19:10:44 +00:00
|
|
|
self.updateAutoSIPrefix()
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2013-10-18 18:47:49 +00:00
|
|
|
def updateAutoSIPrefix(self):
|
|
|
|
if self.label.isVisible():
|
2019-09-13 04:58:49 +00:00
|
|
|
if self.logMode:
|
|
|
|
_range = 10**np.array(self.range)
|
|
|
|
else:
|
|
|
|
_range = self.range
|
|
|
|
(scale, prefix) = fn.siScale(max(abs(_range[0]*self.scale), abs(_range[1]*self.scale)))
|
2013-10-18 18:47:49 +00:00
|
|
|
if self.labelUnits == '' and prefix in ['k', 'm']: ## If we are not showing units, wait until 1e6 before scaling.
|
|
|
|
scale = 1.0
|
|
|
|
prefix = ''
|
2019-06-27 18:45:37 +00:00
|
|
|
self.autoSIPrefixScale = scale
|
2020-10-15 03:40:54 +00:00
|
|
|
self.labelUnitPrefix = prefix
|
2013-10-18 18:47:49 +00:00
|
|
|
else:
|
2019-06-27 18:45:37 +00:00
|
|
|
self.autoSIPrefixScale = 1.0
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2020-10-15 03:40:54 +00:00
|
|
|
self._updateLabel()
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-03-02 02:55:32 +00:00
|
|
|
def setRange(self, mn, mx):
|
2012-05-21 21:31:09 +00:00
|
|
|
"""Set the range of values displayed by the axis.
|
|
|
|
Usually this is handled automatically by linking the axis to a ViewBox with :func:`linkToView <pyqtgraph.AxisItem.linkToView>`"""
|
2021-04-21 04:42:01 +00:00
|
|
|
if not isfinite(mn) or not isfinite(mx):
|
2012-03-02 02:55:32 +00:00
|
|
|
raise Exception("Not setting range to [%s, %s]" % (str(mn), str(mx)))
|
|
|
|
self.range = [mn, mx]
|
2013-10-18 18:47:49 +00:00
|
|
|
if self.autoSIPrefix:
|
2020-10-15 03:40:54 +00:00
|
|
|
# XXX: Will already update once!
|
2013-10-18 18:47:49 +00:00
|
|
|
self.updateAutoSIPrefix()
|
2020-10-15 03:40:54 +00:00
|
|
|
else:
|
|
|
|
self.picture = None
|
|
|
|
self.update()
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-03-12 14:04:59 +00:00
|
|
|
def linkedView(self):
|
|
|
|
"""Return the ViewBox this axis is linked to"""
|
|
|
|
if self._linkedView is None:
|
|
|
|
return None
|
|
|
|
else:
|
|
|
|
return self._linkedView()
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2021-06-07 14:44:19 +00:00
|
|
|
def _linkToView_internal(self, view):
|
|
|
|
# We need this code to be available without override,
|
|
|
|
# even though DateAxisItem overrides the user-side linkToView method
|
Date axis item (#1154)
* Add DateAxisItem
* Change style to camelCase
* Fix missing first tick for negative timestamps
* Add ms precision, auto skipping
Auto skipping allows a zoom level to skip ticks automatically if the
maximum number of ticks/pt is exceeded
* fixes suggested by @goetzc
* workaround for negative argument to utcfromtimestamp on windows
* attachToPlotItem method
* default date axis orientation
* Use new DateAxisItem in Plot Customization example
* attachToPlotItem bugfix
* examples of DateAxisItem
* modified description of customPlot example
* added descriptions to the new examples, reformatted their code, included the first one into utils.py
* typo
* Refactored code for setting axis items into new function
Replaces "DateAxisItem.attachToPlotItem"
* Fix string comparison with ==
* Doc: Slightly more text for DateAxisItem, small improvement for PlotItem
* Make PlotWidget.setAxisItems official
* Fix typo in docstring
* renamed an example
* merge bug fix
* Revert "merge bug fix"
This reverts commit 876b5a7cdb50cd824b4a5218427081b3ce5c2fe4.
* Real bug fix
* support for dates upto -1e13..1e13
* Automatically limit DateAxisItem to a range from -1e12 to 1e12 years
Very large years (|y|>1e13) cause infinite loop, and since nobody
needs time 100 times larger than the age of the universe anyways,
this constrains it to 1e12.
Following suggestion by @axil:
https://github.com/pyqtgraph/pyqtgraph/pull/1154#issuecomment-612662168
* Also catch ValueErrors occuring on Linux before OverfloeErrors
While zooming out, before hitting OverflowErrors, utctimestamp
produces ValueErrors (at least on my Linux machine), so they
are also catched.
* Fix: Timestamp 0 corresponds to year 1970
For large years, x axis labels jump by 1970 years if it is not
accounted for timestamp 0 to be equal to year 1970.
* Fix: When zooming into extreme dates, OSError occurs
This commit catches the OSError like the other observed errors
* Disable stepping below years for dates outside *_REGULAR_TIMESTAMP
2 reasons: First: At least on my Linux machine, zooming into
those dates creates infinite loops. Second: Nobody needs
sub-year-precision for those extreme years anyways.
* Adapt zoom level sizes based on current font size and screen resolution
This is somewhat experimental. With this commit, no longer 60 px are
assumed as width for all zoom levels, but the current font and
display resolution are considered to calculate the width of ticks in
each zoom level. See the new function `updateZoomLevels` for
details.
Before calling this function, overridden functions `paint` and
`generateDrawSpecs` provide information over the current display
and font via `self.fontScaleFactor` and `self.fontMetrics`.
* Meaningful error meassage when adding axis to multiple PlotItems
As @axil noted in the DateAxisItem PR, currently users get a
segmentation fault when one tries to add an axis to multiple
PlotItems. This commit adds a meaningful RuntimeError message
for that case.
* setZoomLevelForDensity: Refactoring and calculating optimal spacing on the fly
* DateTimeAxis Fix: 1970 shows when zooming far out
* Refactoring: Make zoomLevels a customizable dict again
* updated the dateaxisitem example
* Fix: Get screen resolution in a way that also works for Qt 4
This is both a simplification in code and an improvement in backwards compatibility with Qt 4.
* DateAxisItem Fix: Also resolve time below 0.5 seconds
* unix line endings in examples
* DateTimeAxis Fix: For years < 1 and > 9999, stepping broke
Stepping was off by 1970 years for years < 1 and > 9999,
resulting in a gap in ticks visible when zooming out. Fixed by
subtracting the usual 1970 years.
* DateTimeAxis Fix: Zooming out too far causes infinite loop
Fixed by setting default limits to +/- 1e10 years. Should still
be enough.
* improved second dateaxisitem example
* 1..9999 years limit
* DateTimeAxis: Use OrderedDict to stay compatible with Python < 3-6
* DateAxisItem: Use font height to determine spacing for vertical axes
* window title
* added dateaxisitem.rst
* updated index.rst
Co-authored-by: Lukas Heiniger <lukas.heiniger@sed.ethz.ch>
Co-authored-by: Lev Maximov <lev.maximov@gmail.com>
Co-authored-by: 2xB <2xB@users.noreply.github.com>
2020-04-27 18:43:22 +00:00
|
|
|
self.unlinkFromView()
|
|
|
|
|
2012-03-12 14:04:59 +00:00
|
|
|
self._linkedView = weakref.ref(view)
|
Date axis item (#1154)
* Add DateAxisItem
* Change style to camelCase
* Fix missing first tick for negative timestamps
* Add ms precision, auto skipping
Auto skipping allows a zoom level to skip ticks automatically if the
maximum number of ticks/pt is exceeded
* fixes suggested by @goetzc
* workaround for negative argument to utcfromtimestamp on windows
* attachToPlotItem method
* default date axis orientation
* Use new DateAxisItem in Plot Customization example
* attachToPlotItem bugfix
* examples of DateAxisItem
* modified description of customPlot example
* added descriptions to the new examples, reformatted their code, included the first one into utils.py
* typo
* Refactored code for setting axis items into new function
Replaces "DateAxisItem.attachToPlotItem"
* Fix string comparison with ==
* Doc: Slightly more text for DateAxisItem, small improvement for PlotItem
* Make PlotWidget.setAxisItems official
* Fix typo in docstring
* renamed an example
* merge bug fix
* Revert "merge bug fix"
This reverts commit 876b5a7cdb50cd824b4a5218427081b3ce5c2fe4.
* Real bug fix
* support for dates upto -1e13..1e13
* Automatically limit DateAxisItem to a range from -1e12 to 1e12 years
Very large years (|y|>1e13) cause infinite loop, and since nobody
needs time 100 times larger than the age of the universe anyways,
this constrains it to 1e12.
Following suggestion by @axil:
https://github.com/pyqtgraph/pyqtgraph/pull/1154#issuecomment-612662168
* Also catch ValueErrors occuring on Linux before OverfloeErrors
While zooming out, before hitting OverflowErrors, utctimestamp
produces ValueErrors (at least on my Linux machine), so they
are also catched.
* Fix: Timestamp 0 corresponds to year 1970
For large years, x axis labels jump by 1970 years if it is not
accounted for timestamp 0 to be equal to year 1970.
* Fix: When zooming into extreme dates, OSError occurs
This commit catches the OSError like the other observed errors
* Disable stepping below years for dates outside *_REGULAR_TIMESTAMP
2 reasons: First: At least on my Linux machine, zooming into
those dates creates infinite loops. Second: Nobody needs
sub-year-precision for those extreme years anyways.
* Adapt zoom level sizes based on current font size and screen resolution
This is somewhat experimental. With this commit, no longer 60 px are
assumed as width for all zoom levels, but the current font and
display resolution are considered to calculate the width of ticks in
each zoom level. See the new function `updateZoomLevels` for
details.
Before calling this function, overridden functions `paint` and
`generateDrawSpecs` provide information over the current display
and font via `self.fontScaleFactor` and `self.fontMetrics`.
* Meaningful error meassage when adding axis to multiple PlotItems
As @axil noted in the DateAxisItem PR, currently users get a
segmentation fault when one tries to add an axis to multiple
PlotItems. This commit adds a meaningful RuntimeError message
for that case.
* setZoomLevelForDensity: Refactoring and calculating optimal spacing on the fly
* DateTimeAxis Fix: 1970 shows when zooming far out
* Refactoring: Make zoomLevels a customizable dict again
* updated the dateaxisitem example
* Fix: Get screen resolution in a way that also works for Qt 4
This is both a simplification in code and an improvement in backwards compatibility with Qt 4.
* DateAxisItem Fix: Also resolve time below 0.5 seconds
* unix line endings in examples
* DateTimeAxis Fix: For years < 1 and > 9999, stepping broke
Stepping was off by 1970 years for years < 1 and > 9999,
resulting in a gap in ticks visible when zooming out. Fixed by
subtracting the usual 1970 years.
* DateTimeAxis Fix: Zooming out too far causes infinite loop
Fixed by setting default limits to +/- 1e10 years. Should still
be enough.
* improved second dateaxisitem example
* 1..9999 years limit
* DateTimeAxis: Use OrderedDict to stay compatible with Python < 3-6
* DateAxisItem: Use font height to determine spacing for vertical axes
* window title
* added dateaxisitem.rst
* updated index.rst
Co-authored-by: Lukas Heiniger <lukas.heiniger@sed.ethz.ch>
Co-authored-by: Lev Maximov <lev.maximov@gmail.com>
Co-authored-by: 2xB <2xB@users.noreply.github.com>
2020-04-27 18:43:22 +00:00
|
|
|
if self.orientation in ['right', 'left']:
|
|
|
|
view.sigYRangeChanged.connect(self.linkedViewChanged)
|
|
|
|
else:
|
|
|
|
view.sigXRangeChanged.connect(self.linkedViewChanged)
|
|
|
|
view.sigResized.connect(self.linkedViewChanged)
|
2021-06-07 14:44:19 +00:00
|
|
|
|
|
|
|
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)
|
Date axis item (#1154)
* Add DateAxisItem
* Change style to camelCase
* Fix missing first tick for negative timestamps
* Add ms precision, auto skipping
Auto skipping allows a zoom level to skip ticks automatically if the
maximum number of ticks/pt is exceeded
* fixes suggested by @goetzc
* workaround for negative argument to utcfromtimestamp on windows
* attachToPlotItem method
* default date axis orientation
* Use new DateAxisItem in Plot Customization example
* attachToPlotItem bugfix
* examples of DateAxisItem
* modified description of customPlot example
* added descriptions to the new examples, reformatted their code, included the first one into utils.py
* typo
* Refactored code for setting axis items into new function
Replaces "DateAxisItem.attachToPlotItem"
* Fix string comparison with ==
* Doc: Slightly more text for DateAxisItem, small improvement for PlotItem
* Make PlotWidget.setAxisItems official
* Fix typo in docstring
* renamed an example
* merge bug fix
* Revert "merge bug fix"
This reverts commit 876b5a7cdb50cd824b4a5218427081b3ce5c2fe4.
* Real bug fix
* support for dates upto -1e13..1e13
* Automatically limit DateAxisItem to a range from -1e12 to 1e12 years
Very large years (|y|>1e13) cause infinite loop, and since nobody
needs time 100 times larger than the age of the universe anyways,
this constrains it to 1e12.
Following suggestion by @axil:
https://github.com/pyqtgraph/pyqtgraph/pull/1154#issuecomment-612662168
* Also catch ValueErrors occuring on Linux before OverfloeErrors
While zooming out, before hitting OverflowErrors, utctimestamp
produces ValueErrors (at least on my Linux machine), so they
are also catched.
* Fix: Timestamp 0 corresponds to year 1970
For large years, x axis labels jump by 1970 years if it is not
accounted for timestamp 0 to be equal to year 1970.
* Fix: When zooming into extreme dates, OSError occurs
This commit catches the OSError like the other observed errors
* Disable stepping below years for dates outside *_REGULAR_TIMESTAMP
2 reasons: First: At least on my Linux machine, zooming into
those dates creates infinite loops. Second: Nobody needs
sub-year-precision for those extreme years anyways.
* Adapt zoom level sizes based on current font size and screen resolution
This is somewhat experimental. With this commit, no longer 60 px are
assumed as width for all zoom levels, but the current font and
display resolution are considered to calculate the width of ticks in
each zoom level. See the new function `updateZoomLevels` for
details.
Before calling this function, overridden functions `paint` and
`generateDrawSpecs` provide information over the current display
and font via `self.fontScaleFactor` and `self.fontMetrics`.
* Meaningful error meassage when adding axis to multiple PlotItems
As @axil noted in the DateAxisItem PR, currently users get a
segmentation fault when one tries to add an axis to multiple
PlotItems. This commit adds a meaningful RuntimeError message
for that case.
* setZoomLevelForDensity: Refactoring and calculating optimal spacing on the fly
* DateTimeAxis Fix: 1970 shows when zooming far out
* Refactoring: Make zoomLevels a customizable dict again
* updated the dateaxisitem example
* Fix: Get screen resolution in a way that also works for Qt 4
This is both a simplification in code and an improvement in backwards compatibility with Qt 4.
* DateAxisItem Fix: Also resolve time below 0.5 seconds
* unix line endings in examples
* DateTimeAxis Fix: For years < 1 and > 9999, stepping broke
Stepping was off by 1970 years for years < 1 and > 9999,
resulting in a gap in ticks visible when zooming out. Fixed by
subtracting the usual 1970 years.
* DateTimeAxis Fix: Zooming out too far causes infinite loop
Fixed by setting default limits to +/- 1e10 years. Should still
be enough.
* improved second dateaxisitem example
* 1..9999 years limit
* DateTimeAxis: Use OrderedDict to stay compatible with Python < 3-6
* DateAxisItem: Use font height to determine spacing for vertical axes
* window title
* added dateaxisitem.rst
* updated index.rst
Co-authored-by: Lukas Heiniger <lukas.heiniger@sed.ethz.ch>
Co-authored-by: Lev Maximov <lev.maximov@gmail.com>
Co-authored-by: 2xB <2xB@users.noreply.github.com>
2020-04-27 18:43:22 +00:00
|
|
|
|
|
|
|
def unlinkFromView(self):
|
|
|
|
"""Unlink this axis from a ViewBox."""
|
|
|
|
oldView = self.linkedView()
|
|
|
|
self._linkedView = None
|
2012-03-02 02:55:32 +00:00
|
|
|
if self.orientation in ['right', 'left']:
|
2012-03-12 14:04:59 +00:00
|
|
|
if oldView is not None:
|
|
|
|
oldView.sigYRangeChanged.disconnect(self.linkedViewChanged)
|
2012-03-02 02:55:32 +00:00
|
|
|
else:
|
2012-03-12 14:04:59 +00:00
|
|
|
if oldView is not None:
|
|
|
|
oldView.sigXRangeChanged.disconnect(self.linkedViewChanged)
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2013-02-27 02:54:56 +00:00
|
|
|
if oldView is not None:
|
|
|
|
oldView.sigResized.disconnect(self.linkedViewChanged)
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2013-02-27 02:54:56 +00:00
|
|
|
def linkedViewChanged(self, view, newRange=None):
|
2013-03-19 15:49:10 +00:00
|
|
|
if self.orientation in ['right', 'left']:
|
2013-02-27 02:54:56 +00:00
|
|
|
if newRange is None:
|
|
|
|
newRange = view.viewRange()[1]
|
2013-03-19 15:49:10 +00:00
|
|
|
if view.yInverted():
|
|
|
|
self.setRange(*newRange[::-1])
|
|
|
|
else:
|
|
|
|
self.setRange(*newRange)
|
2012-06-18 17:50:44 +00:00
|
|
|
else:
|
2013-02-27 02:54:56 +00:00
|
|
|
if newRange is None:
|
|
|
|
newRange = view.viewRange()[0]
|
2014-04-28 11:36:59 +00:00
|
|
|
if view.xInverted():
|
|
|
|
self.setRange(*newRange[::-1])
|
|
|
|
else:
|
|
|
|
self.setRange(*newRange)
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-03-02 02:55:32 +00:00
|
|
|
def boundingRect(self):
|
2012-03-12 14:04:59 +00:00
|
|
|
linkedView = self.linkedView()
|
|
|
|
if linkedView is None or self.grid is False:
|
2012-03-02 02:55:32 +00:00
|
|
|
rect = self.mapRectFromParent(self.geometry())
|
|
|
|
## extend rect if ticks go in negative direction
|
2013-01-24 18:47:05 +00:00
|
|
|
## also extend to account for text that flows past the edges
|
2014-04-08 19:29:31 +00:00
|
|
|
tl = self.style['tickLength']
|
2012-03-02 02:55:32 +00:00
|
|
|
if self.orientation == 'left':
|
2014-04-08 19:29:31 +00:00
|
|
|
rect = rect.adjusted(0, -15, -min(0,tl), 15)
|
2012-03-02 02:55:32 +00:00
|
|
|
elif self.orientation == 'right':
|
2014-04-08 19:29:31 +00:00
|
|
|
rect = rect.adjusted(min(0,tl), -15, 0, 15)
|
2012-03-02 02:55:32 +00:00
|
|
|
elif self.orientation == 'top':
|
2014-04-08 19:29:31 +00:00
|
|
|
rect = rect.adjusted(-15, 0, 15, -min(0,tl))
|
2012-03-02 02:55:32 +00:00
|
|
|
elif self.orientation == 'bottom':
|
2014-04-08 19:29:31 +00:00
|
|
|
rect = rect.adjusted(-15, min(0,tl), 15, 0)
|
2012-03-02 02:55:32 +00:00
|
|
|
return rect
|
|
|
|
else:
|
2012-03-12 14:04:59 +00:00
|
|
|
return self.mapRectFromParent(self.geometry()) | linkedView.mapRectToItem(self, linkedView.boundingRect())
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-03-02 02:55:32 +00:00
|
|
|
def paint(self, p, opt, widget):
|
2013-11-27 06:16:13 +00:00
|
|
|
profiler = debug.Profiler()
|
2012-03-12 14:04:59 +00:00
|
|
|
if self.picture is None:
|
|
|
|
try:
|
2013-03-28 00:24:01 +00:00
|
|
|
picture = QtGui.QPicture()
|
|
|
|
painter = QtGui.QPainter(picture)
|
2020-06-15 09:03:50 +00:00
|
|
|
if self.style["tickFont"]:
|
|
|
|
painter.setFont(self.style["tickFont"])
|
2013-03-28 00:24:01 +00:00
|
|
|
specs = self.generateDrawSpecs(painter)
|
2013-11-27 06:16:13 +00:00
|
|
|
profiler('generate specs')
|
2013-03-28 16:34:17 +00:00
|
|
|
if specs is not None:
|
|
|
|
self.drawPicture(painter, *specs)
|
2013-11-27 06:16:13 +00:00
|
|
|
profiler('draw picture')
|
2012-03-12 14:04:59 +00:00
|
|
|
finally:
|
|
|
|
painter.end()
|
2013-03-28 00:24:01 +00:00
|
|
|
self.picture = picture
|
2021-06-06 01:17:43 +00:00
|
|
|
#p.setRenderHint(p.RenderHint.Antialiasing, False) ## Sometimes we get a segfault here ???
|
|
|
|
#p.setRenderHint(p.RenderHint.TextAntialiasing, True)
|
2012-03-12 14:04:59 +00:00
|
|
|
self.picture.play(p)
|
2012-04-04 13:31:58 +00:00
|
|
|
|
2012-05-21 21:31:09 +00:00
|
|
|
def setTicks(self, ticks):
|
|
|
|
"""Explicitly determine which ticks to display.
|
|
|
|
This overrides the behavior specified by tickSpacing(), tickValues(), and tickStrings()
|
|
|
|
The format for *ticks* looks like::
|
2012-04-04 13:31:58 +00:00
|
|
|
|
2012-05-21 21:31:09 +00:00
|
|
|
[
|
|
|
|
[ (majorTickValue1, majorTickString1), (majorTickValue2, majorTickString2), ... ],
|
|
|
|
[ (minorTickValue1, minorTickString1), (minorTickValue2, minorTickString2), ... ],
|
|
|
|
...
|
|
|
|
]
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-05-21 21:31:09 +00:00
|
|
|
If *ticks* is None, then the default tick system will be used instead.
|
|
|
|
"""
|
|
|
|
self._tickLevels = ticks
|
|
|
|
self.picture = None
|
|
|
|
self.update()
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2014-07-25 11:52:58 +00:00
|
|
|
def setTickSpacing(self, major=None, minor=None, levels=None):
|
|
|
|
"""
|
2019-01-16 14:43:04 +00:00
|
|
|
Explicitly determine the spacing of major and minor ticks. This
|
2014-07-25 11:52:58 +00:00
|
|
|
overrides the default behavior of the tickSpacing method, and disables
|
2019-01-16 14:43:04 +00:00
|
|
|
the effect of setTicks(). Arguments may be either *major* and *minor*,
|
|
|
|
or *levels* which is a list of (spacing, offset) tuples for each
|
2014-07-25 11:52:58 +00:00
|
|
|
tick level desired.
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2014-07-25 11:52:58 +00:00
|
|
|
If no arguments are given, then the default behavior of tickSpacing
|
|
|
|
is enabled.
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2014-07-25 11:52:58 +00:00
|
|
|
Examples::
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2014-07-25 11:52:58 +00:00
|
|
|
# two levels, all offsets = 0
|
|
|
|
axis.setTickSpacing(5, 1)
|
|
|
|
# three levels, all offsets = 0
|
|
|
|
axis.setTickSpacing([(3, 0), (1, 0), (0.25, 0)])
|
|
|
|
# reset to default
|
|
|
|
axis.setTickSpacing()
|
|
|
|
"""
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2014-07-25 11:52:58 +00:00
|
|
|
if levels is None:
|
|
|
|
if major is None:
|
|
|
|
levels = None
|
|
|
|
else:
|
|
|
|
levels = [(major, 0), (minor, 0)]
|
|
|
|
self._tickSpacing = levels
|
|
|
|
self.picture = None
|
|
|
|
self.update()
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-04-04 13:31:58 +00:00
|
|
|
def tickSpacing(self, minVal, maxVal, size):
|
|
|
|
"""Return values describing the desired spacing and offset of ticks.
|
2019-01-16 14:43:04 +00:00
|
|
|
|
|
|
|
This method is called whenever the axis needs to be redrawn and is a
|
2012-04-04 13:31:58 +00:00
|
|
|
good method to override in subclasses that require control over tick locations.
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2013-03-22 19:52:44 +00:00
|
|
|
The return value must be a list of tuples, one for each set of ticks::
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-04-04 13:31:58 +00:00
|
|
|
[
|
|
|
|
(major tick spacing, offset),
|
|
|
|
(minor tick spacing, offset),
|
|
|
|
(sub-minor tick spacing, offset),
|
|
|
|
...
|
|
|
|
]
|
|
|
|
"""
|
2014-07-25 11:52:58 +00:00
|
|
|
# First check for override tick spacing
|
|
|
|
if self._tickSpacing is not None:
|
|
|
|
return self._tickSpacing
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-04-04 13:31:58 +00:00
|
|
|
dif = abs(maxVal - minVal)
|
|
|
|
if dif == 0:
|
|
|
|
return []
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-04-04 13:31:58 +00:00
|
|
|
## decide optimal minor tick spacing in pixels (this is just aesthetics)
|
2021-04-19 06:16:45 +00:00
|
|
|
optimalTickCount = max(2., log(size))
|
2019-01-16 14:43:04 +00:00
|
|
|
|
|
|
|
## optimal minor tick spacing
|
2012-04-04 13:31:58 +00:00
|
|
|
optimalSpacing = dif / optimalTickCount
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-04-04 13:31:58 +00:00
|
|
|
## the largest power-of-10 spacing which is smaller than optimal
|
2021-04-19 06:16:45 +00:00
|
|
|
p10unit = 10 ** floor(log10(optimalSpacing))
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-04-04 13:31:58 +00:00
|
|
|
## Determine major/minor tick spacings which flank the optimal spacing.
|
|
|
|
intervals = np.array([1., 2., 10., 20., 100.]) * p10unit
|
|
|
|
minorIndex = 0
|
|
|
|
while intervals[minorIndex+1] <= optimalSpacing:
|
|
|
|
minorIndex += 1
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2013-01-12 01:21:11 +00:00
|
|
|
levels = [
|
2012-04-04 13:31:58 +00:00
|
|
|
(intervals[minorIndex+2], 0),
|
|
|
|
(intervals[minorIndex+1], 0),
|
2013-01-10 03:21:32 +00:00
|
|
|
#(intervals[minorIndex], 0) ## Pretty, but eats up CPU
|
2012-04-04 13:31:58 +00:00
|
|
|
]
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2014-07-25 11:52:58 +00:00
|
|
|
if self.style['maxTickLevel'] >= 2:
|
|
|
|
## decide whether to include the last level of ticks
|
|
|
|
minSpacing = min(size / 20., 30.)
|
|
|
|
maxTickCount = size / minSpacing
|
|
|
|
if dif / intervals[minorIndex] <= maxTickCount:
|
|
|
|
levels.append((intervals[minorIndex], 0))
|
2019-06-24 00:27:16 +00:00
|
|
|
|
|
|
|
return levels
|
|
|
|
|
2012-07-09 12:38:30 +00:00
|
|
|
##### This does not work -- switching between 2/5 confuses the automatic text-level-selection
|
|
|
|
### Determine major/minor tick spacings which flank the optimal spacing.
|
|
|
|
#intervals = np.array([1., 2., 5., 10., 20., 50., 100.]) * p10unit
|
|
|
|
#minorIndex = 0
|
|
|
|
#while intervals[minorIndex+1] <= optimalSpacing:
|
|
|
|
#minorIndex += 1
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-07-09 12:38:30 +00:00
|
|
|
### make sure we never see 5 and 2 at the same time
|
|
|
|
#intIndexes = [
|
|
|
|
#[0,1,3],
|
|
|
|
#[0,2,3],
|
|
|
|
#[2,3,4],
|
|
|
|
#[3,4,6],
|
|
|
|
#[3,5,6],
|
|
|
|
#][minorIndex]
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-07-09 12:38:30 +00:00
|
|
|
#return [
|
|
|
|
#(intervals[intIndexes[2]], 0),
|
|
|
|
#(intervals[intIndexes[1]], 0),
|
|
|
|
#(intervals[intIndexes[0]], 0)
|
|
|
|
#]
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-04-04 13:31:58 +00:00
|
|
|
def tickValues(self, minVal, maxVal, size):
|
|
|
|
"""
|
2012-04-28 20:00:42 +00:00
|
|
|
Return the values and spacing of ticks to draw::
|
2019-01-16 14:43:04 +00:00
|
|
|
|
|
|
|
[
|
|
|
|
(spacing, [major ticks]),
|
|
|
|
(spacing, [minor ticks]),
|
|
|
|
...
|
2012-04-28 20:00:42 +00:00
|
|
|
]
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-04-04 13:31:58 +00:00
|
|
|
By default, this method calls tickSpacing to determine the correct tick locations.
|
|
|
|
This is a good method to override in subclasses.
|
|
|
|
"""
|
2012-06-18 17:50:44 +00:00
|
|
|
minVal, maxVal = sorted((minVal, maxVal))
|
2013-05-10 03:02:14 +00:00
|
|
|
|
2019-01-16 14:43:04 +00:00
|
|
|
|
|
|
|
minVal *= self.scale
|
2013-05-10 03:02:14 +00:00
|
|
|
maxVal *= self.scale
|
|
|
|
#size *= self.scale
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-04-04 13:31:58 +00:00
|
|
|
ticks = []
|
|
|
|
tickLevels = self.tickSpacing(minVal, maxVal, size)
|
2012-06-18 17:50:44 +00:00
|
|
|
allValues = np.array([])
|
2012-04-04 13:31:58 +00:00
|
|
|
for i in range(len(tickLevels)):
|
|
|
|
spacing, offset = tickLevels[i]
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-04-04 13:31:58 +00:00
|
|
|
## determine starting tick
|
2021-04-19 06:16:45 +00:00
|
|
|
start = (ceil((minVal-offset) / spacing) * spacing) + offset
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-04-04 13:31:58 +00:00
|
|
|
## determine number of ticks
|
|
|
|
num = int((maxVal-start) / spacing) + 1
|
2013-05-10 03:02:14 +00:00
|
|
|
values = (np.arange(num) * spacing + start) / self.scale
|
2012-06-18 17:50:44 +00:00
|
|
|
## remove any ticks that were present in higher levels
|
|
|
|
## we assume here that if the difference between a tick value and a previously seen tick value
|
|
|
|
## is less than spacing/100, then they are 'equal' and we can ignore the new tick.
|
2021-04-21 04:42:01 +00:00
|
|
|
values = list(filter(lambda x: np.all(np.abs(allValues-x) > spacing/self.scale*0.01), values))
|
2012-06-18 17:50:44 +00:00
|
|
|
allValues = np.concatenate([allValues, values])
|
2013-05-10 03:02:14 +00:00
|
|
|
ticks.append((spacing/self.scale, values))
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-06-29 18:39:27 +00:00
|
|
|
if self.logMode:
|
|
|
|
return self.logTickValues(minVal, maxVal, size, ticks)
|
2019-01-16 14:43:04 +00:00
|
|
|
|
|
|
|
|
2013-05-10 03:02:14 +00:00
|
|
|
#nticks = []
|
|
|
|
#for t in ticks:
|
|
|
|
#nvals = []
|
|
|
|
#for v in t[1]:
|
|
|
|
#nvals.append(v/self.scale)
|
|
|
|
#nticks.append((t[0]/self.scale,nvals))
|
|
|
|
#ticks = nticks
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-04-04 13:31:58 +00:00
|
|
|
return ticks
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-06-29 18:39:27 +00:00
|
|
|
def logTickValues(self, minVal, maxVal, size, stdTicks):
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-06-29 18:39:27 +00:00
|
|
|
## start with the tick spacing given by tickValues().
|
|
|
|
## Any level whose spacing is < 1 needs to be converted to log scale
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-06-29 18:39:27 +00:00
|
|
|
ticks = []
|
|
|
|
for (spacing, t) in stdTicks:
|
|
|
|
if spacing >= 1.0:
|
|
|
|
ticks.append((spacing, t))
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-06-29 18:39:27 +00:00
|
|
|
if len(ticks) < 3:
|
2021-04-19 06:16:45 +00:00
|
|
|
v1 = int(floor(minVal))
|
|
|
|
v2 = int(ceil(maxVal))
|
2012-06-29 18:39:27 +00:00
|
|
|
#major = list(range(v1+1, v2))
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-06-29 18:39:27 +00:00
|
|
|
minor = []
|
|
|
|
for v in range(v1, v2):
|
|
|
|
minor.extend(v + np.log10(np.arange(1, 10)))
|
|
|
|
minor = [x for x in minor if x>minVal and x<maxVal]
|
|
|
|
ticks.append((None, minor))
|
|
|
|
return ticks
|
2012-04-04 13:31:58 +00:00
|
|
|
|
|
|
|
def tickStrings(self, values, scale, spacing):
|
2019-01-16 14:43:04 +00:00
|
|
|
"""Return the strings that should be placed next to ticks. This method is called
|
2012-04-04 13:31:58 +00:00
|
|
|
when redrawing the axis and is a good method to override in subclasses.
|
2019-01-16 14:43:04 +00:00
|
|
|
The method is called with a list of tick values, a scaling factor (see below), and the
|
|
|
|
spacing between ticks (this is required since, in some instances, there may be only
|
2012-04-04 13:31:58 +00:00
|
|
|
one tick and thus no other way to determine the tick spacing)
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-04-04 13:31:58 +00:00
|
|
|
The scale argument is used when the axis label is displaying units which may have an SI scaling prefix.
|
|
|
|
When determining the text to display, use value*scale to correctly account for this prefix.
|
|
|
|
For example, if the axis label's units are set to 'V', then a tick value of 0.001 might
|
2019-01-16 14:43:04 +00:00
|
|
|
be accompanied by a scale value of 1000. This indicates that the label is displaying 'mV', and
|
2012-04-04 13:31:58 +00:00
|
|
|
thus the tick should display 0.001 * 1000 = 1.
|
|
|
|
"""
|
2012-04-21 19:57:47 +00:00
|
|
|
if self.logMode:
|
|
|
|
return self.logTickStrings(values, scale, spacing)
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2021-04-19 06:16:45 +00:00
|
|
|
places = max(0, ceil(-log10(spacing*scale)))
|
2012-04-04 13:31:58 +00:00
|
|
|
strings = []
|
|
|
|
for v in values:
|
|
|
|
vs = v * scale
|
|
|
|
if abs(vs) < .001 or abs(vs) >= 10000:
|
|
|
|
vstr = "%g" % vs
|
|
|
|
else:
|
|
|
|
vstr = ("%%0.%df" % places) % vs
|
|
|
|
strings.append(vstr)
|
|
|
|
return strings
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-04-21 19:57:47 +00:00
|
|
|
def logTickStrings(self, values, scale, spacing):
|
2020-05-30 20:53:38 +00:00
|
|
|
estrings = ["%0.1g"%x for x in 10 ** np.array(values).astype(float) * np.array(scale)]
|
2021-08-02 16:02:11 +00:00
|
|
|
convdict = {"0": "⁰",
|
|
|
|
"1": "¹",
|
|
|
|
"2": "²",
|
|
|
|
"3": "³",
|
|
|
|
"4": "⁴",
|
|
|
|
"5": "⁵",
|
|
|
|
"6": "⁶",
|
|
|
|
"7": "⁷",
|
|
|
|
"8": "⁸",
|
|
|
|
"9": "⁹",
|
|
|
|
}
|
|
|
|
dstrings = []
|
|
|
|
for e in estrings:
|
|
|
|
if e.count("e"):
|
|
|
|
v, p = e.split("e")
|
|
|
|
sign = "⁻" if p[0] == "-" else ""
|
|
|
|
pot = "".join([convdict[pp] for pp in p[1:].lstrip("0")])
|
|
|
|
if v == "1":
|
|
|
|
v = ""
|
2020-05-30 20:53:38 +00:00
|
|
|
else:
|
2021-08-02 16:02:11 +00:00
|
|
|
v = v + "·"
|
|
|
|
dstrings.append(v + "10" + sign + pot)
|
|
|
|
else:
|
|
|
|
dstrings.append(e)
|
|
|
|
return dstrings
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2013-03-28 00:24:01 +00:00
|
|
|
def generateDrawSpecs(self, p):
|
|
|
|
"""
|
2014-04-12 21:02:56 +00:00
|
|
|
Calls tickValues() and tickStrings() to determine where and how ticks should
|
2019-01-16 14:43:04 +00:00
|
|
|
be drawn, then generates from this a set of drawing commands to be
|
2013-03-28 00:24:01 +00:00
|
|
|
interpreted by drawPicture().
|
|
|
|
"""
|
2013-11-27 06:16:13 +00:00
|
|
|
profiler = debug.Profiler()
|
2020-06-13 05:28:26 +00:00
|
|
|
if self.style['tickFont'] is not None:
|
|
|
|
p.setFont(self.style['tickFont'])
|
2012-03-02 02:55:32 +00:00
|
|
|
bounds = self.mapRectFromParent(self.geometry())
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-03-12 14:04:59 +00:00
|
|
|
linkedView = self.linkedView()
|
|
|
|
if linkedView is None or self.grid is False:
|
2012-04-04 13:31:58 +00:00
|
|
|
tickBounds = bounds
|
2012-03-02 02:55:32 +00:00
|
|
|
else:
|
2012-04-04 13:31:58 +00:00
|
|
|
tickBounds = linkedView.mapRectToItem(self, linkedView.boundingRect())
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-03-02 02:55:32 +00:00
|
|
|
if self.orientation == 'left':
|
|
|
|
span = (bounds.topRight(), bounds.bottomRight())
|
2012-04-04 13:31:58 +00:00
|
|
|
tickStart = tickBounds.right()
|
2012-03-02 02:55:32 +00:00
|
|
|
tickStop = bounds.right()
|
|
|
|
tickDir = -1
|
|
|
|
axis = 0
|
|
|
|
elif self.orientation == 'right':
|
|
|
|
span = (bounds.topLeft(), bounds.bottomLeft())
|
2012-04-04 13:31:58 +00:00
|
|
|
tickStart = tickBounds.left()
|
2012-03-02 02:55:32 +00:00
|
|
|
tickStop = bounds.left()
|
|
|
|
tickDir = 1
|
|
|
|
axis = 0
|
|
|
|
elif self.orientation == 'top':
|
|
|
|
span = (bounds.bottomLeft(), bounds.bottomRight())
|
2012-04-04 13:31:58 +00:00
|
|
|
tickStart = tickBounds.bottom()
|
2012-03-02 02:55:32 +00:00
|
|
|
tickStop = bounds.bottom()
|
|
|
|
tickDir = -1
|
|
|
|
axis = 1
|
|
|
|
elif self.orientation == 'bottom':
|
|
|
|
span = (bounds.topLeft(), bounds.topRight())
|
2012-04-04 13:31:58 +00:00
|
|
|
tickStart = tickBounds.top()
|
2012-03-02 02:55:32 +00:00
|
|
|
tickStop = bounds.top()
|
|
|
|
tickDir = 1
|
|
|
|
axis = 1
|
2012-04-04 13:31:58 +00:00
|
|
|
#print tickStart, tickStop, span
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-03-02 02:55:32 +00:00
|
|
|
## determine size of this item in pixels
|
2012-05-11 22:05:41 +00:00
|
|
|
points = list(map(self.mapToDevice, span))
|
2012-06-22 01:52:34 +00:00
|
|
|
if None in points:
|
|
|
|
return
|
2012-03-02 02:55:32 +00:00
|
|
|
lengthInPixels = Point(points[1] - points[0]).length()
|
2012-04-04 13:31:58 +00:00
|
|
|
if lengthInPixels == 0:
|
|
|
|
return
|
2012-03-02 02:55:32 +00:00
|
|
|
|
2014-04-12 21:02:56 +00:00
|
|
|
# Determine major / minor / subminor axis ticks
|
2012-05-21 21:31:09 +00:00
|
|
|
if self._tickLevels is None:
|
|
|
|
tickLevels = self.tickValues(self.range[0], self.range[1], lengthInPixels)
|
|
|
|
tickStrings = None
|
|
|
|
else:
|
|
|
|
## parse self.tickLevels into the formats returned by tickLevels() and tickStrings()
|
|
|
|
tickLevels = []
|
|
|
|
tickStrings = []
|
|
|
|
for level in self._tickLevels:
|
|
|
|
values = []
|
|
|
|
strings = []
|
|
|
|
tickLevels.append((None, values))
|
|
|
|
tickStrings.append(strings)
|
|
|
|
for val, strn in level:
|
|
|
|
values.append(val)
|
|
|
|
strings.append(strn)
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-04-04 13:31:58 +00:00
|
|
|
## determine mapping between tick values and local coordinates
|
|
|
|
dif = self.range[1] - self.range[0]
|
2013-03-28 16:34:17 +00:00
|
|
|
if dif == 0:
|
2014-02-01 02:26:01 +00:00
|
|
|
xScale = 1
|
2013-03-28 16:34:17 +00:00
|
|
|
offset = 0
|
2012-03-02 02:55:32 +00:00
|
|
|
else:
|
2013-03-28 16:34:17 +00:00
|
|
|
if axis == 0:
|
|
|
|
xScale = -bounds.height() / dif
|
|
|
|
offset = self.range[0] * xScale - bounds.height()
|
|
|
|
else:
|
|
|
|
xScale = bounds.width() / dif
|
|
|
|
offset = self.range[0] * xScale
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-07-01 03:32:26 +00:00
|
|
|
xRange = [x * xScale - offset for x in self.range]
|
|
|
|
xMin = min(xRange)
|
|
|
|
xMax = max(xRange)
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2013-11-27 06:16:13 +00:00
|
|
|
profiler('init')
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-04-04 13:31:58 +00:00
|
|
|
tickPositions = [] # remembers positions of previously drawn ticks
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2014-04-12 21:02:56 +00:00
|
|
|
## compute coordinates to draw ticks
|
2012-03-02 02:55:32 +00:00
|
|
|
## draw three different intervals, long ticks first
|
2013-03-28 00:24:01 +00:00
|
|
|
tickSpecs = []
|
2012-04-04 13:31:58 +00:00
|
|
|
for i in range(len(tickLevels)):
|
|
|
|
tickPositions.append([])
|
|
|
|
ticks = tickLevels[i][1]
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-03-02 02:55:32 +00:00
|
|
|
## length of tick
|
2014-04-08 19:29:31 +00:00
|
|
|
tickLength = self.style['tickLength'] / ((i*0.5)+1.0)
|
2020-06-25 00:42:28 +00:00
|
|
|
|
|
|
|
lineAlpha = self.style["tickAlpha"]
|
|
|
|
if lineAlpha is None:
|
|
|
|
lineAlpha = 255 / (i+1)
|
|
|
|
if self.grid is not False:
|
2021-04-19 06:16:45 +00:00
|
|
|
lineAlpha *= self.grid/255. * fn.clip_scalar((0.05 * lengthInPixels / (len(ticks)+1)), 0., 1.)
|
2020-06-25 00:42:28 +00:00
|
|
|
elif isinstance(lineAlpha, float):
|
|
|
|
lineAlpha *= 255
|
|
|
|
lineAlpha = max(0, int(round(lineAlpha)))
|
|
|
|
lineAlpha = min(255, int(round(lineAlpha)))
|
|
|
|
elif isinstance(lineAlpha, int):
|
|
|
|
if (lineAlpha > 255) or (lineAlpha < 0):
|
|
|
|
raise ValueError("lineAlpha should be [0..255]")
|
|
|
|
else:
|
|
|
|
raise TypeError("Line Alpha should be of type None, float or int")
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-04-04 13:31:58 +00:00
|
|
|
for v in ticks:
|
2012-07-01 03:32:26 +00:00
|
|
|
## determine actual position to draw this tick
|
2012-04-04 13:31:58 +00:00
|
|
|
x = (v * xScale) - offset
|
2012-07-01 03:32:26 +00:00
|
|
|
if x < xMin or x > xMax: ## last check to make sure no out-of-bounds ticks are drawn
|
|
|
|
tickPositions[i].append(None)
|
|
|
|
continue
|
|
|
|
tickPositions[i].append(x)
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-04-04 13:31:58 +00:00
|
|
|
p1 = [x, x]
|
|
|
|
p2 = [x, x]
|
2012-03-02 02:55:32 +00:00
|
|
|
p1[axis] = tickStart
|
2012-03-12 14:04:59 +00:00
|
|
|
p2[axis] = tickStop
|
|
|
|
if self.grid is False:
|
2012-04-04 13:31:58 +00:00
|
|
|
p2[axis] += tickLength*tickDir
|
2012-06-29 18:39:27 +00:00
|
|
|
tickPen = self.pen()
|
|
|
|
color = tickPen.color()
|
2020-02-28 22:27:10 +00:00
|
|
|
color.setAlpha(int(lineAlpha))
|
2012-06-29 18:39:27 +00:00
|
|
|
tickPen.setColor(color)
|
2013-03-28 00:24:01 +00:00
|
|
|
tickSpecs.append((tickPen, Point(p1), Point(p2)))
|
2013-11-27 06:16:13 +00:00
|
|
|
profiler('compute ticks')
|
2012-05-21 21:31:09 +00:00
|
|
|
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2013-03-28 00:24:01 +00:00
|
|
|
if self.style['stopAxisAtTick'][0] is True:
|
2019-06-20 05:07:57 +00:00
|
|
|
minTickPosition = min(map(min, tickPositions))
|
2013-03-28 00:24:01 +00:00
|
|
|
if axis == 0:
|
2019-06-20 05:07:57 +00:00
|
|
|
stop = max(span[0].y(), minTickPosition)
|
2013-03-28 00:24:01 +00:00
|
|
|
span[0].setY(stop)
|
|
|
|
else:
|
2019-06-20 05:07:57 +00:00
|
|
|
stop = max(span[0].x(), minTickPosition)
|
2013-03-28 00:24:01 +00:00
|
|
|
span[0].setX(stop)
|
|
|
|
if self.style['stopAxisAtTick'][1] is True:
|
2019-06-20 05:07:57 +00:00
|
|
|
maxTickPosition = max(map(max, tickPositions))
|
2013-03-28 00:24:01 +00:00
|
|
|
if axis == 0:
|
2019-06-20 05:07:57 +00:00
|
|
|
stop = min(span[1].y(), maxTickPosition)
|
2013-03-28 00:24:01 +00:00
|
|
|
span[1].setY(stop)
|
|
|
|
else:
|
2019-06-20 05:07:57 +00:00
|
|
|
stop = min(span[1].x(), maxTickPosition)
|
2013-03-28 00:24:01 +00:00
|
|
|
span[1].setX(stop)
|
|
|
|
axisSpec = (self.pen(), span[0], span[1])
|
|
|
|
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2013-07-03 15:20:49 +00:00
|
|
|
textOffset = self.style['tickTextOffset'][axis] ## spacing between axis and text
|
2013-03-28 00:24:01 +00:00
|
|
|
#if self.style['autoExpandTextSpace'] is True:
|
|
|
|
#textWidth = self.textWidth
|
|
|
|
#textHeight = self.textHeight
|
|
|
|
#else:
|
|
|
|
#textWidth = self.style['tickTextWidth'] ## space allocated for horizontal text
|
|
|
|
#textHeight = self.style['tickTextHeight'] ## space allocated for horizontal text
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2013-07-03 15:20:49 +00:00
|
|
|
textSize2 = 0
|
2020-06-23 06:02:48 +00:00
|
|
|
lastTextSize2 = 0
|
2012-05-21 21:31:09 +00:00
|
|
|
textRects = []
|
2013-03-28 00:24:01 +00:00
|
|
|
textSpecs = [] ## list of draw
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2014-03-29 15:31:23 +00:00
|
|
|
# If values are hidden, return early
|
|
|
|
if not self.style['showValues']:
|
|
|
|
return (axisSpec, tickSpecs, textSpecs)
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2014-07-25 11:52:58 +00:00
|
|
|
for i in range(min(len(tickLevels), self.style['maxTextLevel']+1)):
|
2014-03-29 15:31:23 +00:00
|
|
|
## Get the list of strings to display for this level
|
|
|
|
if tickStrings is None:
|
|
|
|
spacing, values = tickLevels[i]
|
|
|
|
strings = self.tickStrings(values, self.autoSIPrefixScale * self.scale, spacing)
|
|
|
|
else:
|
|
|
|
strings = tickStrings[i]
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2014-03-29 15:31:23 +00:00
|
|
|
if len(strings) == 0:
|
|
|
|
continue
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2014-03-29 15:31:23 +00:00
|
|
|
## ignore strings belonging to ticks that were previously ignored
|
|
|
|
for j in range(len(strings)):
|
|
|
|
if tickPositions[i][j] is None:
|
|
|
|
strings[j] = None
|
|
|
|
|
|
|
|
## Measure density of text; decide whether to draw this level
|
|
|
|
rects = []
|
|
|
|
for s in strings:
|
|
|
|
if s is None:
|
|
|
|
rects.append(None)
|
2013-03-28 00:24:01 +00:00
|
|
|
else:
|
2021-08-02 04:43:32 +00:00
|
|
|
br = p.boundingRect(QtCore.QRectF(0, 0, 100, 100), QtCore.Qt.AlignmentFlag.AlignCenter, s)
|
2014-03-29 15:31:23 +00:00
|
|
|
## boundingRect is usually just a bit too large
|
|
|
|
## (but this probably depends on per-font metrics?)
|
|
|
|
br.setHeight(br.height() * 0.8)
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2014-03-29 15:31:23 +00:00
|
|
|
rects.append(br)
|
|
|
|
textRects.append(rects[-1])
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2014-08-07 13:03:26 +00:00
|
|
|
if len(textRects) > 0:
|
|
|
|
## measure all text, make sure there's enough room
|
|
|
|
if axis == 0:
|
|
|
|
textSize = np.sum([r.height() for r in textRects])
|
|
|
|
textSize2 = np.max([r.width() for r in textRects])
|
|
|
|
else:
|
|
|
|
textSize = np.sum([r.width() for r in textRects])
|
|
|
|
textSize2 = np.max([r.height() for r in textRects])
|
2014-04-12 21:02:56 +00:00
|
|
|
else:
|
2014-08-07 13:03:26 +00:00
|
|
|
textSize = 0
|
|
|
|
textSize2 = 0
|
2014-03-29 15:31:23 +00:00
|
|
|
|
2014-04-12 21:02:56 +00:00
|
|
|
if i > 0: ## always draw top level
|
2014-03-29 15:31:23 +00:00
|
|
|
## If the strings are too crowded, stop drawing text now.
|
|
|
|
## We use three different crowding limits based on the number
|
|
|
|
## of texts drawn so far.
|
|
|
|
textFillRatio = float(textSize) / lengthInPixels
|
|
|
|
finished = False
|
|
|
|
for nTexts, limit in self.style['textFillLimits']:
|
|
|
|
if len(textSpecs) >= nTexts and textFillRatio >= limit:
|
|
|
|
finished = True
|
2013-07-03 15:20:49 +00:00
|
|
|
break
|
2014-03-29 15:31:23 +00:00
|
|
|
if finished:
|
|
|
|
break
|
2020-06-23 06:02:48 +00:00
|
|
|
|
|
|
|
lastTextSize2 = textSize2
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2014-03-29 15:31:23 +00:00
|
|
|
#spacing, values = tickLevels[best]
|
|
|
|
#strings = self.tickStrings(values, self.scale, spacing)
|
2014-04-12 21:02:56 +00:00
|
|
|
# Determine exactly where tick text should be drawn
|
2014-03-29 15:31:23 +00:00
|
|
|
for j in range(len(strings)):
|
|
|
|
vstr = strings[j]
|
|
|
|
if vstr is None: ## this tick was ignored because it is out of bounds
|
|
|
|
continue
|
|
|
|
x = tickPositions[i][j]
|
2021-06-06 01:17:43 +00:00
|
|
|
#textRect = p.boundingRect(QtCore.QRectF(0, 0, 100, 100), QtCore.Qt.AlignmentFlag.AlignCenter, vstr)
|
2014-03-29 15:31:23 +00:00
|
|
|
textRect = rects[j]
|
|
|
|
height = textRect.height()
|
|
|
|
width = textRect.width()
|
|
|
|
#self.textHeight = height
|
2014-04-08 19:29:31 +00:00
|
|
|
offset = max(0,self.style['tickLength']) + textOffset
|
2021-01-16 23:19:51 +00:00
|
|
|
|
2014-03-29 15:31:23 +00:00
|
|
|
if self.orientation == 'left':
|
2021-06-06 01:17:43 +00:00
|
|
|
alignFlags = QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignVCenter
|
2014-03-29 15:31:23 +00:00
|
|
|
rect = QtCore.QRectF(tickStop-offset-width, x-(height/2), width, height)
|
|
|
|
elif self.orientation == 'right':
|
2021-06-06 01:17:43 +00:00
|
|
|
alignFlags = QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignVCenter
|
2014-03-29 15:31:23 +00:00
|
|
|
rect = QtCore.QRectF(tickStop+offset, x-(height/2), width, height)
|
|
|
|
elif self.orientation == 'top':
|
2021-06-06 01:17:43 +00:00
|
|
|
alignFlags = QtCore.Qt.AlignmentFlag.AlignHCenter|QtCore.Qt.AlignmentFlag.AlignBottom
|
2014-03-29 15:31:23 +00:00
|
|
|
rect = QtCore.QRectF(x-width/2., tickStop-offset-height, width, height)
|
|
|
|
elif self.orientation == 'bottom':
|
2021-06-06 01:17:43 +00:00
|
|
|
alignFlags = QtCore.Qt.AlignmentFlag.AlignHCenter|QtCore.Qt.AlignmentFlag.AlignTop
|
2014-03-29 15:31:23 +00:00
|
|
|
rect = QtCore.QRectF(x-width/2., tickStop+offset, width, height)
|
|
|
|
|
2021-06-06 01:22:10 +00:00
|
|
|
textFlags = alignFlags | QtCore.Qt.TextFlag.TextDontClip
|
2014-03-29 15:31:23 +00:00
|
|
|
#p.setPen(self.pen())
|
|
|
|
#p.drawText(rect, textFlags, vstr)
|
|
|
|
textSpecs.append((rect, textFlags, vstr))
|
|
|
|
profiler('compute text')
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2014-03-16 09:38:08 +00:00
|
|
|
## update max text size if needed.
|
2020-06-23 06:02:48 +00:00
|
|
|
self._updateMaxTextSize(lastTextSize2)
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2013-03-28 00:24:01 +00:00
|
|
|
return (axisSpec, tickSpecs, textSpecs)
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2013-03-28 00:24:01 +00:00
|
|
|
def drawPicture(self, p, axisSpec, tickSpecs, textSpecs):
|
2013-11-27 06:16:13 +00:00
|
|
|
profiler = debug.Profiler()
|
|
|
|
|
2021-06-06 01:17:43 +00:00
|
|
|
p.setRenderHint(p.RenderHint.Antialiasing, False)
|
|
|
|
p.setRenderHint(p.RenderHint.TextAntialiasing, True)
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2013-03-28 00:24:01 +00:00
|
|
|
## draw long line along axis
|
|
|
|
pen, p1, p2 = axisSpec
|
|
|
|
p.setPen(pen)
|
|
|
|
p.drawLine(p1, p2)
|
2021-06-22 23:17:39 +00:00
|
|
|
# p.translate(0.5,0) ## resolves some damn pixel ambiguity
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2013-03-28 00:24:01 +00:00
|
|
|
## draw ticks
|
|
|
|
for pen, p1, p2 in tickSpecs:
|
|
|
|
p.setPen(pen)
|
|
|
|
p.drawLine(p1, p2)
|
2013-11-27 06:16:13 +00:00
|
|
|
profiler('draw ticks')
|
|
|
|
|
2019-11-20 04:43:27 +00:00
|
|
|
# Draw all text
|
2020-05-04 21:58:29 +00:00
|
|
|
if self.style['tickFont'] is not None:
|
|
|
|
p.setFont(self.style['tickFont'])
|
2019-11-20 04:43:27 +00:00
|
|
|
p.setPen(self.textPen())
|
2020-11-21 08:46:31 +00:00
|
|
|
bounding = self.boundingRect().toAlignedRect()
|
|
|
|
p.setClipRect(bounding)
|
2013-03-28 00:24:01 +00:00
|
|
|
for rect, flags, text in textSpecs:
|
2020-02-29 22:38:19 +00:00
|
|
|
p.drawText(rect, int(flags), text)
|
2019-11-20 04:43:27 +00:00
|
|
|
|
2013-11-27 06:16:13 +00:00
|
|
|
profiler('draw text')
|
|
|
|
|
2012-03-02 02:55:32 +00:00
|
|
|
def show(self):
|
2014-08-30 02:50:14 +00:00
|
|
|
GraphicsWidget.show(self)
|
2012-03-02 02:55:32 +00:00
|
|
|
if self.orientation in ['left', 'right']:
|
2014-08-30 02:50:14 +00:00
|
|
|
self._updateWidth()
|
2012-03-02 02:55:32 +00:00
|
|
|
else:
|
2014-08-30 02:50:14 +00:00
|
|
|
self._updateHeight()
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-03-02 02:55:32 +00:00
|
|
|
def hide(self):
|
2014-08-30 02:50:14 +00:00
|
|
|
GraphicsWidget.hide(self)
|
2012-03-02 02:55:32 +00:00
|
|
|
if self.orientation in ['left', 'right']:
|
2014-08-30 02:50:14 +00:00
|
|
|
self._updateWidth()
|
2012-03-02 02:55:32 +00:00
|
|
|
else:
|
2014-08-30 02:50:14 +00:00
|
|
|
self._updateHeight()
|
2012-03-02 02:55:32 +00:00
|
|
|
|
2021-06-22 23:17:39 +00:00
|
|
|
def wheelEvent(self, event):
|
2020-04-14 15:24:54 +00:00
|
|
|
lv = self.linkedView()
|
|
|
|
if lv is None:
|
2012-03-12 14:04:59 +00:00
|
|
|
return
|
2021-06-22 23:17:39 +00:00
|
|
|
# Did the event occur inside the linked ViewBox (and not over the axis iteself)?
|
|
|
|
if lv.sceneBoundingRect().contains(event.scenePos()):
|
|
|
|
# pass event to linked ViewBox without marking it as single axis zoom
|
|
|
|
lv.wheelEvent(event)
|
2012-03-02 02:55:32 +00:00
|
|
|
else:
|
2021-06-22 23:17:39 +00:00
|
|
|
# pass event to linked viewbox with appropriate single axis zoom parameter
|
|
|
|
if self.orientation in ['left', 'right']:
|
|
|
|
lv.wheelEvent(event, axis=1)
|
|
|
|
else:
|
|
|
|
lv.wheelEvent(event, axis=0)
|
|
|
|
event.accept()
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-03-12 14:04:59 +00:00
|
|
|
def mouseDragEvent(self, event):
|
2020-04-14 15:24:54 +00:00
|
|
|
lv = self.linkedView()
|
|
|
|
if lv is None:
|
2012-03-12 14:04:59 +00:00
|
|
|
return
|
2021-06-22 23:17:39 +00:00
|
|
|
# Did the mouse down event occur inside the linked ViewBox (and not the axis)?
|
|
|
|
if lv.sceneBoundingRect().contains(event.buttonDownScenePos()):
|
|
|
|
# pass event to linked ViewBox without marking it as single axis pan
|
|
|
|
return lv.mouseDragEvent(event)
|
|
|
|
# otherwise pass event to linked viewbox with appropriate single axis parameter
|
2012-03-12 14:04:59 +00:00
|
|
|
if self.orientation in ['left', 'right']:
|
2020-04-14 15:24:54 +00:00
|
|
|
return lv.mouseDragEvent(event, axis=1)
|
2012-03-12 14:04:59 +00:00
|
|
|
else:
|
2020-04-14 15:24:54 +00:00
|
|
|
return lv.mouseDragEvent(event, axis=0)
|
2019-01-16 14:43:04 +00:00
|
|
|
|
2012-03-12 14:04:59 +00:00
|
|
|
def mouseClickEvent(self, event):
|
2020-04-14 15:24:54 +00:00
|
|
|
lv = self.linkedView()
|
|
|
|
if lv is None:
|
2012-03-12 14:04:59 +00:00
|
|
|
return
|
2020-04-14 15:24:54 +00:00
|
|
|
return lv.mouseClickEvent(event)
|