Skip to content

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

Closed

Description

@fabrivelas

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()

Activity

  1. added this to the milestone on Mar 10, 2016
  2. added
    API: consistencyConsistency of the matplotlib API, including naming, behavior, defaults, …
    on Mar 10, 2016
  3. tacaswell commented on Mar 10, 2016

    @tacaswell
    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()
  4. WeatherGod commented on Mar 10, 2016

    @WeatherGod
    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)
    .

  5. fabrivelas commented on Mar 10, 2016

    @fabrivelas
    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()
    
  6. WeatherGod commented on Mar 10, 2016

    @WeatherGod
    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)
    .

  7. tacaswell commented on Mar 10, 2016

    @tacaswell
    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.

  8. modified the milestones: v3.0, v3.1 on Aug 11, 2018
  9. modified the milestones: v3.1.0, v3.2.0 on Mar 18, 2019
  10. ellieLitwack commented on Jun 28, 2022

    @ellieLitwack

    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

  11. github-actions commented on Jun 30, 2023

    @github-actions

    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!

  12. QuLogic commented on Jun 30, 2023

    @QuLogic
    Member

    This was fixed by #25247 turning ContourSet into Collection.

  13. fabrivelas commented on Jun 30, 2023

    @fabrivelas
    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

Metadata

Metadata

Assignees

No one assigned

    Labels

    API: consistencyConsistency of the matplotlib API, including naming, behavior, defaults, …

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions