Repository: pyqtgraph/pyqtgraph

Issue:
GridItem causes duplicate view udpates
When a GridItem is added to a ViewBox, any change to the view range results in the view being repainted twice.



Discussion:
### Code to reproduce
```python3
# -*- coding: utf-8 -*-
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui
import numpy as np

## create GUI
app = QtGui.QApplication([])
w = pg.GraphicsLayoutWidget()
w.show()

class TestVB(pg.ViewBox):
    count = 0
    def paint(self, *args, **kwargs):
        TestVB.count += 1
        print(TestVB.count, "vb paint called")
        pg.ViewBox.paint(self, *args, **kwargs)
    def update(self, *args, **kwargs):
        TestVB.count += 1
        print(TestVB.count, "vb update called")
        pg.ViewBox.update(self, *args, **kwargs)

vb = TestVB()
w.setCentralItem(vb)
p1 = pg.PlotDataItem([1,2],[3,4])
vb.addItem(p1)
print("---")
g = pg.GridItem()
vb.addItem(g)
print("---")

## Start Qt event loop unless running in interactive mode or using pyside.
if __name__ == '__main__':
    import sys
    if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
        QtGui.QApplication.instance().exec_()
```
### Tested environment(s)

 * PyQtGraph version: commit ad7453c7818eddb3b18354c65370f416cb668b29 from `develop`
 * Qt Python binding: PyQt5 5.9.2 Qt 5.9.6
 * Python version: 3.6.3
 * NumPy version: 1.12.1
 * Operating system: Xubuntu 16.04.6 LTS

Resolving patch:
diff --git a/pyqtgraph/graphicsItems/ViewBox/ViewBox.py b/pyqtgraph/graphicsItems/ViewBox/ViewBox.py
--- a/pyqtgraph/graphicsItems/ViewBox/ViewBox.py
+++ b/pyqtgraph/graphicsItems/ViewBox/ViewBox.py
@@ -281,23 +281,10 @@ def implements(self, interface):
             #if scene is not None and hasattr(scene, 'sigPrepareForPaint'):
                 #scene.sigPrepareForPaint.connect(self.prepareForPaint)
         #return ret
-
-    def checkSceneChange(self):
-        # ViewBox needs to receive sigPrepareForPaint from its scene before
-        # being painted. However, we have no way of being informed when the
-        # scene has changed in order to make this connection. The usual way
-        # to do this is via itemChange(), but bugs prevent this approach
-        # (see above). Instead, we simply check at every paint to see whether
-        # (the scene has changed.
-        scene = self.scene()
-        if scene == self._lastScene:
-            return
-        if self._lastScene is not None and hasattr(self._lastScene, 'sigPrepareForPaint'):
-            self._lastScene.sigPrepareForPaint.disconnect(self.prepareForPaint)
-        if scene is not None and hasattr(scene, 'sigPrepareForPaint'):
-            scene.sigPrepareForPaint.connect(self.prepareForPaint)
+        
+    def update(self, *args, **kwargs):
         self.prepareForPaint()
-        self._lastScene = scene
+        GraphicsWidget.update(self, *args, **kwargs)
 
     def prepareForPaint(self):
         #autoRangeEnabled = (self.state['autoRange'][0] is not False) or (self.state['autoRange'][1] is not False)
@@ -437,13 +424,20 @@ def clear(self):
 
     def resizeEvent(self, ev):
         self._matrixNeedsUpdate = True
+        self.updateMatrix()
+        
         self.linkedXChanged()
         self.linkedYChanged()
+        
         self.updateAutoRange()
         self.updateViewRange()
+        
         self._matrixNeedsUpdate = True
+        self.updateMatrix()
+        
         self.background.setRect(self.rect())
         self.borderRect.setRect(self.rect())
+        
         self.sigStateChanged.emit(self)
         self.sigResized.emit(self)
         self.childGroup.prepareGeometryChange()
@@ -529,6 +523,14 @@ def setRange(self, rect=None, xRange=None, yRange=None, padding=None, update=Tru
 
         # Update axes one at a time
         changed = [False, False]
+
+        # Disable auto-range for each axis that was requested to be set
+        if disableAutoRange:
+            xOff = False if setRequested[0] else None
+            yOff = False if setRequested[1] else None
+            self.enableAutoRange(x=xOff, y=yOff)
+            changed.append(True)
+            
         for ax, range in changes.items():
             mn = min(range)
             mx = max(range)
@@ -569,13 +571,6 @@ def setRange(self, rect=None, xRange=None, yRange=None, padding=None, update=Tru
             lockY = False
         self.updateViewRange(lockX, lockY)
 
-        # Disable auto-range for each axis that was requested to be set
-        if disableAutoRange:
-            xOff = False if setRequested[0] else None
-            yOff = False if setRequested[1] else None
-            self.enableAutoRange(x=xOff, y=yOff)
-            changed.append(True)
-
         # If nothing has changed, we are done.
         if any(changed):
             # Update target rect for debugging
@@ -1548,7 +1543,6 @@ def updateViewRange(self, forceX=False, forceY=False):
     def updateMatrix(self, changed=None):
         if not self._matrixNeedsUpdate:
             return
-
         ## Make the childGroup's transform match the requested viewRange.
         bounds = self.rect()
 
@@ -1577,8 +1571,6 @@ def updateMatrix(self, changed=None):
         self.sigTransformChanged.emit(self)  ## segfaults here: 1
 
     def paint(self, p, opt, widget):
-        self.checkSceneChange()
-
         if self.border is not None:
             bounds = self.shape()
             p.setPen(self.border)


Test patch: