0

I've noticed that every solution to plotting continuously updating data (I've found) with a continuously increasing length has one huge setback - If the data isn't there immediately, the matplotlib window freezes (says not responding). Take this for example:

from matplotlib import pyplot as plt
from matplotlib import animation
from random import randint
from time import sleep
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
line, = ax.plot([])
x = []
y = []
def animate(i):
    x.append(i)
    y.append(randint(0,10))
    for i in range(100000000):
        # Do calculations to attain next data point
        pass
    line.set_data(x, y)
    return line,

anim = animation.FuncAnimation(fig, animate,
                               frames=200, interval=20, blit=True)
plt.show()

This code works fine without the data acquisition for loop in the animate function, but with it there, the graph window freezes. Take this as well:

plt.ion()
x = []

for i in range(1000):
    x.append(randint(0,10))
    for i in range(100000000):
        # Do calculations to attain next data point
        pass
    plt.plot(x)
    plt.pause(0.001)

Also freezes. (Thank god for that, because using this method it's borderline impossible to close as the graph keeps popping up in front of everything. I do not recommend removing the sleep)

This too:

plt.ion()
x = []

for i in range(1000):
    x.append(randint(0,10))
    for i in range(100000000):
        # Do calculations to attain next data point
        pass
    plt.plot(x)
    plt.draw()
    plt.pause(0.001)
    plt.clf()

Also this: (copied from https://stackoverflow.com/a/4098938/9546874)

import matplotlib.pyplot as plt
import numpy as np
from time import sleep
x = np.linspace(0, 6*np.pi, 100)
y = np.sin(x)

# You probably won't need this if you're embedding things in a tkinter plot...
plt.ion()

fig = plt.figure()
ax = fig.add_subplot(111)
line1, = ax.plot(x, y, 'r-') # Returns a tuple of line objects, thus the comma

for phase in np.linspace(0, 10*np.pi, 500):
    line1.set_ydata(np.sin(x + phase))
    for i in range(100000000):
        # Do calculations to attain next data point
        pass
    fig.canvas.draw()
    fig.canvas.flush_events()

This is a huge problem, as it's naive to think all the data will come at consistent intervals. I just want a graph that updates when data comes, and doesn't implode in the downtime. Keep in mind the interval between data could change, it could be 2 seconds, or 5 minutes.

EDIT:

After further testing, the FuncAnimation one can be used, but it's very hacky, and is still a bit broken. If you increase the interval to above the expected time of animate, it will work, but every time you pan or zoom the graph, all the data disappears until the next update. So once you have a view, you can't touch it.

Edit:

Changed sleep to a for loop for clarity

Recessive
  • 1,780
  • 2
  • 14
  • 37
  • I like this solution: https://stackoverflow.com/a/15724978 – IcedLance Jul 11 '19 at 07:02
  • This is an inherent problem with GUIs. On the one hand you want a responsive GUI, which means that an event loop needs to constantly run. On the other hand you want to perform an extensive calculation. To satisfy both requirements, the event loop and the calculation need to run in different threads. This would be shown in [Python update Matplotlib from threads](https://stackoverflow.com/questions/48389470/python-update-matplotlib-from-threads/48389850#48389850) – ImportanceOfBeingErnest Jul 11 '19 at 13:33

2 Answers2

1

Updated Answer: The problem is that data aquisition or generation and the matplotlib window run on the same thread so that the former is blocking the latter. To overcome this move the data aquisition into a seperate process as shown in this example. Instead of processes and pipes you can also use threads and queues.

Stef
  • 28,728
  • 2
  • 24
  • 52
  • That's not the point, I used `sleep` as a place holder for some block of code that could take up to 2 seconds. I have replaced `sleep` with such for clarity. – Recessive Jul 11 '19 at 06:47
  • I think this is the very point. Sleep or the for loop you now put in your examples are executed on the main loop for tkinter thus making the program unresponsive. You have to do the data aquisition asynchronously. This is demonstrated with plt.pause vs sleep. – Stef Jul 11 '19 at 06:54
  • You mean thread the data collection, and use `plt.pause` to wait for the thread to return? Is there any other function for matplotlib that instead of waiting a hardcoded amount of time, it dynamically waits until the thread returns? – Recessive Jul 11 '19 at 06:57
  • 1
    Yes, the basic idea would be to aquire your data on a seperate thread. Maybe [this example](https://matplotlib.org/3.1.0/gallery/misc/multiprocess_sgskip.html) is of any help. – Stef Jul 11 '19 at 07:39
  • 1
    I just tested the approach of the [linked example](https://matplotlib.org/3.1.0/gallery/misc/multiprocess_sgskip.html) by inserting a `sleep(5)` before `data = np.random.random(2)`, simulating that new data will arrive every 5 seconds only, and the matplotlib window stays perfectly responsive. – Stef Jul 11 '19 at 10:33
  • Yep that does it, if you edit your answer to include this information I'll mark it as answered – Recessive Jul 18 '19 at 06:56
0

See this example with sleep usage, it's working well:

=^..^=

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation


fig, ax = plt.subplots()
x = np.arange(0, 2*np.pi, 0.01)
line, = ax.plot(x)


def animate(i):
    y_data = 0
    for j in range(10):
        y_data = np.random.uniform(-1, j, 1)

    line.set_ydata(y_data)
    plt.pause(1)
    return line,


ani = animation.FuncAnimation(
    fig, animate, interval=2, blit=True, save_count=50)


plt.ylim(-2, 11)
plt.show()
Zaraki Kenpachi
  • 5,510
  • 2
  • 15
  • 38
  • While the window no longer gets stuck on "not responding", it is now incredibly slow. Is there any other solution? It's still unusable. This data also has a fixed length, where as the data I'll be using will not (I have updated this above) – Recessive Jul 11 '19 at 06:52
  • @Recessive hmm.. don't understand you problem. What is expected result of all of this? – Zaraki Kenpachi Jul 11 '19 at 07:02
  • It's just the actual window that shows the graph is very slow. Moving the graph window takes ages, and it frequently freezes my screen so I can't use my cursor. – Recessive Jul 11 '19 at 07:05
  • @Recessive you need to make any actions to window like: resize, zoom? – Zaraki Kenpachi Jul 11 '19 at 07:06
  • Yes, but those aren't so much of a concern when my screen is frozen – Recessive Jul 11 '19 at 07:08