#1
  1. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Apr 2013
    Posts
    9
    Rep Power
    0

    Matplotlib widgets: How to disconnect spanselector once selection is completed?


    I wonder if anyone could help me with this problem in the application of the matplotlib widgets:

    I have to work with the spectra of HII galaxies (these are simply a flat plots with some sudden peaks whose location is known to us). I need to "confirm" the location of these lines (you could say the thickness of the peaks on the plot) and determine the continuum level (this means the average y coordinate of the plot at the left and right regions of the peak). This is a simple task but given the number of plots and the number of lines in each plot I would like to use a short python code to simplify the process and avoid me missing some of the peaks. They look like the ones in this image (they also have set of selections I want to perform at each loop)

    https://www.dropbox.com/s/tr9fijsyae5h474/GraphExample4.png

    I have watched the lectures of John Hunter on youtube and I believe I will be more than happy with the default widgets to use as my GUI, particularly the spanselector, press event and cursor event.

    However my problem is that when I try to run several instances of the spanselector widget only the last works. As it happens in this code example:

    Code:
    import numpy as np 
    import matplotlib.pyplot as plt 
    from matplotlib.widgets import SpanSelector
    
    def onselectPeak(xmin, xmax): 
    indmin, indmax = np.searchsorted(x, (xmin, xmax)) 
    indmax = min(len(x)-1, indmax) 
    print "Xmin " + str(indmin) + " at index " + str(indmin) 
    print "Xmax " + str(indmax) + " at index " + str(indmax) 
    ax.fill_between(x[indmin:indmax], -1.0, y[indmin:indmax],facecolor='Red',alpha=0.5)
    
    def onselectLeftContinuum(xmin, xmax): 
    indmin, indmax = np.searchsorted(x, (xmin, xmax)) 
    indmax = min(len(x)-1, indmax)
    
    print "Leftmin " + str(indmin) + " at index " + str(indmin) 
    print "Leftmax " + str(indmax) + " at index " + str(indmax) 
    ax.fill_between(x[indmin:indmax], -1.0, y[indmin:indmax],facecolor='Blue',alpha=0.5)
    
    def onselectRightContinuum(xmin, xmax): 
    indmin, indmax = np.searchsorted(x, (xmin, xmax)) 
    indmax = min(len(x)-1, indmax) 
    print "Xmin " + str(indmin) + " at index " + str(indmin) 
    print "Xmax " + str(indmax) + " at index " + str(indmax) 
    ax.fill_between(x[indmin:indmax], -1.0, y[indmin:indmax],facecolor='Blue',alpha=0.5)
    
    fig = plt.figure(figsize=(8,6)) 
    ax = fig.add_subplot(111, axisbg='#FFFFCC')
    
    x = np.arange(0.0, 10.0, 1.0) 
    y = [0.0,0.0,0.0,0.0,5.0,0.0,0.0,0.0,0.0,0.0,]
    
    ax.plot(x, y, '-') 
    ax.set_ylim([-1.0,6.0])
    
    ax.set_title('Press left mouse button and drag Line region') 
    span = SpanSelector(ax, onselectPeak, 'horizontal', useblit=True, rectprops=dict(alpha=0.5, facecolor='red') )
    
    ax.set_title('Press left mouse button and drag left region of the continuum') 
    span = SpanSelector(ax, onselectLeftContinuum, 'horizontal', useblit=True, rectprops=dict(alpha=0.5, facecolor='blue') )
    
    ax.set_title('Press left mouse button and drag right region of the continuum') 
    span = SpanSelector(ax, onselectRightContinuum, 'horizontal', useblit=True, rectprops=dict(alpha=0.5, facecolor='blue') )
    
    plt.show()
    
    print "Task Completed"
    In all the examples I have found the spanselector just keeps running... I wonder which is the right way to disconnect it so you can keep working on the figure.

    Please any advice would be most welcome.
  2. #2
  3. Contributing User
    Devshed Demi-God (4500 - 4999 posts)

    Join Date
    Aug 2011
    Posts
    4,893
    Rep Power
    481
    I don't know how it's supposed to behave, nor why you need to manually select peaks (sometimes it's a good choice).

    In this version of your program I saved references to each SpanSelector object, whereas you discarded them by reassigning span. Actually, I had thought that matplotlib would keep a reference and initially removed all the
    span =
    which emasculated the functionality. Maybe matplotlib should maintain a reference, in which case the matplotlib code would need to increfcount--- Py_INCREF()---if it's in c code.

    Reassigning span allowed python to deallocate the SpanSelector. Only the last one was sure to work. I think.
    Code:
    import numpy as np
    import matplotlib.pyplot as plt
    from matplotlib.widgets import SpanSelector
    
    def onselectPeak(xmin, xmax):
        indmin, indmax = np.searchsorted(x, (xmin, xmax))
        indmax = min(len(x)-1, indmax)
        print "Xmin " + str(indmin) + " at index " + str(indmin)
        print "Xmax " + str(indmax) + " at index " + str(indmax)
        ax.fill_between(x[indmin:indmax], -1.0, y[indmin:indmax],facecolor='Red',alpha=0.5)
    
    def onselectLeftContinuum(xmin, xmax):
        indmin, indmax = np.searchsorted(x, (xmin, xmax))
        indmax = min(len(x)-1, indmax)
        print "Leftmin " + str(indmin) + " at index " + str(indmin)
        print "Leftmax " + str(indmax) + " at index " + str(indmax)
        ax.fill_between(x[indmin:indmax], -1.0, y[indmin:indmax],facecolor='Blue',alpha=0.5)
    
    def onselectRightContinuum(xmin, xmax):
        indmin, indmax = np.searchsorted(x, (xmin, xmax))
        indmax = min(len(x)-1, indmax)
        print "Xmin " + str(indmin) + " at index " + str(indmin)
        print "Xmax " + str(indmax) + " at index " + str(indmax)
        ax.fill_between(x[indmin:indmax], -1.0, y[indmin:indmax],facecolor='Blue',alpha=0.5)
    
    fig = plt.figure(figsize=(8,6))
    ax = fig.add_subplot(111, axisbg='#FFFFCC')
    
    x = np.arange(0.0, 10.0, 1.0)
    y = [0.0,0.0,0.0,0.0,5.0,0.0,0.0,0.0,0.0,0.0,]
    
    ax.plot(x, y, '-')
    ax.set_ylim([-1.0,6.0])
    
    spans = []  # don't let the garbage collector have your spans!
    
    ax.set_title('Press left mouse button and drag Line region')
    spans.append(
        SpanSelector(ax, onselectPeak, 'horizontal', useblit=True, rectprops=dict(alpha=0.5, facecolor='red') ))
    
    ax.set_title('Press left mouse button and drag left region of the continuum')
    spans.append(
        SpanSelector(ax, onselectLeftContinuum, 'horizontal', useblit=True, rectprops=dict(alpha=0.5, facecolor='blue') ))
    
    ax.set_title('Press left mouse button and drag right region of the continuum')
    spans.append(
        SpanSelector(ax, onselectRightContinuum, 'horizontal', useblit=True, rectprops=dict(alpha=0.5, facecolor='blue') ))
    
    plt.show()
    
    print "Task Completed"
    [code]Code tags[/code] are essential for python code and Makefiles!
  4. #3
  5. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Apr 2013
    Posts
    9
    Rep Power
    0
    Hello "b49P23TIvg"!

    Thank you kindly for your reply. Sorry if I did not explain the context of my request (I did not really want to bore the readers): My interest is not really the actual location but the thickness of the peak which has a physical meaning for us. By manually selecting the region of these peaks and the "ground" level at its sides I can then use python to determine the gaussian curve which best approaches the data points of a given peak. (That however I know how to do)

    Your advice to make an "spans" list is great: I can use this format to set a maximum number of elements (6) which I can sort from smaller to larger wavelengths easily to make my filled regions... many great options...

    But I still have the problem of not being able to stop the SpanSelector... in the approach you gave me actually all three span instances are running simultaneously: It can be seen in the printed messages and in the fact that the filled region is purple (from the combination of the red and the blue... this is actually quite awesome!)

    I do not want to close the plot because after I have selected the three regions, I need to check the ground level (by plotting a line whose gradient is calculated from the side regions points) and the gaussian curve... Is there a way to stop the spanselector running?

    Or this has to do with the actual plt.show() and the plot has to be disconnected so the code can continue running?

    Thanks a lot for the time
  6. #4
  7. Contributing User
    Devshed Demi-God (4500 - 4999 posts)

    Join Date
    Aug 2011
    Posts
    4,893
    Rep Power
    481
    I haven't watched the late John Hunter's lectures. Maybe someone else has a clue?

    Multi-threading or multi-processing might provide work-around if you can't otherwise figure out a solution.
    [code]Code tags[/code] are essential for python code and Makefiles!
  8. #5
  9. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Apr 2013
    Posts
    9
    Rep Power
    0
    I have been working on this problem for the last week and I have finally started to understand how it should work:

    1) Events and plt.show must be running all the time. Indeed, the latter is designed to be set implemented at the end of the code. The events call should be just before

    2) To change the data on your figures you can use a key event. This post illustrates the right way to do it and it includes a class for it (i cannot post links just look for "Using events with matplotlib in a for loop" in starkoverflow)

    3) The problem with this structure is that your code must be fragmented in different methods. You need to make sure to define your variables in the main code so they can be imported across different methods (I am still struggling with this... I will post an example code when I am done)

    One additional question: When I try to run the cursor widget with the spanselector widget the widgets glitches in a rather... painful way to the eye way... Is there anyway to avoid this? I have been playing with the cursor useblit widget but no luck.

IMN logo majestic logo threadwatch logo seochat tools logo