class_docstr: **Bases:** :class:`GraphicsObject <pyqtgraph.GraphicsObject>`
  
  :class:`PlotDataItem` provides a unified interface for displaying plot curves, scatter plots, or both.
  It also contains methods to transform or decimate the original data before it is displayed. 
  
  As pyqtgraph's standard plotting object, ``plot()`` methods such as :func:`pyqtgraph.plot` and
  :func:`PlotItem.plot() <pyqtgraph.PlotItem.plot>` create instances of :class:`PlotDataItem`.
  
  While it is possible to use :class:`PlotCurveItem <pyqtgraph.PlotCurveItem>` or
  :class:`ScatterPlotItem <pyqtgraph.ScatterPlotItem>` individually, this is recommended only
  where performance is critical and the limited functionality of these classes is sufficient.
  
  ==================================  ==============================================
  **Signals:**
  sigPlotChanged(self)                Emitted when the data in this item is updated.
  sigClicked(self, ev)                Emitted when the item is clicked.
  sigPointsClicked(self, points, ev)  Emitted when a plot point is clicked
                                      Sends the list of points under the mouse.
  sigPointsHovered(self, points, ev)  Emitted when a plot point is hovered over.
                                      Sends the list of points under the mouse.
  ==================================  ==============================================
class_docstr_tok: ["*","*","Bases",":","*","*",":","class",":","`","GraphicsObject","<","pyqtgraph.GraphicsObject",">","`",":","class",":","`","PlotDataItem","`","provides","a","unified","interface","for","displaying","plot","curves",",","scatter","plots",",","or","both",".","It","also","contains","methods","to","transform","or","decimate","the","original","data","before","it","is","displayed",".","As","pyqtgraph","'s","standard","plotting","object",",","``","plot","(",")","``","methods","such","as",":","func",":","`","pyqtgraph.plot","`","and",":","func",":","`","PlotItem.plot","(",")","<","pyqtgraph.PlotItem.plot",">","`","create","instances","of",":","class",":","`","PlotDataItem","`",".","While","it","is","possible","to","use",":","class",":","`","PlotCurveItem","<","pyqtgraph.PlotCurveItem",">","`","or",":","class",":","`","ScatterPlotItem","<","pyqtgraph.ScatterPlotItem",">","`","individually",",","this","is","recommended","only","where","performance","is","critical","and","the","limited","functionality","of","these","classes","is","sufficient",".","==================================","==============================================","*","*","Signals",":","*","*","sigPlotChanged","(","self",")","Emitted","when","the","data","in","this","item","is","updated",".","sigClicked","(","self",",","ev",")","Emitted","when","the","item","is","clicked",".","sigPointsClicked","(","self",",","points",",","ev",")","Emitted","when","a","plot","point","is","clicked","Sends","the","list","of","points","under","the","mouse",".","sigPointsHovered","(","self",",","points",",","ev",")","Emitted","when","a","plot","point","is","hovered","over",".","Sends","the","list","of","points","under","the","mouse",".","==================================","=============================================="]
docstr: Returns a :class:`~.PlotDataset` object that contains data suitable for display 
  (after mapping and data reduction) as ``dataset.x`` and ``dataset.y``.
  Intended for internal use.
