Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

'QuadContourSet' object has no attribute 'set_visible' or 'set_animated' #6139

Closed
fabrivelas opened this issue Mar 10, 2016 · 9 comments
Closed

Comments

@fabrivelas
Copy link

matplotlib/1.5.1
python3/3.5.1
osx 10.11.3
Installation using homebrew.

The following code gives me an error 'QuadContourSet' object has no attribute 'set_visible'
I added a workaround (http://stackoverflow.com/questions/23070305/how-can-i-make-an-animation-with-contourf) and it worked for version 1.3.1 of matplotlib but with matplotlib version 1.5.1 I get the error 'QuadContourSet' object has no attribute 'set_animated'. Adding extra code similar to that for 'set_visible' makes the code work again.
I would expect the code to work without the Bug fix code snippets included.

#! coding=utf-8
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
import types

#################################################################
## Bug fix for Quad Contour set not having attribute 'set_visible'


def setvisible(self,vis):
    for c in self.collections: c.set_visible(vis)
def setanimated(self,ani):
    for c in self.collections: c.set_animated(ani)
####################################################################

fig = plt.figure()

# Some 2D geo arrays to plot (time,lat,lon)
data = np.random.random_sample((20,90,360))
lat = range(len(data[0,:,0]))
lon = range(len(data[0,0,:]))
lons,lats = np.meshgrid(lon,lat)

# ims is a list of lists, each row is a list of artists to draw in the
# current frame; here we are animating three artists, the image and 2 
# annotatons (title), in each frame
ims = []
for i in range(len(data[:,0,0])):
    t_step = int(i)
    im = plt.contourf(lons,lats,data[i,:,:])
    text = 'title=' + str(i)
    te = plt.text(90,90,str(text))
    an = plt.annotate(str(text), xy=(0.45, 1.05), xycoords='axes fraction')
    #################################################################
    ## Bug fix for Quad Contour set not having the attributes
    ## 'set_visible' and 'set_animated'
    # ** uncomment the following 2 lines to make the code work:**
#   im.set_visible = types.MethodType(setvisible,im)
#   im.set_animated = types.MethodType(setanimated,im)
    im.axes = plt.gca()
    im.figure = fig
    ####################################################################
    ims.append([im,te,an])

#ani = animation.ArtistAnimation(fig, ims, interval=70,repeat_delay=1000, blit=False)
ani = animation.ArtistAnimation(fig, ims)
## For saving the animation we need ffmpeg binary:
#FFwriter = animation.FFMpegWriter()
#ani.save('basic_animation.mp4', writer = FFwriter)
plt.show()
@tacaswell tacaswell added this to the 3.0 (future) milestone Mar 10, 2016
@tacaswell
Copy link
Member

Because (surprisingly) ContourSets are not Artists. The actual artists are in the collection attribute as you discovered.

I am 👎 on including this type of fix upstream, this should probably be fixed by promoting ContourSets to being Artists (which means they get all the expected methods, end up in the draw tree etc) which is a pretty major refactor job.

A simpler fix for your case would be

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

fig, ax = plt.subplots()

# Some 2D geo arrays to plot (time,lat,lon)
data = np.random.random_sample((20,90,360))
lat = np.arange(len(data[0,:,0]))
lon = np.arange(len(data[0,0,:]))
lons,lats = np.meshgrid(lon,lat)

mode = 'imshow'
# ims is a list of lists, each row is a list of artists to draw in the
# current frame; here we are animating three artists, the contour and 2 
# annotatons (title), in each frame
ims = []
for i in range(len(data[:,0,0])):
    if mode == 'contour':
        im = ax.contourf(lons,lats,data[i,:,:])
        add_arts = im.collections
    elif mode == 'imshow':
        im = ax.imshow(data[i, :, :], extent=[np.min(lons), np.max(lons),
                                               np.min(lats), np.max(lats)],
                       aspect='auto')
        add_arts = [im, ]

    text = 'title={0!r}'.format(i)
    te = ax.text(90, 90, text)
    an = ax.annotate(text, xy=(0.45, 1.05), xycoords='axes fraction')
    ims.append(add_arts + [te,an])

#ani = animation.ArtistAnimation(fig, ims, interval=70,repeat_delay=1000, blit=False)
ani = animation.ArtistAnimation(fig, ims)
## For saving the animation we need ffmpeg binary:
#FFwriter = animation.FFMpegWriter()
#ani.save('basic_animation.mp4', writer = FFwriter)
plt.show()

@WeatherGod
Copy link
Member

Could you check to see if just simply passing "corner_mask='legacy'" to the
contourf() call works. This might be a regression.

On Thu, Mar 10, 2016 at 8:37 AM, Thomas A Caswell notifications@github.com
wrote:

Because (surprisingly) ContourSets are not Artists. The actual artists
are in the collection attribute as you discovered.

I am [image: 👎] on including this type of fix upstream, this should
probably be fixed by promoting ContourSets to being Artists (which means
they get all the expected methods, end up in the draw tree etc) which is a
pretty major refactor job.

A simpler fix for your case would be

from matplotlib import animationimport numpy as npimport matplotlib.pyplot as plt

fig, ax = plt.subplots()

Some 2D geo arrays to plot (time,lat,lon)

data = np.random.random_sample((20,90,360))
lat = np.arange(len(data[0,:,0]))
lon = np.arange(len(data[0,0,:]))
lons,lats = np.meshgrid(lon,lat)

mode = 'imshow'# ims is a list of lists, each row is a list of artists to draw in the# current frame; here we are animating three artists, the contour and 2 # annotatons (title), in each frame
ims = []for i in range(len(data[:,0,0])):
if mode == 'contour':
im = ax.contourf(lons,lats,data[i,:,:])
add_arts = im.collections
elif mode == 'imshow':
im = ax.imshow(data[i, :, :], extent=[np.min(lons), np.max(lons),
np.min(lats), np.max(lats)],
aspect='auto')
add_arts = [im, ]

text = 'title={0!r}'.format(i)
te = ax.text(90, 90, text)
an = ax.annotate(text, xy=(0.45, 1.05), xycoords='axes fraction')
ims.append(add_arts + [te,an])

#ani = animation.ArtistAnimation(fig, ims, interval=70,repeat_delay=1000, blit=False)
ani = animation.ArtistAnimation(fig, ims)## For saving the animation we need ffmpeg binary:#FFwriter = animation.FFMpegWriter()#ani.save('basic_animation.mp4', writer = FFwriter)
plt.show()


Reply to this email directly or view it on GitHub
#6139 (comment)
.

@fabrivelas
Copy link
Author

@WeatherGod "corner_mask='legacy' did not work. It also gave me a warning to use corner_mask=False or True instead.

@tacaswell thanks for your improvement. The imshow method does not seem to work, however, if I want to use Basemap (hence the lat and lon variables ;-) ). I guess I then just use m.contourf instead of ax.contourf:

