def diagonal(rect: Rectangle,
             to: tuple,
             step: float = .1,
             interval: float = .001):
    end = to[0]

    if to[0] < rect.get_x():
        step *= -1
        end += step
    else:
        end += step

    x_range = np.arange(rect.get_x(), end, step)

    slope = (to[1] - rect.get_y()) / (to[0] - rect.get_x())

    start_y = rect.get_y()
    start_x = rect.get_x()

    figure = rect.figure

    for x in x_range:
        rect.set_xy((x, slope * (x - start_x) + start_y))
        figure.canvas.draw()
        figure.canvas.flush_events()
        plt.pause(interval)
Exemple #2
0
class click_xrange:
   '''An interactive xrange selector.  Given an axis and a starting
   x0 location, draw a full-height rectange that follows the mouise.
   Similar to click_window, but more appropriate for selecting out
   an x-range.'''

   def __init__(self, ax, x0):
      self.ax = ax
      self.x0 = x0
      y0,y1 = ax.get_ybound()
      self.rect = Rectangle((x0,y0), width=0, height=(y1-y0), alpha=0.1)
      ax.add_artist(self.rect)

   def connect(self):
      self.cidmotion = self.rect.figure.canvas.mpl_connect(
            'motion_notify_event', self.on_motion)

   def on_motion(self, event):
      # Have we left the axes?
      if event.inaxes != self.rect.axes:  return

      self.rect.set_width(event.xdata - self.x0)
      self.ax.figure.canvas.draw()

   def close(self):
      self.rect.figure.canvas.mpl_disconnect(self.cidmotion)
      self.rect.remove()
      self.ax.figure.canvas.draw()
      return(self.x0, self.rect.get_x()+self.rect.get_width())
Exemple #3
0
class click_xrange:
    '''An interactive xrange selector.  Given an axis and a starting
   x0 location, draw a full-height rectange that follows the mouise.
   Similar to click_window, but more appropriate for selecting out
   an x-range.'''
    def __init__(self, ax, x0):
        self.ax = ax
        self.x0 = x0
        y0, y1 = ax.get_ybound()
        self.rect = Rectangle((x0, y0), width=0, height=(y1 - y0), alpha=0.1)
        ax.add_artist(self.rect)

    def connect(self):
        self.cidmotion = self.rect.figure.canvas.mpl_connect(
            'motion_notify_event', self.on_motion)

    def on_motion(self, event):
        # Have we left the axes?
        if event.inaxes != self.rect.axes: return

        self.rect.set_width(event.xdata - self.x0)
        self.ax.figure.canvas.draw()

    def close(self):
        self.rect.figure.canvas.mpl_disconnect(self.cidmotion)
        self.rect.remove()
        self.ax.figure.canvas.draw()
        return (self.x0, self.rect.get_x() + self.rect.get_width())
def show_image(image, labels):
    rect = Rectangle((labels[0], labels[1]),
                     labels[2] - labels[0],
                     labels[3] - labels[1],
                     edgecolor='r',
                     fill=False)
    plt.imshow(image)
    gca = plt.gca()
    gca.add_patch(rect)
    return rect.get_x(), rect.get_y(), rect.get_width(), rect.get_height()
def right_left(rect: Rectangle,
               to: tuple,
               step: float = .1,
               interval: float = .001):
    new_step = step
    if to[0] < rect.get_x():
        new_step *= -1

    figure = rect.figure

    right = rect.get_x() < to[0]

    while (rect.get_x() - to[0] <= step / 2) if right else (
            rect.get_x() - to[0] >= step / 2):
        rect.set_x(rect.get_x() + new_step)

        figure.canvas.draw()
        figure.canvas.flush_events()
        plt.pause(interval)
Exemple #6
0
    class ChipSelector(object):

        def __init__(self, image_name, chip, **kwargs):
            plt.ioff()
            self.chip = chip
            fig, ax = plt.subplots()
            im, extent = create_image_for_viewer(image_name, chip)
            plt.imshow(np.flipud(im), extent=extent)
            xy = (chip['coordinates']['xmin'], chip['coordinates']['ymin'])
            width = chip['coordinates']['xmax'] - chip['coordinates']['xmin']
            height = chip['coordinates']['ymax'] - chip['coordinates']['ymin']

            from matplotlib.patches import Rectangle
            self.__rect = Rectangle(xy, width, height, fill=False, color='r')
            ax.add_patch(self.__rect)
            drr = DraggableResizeableRectangle(self.__rect, fixed_aspect_ratio=False)
            drr.connect()

            plt.subplots_adjust(bottom=0.2)

            ax.axes.get_xaxis().set_visible(False)
            ax.axes.get_yaxis().set_visible(False)
            ax_reject = plt.axes([0.2, 0.05, 0.1, 0.075])
            ax_accept = plt.axes([0.75, 0.05, 0.1, 0.075])
            self._b_reject = Button(ax_reject, 'Reject')
            self._b_accept = Button(ax_accept, "Accept")
            self._b_reject.on_clicked(self.answer)
            self._b_accept.on_clicked(self.answer)
            plt.show()

        def answer(self, event):
            if event.inaxes.texts[0]._text == 'Accept':
                self.chip = {'name': self.chip['name'],
                             'coordinates': {'xmin': self.__rect.get_x(),
                                             'ymin': self.__rect.get_y(),
                                             'xmax': self.__rect.get_x()+self.__rect.get_width(),
                                             'ymax': self.__rect.get_y()+self.__rect.get_height()}
                             }
            else:
                self.chip = None
            plt.close()
Exemple #7
0
    def draw_label(self, bar: Rectangle, fmt: str) -> plt.Annotation:
        w, h = bar.get_width(), bar.get_height()

        x = bar.get_x() + w * 0.5
        y = bar.get_y() + h

        label = self._ax.annotate(format(h, fmt), (x, y),
                                  xytext=(0.0, 4.0), textcoords='offset points',
                                  ha='center', va='bottom')
        label.draggable()
        label.set_rotation(self.label_rot)

        return label
def move_to(rect: Rectangle,
            to: tuple,
            step: float = .1,
            interval: float = .001,
            show_animation: bool = True):
    if show_animation:
        if abs(rect.get_x() - to[0]) < .001:
            if not abs(rect.get_y() - to[1]) < .001:
                up_down(rect, to, step, interval)
            else:
                return
        elif abs(rect.get_y() - to[1]) < .001:
            right_left(rect, to, step, interval)
        else:
            diagonal(rect, to, step, interval)

    figure = rect.figure
    rect.set_xy(to)
    figure.canvas.draw()
    figure.canvas.flush_events()
    plt.pause(interval)
Exemple #9
0
class ZoneInteret:
    @staticmethod
    def verifier_presence_fichier_ini():
        return os.path.isfile('./zi/param.ini')

    @staticmethod
    def supprimer_ZI(window):
        if os.path.isfile('./zi/param.ini'):
            try:
                os.remove("./zi/param.ini")
                os.remove("./zi/image_modele.png")
                os.remove("./zi/image_zone_interet.png")
                QMessageBox.information(
                    window, "Information",
                    "Supprimer la Zone d'intérêt avec succès", QMessageBox.Ok)
            except OSError:
                QMessageBox.warning(
                    window, "Erreur",
                    "Impossible de supprimer les fichiers dans le repertoire /zi",
                    QMessageBox.Ok)
        else:
            QMessageBox.warning(
                window, "Erreur",
                "Impossible de trouver les fichiers dans le repertoire /zi",
                QMessageBox.Ok)

    # Initialise les variables nécessaires à l'affichage de l'image et aux événements
    def __init__(self, video):

        self.flag = False
        self.get_one_image_from_video(video)

        # On se sert de l'image extraite précédemment
        self.img = mpimg.imread('./zi/image_modele.png')

        # On initialise le titre de la fenêtre
        fig = plt.figure(1)
        fig.canvas.set_window_title("Zone Interet")

        # On récupère les infos des axes
        self.ax = plt.gca()

        # On initialise le futur rectangle dessiné (non rempli aux bordures rouges)
        self.rect = Rectangle((0, 0), 1, 1, fill=False, edgecolor="red")

        # Initialisation des points du rectangle
        self.x0 = None
        self.y0 = None
        self.x1 = None
        self.y1 = None
        self.ax.add_patch(self.rect)

        # Liaison des événements
        self.ax.figure.canvas.mpl_connect('button_press_event',
                                          self.on_mouseclick_press)
        self.ax.figure.canvas.mpl_connect('button_release_event',
                                          self.on_mouseclick_release)
        self.ax.figure.canvas.mpl_connect('key_press_event',
                                          self.on_keyboard_press)

        # Affichage de l'image dans la fenêtre
        self.imgplot = plt.imshow(self.img)

        self.show_window()

    # Un click gauche -> sauvegarde des coordonnées du pointeur
    def on_mouseclick_press(self, event):
        self.x0 = event.xdata
        self.y0 = event.ydata

    # Click gauche relâché -> dessin du rectangle
    def on_mouseclick_release(self, event):
        self.x1 = event.xdata
        self.y1 = event.ydata
        self.rect.set_width(self.x1 - self.x0)
        self.rect.set_height(self.y1 - self.y0)
        self.rect.set_xy((self.x0, self.y0))
        self.ax.figure.canvas.draw()

    # Si la touche "enter" est appuyée, on sauvegarde la zone d'intérêt
    def on_keyboard_press(self, event):
        if event.key == 'enter':
            self.flag = True
            with open("./zi/param.ini", "w") as file:
                file.write(str(int(self.rect.get_x())) + ",")
                file.write(str(int(self.rect.get_y())) + ",")
                file.write(str(int(self.rect.get_width())) + ",")
                file.write(str(int(self.rect.get_height())))

            # On cache les axes avant d'enregistrer l'image modele avec la zone d'interet
            self.ax.get_xaxis().set_visible(False)
            self.ax.get_yaxis().set_visible(False)
            plt.title("Zone interet")
            plt.savefig("./zi/image_zone_interet.png")
            plt.close()

    def show_window(self):
        plt.title(
            "Selectionner la zone interet avec la souris. Appuyez sur entrer pour valider."
        )
        plt.show()

    # extrait une image de la vidéo selectionnée
    def get_one_image_from_video(self, video):
        video_capture = cv2.VideoCapture(video)
        # TODO : bien récupérer la dernière frame
        nb_frame = video_capture.get(cv2.CAP_PROP_FRAME_COUNT)
        video_capture.set(cv2.CAP_PROP_FRAME_COUNT, int(nb_frame - 1))
        success, self.image = video_capture.read()
        print(success)
        cv2.imwrite("zi/image_modele.png", self.image)
class WindowSelectionRectangle(object):
    def __init__(self, event, axis, on_window_selection_callback):
        self.axis = axis
        if event.inaxes != self.axis:
            return
        # Store the axes it has been initialized in.
        self.axes = event.inaxes
        ymin, ymax = self.axes.get_ylim()
        self.min_x = event.xdata
        self.intial_selection_active = True
        self.rect = Rectangle((event.xdata, ymin), 0, ymax - ymin, color="0.3",
            alpha=0.5, edgecolor="0.5")
        self.axes.add_patch(self.rect)
        # Get the canvas.
        self.canvas = self.rect.figure.canvas

        # Use blittig for fast animations.
        self.rect.set_animated(True)
        self.background = self.canvas.copy_from_bbox(self.rect.axes.bbox)

        self._connect()

        self.on_window_selection_callback = on_window_selection_callback

    #def __del__(self):
        #"""
        #Disconnect the events upon deallocating.
        #"""
        #self.canvas.mpl_disconnect(self.conn_button_press)
        #self.canvas.mpl_disconnect(self.conn_button_release)
        #self.canvas.mpl_disconnect(self.conn_mouse_motion)

    def _connect(self):
        """
        Connect to the necessary events.
        """
        self.conn_button_press = self.rect.figure.canvas.mpl_connect(
            'button_press_event', self.on_button_press)
        self.conn_button_release = self.rect.figure.canvas.mpl_connect(
            'button_release_event', self.on_button_release)
        self.conn_mouse_motion = self.rect.figure.canvas.mpl_connect(
            'motion_notify_event', self.on_mouse_motion)

    def on_button_press(self, event):
        pass

    def on_button_release(self, event):
        if event.inaxes != self.axis:
            return

        if event.button != 1:
            return
        # turn off the rect animation property and reset the background
        self.rect.set_animated(False)
        self.background = None

        self.intial_selection_active = False
        self.canvas.draw()

        x = self.rect.get_x()
        width = self.rect.get_width()

        if width < 0:
            x = x + width
            width = abs(width)

        self.on_window_selection_callback(x, width, self.axis)

    def on_mouse_motion(self, event):
        if event.button != 1 or \
                self.intial_selection_active is not True:
            return
        if event.xdata is not None:
            self.rect.set_width(event.xdata - self.min_x)

        # restore the background region
        self.canvas.restore_region(self.background)
        # redraw just the current rectangle
        self.axes.draw_artist(self.rect)
        # blit just the redrawn area
        self.canvas.blit(self.axes.bbox)
