Ejemplo n.º 1
0
class MplInteraction(object):

    def __init__(self, figure):
        """Initializer
        :param Figure figure: The matplotlib figure to attach the behavior to.
        :param Canvas FigureCanvas: The matplotlib PyQT canvas to allow focus after declaration
        """
        self._fig_ref = weakref.ref(figure)
        self.canvas = FigureCanvas(figure)
        self._cids_zoom = []
        self._cids_pan = []
        self._cids_rectangle = []
        self._cids_callback_zoom = {}
        self._cids_callback_pan = {}
        self._cids_callback_rectangle = {}

        self.rectangleStats = rectangle_selection(0,0,0,0)

        self._rectangle_selector = None
        self._callback_rectangle = None

        self._cids = []

    def __del__(self):
        self.disconnect()

    def _add_connection(self, event_name, callback):
        """Called to add a connection to an event of the figure
        :param str event_name: The matplotlib event name to connect to.
        :param callback: The callback to register to this event.
        """
        cid = self.canvas.mpl_connect(event_name, callback)
        self._cids.append(cid)

    def _add_rectangle_callback(self, callback):
        """
        Because the callback method can only be created when the Zoom event is
        created and the axis can only be known after the creation if it, this
        method allow to assign the callback before the creation fo the
        rectangle selector object
        """
        self._callback_rectangle = callback

    def _add_connection_rectangle(self, event_name, callback):
        """Called to add a connection of type rectangle release to an event of the figure
        :param str event_name: The matplotlib event name to connect to.
        :param callback: The callback to register to this event.
        """
        self._cids_callback_rectangle[event_name] = callback

    def _add_connection_zoom(self, event_name, callback):
        """Called to add a connection of type zoom to an event of the figure
        :param str event_name: The matplotlib event name to connect to.
        :param callback: The callback to register to this event.
        """

        #cid = self.canvas.mpl_connect(event_name, callback)
        #self._cids_zoom.append(cid)
        self._cids_callback_zoom[event_name] = callback

    def _add_connection_pan(self, event_name, callback):
        """Called to add a connection of type pan to an event of the figure
        :param str event_name: The matplotlib event name to connect to.
        :param callback: The callback to register to this event.
        """
        #cid = self.canvas.mpl_connect(event_name, callback)
        #self._cids_pan.append(cid)
        self._cids_callback_pan[event_name] = callback

    def disconnect_rectangle(self):
        if self._fig_ref is not None:
            figure = self._fig_ref()
            if figure is not None:
                for cid in self._cids_rectangle:
                    figure.canvas.mpl_disconnect(cid)
            self._disable_rectangle()
            self._cids_rectangle.clear()

    def disconnect_zoom(self):
        """
        Disconnect all zoom events and disable the rectangle selector
        """
        if self._fig_ref is not None:
            figure = self._fig_ref()
            if figure is not None:
                for cid in self._cids_zoom:
                    figure.canvas.mpl_disconnect(cid)
            self._cids_zoom.clear()

    def disconnect_pan(self):
        """
        Disconnect all pan events
        """
        if self._fig_ref is not None:
            figure = self._fig_ref()
            if figure is not None:
                for cid in self._cids_pan:
                    figure.canvas.mpl_disconnect(cid)
            self._cids_pan.clear()

    def _disable_rectangle(self):
        self._rectangle_selector.set_visible(False)
        self._rectangle_selector.set_active(False)

    def disconnect(self):
        """Disconnect interaction from Figure."""
        if self._fig_ref is not None:
            figure = self._fig_ref()
            if figure is not None:
                for cid in self._cids:
                    figure.canvas.mpl_disconnect(cid)
            self._fig_ref = None

    def _enable_rectangle(self):
            if not self._rectangle_selector.active:
                self._rectangle_selector.set_active(True)

            if not self._rectangle_selector.visible:
                self._rectangle_selector.set_visible(True)

    def connect_rectangle(self):
        for event_name, callback in self._cids_callback_rectangle.items():
            cid = self.canvas.mpl_connect(event_name, callback)
            self._cids_rectangle.append(cid)
        self._enable_rectangle()

    def connect_zoom(self):
        """
        Assign all callback zoom events to the mpl
        """
        for event_name, callback in self._cids_callback_zoom.items():
            cid = self.canvas.mpl_connect(event_name, callback)
            self._cids_zoom.append(cid)

        self._enable_rectangle()

    def connect_pan(self):
        """
        Assign all callback pan events to the mpl
        """
        for event_name, callback in self._cids_callback_pan.items():
            cid = self.canvas.mpl_connect(event_name, callback)
            self._cids_pan.append(cid)

    @property
    def figure(self):
        """The Figure this interaction is connected to or
        None if not connected."""
        return self._fig_ref() if self._fig_ref is not None else None

    def _axes_to_update(self, event):
        """Returns two sets of Axes to update according to event.
        :param MouseEvent event: Matplotlib event to consider
        :return: Axes for which to update xlimits and ylimits
        :rtype: 2-tuple of set (xaxes, yaxes)
        """
        x_axes, y_axes = set(), set()

        for ax in self.figure.axes:
            if ax.contains(event)[0]:
                x_axes.add(ax)
                y_axes.add(ax)

        return x_axes, y_axes

    def create_rectangle_ax(self, ax):
        rectprops = dict(facecolor = 'red', edgecolor = 'black', alpha = 0.1,
                         fill = True, linewidth = 1, linestyle= '-')

        self._rectangle_selector = RectangleSelector(ax, self._callback_rectangle,
                                    drawtype= 'box', useblit= True, rectprops= rectprops,
                                    button= [1,3], minspanx = 5, minspany=5,
                                    spancoords='pixels', interactive=True)
        self._disable_rectangle()

    def update_rectangle(self, coord1_1, coord1_2, coord2_1, coord2_2):
        """
        Set coordinates of the rectangle to be drawed, and send message to
        draw the new spectrum
        :param int coord1_1: initial x value of the coordinate
        :param int coord1_2: initial y value of the coordinate
        :param int coord2_1: end x value of the coordinate
        :param int coord2_2: end y value of the coordinate
        """
        #self.disconnect_zoom()
        #self.disconnect_pan()
        self.connect_rectangle()
        self.figure.canvas.clearFocus()
        self._rectangle_selector.extents = (coord1_1, coord2_1, coord1_2, coord2_2)

        self.rectangleStats.ix = coord1_1
        self.rectangleStats.iy = coord1_2
        self.rectangleStats.ex = coord2_1
        self.rectangleStats.ey = coord2_2

        pub.sendMessage('rectangleSelected', ix = coord1_1, iy =coord1_2
                        , ex = coord2_1, ey= coord2_2)
        #self.disconnect_rectangle()

    def redraw_rectangle_without_interaction_tool(self):
        """
        Redraw the rectangle on the axe that does not require to activate tools(pan or zoom)
        after it and to emit the signal named rectangleSelected
        """

        if self.rectangleStats.ix != 0:
            self.connect_rectangle()
            self._rectangle_selector.extents = (self.rectangleStats.ix, self.rectangleStats.ex,
                                                self.rectangleStats.iy, self.rectangleStats.ey)

            pub.sendMessage('rectangleSelected', ix = self.rectangleStats.ix, iy =self.rectangleStats.iy
                            , ex = self.rectangleStats.ex, ey= self.rectangleStats.ey)


    def redraw_rectangle_from_slider(self):
        """
        Redraw the rectangle on the axe and maintain the state of the active tool when
        a slice had been changed
        """

        if self.rectangleStats.ix != 0:
            self._enable_rectangle()
            self._rectangle_selector.extents = (self.rectangleStats.ix, self.rectangleStats.ex,
                                                self.rectangleStats.iy, self.rectangleStats.ey)

        pub.sendMessage('rectangleSelected', ix = self.rectangleStats.ix, iy =self.rectangleStats.iy
                            , ex = self.rectangleStats.ex, ey= self.rectangleStats.ey)

    def redraw_rectangle_with_interaction_tool(self):
        """
        Redraw the rectangle  on the axe that requiere to activate tools (pan or zoom) after
        it and to prevent the signal emission
        """

        if self.rectangleStats.ix != 0:
            self._rectangle_selector.set_visible(True)
            self._rectangle_selector.extents = (self.rectangleStats.ix, self.rectangleStats.ex,
                                                self.rectangleStats.iy, self.rectangleStats.ey)

    def _draw(self):
        """Conveninent method to redraw the figure"""
        self.canvas.draw()
