code: def scrollContentsBy(self, dx, dy):
          
          if self._adjusting:
              return
  
          self._scrolling = True
          try:
              bd = self.viewer.get_bindings()
              res = bd.calc_pan_pct(self.viewer, pad=self.pad)
              if res is None:
                  return
              pct_x, pct_y = res.pan_pct_x, res.pan_pct_y
  
              # Only adjust pan setting for axes that have changed
              if dx != 0:
                  hsb = self.horizontalScrollBar()
                  pos_x = float(hsb.value())
                  pct_x = pos_x / float(self.upper_h)
              if dy != 0:
                  vsb = self.verticalScrollBar()
                  pos_y = float(vsb.value())
                  # invert Y pct because of orientation of scrollbar
                  pct_y = 1.0 - (pos_y / float(self.upper_v))
  
              bd = self.viewer.get_bindings()
              bd.pan_by_pct(self.viewer, pct_x, pct_y, pad=self.pad)
  
              # This shouldn't be necessary, but seems to be
              self.viewer.redraw(whence=0)
  
          finally:
              self._scrolling = False
docs: Override from QAbstractScrollArea.
          Called when the scroll bars are adjusted by the user.