Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
445 views
in Technique[技术] by (71.8m points)

python - Matplotlib Updating slider widget range

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 
'

        #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 
' %(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()
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

In order to update a slider range you may set the min and max value of it directly,

slider.valmin = 3
slider.valmax = 7

In order to reflect this change in the slider axes you need to set the limits of the axes,

slider.ax.set_xlim(slider.valmin,slider.valmax)

A complete example, where typing in any digit changes the valmin of the slider to that value.

import matplotlib.pyplot as plt
import matplotlib.widgets

fig, (ax,sliderax) = plt.subplots(nrows=2,gridspec_kw=dict(height_ratios=[1,.05]))

ax.plot(range(11))
ax.set_xlim(5,None)
ax.set_title("Type number to set minimum slider value")
def update_range(val):
    ax.set_xlim(val,None)

def update_slider(evt):
    print(evt.key)
    try:
        val = int(evt.key)
        slider.valmin = val
        slider.ax.set_xlim(slider.valmin,None)
        if val > slider.val:
            slider.val=val
            update_range(val)
        fig.canvas.draw_idle()
    except:
        pass

slider=matplotlib.widgets.Slider(sliderax,"xlim",0,10,5)
slider.on_changed(update_range)

fig.canvas.mpl_connect('key_press_event', update_slider)

plt.show()

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...