5

I am trying to embed a matplotlib graph that updates every second into a PyQt GUI main window.

In my program I call an update function every second using threading.Timer via the timer function shown below. I have a problem: my program grows bigger every second - at a rate of about 1k every 4 seconds. My initial thoughts are that the append function (that returns a new array in update_figure) does not delete the old array? Is it possible this is the cause of my problem?

def update_figure(self):
    self.yAxis = np.append(self.yAxis, (getCO22()))
    self.xAxis = np.append(self.xAxis, self.i)
    # print(self.xAxis)
    if len(self.yAxis) > 10:
        self.yAxis = np.delete(self.yAxis, 0)

    if len(self.xAxis) > 10:
        self.xAxis = np.delete(self.xAxis, 0)

    self.axes.plot(self.xAxis, self.yAxis, scaley=False)
    self.axes.grid(True)

    self.i = self.i + 1

    self.draw()

This is my timer function - this is triggered by the click of a button in my PyQt GUI and then calls itself as you can see:

def timer(self):
    getCH4()
    getCO2()
    getConnectedDevices()
    self.dc.update_figure()
    t = threading.Timer(1.0, self.timer)
    t.start()

EDIT: I cant post my entire code because it requires a lot of .dll includes. So i'll try to explain what this program does.

In my GUI I want to show the my CO2 value over time. My get_co22 function just returns a float value and I'm 100% sure this works fine. With my timer, shown above, I want to keep append a value to a matplotlib graph - the Axes object is available to me as self.axes. I try to plot the last 10 values of the data.

EDIT 2: After some discussion in chat, I tried putting the call to update_figure() in a while loop and using just one thread to call it and was able to make this minimal example http://pastebin.com/RXya6Zah. This changed the structure of the code to call update_figure() to the following:

def task(self):
    while True:
        ui.dc.update_figure()
        time.sleep(1.0)

def timer(self):
    t = Timer(1.0, self.task())
    t.start()

but now the program crashes after 5 iterations or so.

11
  • 3
    Can you craft a simple example that we can actually run and verify? For example by removing the calls to additional functions and without the timer, s.t. it consumes memory really quick? Commented Apr 21, 2015 at 8:09
  • What do you intend to achieve with np.delete calls? Docs says, that it deletes subarray. So it seems like your arrays grows faster than they are shrinks. Commented Apr 21, 2015 at 8:16
  • @moooeeeep I edited my problem, is this better? Commented Apr 21, 2015 at 8:25
  • I think you need to show how your plotting works. My suspicion is that every time you call self.draw(), references to the arrays and other (maybe bigger) objects are retained. Commented Apr 21, 2015 at 8:25
  • 1
    More succinctly - I wonder if your GUI is the problem. Try commenting out self.draw() and / or self.axes.plot(... and see if you still have the memory leak. If not - you've got a clue where the problem lies. Commented Apr 21, 2015 at 8:26

2 Answers 2

2

The problem is definitely not with how you are appending to your numpy array, or truncating it.

The problem here is with your threading model. Integrating calculation loops with a GUI control loop is difficult.

Fundamentally, you need your GUI threading to have control of when your update code is called (spawning a new thread to handle it if necessary) - so that

  1. your code does not block the GUI updating,
  2. the GUI updating does not block your code executing and
  3. you don't spawn loads of threads holding multiple copies of objects (which might be where your memory leak comes from).

In this case, as your main window is controlled by PyQt4, you want to use a QTimer (see a simple example here)

So - alter your timer code to

def task(self):
    getCH4()
    getCO2()
    getConnectedDevices()
    self.dc.update_figure()

def timer(self):
    self.t = QtCore.QTimer()
    self.t.timeout.connect(self.task)
    self.t.start(1000)

and this should work. Keeping the reference to the QTimer is essential - hence self.t = QtCore.QTimer() rather than t = QtCore.QTimer(), otherwise the QTimer object will be garbage collected.


Note:

This is a summary of a long thread in chat clarifying the issue and working through several possible solutions. In particular - the OP managed to mock up a simpler runnable example here: http://pastebin.com/RXya6Zah

and the fixed version of the full runnable example is here: http://pastebin.com/gv7Cmapr

The relevant code and explanation is above, but the links might help anyone who wants to replicate / solve the issue. Note that they require PyQt4 to be installed

Sign up to request clarification or add additional context in comments.

Comments

0

if you are creating a new figure for every time this is quite common.

matplotlib do not free the figures you create, unless you ask it, somethink like:

pylab.close() 

see How can I release memory after creating matplotlib figures

1 Comment

Yes - that's where I was going with my comments to the OP, but it's not clear whether they're using matplotlib. It also seems that commenting out all the plotting did not relieve the problem.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.