from matplotlib import animation
from matplotlib import pyplot as plt
import numpy as np
from mpl_toolkits.basemap import Basemap


fig, ax = plt.subplots()

# set up map projection
m = Basemap(projection='nsper',lon_0=-0,lat_0=90)
m.drawcoastlines()
m.drawparallels(np.arange(0.,180.,30.))
m.drawmeridians(np.arange(0.,360.,60.))

# some 2D geo arrays to plot (time,lat,lon)
data = np.random.random_sample((20,90,360))
lat = np.arange(len(data[0,:,0]))
lon = np.arange(len(data[0,0,:]))
lons,lats = np.meshgrid(lon,lat)

# ims is a list of lists, each row is a list of artists to draw in the
# current frame; here we are animating three artists, the contour and 2 
# annotatons (title), in each frame
ims = []
for i in range(len(data[:,0,0])):
    im = m.contourf(lons,lats,data[i,:,:],latlon=True)
    add_arts = im.collections
    text = 'title={0!r}'.format(i)
    te = ax.text(90, 90, text)
    an = ax.annotate(text, xy=(0.45, 1.05), xycoords='axes fraction')
    ims.append(add_arts + [te,an])

ani = animation.ArtistAnimation(fig, ims)
# For saving the animation we need ffmpeg binary:
# FFwriter = animation.FFMpegWriter()
# ani.save('basic_animation.mp4', writer = FFwriter)
plt.show()

@WeatherGod
Copy link
Member

@fabrivelas, yeah, the warning message is because the "legacy" mode is
deprecated, but it forces the use of the old codepaths. I am not sure how
this used to work in 1.3.1 if the legacy flag doesn't work either.

On Thu, Mar 10, 2016 at 10:01 AM, fabrivelas notifications@github.com
wrote:

@WeatherGod https://github.com/WeatherGod "corner_mask='legacy' did not
work. It also gave me a warning to use corner_mask=False or True instead.

@tacaswell https://github.com/tacaswell thanks for your improvement.
The imshow method does not seem to work, however, if I want to use Basemap
(hence the lat and lon variables ;-) ). I guess I then just use m.contourf
instead of ax.contourf:

from matplotlib import animation
from matplotlib import pyplot as plt
import numpy as np
from mpl_toolkits.basemap import Basemap

fig, ax = plt.subplots()

set up map projection