Exemple #11
0
class RectangleCreationOnClick(PanOnClick):
    """Class providing rectangle creation patch to a matplotlib Figure.
    Left button to create a rectangle with the desired width.
    """
    def __init__(self, figure=None, scale_factor=1.1):
        """Initializer
        :param Figure figure: The matplotlib figure to attach the behavior to.
        :param float scale_factor: The scale factor to apply on wheel event.
        """
        super(RectangleCreationOnClick, self).__init__(figure,
                                                       scale_factor=1.1)
        self._add_connection_rectangle_creation('button_press_event',
                                                self._on_mouse_press_creation)
        self._add_connection_rectangle_creation(
            'button_release_event', self._on_mouse_release_creation)
        self._add_connection_rectangle_creation('motion_notify_event',
                                                self._on_mouse_motion_creation)

        self._pressed_button = None  # To store active button
        self._axes = None  # To store x and y axes concerned by interaction
        self._event = None  # To store reference event during interaction

    def update_current_rectangle(self, ax, curr_event, past_event):
        """Create the rectangle with the width stablished from initial click
        and current position of the mouse
        :param matplotlib.axes._subplots.AxesSubplot ax: axis where the rectangle is gonna be drawed or it's width modified
        :param matplotlib.backend_bases.MouseEvent curr_event: data from the current position of the mouse of the motion event
        :param matplotlib.backend_bases.MouseEvent past_event: data from the initial position where a click has been done
        """
        try:
            #If current click does not goes to the limit (which would cause to return None)
            if curr_event.xdata != None:

                yAxis_limits = self._rectangle_yAxis_limits
                #If rectangle creation goes from left to right
                if curr_event.xdata < past_event.xdata:

                    #If the rectangle has not been created, create it
                    if not isinstance(self._rectangle_interactions, Rectangle):

                        self._rectangle_interactions = Rectangle(
                            [curr_event.xdata, yAxis_limits[0] * 0.9],
                            abs(past_event.xdata - curr_event.xdata),
                            yAxis_limits[1] * 1.1,
                            color='black',
                            alpha=0.3)

                        self._rectangleStats.direction = -1
                        ax.add_patch(self._rectangle_interactions)
                    #If it has been created and goes from left to right, update width
                    elif isinstance(self._rectangle_interactions, Rectangle
                                    ) and self._rectangleStats.direction == -1:

                        self._rectangle_interactions.set_height(
                            abs(yAxis_limits[0] * 0.9 - yAxis_limits[1] * 1.1))
                        self._rectangle_interactions.set_y(yAxis_limits[0] *
                                                           0.9)
                        self._rectangle_interactions.set_x(curr_event.xdata)
                        self._rectangle_interactions.set_width(
                            abs(past_event.xdata - curr_event.xdata))

                    #If it has been created but changed direction from right-left to left-right, update width
                    elif self._rectangleStats.direction == 1:

                        self._rectangle_interactions.set_height(
                            abs(yAxis_limits[0] * 0.9 - yAxis_limits[1] * 1.1))
                        self._rectangle_interactions.set_y(yAxis_limits[0] *
                                                           0.9)
                        self._rectangleStats.direction = -1
                        self._rectangle_interactions.set_x(curr_event.xdata)
                        self._rectangle_interactions.set_width(
                            abs(past_event.xdata - curr_event.xdata))

                #If rectangle creation goes from right to left
                else:
                    #If the rectangle has not been created, create it
                    if not isinstance(self._rectangle_interactions, Rectangle):
                        self._rectangle_interactions = Rectangle(
                            [past_event.xdata, yAxis_limits[0] * 0.9],
                            abs(past_event.xdata - curr_event.xdata),
                            yAxis_limits[1] * 1.1,
                            color='black',
                            alpha=0.3)

                        self._rectangleStats.direction = 1
                        ax.add_patch(self._rectangle_interactions)
                    #If it has been created and goes from left to right, update width
                    elif isinstance(
                            self._rectangle_interactions,
                            Rectangle) and self._rectangleStats.direction == 1:

                        self._rectangle_interactions.set_height(
                            abs(yAxis_limits[0] * 0.9 - yAxis_limits[1] * 1.1))
                        self._rectangle_interactions.set_y(yAxis_limits[0] *
                                                           0.9)
                        self._rectangle_interactions.set_x(past_event.xdata)
                        self._rectangle_interactions.set_width(
                            abs(past_event.xdata - curr_event.xdata))

                    #If it has been created but changed direction from right-left to left-right, update width
                    elif self._rectangleStats.direction == -1:

                        self._rectangle_interactions.set_height(
                            abs(yAxis_limits[0] * 0.9 - yAxis_limits[1] * 1.1))
                        self._rectangle_interactions.set_y(yAxis_limits[0] *
                                                           0.9)
                        self._rectangleStats.direction = 1
                        self._rectangle_interactions.set_x(past_event.xdata)
                        self._rectangle_interactions.set_width(
                            abs(past_event.xdata - curr_event.xdata))

        except Exception as e:
            pass

    def _rectangle_creation(self, event):
        """Execute function based on the name of it"""
        if event.name == 'button_press_event':  # begin creation
            self._original_event = event
            self._event = event

        elif event.name == 'button_release_event':  # end creation
            self._event = None
            pub.sendMessage('rangeData',
                            iw=self._rectangle_interactions.get_x(),
                            ew=self._rectangle_interactions.get_bbox().x1)

        elif event.name == 'motion_notify_event':  # set range for creation
            if self._event is None:
                return

            if event.x != self._event.x:
                for ax in self._axes[0]:
                    self.update_current_rectangle(ax, event,
                                                  self._original_event)

            if event.x != self._event.x:
                self._draw()

            self._event = event

    def _on_mouse_press_creation(self, event):
        """Set axes values based on point selected"""
        if self._pressed_button is not None:
            return  # Discard event if a button is already pressed

        x_axes = set()
        y_axes = set()
        for ax in self.figure.axes:
            #Simmilar to event.inaxis = axis
            if ax.contains(event)[0]:
                x_axes.add(ax)
                y_axes.add(ax)
                self._axes = x_axes, y_axes
                self._pressed_button = event.button
                if self._pressed_button == 1:
                    self._rectangle_creation(event)

    def _on_mouse_release_creation(self, event):
        if self._pressed_button == 1:
            self._rectangle_creation(event)

        self._pressed_button = None

    def _on_mouse_motion_creation(self, event):
        if self._pressed_button == 1:
            self._rectangle_creation(event)
Exemple #12
0
class cropWidget(QWidget):
    cropDoneSignal = pyqtSignal(cropInfo)

    def __init__(self, currentFileLocation = ''):
        super().__init__()
        self.filename = currentFileLocation
        self.cropInfo = cropInfo()
        self.isPressed = False
        self.cropCheckWidget = cropCheckWidget(self.cropInfo)
        self.initUI()

    def initUI(self):
        self.hbox = QHBoxLayout()

        self.fig = plt.Figure()
        self.canvas = FigureCanvas(self.fig)
        self.ax = self.fig.add_subplot(111)

        self.canvas.mpl_connect("button_press_event", self.on_press)
        self.canvas.mpl_connect("motion_notify_event", self.on_move)
        self.canvas.mpl_connect("button_release_event", self.on_release)

        self.hbox.addWidget(self.canvas)
        self.setLayout(self.hbox)

        if (self.filename !=''):
            self.data = fits.open(Path(self.filename))[0].data
            zimshow(self.ax, self.data)
        self.canvas.draw()

    def setFileName(self, fileName):
        self.filename = fileName
        self.data = fits.open(Path(self.filename))[0].data
        zimshow(self.ax, self.data)
        self.canvas.draw()

    def on_press(self, event):
        if not event.inaxes : return
        if event.inaxes != self.ax: return
        self.rect = Rectangle((0, 0), 1, 1, alpha=0.5)
        self.ax.add_patch(self.rect)
        self.x0 = event.xdata
        self.y0 = event.ydata
        self.isPressed = True

    def on_move(self, event):
        if not event.inaxes : return
        if event.inaxes != self.ax: return
        if not self.isPressed : return

        self.x1 = event.xdata
        self.y1 = event.ydata
        self.rect.set_width(self.x1 - self.x0)
        self.rect.set_height(self.y1 - self.y0)
        self.rect.set_xy((self.x0, self.y0))
        self.ax.figure.canvas.draw()

    def on_release(self, event):
        if not event.inaxes : return
        if event.inaxes != self.ax: return
        if not self.isPressed: return
        x = int(self.rect.get_x())
        y = int(self.rect.get_y())
        width = int(self.rect.get_width())
        height = int(self.rect.get_height())

        x0 = x
        x1 = x + width
        y0 = y
        y1 = y + height
        if (x0 > x1):
            x0, x1 = x1, x0
        if (y0 > y1):
            y0, y1 = y1, y0

        self.cropInfo.x0 = x0
        self.cropInfo.x1 = x1
        self.cropInfo.y0 = y0
        self.cropInfo.y1 = y1
        self.cropInfo.filename = self.filename
        self.cropDoneSignal.emit(self.cropInfo)
        self.rect.remove()
        self.ax.figure.canvas.draw()
        self.isPressed = False
        self.cropCheckWidget.setCropInfo(self.cropInfo)
        self.cropCheckWidget.show()
        self.cropCheckWidget.raise_()
class EditableRectangle:

    _angle = 0

    def __init__(self, ax):
        self.ax = ax

        # Set up main rectangle
        self.rect = Rectangle((0, 0), 0, 0, visible=False, transform=None, picker=True)
        self.ax.add_patch(self.rect)

        # Set up anchors
        self.anchors = []
        for i in range(len(RECTANGLE_ANCHOR_LOCS)):
            anchor = Rectangle((0, 0), ANCHOR_SIZE, ANCHOR_SIZE, visible=False, transform=None, facecolor='red', picker=True)
            self.anchors.append(anchor)
            self.ax.add_patch(anchor)

        self.press = None
        self.mode = None
        self.connect()

    def connect(self):
        self.cid_press = self.ax.figure.canvas.mpl_connect('button_press_event', self.on_press)
        self.cid_release = self.ax.figure.canvas.mpl_connect('button_release_event', self.on_release)
        self.cid_motion = self.ax.figure.canvas.mpl_connect('motion_notify_event', self.on_motion)
        self.cid_pick = self.ax.figure.canvas.mpl_connect('pick_event', self.on_pick)

    def on_pick(self, event):
        if event.artist in self.anchors:
            if event.mouseevent.key == 'r':
                self.mode = 'anchor-rotate'
                self.center_x = self.x0 + self.width * 0.5
                self.center_y = self.y0 + self.height * 0.5
                self.angle_start = self.angle
                self.angle_drag_start = np.degrees(np.arctan2(event.mouseevent.y - self.center_y, event.mouseevent.x - self.center_x))
                print(self.angle_start)
            else:
                self.mode = 'anchor-drag'
                anchor_index = self.anchors.index(event.artist)
                self.active_anchor_index = anchor_index
            self.press = True
        elif event.artist is self.rect:
            self.mode = 'rectangle-drag'
            self.drag_start_x0 = self.x0
            self.drag_start_y0 = self.y0
            self.drag_start_x = event.mouseevent.x
            self.drag_start_y = event.mouseevent.y
            self.press = True

    def on_press(self, event):

        if event.inaxes != self.ax:
            return

        if self.mode == 'create':
            self.x0 = event.x
            self.y0 = event.y
            self.rect.set_visible(True)
            self.press = True

        # contains, attrd = self.rect.contains(event)
        # if not contains: return
        # print('event contains', self.rect.xy)
        # x0, y0 = self.rect.xy
        # self.press = x0, y0, event.x, event.y

    @property
    def angle(self):
        return self._angle

    @angle.setter
    def angle(self, value):
        self._angle = value
        self.rect.angle = value
        self.rect._update_patch_transform()

    @property
    def x0(self):
        return self.rect.get_x()

    @x0.setter
    def x0(self, value):
        self.rect.set_x(value)

    @property
    def y0(self):
        return self.rect.get_y()

    @y0.setter
    def y0(self, value):
        self.rect.set_y(value)

    @property
    def width(self):
        return self.rect.get_width()

    @width.setter
    def width(self, value):
        self.rect.set_width(value)

    @property
    def height(self):
        return self.rect.get_height()

    @height.setter
    def height(self, value):
        self.rect.set_height(value)

    def on_motion(self, event):

        if self.press is None:
            return

        if event.inaxes != self.ax:
            return

        if self.mode == 'create':

            self.width = event.x - self.x0
            self.height = event.y - self.y0

            self.rect.figure.canvas.draw()

        elif self.mode == 'rectangle-drag':

            self.x0 = self.drag_start_x0 + event.x - self.drag_start_x
            self.y0 = self.drag_start_y0 + event.y - self.drag_start_y

            self.update_anchors()

            self.rect.figure.canvas.draw()

        elif self.mode == 'anchor-drag':

            px, py = RECTANGLE_ANCHOR_LOCS[self.active_anchor_index]

            if px == -1:
                self.x0, self.width = event.x, self.x0 + self.width - event.x
            elif px == 1:
                self.width = event.x - self.x0

            if py == -1:
                self.y0, self.height = event.y, self.y0 + self.height - event.y
            elif py == 1:
                self.height = event.y - self.y0

            self.update_anchors()

            self.rect.figure.canvas.draw()

        elif self.mode == 'anchor-rotate':

            angle_current = np.degrees(np.arctan2(event.y - self.center_y, event.x - self.center_x))

            self.angle = self.angle_start + (angle_current - self.angle_drag_start)

            self.update_anchors()

            self.rect.figure.canvas.draw()

    def on_release(self, event):
        if self.mode == 'create':
            self.update_anchors()
            self.set_anchor_visibility(True)
        self.press = None
        self.mode = None
        self.rect.figure.canvas.draw()

    def set_anchor_visibility(self, visible):
        for anchor in self.anchors:
            anchor.set_visible(visible)

    def update_anchors(self):
        for anchor, (dx, dy) in zip(self.anchors, RECTANGLE_ANCHOR_LOCS):
            xc = self.x0 + 0.5 * self.width
            yc = self.y0 + 0.5 * self.height
            dx = 0.5 * (dx * self.width - ANCHOR_SIZE)
            dy = 0.5 * (dy * self.height - ANCHOR_SIZE)
            dxr = dx * np.cos(np.radians(self.angle)) - dy * np.sin(np.radians(self.angle))
            dyr = dx * np.sin(np.radians(self.angle)) + dy * np.cos(np.radians(self.angle))
            anchor.set_xy((xc + dxr, yc + dyr))

    def disconnect(self):
        self.ax.figure.canvas.mpl_disconnect(self.cid_press)
        self.ax.figure.canvas.mpl_disconnect(self.cid_release)
        self.ax.figure.canvas.mpl_disconnect(self.cid_motion)
