How to Detach Matplotlib Plots So That the Computation Can Continue

Is there a way to detach matplotlib plots so that the computation can continue?

Use matplotlib's calls that won't block:

Using draw():

from matplotlib.pyplot import plot, draw, show
plot([1,2,3])
draw()
print('continue computation')

# at the end call show to ensure window won't close.
show()

Using interactive mode:

from matplotlib.pyplot import plot, ion, show
ion() # enables interactive mode
plot([1,2,3]) # result shows immediatelly (implicit draw())

print('continue computation')

# at the end call show to ensure window won't close.
show()

python: how to keep the script run after plt.show()

I did have a similar problem and found solution here on SO.I think that you need plt.ion().One example

import numpy as np
from matplotlib import pyplot as plt

def main():
plt.axis([-20,20,0,10000])
plt.ion()
plt.show()

x = np.arange(-20, 21)
for pow in range(1,5): # plot x^1, x^2, ..., x^4
y = [Xi**pow for Xi in x]
plt.plot(x, y)
plt.draw()
plt.pause(0.001)
input("Press [enter] to continue.")

if __name__ == '__main__':
main()

Works fine,although it gives this warning

 MatplotlibDeprecationWarning: Using default event loop until function specific to this GUI is implemented
warnings.warn(str, mplDeprecation)

I am not sure if that is related to 3.6 version or not.

matplotlib.plot.show continues shows plots even the script finished

The plots will close when the script is finished, even if you use block=False

Similar to the answer here, when running from the terminal you have to call plt.show at the end of the script to keep these plots open after you've finished. I made a similar code to yours which works as intended; the plot simply refreshes at the end of the code. (added a for loop for a 5 second delay just so you can see it's running).

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

if __name__ == '__main__':
plt.figure(figsize=(10, 10))
plt.plot(range(5), lw=2, label='Real')
plt.title('Prediction')
plt.legend(loc="best")
plt.show(block=False)
print("---Plot graph finish---")
for i in range(5):
print('waiting...{}'.format(i))
time.sleep(1)
print('code is done')
plt.show()

Plotting in a non-blocking way with Matplotlib

I spent a long time looking for solutions, and found this answer.

It looks like, in order to get what you (and I) want, you need the combination of plt.ion(), plt.show() (not with block=False) and, most importantly, plt.pause(.001) (or whatever time you want). The pause is needed because the GUI events happen while the main code is sleeping, including drawing. It's possible that this is implemented by picking up time from a sleeping thread, so maybe IDEs mess with that—I don't know.

Here's an implementation that works for me on python 3.5:

import numpy as np
from matplotlib import pyplot as plt

def main():
plt.axis([-50,50,0,10000])
plt.ion()
plt.show()

x = np.arange(-50, 51)
for pow in range(1,5): # plot x^1, x^2, ..., x^4
y = [Xi**pow for Xi in x]
plt.plot(x, y)
plt.draw()
plt.pause(0.001)
input("Press [enter] to continue.")

if __name__ == '__main__':
main()

Matplotlib: Is there a way so that we can see and work with plot result while the routines after the `.show()` is being processed by Python?

You could use a combination of a separate python process (via multiprocessing) and the blocking behaviour of plt.show() to achieve the desired result:

import matplotlib.pyplot as plt
import time
from multiprocessing import Process

def show_plot(data):
fig, ax = plt.subplots()
ax.plot(data)
plt.show()

def do_calculation():
for i in range(100):
print(i)
time.sleep(0.1)

if __name__ == '__main__':
p = Process(target=show_plot, args=([1,2,3],))
p.start() # start parallel plotting process
do_calculation()
p.join() # the process will terminate once the plot has been closed

Plotting matplotlib plots in pyscript when a button is clicked

To make it work you should have a button listener defined to ensure the call back. To do that use create_proxy function and then process the button.
It looks like this:

1. index.html

I moved your python code to main.py file to have html more readable. Added - paths: in py-env node and src="./main.py" in py-script node. There are some stylings for the button and a hidden div that I use if there are more buttons to control all of them with the same button processing function.

<html>

<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>

<py-env>
- matplotlib
- paths:
./main.py
</py-env>

<style>
.button:hover {
background: #d9d9d9;
}
.button {
text-decoration: none;
display: inline;
padding: 5px;
width: 120px;
font-size: 1em;
background: #afc7b5;
color: darkgreen;
font-weight: bold;
}
</style>
</head>

<body>
<h1>Matplotlib</h1>
<div hidden id="evtMsg">0</div>

<py-script> print("My Lineplot")</py-script>
<div id="lineplot"></div>
<button id="plot-button" onClick="document.getElementById('evtMsg').innerHTML=100" class="button" style="">Plot</button>

<py-script src="./main.py"></py-script>
</body>

</html>

2. main.py

Your code is here along with imports and listener definition and button processing code. I changed the name of your function to plot_it and declared it asynchronous.

import matplotlib.pyplot as plt

import asyncio
from pyodide import create_proxy

def Setup_Button_Listeners():
btnList = document.querySelectorAll(".button")
for i in range(len(btnList)):
e = document.getElementById(btnList[i].id)
btn_event = create_proxy(Process_Button)
e.addEventListener("click", btn_event)
#
#
async def Process_Button(event):
if document.getElementById("evtMsg").innerHTML == '100': # button plot_it
fig = await plot_it()
pyscript.write('lineplot', fig)

async def plot_it(*args, **kwargs):
fig, ax = plt.subplots()
year_1 = [2016, 2017, 2018, 2019, 2020, 2021]
population_1 = [42, 43, 45, 47, 48, 50]
year_2 = [2016, 2017, 2018, 2019, 2020, 2021]
population_2 = [43, 43, 44, 44, 45, 45]
plt.plot(year_1, population_1, marker='o', linestyle='--', color='g', label='Country 1')
plt.plot(year_2, population_2, marker='d', linestyle='-', color='r', label='Country 2')
plt.xlabel('Year')
plt.ylabel('Population (M)')
plt.title('Year vs Population')
plt.legend(loc='lower right')
fig
return fig

Setup_Button_Listeners()

This way, when page loaded, the button listener will be established waiting for you to click it. When you do - the proxy will call Process_Button function where you can do whatever you want to do with that button clicked. Regards...

Multiple mouse pick event Error on thinker Matplotlib Canvas plots

event doesn't care what you clicked. It runs both functions when you click object on canvas.

And different objects have different functions and you should check instance of event.artist to run correct code for clicked object.

def onpick(event):
print('onpick', event.artist)

if isinstance(event.artist, matplotlib.lines.Line2D):
thisline = event.artist
xdata = thisline.get_xdata()
ydata = thisline.get_ydata()
ind = event.ind
print(xdata[ind][0])
self.points = []
self.points.append([xdata[ind][0], ydata[ind][0]])
print('this point click on polygon', self.points)

def onpick2(event):
print('onpick2', event.artist)

if isinstance(event.artist, matplotlib.collections.PathCollection):
index = event.ind
xy = event.artist.get_offsets()
print ('hi scatter', xy[index])

You can even put all in one function.

def onpick(event):
print('onpick', event.artist)

if isinstance(event.artist, matplotlib.lines.Line2D):
thisline = event.artist
xdata = thisline.get_xdata()
ydata = thisline.get_ydata()
ind = event.ind
print(xdata[ind][0])
self.points = []
self.points.append([xdata[ind][0], ydata[ind][0]])
print('this point click on polygon', self.points)

if isinstance(event.artist, matplotlib.collections.PathCollection):
index = event.ind
xy = event.artist.get_offsets()
print ('hi scatter', xy[index])


Related Topics



Leave a reply



Submit