m = Basemap(projection='nsper',lon_0=-0,lat_0=90)
m.drawcoastlines()
m.drawparallels(np.arange(0.,180.,30.))
m.drawmeridians(np.arange(0.,360.,60.))

some 2D geo arrays to plot (time,lat,lon)

data = np.random.random_sample((20,90,360))
lat = np.arange(len(data[0,:,0]))
lon = np.arange(len(data[0,0,:]))
lons,lats = np.meshgrid(lon,lat)

ims is a list of lists, each row is a list of artists to draw in the

current frame; here we are animating three artists, the contour and 2

annotatons (title), in each frame

ims = []
for i in range(len(data[:,0,0])):
im = m.contourf(lons,lats,data[i,:,:],latlon=True)
add_arts = im.collections
text = 'title={0!r}'.format(i)
te = ax.text(90, 90, text)
an = ax.annotate(text, xy=(0.45, 1.05), xycoords='axes fraction')
ims.append(add_arts + [te,an])

ani = animation.ArtistAnimation(fig, ims)

For saving the animation we need ffmpeg binary:

FFwriter = animation.FFMpegWriter()

ani.save('basic_animation.mp4', writer = FFwriter)

plt.show()


Reply to this email directly or view it on GitHub
#6139 (comment)
.

@tacaswell
Copy link
Member

@WeatherGod The change is that with the auto-draw changes animation became much stricter about making sure it's artists were actually in 'animated' mode. In 1.3.1 the OP only had to monkey-patch set_visible, in 1.5 + monkey patching set_animated is also needed.

In either case, it was abuse of the ArtistAnimation API in that non-artists were in the list and an inconsistency in that contourf does not retrun an Artist.

@tacaswell tacaswell modified the milestones: v3.0, v3.1 Aug 11, 2018
@tacaswell tacaswell modified the milestones: v3.1.0, v3.2.0 Mar 18, 2019
stfnwong added a commit to stfnwong/pymllib that referenced this issue Aug 19, 2019
@tacaswell tacaswell modified the milestones: v3.2.0, needs sorting Sep 10, 2019
@ellieLitwack
Copy link

Just want to add that this issue breaks using interactive functions to place clabels as shown in this matplotlib.org tutorial: https://matplotlib.org/stable/gallery/event_handling/ginput_manual_clabel_sgskip.html

@github-actions
Copy link

This issue has been marked "inactive" because it has been 365 days since the last comment. If this issue is still present in recent Matplotlib releases, or the feature request is still wanted, please leave a comment and this label will be removed. If there are no updates in another 30 days, this issue will be automatically closed, but you are free to re-open or create a new issue if needed. We value issue reports, and this procedure is meant to help us resurface and prioritize issues that have not been addressed yet, not make them disappear. Thanks for your help!

@github-actions github-actions bot added the status: inactive Marked by the “Stale” Github Action label Jun 30, 2023
@QuLogic
Copy link
Member

QuLogic commented Jun 30, 2023

This was fixed by #25247 turning ContourSet into Collection.

@QuLogic QuLogic closed this as completed Jun 30, 2023
@QuLogic QuLogic removed the status: inactive Marked by the “Stale” Github Action label Jun 30, 2023
@QuLogic QuLogic modified the milestones: future releases, v3.8.0 Jun 30, 2023
@fabrivelas
Copy link
Author

For reference the code from the original message from 2016 above has been working when using the workaround by adding the artists suggested by @tacaswell add_arts = im.collections but not otherwise, which is probably intended. :-)

#! coding=utf-8
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np

fig = plt.figure()

# Some 2D geo arrays to plot (time,lat,lon)
data = np.random.random_sample((20,90,360))
lat = range(len(data[0,:,0]))
lon = range(len(data[0,0,:]))
lons,lats = np.meshgrid(lon,lat)

# ims is a list of lists, each row is a list of artists to draw in the
# current frame; here we are animating three artists, the image and 2 
# annotatons (title), in each frame
ims = []
for i in range(len(data[:,0,0])):
    t_step = int(i)
    im = plt.contourf(lons,lats,data[i,:,:])
    add_arts = im.collections
    text = 'title=' + str(i)
    te = plt.text(90,90,str(text))
    an = plt.annotate(str(text), xy=(0.45, 1.05), xycoords='axes fraction')
    ims.append(add_arts + [te,an])

#ani = animation.ArtistAnimation(fig, ims, interval=70,repeat_delay=1000, blit=False)
ani = animation.ArtistAnimation(fig, ims)
## For saving the animation we need ffmpeg binary:
#FFwriter = animation.FFMpegWriter()
#ani.save('basic_animation.mp4', writer = FFwriter)
plt.show()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants