diff options
Diffstat (limited to 'src/silx/gui/plot/items/axis.py')
-rw-r--r-- | src/silx/gui/plot/items/axis.py | 88 |
1 files changed, 63 insertions, 25 deletions
diff --git a/src/silx/gui/plot/items/axis.py b/src/silx/gui/plot/items/axis.py index fa3f6d7..1ae1ef1 100644 --- a/src/silx/gui/plot/items/axis.py +++ b/src/silx/gui/plot/items/axis.py @@ -1,6 +1,6 @@ # /*########################################################################## # -# Copyright (c) 2017-2022 European Synchrotron Radiation Facility +# Copyright (c) 2017-2023 European Synchrotron Radiation Facility # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -24,28 +24,28 @@ """This module provides the class for axes of the :class:`PlotWidget`. """ +from __future__ import annotations + __authors__ = ["V. Valls"] __license__ = "MIT" __date__ = "22/11/2018" import datetime as dt import enum -import logging +from typing import Optional import dateutil.tz -import numpy +from ....utils.proxy import docstring from ... import qt from .. import _utils -_logger = logging.getLogger(__name__) - - class TickMode(enum.Enum): """Determines if ticks are regular number or datetimes.""" - DEFAULT = 0 # Ticks are regular numbers - TIME_SERIES = 1 # Ticks are datetime objects + + DEFAULT = 0 # Ticks are regular numbers + TIME_SERIES = 1 # Ticks are datetime objects class Axis(qt.QObject): @@ -53,6 +53,7 @@ class Axis(qt.QObject): Note: This is an abstract class. """ + # States are half-stored on the backend of the plot, and half-stored on this # object. # TODO It would be good to store all the states of an axis in this object. @@ -91,10 +92,10 @@ class Axis(qt.QObject): self._scale = self.LINEAR self._isAutoScale = True # Store default labels provided to setGraph[X|Y]Label - self._defaultLabel = '' + self._defaultLabel = "" # Store currently displayed labels # Current label can differ from input one with active curve handling - self._currentLabel = '' + self._currentLabel = "" def _getPlot(self): """Returns the PlotWidget this Axis belongs to. @@ -150,7 +151,12 @@ class Axis(qt.QObject): :rtype: 2-tuple of float """ return _utils.checkAxisLimits( - vmin, vmax, isLog=self._isLogarithmic(), name=self._defaultLabel) + vmin, vmax, isLog=self._isLogarithmic(), name=self._defaultLabel + ) + + def _getDataRange(self) -> Optional[tuple[float, float]]: + """Returns the range of data items over this axis as (vmin, vmax)""" + raise NotImplementedError() def isInverted(self): """Return True if the axis is inverted (top to bottom for the y-axis), @@ -172,6 +178,10 @@ class Axis(qt.QObject): return raise NotImplementedError() + def isVisible(self) -> bool: + """Returns whether the axis is displayed or not""" + return True + def getLabel(self): """Return the current displayed label of this axis. @@ -199,10 +209,10 @@ class Axis(qt.QObject): :param str label: Currently displayed label """ - if label is None or label == '': + if label is None or label == "": label = self._defaultLabel if label is None: - label = '' + label = "" self._currentLabel = label self._internalSetCurrentLabel(label) @@ -218,7 +228,7 @@ class Axis(qt.QObject): :param str scale: Name of the scale ("log", or "linear") """ - assert(scale in self._SCALES) + assert scale in self._SCALES if self._scale == scale: return @@ -227,6 +237,8 @@ class Axis(qt.QObject): self._scale = scale + vmin, vmax = self.getLimits() + # TODO hackish way of forcing update of curves and images plot = self._getPlot() for item in plot.getItems(): @@ -235,13 +247,20 @@ class Axis(qt.QObject): if scale == self.LOGARITHMIC: self._internalSetLogarithmic(True) + if vmin <= 0: + dataRange = self._getDataRange() + if dataRange is None: + self.setLimits(1.0, 100.0) + else: + if vmax > 0 and dataRange[0] < vmax: + self.setLimits(dataRange[0], vmax) + else: + self.setLimits(*dataRange) elif scale == self.LINEAR: self._internalSetLogarithmic(False) else: raise ValueError("Scale %s unsupported" % scale) - plot._forceResetZoom() - self.sigScaleChanged.emit(self._scale) if emitLog: self._sigLogarithmicChanged.emit(self._scale == self.LOGARITHMIC) @@ -328,7 +347,7 @@ class Axis(qt.QObject): plot = self._getPlot() xMin, xMax = plot.getXAxis().getLimits() yMin, yMax = plot.getYAxis().getLimits() - y2Min, y2Max = plot.getYAxis('right').getLimits() + y2Min, y2Max = plot.getYAxis("right").getLimits() plot.setLimits(xMin, xMax, yMin, yMax, y2Min, y2Max) return updated @@ -351,7 +370,7 @@ class Axis(qt.QObject): plot = self._getPlot() xMin, xMax = plot.getXAxis().getLimits() yMin, yMax = plot.getYAxis().getLimits() - y2Min, y2Max = plot.getYAxis('right').getLimits() + y2Min, y2Max = plot.getYAxis("right").getLimits() plot.setLimits(xMin, xMax, yMin, yMax, y2Min, y2Max) return updated @@ -368,7 +387,7 @@ class XAxis(Axis): def setTimeZone(self, tz): if isinstance(tz, str) and tz.upper() == "UTC": tz = dateutil.tz.tzutc() - elif not(tz is None or isinstance(tz, dt.tzinfo)): + elif not (tz is None or isinstance(tz, dt.tzinfo)): raise TypeError("tz must be a dt.tzinfo object, None or 'UTC'.") self._getBackend().setXAxisTimeZone(tz) @@ -410,6 +429,11 @@ class XAxis(Axis): updated = constrains.update(minXRange=minRange, maxXRange=maxRange) return updated + @docstring(Axis) + def _getDataRange(self) -> Optional[tuple[float, float]]: + ranges = self._getPlot().getDataRange() + return ranges.x + class YAxis(Axis): """Axis class defining primitives for the Y axis""" @@ -418,13 +442,13 @@ class YAxis(Axis): # specialised implementations (prefixel by '_internal') def _internalSetCurrentLabel(self, label): - self._getBackend().setGraphYLabel(label, axis='left') + self._getBackend().setGraphYLabel(label, axis="left") def _internalGetLimits(self): - return self._getBackend().getGraphYLimits(axis='left') + return self._getBackend().getGraphYLimits(axis="left") def _internalSetLimits(self, ymin, ymax): - self._getBackend().setGraphYLimits(ymin, ymax, axis='left') + self._getBackend().setGraphYLimits(ymin, ymax, axis="left") def _internalSetLogarithmic(self, flag): self._getBackend().setYAxisLogarithmic(flag) @@ -462,6 +486,11 @@ class YAxis(Axis): updated = constrains.update(minYRange=minRange, maxYRange=maxRange) return updated + @docstring(Axis) + def _getDataRange(self) -> Optional[tuple[float, float]]: + ranges = self._getPlot().getDataRange() + return ranges.y + class YRightAxis(Axis): """Proxy axis for the secondary Y axes. It manages it own label and limit @@ -485,13 +514,13 @@ class YRightAxis(Axis): self.__mainAxis.sigAutoScaleChanged.connect(self.sigAutoScaleChanged.emit) def _internalSetCurrentLabel(self, label): - self._getBackend().setGraphYLabel(label, axis='right') + self._getBackend().setGraphYLabel(label, axis="right") def _internalGetLimits(self): - return self._getBackend().getGraphYLimits(axis='right') + return self._getBackend().getGraphYLimits(axis="right") def _internalSetLimits(self, ymin, ymax): - self._getBackend().setGraphYLimits(ymin, ymax, axis='right') + self._getBackend().setGraphYLimits(ymin, ymax, axis="right") def setInverted(self, flag=True): """Set the Y axis orientation. @@ -505,6 +534,10 @@ class YRightAxis(Axis): """Return True if Y axis goes from top to bottom, False otherwise.""" return self.__mainAxis.isInverted() + def isVisible(self) -> bool: + """Returns whether the axis is displayed or not""" + return self._getBackend().isYRightAxisVisible() + def getScale(self): """Return the name of the scale used by this axis. @@ -541,3 +574,8 @@ class YRightAxis(Axis): False to disable it. """ return self.__mainAxis.setAutoScale(flag) + + @docstring(Axis) + def _getDataRange(self) -> Optional[tuple[float, float]]: + ranges = self._getPlot().getDataRange() + return ranges.y2 |