Exemple #14
0
def show_image(im: Union[np.ndarray, Tensor],
               axis: plt.Axes = None,
               fig: plt.Figure = None,
               title: Optional[str] = None,
               color_map: str = "inferno",
               stack_depth: int = 0) -> Optional[plt.Figure]:
    """Plots a given image onto an axis. The repeated invocation of this function will cause figure plot overlap.

    If `im` is 2D and the length of second dimension are 4 or 5, it will be viewed as bounding box data (x0, y0, w, h,
    <label>).

    ```python
    boxes = np.array([[0, 0, 10, 20, "apple"],
                      [10, 20, 30, 50, "dog"],
                      [40, 70, 200, 200, "cat"],
                      [0, 0, 0, 0, "not_shown"],
                      [0, 0, -10, -20, "not_shown2"]])

    img = np.zeros((150, 150))
    fig, axis = plt.subplots(1, 1)
    fe.util.show_image(img, fig=fig, axis=axis) # need to plot image first
    fe.util.show_image(boxes, fig=fig, axis=axis)
    ```

    Users can also directly plot text

    ```python
    fig, axis = plt.subplots(1, 1)
    fe.util.show_image("apple", fig=fig, axis=axis)
    ```

    Args:
        axis: The matplotlib axis to plot on, or None for a new plot.
        fig: A reference to the figure to plot on, or None if new plot.
        im: The image (width X height) / bounding box / text to display.
        title: A title for the image.
        color_map: Which colormap to use for greyscale images.
        stack_depth: Multiple images can be drawn onto the same axis. When stack depth is greater than zero, the `im`
            will be alpha blended on top of a given axis.

    Returns:
        plotted figure. It will be the same object as user have provided in the argument.
    """
    if axis is None:
        fig, axis = plt.subplots(1, 1)
    axis.axis('off')
    # Compute width of axis for text font size
    bbox = axis.get_window_extent().transformed(fig.dpi_scale_trans.inverted())
    width, height = bbox.width * fig.dpi, bbox.height * fig.dpi
    space = min(width, height)
    if not hasattr(im, 'shape') or len(im.shape) < 2:
        # text data
        im = to_number(im)
        if hasattr(im, 'shape') and len(im.shape) == 1:
            im = im[0]
        im = im.item()
        if isinstance(im, bytes):
            im = im.decode('utf8')
        text = "{}".format(im)
        axis.text(0.5,
                  0.5,
                  im,
                  ha='center',
                  transform=axis.transAxes,
                  va='center',
                  wrap=False,
                  family='monospace',
                  fontsize=min(45, space // len(text)))
    elif len(im.shape) == 2 and (im.shape[1] == 4 or im.shape[1] == 5):
        # Bounding Box Data. Should be (x0, y0, w, h, <label>)
        boxes = []
        im = to_number(im)
        color = ["m", "r", "c", "g", "y", "b"][stack_depth % 6]
        for box in im:
            # Unpack the box, which may or may not have a label
            x0 = float(box[0])
            y0 = float(box[1])
            width = float(box[2])
            height = float(box[3])
            label = None if len(box) < 5 else str(box[4])

            # Don't draw empty boxes, or invalid box
            if width <= 0 or height <= 0:
                continue
            r = Rectangle((x0, y0),
                          width=width,
                          height=height,
                          fill=False,
                          edgecolor=color,
                          linewidth=3)
            boxes.append(r)
            if label:
                axis.text(r.get_x() + 3,
                          r.get_y() + 3,
                          label,
                          ha='left',
                          va='top',
                          color=color,
                          fontsize=max(8, min(14, width // len(label))),
                          fontweight='bold',
                          family='monospace')
        pc = PatchCollection(boxes, match_original=True)
        axis.add_collection(pc)
    else:
        if isinstance(im, torch.Tensor) and len(im.shape) > 2:
            # Move channel first to channel last
            channels = list(range(len(im.shape)))
            channels.append(channels.pop(0))
            im = im.permute(*channels)
        # image data
        im = to_number(im)
        im_max = np.max(im)
        im_min = np.min(im)
        if np.issubdtype(im.dtype, np.integer):
            # im is already in int format
            im = im.astype(np.uint8)
        elif 0 <= im_min <= im_max <= 1:  # im is [0,1]
            im = (im * 255).astype(np.uint8)
        elif -0.5 <= im_min < 0 < im_max <= 0.5:  # im is [-0.5, 0.5]
            im = ((im + 0.5) * 255).astype(np.uint8)
        elif -1 <= im_min < 0 < im_max <= 1:  # im is [-1, 1]
            im = ((im + 1) * 127.5).astype(np.uint8)
        else:  # im is in some arbitrary range, probably due to the Normalize Op
            ma = abs(
                np.max(im,
                       axis=tuple([i for i in range(len(im.shape) - 1)])
                       if len(im.shape) > 2 else None))
            mi = abs(
                np.min(im,
                       axis=tuple([i for i in range(len(im.shape) - 1)])
                       if len(im.shape) > 2 else None))
            im = (((im + mi) / (ma + mi)) * 255).astype(np.uint8)
        # matplotlib doesn't support (x,y,1) images, so convert them to (x,y)
        if len(im.shape) == 3 and im.shape[2] == 1:
            im = np.reshape(im, (im.shape[0], im.shape[1]))
        alpha = 1 if stack_depth == 0 else 0.3
        if len(im.shape) == 2:
            axis.imshow(im, cmap=plt.get_cmap(name=color_map), alpha=alpha)
        else:
            axis.imshow(im, alpha=alpha)
    if title is not None:
        axis.set_title(title,
                       fontsize=min(20, 1 + width // len(title)),
                       family='monospace')
    return fig
Exemple #15
0
def drawHeatPlot(comparisonDescription,
                 detector,
                 layer,
                 dof,
                 outputDir,
                 layerFillList,
                 GeometryDict,
                 diffColorRange,
                 drawNames=False):
    fig = plt.figure(figsize=(11.6929134, 8.26771654),
                     subplotpars=SubplotParams(wspace=0.35,
                                               left=0.1,
                                               bottom=0.1,
                                               right=0.98))
    ax = fig.add_subplot(111, axisbg='#E6E6E6')
    ax.set_aspect("equal", adjustable="box")
    ax.set_title(comparisonDescription +
                 "\n Detector: %s,   Layer: %s,   Degree of Freedom: %s" %
                 (detector, layer, dof))
    ax.set_xlabel(
        "A side $\qquad \qquad \qquad \qquad \qquad$ x (cm) $\qquad \qquad \qquad \qquad \qquad$ C side"
    )
    #    ax.set_xlabel("x (cm)")
    ax.set_ylabel("y (cm)")
    ax.grid(True, linestyle='-', linewidth=1.5, alpha=0.1)
    # put grid behind polygons
    ax.set_axisbelow(True)
    # reverse x axis to match LHCb coodrinates from VELO perspective
    ax.set_xlim(ax.get_xlim()[::-1])

    patches = []
    # values will be overwritten, we just need a numpy array at least as big as the fill list
    colorArray = np.array([x for x in range(len(layerFillList))],
                          dtype=np.float64)

    stereoRotation = 0
    if layer.find("U") != -1: stereoRotation = -5
    if layer.find("V") != -1: stereoRotation = 5

    logging.debug(
        "Building list of alignment elements and color array of corresponding alignment parameters"
    )
    for i, (name, unused, matrix) in enumerate(layerFillList):
        _shape = lambda j: GeometryDict[name][
            j]  # (xy, width, height, rotateY, zorder)
        # nb: with x axis reversed, xy of rectangle is lower right point
        poly = Rectangle(_shape(0), _shape(1), _shape(2), zorder=_shape(4))
        if stereoRotation != 0:
            rotate = mpl.transforms.Affine2D().rotate_deg_around(
                poly.get_x() + _shape(1) * 0.5, _shape(3), stereoRotation)
            poly.set_transform(rotate)
        patches.append(poly)
        colorArray[i] = getattr(matrix, dof)

        # element labels
        if drawNames:
            splitName = name.split("/")
            if detector == "TT":
                elementName = "\n".join(splitName[-3:])
                labelRotation = 0
                textSize = 4
            elif detector == "IT":
                elementName = "\n".join(splitName[-3::2])
                labelRotation = 90
                textSize = 8
            elif detector == "OT":
                elementName = "/".join(splitName[-2:])
                labelRotation = 90
                textSize = 10
            smallAngleShift = 0
            if stereoRotation != 0 and detector != "IT":
                tan = 0.08748866
                smallAngleShift = -(poly.get_y() + _shape(2) *
                                    0.5) * tan * cmp(stereoRotation, 0)
            elementLabel = plt.text(poly.get_x() + _shape(1) * 0.5 +
                                    smallAngleShift,
                                    poly.get_y() + _shape(2) * 0.5,
                                    elementName,
                                    verticalalignment='center',
                                    horizontalalignment='center',
                                    rotation=labelRotation - stereoRotation,
                                    size=textSize)

    polyCollection = PatchCollection(patches, cmap=mpl.cm.RdBu)
    polyCollection.set_array(colorArray)
    polyCollection.set_clim([-diffColorRange, diffColorRange])
    ax.add_collection(polyCollection)

    cbar = plt.colorbar(polyCollection)
    if dof.startswith("T"):
        cbar.set_label("%s (mm)" % dof)
    elif dof.startswith("R"):
        cbar.set_label("%s (mrad)" % dof)

    plt.axis('equal')

    # this is busted for stereo layers, just putting the labels in the x axis title
    #     if detector == "IT":
    #         ax.text(ax.get_xlim()[0], 0, '$\quad$A side', horizontalalignment='left', verticalalignment='center')
    #         ax.text(ax.get_xlim()[1], 0, '$\!\!\!$ C side', horizontalalignment='right', verticalalignment='center')
    #     else:
    #         ax.text(ax.get_xlim()[0], 0, 'A side     $\qquad$', horizontalalignment='right', verticalalignment='center')
    #         ax.text(ax.get_xlim()[1], 0, '$\quad$C side', horizontalalignment='left', verticalalignment='center')

    detectorOutputDir = os.path.join(*([outputDir] + [detector]))
    if not os.path.isdir(detectorOutputDir):
        os.makedirs(detectorOutputDir)
    layerName = layer.replace("/", "_")
    fileName = "_".join((detector, layerName, dof)) + ".pdf"
    outputPath = os.path.join(*([detectorOutputDir] + [fileName]))
    print " Writing %s" % outputPath
    fig.savefig(outputPath)
Exemple #16
0
class rectSelector(object):
	
	def __init__(self, fig, df):
		
		self.fig = fig
		self.df = df
		self.cid_on = fig.canvas.mpl_connect('button_press_event', self)
		self.cid_off = fig.canvas.mpl_connect('button_release_event', self)
		self.cid_move = None
		self.feats = {'lim1': [-np.inf, +np.inf], 'lim2': [-np.inf, +np.inf], 'feat1': None, 'feat2': None}
		self.rect = None

	def __call__(self, event):

		if event.inaxes is None: return
		if event.name=='button_press_event':
			self.on_press(event)
		elif event.name=='button_release_event':
			self.on_release(event)

	def on_press(self, event):

		if self.rect is not None:
			self.rect.remove()
		self.feats['lim1'] = [-np.inf, +np.inf]
		self.feats['lim2'] = [-np.inf, +np.inf]

		(feat1, feat2) = [feat for ax, feat in ax2feat.iteritems() if ax==event.inaxes][0]
		
		self.feats = {'feat1': feat1, 'feat2': feat2,
					'lim1': [event.xdata]*2, 'lim2':[event.ydata]*2}

		self.rect = Rectangle([event.xdata, event.ydata], 0., 0., alpha=0.3)
		
		event.inaxes.add_patch(self.rect)

		self.cid_move = self.fig.canvas.mpl_connect('motion_notify_event', self.on_move)

	def on_release(self, event):
		self.fig.canvas.mpl_disconnect(self.cid_move)

	def on_move(self, event):

		if event.inaxes is None:
			return

		# resize the rectangle so the new width tracks the mouse pointer
		self.rect.set_width(event.xdata - self.rect.get_x())
		self.rect.set_height(event.ydata - self.rect.get_y())

		# update the limits for each feature
		self.feats['lim1'] = [self.rect.get_x(), event.xdata]
		if event.xdata<self.rect.get_x():
			self.feats['lim1'] = self.feats['lim1'][::-1]

		self.feats['lim2'] = [self.rect.get_y(), event.ydata]
		if event.ydata<self.rect.get_y():
			self.feats['lim2'] = self.feats['lim2'][::-1]

		self.filterData()

	def filterData(self):

		feat1 = self.feats['feat1']
		feat2 = self.feats['feat2']
		lim1 = self.feats['lim1']
		lim2 = self.feats['lim2']
		df = self.df
		ix = np.vstack((df[feat1]>lim1[0], df[feat1]<lim1[1],
			df[feat2]>lim2[0], df[feat2]<lim2[1])).all(0)
		colors = np.array([colordict[i] for i in df['species']], dtype = 'S4')

		colors[~ix] = 'gray'

		for sc in scatters:
			sc.set_color(colors)

		plt.show()
Exemple #17
0
class StatsPanel(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent, wx.ID_ANY, wx.DefaultPosition,
                          wx.DefaultSize)
        self.ztv_frame = self.GetTopLevelParent()
        self.ztv_frame.primary_image_panel.popup_menu_cursor_modes.append(
            'Stats box')
        self.ztv_frame.primary_image_panel.available_cursor_modes[
            'Stats box'] = {
                'set-to-mode': self.set_cursor_to_stats_box_mode,
                'on_button_press': self.on_button_press,
                'on_motion': self.on_motion,
                'on_button_release': self.on_button_release
            }

        self.stats_info = None

        self.last_string_values = {
            'x0': '',
            'xsize': '',
            'x1': '',
            'y0': '',
            'ysize': '',
            'y1': ''
        }
        self.stats_rect = Rectangle((0, 0),
                                    10,
                                    10,
                                    color='magenta',
                                    fill=False,
                                    zorder=100)
        # use self.stats_rect as where we store/retrieve the x0,y0,x1,y1
        # x0,y0,x1,y1 should be limited to range of 0 to shape-1
        # but, stats should be calculated over e.g. x0:x1+1  (so that have pixels to do stats on even if x0==x1)
        # and, width/height of stats_rect should always be >= 0

        textentry_font = wx.Font(14, wx.FONTFAMILY_MODERN, wx.NORMAL,
                                 wx.FONTWEIGHT_LIGHT, False)

        values_sizer = wx.FlexGridSizer(10, 5, 0, 0)
        values_sizer.SetFlexibleDirection(wx.BOTH)
        values_sizer.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_SPECIFIED)

        values_sizer.AddSpacer((0, 0), 0, wx.EXPAND)

        self.low_static_text = wx.StaticText(self, wx.ID_ANY, u"Low",
                                             wx.DefaultPosition,
                                             wx.DefaultSize, wx.ALIGN_RIGHT)
        self.low_static_text.Wrap(-1)
        values_sizer.Add(self.low_static_text, 0,
                         wx.ALL | wx.ALIGN_CENTER_HORIZONTAL, 0)

        self.low_static_text = wx.StaticText(self, wx.ID_ANY, u"# pix",
                                             wx.DefaultPosition,
                                             wx.DefaultSize, 0)
        self.low_static_text.Wrap(-1)
        values_sizer.Add(self.low_static_text, 0,
                         wx.ALL | wx.ALIGN_CENTER_HORIZONTAL, 0)

        self.high_static_text = wx.StaticText(self, wx.ID_ANY, u"High",
                                              wx.DefaultPosition,
                                              wx.DefaultSize, 0)
        self.high_static_text.Wrap(-1)
        values_sizer.Add(self.high_static_text, 0,
                         wx.ALL | wx.ALIGN_CENTER_HORIZONTAL, 0)

        values_sizer.AddSpacer((0, 0), 0, wx.EXPAND)

        self.x_static_text = wx.StaticText(self, wx.ID_ANY, u"x",
                                           wx.DefaultPosition, wx.DefaultSize,
                                           0)
        self.x_static_text.Wrap(-1)
        values_sizer.Add(self.x_static_text, 0,
                         wx.ALL | wx.ALIGN_CENTER_VERTICAL, 0)

        self.x0_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString,
                                       wx.DefaultPosition, wx.DefaultSize,
                                       wx.TE_PROCESS_ENTER)
        self.x0_textctrl.SetFont(textentry_font)
        values_sizer.Add(self.x0_textctrl, 0, wx.ALL, 2)
        self.x0_textctrl.Bind(wx.EVT_TEXT, self.x0_textctrl_changed)
        self.x0_textctrl.Bind(wx.EVT_TEXT_ENTER, self.x0_textctrl_entered)

        self.xsize_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString,
                                          wx.DefaultPosition, wx.DefaultSize,
                                          wx.TE_PROCESS_ENTER)
        self.xsize_textctrl.SetFont(textentry_font)
        values_sizer.Add(self.xsize_textctrl, 0, wx.ALL, 2)
        self.xsize_textctrl.Bind(wx.EVT_TEXT, self.xsize_textctrl_changed)
        self.xsize_textctrl.Bind(wx.EVT_TEXT_ENTER,
                                 self.xsize_textctrl_entered)

        self.x1_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString,
                                       wx.DefaultPosition, wx.DefaultSize,
                                       wx.TE_PROCESS_ENTER)
        self.x1_textctrl.SetFont(textentry_font)
        values_sizer.Add(self.x1_textctrl, 0, wx.ALL, 2)
        self.x1_textctrl.Bind(wx.EVT_TEXT, self.x1_textctrl_changed)
        self.x1_textctrl.Bind(wx.EVT_TEXT_ENTER, self.x1_textctrl_entered)

        self.npix_static_text = wx.StaticText(self, wx.ID_ANY, u"# pixels",
                                              wx.DefaultPosition,
                                              wx.DefaultSize, 0)
        self.npix_static_text.Wrap(-1)
        values_sizer.Add(self.npix_static_text, 0,
                         wx.ALL | wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_BOTTOM,
                         0)

        self.y_static_text = wx.StaticText(self, wx.ID_ANY, u"y",
                                           wx.DefaultPosition, wx.DefaultSize,
                                           0)
        self.y_static_text.Wrap(-1)
        values_sizer.Add(self.y_static_text, 0,
                         wx.ALL | wx.ALIGN_CENTER_VERTICAL, 0)

        self.y0_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString,
                                       wx.DefaultPosition, wx.DefaultSize,
                                       wx.TE_PROCESS_ENTER)
        self.y0_textctrl.SetFont(textentry_font)
        values_sizer.Add(self.y0_textctrl, 0, wx.ALL, 2)
        self.y0_textctrl.Bind(wx.EVT_TEXT, self.y0_textctrl_changed)
        self.y0_textctrl.Bind(wx.EVT_TEXT_ENTER, self.y0_textctrl_entered)

        self.ysize_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString,
                                          wx.DefaultPosition, wx.DefaultSize,
                                          wx.TE_PROCESS_ENTER)
        self.ysize_textctrl.SetFont(textentry_font)
        values_sizer.Add(self.ysize_textctrl, 0, wx.ALL, 2)
        self.ysize_textctrl.Bind(wx.EVT_TEXT, self.ysize_textctrl_changed)
        self.ysize_textctrl.Bind(wx.EVT_TEXT_ENTER,
                                 self.ysize_textctrl_entered)

        self.y1_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString,
                                       wx.DefaultPosition, wx.DefaultSize,
                                       wx.TE_PROCESS_ENTER)
        self.y1_textctrl.SetFont(textentry_font)
        values_sizer.Add(self.y1_textctrl, 0, wx.ALL, 2)
        self.y1_textctrl.Bind(wx.EVT_TEXT, self.y1_textctrl_changed)
        self.y1_textctrl.Bind(wx.EVT_TEXT_ENTER, self.y1_textctrl_entered)

        self.npix_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString,
                                         wx.DefaultPosition, wx.DefaultSize,
                                         wx.TE_READONLY)
        self.npix_textctrl.SetFont(textentry_font)
        self.npix_textctrl.SetBackgroundColour(
            textctrl_output_only_background_color)
        values_sizer.Add(self.npix_textctrl, 0,
                         wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT, 0)

        values_sizer.AddSpacer((0, 15), 0, wx.EXPAND)
        values_sizer.AddSpacer((0, 0), 0, wx.EXPAND)
        values_sizer.AddSpacer((0, 0), 0, wx.EXPAND)
        values_sizer.AddSpacer((0, 0), 0, wx.EXPAND)
        values_sizer.AddSpacer((0, 0), 0, wx.EXPAND)

        values_sizer.AddSpacer((0, 0), 0, wx.EXPAND)
        self.median_static_text = wx.StaticText(self, wx.ID_ANY, u"Median",
                                                wx.DefaultPosition,
                                                wx.DefaultSize, 0)
        self.median_static_text.Wrap(-1)
        values_sizer.Add(self.median_static_text, 0,
                         wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT, 0)
        self.median_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString,
                                           wx.DefaultPosition, wx.DefaultSize,
                                           wx.TE_READONLY)
        self.median_textctrl.SetFont(textentry_font)
        self.median_textctrl.SetBackgroundColour(
            textctrl_output_only_background_color)
        values_sizer.Add(self.median_textctrl, 0, wx.ALL, 2)
        self.robust_static_text = wx.StaticText(self, wx.ID_ANY, u"Robust",
                                                wx.DefaultPosition,
                                                wx.DefaultSize, 0)
        self.robust_static_text.Wrap(-1)
        values_sizer.Add(self.robust_static_text, 0,
                         wx.ALL | wx.ALIGN_BOTTOM | wx.ALIGN_CENTER_HORIZONTAL,
                         0)
        values_sizer.AddSpacer((0, 0), 0, wx.EXPAND)

        values_sizer.AddSpacer((0, 0), 0, wx.EXPAND)
        self.mean_static_text = wx.StaticText(self, wx.ID_ANY, u"Mean",
                                              wx.DefaultPosition,
                                              wx.DefaultSize, 0)
        self.mean_static_text.Wrap(-1)
        values_sizer.Add(self.mean_static_text, 0,
                         wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT, 0)
        self.mean_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString,
                                         wx.DefaultPosition, wx.DefaultSize,
                                         wx.TE_READONLY)
        self.mean_textctrl.SetFont(textentry_font)
        self.mean_textctrl.SetBackgroundColour(
            textctrl_output_only_background_color)
        values_sizer.Add(self.mean_textctrl, 0, wx.ALL, 2)
        self.robust_mean_textctrl = wx.TextCtrl(self, wx.ID_ANY,
                                                wx.EmptyString,
                                                wx.DefaultPosition,
                                                wx.DefaultSize, wx.TE_READONLY)
        self.robust_mean_textctrl.SetFont(textentry_font)
        self.robust_mean_textctrl.SetBackgroundColour(
            textctrl_output_only_background_color)
        values_sizer.Add(self.robust_mean_textctrl, 0, wx.ALL, 2)
        values_sizer.AddSpacer((0, 0), 0, wx.EXPAND)

        values_sizer.AddSpacer((0, 0), 0, wx.EXPAND)
        self.stdev_static_text = wx.StaticText(self, wx.ID_ANY, u"Stdev",
                                               wx.DefaultPosition,
                                               wx.DefaultSize, 0)
        self.stdev_static_text.Wrap(-1)
        values_sizer.Add(self.stdev_static_text, 0,
                         wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT, 0)
        self.stdev_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString,
                                          wx.DefaultPosition, wx.DefaultSize,
                                          wx.TE_READONLY)
        self.stdev_textctrl.SetFont(textentry_font)
        self.stdev_textctrl.SetBackgroundColour(
            textctrl_output_only_background_color)
        values_sizer.Add(self.stdev_textctrl, 0, wx.ALL, 2)
        self.robust_stdev_textctrl = wx.TextCtrl(self, wx.ID_ANY,
                                                 wx.EmptyString,
                                                 wx.DefaultPosition,
                                                 wx.DefaultSize,
                                                 wx.TE_READONLY)
        self.robust_stdev_textctrl.SetFont(textentry_font)
        self.robust_stdev_textctrl.SetBackgroundColour(
            textctrl_output_only_background_color)
        values_sizer.Add(self.robust_stdev_textctrl, 0, wx.ALL, 2)
        values_sizer.AddSpacer((0, 0), 0, wx.EXPAND)

        values_sizer.AddSpacer((0, 15), 0, wx.EXPAND)
        values_sizer.AddSpacer((0, 0), 0, wx.EXPAND)
        values_sizer.AddSpacer((0, 0), 0, wx.EXPAND)
        values_sizer.AddSpacer((0, 0), 0, wx.EXPAND)
        values_sizer.AddSpacer((0, 0), 0, wx.EXPAND)

        values_sizer.AddSpacer((0, 0), 0, wx.EXPAND)
        self.min_static_text = wx.StaticText(self, wx.ID_ANY, u"Min",
                                             wx.DefaultPosition,
                                             wx.DefaultSize, 0)
        self.min_static_text.Wrap(-1)
        values_sizer.Add(self.min_static_text, 0,
                         wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT, 0)
        self.minval_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString,
                                           wx.DefaultPosition, wx.DefaultSize,
                                           wx.TE_READONLY)
        self.minval_textctrl.SetFont(textentry_font)
        self.minval_textctrl.SetBackgroundColour(
            textctrl_output_only_background_color)
        values_sizer.Add(self.minval_textctrl, 0, wx.ALL, 2)
        self.minpos_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString,
                                           wx.DefaultPosition, wx.DefaultSize,
                                           wx.TE_READONLY)
        self.minpos_textctrl.SetFont(textentry_font)
        self.minpos_textctrl.SetBackgroundColour(
            textctrl_output_only_background_color)
        values_sizer.Add(self.minpos_textctrl, 0, wx.ALL, 2)
        values_sizer.AddSpacer((0, 0), 0, wx.EXPAND)

        values_sizer.AddSpacer((0, 0), 0, wx.EXPAND)
        self.max_static_text = wx.StaticText(self, wx.ID_ANY, u"Max",
                                             wx.DefaultPosition,
                                             wx.DefaultSize, 0)
        self.max_static_text.Wrap(-1)
        values_sizer.Add(self.max_static_text, 0,
                         wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT, 0)
        self.maxval_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString,
                                           wx.DefaultPosition, wx.DefaultSize,
                                           wx.TE_READONLY)
        self.maxval_textctrl.SetFont(textentry_font)
        self.maxval_textctrl.SetBackgroundColour(
            textctrl_output_only_background_color)
        values_sizer.Add(self.maxval_textctrl, 0, wx.ALL, 2)
        self.maxpos_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString,
                                           wx.DefaultPosition, wx.DefaultSize,
                                           wx.TE_READONLY)
        self.maxpos_textctrl.SetFont(textentry_font)
        self.maxpos_textctrl.SetBackgroundColour(
            textctrl_output_only_background_color)
        values_sizer.Add(self.maxpos_textctrl, 0, wx.ALL, 2)

        self.hideshow_button = wx.Button(self, wx.ID_ANY, u"Show",
                                         wx.DefaultPosition, wx.DefaultSize, 0)
        values_sizer.Add(
            self.hideshow_button, 0,
            wx.ALL | wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL, 2)
        self.hideshow_button.Bind(wx.EVT_BUTTON, self.on_hideshow_button)

        v_sizer1 = wx.BoxSizer(wx.VERTICAL)
        v_sizer1.AddStretchSpacer(1.0)
        v_sizer1.Add(values_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL)
        v_sizer1.AddStretchSpacer(1.0)
        self.SetSizer(v_sizer1)
        pub.subscribe(self.queue_update_stats, 'recalc-display-image-called')
        pub.subscribe(self._set_stats_box_parameters,
                      'set-stats-box-parameters')
        pub.subscribe(self.publish_stats_to_stream, 'get-stats-box-info')

    def publish_stats_to_stream(self, msg=None):
        wx.CallAfter(send_to_stream, sys.stdout,
                     ('stats-box-info', self.stats_info))

    def on_button_press(self, event):
        self.select_panel()
        self.stats_start_timestamp = event.guiEvent.GetTimestamp()  # millisec
        self.update_stats_box(event.xdata, event.ydata, event.xdata,
                              event.ydata)
        self.redraw_overplot_on_image()
        self.cursor_stats_box_x0, self.cursor_stats_box_y0 = event.xdata, event.ydata

    def on_motion(self, event):
        self.update_stats_box(self.cursor_stats_box_x0,
                              self.cursor_stats_box_y0, event.xdata,
                              event.ydata)
        self.redraw_overplot_on_image()
        self.update_stats()

    def on_button_release(self, event):
        self.redraw_overplot_on_image()
        self.update_stats()

    def set_cursor_to_stats_box_mode(self, event):
        self.ztv_frame.primary_image_panel.cursor_mode = 'Stats box'
        self.ztv_frame.stats_panel.select_panel()
        self.ztv_frame.stats_panel.highlight_panel()

    def queue_update_stats(self, msg=None):
        """
        wrapper to call update_stats from CallAfter in order to make GUI as responsive as possible.
        """
        wx.CallAfter(self.update_stats, msg=None)

    def _set_stats_box_parameters(self, msg):
        """
        wrapper to update_stats_box to receive messages & translate them correctly
        """
        x0, x1, y0, y1 = [None] * 4
        if msg['xrange'] is not None:
            x0, x1 = msg['xrange']
        if msg['yrange'] is not None:
            y0, y1 = msg['yrange']
        if msg['xrange'] is not None or msg['yrange'] is not None:
            self.update_stats_box(x0, y0, x1, y1)
        if msg['show_overplot'] is not None:
            if msg['show_overplot']:
                self.redraw_overplot_on_image()
            else:
                self.remove_overplot_on_image()
        send_to_stream(sys.stdout, ('set-stats-box-parameters-done', True))

    def update_stats_box(self, x0=None, y0=None, x1=None, y1=None):
        if x0 is None:
            x0 = self.stats_rect.get_x()
        if y0 is None:
            y0 = self.stats_rect.get_y()
        if x1 is None:
            x1 = self.stats_rect.get_x() + self.stats_rect.get_width()
        if y1 is None:
            y1 = self.stats_rect.get_y() + self.stats_rect.get_height()
        if x0 > x1:
            x0, x1 = x1, x0
        if y0 > y1:
            y0, y1 = y1, y0
        x0 = min(max(0, x0), self.ztv_frame.display_image.shape[1] - 1)
        y0 = min(max(0, y0), self.ztv_frame.display_image.shape[0] - 1)
        x1 = min(max(0, x1), self.ztv_frame.display_image.shape[1] - 1)
        y1 = min(max(0, y1), self.ztv_frame.display_image.shape[0] - 1)
        self.stats_rect.set_bounds(x0, y0, x1 - x0, y1 - y0)
        self.ztv_frame.primary_image_panel.figure.canvas.draw()
        self.update_stats()

    def remove_overplot_on_image(self):
        if self.stats_rect in self.ztv_frame.primary_image_panel.axes.patches:
            self.ztv_frame.primary_image_panel.axes.patches.remove(
                self.stats_rect)
        self.ztv_frame.primary_image_panel.figure.canvas.draw()
        self.hideshow_button.SetLabel(u"Show")

    def redraw_overplot_on_image(self):
        if self.stats_rect not in self.ztv_frame.primary_image_panel.axes.patches:
            self.ztv_frame.primary_image_panel.axes.add_patch(self.stats_rect)
        self.ztv_frame.primary_image_panel.figure.canvas.draw()
        self.hideshow_button.SetLabel(u"Hide")

    def on_hideshow_button(self, evt):
        if self.hideshow_button.GetLabel() == 'Hide':
            self.remove_overplot_on_image()
        else:
            self.redraw_overplot_on_image()

    def get_x0y0x1y1_from_stats_rect(self):
        x0 = self.stats_rect.get_x()
        y0 = self.stats_rect.get_y()
        x1 = x0 + self.stats_rect.get_width()
        y1 = y0 + self.stats_rect.get_height()
        return x0, y0, x1, y1

    def update_stats(self, msg=None):
        x0, y0, x1, y1 = self.get_x0y0x1y1_from_stats_rect()
        x0, y0 = int(np.round(x0)), int(np.round(y0))
        x1, y1 = int(np.round(x1)), int(np.round(y1))
        self.last_string_values['x0'] = str(int(x0))
        self.x0_textctrl.SetValue(self.last_string_values['x0'])
        self.last_string_values['y0'] = str(int(y0))
        self.y0_textctrl.SetValue(self.last_string_values['y0'])

        x_npix = int(x1 - x0 + 1)
        self.last_string_values['xsize'] = str(x_npix)
        self.xsize_textctrl.SetValue(self.last_string_values['xsize'])
        y_npix = int(y1 - y0 + 1)
        self.last_string_values['ysize'] = str(y_npix)
        self.ysize_textctrl.SetValue(self.last_string_values['ysize'])

        self.last_string_values['x1'] = str(int(x1))
        self.x1_textctrl.SetValue(self.last_string_values['x1'])
        self.last_string_values['y1'] = str(int(y1))
        self.y1_textctrl.SetValue(self.last_string_values['y1'])

        self.npix_textctrl.SetValue(str(x_npix * y_npix))

        stats_data = self.ztv_frame.display_image[y0:y1 + 1, x0:x1 + 1]
        finite_mask = np.isfinite(stats_data)
        if finite_mask.max() is np.True_:
            stats_data_mean = stats_data[finite_mask].mean()
            stats_data_median = np.median(stats_data[finite_mask])
            stats_data_std = stats_data[finite_mask].std()
            robust_mean, robust_median, robust_std = sigma_clipped_stats(
                stats_data[finite_mask])
        else:
            stats_data_mean = np.nan
            stats_data_median = np.nan
            stats_data_std = np.inf
            robust_mean, robust_median, robust_std = np.nan, np.nan, np.inf
        self.stats_info = {
            'xrange': [x0, x1],
            'yrange': [y0, y1],
            'mean': stats_data_mean,
            'median': stats_data_median,
            'std': stats_data_std,
            'min': stats_data.min(),
            'max': stats_data.max()
        }  # want min/max to reflect any Inf/NaN
        self.mean_textctrl.SetValue("{:0.4g}".format(self.stats_info['mean']))
        self.median_textctrl.SetValue("{:0.4g}".format(
            self.stats_info['median']))
        self.stdev_textctrl.SetValue("{:0.4g}".format(self.stats_info['std']))
        self.stats_info['robust-mean'] = robust_mean
        self.stats_info['robust-median'] = robust_median
        self.stats_info['robust-std'] = robust_std
        self.robust_mean_textctrl.SetValue("{:0.4g}".format(robust_mean))
        self.robust_stdev_textctrl.SetValue("{:0.4g}".format(robust_std))
        self.minval_textctrl.SetValue("{:0.4g}".format(self.stats_info['min']))
        self.maxval_textctrl.SetValue("{:0.4g}".format(self.stats_info['max']))
        wmin = np.where(stats_data == stats_data.min())
        wmin = [(wmin[1][i] + x0, wmin[0][i] + y0)
                for i in np.arange(wmin[0].size)]
        if len(wmin) == 1:
            wmin = wmin[0]
        self.minpos_textctrl.SetValue("{}".format(wmin))
        self.stats_info['wmin'] = wmin
        wmax = np.where(stats_data == stats_data.max())
        wmax = [(wmax[1][i] + x0, wmax[0][i] + y0)
                for i in np.arange(wmax[0].size)]
        if len(wmax) == 1:
            wmax = wmax[0]
        self.maxpos_textctrl.SetValue("{}".format(wmax))
        self.stats_info['wmax'] = wmax
        set_textctrl_background_color(self.x0_textctrl, 'ok')
        set_textctrl_background_color(self.x1_textctrl, 'ok')
        set_textctrl_background_color(self.xsize_textctrl, 'ok')
        set_textctrl_background_color(self.y0_textctrl, 'ok')
        set_textctrl_background_color(self.y1_textctrl, 'ok')
        set_textctrl_background_color(self.ysize_textctrl, 'ok')

    def x0_textctrl_changed(self, evt):
        validate_textctrl_str(self.x0_textctrl, int,
                              self.last_string_values['x0'])

    def x0_textctrl_entered(self, evt):
        if validate_textctrl_str(self.x0_textctrl, int,
                                 self.last_string_values['x0']):
            self.last_string_values['x0'] = self.x0_textctrl.GetValue()
            self.update_stats_box(int(self.last_string_values['x0']), None,
                                  None, None)
            self.x0_textctrl.SetSelection(-1, -1)
            self.redraw_overplot_on_image()

    def xsize_textctrl_changed(self, evt):
        validate_textctrl_str(self.xsize_textctrl, int,
                              self.last_string_values['xsize'])

    def xsize_textctrl_entered(self, evt):
        if validate_textctrl_str(self.xsize_textctrl, int,
                                 self.last_string_values['xsize']):
            self.last_string_values['xsize'] = self.xsize_textctrl.GetValue()
            xsize = int(self.last_string_values['xsize'])
            sys.stderr.write("\n\nxsize = {}\n\n".format(xsize))
            x0, y0, x1, y1 = self.get_x0y0x1y1_from_stats_rect()
            xc = (x0 + x1) / 2.
            x0 = max(0, int(xc - xsize / 2.))
            x1 = x0 + xsize - 1
            x1 = min(x1, self.ztv_frame.display_image.shape[1] - 1)
            x0 = x1 - xsize + 1
            x0 = max(0, int(xc - xsize / 2.))
            self.update_stats_box(x0, y0, x1, y1)
            self.xsize_textctrl.SetSelection(-1, -1)
            self.redraw_overplot_on_image()

    def x1_textctrl_changed(self, evt):
        validate_textctrl_str(self.x1_textctrl, int,
                              self.last_string_values['x1'])

    def x1_textctrl_entered(self, evt):
        if validate_textctrl_str(self.x1_textctrl, int,
                                 self.last_string_values['x1']):
            self.last_string_values['x1'] = self.x1_textctrl.GetValue()
            self.update_stats_box(None, None,
                                  int(self.last_string_values['x1']), None)
            self.x1_textctrl.SetSelection(-1, -1)
            self.redraw_overplot_on_image()

    def y0_textctrl_changed(self, evt):
        validate_textctrl_str(self.y0_textctrl, int,
                              self.last_string_values['y0'])

    def y0_textctrl_entered(self, evt):
        if validate_textctrl_str(self.y0_textctrl, int,
                                 self.last_string_values['y0']):
            self.last_string_values['y0'] = self.y0_textctrl.GetValue()
            self.update_stats_box(None, int(self.last_string_values['y0']),
                                  None, None)
            self.y0_textctrl.SetSelection(-1, -1)
            self.redraw_overplot_on_image()

    def ysize_textctrl_changed(self, evt):
        validate_textctrl_str(self.ysize_textctrl, int,
                              self.last_string_values['ysize'])

    def ysize_textctrl_entered(self, evt):
        if validate_textctrl_str(self.ysize_textctrl, int,
                                 self.last_string_values['ysize']):
            self.last_string_values['ysize'] = self.ysize_textctrl.GetValue()
            ysize = int(self.last_string_values['ysize'])
            x0, y0, x1, y1 = self.get_x0y0x1y1_from_stats_rect()
            yc = (y0 + y1) / 2.
            y0 = max(0, int(yc - ysize / 2.))
            y1 = y0 + ysize - 1
            y1 = min(y1, self.ztv_frame.display_image.shape[0] - 1)
            y0 = y1 - ysize + 1
            y0 = max(0, int(yc - ysize / 2.))
            self.update_stats_box(x0, y0, x1, y1)
            self.ysize_textctrl.SetSelection(-1, -1)
            self.redraw_overplot_on_image()

    def y1_textctrl_changed(self, evt):
        validate_textctrl_str(self.y1_textctrl, int,
                              self.last_string_values['y1'])

    def y1_textctrl_entered(self, evt):
        if validate_textctrl_str(self.y1_textctrl, int,
                                 self.last_string_values['y1']):
            self.last_string_values['y1'] = self.y1_textctrl.GetValue()
            self.update_stats_box(None, None, None,
                                  int(self.last_string_values['y1']))
            self.y1_textctrl.SetSelection(-1, -1)
            self.redraw_overplot_on_image()
