(wx.lib.plot) PlotCanvas does not update itself

Hi.
I adjust the objects in the plot dynamically, but it only applies when the frame has been resized manually by mouse dragging.
I had got a similar problem long before and Refresh(), Update(), or something was helpful at that time as I remember and the old posts says. But it is not working now.
Here is the simplified code that adds and removes a marker by button clicked. As you can see, you have to resize the frame to see the change. (Only the first addition is shown immediately as you add)

How can I force update?

[Environment]
Windows 10
Python 3.9.6
wxPython 4.1.1


import wx
import numpy as np
from wx.lib.plot import PlotCanvas, PlotGraphics, PolyMarker

class myframe(wx.Frame):

    def __init__(self):
        wx.Frame.__init__(self, None)

        self.panel = wx.Panel(self)
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.x = []
        self.y = []
        self.marker = PolyMarker([])
        self.graphics = PlotGraphics([self.marker])
        self.canvas = PlotCanvas(self.panel)
        self.canvas.Draw(self.graphics, xAxis=(-1, 11), yAxis=(-1, 11))

        sizer_btn = wx.BoxSizer(wx.HORIZONTAL)

        self.btn1 = wx.Button(self.panel, label='Add')
        self.btn1.Bind(wx.EVT_BUTTON, self.add)
        sizer_btn.Add(self.btn1, 0, wx.ALL, 3)

        self.btn2 = wx.Button(self.panel, label='Remove')
        self.btn2.Bind(wx.EVT_BUTTON, self.remove)
        sizer_btn.Add(self.btn2, 0, wx.ALL, 3)

        sizer.Add(self.canvas, 1, wx.EXPAND)
        sizer.Add(sizer_btn, 0, wx.ALIGN_RIGHT)
        self.panel.SetSizer(sizer)

    def add(self, e):
        self.x.append(len(self.x))
        self.y.append(len(self.y))       
        self.my_redraw()    

    def remove(self, e):
        if len(self.x) >= 1:
            self.x.pop()
            self.y.pop()
            self.my_redraw()    

    def my_redraw(self):
        points = np.array([self.x, self.y]).transpose().tolist()
        self.marker.points = points

        # Option 1 ------------
        self.canvas.Redraw()
        # ---------------------

        # Option 2 ------------
        # self.canvas.Clear()
        # self.canvas.Draw(self.graphics, xAxis=(0, 10), yAxis=(0, 10))
        # ---------------------

        # Nothing worked here ---------------------
        # self.canvas.Refresh()
        # self.canvas.Update()
        # self.canvas.Layout()
        # self.panel.Refresh()
        # self.panel.Update()
        # self.panel.Layout()
        # self.Refresh()
        # self.Update()
        # self.Layout()
        # -----------------------------------------

if __name__ == '__main__':
    app = wx.App()
    frame = myframe()
    frame.Show()
    app.MainLoop()

The fun part is that not even “self.canvas.Redraw” is necessary to the (non-)functioning of your example. If you comment it out, you’ll see that your code will work just the same. So, apparently “Redraw” does not redraws at all, at least not the way it’s supposed to.
The frustrating part is that the canvas won’t respond to a “SendSizeEvent()” either, a trick that usually works in such cases.
Now, from a cursory glance at the code of the examples, it looks like wx.lib.plot is intended for “static” plots only, and there are no hooks for updating “on the fly” an already existing graph. Of course, nothing stops you from erasing all the content and start over… but if you need frequent updates (say, a “live” graphic plotting from a data stream) then you will end up with some flickering I’m afraid.
You should probably dig in the source code and find out what exactly happens in response to a EVT_SIZE (since this is when your plot get updated, apparently) and see if you can trigger the same thing on demand. And while you’re at it, perhaps also take a look at that “Redraw” thing, and figure out what it really does.

omg, it is true that I even don’t need “Redraw().”
I will look into EVT_SIZE as you mentioned.
By the way, do you have any recommended sight-package for ‘live’ plotting? I use Matplotlib usually but it it not good for live at all.

Not sure what you need for “live” plotting, but I’ve discovered a way to make “oscilloscope” type plots with Matplotlib with fairly rapid re-plotting. Here’s an example:
oscope.py (473 Bytes)
This runs pretty fast on my machine using the WxAgg backend.

Had the same issue where I was regenerating plots but the matplot canvas would only update whenever I resized the wxPython frame.
Tried generating resize events, or plt.show(), redraw() etc etc. but none of this helped.

Not sure why but setting matplotlib to interactive mode ( matplotlib.pyplot.ion() ) solved the issue for me. (I used TkAgg as backend)

Hope this helps

I suppose you have a FigureCanvasWxAgg instance to embed matplotlib canvas into wx.

Then, usually what you need to do for updating the canvas is FigureCanvasWxAgg.draw().

I think wx.lib.plot does not work as you suppose. Try this:

def my_redraw(self):
    points = np.array([self.x, self.y]).transpose().tolist()

    marker = PolyMarker(points)
    graphics = PlotGraphics([marker])

    self.canvas.Draw(graphics)

I guess one could say wx.lib.plot is not a modern design. I have used it for >20 years and it was always like this :slight_smile:

Redraw() is just to redraw exactly the same what was shown before, e.g. after a resize event.

if you are using wxPython and matplotib already, you might find wxmplot (pip install wxmplot) interesting. It can be used for “live updating”. The examples include a simple strip chart application and a timing test for updating a plot. Docs are at wxmplot: wxPython plotting widgets using matplotlib — WXMPLOT: plotting widgets using wxPython and matplotlib

1 Like