Repository: pyqtgraph/pyqtgraph

Issue:
BarGraphItem is not showing if bar coordinates are huge
### Short description
See example below, use `data` to create a `BarGraphItem`, there will be nothing, whereas use `data_ok` everything is normal.

### Code to reproduce
```python
"""
Simple example using BarGraphItem
"""

import numpy as np
import pyqtgraph as pg

win = pg.plot()
win.setWindowTitle('pyqtgraph example: BarGraphItem')

# prepare data

# bars are invisible, might show up when resizing
data = np.random.randint(2E9, 4E9, 1000)

# # if data is not very big, everything is fine
# data_ok = np.random.randint(200, 400, 1000)

counts, edges = np.histogram(data, bins=50)
height = edges[1]-edges[0]
bg1 = pg.BarGraphItem(x0=0, y=edges[:-1], height=height, width=counts, brush='r')
# set viewbox range
vb = win.getPlotItem().getViewBox()
vb.setLimits(xMin=0, 
             xMax=counts.max() * 1.2,
             yMin=edges[0] * 0.9, 
             yMax=edges[-1] * 1.1)

win.addItem(bg1)

if __name__ == '__main__':
    pg.exec()
```

### Real behavior
Blank scene.
<img width="752" alt="image" src="https://user-images.githubusercontent.com/26359487/206895825-8e77897a-3b3f-4b19-83b4-cfd16a7c66e1.png">


### Tested environment(s)

 * PyQtGraph version: 0.13.1
 * Qt Python binding: PyQt5 5.15.4 Qt 5.15.2
 * Python version: 3.9
 * NumPy version: 1.20.3
 * Operating system: macOS
 * Installation method: pip



Discussion:
FYI, if we right click on this blank scene and export it to png image, we can get the expected result.

It looks like something's wrong in viewbox?
The issue seems to be that `BarGraphItem`, `NonUniformImage`, `PColorMeshItem` and `customGraphicsItem` all compute their `boundingRect` using `QPicture.boundingRect() -> QRect`. As `QPicture`'s bounding rectangle is only a `QRect` and not a `QRectF`, its limits are determined by the range of an `int32`.

For `BarGraphItem`, it seems that your issue could be solved if `BarGraphItem::boundingRect()` were defined as:
```python
def boundingRect(self):
    return self.shape().boundingRect()
```
where `self.shape()` is a `QPainterPath` and `QPainterPath.boundingRect() -> QRectF`. 


By the way, `data = np.random.randint(2E9, 4E9, 1000)`  raises an exception on Windows platform
Thanks @pijyoi , It works!

You could close this issue now.
@pijyoi we should probably implement the suggested feature in the library, _but_ `self.shape().boundingRect()` is much more expensive than `QPicture.boundingRect()` if I remember right.  Can you think of a potentially easier method to check if calling self.shape().boundingRect() would be needed?
Each bar is drawn as a rectangle. It should be possible to build up the bounding rectangle while drawing each bar.

There's also `self.shape().controlPointRect()` which "_is significantly faster to compute than the exact boundingRect()_".
For both `QPainterPath::boundingRect()` and `QPainterPath::controlPointRect()`, the results are cached.

Resolving patch:
diff --git a/pyqtgraph/examples/customGraphicsItem.py b/pyqtgraph/examples/customGraphicsItem.py
--- a/pyqtgraph/examples/customGraphicsItem.py
+++ b/pyqtgraph/examples/customGraphicsItem.py
@@ -19,6 +19,7 @@ def __init__(self, data):
     def generatePicture(self):
         ## pre-computing a QPicture object allows paint() to run much more quickly, 
         ## rather than re-drawing the shapes every time.
+        bounds = QtCore.QRectF()
         self.picture = QtGui.QPicture()
         p = QtGui.QPainter(self.picture)
         p.setPen(pg.mkPen('w'))
@@ -30,7 +31,9 @@ def generatePicture(self):
             else:
                 p.setBrush(pg.mkBrush('g'))
             p.drawRect(QtCore.QRectF(t-w, open, w*2, close-open))
+            bounds |= QtCore.QRectF(t-w, min, w*2, max-min)
         p.end()
+        self._bounds = bounds
     
     def paint(self, p, *args):
         p.drawPicture(0, 0, self.picture)
@@ -38,8 +41,7 @@ def paint(self, p, *args):
     def boundingRect(self):
         ## boundingRect _must_ indicate the entire area that will be drawn on
         ## or else we will get artifacts and possibly crashing.
-        ## (in this case, QPicture does all the work of computing the bouning rect for us)
-        return QtCore.QRectF(self.picture.boundingRect())
+        return self._bounds
 
 data = [  ## fields are (time, open, close, min, max).
     (1., 10, 13, 5, 15),
diff --git a/pyqtgraph/graphicsItems/BarGraphItem.py b/pyqtgraph/graphicsItems/BarGraphItem.py
--- a/pyqtgraph/graphicsItems/BarGraphItem.py
+++ b/pyqtgraph/graphicsItems/BarGraphItem.py
@@ -159,9 +159,7 @@ def paint(self, p, *args):
         self.picture.play(p)
             
     def boundingRect(self):
-        if self.picture is None:
-            self.drawPicture()
-        return QtCore.QRectF(self.picture.boundingRect())
+        return self.shape().controlPointRect()
     
     def shape(self):
         if self.picture is None:
diff --git a/pyqtgraph/graphicsItems/NonUniformImage.py b/pyqtgraph/graphicsItems/NonUniformImage.py
--- a/pyqtgraph/graphicsItems/NonUniformImage.py
+++ b/pyqtgraph/graphicsItems/NonUniformImage.py
@@ -76,6 +76,7 @@ def generatePicture(self):
 
         x, y, z = self.data
 
+        bounds = QtCore.QRectF()
         self.picture = QtGui.QPicture()
         p = QtGui.QPainter(self.picture)
         p.setPen(mkPen(None))
@@ -116,7 +117,11 @@ def generatePicture(self):
                 b = y[0] if j == 0 else (y[j - 1] + y[j]) / 2
                 t = (y[j] + y[j + 1]) / 2 if j < y.size - 1 else y[-1]
 
-                p.drawRect(QtCore.QRectF(l, t, r - l, b - t))
+                rect = QtCore.QRectF(l, t, r - l, b - t)
+                bounds |= rect
+                p.drawRect(rect)
+
+        self._bounds = bounds
 
         if self.border is not None:
             p.setPen(self.border)
@@ -131,4 +136,4 @@ def paint(self, p, *args):
         p.drawPicture(0, 0, self.picture)
 
     def boundingRect(self):
-        return QtCore.QRectF(self.picture.boundingRect())
+        return self._bounds


Test patch: