【问题标题】:Matplotlib Updating slider widget rangeMatplotlib 更新滑块小部件范围
【发布时间】:2012-11-21 12:30:45
【问题描述】:

我正在尝试编写一小段代码,使用 matplotlib 以交互方式删除图像系列中的选定切片。我创建了一个“删除”按钮,它存储了在选择“更新”按钮时要删除的多个索引。但是,我目前无法重置滑块小部件的范围,即从 valmax 中删除已删除切片的数量。这个问题的pythonic解决方案是什么?

这是我的代码:

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

【问题讨论】:

    标签: python widget matplotlib slider


    【解决方案1】:

    为了更新滑块范围,您可以直接设置它的最小值和最大值,

    slider.valmin = 3
    slider.valmax = 7
    

    为了反映滑块轴的这种变化,您需要设置轴的范围,

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

    一个完整的示例,输入任何数字会将滑块的valmin 更改为该值。

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

    【讨论】:

      【解决方案2】:

      滑块似乎无法更新范围 (api)。我建议将滑块的范围设置为 [0,1] 并做

      frame = int(self.nframes * value)
      

      在一些相关的注释中,我会将frame 实例变量设置为数据属性而不是全局变量 (tutorial)。

      【讨论】:

      • 感谢您的回复。我仍然在掌握 python,但很享受它。是实例变量还是局部变量。我只是在 main 函数中设置 frame = 0 吗?
      • self.frame,一个变量,其值与类的实例相关联(我有点搞砸了术语,应该说是“数据属性”)。见docs.python.org/2/tutorial/classes.html#instance-objects
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-18
      相关资源
      最近更新 更多