Pyqtchart GraphicsView ImageItem doesn't update same reference!

Hello folks, New python/pyqtgraph user here, 30yr C/C++/etc programmer,

I’m trying to find a solution to either a bug in pyqtgraph’s QGraphicsView updating or my bad use of it.

Long story short, ahem, I have a class that manages some image data, we’ll call it imgframe, this is made up of two numpy arrays of RGB values. I’m using two because I’m buffering the drawing of changing data over a static unchanging background, So each new frame consists of a static background and overtop of that each time, new image data that is, in effect, the changes of some other data this frame.

so while the next frame is being drawn, the changes are calculated as small region splices and these small numpy arrays that represent these changes are integrated into the right region of the larger numpy array.

to do this, my method was to save one copy of the numpy image data array as _static_background_arr, and the other as _curr_frame.

This image data management class, imgframe, exports its current frame as a property, curr_frame, which just returns the _curr_frame member.

At the UI level, I have a pg.GraphicsView that is programmatically created in my Qt App’s Init, along with the immediate creation of a pg.ImageItem(), which I immediately add to the GraphicsView via .addItem(). These objects are maintained as members, we’ll call them _canvas and _canvas_img

This is all the setup needed, and at initialization, the _static_background_arr and _curr_frame arrays are distinct, but contain the same data.

When the app starts, I see the expected static background. So far so good.

When the UI draws frames, it’s a simple method, (not working exactly, but sort of, we’ll get to that in a sec.)

I simply call

  _canvas_img.setImage(imgframe.curr_frame)
  imgframe.next()

Now, follow me here, this gets nuanced. The imgframe class has a next() method to buffer and build the next frame from a static and unchanging background, and data that changes on top of that.

All “drawing” is done on the _curr_frame array, then this is swapped out for a clean background, copied over from _static_background_arr, so _curr_frame has a new background to start drawing for the next frame.

Now this WORKS perfectly drawing the first frame, and I see the background I expect immediately upon intialization and load of the app.

Here is the image data class’s next() method,

   def next(self):
      np.copyto(_curr_frame, _static_background_arr)

Ok, so here’s what happens, the app and GUI load, I see my static background image. As I manually advance 1 frame, none of the changed data is displayed.

So, I removed the call to imgframe.next() in the draw method,

And voila’ i get the changed data displayed, however, it’s overtop, and since it’s DIFFERENT it’s right next to the old data.

In other words, because I eliminated the call to .next(), the imgframe class’s _curr_frame data contains the static background, the data from the inital frame, and the data from the NEXT frame also shoved right on top. So my lines and drawing objects look, shall we say thicker, as their only pixels off from their last position. I’m drawing each new frame on the accumulated current frame.

SO, this OBVIOUSLY has something to do with pg.GraphicView and pg.ImageItem NOT handling new data, IF THE REFERENCE OBJECT to that data remains UNCHANGED!!

This would obviously be a design bug.

IF IN FACT, I KEEP the call to imgframe.next() BUT INSTEAD, force a deep copy of the numpy array, before passing it off to the ImageItem,

  data_copy = imgframe.curr_frame.copy()
  _canvas_img.setImage(data_copy)
  imgframe.next()

EVERYTHING WORKS PERFECTLY, albeit not as fast as my trying to buffer each frame myself! BUT this tells us that my framework, up to this point is working, although I
don’t assert it’s working as it should, despite appearing to.

My problem is that I’m fairly minimalist and I purposely dispensed with junk I didn’t need to. For example, a lot of discussion near this topic, involves managing your scene. I noticed that calling .addItem on the GraphicsView object, does internally so a .scene.addItem, and since I’m already buffering the frames, because I NEED to, I just stayed with keeping this one default scene, and trying to update my imageItem. Still, I’m not experienced enough with pyqtgraph to know whether how I’m using it, in this minimalist way, might be causing some side-effects I don’t understand. I don’t think so, but I’m leaving that door wide open.

For the Record, my buffering works, I’ve confirmed that the memory addresses of BOTH my data arrays are DISTINCT, and once I’ve created them internally, i’m just copying data back and forth not reallocating each.

I still think this is a bug in GraphicsView/ImageItem, as short circuiting an update just because my numpy array is memory wise the SAME is a design flaw, this sort of data array flipping is
common and usually trivial, and in the short bit I do know of pyqtgraph I don’t see any part of that framework making what I’m tying to do easier, or more efficient. And it seems a fairly normal and expected task of creating frames of changing data!

I’ve tried everything I can think of to force an update. My draw frame method looks a lot like this,

   #   data_copy = _curr_frame.copy()
   #   _canvas_img.clear()

   #   _canvas_img = pg.ImageItem()
   #   _canvas.addItem(_canvas_img)

      _canvas_img.setImage(imgframe.curr_frame)
   #   _canvas_img.updateImage(imgframe.curr_frame)
   #   _canvas_img.setImage(data_copy)

   #   _canvas_img.informViewBoundsChanged()
   #   _canvas_img.update()

      imgframe.next()

But so far, nothing. The only thing that works is a deep copy of the current frame array, before passing it to .setImage().

Which is exactly what I don’t want to have to do!

Ideas anyone?
Many thanks from Houston
Jim