Ejemplo n.º 2
0
class ROIDataAquisition(object):
    '''
    This class provides a GUI for selecting box shaped Regions Of Interest (ROIs). Each ROI is represented as a
    tuple: ((min_x,max_x),(min_y,max_y),(min_z,max_z)).
    When using the zoom/pan tool from the toolbar ROI selection is disabled. Once you click again on the zoom/pan
    button zooming/panning will be disabled and ROI selection is enabled.
    Note that when you are marking the ROI on a slice that is outside the Z-range selected by the
    range slider, once you are done selecting the ROI, you will see no change on the current slice. This is the
    correct behavior, though initially you may be surprised by it.
    '''
    def __init__(self, image, window_level=None, figure_size=(10, 8)):
        self.image = image
        self.npa, self.min_intensity, self.max_intensity = self.get_window_level_numpy_array(
            self.image, window_level)
        self.rois = []

        # ROI display settings
        self.roi_display_properties = dict(facecolor='red',
                                           edgecolor='black',
                                           alpha=0.2,
                                           fill=True)

        # Create a figure.
        self.fig, self.axes = plt.subplots(1, 1, figsize=figure_size)
        # Connect the mouse button press to the canvas (__call__ method is the invoked callback).
        self.fig.canvas.mpl_connect('button_press_event', self)
        self.roi_selector = RectangleSelector(
            self.axes,
            lambda eclick, erelease: None,
            drawtype='box',
            useblit=True,
            button=[1, 3],  # Left, right buttons only.
            minspanx=5,
            minspany=5,  # Ignore motion smaller than 5 pixels.
            spancoords='pixels',
            interactive=True,
            rectprops=self.roi_display_properties)
        self.roi_selector.set_visible(False)

        ui = self.create_ui()

        # Display the data and the controls, first time we display the image is outside the "update_display" method
        # as that method relies on the existance of a previous image which is removed from the figure.
        self.axes.imshow(self.npa[self.slice_slider.value, :, :],
                         cmap=plt.cm.Greys_r,
                         vmin=self.min_intensity,
                         vmax=self.max_intensity)
        self.update_display()
        display(ui)

    def create_ui(self):
        # Create the active UI components. Height and width are specified in 'em' units. This is
        # a html size specification, size relative to current font size.
        self.addroi_button = widgets.Button(description='Add ROI',
                                            width='7em',
                                            height='3em')
        self.addroi_button.on_click(self.add_roi)
        self.clearlast_button = widgets.Button(description='Clear Last',
                                               width='7em',
                                               height='3em')
        self.clearlast_button.on_click(self.clear_last)

        self.clearall_button = widgets.Button(description='Clear All',
                                              width='7em',
                                              height='3em')
        self.clearall_button.on_click(self.clear_all)

        self.roi_range_slider = widgets.IntRangeSlider(
            description='ROI z range:',
            min=0,
            max=self.npa.shape[0] - 1,
            step=1,
            value=[0, self.npa.shape[0] - 1],
            width='20em')

        self.slice_slider = widgets.IntSlider(description='image z slice:',
                                              min=0,
                                              max=self.npa.shape[0] - 1,
                                              step=1,
                                              value=int(
                                                  (self.npa.shape[0] - 1) / 2),
                                              width='20em')
        self.slice_slider.observe(self.on_slice_slider_value_change,
                                  names='value')

        # Layout of UI components. This is pure ugliness because we are not using a UI toolkit. Layout is done
        # using the box widget and padding so that the visible UI components are spaced nicely.
        bx0 = widgets.Box(padding=7, children=[self.slice_slider])
        bx1 = widgets.Box(padding=7, children=[self.addroi_button])
        bx2 = widgets.Box(padding=15, children=[self.clearlast_button])
        bx3 = widgets.Box(padding=15, children=[self.clearall_button])
        bx4 = widgets.Box(padding=15, children=[self.roi_range_slider])
        return widgets.HBox(children=[
            widgets.HBox(children=[bx1, bx2, bx3]),
            widgets.VBox(children=[bx0, bx4])
        ])

    def on_slice_slider_value_change(self, change):
        self.update_display()

    def get_window_level_numpy_array(self, image, window_level):
        npa = sitk.GetArrayViewFromImage(image)
        # We don't take the minimum/maximum values, just in case there are outliers (top/bottom 2%)
        if not window_level:
            min_max = np.percentile(npa.flatten(), [2, 98])
            return npa, min_max[0], min_max[1]
        else:
            return npa, window_level[1] - window_level[0] / 2.0, window_level[
                1] + window_level[0] / 2.0

    def update_display(self):
        # Draw the image and ROIs.
        # imshow adds an image to the axes, so we also remove the previous one.
        self.axes.imshow(self.npa[self.slice_slider.value, :, :],
                         cmap=plt.cm.Greys_r,
                         vmin=self.min_intensity,
                         vmax=self.max_intensity)
        self.axes.images[0].remove()
        # Iterate over all of the ROIs and only display/undisplay those that are relevant.
        for roi_data in self.rois:
            if self.slice_slider.value >= roi_data[3][
                    0] and self.slice_slider.value <= roi_data[3][1]:
                roi_data[0].set_visible(True)
            else:
                roi_data[0].set_visible(False)
        self.axes.set_title('selected {0} ROIs'.format(len(self.rois)))
        self.axes.set_axis_off()

        self.fig.canvas.draw_idle()

    def add_roi_data(self, roi_data):
        '''
        Add regions of interest to this GUI.
        Input is an iterable containing tuples where each tuple contains
        three tuples (min_x,max_x),(min_y,max_y), (min_z,max_z). The ROI
        is the box defined by these integer values and includes
        both min/max values.
        '''
        self.validate_rois(roi_data)
        for roi in roi_data:
            self.rois.append((patches.Rectangle(
                (roi[0][0], roi[1][0]), roi[0][1] - roi[0][0],
                roi[1][1] - roi[1][0],
                **self.roi_display_properties), roi[0], roi[1], roi[2]))
            self.axes.add_patch(self.rois[-1][0])
        self.update_display()

    def set_rois(self, roi_data):
        '''
        Clear any existing ROIs and set the display to the given ones.
        Input is an iterable containing tuples where each tuple contains
        three tuples (min_x,max_x),(min_y,max_y), (min_z,max_z). The ROI
        is the box defined by these integer values and includes
        both min/max values.
        '''
        self.clear_all_data()
        self.add_roi_data(roi_data)

    def validate_rois(self, roi_data):
        for roi in roi_data:
            # First element in each tuple is expected to be smaller or equal to the second element.
            if roi[0][0] > roi[0][1] or roi[1][0] > roi[1][1] or roi[2][
                    0] > roi[2][1]:
                raise ValueError(
                    'First element in each tuple is expected to be smaller than second element, error in ROI ('
                    + ', '.join(map(str, roi)) + ').')
            # Note that SimpleITK uses x-y-z specification vs. numpy's z-y-x
            if roi[0][0] >= self.npa.shape[2] or roi[0][1] < 0 or roi[1][
                    0] >= self.npa.shape[1] or roi[1][1] < 0 or roi[2][
                        0] >= self.npa.shape[0] or roi[2][0] < 0:
                raise ValueError('Given ROI (' + ', '.join(map(str, roi)) +
                                 ') is outside the image bounds.')

    def add_roi(self, button):
        if self.roi_selector.visible:
            self.roi_selector.set_visible(False)
            # Extent is in sub-pixel coordinates, we need it in pixels/voxels.
            roi_extent = [
                int(round(coord)) for coord in self.roi_selector.extents
            ]
            # We keep the patch for display and the x,y,z ranges of the ROI.
            self.rois.append((patches.Rectangle(
                (roi_extent[0], roi_extent[2]), roi_extent[1] - roi_extent[0],
                roi_extent[3] - roi_extent[2],
                **self.roi_display_properties), (roi_extent[0], roi_extent[1]),
                              (roi_extent[2],
                               roi_extent[3]), self.roi_range_slider.value))
            self.axes.add_patch(self.rois[-1][0])
            self.update_display()

    def clear_all_data(self):
        for roi_data in self.rois:
            roi_data[0].remove()
        del self.rois[:]

    def clear_all(self, button):
        self.clear_all_data()
        self.update_display()

    def clear_last(self, button):
        if self.rois:
            self.rois[-1][0].remove()
            self.rois.pop()
            self.update_display()

    def get_rois(self):
        '''
        Return a list of tuples representing the ROIs. Each tuple contains three tuples (min_x,max_x),
        (min_y,max_y), (min_z,max_z). The ROI is the box defined by these integer values and includes
        both min/max values.
        '''
        return [(roi_data[1], roi_data[2], roi_data[3])
                for roi_data in self.rois]

    def __call__(self, event):
        # This is dangerous as we are accessing a "private" variable to find the state
        # of the figure's toolbar ('ZOOM',PAN' or None). When Zoom or pan are active we will
        # ignore the button press, once the user deactivates the zoom/pan we can allow them
        # to select the ROI.
        # Discussion on stack overflow with matplotlib developer (circa 2013), no change to date:
        # http://stackoverflow.com/questions/20711148/ignore-matplotlib-cursor-widget-when-toolbar-widget-selected
        if self.fig.canvas.toolbar._active is None:
            self.roi_selector.set_visible(True)
            self.addroi_button.disabled = False
            self.update_display()
Ejemplo n.º 3
0
def gui():
    """""" """""" """""" """
     G  L  O  B  A  L  S
    """ """""" """""" """"""
    #    global dispInterf_state
    #    global stop_event

    global root, livefeed_canvas, imageplots_frame

    global slider_exposure, measurementfolder_name, calibrationfolder_name
    global button_initcamera, button_release, button_saveconfig
    global text_config, tbox, entry_measfolder, entry_calibfolder, piezosteps_var, CheckVar, output_text
    global a, colors, canvas_plot, line, ax

    global updateconfig_event

    global POI, ROI
    global displaymidline_state
    global piezo_dispaxis, calib_linear_region
    global toggle_selector_RS

    POI, ROI = [], []
    displaymidline_state = False
    measurementfolder_name = 'stack'
    calibrationfolder_name = 'stack'
    calib_linear_region = [19, None]

    #    dispInterf_state = 0

    q = Queue()
    stdout_queue = Queue()

    beginlive_event = Event()
    stoplive_event = Event()
    release_event = Event()
    #    stop_event = Event()
    piezostep_event = Event()
    updateconfig_event = Event()
    plotmidline_event = Event()
    """
    MAIN WINDOW
    """
    root = tk.Tk()
    root.iconbitmap('winicon.ico')
    #root.wm_attributes('-topmost', 1)
    #    w, h = root.winfo_screenwidth(), root.winfo_screenheight()
    #root.geometry("%dx%d+0+0" % (w, h))
    root.title('White light interferometry: Topography')
    root.configure(background='grey')
    """
    MENU
    """
    menubar = tk.Menu(root)
    filemenu = tk.Menu(menubar, tearoff=0)
    #filemenu.add_command(label = 'Load image', command=openimage)
    filemenu.add_command(label='Save displayed image')
    menubar.add_cascade(label='File', menu=filemenu)

    optionsmenu = tk.Menu(menubar, tearoff=0)
    optionsmenu.add_command(label='Configure camera')
    menubar.add_cascade(label='Options', menu=optionsmenu)

    menubar.add_command(label='Help')
    """""" """""" """""" """""" """""" """""" """""" """""" """""" """""" """""" """
            L        A        Y        O        U        T
    """ """""" """""" """""" """""" """""" """""" """""" """""" """""" """""" """"""
    """
    2 MAIN TABS
    """
    tabControl = ttk.Notebook(root)
    tab_ops = ttk.Frame(tabControl)
    tab_config = ttk.Frame(tabControl)
    tabControl.add(tab_ops, text='Operations')
    tabControl.add(tab_config, text='Camera configuration')
    tabControl.grid(row=0, sticky='we')
    """
    CAMERA CONFIGURATIONS FROM .INI FILE
    """
    text_config = tk.Text(tab_config, bg='gray18', fg='thistle1')
    text_config.grid(row=0, sticky='we')

    scrollb = tk.Scrollbar(tab_config, command=text_config.yview)
    scrollb.grid(row=0, column=1, sticky='nswe')
    text_config['yscrollcommand'] = scrollb.set

    root.config_ini = 'config.ini'
    file_contents = open(root.config_ini).read()
    text_config.insert('end', file_contents)

    tbox_contents = text_config.get('1.0', 'end')
    # c.char is a fixed array, so in case future editing needs more array space,
    # an expanded array is passed as an argument
    tbox = Array(ctypes.c_char, bytes(tbox_contents + '\n' * 10, 'utf8'))

    text_config.tag_config('section', foreground='khaki1')
    update_tboxEmbellishments()
    """
    SAVE CONFIGURATION CHANGES
    """
    button_saveconfig = tk.Button(tab_config,
                                  text="Save changes",
                                  bg="white",
                                  fg="black",
                                  command=update_config)

    button_saveconfig.grid(row=0, column=2, padx=10, pady=10)
    """
    CAMERA CONNECTION/DISCONNECTION FRAME
    """
    cameraonoff_frame = tk.Frame(tab_ops)
    cameraonoff_frame.grid(row=0, sticky='we')

    cameraonoff_frame.grid_rowconfigure(0, weight=1)
    cameraonoff_frame.grid_columnconfigure(0, weight=1)
    cameraonoff_frame.grid_columnconfigure(1, weight=1)
    """
    INITIALIZE CAMERA
    """
    button_initcamera = tk.Button(cameraonoff_frame,
                       text="INITIALIZE CAMERA",
                       bg="white",
                       fg="black",
                       command=lambda: create_cameraprocess(q, release_event, \
                            beginlive_event, stoplive_event, piezostep_event, \
                            updateconfig_event, plotmidline_event, stdout_queue))

    button_initcamera.grid(row=0, column=0, padx=10, pady=10)
    """
    RELEASE CAMERA
    """
    button_release = tk.Button(
        cameraonoff_frame,
        text="RELEASE CAMERA",
        bg="white",
        fg="black",
        command=lambda: notify_releasecamera(release_event))
    button_release.grid(row=0, column=1, padx=10, pady=10)

    #    """
    #    EXPOSURE TIME CONFIGURATION
    #    """
    #    label_exposure = tk.Label(cameraonoff_frame, text='Exposure time: ')
    #    label_exposure.grid(row=0,column=2,padx=10,pady=10,sticky='we')
    #
    #    slider_exposure = tk.Scale(cameraonoff_frame, from_=6, to=2000, orient='horizontal')
    #    slider_exposure.grid(row=0,column=3,padx=10,pady=10,sticky='we')
    #
    #    entry_exposure = tk.Entry(cameraonoff_frame)
    #    entry_exposure.grid(row=0,column=4,padx=10,pady=10,sticky='we')
    #    entry_exposure.delete(0, 'end')
    #    entry_exposure.insert(0, '')
    #    entry_exposure.bind("<Return>", get_exposure)
    #
    #    button_exposure = tk.Button(cameraonoff_frame,
    #                       text="Set",
    #                       bg="white",
    #                       fg="black",
    #                       command=set_exposure)
    #    button_exposure.grid(row=0,column=5,padx=10,pady=10,sticky='we')

    root.rowconfigure(0, weight=1)
    root.columnconfigure(0, weight=1)

    tab_ops.rowconfigure(0, weight=1)
    tab_ops.columnconfigure(0, weight=1)

    tab_config.rowconfigure(0, weight=1)
    tab_config.columnconfigure(0, weight=1)

    main_frame = tk.Frame(tab_ops)
    main_frame.grid(row=1, column=0, sticky='nswe')

    main_frame.grid_rowconfigure(0, weight=1)
    main_frame.grid_rowconfigure(1, weight=1)
    main_frame.grid_rowconfigure(2, weight=1)
    main_frame.grid_rowconfigure(3, weight=1)
    main_frame.grid_columnconfigure(0, weight=1)
    main_frame.grid_columnconfigure(1, weight=1)
    main_frame.grid_columnconfigure(2, weight=1)

    imageplots_frame = tk.Frame(main_frame)
    imageplots_frame.grid(row=0, column=0, sticky='nswe')

    imageplots_frame.grid_rowconfigure(0, weight=1)
    imageplots_frame.grid_columnconfigure(0, weight=1)

    oscillation_frame = tk.Frame(main_frame)
    oscillation_frame.grid(row=0, column=1, sticky='nswe')

    oscillation_frame.grid_rowconfigure(0, weight=1)
    oscillation_frame.grid_columnconfigure(0, weight=1)

    buttons_frame = tk.Frame(main_frame)
    buttons_frame.grid(row=0, column=2, sticky='nswe')

    buttons_frame.grid_rowconfigure(0, weight=1)
    buttons_frame.grid_columnconfigure(0, weight=1)

    piezosteps_frame = tk.Frame(buttons_frame)
    piezosteps_frame.grid(row=0, column=0, sticky='nswe')

    piezosteps_frame.grid_rowconfigure(0, weight=1)
    piezosteps_frame.grid_columnconfigure(0, weight=1)

    preperation_frame = tk.Frame(buttons_frame, borderwidth=1, relief='solid')
    preperation_frame.grid(row=1, column=0, sticky='nswe')

    preperation_frame.grid_rowconfigure(0, weight=1)
    preperation_frame.grid_columnconfigure(0, weight=1)

    measurement_frame = tk.Frame(buttons_frame, borderwidth=1, relief='solid')
    measurement_frame.grid(row=2, column=0, sticky='nswe', pady=10)

    measurement_frame.grid_rowconfigure(0, weight=1)
    measurement_frame.grid_columnconfigure(0, weight=1)

    selections_frame = tk.Frame(main_frame)
    selections_frame.grid(row=2, column=0, sticky='nswe')

    selections_frame.grid_rowconfigure(0, weight=1)
    selections_frame.grid_columnconfigure(0, weight=1)
    """
    CREATE CANVAS FOR IMAGE DISPLAY
    """
    #    sections = cfg.load_config_fromtkText('ALL', text_config.get('1.0', 'end'))
    #    h = eval(sections[2]['height'])
    #    w = eval(sections[2]['width'])
    dpi = 96.0
    #    f = Figure(figsize=(w/dpi,h/dpi))
    f = Figure(figsize=(500 / dpi, 500 / dpi), dpi=96)
    f.subplots_adjust(left=0.0, bottom=0.0, right=1.0, top=1.0)
    ax = f.add_subplot(111)
    ax.set_axis_off()
    img = Image.frombytes('L', (500, 500), b'\x00' * 250000)
    ax.imshow(img, cmap='gray', vmin=0, vmax=65535)
    livefeed_canvas = FigureCanvasTkAgg(f, master=imageplots_frame)
    livefeed_canvas.get_tk_widget().grid(row=0, column=0, sticky='nswe')
    livefeed_canvas.draw()
    """
    TOOLBAR - IMAGE SHOW
    """
    toolbarimshowFrame = tk.Frame(master=imageplots_frame)
    toolbarimshowFrame.grid(row=1, column=0)
    toolbarimshow = NavigationToolbar2Tk(livefeed_canvas, toolbarimshowFrame)
    toolbarimshow.update()

    # create global list of different plotting colors
    colors = lspec()
    """
    CREATE CANVAS FOR POI AND MIDLINE PLOTTING
    """
    #    dpi = 96
    #    fig = Figure(figsize=(imageplots_frame.winfo_height()/dpi, imageplots_frame.winfo_width()/3/dpi))
    fig = Figure(tight_layout=True)
    a = fig.add_subplot(111)
    canvas_plot = FigureCanvasTkAgg(fig, master=imageplots_frame)
    canvas_plot.get_tk_widget().grid(row=0, column=1, sticky='nswe')
    """
    TOOLBAR - LINE PLOT
    """
    toolbarplotFrame = tk.Frame(master=imageplots_frame)
    toolbarplotFrame.grid(row=1, column=1)
    toolbarplot = NavigationToolbar2Tk(canvas_plot, toolbarplotFrame)
    toolbarplot.update()

    label_preparation = tk.Label(preperation_frame,
                                 text='P R E P A R A T I O N')
    label_preparation.grid(row=0, columnspan=2)
    """
    POINTS OF INTEREST SELECTION BUTTONS
    """
    button_POIenable = tk.Button(preperation_frame,
                                 text="Select POI",
                                 fg="gold2",
                                 bg='grey18',
                                 command=enable_POIsel)
    button_POIenable.grid(row=1, column=0, padx=0, pady=10, sticky='ew')

    button_POIdisable = tk.Button(preperation_frame,
                                  text="OK!",
                                  fg="black",
                                  command=disable_POIsel)
    button_POIdisable.grid(row=1, column=1, padx=0, pady=0, sticky='ew')
    """
    REGION OF INTEREST SELECT BUTTONS
    """
    button_ROIenable = tk.Button(preperation_frame,
                                 text="Select ROI",
                                 fg="chocolate2",
                                 bg='grey18',
                                 command=enable_ROIsel)
    button_ROIenable.grid(row=2, column=0, padx=0, pady=0, sticky='ew')

    #    button_ROIdisable = tk.Button(preperation_frame,
    #                       text="OK!",
    #                       fg="black",
    #                       command=disable_ROIsel)
    #    button_ROIdisable.grid(row=2,column=1,padx=0,pady=0,sticky='ew')

    simplebuttons_frame = tk.Frame(selections_frame)
    simplebuttons_frame.grid(row=0, column=0, sticky='nswe')

    selections_frame.grid_rowconfigure(0, weight=1)
    selections_frame.grid_columnconfigure(0, weight=1)
    """
    OSCILLATE PIEZO BUTTON
    """
    button_oscillate = tk.Button(preperation_frame,
                                 text="Oscillate Piezo",
                                 fg="black",
                                 command=lambda: create_oscillationprocess(
                                     piezostep_event, stdout_queue))
    button_oscillate.grid(row=3, column=0, padx=0, pady=0, sticky='ew')
    """
    PLOT MIDLINE BUTTON
    """
    button_oscillate = tk.Button(
        preperation_frame,
        text="Display intensity across\nhor/ntal line",
        fg="black",
        command=lambda: toggle_displayMidline(plotmidline_event))
    button_oscillate.grid(row=3, column=1, padx=0, pady=0, sticky='ew')
    """
    OPTION LIST FOR NUMBER OF PIEZO STEPS - LABEL
    """
    label_piezosteps = tk.Label(piezosteps_frame, text='Piezo steps:')
    label_piezosteps.grid(row=0, column=0, padx=0, pady=0, sticky='ew')
    """
    OPTION LIST FOR NUMBER OF PIEZO STEPS - VALUES
    """
    piezosteps_options = ['100', '200', '300', '400', '500', '600']
    piezosteps_var = tk.StringVar()
    piezosteps_var.set(piezosteps_options[-1])  # default value

    optionmenu_piezosteps = tk.OptionMenu(piezosteps_frame, piezosteps_var,
                                          *piezosteps_options)
    optionmenu_piezosteps.grid(row=0, column=1, padx=0, pady=0, sticky='ew')

    label_measurement = tk.Label(measurement_frame,
                                 text='M E A S U R E M E N T')
    label_measurement.grid(row=0, columnspan=2)
    """
    EXECUTE PIEZO SCAN FOR INTERFEROGRAM STACK CAPTURING
    """
    button_oscillate = tk.Button(measurement_frame,
                                 text="Piezo scan\nCapture interferograms",
                                 fg="khaki1",
                                 bg='grey18',
                                 command=prepare_piezoscan)
    button_oscillate.grid(row=1,
                          column=0,
                          columnspan=2,
                          padx=0,
                          pady=10,
                          sticky='ew')

    CheckVar = tk.StringVar()
    CheckVar.set('m')
    C1 = tk.Radiobutton(measurement_frame,
                        text='Measurement',
                        variable=CheckVar,
                        value='m')
    C2 = tk.Radiobutton(measurement_frame,
                        text='Calibration',
                        variable=CheckVar,
                        value='c')
    C1.grid(row=2, column=0, padx=0, pady=0, sticky='we')
    C2.grid(row=2, column=1, padx=0, pady=0, sticky='we')
    """
    EXECUTE PIEZO SCAN FOR CALIBRATION PROCESS
    """
    button_oscillate = tk.Button(measurement_frame,
                                 text="Calibrate",
                                 fg="khaki1",
                                 bg='grey18',
                                 command=prepare_calibrate)
    button_oscillate.grid(row=5, column=0, padx=0, pady=0, sticky='ew')
    """
    INSERT MEASUREMENT IMAGE STACK FOLDER NAME
    """
    label_folder = tk.Label(measurement_frame,
                            text='Measurement\nfolder name:')
    label_folder.grid(row=3, column=0, padx=0, pady=0, sticky='ew')

    entry_measfolder = tk.Entry(measurement_frame)
    entry_measfolder.grid(row=3, column=1, padx=0, pady=0, sticky='we')
    entry_measfolder.delete(0, 'end')
    entry_measfolder.insert(0, 'stack')
    entry_measfolder.bind("<Return>", setMeasurementFolderName)
    """
    INSERT CALIBRATION IMAGE STACK FOLDER NAME
    """
    label_folder = tk.Label(measurement_frame,
                            text='Calibration\nfolder name:')
    label_folder.grid(row=4, column=0, padx=0, pady=0, sticky='ew')

    entry_calibfolder = tk.Entry(measurement_frame)
    entry_calibfolder.grid(row=4, column=1, padx=0, pady=0, sticky='we')
    entry_calibfolder.delete(0, 'end')
    entry_calibfolder.insert(0, 'stack')
    entry_calibfolder.bind("<Return>", setCalibrationFolderName)
    """
    EXECUTE INTERFEROGRAM STACK ANALYSIS FOR SURFACE ELEVATION MAP EXTRACTION
    """
    button_oscillate = tk.Button(measurement_frame,
                                 text="Analyze",
                                 fg="khaki1",
                                 bg='grey18',
                                 command=prepare_analysis)
    button_oscillate.grid(row=5, column=1, padx=0, pady=0, sticky='ew')

    #    """
    #    DISPLAY POI INTERFEROGRAMS
    #    """
    #    button_toggleDispInterf = tk.Button(oscillation_frame,
    #                       text="Display/Hide\nPOI Interferograms",
    #                       fg="black",
    #                       command=toggleDispInterf_threaded)
    #    button_toggleDispInterf.grid(row=2,column=1,padx=0,pady=0,sticky='ew')
    """
    LIVE FEED BUTTON
    """
    button_live = tk.Button(simplebuttons_frame,
                            text="Live!",
                            fg="red",
                            bg='grey18',
                            command=lambda: notify_beginlive(beginlive_event))
    button_live.grid(row=0, column=0, padx=10, pady=10, sticky='ew')
    """
    STOP LIVE BUTTON
    """
    button_reset = tk.Button(simplebuttons_frame,
                             text="Stop Live Feed",
                             fg="red",
                             bg='grey18',
                             command=lambda: notify_stoplive(stoplive_event))
    button_reset.grid(row=0, column=1, padx=10, pady=10, sticky='we')
    """""" """
     || || ||
     || || ||
     VV VV VV
    """ """"""
    piezo_dispaxis = np.loadtxt('Mapping_Steps_Displacement_2.txt')
    """
    OUTPUT TEXT
    """
    redirectstdout_frame = tk.Frame(main_frame)
    redirectstdout_frame.grid(row=3, column=0, sticky='nswe')

    output_text = ScrolledText(redirectstdout_frame,
                               bg='gray18',
                               fg='thistle1',
                               width=75,
                               height=10)
    output_text.see('end')
    output_text.grid(row=0, padx=10, pady=10, sticky='nswe')
    """
    CLEAR OUTPUT TEXT BOX
    """
    button_cleartbox = tk.Button(redirectstdout_frame,
                                 text="Clear",
                                 fg="lawn green",
                                 bg='grey18',
                                 command=clear_outputtext)
    button_cleartbox.grid(row=0, column=1, padx=10, pady=10, sticky='we')
    """
    RECTANGLE SELECTOR OBJECT - for ROI selection
    """
    toggle_selector_RS = RectangleSelector(
        ax,
        line_select_callback,
        drawtype='box',
        useblit=True,
        button=[1, 3],  # don't use middle button
        minspanx=5,
        minspany=5,
        spancoords='pixels',
        interactive=True)
    toggle_selector_RS.set_active(False)
    toggle_selector_RS.set_visible(False)
    """
    STDOUT REDIRECTION
    """
    sys.stdout = StdoutQueue(stdout_queue)
    sys.stderr = StdoutQueue(stdout_queue)

    # Instantiate and start the text monitor
    monitor = Thread(target=text_catcher, args=(output_text, stdout_queue))
    monitor.daemon = True
    monitor.start()

    #    sys.stdout = StdoutRedirector(output_text)
    #    sys.stderr = StderrRedirector(output_text)

    root.protocol("WM_DELETE_WINDOW", on_close)
    root.config(menu=menubar)
    root.mainloop()
class MplInteraction(object):


    def __init__(self, figure):
        """Initializer
        :param Figure figure: The matplotlib figure to attach the behavior to.
        """
        self._fig_ref = weakref.ref(figure)
        self.canvas = FigureCanvas(figure)
        self._cids_zoom = []
        self._cids_pan = []
        self._cids = []
        self._cids_callback_zoom = {}
        self._cids_callback_pan = {}
        self._callback_rectangle = None
        self._rectangle_selector = None
        self._xLimits = None
        self._yLimits = None

        #Create invokers
        self._invokerZoom = UndoHistoryZoomInvoker(figure.canvas)
        self._invokerPan = UndoHistoryPanInvoker(figure.canvas)

    def _add_connection(self, event_name, callback):

        cid = self.canvas.mpl_connect(event_name, callback)
        self._cids.append(cid)

    def create_rectangle_ax(self, ax):
        rectprops = dict(facecolor = None, edgecolor = 'black', alpha = 1,
         fill=False, linewidth = 1, linestyle = '-')
        self._rectangle_selector = RectangleSelector(ax, self._callback_rectangle,
                                           drawtype='box', useblit=True,
                                           rectprops = rectprops,
                                           button=[1, 3],  # don't use middle button
                                           minspanx=5, minspany=5,
                                           spancoords='pixels',
                                           interactive=False)

        self._rectangle_selector.set_visible(False)

    def set_axes_limits(self, xLimits, yLimits):
        """
        Get initial limits to allow to adjust the last zoom command to be
        the inital limits
        """
        self._xLimits = xLimits
        self._yLimits = yLimits

    def __del__(self):
        self.disconnect()

    def _add_rectangle_callback(self, callback):
        """
        Beacuse the callback method can only be created when the
        Zoom event is created and the axe can only be know after the creation of it,
        this method allow to assign the callback before
        the creation of the rectangle selector object
        """
        self._callback_rectangle = callback

    def _add_connection_zoom(self, event_name, callback):
        """Called to add a connection of type zoom to an event of the figure
        :param str event_name: The matplotlib event name to connect to.
        :param callback: The callback to register to this event.
        """

        #cid = self.canvas.mpl_connect(event_name, callback)
        #self._cids_zoom.append(cid)
        self._cids_callback_zoom[event_name] = callback

    def _add_connection_pan(self, event_name, callback):
        """Called to add a connection of type pan to an event of the figure
        :param str event_name: The matplotlib event name to connect to.
        :param callback: The callback to register to this event.
        """
        #cid = self.canvas.mpl_connect(event_name, callback)
        #self._cids_pan.append(cid)
        self._cids_callback_pan[event_name] = callback

    def disconnect_zoom(self):
        """
        Disconnect all zoom events and disable the rectangle selector
        """
        if self._fig_ref is not None:
            figure = self._fig_ref()
            if figure is not None:
                for cid in self._cids_zoom:
                    figure.canvas.mpl_disconnect(cid)
                self._disable_rectangle()

        self._cids_zoom.clear()

    def disconnect_pan(self):
        """
        Disconnect all pan events
        """
        if self._fig_ref is not None:
            figure = self._fig_ref()
            if figure is not None:
                for cid in self._cids_pan:
                    figure.canvas.mpl_disconnect(cid)
        self._cids_pan.clear()

    def _disable_rectangle(self):
        self._rectangle_selector.set_visible(False)
        self._rectangle_selector.set_active(False)

    def _enable_rectangle(self):
        self._rectangle_selector.set_visible(True)
        self._rectangle_selector.set_active(True)

    def connect_zoom(self):
        """
        Assign all callback zoom events to the mpl
        """
        for event_name, callback in self._cids_callback_zoom.items():
            cid = self.canvas.mpl_connect(event_name, callback)
            self._cids_zoom.append(cid)

        self._enable_rectangle()

    def connect_pan(self):
        """
        Assign all callback pan events to the mpl
        """
        for event_name, callback in self._cids_callback_pan.items():
            cid = self.canvas.mpl_connect(event_name, callback)
            self._cids_pan.append(cid)

    def disconnect(self):
        """Disconnect interaction from Figure."""
        if self._fig_ref is not None:
            figure = self._fig_ref()
            if figure is not None:
                for cid in self._cids_zoom:
                    figure.canvas.mpl_disconnect(cid)
                for cid in self._cids_pan:
                    figure.canvas.mpl_disconnect(cid)
                for cid in self._cids:
                    figure.canvas.mpl_disconnect(cid)
            self._fig_ref = None

    @property
    def figure(self):
        """The Figure this interaction is connected to or
        None if not connected."""
        return self._fig_ref() if self._fig_ref is not None else None


    def undo_last_action(self):
        """
        First, it undo the last action made by the zoom event
        Second, because the command list contains each command, the first one
        is related to adjust the zoom, which ocurred before, so the command list
        execute twice the same event, and because of that, the undo button need to
        be disabled and the command list clear
        """
        self._invokerZoom.undo()
        if self._invokerZoom.command_list_length() <= 1:
            self._invokerZoom.clear_command_list()
            pub.sendMessage('setStateUndo', state = False)

    def add_zoom_reset(self):
        if self._invokerZoom.command_list_length() == 0:
            #Send the signal to change undo button state
            pub.sendMessage('setStateUndo', state = True)

        zoomResetCommand = ZoomResetCommand(self.figure, self._xLimits, self._yLimits)
        self._invokerZoom.command(zoomResetCommand)
        self._draw_idle()

    def _add_initial_zoom_reset(self):
        if self._invokerZoom.command_list_length() == 0:
            #Send the signal to change undo button state
            pub.sendMessage('setStateUndo', state = True)

            zoomResetCommand = ZoomResetCommand(self.figure, self._xLimits, self._yLimits)
            self._invokerZoom.command(zoomResetCommand)
            self._draw_idle()


    def clear_commands(self):
        """
        Delete all commands
        """
        self._invokerZoom.clear_command_list()

    def _draw_idle(self):
        """Update altered figure, but not automatically re-drawn"""
        self.canvas.draw_idle()

    def _draw(self):
        """Conveninent method to redraw the figure"""
        self.canvas.draw()