docstr_tok: ["Returns","a",":","class",":","`","~.PlotDataset","`","object","that","contains","data","suitable","for","display","(","after","mapping","and","data","reduction",")","as","``","dataset.x","``","and","``","dataset.y","``",".","Intended","for","internal","use","."]
code: def _getDisplayDataset(self):
      """
      Returns a :class:`~.PlotDataset` object that contains data suitable for display
      (after mapping and data reduction) as ``dataset.x`` and ``dataset.y``.
      Intended for internal use.
      """
      if self._dataset is None:
          return None
      # Return cached processed dataset if available and still valid:
      if (
          self._datasetDisplay is not None
          and not (
              self.property("xViewRangeWasChanged")
              and self.opts["clipToView"]
          )
          and not (
              self.property("xViewRangeWasChanged")
              and self.opts["autoDownsample"]
          )
          and not (
              self.property("yViewRangeWasChanged")
              and self.opts["dynamicRangeLimit"] is not None
          )
      ):
          return self._datasetDisplay
  
      # Apply data mapping functions if mapped dataset is not yet available:
      if self._datasetMapped is None:
          x = self._dataset.x
          y = self._dataset.y
          if y.dtype == bool:
              y = y.astype(np.uint8)
          if x.dtype == bool:
              x = x.astype(np.uint8)
  
          if self.opts["fftMode"]:
              x, y = self._fourierTransform(x, y)
              # Ignore the first bin for fft data if we have a logx scale
              if self.opts["logMode"][0]:
                  x = x[1:]
                  y = y[1:]
  
          if self.opts["derivativeMode"]:  # plot dV/dt
              y = np.diff(self._dataset.y) / np.diff(self._dataset.x)
              x = x[:-1]
          if self.opts["phasemapMode"]:  # plot dV/dt vs V
              x = self._dataset.y[:-1]
              y = np.diff(self._dataset.y) / np.diff(self._dataset.x)
  
          dataset = PlotDataset(
              x, y, self._dataset.xAllFinite, self._dataset.yAllFinite
          )
  
          if True in self.opts["logMode"]:
              dataset.applyLogMapping(
                  self.opts["logMode"]
              )  # Apply log scaling for x and/or y axis
  
          self._datasetMapped = dataset
  
      # apply processing that affects the on-screen display of data:
      x = self._datasetMapped.x
      y = self._datasetMapped.y
      xAllFinite = self._datasetMapped.xAllFinite
      yAllFinite = self._datasetMapped.yAllFinite
  
      view = self.getViewBox()
      if view is None:
          view_range = None
      else:
          view_range = view.viewRect()  # this is always up-to-date
      if view_range is None:
          view_range = self.viewRect()
  
      ds = self.opts["downsample"]
      if not isinstance(ds, int):
          ds = 1
  
      if self.opts["autoDownsample"]:
          # this option presumes that x-values have uniform spacing
  
          if xAllFinite:
              finite_x = x
          else:
              # False: (we checked and found non-finites)
              # None : (we haven't performed a check for non-finites yet)
              finite_x = x[
                  np.isfinite(x)
              ]  # ignore infinite and nan values
  
          if view_range is not None and len(finite_x) > 1:
              dx = float(finite_x[-1] - finite_x[0]) / (
                  len(finite_x) - 1
              )
              if dx != 0.0:
                  width = self.getViewBox().width()
                  if (
                      width != 0.0
                  ):  # autoDownsampleFactor _should_ be > 1.0
                      ds_float = max(
                          1.0,
                          abs(
                              view_range.width()
                              / dx
                              / (
                                  width
                                  * self.opts["autoDownsampleFactor"]
                              )
                          ),
                      )
                      if math.isfinite(ds_float):
                          ds = int(ds_float)
                  ## downsampling is expensive; delay until after clipping.
  
          # use the last computed value if our new value is not too different.
          # this guards against an infinite cycle where the plot never stabilizes.
          if math.isclose(ds, self._adsLastValue, rel_tol=0.01):
              ds = self._adsLastValue
          self._adsLastValue = ds
  
      if self.opts["clipToView"]:
          if view is None or view.autoRangeEnabled()[0]:
              pass  # no ViewBox to clip to, or view will autoscale to data range.
          else:
              # clip-to-view always presumes that x-values are in increasing order
              if view_range is not None and len(x) > 1:
                  # find first in-view value (left edge) and first out-of-view value (right edge)
                  # since we want the curve to go to the edge of the screen, we need to preserve
                  # one down-sampled point on the left and one of the right, so we extend the interval
                  x0 = bisect.bisect_left(x, view_range.left()) - ds
                  x0 = fn.clip_scalar(x0, 0, len(x))  # workaround
                  # x0 = np.searchsorted(x, view_range.left()) - ds
                  # x0 = np.clip(x0, 0, len(x))
  
                  x1 = bisect.bisect_left(x, view_range.right()) + ds
                  x1 = fn.clip_scalar(x1, x0, len(x))
                  # x1 = np.searchsorted(x, view_range.right()) + ds
                  # x1 = np.clip(x1, 0, len(x))
                  x = x[x0:x1]
                  y = y[x0:x1]
  
      if ds > 1:
          if self.opts["downsampleMethod"] == "subsample":
              x = x[::ds]
              y = y[::ds]
          elif self.opts["downsampleMethod"] == "mean":
              n = len(x) // ds
              stx = (
                  ds // 2
              )  # start of x-values; try to select a somewhat centered point
              x = x[stx : stx + n * ds : ds]
              y = y[: n * ds].reshape(n, ds).mean(axis=1)
          elif self.opts["downsampleMethod"] == "peak":
              n = len(x) // ds
              x1 = np.empty((n, 2))
              stx = (
                  ds // 2
              )  # start of x-values; try to select a somewhat centered point
              x1[:] = x[stx : stx + n * ds : ds, np.newaxis]
              x = x1.reshape(n * 2)
              y1 = np.empty((n, 2))
              y2 = y[: n * ds].reshape((n, ds))
              y1[:, 0] = y2.max(axis=1)
              y1[:, 1] = y2.min(axis=1)
              y = y1.reshape(n * 2)
  
      if self.opts["dynamicRangeLimit"] is not None:
          if view_range is not None:
              data_range = self._datasetMapped.dataRect()
              if data_range is not None:
                  view_height = view_range.height()
                  limit = self.opts["dynamicRangeLimit"]
                  hyst = self.opts["dynamicRangeHyst"]
                  # never clip data if it fits into +/- (extended) limit * view height
                  if (  # note that "bottom" is the larger number, and "top" is the smaller one.
                      view_height
                      > 0  # never clip if the view does not show anything and would cause division by zero
                      and not data_range.bottom()
                      < view_range.top()  # never clip if all data is too small to see
                      and not data_range.top()
                      > view_range.bottom()  # never clip if all data is too large to see
                      and data_range.height()
                      > 2 * hyst * limit * view_height
                  ):
                      cache_is_good = False
                      # check if cached display data can be reused:
                      if (
                          self._datasetDisplay is not None
                      ):  # top is minimum value, bottom is maximum value
                          # how many multiples of the current view height does the clipped plot extend to the top and bottom?
                          top_exc = (
                              -(
                                  self._drlLastClip[0]
                                  - view_range.bottom()
                              )
                              / view_height
                          )
                          bot_exc = (
                              self._drlLastClip[1] - view_range.top()
                          ) / view_height
                          # print(top_exc, bot_exc, hyst)
                          if (
                              top_exc >= limit / hyst
                              and top_exc <= limit * hyst
                              and bot_exc >= limit / hyst
                              and bot_exc <= limit * hyst
                          ):
                              # restore cached values
                              x = self._datasetDisplay.x
                              y = self._datasetDisplay.y
                              cache_is_good = True
                      if not cache_is_good:
                          min_val = (
                              view_range.bottom() - limit * view_height
                          )
                          max_val = (
                              view_range.top() + limit * view_height
                          )
                          # print('alloc:', end='')
                          # workaround for slowdown from numpy deprecation issues in 1.17 to 1.20+ :
                          # y = np.clip(y, a_min=min_val, a_max=max_val)
                          y = fn.clip_array(y, min_val, max_val)
                          self._drlLastClip = (min_val, max_val)
      self._datasetDisplay = PlotDataset(x, y, xAllFinite, yAllFinite)
      self.setProperty("xViewRangeWasChanged", False)
      self.setProperty("yViewRangeWasChanged", False)
  
      return self._datasetDisplay
  
imports: import math
  import warnings
  import bisect
  import numpy
  from ..None import debug
  from ..None import functions
  from ..None import getConfigOption
  from ..Qt import QtCore
  from .GraphicsObject import GraphicsObject
  from .PlotCurveItem import PlotCurveItem
  from .ScatterPlotItem import ScatterPlotItem
label_desc: Use image node_id 31 for calling the PlotDataItem obj's underlying member method code with example usage: obj._getDisplayDataset() and returns: self, None, self