Exemple #18
0
class ZoneInteret:

    """
    Cette classe permet de gérer les information de zone interet
    Une zone interet est un cadre d'image sur l'image entière.
    On se concentre sur cette zone interet pour faire le traitement.
    C'est une manière de réduire le bruit sur le résultat de traitement
    @version 2.0
    """

    @staticmethod
    def verifier_presence_fichier_ini():
        """
        Vérifier si les fichiers de zone interet sont déjà présents dans le dossier
        :return: true si présent, false sinon
        """
        return os.path.isfile('./zi/param.ini')

    @staticmethod
    def supprimer_ZI(window):
        """
        La méthode pour gérer la suppresion de zone interet
        :param window: le fenetre principale
        :return:
        """
        if os.path.isfile('./zi/param.ini'):
            try:
                os.remove("./zi/param.ini")
                os.remove("./zi/image_modele.png")
                os.remove("./zi/image_zone_interet.png")
                QMessageBox.information(window, "Information", "Supprimer la Zone d'intérêt avec succès", QMessageBox.Ok)
            except OSError:
                QMessageBox.warning(window, "Erreur", "Impossible de supprimer les fichiers dans le repertoire /zi",
                                    QMessageBox.Ok)
        else:
            QMessageBox.warning(window, "Erreur", "Impossible de trouver les fichiers dans le repertoire /zi",
                                QMessageBox.Ok)


    def __init__(self, video):
        """
        Initialise les variables nécessaires à l'affichage de l'image et aux événements
        :param video: la vidéo à traiter
        """
        self.flag = False
        self.get_one_image_from_video(video)

        # On se sert de l'image extraite précédemment
        self.img = mpimg.imread('./zi/image_modele.png')

        # On initialise le titre de la fenêtre
        fig = plt.figure(1)
        fig.canvas.set_window_title("Zone Interet")

        # On récupère les infos des axes
        self.ax = plt.gca()

        # On initialise le futur rectangle dessiné (non rempli aux bordures rouges)
        self.rect = Rectangle((0, 0), 1, 1, fill=False, edgecolor="red")

        # Initialisation des points du rectangle
        self.x0 = None
        self.y0 = None
        self.x1 = None
        self.y1 = None
        self.ax.add_patch(self.rect)

        # Liaison des événements
        self.ax.figure.canvas.mpl_connect('button_press_event', self.on_mouseclick_press)
        self.ax.figure.canvas.mpl_connect('button_release_event', self.on_mouseclick_release)
        self.ax.figure.canvas.mpl_connect('key_press_event', self.on_keyboard_press)

        # Affichage de l'image dans la fenêtre
        self.imgplot = plt.imshow(self.img)

        self.show_window()

    def on_mouseclick_press(self, event):
        """
        Un click gauche -> sauvegarde des coordonnées du pointeur
        :param event: évènement de clique
        :return:
        """
        self.x0 = event.xdata
        self.y0 = event.ydata


    def on_mouseclick_release(self, event):
        """
        Click gauche relâché -> dessin du rectangle
        :param event: évènement de souris
        :return:
        """
        self.x1 = event.xdata
        self.y1 = event.ydata
        self.rect.set_width(self.x1 - self.x0)
        self.rect.set_height(self.y1 - self.y0)
        self.rect.set_xy((self.x0, self.y0))
        self.ax.figure.canvas.draw()


    def on_keyboard_press(self, event):
        """
        Si la touche "enter" est appuyée, on sauvegarde la zone d'intérêt
        :param event: évenenment de keyboard
        :return:
        """
        if event.key == 'enter':
            self.flag = True
            with open("./zi/param.ini", "w") as file:
                file.write(str(int(self.rect.get_x())) + ",")
                file.write(str(int(self.rect.get_y())) + ",")
                file.write(str(int(self.rect.get_width())) + ",")
                file.write(str(int(self.rect.get_height())))

            # On cache les axes avant d'enregistrer l'image modele avec la zone d'interet
            self.ax.get_xaxis().set_visible(False)
            self.ax.get_yaxis().set_visible(False)
            plt.title("Zone interet")
            plt.savefig("./zi/image_zone_interet.png")
            plt.close()


    def show_window(self):
        """
        Pour afficher la fenêtre qui est utilisée pour choisir une zone interet
        :return:
        """
        plt.title("Selectionner la zone interet avec la souris. Appuyez sur entrer pour valider.")
        plt.show()

    def get_one_image_from_video(self, video):
        """
        Extrait une image de la vidéo selectionnée
        Cette image est utilisée pour choisir une zone interet
        :param video: la vidéo choisie
        :return:
        """
        video_capture = cv2.VideoCapture(video)
        nb_frame = video_capture.get(cv2.CAP_PROP_FRAME_COUNT)
        video_capture.set(cv2.CAP_PROP_FRAME_COUNT, int(nb_frame - 1))
        success, self.image = video_capture.read()
        #sauvegarder l'image
        cv2.imwrite("zi/image_modele.png", self.image)