Ejemplo n.º 5
0
class IsoCenter_Child(QMain, IsoCenter.Ui_IsoCenter):
    "Class that contains subroutines to define isocenter from Lynx image"

    def __init__(self, parent, owner):
        super(IsoCenter_Child, self).__init__()

        self.Owner = owner
        self.setupUi(self)
        self.setStyleSheet(parent.styleSheet())

        self.parent = parent
        self.canvas = self.Display_IsoCenter.canvas
        self.toolbar = self.canvas.toolbar

        # Connect buttons
        self.Button_LoadSpot.clicked.connect(self.load)
        self.Button_detectIsoCenter.clicked.connect(self.drawRect)
        self.Button_SetIsoCenter.clicked.connect(self.LockIsoCenter)
        self.Button_Done.clicked.connect(self.Done)

        # Works only after first rectangle was drawn
        try:
            self.Button_detectIsoCenter.clicked.connect(self.initclick)
        except AttributeError:
            pass

        # Flags and Containers
        self.Image = None
        self.press = None
        self.rects = []
        self.target_markers = []

        # Flags
        self.IsoCenter_flag = False

        # Lists for isocenter markers in canvas
        self.target_markers = []

    def drawRect(self):

        # Remove previous spotdetections
        for item in self.target_markers:
            if type(item) == matplotlib.contour.QuadContourSet:
                [artist.set_visible(False) for artist in item.collections]
            else:
                item.set_visible(False)

        # change cursor style
        QApplication.setOverrideCursor(Qt.CrossCursor)

        # Rectangle selector for 2d fit
        rectprops = dict(facecolor='orange',
                         edgecolor=None,
                         alpha=0.2,
                         fill=True)
        # drawtype is 'box' or 'line' or 'none'
        self.RS = RectangleSelector(
            self.canvas.axes,
            self.line_select_callback,
            drawtype='box',
            rectprops=rectprops,
            button=[1],  # don't use middle button
            minspanx=5,
            minspany=5,
            spancoords='pixels',
            useblit=True,
            interactive=True)
        self.canvas.draw()
        self.bg = self.canvas.copy_from_bbox(self.RS.ax.bbox)
        self.RS.set_visible(True)

        ext = (0, 4, 0, 1)
        self.RS.draw_shape(ext)

        # Update displayed handles
        self.RS._corner_handles.set_data(*self.RS.corners)
        self.RS._edge_handles.set_data(*self.RS.edge_centers)
        self.RS._center_handle.set_data(*self.RS.center)
        for artist in self.RS.artists:
            self.RS.ax.draw_artist(artist)
            artist.set_animated(False)
        self.canvas.draw()
        self.cid = self.canvas.mpl_connect("button_press_event",
                                           self.initclick)

    def line_select_callback(self, eclick, erelease):
        x1, y1 = eclick.xdata, eclick.ydata
        x2, y2 = erelease.xdata, erelease.ydata

        p1 = (x1, y1)
        p2 = (x2, y2)

        self.spotDetect(p1, p2)

    def initclick(self, evt):
        self.RS.background = self.bg
        self.RS.update()
        for artist in self.RS.artists:
            artist.set_animated(True)
        self.canvas.mpl_disconnect(self.cid)

    def load(self):
        "load radiography image of beam IsoCenter"

        # get filename from full path and display
        fname = Qfile.getOpenFileName(self, 'Open file', "",
                                      "Dicom files (*.dcm *tiff *tif)")[0]
        try:
            # import imagedata with regard to filetype
            if fname.endswith("dcm"):
                meta = dcm.read_file(fname)
                self.Image = RadiographyImage(fname, meta.pixel_array,
                                              meta.PixelSpacing)

            elif fname.endswith("tif") or fname.endswith("tiff"):
                pw, okx = QInputDialog.getDouble(self,
                                                 'Pixel Spacing',
                                                 'pixel width (mm):',
                                                 0.05,
                                                 decimals=2)
                self.Image = RadiographyImage(fname, tifffile.imread(fname),
                                              pw)

            self.Text_Filename.setText(fname)  # display filename
            self.canvas.axes.imshow(self.Image.array,
                                    cmap='gray',
                                    zorder=1,
                                    origin='lower')
            self.canvas.draw()
            logging.info('{:s} imported as Isocenter'.format(fname))

        except Exception:
            logging.ERROR("{:s} could not be opened".format(fname))
            self.IsoCenter_flag = False
            return 0

    def LockIsoCenter(self):
        """ Read current values from sliders/ spot location text fields
        and set as final isocenter coordinates to be used for the
        actual positioning"""
        self.SpotTxt_x.setStyleSheet("color: rgb(255, 0, 0);")
        self.SpotTxt_y.setStyleSheet("color: rgb(255, 0, 0);")
        # Raise flag for checksum check later
        self.IsoCenter_flag = True

        # Function to pass IsoCenter values to parent window
        self.Owner.return_isocenter(
            self.Image, [self.SpotTxt_x.value(),
                         self.SpotTxt_y.value()])
        logging.info('Isocenter coordinates confirmed')

    def update_crosshair(self):
        """Get value from Spinboxes and update all
        markers/plots if that value is changed"""
        x = self.SpotTxt_x.value()
        y = self.SpotTxt_y.value()

        # Update Plot Markers
        self.hline.set_ydata(y)
        self.vline.set_xdata(x)

        # Update Plot
        self.Display_IsoCenter.canvas.draw()

        self.SpotTxt_x.setStyleSheet("color: rgb(0, 0, 0);")
        self.SpotTxt_y.setStyleSheet("color: rgb(0, 0, 0);")
        self.IsoCenter_flag = False

    def spotDetect(self, p1, p2):
        " Function that is invoked by ROI selection, autodetects earpin"

        # Restore old cursor
        QApplication.restoreOverrideCursor()

        # Get ROI limits from drawn rectangle corners
        x = int(min(p1[0], p2[0]) + 0.5)
        y = int(min(p1[1], p2[1]) + 0.5)
        width = int(np.abs(p1[0] - p2[0]) + 0.5)
        height = int(np.abs(p1[1] - p2[1]) + 0.5)

        subset = self.Image.array[y:y + height, x:x + width]

        # Calculate fit function values
        try:
            popt, pcov = find_center(subset, x, y, sigma=5.0)
            logging.info('Detected coordinates for isocenter:'
                         'x = {:2.1f}, y = {:2.1f}'.format(popt[1], popt[2]))
        except Exception:
            logging.error('Autodetection of Landmark in ROI failed.')
            # self.TxtEarpinX.setValue(0)
            # self.TxtEarpinY.setValue(0)
            return 0

        xx, yy, xrange, yrange = array2mesh(self.Image.array)
        data_fitted = twoD_Gaussian((xx, yy), *popt)

        # Print markers into image
        ax = self.canvas.axes
        self.target_markers.append(
            ax.contour(xx, yy, data_fitted.reshape(yrange, xrange), 5))
        self.target_markers.append(ax.axvline(popt[1], 0, ax.get_ylim()[1]))
        self.target_markers.append(ax.axhline(popt[2], 0, ax.get_xlim()[1]))
        self.canvas.draw()

        self.SpotTxt_x.setValue(popt[1])
        self.SpotTxt_y.setValue(popt[2])

        logging.info('Coordinates of IsoCenter set to '
                     'x = {:.1f}, y = {:.1f}'.format(popt[1], popt[2]))

    def Done(self):
        "Ends IsoCenter Definition and closes Child"
        # Also check whether all values were locked to main window
        if not self.IsoCenter_flag:
            Hint = QMessage()
            Hint.setStandardButtons(QMessage.No | QMessage.Yes)
            Hint.setIcon(QMessage.Information)
            Hint.setText("Some values have not been locked or were modified!"
                         "\nProceed?")
            answer = Hint.exec_()
            if answer == QMessage.Yes:
                self.close()
        else:
            self.close()
