Matplotlib Updating slider widget range
Question: I am trying to write a small bit of code that interactively deletes selected
slices in an image series using matplotlib. I have created a button 'delete'
which stores a number of indices to be deleted when the button 'update' is
selected. However, I am currently unable to reset the range of my slider
widget, i.e. removing the number of deleted slices from valmax. What is the
pythonic solution to this problem?

Here is my code:

    
    
    import dicom
    import numpy as np
    import matplotlib.pyplot as plt
    from matplotlib.widgets import Slider, Button
    
    frame = 0
    #store indices of slices to be deleted
    delete_list = []
    
    
    def main():
    
        data = np.random.rand(16,256,256)
        nframes = data.shape[0]
    
        raw_dicom_stack = []
        for x in range (nframes):
            raw_dicom_stack.append(data[x,:,:])
    
        #yframe = 0
    
        # Visualize it
        viewer = VolumeViewer(raw_dicom_stack, nframes)
        viewer.show()
    
    class VolumeViewer(object):
        def __init__(self, raw_dicom_stack, nframes):
    
            global delete_list
    
            self.raw_dicom_stack = raw_dicom_stack
            self.nframes = nframes
            self.delete_list = delete_list
    
            # Setup the axes.
            self.fig, self.ax = plt.subplots()
            self.slider_ax = self.fig.add_axes([0.2, 0.03, 0.65, 0.03])
            self.delete_ax = self.fig.add_axes([0.85,0.84,0.1,0.04])
            self.update_ax = self.fig.add_axes([0.85,0.78,0.1,0.04])
            self.register_ax = self.fig.add_axes([0.85,0.72,0.1,0.04])
            self.add_ax = self.fig.add_axes([0.85,0.66,0.1,0.04])
    
            # Make the slider
            self.slider = Slider(self.slider_ax, 'Frame', 1, self.nframes, 
                                valinit=1, valfmt='%1d/{}'.format(self.nframes))
            self.slider.on_changed(self.update)
    
            #Make the buttons
            self.del_button = Button(self.delete_ax, 'Delete')
            self.del_button.on_clicked(self.delete)
    
            self.upd_button = Button(self.update_ax, 'Update')
            self.upd_button.on_clicked(self.img_update)
    
            self.reg_button = Button(self.register_ax, 'Register')
    
            self.add_button = Button(self.add_ax, "Add")
    
            # Plot the first slice of the image
            self.im = self.ax.imshow(np.array(raw_dicom_stack[0]))
    
        def update(self, value):
            global frame
            frame = int(np.round(value - 1))
    
            # Update the image data
            dat = np.array(self.raw_dicom_stack[frame])
            self.im.set_data(dat)
    
            # Reset the image scaling bounds (this may not be necessary for you)
            self.im.set_clim([dat.min(), dat.max()])
    
            # Redraw the plot
            self.fig.canvas.draw()
    
        def delete(self,event):
            global frame
            global delete_list
    
            delete_list.append(frame)
            print 'Frame %s has been added to list of slices to be deleted' %str(frame+1)
            print 'Please click update to delete these slices and show updated image series \n'
    
            #Remove duplicates from delete list
    
        def img_update(self,event):
            #function deletes image stacks and updates viewer
            global delete_list
    
            #Remove duplicates from list and sort into numerical order
            delete_list = list(set(delete_list))
            delete_list.sort()
    
            #Make sure delete_list is not empty
            if not delete_list:
                print "Delete list is empty, no slices to delete"
            #Loop through delete list in reverse numerical order and remove slices from series
            else:
                for i in reversed(delete_list):
                    self.raw_dicom_stack.pop(i)
                    print 'Slice %i removed from dicom series \n' %(i+1)
    
            #Can now remove contents from delete_list
            del delete_list[:]
            #Update slider range
            self.nframes =  len(self.raw_dicom_stack)
    
    
        def show(self):
            plt.show()
    
    if __name__ == '__main__':
        main()
    


Answer: It looks like the slider does not have a way to update the range
([api](http://matplotlib.org/api/widgets_api.html#matplotlib.widgets.Slider)).
I would suggest setting the range of the slider to be [0,1] and doing

    
    
    frame = int(self.nframes * value)
    

On a somewhat related note, I would have made `frame` ~~an instance variable~~
a data attribute instead of a global variable
([tutorial](http://docs.python.org/2/tutorial/classes.html#instance-objects)).