Exemple #19
0
class DraggableRectangle(object):
    def __init__(self, axes, rectwidth=100, lrwidth=10, recty=100):
        self.ax = axes  # plt.gca()

        self.rect_x = axes.get_xlim()[1] - rectwidth - lrwidth
        self.rect = Rectangle((self.rect_x, 0),
                              rectwidth,
                              recty,
                              facecolor='g',
                              alpha=0.2)
        self.rectl = Rectangle((self.rect_x - lrwidth, 0),
                               lrwidth,
                               recty,
                               facecolor='b',
                               alpha=0.8)
        self.rectr = Rectangle((self.rect_x + rectwidth, 0),
                               lrwidth,
                               recty,
                               facecolor='b',
                               alpha=0.8)

        self.ax.add_patch(self.rect)
        self.ax.add_patch(self.rectl)
        self.ax.add_patch(self.rectr)

        self.rect_width = rectwidth
        self.press = None  # 0,
        self.pressl = None
        self.pressr = None

        self.ax.figure.canvas.mpl_connect('button_press_event', self.on_press)
        self.ax.figure.canvas.mpl_connect('button_release_event',
                                          self.on_release)
        self.ax.figure.canvas.mpl_connect('motion_notify_event',
                                          self.on_motion)

    def on_press(self, event):
        if event.inaxes != self.rect.axes: return

        contains, attrd = self.rect.contains(event)
        containsl, attrdl = self.rectl.contains(event)
        containsr, attrdr = self.rectr.contains(event)
        if contains:
            x0, y0 = self.rect.xy
            self.press = x0, y0, event.xdata, event.ydata
        elif containsl:
            xl, yl = self.rectl.xy
            self.pressl = xl, yl, event.xdata, event.ydata
        elif containsr:
            xr, yr = self.rectr.xy
            self.pressr = xr, yr, event.xdata, event.ydata
        else:
            return

    def on_release(self, event):
        # print ('release')
        self.press = None
        self.pressl = None
        self.pressr = None
        self.ax.figure.canvas.draw()

    def on_motion(self, event):
        'on motion we will move the rect if the mouse is over us'
        if self.press is None and self.pressl is None and self.pressr is None:
            return
        if event.inaxes != self.rect.axes and event.inaxes != self.rectl.axes and event.inaxes != self.rectr.axes:
            return

        rectwidth = self.rect_width
        if self.press != None:
            x0, y0, xpress, ypress = self.press
            dx = event.xdata - xpress
            rectl_x = x0 + dx - self.rectl.get_width()

            # to tell the left edge overriding
            if rectl_x < 0:
                rectl_x = 0
            # to tell the right edge overriding
            if rectl_x + self.rectl.get_width(
            ) + rectwidth + self.rectr.get_width() > self.ax.get_xlim()[1] - 1:
                rectl_x = self.ax.get_xlim(
                )[1] - rectwidth - self.rectr.get_width(
                ) - self.rectl.get_width()
                print("self.ax.get_xlim()[1]=",
                      self.ax.get_xlim()[1], " rectLx=", rectl_x)

            self.rect_x = rectl_x + self.rectl.get_width()
            self.rect.set_x(self.rect_x)
            self.rectl.set_x(rectl_x)
            self.rectr.set_x(self.rect_x + self.rect.get_width())
            # self.rect.figure.canvas.draw()
        elif self.pressl != None:
            if event.xdata >= self.rectr.get_x() - 10:
                self.rect_width = 10
                return

            xl, yl, xpress, ypress = self.pressl
            dx = event.xdata - xpress
            rectl_x = xl + dx  #- self.rectl.get_width()

            # to tell the left edge overriding
            if rectl_x < 0:
                self.rect_width = self.rectr.get_x() - self.rectl.get_width()
                rectl_x = 0

            self.rectl.set_x(rectl_x)
            # draw rect
            self.rect.set_x(rectl_x + self.rectl.get_width())
            xr = self.rectr.get_x()
            rectwidth = xr - rectl_x - self.rectl.get_width()
            # self.rectl.figure.canvas.draw()
        elif self.pressr != None:
            if event.xdata <= self.rectl.get_x() + self.rectl.get_width() + 10:
                self.rect_width = 10
                return

            xr, yr, xpress, ypress = self.pressr
            dx = event.xdata - xpress
            rectr_x = xr + dx

            # to tell the right edge overriding
            xlim = self.ax.get_xlim()[1]
            print('xlim=', xlim)
            if rectr_x + self.rectr.get_width() > xlim:
                self.rect_width = xlim - self.rectl.get_x(
                ) - self.rectl.get_width()
                rectr_x = xlim - self.rectr.get_width()

            self.rectr.set_x(rectr_x)
            # draw rect
            rectwidth = rectr_x - self.rectl.get_x() - self.rectl.get_width()
            # self.rectr.figure.canvas.draw()

        self.rect.set_width(rectwidth)
        self.rect_width = rectwidth
        self.ax.figure.canvas.draw()
        # self.rectl.figure.canvas.draw()  # why we just draw rect here, but rectl and rectr are drawed too ?

    def reset_rects(self, xl, xr):
        xlim = self.ax.get_xlim()[1]
        if xr > xlim: return
        if xl < 0: return

        self.rectl.set_x(xl)
        self.rectr.set_x(xr - self.rectr.get_width())
        self.rect_x = xl + self.rectl.get_width()
        self.rect.set_x(self.rect_x)
        self.rect_width = xr - self.rectr.get_width() - self.rect_x
        self.rect.set_width(self.rect_width)
        self.ax.figure.canvas.draw()

    def is_pressed(self):
        r = 0
        if self.press:
            r = 1
        elif self.pressl:
            r = 2
        elif self.pressr:
            r = 3
        else:
            r = 0
        return r

    def get_rect_x1(self):
        return int(self.rectl.get_x())

    def get_rect_x2(self):
        return int(self.rectr.get_x() + self.rectr.get_width() - 1)