Ejemplo n.º 6
0
class Axes2D(object):
    def __init__(self, ax, parent):
        self.ax = ax
        self.parent = parent

        self.params_2d = {}
        self.y, self.x1, self.x2, self.data = [], [], [], []
        self.rectangle_coordinates = None
        self.RectangleSelector = None
        self.cut_window = None
        self.colormap_window = None
        self.apply_log_status = False
        self.plot_obj = None
        self.Ranges = Plot2DRangesHandler(self)

    def set_rectangle(self):
        self.RectangleSelector = RectangleSelector(
            self.ax,
            self.line_select_callback,
            drawtype='box',
            button=[1],  # don't use middle button
            minspanx=5,
            minspany=5,
            spancoords='pixels',
            interactive=True)

        def update_rs(event):
            if self.RectangleSelector is not None:
                if self.RectangleSelector.active and self.parent.status == 2:
                    self.RectangleSelector.update()

        self.parent.mpl_connect('draw_event', update_rs)
        if self.rectangle_coordinates is not None:
            x1, y1, x2, y2 = self.rectangle_coordinates
            self.RectangleSelector.extents = (x1, x2, y1, y2)
            self.RectangleSelector.update()

    def line_select_callback(self, eclick, erelease):
        """eclick and erelease are the press and release events"""
        assert self.cut_window is not None
        self.rectangle_coordinates = eclick.xdata, eclick.ydata, erelease.xdata, erelease.ydata
        self.cut_window.canvas.update_cut_plot()

    def context_menu(self, event):
        menu = QMenu()
        y = self.parent.parent().height()
        position = self.parent.mapFromParent(QPoint(event.x, y - event.y))
        parameter_menu = menu.addMenu('Plot parameters')
        change_colormap_action = parameter_menu.addAction("Change colormap")
        change_colormap_action.triggered.connect(self.open_colormap_window)

        log_action_name = "Disable log" if self.apply_log_status else "Apply log"
        change_log_action = parameter_menu.addAction(log_action_name)
        change_log_action.triggered.connect(self.change_log_status)

        reset_action = parameter_menu.addAction("Reset parameters")
        reset_action.triggered.connect(self.reset_parameters)

        redraw_action = menu.addAction("Redraw graph")
        redraw_action.triggered.connect(self.redraw_2d_plot)

        menu.addSeparator()
        open_cut_window_action = menu.addAction("Open cut window")
        open_cut_window_action.triggered.connect(self.open_cut_window)
        open_cut_window_action.setEnabled(self.cut_window is None)
        menu.exec_(self.parent.parent().mapToGlobal(position))

    def open_colormap_window(self, event):
        range_init, range_whole = self.Ranges.get_ranges_for_colormap(self.y)
        self.colormap_window = ColormapWindow(range_init,
                                              range_whole,
                                              title='Colormap')
        self.colormap_window.set_callback(self.colormap_callback)
        self.colormap_window.show()

    def redraw_2d_plot(self):
        self.ax.cla()
        self.plot_obj = self.ax.imshow(self.y, **self.params_2d)
        if self.cut_window:
            self.set_rectangle()

    def colormap_callback(self, range_):
        self.Ranges.update_params(range_)
        self.redraw_2d_plot()
        self.parent.draw()

    def change_log_status(self, event):
        self.apply_log_status = not self.apply_log_status
        if self.apply_log_status:
            self.y = self.apply_log(self.data)
        else:
            self.y = self.data
        self.Ranges.change_regime()
        self.redraw_2d_plot()
        self.parent.draw()

    def reset_parameters(self, event):
        self.apply_log_status = False
        self.params_2d.pop('vmin', None)
        self.params_2d.pop('vmax', None)
        self.y = self.data
        self.redraw_2d_plot()
        self.parent.draw()

    @staticmethod
    def apply_log(data):
        min_value = max([0.1, np.amin(data)])
        max_value = np.amax(data)
        return np.log(np.clip(data, min_value, max_value))

    def update_plot(self, obj, file):
        self.data = obj[()]
        if self.apply_log_status:
            self.y = self.apply_log(self.data)
        else:
            self.y = self.data

        try:
            x_ax = file[obj.attrs['x_axis']][()]
            y_ax = file[obj.attrs['y_axis']][()]
            assert (len(x_ax), len(y_ax)) == self.y.shape \
                   or (len(y_ax), len(x_ax)) == self.y.shape, 'shapes are wrong'
            if (len(y_ax), len(x_ax)) == self.y.shape:
                y_ax, x_ax = x_ax, y_ax
            self.x1 = y_ax
            self.x2 = x_ax
        except (AssertionError, TypeError, KeyError):
            self.params_2d.pop('extent', None)
            self.x1 = list(range(0, self.y.shape[1]))
            self.x2 = list(range(0, self.y.shape[0]))

        except Exception as er:
            print(er)
            return

        self.params_2d.update(
            dict(extent=[self.x1[0], self.x1[-1], self.x2[0], self.x2[-1]]))

        if self.plot_obj is not None:
            self.plot_obj.set_data(self.y)
            self.plot_obj.set_extent(self.params_2d['extent'])
            self.ax.relim()  # Recalculate limits
            self.ax.autoscale_view(True, True, True)
        else:
            self.plot_obj = self.ax.imshow(self.y, **self.params_2d)
        self.parent.draw()
        if self.cut_window:
            self.cut_window.canvas.update_cut_plot()

    def open_cut_window(self):
        self.cut_window = CutWindow(self)
        self.set_rectangle()
        if self.rectangle_coordinates:
            self.cut_window.canvas.update_cut_plot()

    def on_closing_cut_window(self):
        self.RectangleSelector.set_visible(False)
        self.RectangleSelector.update()
        self.RectangleSelector = None
        self.cut_window = None
