0

I am trying to add two rectangular selectors (docs) to my plots:

Currently, I create the Area and Background classes (both containing the rectangular selectors) upon pressing a keyboard key. Upon pressing another key I create two more classes that fetch the attributes from the Area and Background classes.

import numpy as np
import matplotlib.pyplot as plt


from matplotlib.widgets import RectangleSelector

class AreaSelector():
    """ lets the user select an area"""
    def __init__(self, fig, ax, im):
        self.x1                         = None
        self.y1                         = None
        self.x2                         = None
        self.y2                         = None

        self.observers                  = []

        toggle_selector.RS              = RectangleSelector(ax, self.line_select_callback,
                                                            drawtype='box', useblit=True,
                                                            button=[1],  # don't use middle button
                                                            minspanx=1, minspany=1,
                                                            spancoords='pixels',
                                                            interactive=True,
                                                            rectprops=dict(facecolor='green',edgecolor='black', alpha=0.3))

    def line_select_callback(self, eclick, erelease):
        """ gets the positions and sends them to the observer"""
        'eclick and erelease are the press and release events'
        self.x1, self.y1 = eclick.xdata, eclick.ydata
        self.x2, self.y2 = erelease.xdata, erelease.ydata
        self.set_new_position(self.x1, self.y1, self.x2, self.y2)

    def set_new_position(self, x1, y1, x2, y2):
        """ informs the observer about the new positions"""
        self.x1                     = x1
        self.y1                     = y1
        self.x2                     = x2
        self.y2                     = y2 
        for callback in self.observers:
            callback(self.x1, self.y1, self.x2, self.y2)

    def bind_to(self, callback):
        """ binds the observer"""
        self.observers.append(callback)

class ExtractSpectra():
    def __init__(self, cube, area_selector):
        """ class that will do fancy things with the selected area"""
        self.cube                   = cube
        self.x1_area                = None
        self.y1_area                = None
        self.x2_area                = None
        self.y2_area                = None
        self.sum_area               = None
        self.sum_background         = None

        self.area_selector          = area_selector

        self.area_selector.bind_to(self.update_position_area)

    def update_position_area(self, x1, y1, x2, y2):
        """ updates position upon changes in Area Selector"""
        self.x1_area                = x1
        self.y1_area                = y1
        self.x2_area                = x2
        self.y2_area                = y2

    def get_spectra(self): 
        """ some math on the selected area"""
        if self.x1_area != None:
            self.sum_area           = np.sum(self.cube[np.round(self.y1_area,0).astype(int) : np.round(self.y2_area,0).astype(int),
                                                       np.round(self.x1_area,0).astype(int) : np.round(self.x2_area,0).astype(int)])
            print(self.sum_area)


        else:
            print("Area not selected!")


class BackgroundSelector():
    """ lets the user select an area, same structur as other selector"""
    def __init__(self, fig, ax, im):
        self.x1                     = None
        self.y1                     = None
        self.x2                     = None
        self.y2                     = None

        self.observers              = []

        toggle_selector.RS          = RectangleSelector(ax, self.line_select_callback,
                                                            drawtype='box', useblit=True,
                                                            button=[1],  # don't use middle button
                                                            minspanx=1, minspany=1,
                                                            spancoords='pixels',
                                                            interactive=True,
                                                            rectprops=dict(facecolor='red',edgecolor='black', alpha=0.3))        

    def line_select_callback(self, eclick, erelease):
        'eclick and erelease are the press and release events'
        self.x1, self.y1            = eclick.xdata,   eclick.ydata
        self.x2, self.y2            = erelease.xdata, erelease.ydata
        self.set_new_position(self.x1, self.y1, self.x2, self.y2)

    def set_new_position(self, x1, y1, x2, y2):
        self.x1                     = x1
        self.y1                     = y1
        self.x2                     = x2
        self.y2                     = y2 
        for callback in self.observers:
            callback(self.x1, self.y1, self.x2, self.y2)

    def bind_to(self, callback):
        self.observers.append(callback)

class ExtractBackground():
    def __init__(self, cube, background_selector):
        self.cube                   = cube
        self.x1_background          = None
        self.y1_background          = None
        self.x2_background          = None
        self.y2_background          = None
        self.sum_background         = None

        self.background_selector    = background_selector

        self.background_selector.bind_to(self.update_position_background)


    def update_position_background(self, x1, y1, x2, y2):
        self.x1_background          = x1
        self.y1_background          = y1
        self.x2_background          = x2
        self.y2_background          = y2


    def get_background(self):
        if self.x1_background != None:
            self.sum_background     = np.sum(self.cube[np.round(self.y1_background,0).astype(int) : np.round(self.y2_background,0).astype(int),
                                                       np.round(self.x1_background,0).astype(int) : np.round(self.x2_background,0).astype(int)])
            print(self.sum_background)            
        else:
            print("Background not selected!")







def sample_plot():
    fig                             = plt.figure()
    ax                              = plt.subplot()

    im                              = np.ones((100,100))*-1
    im[10:20,10:40]                 = 1.

    img                             = ax.imshow(im, origin='lower', cmap='inferno')

    return(fig, ax, img, im)





if __name__ == "__main__":



    def toggle_selector( event):
        print(' Key pressed.')        

        if event.key in ['E', 'e']:
            area_selector                   = AreaSelector(fig, ax, im)
            extractor_area                  = ExtractSpectra(im, area_selector)
            global extractor_area
            print('e')

        if event.key in ['C', 'c']:
            background_selector             = BackgroundSelector(fig, ax, im)
            extractor_background            = ExtractBackground(im, background_selector)
            global extractor_background
            print('c')

        if event.key in ['Y', 'y']:

            try: extractor_area.get_spectra()
            except: print('extrator area not designated')
            try: extractor_background.get_background()
            except: print('background area not designated')
            print('y')


    fig, ax, img, im                = sample_plot()


    plt.connect('key_press_event', toggle_selector)
    plt.show()

This essentially works fine (even though I am not really happy with the way I've written this [why 4 classes?, use of globals, ect.], but the AreaSelector creates a weird pattern on the screen when moved around (Solitaire players will recognize it!)

enter image description here

What's happening? And if you're in the mood, how could I do that better? I'm trying to up my programming game!

Thanks!

Sebastiano1991
  • 867
  • 1
  • 10
  • 26
  • Without looking deep inside the code, I can say the following: You are not creating 4 RectangleSelectors but `N` of them where `N` is the number of times you press the key. Is that really desired? – ImportanceOfBeingErnest Jul 04 '18 at 14:02
  • I think every time I press the right key the old Rectangle Selector object the reference to the old selector is overwritten, right? In case this happens I'm essentially OK with it (GC, kills them?) – Sebastiano1991 Jul 04 '18 at 14:08

0 Answers0