Exemple #20
0
class StatsPanel(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize)
        self.ztv_frame = self.GetTopLevelParent()
        self.ztv_frame.primary_image_panel.popup_menu_cursor_modes.append('Stats box')
        self.ztv_frame.primary_image_panel.available_cursor_modes['Stats box'] = {
                'set-to-mode':self.set_cursor_to_stats_box_mode,
                'on_button_press':self.on_button_press,
                'on_motion':self.on_motion,
                'on_button_release':self.on_button_release}
        self.textentry_font = wx.Font(14, wx.FONTFAMILY_MODERN, wx.NORMAL, wx.FONTWEIGHT_LIGHT, False)

        self.stats_info = None
        
        self.last_string_values = {'x0':'', 'xsize':'', 'x1':'', 'y0':'', 'ysize':'', 'y1':''}
        self.stats_rect = Rectangle((0, 0), 10, 10, color='magenta', fill=False, zorder=100)
        # use self.stats_rect as where we store/retrieve the x0,y0,x1,y1
        # x0,y0,x1,y1 should be limited to range of 0 to shape-1
        # but, stats should be calculated over e.g. x0:x1+1  (so that have pixels to do stats on even if x0==x1)
        # and, width/height of stats_rect should always be >= 0
        
        values_sizer = wx.FlexGridSizer( 10, 5, 0, 0 )
        values_sizer.SetFlexibleDirection( wx.BOTH )
        values_sizer.SetNonFlexibleGrowMode( wx.FLEX_GROWMODE_SPECIFIED )

        values_sizer.AddSpacer((0,0), 0, wx.EXPAND)

        self.low_static_text = wx.StaticText( self, wx.ID_ANY, u"Low", wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_RIGHT )
        self.low_static_text.Wrap( -1 )
        values_sizer.Add(self.low_static_text, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL, 0)

        self.low_static_text = wx.StaticText( self, wx.ID_ANY, u"# pix", wx.DefaultPosition, wx.DefaultSize, 0 )
        self.low_static_text.Wrap( -1 )
        values_sizer.Add(self.low_static_text, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL, 0)

        self.high_static_text = wx.StaticText( self, wx.ID_ANY, u"High", wx.DefaultPosition, wx.DefaultSize, 0 )
        self.high_static_text.Wrap( -1 )
        values_sizer.Add(self.high_static_text, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL, 0)

        values_sizer.AddSpacer((0,0), 0, wx.EXPAND)

        self.x_static_text = wx.StaticText( self, wx.ID_ANY, u"x", wx.DefaultPosition, wx.DefaultSize, 0 )
        self.x_static_text.Wrap( -1 )
        values_sizer.Add(self.x_static_text, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 0)

        self.x0_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize,
                                       wx.TE_PROCESS_ENTER)
        self.x0_textctrl.SetFont(self.textentry_font)
        values_sizer.Add(self.x0_textctrl, 0, wx.ALL, 2)
        self.x0_textctrl.Bind(wx.EVT_TEXT, self.x0_textctrl_changed)
        self.x0_textctrl.Bind(wx.EVT_TEXT_ENTER, self.x0_textctrl_entered)

        self.xsize_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize,
                                          wx.TE_PROCESS_ENTER)
        self.xsize_textctrl.SetFont(self.textentry_font)
        values_sizer.Add(self.xsize_textctrl, 0, wx.ALL, 2)
        self.xsize_textctrl.Bind(wx.EVT_TEXT, self.xsize_textctrl_changed)
        self.xsize_textctrl.Bind(wx.EVT_TEXT_ENTER, self.xsize_textctrl_entered)

        self.x1_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize,
                                       wx.TE_PROCESS_ENTER)
        self.x1_textctrl.SetFont(self.textentry_font)
        values_sizer.Add(self.x1_textctrl, 0, wx.ALL, 2)
        self.x1_textctrl.Bind(wx.EVT_TEXT, self.x1_textctrl_changed)
        self.x1_textctrl.Bind(wx.EVT_TEXT_ENTER, self.x1_textctrl_entered)

        self.npix_static_text = wx.StaticText( self, wx.ID_ANY, u"# pixels", wx.DefaultPosition, wx.DefaultSize, 0 )
        self.npix_static_text.Wrap( -1 )
        values_sizer.Add(self.npix_static_text, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_BOTTOM, 0)

        self.y_static_text = wx.StaticText( self, wx.ID_ANY, u"y", wx.DefaultPosition, wx.DefaultSize, 0 )
        self.y_static_text.Wrap( -1 )
        values_sizer.Add(self.y_static_text, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 0)

        self.y0_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize,
                                       wx.TE_PROCESS_ENTER)
        self.y0_textctrl.SetFont(self.textentry_font)
        values_sizer.Add(self.y0_textctrl, 0, wx.ALL, 2)
        self.y0_textctrl.Bind(wx.EVT_TEXT, self.y0_textctrl_changed)
        self.y0_textctrl.Bind(wx.EVT_TEXT_ENTER, self.y0_textctrl_entered)

        self.ysize_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize,
                                          wx.TE_PROCESS_ENTER)
        self.ysize_textctrl.SetFont(self.textentry_font)
        values_sizer.Add(self.ysize_textctrl, 0, wx.ALL, 2)
        self.ysize_textctrl.Bind(wx.EVT_TEXT, self.ysize_textctrl_changed)
        self.ysize_textctrl.Bind(wx.EVT_TEXT_ENTER, self.ysize_textctrl_entered)

        self.y1_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize,
                                       wx.TE_PROCESS_ENTER)
        self.y1_textctrl.SetFont(self.textentry_font)
        values_sizer.Add(self.y1_textctrl, 0, wx.ALL, 2)
        self.y1_textctrl.Bind(wx.EVT_TEXT, self.y1_textctrl_changed)
        self.y1_textctrl.Bind(wx.EVT_TEXT_ENTER, self.y1_textctrl_entered)
        
        self.npix_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize,
                                         wx.TE_READONLY)
        self.npix_textctrl.SetFont(self.textentry_font)
        self.npix_textctrl.SetBackgroundColour(textctrl_output_only_background_color)
        values_sizer.Add(self.npix_textctrl, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT, 0)
  
        values_sizer.AddSpacer((0,15), 0, wx.EXPAND)
        values_sizer.AddSpacer((0,0), 0, wx.EXPAND)
        values_sizer.AddSpacer((0,0), 0, wx.EXPAND)
        values_sizer.AddSpacer((0,0), 0, wx.EXPAND)
        values_sizer.AddSpacer((0,0), 0, wx.EXPAND)

        values_sizer.AddSpacer((0,0), 0, wx.EXPAND)
        self.median_static_text = wx.StaticText( self, wx.ID_ANY, u"Median", wx.DefaultPosition, wx.DefaultSize, 0 )
        self.median_static_text.Wrap( -1 )
        values_sizer.Add(self.median_static_text, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT, 0)
        self.median_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize,
                                       wx.TE_READONLY)
        self.median_textctrl.SetFont(self.textentry_font)
        self.median_textctrl.SetBackgroundColour(textctrl_output_only_background_color)
        values_sizer.Add(self.median_textctrl, 0, wx.ALL, 2)
        self.robust_static_text = wx.StaticText( self, wx.ID_ANY, u"Robust", wx.DefaultPosition, wx.DefaultSize, 0 )
        self.robust_static_text.Wrap( -1 )
        values_sizer.Add(self.robust_static_text, 0, wx.ALL|wx.ALIGN_BOTTOM|wx.ALIGN_CENTER_HORIZONTAL, 0)
        values_sizer.AddSpacer((0,0), 0, wx.EXPAND)

        values_sizer.AddSpacer((0,0), 0, wx.EXPAND)
        self.mean_static_text = wx.StaticText( self, wx.ID_ANY, u"Mean", wx.DefaultPosition, wx.DefaultSize, 0 )
        self.mean_static_text.Wrap( -1 )
        values_sizer.Add(self.mean_static_text, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT, 0)
        self.mean_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize,
                                       wx.TE_READONLY)
        self.mean_textctrl.SetFont(self.textentry_font)
        self.mean_textctrl.SetBackgroundColour(textctrl_output_only_background_color)
        values_sizer.Add(self.mean_textctrl, 0, wx.ALL, 2)
        self.robust_mean_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize,
                                       wx.TE_READONLY)
        self.robust_mean_textctrl.SetFont(self.textentry_font)
        self.robust_mean_textctrl.SetBackgroundColour(textctrl_output_only_background_color)
        values_sizer.Add(self.robust_mean_textctrl, 0, wx.ALL, 2)
        values_sizer.AddSpacer((0,0), 0, wx.EXPAND)

        values_sizer.AddSpacer((0,0), 0, wx.EXPAND)
        self.stdev_static_text = wx.StaticText( self, wx.ID_ANY, u"Stdev", wx.DefaultPosition, wx.DefaultSize, 0 )
        self.stdev_static_text.Wrap( -1 )
        values_sizer.Add(self.stdev_static_text, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT, 0)
        self.stdev_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize,
                                       wx.TE_READONLY)
        self.stdev_textctrl.SetFont(self.textentry_font)
        self.stdev_textctrl.SetBackgroundColour(textctrl_output_only_background_color)
        values_sizer.Add(self.stdev_textctrl, 0, wx.ALL, 2)
        self.robust_stdev_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize,
                                       wx.TE_READONLY)
        self.robust_stdev_textctrl.SetFont(self.textentry_font)
        self.robust_stdev_textctrl.SetBackgroundColour(textctrl_output_only_background_color)
        values_sizer.Add(self.robust_stdev_textctrl, 0, wx.ALL, 2)
        values_sizer.AddSpacer((0,0), 0, wx.EXPAND)

        values_sizer.AddSpacer((0,15), 0, wx.EXPAND)
        values_sizer.AddSpacer((0,0), 0, wx.EXPAND)
        values_sizer.AddSpacer((0,0), 0, wx.EXPAND)
        values_sizer.AddSpacer((0,0), 0, wx.EXPAND)
        values_sizer.AddSpacer((0,0), 0, wx.EXPAND)

        values_sizer.AddSpacer((0,0), 0, wx.EXPAND)
        self.min_static_text = wx.StaticText( self, wx.ID_ANY, u"Min", wx.DefaultPosition, wx.DefaultSize, 0 )
        self.min_static_text.Wrap( -1 )
        values_sizer.Add(self.min_static_text, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT, 0)
        self.minval_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize,
                                           wx.TE_READONLY)
        self.minval_textctrl.SetFont(self.textentry_font)
        self.minval_textctrl.SetBackgroundColour(textctrl_output_only_background_color)
        values_sizer.Add(self.minval_textctrl, 0, wx.ALL, 2)
        self.minpos_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize,
                                           wx.TE_READONLY)
        self.minpos_textctrl.SetFont(self.textentry_font)
        self.minpos_textctrl.SetBackgroundColour(textctrl_output_only_background_color)
        values_sizer.Add(self.minpos_textctrl, 0, wx.ALL, 2)
        values_sizer.AddSpacer((0,0), 0, wx.EXPAND)

        values_sizer.AddSpacer((0,0), 0, wx.EXPAND)
        self.max_static_text = wx.StaticText( self, wx.ID_ANY, u"Max", wx.DefaultPosition, wx.DefaultSize, 0 )
        self.max_static_text.Wrap( -1 )
        values_sizer.Add(self.max_static_text, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT, 0)
        self.maxval_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize,
                                           wx.TE_READONLY)
        self.maxval_textctrl.SetFont(self.textentry_font)
        self.maxval_textctrl.SetBackgroundColour(textctrl_output_only_background_color)
        values_sizer.Add(self.maxval_textctrl, 0, wx.ALL, 2)
        self.maxpos_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize,
                                           wx.TE_READONLY)
        self.maxpos_textctrl.SetFont(self.textentry_font)
        self.maxpos_textctrl.SetBackgroundColour(textctrl_output_only_background_color)
        values_sizer.Add(self.maxpos_textctrl, 0, wx.ALL, 2)
             
        self.hideshow_button = wx.Button(self, wx.ID_ANY, u"Show", wx.DefaultPosition, wx.DefaultSize, 0)
        values_sizer.Add(self.hideshow_button, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL, 2)
        self.hideshow_button.Bind(wx.EVT_BUTTON, self.on_hideshow_button)

        v_sizer1 = wx.BoxSizer(wx.VERTICAL)
        v_sizer1.AddStretchSpacer(1.0)
        v_sizer1.Add(values_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL)
        v_sizer1.AddStretchSpacer(1.0)
        self.SetSizer(v_sizer1)
        pub.subscribe(self.queue_update_stats, 'recalc-display-image-called')
        pub.subscribe(self._set_stats_box_parameters, 'set-stats-box-parameters')
        pub.subscribe(self.publish_stats_to_stream, 'get-stats-box-info')

    def publish_stats_to_stream(self, msg=None):
        wx.CallAfter(send_to_stream, sys.stdout, ('stats-box-info', self.stats_info))

    def on_button_press(self, event):
        self.select_panel()
        self.update_stats_box(event.xdata, event.ydata, event.xdata, event.ydata)
        self.redraw_overplot_on_image()
        self.cursor_stats_box_x0, self.cursor_stats_box_y0 = event.xdata, event.ydata

    def on_motion(self, event):
        if event.button is not None:
            self.update_stats_box(self.cursor_stats_box_x0, self.cursor_stats_box_y0, event.xdata, event.ydata)
            self.redraw_overplot_on_image()
            self.update_stats()

    def on_button_release(self, event):
        self.redraw_overplot_on_image()
        self.update_stats()

    def set_cursor_to_stats_box_mode(self, event):
        self.ztv_frame.primary_image_panel.cursor_mode = 'Stats box'
        self.ztv_frame.stats_panel.select_panel()
        self.ztv_frame.stats_panel.highlight_panel()

    def queue_update_stats(self, msg=None):  
        """
        wrapper to call update_stats from CallAfter in order to make GUI as responsive as possible.
        """
        wx.CallAfter(self.update_stats, msg=None)

    def _set_stats_box_parameters(self, msg):
        """
        wrapper to update_stats_box to receive messages & translate them correctly
        """
        x0,x1,y0,y1 = [None]*4
        if msg['xrange'] is not None:
            x0,x1 = msg['xrange']
        if msg['yrange'] is not None:
            y0,y1 = msg['yrange']
        if msg['xrange'] is not None or msg['yrange'] is not None:
            self.update_stats_box(x0, y0, x1, y1)
        if msg['show_overplot'] is not None:
            if msg['show_overplot']:
                self.redraw_overplot_on_image()
            else:
                self.remove_overplot_on_image()
        send_to_stream(sys.stdout, ('set-stats-box-parameters-done', True))

    def update_stats_box(self, x0=None, y0=None, x1=None, y1=None):
        if x0 is None:
            x0 = self.stats_rect.get_x()
        if y0 is None:
            y0 = self.stats_rect.get_y()
        if x1 is None:
            x1 = self.stats_rect.get_x() + self.stats_rect.get_width()
        if y1 is None:
            y1 = self.stats_rect.get_y() + self.stats_rect.get_height()
        if x0 > x1:
            x0, x1 = x1, x0
        if y0 > y1:
            y0, y1 = y1, y0
        x0 = min(max(0, x0), self.ztv_frame.display_image.shape[1] - 1)
        y0 = min(max(0, y0), self.ztv_frame.display_image.shape[0] - 1)
        x1 = min(max(0, x1), self.ztv_frame.display_image.shape[1] - 1)
        y1 = min(max(0, y1), self.ztv_frame.display_image.shape[0] - 1)
        self.stats_rect.set_bounds(x0, y0, x1 - x0, y1 - y0)
        if self.hideshow_button.GetLabel() == 'Hide':  
            self.ztv_frame.primary_image_panel.figure.canvas.draw()
        self.update_stats()

    def remove_overplot_on_image(self):
        self.ztv_frame.primary_image_panel.remove_patch('stats_panel:stats_rect')
        self.hideshow_button.SetLabel(u"Show")

    def redraw_overplot_on_image(self):
        self.ztv_frame.primary_image_panel.add_patch('stats_panel:stats_rect', self.stats_rect)
        self.hideshow_button.SetLabel(u"Hide")        

    def on_hideshow_button(self, evt):
        if self.hideshow_button.GetLabel() == 'Hide':
            self.remove_overplot_on_image()
        else:
            self.redraw_overplot_on_image()

    def get_x0y0x1y1_from_stats_rect(self):
        x0 = self.stats_rect.get_x()
        y0 = self.stats_rect.get_y()
        x1 = x0 + self.stats_rect.get_width()
        y1 = y0 + self.stats_rect.get_height()
        return x0,y0,x1,y1
        
    def update_stats(self, msg=None):
        x0,y0,x1,y1 = self.get_x0y0x1y1_from_stats_rect()
        x0, y0 = int(np.round(x0)), int(np.round(y0))
        x1, y1 = int(np.round(x1)), int(np.round(y1))
        self.last_string_values['x0'] = str(int(x0))
        self.x0_textctrl.SetValue(self.last_string_values['x0'])
        self.last_string_values['y0'] = str(int(y0))
        self.y0_textctrl.SetValue(self.last_string_values['y0'])

        x_npix = int(x1 - x0 + 1)
        self.last_string_values['xsize'] = str(x_npix)
        self.xsize_textctrl.SetValue(self.last_string_values['xsize'])
        y_npix = int(y1 - y0 + 1)
        self.last_string_values['ysize'] = str(y_npix)
        self.ysize_textctrl.SetValue(self.last_string_values['ysize'])

        self.last_string_values['x1'] = str(int(x1))
        self.x1_textctrl.SetValue(self.last_string_values['x1'])
        self.last_string_values['y1'] = str(int(y1))
        self.y1_textctrl.SetValue(self.last_string_values['y1'])
    
        self.npix_textctrl.SetValue(str(x_npix * y_npix))

        stats_data = self.ztv_frame.display_image[y0:y1+1, x0:x1+1]
        finite_mask = np.isfinite(stats_data)
        if finite_mask.max() is np.True_:
            stats_data_mean = stats_data[finite_mask].mean()
            stats_data_median = np.median(stats_data[finite_mask])
            stats_data_std = stats_data[finite_mask].std()
            robust_mean, robust_median, robust_std = sigma_clipped_stats(stats_data[finite_mask])
        else:
            stats_data_mean = np.nan
            stats_data_median = np.nan
            stats_data_std = np.inf
            robust_mean, robust_median, robust_std = np.nan, np.nan, np.inf
        self.stats_info = {'xrange':[x0,x1], 'yrange':[y0,y1],
                           'mean':stats_data_mean, 'median':stats_data_median, 'std':stats_data_std, 
                           'min':stats_data.min(), 'max':stats_data.max()} # want min/max to reflect any Inf/NaN
        self.mean_textctrl.SetValue("{:0.4g}".format(self.stats_info['mean']))
        self.median_textctrl.SetValue("{:0.4g}".format(self.stats_info['median']))
        self.stdev_textctrl.SetValue("{:0.4g}".format(self.stats_info['std']))
        self.stats_info['robust-mean'] = robust_mean
        self.stats_info['robust-median'] = robust_median
        self.stats_info['robust-std'] = robust_std
        self.robust_mean_textctrl.SetValue("{:0.4g}".format(robust_mean)) 
        self.robust_stdev_textctrl.SetValue("{:0.4g}".format(robust_std))
        self.minval_textctrl.SetValue("{:0.4g}".format(self.stats_info['min']))
        self.maxval_textctrl.SetValue("{:0.4g}".format(self.stats_info['max']))
        wmin = np.where(stats_data == stats_data.min())
        wmin = [(wmin[1][i] + x0,wmin[0][i] + y0) for i in np.arange(wmin[0].size)]
        if len(wmin) == 1:
            wmin = wmin[0]
        self.minpos_textctrl.SetValue("{}".format(wmin))
        self.stats_info['wmin'] = wmin
        wmax = np.where(stats_data == stats_data.max())
        wmax = [(wmax[1][i] + x0,wmax[0][i] + y0) for i in np.arange(wmax[0].size)]
        if len(wmax) == 1:
            wmax = wmax[0]
        self.maxpos_textctrl.SetValue("{}".format(wmax))
        self.stats_info['wmax'] = wmax
        set_textctrl_background_color(self.x0_textctrl, 'ok')
        set_textctrl_background_color(self.x1_textctrl, 'ok')
        set_textctrl_background_color(self.xsize_textctrl, 'ok')
        set_textctrl_background_color(self.y0_textctrl, 'ok')
        set_textctrl_background_color(self.y1_textctrl, 'ok')
        set_textctrl_background_color(self.ysize_textctrl, 'ok')
        
    def x0_textctrl_changed(self, evt):
        validate_textctrl_str(self.x0_textctrl, int, self.last_string_values['x0'])

    def x0_textctrl_entered(self, evt):
        if validate_textctrl_str(self.x0_textctrl, int, self.last_string_values['x0']):
            self.last_string_values['x0'] = self.x0_textctrl.GetValue()
            self.update_stats_box(int(self.last_string_values['x0']), None, None, None)
            self.x0_textctrl.SetSelection(-1, -1)
            self.redraw_overplot_on_image()
            
    def xsize_textctrl_changed(self, evt):
        validate_textctrl_str(self.xsize_textctrl, int, self.last_string_values['xsize'])

    def xsize_textctrl_entered(self, evt):
        if validate_textctrl_str(self.xsize_textctrl, int, self.last_string_values['xsize']):
            self.last_string_values['xsize'] = self.xsize_textctrl.GetValue()
            xsize = int(self.last_string_values['xsize'])
            sys.stderr.write("\n\nxsize = {}\n\n".format(xsize))
            x0,y0,x1,y1 = self.get_x0y0x1y1_from_stats_rect()
            xc = (x0 + x1) / 2.
            x0 = max(0, int(xc - xsize / 2.))
            x1 = x0 + xsize - 1
            x1 = min(x1, self.ztv_frame.display_image.shape[1] - 1)
            x0 = x1 - xsize + 1
            x0 = max(0, int(xc - xsize / 2.))
            self.update_stats_box(x0, y0, x1, y1)
            self.xsize_textctrl.SetSelection(-1, -1)
            self.redraw_overplot_on_image()

    def x1_textctrl_changed(self, evt):
        validate_textctrl_str(self.x1_textctrl, int, self.last_string_values['x1'])

    def x1_textctrl_entered(self, evt):
        if validate_textctrl_str(self.x1_textctrl, int, self.last_string_values['x1']):
            self.last_string_values['x1'] = self.x1_textctrl.GetValue()
            self.update_stats_box(None, None, int(self.last_string_values['x1']), None)
            self.x1_textctrl.SetSelection(-1, -1)
            self.redraw_overplot_on_image()

    def y0_textctrl_changed(self, evt):
        validate_textctrl_str(self.y0_textctrl, int, self.last_string_values['y0'])

    def y0_textctrl_entered(self, evt):
        if validate_textctrl_str(self.y0_textctrl, int, self.last_string_values['y0']):
            self.last_string_values['y0'] = self.y0_textctrl.GetValue()
            self.update_stats_box(None, int(self.last_string_values['y0']), None, None)
            self.y0_textctrl.SetSelection(-1, -1)
            self.redraw_overplot_on_image()

    def ysize_textctrl_changed(self, evt):
        validate_textctrl_str(self.ysize_textctrl, int, self.last_string_values['ysize'])

    def ysize_textctrl_entered(self, evt):
        if validate_textctrl_str(self.ysize_textctrl, int, self.last_string_values['ysize']):
            self.last_string_values['ysize'] = self.ysize_textctrl.GetValue()
            ysize = int(self.last_string_values['ysize'])
            x0,y0,x1,y1 = self.get_x0y0x1y1_from_stats_rect()
            yc = (y0 + y1) / 2.
            y0 = max(0, int(yc - ysize / 2.))
            y1 = y0 + ysize - 1
            y1 = min(y1, self.ztv_frame.display_image.shape[0] - 1)
            y0 = y1 - ysize + 1
            y0 = max(0, int(yc - ysize / 2.))
            self.update_stats_box(x0, y0, x1, y1)
            self.ysize_textctrl.SetSelection(-1, -1)
            self.redraw_overplot_on_image()

    def y1_textctrl_changed(self, evt):
        validate_textctrl_str(self.y1_textctrl, int, self.last_string_values['y1'])

    def y1_textctrl_entered(self, evt):
        if validate_textctrl_str(self.y1_textctrl, int, self.last_string_values['y1']):
            self.last_string_values['y1'] = self.y1_textctrl.GetValue()
            self.update_stats_box(None, None, None, int(self.last_string_values['y1']))
            self.y1_textctrl.SetSelection(-1, -1)
            self.redraw_overplot_on_image()