Ejemplo n.º 7
0
class MapView(FigureCanvasQTAgg):
    """
    Map view using cartopy/matplotlib to view multibeam tracklines and surfaces with a map context.
    """
    box_select = Signal(float, float, float, float)

    def __init__(self,
                 parent=None,
                 width: int = 5,
                 height: int = 4,
                 dpi: int = 100,
                 map_proj=ccrs.PlateCarree(),
                 settings=None):
        self.fig = Figure(figsize=(width, height), dpi=dpi)
        self.map_proj = map_proj
        self.axes = self.fig.add_subplot(projection=map_proj)
        # self.axes.coastlines(resolution='10m')
        self.fig.add_axes(self.axes)
        #self.fig.subplots_adjust(left=0, right=1, bottom=0, top=1)

        self.axes.gridlines(draw_labels=True, crs=self.map_proj)
        self.axes.add_feature(cfeature.LAND)
        self.axes.add_feature(cfeature.COASTLINE)

        self.line_objects = {}  # dict of {line name: [lats, lons, lineplot]}
        self.surface_objects = {
        }  # nested dict {surfname: {layername: [lats, lons, surfplot]}}
        self.active_layers = {}  # dict of {surfname: [layername1, layername2]}
        self.data_extents = {
            'min_lat': 999,
            'max_lat': -999,
            'min_lon': 999,
            'max_lon': -999
        }
        self.selected_line_objects = []

        super(MapView, self).__init__(self.fig)

        self.navi_toolbar = NavigationToolbar2QT(self.fig.canvas, self)
        self.rs = RectangleSelector(self.axes,
                                    self._line_select_callback,
                                    drawtype='box',
                                    useblit=False,
                                    button=[1],
                                    minspanx=5,
                                    minspany=5,
                                    spancoords='pixels',
                                    interactive=True)
        self.set_extent(90, -90, 100, -100)

    def set_background(self, layername: str, transparency: float,
                       surf_transparency: float):
        """
        A function for rendering different background layers in QGIS. Disabled for cartopy
        """
        pass

    def set_extent(self,
                   max_lat: float,
                   min_lat: float,
                   max_lon: float,
                   min_lon: float,
                   buffer: bool = True):
        """
        Set the extent of the 2d window

        Parameters
        ----------
        max_lat
            set the maximum latitude of the displayed map
        min_lat
            set the minimum latitude of the displayed map
        max_lon
            set the maximum longitude of the displayed map
        min_lon
            set the minimum longitude of the displayed map
        buffer
            if True, will extend the extents by half the current width/height
        """

        self.data_extents['min_lat'] = np.min(
            [min_lat, self.data_extents['min_lat']])
        self.data_extents['max_lat'] = np.max(
            [max_lat, self.data_extents['max_lat']])
        self.data_extents['min_lon'] = np.min(
            [min_lon, self.data_extents['min_lon']])
        self.data_extents['max_lon'] = np.max(
            [max_lon, self.data_extents['max_lon']])

        if self.data_extents['min_lat'] != 999 and self.data_extents[
                'max_lat'] != -999 and self.data_extents[
                    'min_lon'] != 999 and self.data_extents['max_lon'] != -999:
            if buffer:
                lat_buffer = np.max([(max_lat - min_lat) * 0.5, 0.5])
                lon_buffer = np.max([(max_lon - min_lon) * 0.5, 0.5])
            else:
                lat_buffer = 0
                lon_buffer = 0
            self.axes.set_extent([
                np.clip(min_lon - lon_buffer, -179.999999999, 179.999999999),
                np.clip(max_lon + lon_buffer, -179.999999999, 179.999999999),
                np.clip(min_lat - lat_buffer, -90, 90),
                np.clip(max_lat + lat_buffer, -90, 90)
            ],
                                 crs=ccrs.Geodetic())

    def add_line(self,
                 line_name: str,
                 lats: np.ndarray,
                 lons: np.ndarray,
                 refresh: bool = False):
        """
        Draw a new multibeam trackline on the cartopy display, unless it is already there

        Parameters
        ----------
        line_name
            name of the multibeam line
        lats
            numpy array of latitude values to plot
        lons
            numpy array of longitude values to plot
        refresh
            set to True if you want to show the line after adding here, kluster will redraw the screen after adding
            lines itself
        """

        if line_name in self.line_objects:
            return
        # this is about 3x slower, use transform_points instead
        # lne = self.axes.plot(lons, lats, color='blue', linewidth=2, transform=ccrs.Geodetic())
        ret = self.axes.projection.transform_points(ccrs.Geodetic(), lons,
                                                    lats)
        x = ret[..., 0]
        y = ret[..., 1]
        lne = self.axes.plot(x, y, color='blue', linewidth=2)
        self.line_objects[line_name] = [lats, lons, lne[0]]
        if refresh:
            self.refresh_screen()

    def remove_line(self, line_name, refresh=False):
        """
        Remove a multibeam line from the cartopy display

        Parameters
        ----------
        line_name
            name of the multibeam line
        refresh
            optional screen refresh, True most of the time, unless you want to remove multiple lines and then refresh
            at the end
        """

        if line_name in self.line_objects:
            lne = self.line_objects[line_name][2]
            lne.remove()
            self.line_objects.pop(line_name)
        if refresh:
            self.refresh_screen()

    def add_surface(self, surfname: str, lyrname: str, surfx: np.ndarray,
                    surfy: np.ndarray, surfz: np.ndarray, surf_crs: int):
        """
        Add a new surface/layer with the provided data

        Parameters
        ----------
        surfname
            path to the surface that is used as a name
        lyrname
            band layer name for the provided data
        surfx
            1 dim numpy array for the grid x values
        surfy
            1 dim numpy array for the grid y values
        surfz
            2 dim numpy array for the grid values (depth, uncertainty, etc.)
        surf_crs
            integer epsg code
        """

        try:
            addlyr = True
            if lyrname in self.active_layers[surfname]:
                addlyr = False
        except KeyError:
            addlyr = True

        if addlyr:
            self._add_surface_layer(surfname, lyrname, surfx, surfy, surfz,
                                    surf_crs)
            self.refresh_screen()

    def hide_surface(self, surfname: str, lyrname: str):
        """
        Hide the surface layer that corresponds to the given names.

        Parameters
        ----------
        surfname
            path to the surface that is used as a name
        lyrname
            band layer name for the provided data
        """

        try:
            hidelyr = True
            if lyrname not in self.active_layers[surfname]:
                hidelyr = False
        except KeyError:
            hidelyr = False

        if hidelyr:
            self._hide_surface_layer(surfname, lyrname)
            return True
        else:
            return False

    def show_surface(self, surfname: str, lyrname: str):
        """
        Cartopy backend currently just deletes/adds surface data, doesn't really hide or show.  Return False here to
        signal we did not hide
        """
        return False

    def remove_surface(self, surfname: str):
        """
        Remove the surface from memory by removing the name from the surface_objects dict

        Parameters
        ----------
        surfname
            path to the surface that is used as a name
        """

        if surfname in self.surface_objects:
            for lyr in self.surface_objects[surfname]:
                self.hide_surface(surfname, lyr)
                surf = self.surface_objects[surfname][lyr][2]
                surf.remove()
                self.surface_objects.pop(surfname)
        self.refresh_screen()

    def _add_surface_layer(self, surfname: str, lyrname: str,
                           surfx: np.ndarray, surfy: np.ndarray,
                           surfz: np.ndarray, surf_crs: int):
        """
        Add a new surface/layer with the provided data

        Parameters
        ----------
        surfname
            path to the surface that is used as a name
        lyrname
            band layer name for the provided data
        surfx
            1 dim numpy array for the grid x values
        surfy
            1 dim numpy array for the grid y values
        surfz
            2 dim numpy array for the grid values (depth, uncertainty, etc.)
        surf_crs
            integer epsg code
        """

        try:
            makelyr = True
            if lyrname in self.surface_objects[surfname]:
                makelyr = False
        except KeyError:
            makelyr = True

        if makelyr:
            desired_crs = self.map_proj
            lon2d, lat2d = np.meshgrid(surfx, surfy)
            xyz = desired_crs.transform_points(ccrs.epsg(int(surf_crs)), lon2d,
                                               lat2d)
            lons = xyz[..., 0]
            lats = xyz[..., 1]

            if lyrname != 'depth':
                vmin, vmax = np.nanmin(surfz), np.nanmax(surfz)
            else:  # need an outlier resistant min max depth range value
                twostd = np.nanstd(surfz)
                med = np.nanmedian(surfz)
                vmin, vmax = med - twostd, med + twostd
            # print(vmin, vmax)
            surfplt = self.axes.pcolormesh(lons,
                                           lats,
                                           surfz.T,
                                           vmin=vmin,
                                           vmax=vmax,
                                           zorder=10)
            setextents = False
            if not self.line_objects and not self.surface_objects:  # if this is the first thing you are loading, jump to it's extents
                setextents = True
            self._add_to_active_layers(surfname, lyrname)
            self._add_to_surface_objects(surfname, lyrname,
                                         [lats, lons, surfplt])
            if setextents:
                self.set_extents_from_surfaces()
        else:
            surfplt = self.surface_objects[surfname][lyrname][2]
            newsurfplt = self.axes.add_artist(surfplt)
            # update the object with the newly added artist
            self.surface_objects[surfname][lyrname][2] = newsurfplt
            self._add_to_active_layers(surfname, lyrname)

    def _hide_surface_layer(self, surfname: str, lyrname: str):
        """
        Hide the surface layer that corresponds to the given names.

        Parameters
        ----------
        surfname
            path to the surface that is used as a name
        lyrname
            band layer name for the provided data
        """

        surfplt = self.surface_objects[surfname][lyrname][2]
        surfplt.remove()
        self._remove_from_active_layers(surfname, lyrname)
        self.refresh_screen()

    def _add_to_active_layers(self, surfname: str, lyrname: str):
        """
        Add the surface layer to the active layers dict

        Parameters
        ----------
        surfname
            path to the surface that is used as a name
        lyrname
            band layer name for the provided data
        """

        if surfname in self.active_layers:
            self.active_layers[surfname].append(lyrname)
        else:
            self.active_layers[surfname] = [lyrname]

    def _add_to_surface_objects(self, surfname: str, lyrname: str, data: list):
        """
        Add the surface layer data to the surface objects dict

        Parameters
        ----------
        surfname
            path to the surface that is used as a name
        lyrname
            band layer name for the provided data
        data
            list of [2dim y values for the grid, 2dim x values for the grid, matplotlib.collections.QuadMesh]
        """

        if surfname in self.surface_objects:
            self.surface_objects[surfname][lyrname] = data
        else:
            self.surface_objects[surfname] = {lyrname: data}

    def _remove_from_active_layers(self, surfname: str, lyrname: str):
        """
        Remove the surface layer from the active layers dict

        Parameters
        ----------
        surfname
            path to the surface that is used as a name
        lyrname
            band layer name for the provided data
        """

        if surfname in self.active_layers:
            if lyrname in self.active_layers[surfname]:
                self.active_layers[surfname].remove(lyrname)

    def _remove_from_surface_objects(self, surfname, lyrname):
        """
        Remove the surface layer from the surface objects dict

        Parameters
        ----------
        surfname
            path to the surface that is used as a name
        lyrname
            band layer name for the provided data
        """

        if surfname in self.surface_objects:
            if lyrname in self.surface_objects[surfname]:
                self.surface_objects[surfname].pop(lyrname)

    def change_line_colors(self, line_names: list, color: str):
        """
        Change the provided line names to the provided color

        Parameters
        ----------
        line_names
            list of line names to use as keys in the line objects dict
        color
            string color identifier, ex: 'r' or 'red'
        """

        for line in line_names:
            lne = self.line_objects[line][2]
            lne.set_color(color)
            self.selected_line_objects.append(lne)
        self.refresh_screen()

    def reset_line_colors(self):
        """
        Reset all lines back to the default color
        """

        for lne in self.selected_line_objects:
            lne.set_color('b')
        self.selected_line_objects = []
        self.refresh_screen()

    def _line_select_callback(self, eclick: MouseEvent, erelease: MouseEvent):
        """
        Handle the return of the Matplotlib RectangleSelector, provides an event with the location of the click and
        an event with the location of the release

        Parameters
        ----------
        eclick
            MouseEvent with the position of the initial click
        erelease
            MouseEvent with the position of the final release of the mouse button
        """

        x1, y1 = eclick.xdata, eclick.ydata
        x2, y2 = erelease.xdata, erelease.ydata
        self.rs.set_visible(False)

        # set the visible property back to True so that the next move event shows the box
        self.rs.visible = True

        # signal with min lat, max lat, min lon, max lon
        self.box_select.emit(y1, y2, x1, x2)
        # print("(%3.2f, %3.2f) --> (%3.2f, %3.2f)" % (x1, y1, x2, y2))

    def set_extents_from_lines(self):
        """
        Set the maximum extent based on the line_object coordinates
        """

        lats = []
        lons = []
        for ln in self.line_objects:
            lats.append(self.line_objects[ln][0])
            lons.append(self.line_objects[ln][1])

        if not lats or not lons:
            self.set_extent(90, -90, 100, -100)
        else:
            lats = np.concatenate(lats)
            lons = np.concatenate(lons)

            self.set_extent(np.max(lats), np.min(lats), np.max(lons),
                            np.min(lons))
        self.refresh_screen()

    def set_extents_from_surfaces(self):
        """
        Set the maximum extent based on the surface_objects coordinates
        """

        lats = []
        lons = []
        for surf in self.surface_objects:
            for lyrs in self.surface_objects[surf]:
                lats.append(self.surface_objects[surf][lyrs][0])
                lons.append(self.surface_objects[surf][lyrs][1])

        if not lats or not lons:
            self.set_extent(90, -90, 100, -100)
        else:
            lats = np.concatenate(lats)
            lons = np.concatenate(lons)

            self.set_extent(np.max(lats), np.min(lats), np.max(lons),
                            np.min(lons))
        self.refresh_screen()

    def clear(self):
        """
        Clear all loaded data including surfaces and lines and refresh the screen
        """

        self.line_objects = {}
        self.surface_objects = {}
        self.active_layers = {}
        self.data_extents = {
            'min_lat': 999,
            'max_lat': -999,
            'min_lon': 999,
            'max_lon': -999
        }
        self.selected_line_objects = []
        self.set_extent(90, -90, 100, -100)
        self.refresh_screen()

    def refresh_screen(self):
        """
        Reset to the original zoom/extents
        """

        self.axes.relim()
        self.axes.autoscale_view()

        self.fig.canvas.draw_idle()
        self.fig.canvas.flush_events()
