class_docstr: Class representing a single plot curve. Instances of this class are created
  automatically as part of :class:`PlotDataItem <pyqtgraph.PlotDataItem>`; 
  these rarely need to be instantiated directly.
  
  Features:
  
    - Fast data update
    - Fill under curve
    - Mouse interaction
  
  =====================  ===============================================
  **Signals:**
  sigPlotChanged(self)   Emitted when the data being plotted has changed
  sigClicked(self, ev)   Emitted when the curve is clicked
  =====================  ===============================================
class_docstr_tok: ["Class","representing","a","single","plot","curve",".","Instances","of","this","class","are","created","automatically","as","part","of",":","class",":","`","PlotDataItem","<","pyqtgraph.PlotDataItem",">","`",";","these","rarely","need","to","be","instantiated","directly",".","Features",":","-","Fast","data","update","-","Fill","under","curve","-","Mouse","interaction","=====================","===============================================","*","*","Signals",":","*","*","sigPlotChanged","(","self",")","Emitted","when","the","data","being","plotted","has","changed","sigClicked","(","self",",","ev",")","Emitted","when","the","curve","is","clicked","=====================","==============================================="]
code: def dataBounds(self, ax, frac=1.0, orthoRange=None):
      ## Need this to run as fast as possible.
      ## check cache first:
      cache = self._boundsCache[ax]
      if cache is not None and cache[0] == (frac, orthoRange):
          return cache[1]
  
      (x, y) = self.getData()
      if x is None or len(x) == 0:
          return (None, None)
  
      if ax == 0:
          d = x
          d2 = y
      elif ax == 1:
          d = y
          d2 = x
      else:
          raise ValueError("Invalid axis value")
  
      ## If an orthogonal range is specified, mask the data now
      if orthoRange is not None:
          mask = (d2 >= orthoRange[0]) * (d2 <= orthoRange[1])
          if self.opts.get("stepMode", None) == "center":
              mask = mask[
                  :-1
              ]  # len(y) == len(x) - 1 when stepMode is center
          d = d[mask]
          # d2 = d2[mask]
  
      if len(d) == 0:
          return (None, None)
  
      ## Get min/max (or percentiles) of the requested data range
      if frac >= 1.0:
          # include complete data range
          # first try faster nanmin/max function, then cut out infs if needed.
          with warnings.catch_warnings():
              # All-NaN data is acceptable; Explicit numpy warning is not needed.
              warnings.simplefilter("ignore")
              b = (
                  float(np.nanmin(d)),
                  float(np.nanmax(d)),
              )  # enforce float format for bounds, even if data format is different
          if math.isinf(b[0]) or math.isinf(b[1]):
              mask = np.isfinite(d)
              d = d[mask]
              if len(d) == 0:
                  return (None, None)
              b = (
                  float(d.min()),
                  float(d.max()),
              )  # enforce float format for bounds, even if data format is different
  
      elif frac <= 0.0:
          raise Exception(
              "Value for parameter 'frac' must be > 0. (got %s)"
              % str(frac)
          )
      else:
          # include a percentile of data range
          mask = np.isfinite(d)
          d = d[mask]
          if len(d) == 0:
              return (None, None)
          b = np.percentile(
              d, [50 * (1 - frac), 50 * (1 + frac)]
          )  # percentile result is always float64 or larger
  
      ## adjust for fill level
      if ax == 1 and self.opts["fillLevel"] not in [None, "enclosed"]:
          b = (
              float(min(b[0], self.opts["fillLevel"])),
              float(max(b[1], self.opts["fillLevel"])),
          )  # enforce float format for bounds, even if data format is different
  
      ## Add pen width only if it is non-cosmetic.
      pen = self.opts["pen"]
      spen = self.opts["shadowPen"]
      if (
          pen is not None
          and not pen.isCosmetic()
          and pen.style() != QtCore.Qt.PenStyle.NoPen
      ):
          b = (
              b[0] - pen.widthF() * 0.7072,
              b[1] + pen.widthF() * 0.7072,
          )
      if (
          spen is not None
          and not spen.isCosmetic()
          and spen.style() != QtCore.Qt.PenStyle.NoPen
      ):
          b = (
              b[0] - spen.widthF() * 0.7072,
              b[1] + spen.widthF() * 0.7072,
          )
  
      self._boundsCache[ax] = [(frac, orthoRange), b]
      return b
  
imports: from ..Qt import QtCore, QtGui, QtWidgets
  import math
  import sys
  import warnings
  import numpy
  from ..None import Qt, debug
  from ..None import functions
  from ..None import getConfigOption
  from .GraphicsObject import GraphicsObject
label_desc: Use image node_id 7 for calling the PlotCurveItem obj's underlying member method code with example usage: obj.dataBounds(ax, frac, orthoRange) and returns: b, cache, None,  None, None,  None, None,  None, None,  None