Exemple #21
0
def visualize(Grid):
    fig, axes = plt.subplots(2, 1, dpi=100, figsize=(12, 8))
    axes[0].plot(Grid.Ez)
    axes[0].xaxis.grid(linestyle='dotted')
    axes[0].set_xlabel('Cell')
    axes[0].set_ylabel(r'$E_z$', fontsize=12, rotation=0)
    axes[0].set_title(
        'time passed: {0:.3e}s,  timesteps passed: {timesteps}'.format(
            Grid.time_passed, timesteps=Grid.timesteps_passed))
    #axes[0].set_ylim([-1.5, 1.5])
    axes[1].plot(Grid.Hy)
    axes[1].xaxis.grid(linestyle='dotted')
    axes[1].set_xlabel('Cell')
    axes[1].set_ylabel(r'$H_y$', fontsize=12, rotation=0)
    for mat in Grid.materials:
        media_repr_0 = Rectangle(xy=(mat.position[0] - 0.5, -1.4),
                                 height=2.8,
                                 width=(mat.position[-1] - mat.position[0] +
                                        1),
                                 color='grey',
                                 fill=True,
                                 alpha=mat.eps * 0.12)
        axes[0].add_patch(media_repr_0)
        axes[0].annotate(s=r'$\epsilon_r$={0:.2f}'.format(mat.eps) + '\n' +
                         r'$\sigma$={0:.2f}'.format(mat.conductivity),
                         xy=(media_repr_0.get_x() + 0.1,
                             media_repr_0.get_y() + 0.2),
                         color='black')
        media_repr_1 = Rectangle(xy=(mat.position[0] - 0.5, -1.4),
                                 height=2.8,
                                 width=(mat.position[-1] - mat.position[0] +
                                        1),
                                 color='grey',
                                 fill=True,
                                 alpha=mat.eps * 0.12)
        axes[1].add_patch(media_repr_1)
        if mat.model == 'Lorentz':
            s = r'$\epsilon(\omega)$' + '\n' + r'$\sigma$={0:.2f}'.format(
                mat.conductivity)
        else:
            s = r'$\epsilon_r$={0:.2f}'.format(
                mat.eps) + '\n' + r'$\sigma$={0:.2f}'.format(mat.conductivity)
        axes[1].annotate(s,
                         xy=(media_repr_1.get_x() + 0.1,
                             media_repr_1.get_y() + 0.2),
                         color='black')
    for src in Grid.sources:
        src_repr = Rectangle(xy=(src.position - 0.5, -src.ampl),
                             height=2 * src.ampl,
                             width=1,
                             color='red',
                             alpha=0.3)
        axes[0].add_patch(src_repr)

    for obs in Grid.local_observers:
        if isinstance(obs, QuasiHarmonicObserver):
            obs_repr = Rectangle(xy=(obs.position - 0.5, -1.4),
                                 height=2.8,
                                 width=1,
                                 color='green',
                                 alpha=0.3)
        elif isinstance(obs, E_FFTObserver):
            obs_repr = Rectangle(xy=(obs.position - 0.5, -1.4),
                                 height=2.8,
                                 width=1,
                                 color='indigo',
                                 alpha=0.3)
        elif isinstance(obs, P_FFTObserver):
            obs_repr = Rectangle(xy=(obs.position - 0.5, -1.4),
                                 height=2.8,
                                 width=1,
                                 color='orange',
                                 alpha=0.3)
        elif isinstance(obs, MovingFrame):
            obs_repr = Rectangle(xy=(obs.position[0] - 0.5, -1.4),
                                 height=2.8,
                                 width=obs.position[-1] - obs.position[0],
                                 fc='none',
                                 color='red')
        axes[0].add_patch(obs_repr)
    fig.tight_layout()
    plt.show()
Exemple #22
0
    def __init__(self, grid_obj, final_timestep):
        self.grid = grid_obj
        fig_ani, self.axes_ani = plt.subplots(2, 1, dpi=100, figsize=(12, 10))
        self.final_timestep = final_timestep
        self.grid.timesteps = self.final_timestep

        self.axes_ani[0].xaxis.grid(linestyle='dotted')
        self.axes_ani[0].set_xlabel('Cell')
        self.axes_ani[0].set_ylabel(r'$E_z$', fontsize=12, rotation=0)
        self.axes_ani[1].xaxis.grid(linestyle='dotted')
        self.axes_ani[1].set_xlabel('Cell')
        self.axes_ani[1].set_ylabel(r'$H_y$', fontsize=12, rotation=0)

        self.line_0 = Line2D([], [])
        self.line_1 = Line2D([], [])

        self.axes_ani[0].add_line(self.line_0)
        self.axes_ani[1].add_line(self.line_1)

        self.axes_ani[0].set_xlim([0, self.grid.nx - 1])
        self.axes_ani[1].set_xlim([0, self.grid.nx - 1])
        self.axes_ani[0].set_ylim([-1.5, 1.5])
        self.axes_ani[1].set_ylim(
            [-1.5 / np.sqrt(mu0 / eps0), 1.5 / np.sqrt(mu0 / eps0)])

        for mat in self.grid.materials:
            media_repr_0 = Rectangle(xy=(mat.position[0] - 0.5, -1.4),
                                     height=2.8,
                                     width=(mat.position[-1] -
                                            mat.position[0] + 1),
                                     color='grey',
                                     fill=True,
                                     alpha=mat.eps * 0.12)
            self.axes_ani[0].add_patch(media_repr_0)
            if mat.model == 'Lorentz':
                s = r'$\epsilon(\omega)$' + '\n' + r'$\sigma$={0:.2f}'.format(
                    mat.conductivity)
            else:
                s = r'$\epsilon_r$={0:.2f}'.format(
                    mat.eps) + '\n' + r'$\sigma$={0:.2f}'.format(
                        mat.conductivity)

            self.axes_ani[0].annotate(s,
                                      xy=(media_repr_0.get_x() + 0.1,
                                          media_repr_0.get_y() + 0.2),
                                      color='black')
            media_repr_1 = Rectangle(xy=(mat.position[0] - 0.5, -1.4),
                                     height=2.8,
                                     width=(mat.position[-1] -
                                            mat.position[0] + 1),
                                     color='grey',
                                     fill=True,
                                     alpha=mat.eps * 0.12)
            self.axes_ani[1].add_patch(media_repr_1)

        for src in self.grid.sources:
            src_repr = Rectangle(xy=(src.position - 0.5, -src.ampl),
                                 height=2 * src.ampl,
                                 width=1,
                                 color='red',
                                 alpha=0.3)
            self.axes_ani[0].add_patch(src_repr)

        for obs in self.grid.local_observers:
            if isinstance(obs, QuasiHarmonicObserver):
                obs_repr = Rectangle(xy=(obs.position - 0.5, -1.4),
                                     height=2.8,
                                     width=1,
                                     color='green',
                                     alpha=0.3)
            elif isinstance(obs, E_FFTObserver):
                obs_repr = Rectangle(xy=(obs.position - 0.5, -1.4),
                                     height=2.8,
                                     width=1,
                                     color='indigo',
                                     alpha=0.3)
            elif isinstance(obs, P_FFTObserver):
                obs_repr = Rectangle(xy=(obs.position - 0.5, -1.4),
                                     height=2.8,
                                     width=1,
                                     color='orange',
                                     alpha=0.3)
            self.axes_ani[0].add_patch(obs_repr)

        ani.TimedAnimation.__init__(self,
                                    fig_ani,
                                    blit=True,
                                    interval=5,
                                    repeat=False)