class MvPoints():
    def __init__(self, xdata, ydata,fig,axis,color=0,area=40,alpha=1):
        self.x=xdata
        self.y=ydata        
        self.points=[]
        try:
            if color>=0 or color<10000:    
                self.colors=np.ones(len(xdata))*color
                self.oldcolor=self.colors
            else:
                self.colors=color*2
                self.oldcolor=color*2
        except:
            self.colors=color*2
            self.oldcolor=color*2
        
        self.fig=fig
        self.ax=axis
        self.evnt=None
        self.alpha=alpha
        self.area=area
        self.scat=self.ax[0].scatter(self.x,self.y,s=self.area, c=self.colors,alpha=self.alpha)
        self.labels=[]
        self.labels2=[]
        for i in range(len(xdata)):
            self.labels.append(self.ax[0].annotate(i, (self.x[i], self.y[i])))
            self.labels2.append(self.ax[1].annotate(i, (self.x[i], self.y[i])))
        self.ax[0].set_xlim((-1.3,1.3))
        self.ax[0].set_ylim((-1.3,1.3))    
        
        self.rs = RectangleSelector(self.ax[0], self.line_select_callback,
                       drawtype='box', useblit=False, button=[1], 
                       minspanx=5, minspany=5, spancoords='pixels', 
                       interactive=True)
        
    def line_select_callback(self,eclick, erelease):
        x1, y1 = eclick.xdata, eclick.ydata
        x2, y2 = erelease.xdata, erelease.ydata
        newx=[]
        newy=[]
        self.points=[]
        xacc=0
        yacc=0
        self.rs.set_visible(False)  
        for i in range(len(self.x)):
            newx.append(self.x[i])
            newy.append(self.y[i])    
            if x1 <self.x[i] and x2>self.x[i] and y2>self.y[i] and y1<self.y[i]:
                self.colors[i]=1
                self.points.append(i)
                xacc+=self.x[i]
                yacc+=self.y[i]
        n=len(self.points)
        if n!=0:
            #CENTER THE MOUSE MOVING ACCORDING THE POINTS
            self.x0=xacc/n
            self.y0=yacc/n
            ## REMPLACE THE CHANGES POINTS
            self.x=newx
            self.y=newy
            self.scat.remove()
            self.scat=self.ax[0].scatter(self.x,self.y,s=self.area, c=self.colors,alpha=self.alpha)
            self.ax[0].set_xlim((-1.3,1.3))
            self.ax[0].set_ylim((-1.3,1.3))
            self.onclck=self.fig.canvas.mpl_connect('button_press_event', self.onclick)
            self.evtrelease=self.fig.canvas.mpl_connect('button_release_event', self.onrelease)
            self.evnt=self.fig.canvas.mpl_connect('motion_notify_event', self.on_drag)
            self.rs=None
        else:   
            self.rs = RectangleSelector(self.ax[0], self.line_select_callback,
                       drawtype='box', useblit=False, button=[1], 
                       minspanx=5, minspany=5, spancoords='pixels', 
                       interactive=True)

    def on_drag(self,event):
        xpress, ypress = event.xdata, event.ydata
        try:
            for i in self.points:
                try:
                    self.labels[i].remove()
                    self.labels[i]=self.ax[0].annotate(i, (self.x[i], self.y[i]))
                except:
                    print('x')
                #self.labels[i]=self.ax[0].annotate(i, (self.x[i], self.y[i]))
                self.x[i]=self.x[i]-self.x0+xpress
                self.y[i]=self.y[i]-self.y0+ypress
                
            self.x0=xpress
            self.y0=ypress
            self.scat.remove()
            self.scat=self.ax[0].scatter(self.x,self.y,s=self.area, c=self.colors,alpha=self.alpha)
            self.ax[0].set_xlim((-1.3,1.3))
            self.ax[0].set_ylim((-1.3,1.3))
            self.fig.canvas.draw()
        except:
            print(xpress,ypress)
        
    def onclick(self,event):
        self.fig.canvas.mpl_disconnect(self.evnt)
        self.points=[]
        
    def onrelease(self,event):
        self.rs = RectangleSelector(self.ax[0], self.line_select_callback,
                       drawtype='box', useblit=False, button=[1], 
                       minspanx=5, minspany=5, spancoords='pixels', 
                       interactive=True)
        self.fig.canvas.mpl_disconnect(self.onclck)
        self.fig.canvas.mpl_disconnect(self.evtrelease)
        self.colors=self.oldcolor.copy()
    def change(self,x,y):
        try:
            self.scat2.remove()
        except:
            print("First")
        self.ax[0].set_title("Result of DR with Mixture Kernel")
        self.ax[1].set_title("Expected DR")
        self.scat2=self.ax[1].scatter(self.x,self.y,s=self.area, c=self.colors,alpha=self.alpha)
        self.ax[1].set_xlim((-1.3,1.3))
        self.ax[1].set_ylim((-1.3,1.3))
        
        self.scat.remove()
        self.scat=self.ax[0].scatter(x,y,s=self.area, c=self.colors,alpha=self.alpha)
        self.ax[0].set_xlim((-1.3,1.3))
        self.ax[0].set_ylim((-1.3,1.3))
        for i in range(len(self.x)):
            self.labels[i].remove()
            self.labels[i]=self.ax[0].annotate(i, (x[i], y[i]))
            self.labels2[i].remove()
            self.labels2[i]=self.ax[1].annotate(i, (self.x[i], self.y[i]))
        self.x=x
        self.y=y