Exemple #23
0
class SpanSelector(_SelectorWidget):
    """Custom SpanSelector."""

    # pylint: disable=too-many-instance-attributes
    # pylint: disable=too-many-arguments
    # pylint: disable=attribute-defined-outside-init
    # pylint: disable=invalid-name
    def __init__(self,
                 ax,
                 onselect,
                 direction,
                 minspan=None,
                 useblit=False,
                 rectprops=None,
                 onmove_callback=None,
                 span_stays=False,
                 button=None):

        _SelectorWidget.__init__(self,
                                 ax,
                                 onselect,
                                 useblit=useblit,
                                 button=button)

        if rectprops is None:
            rectprops = dict(facecolor='red', alpha=0.5)

        rectprops['animated'] = self.useblit

        if direction not in ['horizontal', 'vertical']:
            msg = "direction must be in [ 'horizontal' | 'vertical' ]"
            raise ValueError(msg)
        self.direction = direction

        self.rect = None
        self.pressv = None

        self.rectprops = rectprops
        self.onmove_callback = onmove_callback
        self.minspan = minspan
        self.span_stays = span_stays

        # Needed when dragging out of axes
        self.prev = (0, 0)

        # Reset canvas so that `new_axes` connects events.
        self.canvas = None
        self.new_axes(ax)

    def new_axes(self, ax):
        """Set SpanSelector to operate on a new Axes"""
        self.ax = ax
        if self.canvas is not ax.figure.canvas:
            if self.canvas is not None:
                self.disconnect_events()

            self.canvas = ax.figure.canvas
            self.connect_default_events()

        if self.direction == 'horizontal':
            trans = blended_transform_factory(self.ax.transData,
                                              self.ax.transAxes)
            w, h = 0, 2
        else:
            trans = blended_transform_factory(self.ax.transAxes,
                                              self.ax.transData)
            w, h = 1, 0
        self.rect = Rectangle((0, -0.5),
                              w,
                              h,
                              transform=trans,
                              visible=False,
                              **self.rectprops)
        if self.span_stays:
            self.stay_rect = Rectangle((0, 0),
                                       w,
                                       h,
                                       transform=trans,
                                       visible=False,
                                       **self.rectprops)
            self.stay_rect.set_animated(False)
            self.ax.add_patch(self.stay_rect)

        self.ax.add_patch(self.rect)
        self.artists = [self.rect]

    def set_rectprops(self, rectprops):
        """Custom: set new rectprops."""
        self.rectprops = rectprops
        self.new_axes(self.ax)

    def ignore(self, event):
        """return *True* if *event* should be ignored"""
        return _SelectorWidget.ignore(self, event) or not self.visible

    def _press(self, event):
        """on button press event"""
        if self.ignore(event):
            return True
        self.rect.set_visible(self.visible)
        if self.span_stays:
            self.stay_rect.set_visible(False)
            # really force a draw so that the stay rect is not in
            # the blit background
            if self.useblit:
                self.canvas.draw()
        xdata, ydata = self._get_data(event)
        if self.direction == 'horizontal':
            self.pressv = xdata
        else:
            self.pressv = ydata
        return False

    def _release(self, event):
        """on button release event"""
        if self.ignore(event):
            return True
        if self.pressv is None:
            return True
        self.buttonDown = False

        self.rect.set_visible(False)

        if self.span_stays:
            self.stay_rect.set_x(self.rect.get_x())
            self.stay_rect.set_y(self.rect.get_y())
            self.stay_rect.set_width(self.rect.get_width())
            self.stay_rect.set_height(self.rect.get_height())
            self.stay_rect.set_visible(True)

        self.canvas.draw_idle()
        vmin = self.pressv
        xdata, ydata = self._get_data(event)
        if self.direction == 'horizontal':
            vmax = xdata or self.prev[0]
        else:
            vmax = ydata or self.prev[1]

        if vmin > vmax:
            vmin, vmax = vmax, vmin
        span = vmax - vmin
        if self.minspan is not None and span < self.minspan:
            return True
        self.onselect(vmin, vmax)
        self.pressv = None
        return False

    def _onmove(self, event):
        """on motion notify event"""
        if self.ignore(event):
            return True
        if self.pressv is None:
            return True
        x, y = self._get_data(event)
        if x is None:
            return True

        self.prev = x, y
        if self.direction == 'horizontal':
            v = x
        else:
            v = y

        minv, maxv = v, self.pressv
        if minv > maxv:
            minv, maxv = maxv, minv
        if self.direction == 'horizontal':
            self.rect.set_x(minv)
            self.rect.set_width(maxv - minv)
        else:
            self.rect.set_y(minv)
            self.rect.set_height(maxv - minv)

        if self.onmove_callback is not None:
            vmin = self.pressv
            xdata, ydata = self._get_data(event)
            if self.direction == 'horizontal':
                vmax = xdata or self.prev[0]
            else:
                vmax = ydata or self.prev[1]

            if vmin > vmax:
                vmin, vmax = vmax, vmin
            self.onmove_callback(vmin, vmax)

        self.update()
        return False
Exemple #24
0
class CustomToolbar(NavToolbar):

    toolitems = NavToolbar.toolitems + (
        (None, None, None, None),
        ("ROI", "Select ROI", "selection", "_on_custom_select"),
    )

    def __init__(self, plotCanvas):
        # create the default toolbar
        NavToolbar.__init__(self, plotCanvas)
        self.selector = RectSelector(
            self.canvas.figure.axes[0], self.onSelect, button=[1, 3], minspanx=5, minspany=5  # don't use middle button
        )
        self.selector.set_active(True)
        self.ax = self.canvas.figure.axes[0]
        self.roi = None
        self.fixedSize = False
        if wx.Platform == "__WXMAC__":
            self.to_draw = Rectangle(
                (0, 0), 0, 1, visible=False, facecolor="yellow", edgecolor="black", alpha=0.5, fill=True
            )
            self.ax.add_patch(self.to_draw)
            self.background = None

    def _init_toolbar(self):
        self._parent = self.canvas.GetParent()

        self.wx_ids = {}
        for text, tooltip_text, image_file, callback in self.toolitems:
            if text is None:
                self.AddSeparator()
                continue
            self.wx_ids[text] = wx.NewId()
            try:
                bitmap = _load_bitmap(image_file + ".png")
            except IOError:
                bitmap = wx.Bitmap(image_file + ".png")
            if text in ["Pan", "Zoom", "ROI"]:
                self.AddCheckTool(self.wx_ids[text], bitmap, shortHelp=text, longHelp=tooltip_text)
            else:
                self.AddSimpleTool(self.wx_ids[text], bitmap, text, tooltip_text)
            bind(self, wx.EVT_TOOL, getattr(self, callback), id=self.wx_ids[text])

        self.ToggleTool(self.wx_ids["ROI"], True)
        self.Realize()

    def _set_markers(self):
        self.canvas.parentFrame.set_markers()

    def _update_view(self):
        NavToolbar._update_view(self)
        self._set_markers()
        # MacOS needs a forced draw to update plot
        if wx.Platform == "__WXMAC__":
            self.canvas.draw()

    def draw(self):
        self._set_markers()
        NavToolbar.draw(self)
        # MacOS needs a forced draw to update plot
        if wx.Platform == "__WXMAC__":
            self.canvas.draw()

    def zoom(self, ev):
        if wx.Platform == "__WXMAC__":
            self.ToggleTool(self.wx_ids["Zoom"], self.GetToolState(self.wx_ids["Zoom"]))
        NavToolbar.zoom(self, ev)

    def pan(self, ev):
        if wx.Platform == "__WXMAC__":
            self.ToggleTool(self.wx_ids["Pan"], self.GetToolState(self.wx_ids["Pan"]))
        NavToolbar.pan(self, ev)

    def press_zoom(self, ev):
        if wx.Platform == "__WXMAC__":
            self.update_background()
            self.to_draw.set_visible(True)
        NavToolbar.press_zoom(self, ev)

    def release_zoom(self, ev):
        if wx.Platform == "__WXMAC__":
            self.to_draw.set_visible(False)
        NavToolbar.release_zoom(self, ev)

    def draw_rubberband(self, event, x0, y0, x1, y1):
        # XOR does not work on MacOS ...
        if wx.Platform != "__WXMAC__":
            NavToolbar.draw_rubberband(self, event, x0, y0, x1, y1)
        else:
            if self.background is not None:
                self.canvas.restore_region(self.background)
            c0, c1 = self.ax.transData.inverted().transform([[x0, y0], [x1, y1]])
            l, b = c0
            r, t = c1
            self.to_draw.set_bounds(l, b, r - l, t - b)
            self.ax.draw_artist(self.to_draw)
            self.canvas.blit(self.ax.bbox)

    def update_background(self):
        """force an update of the background"""
        self.background = self.canvas.copy_from_bbox(self.ax.bbox)

    # Turn on selection
    # TODO: Proper handling of states, actual functionality.
    def _on_custom_select(self, evt):
        #        for id in ['Zoom','Pan']:
        #            self.ToggleTool(self.wx_ids[id], False)
        #        print('Select ROI: %s' % (self.GetToolState(self.wx_ids['ROI'])))
        #        self.ToggleTool(self.wx_ids['ROI'],
        #                self.GetToolState(self.wx_ids['ROI']) )
        self.toggle_selector()

    #        print('Select ROI: %s' % (self.GetToolState(self.wx_ids['ROI'])))

    def onSelect(self, eclick, erelease):
        "eclick and erelease are matplotlib events at press and release"
        #        print(' startposition : (%f, %f)' % (eclick.xdata, eclick.ydata))
        #        print(' endposition   : (%f, %f)' % (erelease.xdata, erelease.ydata))
        #        print(' used button   : ', eclick.button)
        self.updateROI(
            min(eclick.xdata, erelease.xdata),
            min(eclick.ydata, erelease.ydata),
            abs(eclick.xdata - erelease.xdata),
            abs(eclick.ydata - erelease.ydata),
        )
        if self.canvas.parentFrame.fixedNumberCB.IsChecked():
            # We are working in the fixed-number mode
            # We need to find new roi for this center point
            # The handler will call the update ROI function for us.
            self.canvas.parentFrame.handleROIforN()

    def updateROI(self, x, y, w, h):
        if self.roi is None:
            # print('upd ROI:', x, y, w, h)
            self.roi = Rectangle((x, y), w, h, ls="solid", lw=2, color="r", fill=False, zorder=5)
            self.canvas.figure.axes[0].add_patch(self.roi)
        else:
            self.roi.set_bounds(x, y, w, h)
        self.updateCanvas()

    def toggle_selector(self):
        self.selector.set_active(not self.selector.active)

    def onFixedSize(self, ev):
        self.fixedSize = ev.IsChecked()
        self.updateCanvas()

    def onWidthChange(self, ev):
        if self.roi:
            x = self.roi.get_x()
            w = self.roi.get_width()
            nw = ev.GetValue()
            dw = {"C": (w - nw) / 2, "L": 0, "R": w - nw}[self.canvas.parentFrame.anchorRB.GetStringSelection()[0]]
            self.roi.set_x(x + dw)
            self.roi.set_width(nw)
            self.updateCanvas()

    def onHeightChange(self, ev):
        if self.roi:
            y = self.roi.get_y()
            h = self.roi.get_height()
            nh = ev.GetValue()
            dh = {"C": (h - nh) / 2, "B": 0, "T": h - nh}[self.canvas.parentFrame.anchorRB.GetStringSelection()[-1]]
            self.roi.set_y(y + dh)
            self.roi.set_height(nh)
            self.updateCanvas()

    def updateCanvas(self, redraw=True):
        if self.roi:
            self.canvas.parentFrame.showROI(
                self.roi.get_x(), self.roi.get_y(), self.roi.get_width(), self.roi.get_height()
            )
            self.canvas.parentFrame.setWH(self.roi.get_width(), self.roi.get_height())
            if self.fixedSize:
                self.selector.setSize(self.roi.get_width(), self.roi.get_height())
            else:
                self.selector.setSize()
        if redraw:
            self.draw()
class WindowSelectionRectangle(object):
    def __init__(self, event, axis, on_window_selection_callback):
        self.axis = axis
        if event.inaxes != self.axis:
            return
        # Store the axes it has been initialized in.
        self.axes = event.inaxes
        ymin, ymax = self.axes.get_ylim()
        self.min_x = event.xdata
        self.intial_selection_active = True
        self.rect = Rectangle((event.xdata, ymin),
                              0,
                              ymax - ymin,
                              color="0.3",
                              alpha=0.5,
                              edgecolor="0.5")
        self.axes.add_patch(self.rect)
        # Get the canvas.
        self.canvas = self.rect.figure.canvas

        # Use blittig for fast animations.
        self.rect.set_animated(True)
        self.background = self.canvas.copy_from_bbox(self.rect.axes.bbox)

        self._connect()

        self.on_window_selection_callback = on_window_selection_callback

    #def __del__(self):
    #"""
    #Disconnect the events upon deallocating.
    #"""
    #self.canvas.mpl_disconnect(self.conn_button_press)
    #self.canvas.mpl_disconnect(self.conn_button_release)
    #self.canvas.mpl_disconnect(self.conn_mouse_motion)

    def _connect(self):
        """
        Connect to the necessary events.
        """
        self.conn_button_press = self.rect.figure.canvas.mpl_connect(
            'button_press_event', self.on_button_press)
        self.conn_button_release = self.rect.figure.canvas.mpl_connect(
            'button_release_event', self.on_button_release)
        self.conn_mouse_motion = self.rect.figure.canvas.mpl_connect(
            'motion_notify_event', self.on_mouse_motion)

    def on_button_press(self, event):
        pass

    def on_button_release(self, event):
        if event.inaxes != self.axis:
            return

        if event.button != 1:
            return
        # turn off the rect animation property and reset the background
        self.rect.set_animated(False)
        self.background = None

        self.intial_selection_active = False
        self.canvas.draw()

        x = self.rect.get_x()
        width = self.rect.get_width()

        if width < 0:
            x = x + width
            width = abs(width)

        self.on_window_selection_callback(x, width, self.axis)

    def on_mouse_motion(self, event):
        if event.button != 1 or \
                self.intial_selection_active is not True:
            return
        if event.xdata is not None:
            self.rect.set_width(event.xdata - self.min_x)

        # restore the background region
        self.canvas.restore_region(self.background)
        # redraw just the current rectangle
        self.axes.draw_artist(self.rect)
        # blit just the redrawn area
        self.canvas.blit(self.axes.bbox)