Ejemplo n.º 1
0
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)
Ejemplo n.º 2
0
class click_yrange:
   '''An interactive yrange selector.  Given an axis and a starting
   y0 location, draw a full-width rectange that follows the mouise.
   Similar to click_window, but more appropriate for selecting out
   a y-range.'''

   def __init__(self, ax, y0):
      self.ax = ax
      self.y0 = y0
      x0,x1 = ax.get_xbound()
      self.rect = Rectangle((x0,y0), width=(x1-x0), height=0, 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_height(event.ydata - self.y0)
      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.y0, self.rect.get_y()+self.rect.get_height())
Ejemplo n.º 3
0
class click_yrange:
    '''An interactive yrange selector.  Given an axis and a starting
   y0 location, draw a full-width rectange that follows the mouise.
   Similar to click_window, but more appropriate for selecting out
   a y-range.'''
    def __init__(self, ax, y0):
        self.ax = ax
        self.y0 = y0
        x0, x1 = ax.get_xbound()
        self.rect = Rectangle((x0, y0), width=(x1 - x0), height=0, 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_height(event.ydata - self.y0)
        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.y0, self.rect.get_y() + self.rect.get_height())
Ejemplo n.º 4
0
def up_down(rect: Rectangle,
            to: tuple,
            step: float = .1,
            interval: float = .001):
    new_step = step
    if to[1] < rect.get_y():
        new_step *= -1

    figure = rect.figure

    up = rect.get_y() < to[1]

    while (rect.get_y() - to[1] <= step / 2) if up else (
            rect.get_y() - to[1] >= step / 2):
        rect.set_y(rect.get_y() + new_step)
        figure.canvas.draw()
        figure.canvas.flush_events()
        plt.pause(interval)
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()
Ejemplo n.º 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()
Ejemplo n.º 7
0
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)
Ejemplo n.º 8
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
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)
Ejemplo n.º 10
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
Ejemplo n.º 11
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()
Ejemplo n.º 12
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()
Ejemplo n.º 13
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()
Ejemplo n.º 14
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)
Ejemplo n.º 15
0
class ComboBox(LineEdit):
    """
    A ComboxBox, upon clicking button, drops down a list of items to choose.

    Items can be edited also.
    """
    def __init__(self,
                 width,
                 height,
                 text_list,
                 edit_notify=None,
                 selection_notify=None,
                 **kwargs):

        if len(text_list) > 0:
            text = text_list[0]
        else:
            text = ''

        super(ComboBox, self).__init__(width, height, text,
                                       self._on_edit_notify, **kwargs)

        self._text_list = text_list

        self._edit_notify = edit_notify
        self._selection_notify = selection_notify

        if edit_notify and not callable(edit_notify):
            raise RuntimeError('edit_notify must be a callable function')

        if selection_notify and not callable(selection_notify):
            raise RuntimeError('selection_notify must be a callable function')

        #---------------------------------------------------------------------
        # additional items
        #
        # selection axes for showing the possible selections
        # a rectangle to highlight the selection row
        # a button to show the selection drop down

        self._select_axes = None
        self._select_highlight = None  # just a rectangle
        self._select_posx = None
        self._select_entries = []

        self._cb_state = ComboState.IDLE

        self._n_lines = 5

        self._mouse_motion_cid = None

        self._ignore_edit_notify = False

    def _render(self, fig, x, y):

        super(ComboBox, self)._render(fig, x, y)

        self._render_dropdown_button(fig)
        self._render_dropdown_axis(fig, x, y)

    def _render_dropdown_axis(self, fig, x, y):

        W, H = fig.get_size_inches()

        h = self._n_lines * self._height

        y -= h

        x /= W
        y /= H

        # create the other gui assets but keep them hidden

        # selection axes, same width, by 10 times in height

        w = self._width / W
        h /= H

        ax = fig.add_axes([x, y, w, h], xticks=[], yticks=[])

        ax.set_xlim([0, self._width])
        ax.set_ylim([0, self._n_lines * self._height])

        ax.set_axis_bgcolor('white')

        ax.set_visible(False)

        ax.set_zorder(1000)

        self._select_axes = ax

    def _render_dropdown_button(self, fig):

        w, h = 0.25, 0.125

        hw = w / 2.0
        hh = h / 2.0

        x = self._width - w - 0.02
        y = (self._height - h) / 2.0

        self._select_posx = x + hw

        # Three point polygon:
        #
        #  2 O-----O 3
        #     \   /
        #      \ /
        #       O
        #       1
        #

        points = [
            [x + hw, y],
            [x, y + h],
            [x + w, y + h],
        ]

        points = np.array(points)

        patch = Polygon(points, closed=True, ec='black', fc='black')

        self._axes.add_patch(patch)

    def _cb_change_state(self, new_state):

        if self._cb_state == new_state:
            raise RuntimeError("Already in state %s" % new_state)

        if self._cb_state == ComboState.IDLE:

            if new_state == ComboState.DROP_SELECT:
                self._select_axes.set_visible(True)

                x = self._pad_left
                y = ((self._n_lines - 1) * self._height)

                #--------------------------------------------------------------
                # create highlight

                if self._select_highlight is None:

                    self._select_highlight = Rectangle(
                        (0, y - self._height / 2.0),
                        self._width,
                        self._height,
                        ec=self._hl_color,
                        fc=self._hl_color)

                    self._select_axes.add_patch(self._select_highlight)

                else:
                    self._select_highlight.set_visible(True)

                # delete existing text objects

                for t in self._select_entries:
                    t.remove()
                    del t

                self._select_entries = []

                for t in self._text_list:

                    txt = self._select_axes.text(x,
                                                 y,
                                                 t,
                                                 ha='left',
                                                 va='center')

                    y -= self._height

                    self._select_entries.append(txt)

                self._mouse_motion_cid = self._select_axes.figure.canvas.mpl_connect(
                    'motion_notify_event', self._on_mouse_motion)

                self.canvas().draw()

            else:
                self._unhandled_state(new_state)

        elif self._cb_state == ComboState.DROP_SELECT:

            if new_state == ComboState.IDLE:
                self._select_axes.set_visible(False)
                self.canvas().draw()

            else:
                self._unhandled_state(new_state)

        else:
            self._unhandled_state(new_state)

        self._cb_state = new_state

    def _unhandled_state(self, new_state):
        if _DEV:
            print("unhandled %s --> %s" % (self._cb_state, new_state))

    def _on_mouse_down(self, event):

        if event.inaxes not in [self._axes, self._select_axes]:
            self._ignore_edit_notify = True
            if self._cb_state != ComboState.IDLE:
                self._cb_change_state(ComboState.IDLE)
            super(ComboBox, self)._on_mouse_down(event)
            return

        x, y = event.xdata, event.ydata

        if x is None or y is None:
            super(ComboBox, self)._on_mouse_down(event)
            return

        if self._cb_state == ComboState.IDLE:

            cx = self._select_posx

            d = np.sqrt((x - cx)**2)

            if d <= 0.16:
                self._cb_change_state(ComboState.DROP_SELECT)

            else:
                super(ComboBox, self)._on_mouse_down(event)

        elif self._cb_state == ComboState.DROP_SELECT:

            y = self._select_highlight.get_y()

            idx = self._find_text_entry(y)

            selection = self._text_list[idx]

            self._ignore_edit_notify = True

            self.text(selection)

            if self._selection_notify:
                self._selection_notify(idx, selection)

            self._cb_change_state(ComboState.IDLE)

        elif _DEV:
            print("on_mouse_down(): unhandled %s" % self._cb_state)

    def _on_mouse_motion(self, event):

        if event.inaxes != self._select_axes:
            return

        x, y = event.xdata, event.ydata

        if x is None or y is None:
            return

        if self._cb_state == ComboState.DROP_SELECT:

            idx = self._find_text_entry(y)

            _, y = self._select_entries[idx].get_position()

            self._select_highlight.set_y(y - self._height / 2.0)

            self.canvas().draw()

    def _on_key_press(self, event):

        if event.key == 'escape' and self._cb_state == ComboState.DROP_SELECT:
            self._cb_change_state(ComboState.IDLE)

        self._ignore_edit_notify = False

        super(ComboBox, self)._on_key_press(event)

    def _find_text_entry(self, y):

        # find nearest text

        dist = []

        for txt in self._select_entries:

            _, ydata = txt.get_position()

            d = np.abs(ydata - y)

            dist.append(d)

        return np.argmin(dist)

    def _on_edit_notify(self, text):

        if self._ignore_edit_notify:
            self._ignore_edit_notify = False
            return

        # add to the list

        self._text_list.append(text)

        if self._edit_notify:
            self._edit_notify(text)
Ejemplo n.º 16
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)
Ejemplo n.º 17
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()
Ejemplo n.º 18
0
class CaseSelector(_my_SelectorWidget):
    """
    Select a min/max range of the x or y axes for a matplotlib Axes.
    For the selector to remain responsive you much keep a reference to
    it.
    Example usage::
        ax = subplot(111)
        ax.plot(x,y)
        def onselect(vmin, vmax):
            print vmin, vmax
        span = SpanSelector(ax, onselect, 'horizontal')
    *onmove_callback* is an optional callback that is called on mouse
    move within the span range
    """

    def __init__(self, ax, onselect, onclick, minspan=0.1, nrect=10,
                 update_on_ext_event=True,
                 rectprops=None, stay_rectprops=None, lineprops=None,
                 onmove_callback=None, button=None):
        """
        Create a case selector in *ax*.  When a selection is made, call 
        *onselect* with::
            onselect(vmin, vmax)
        and clear the span.
        *direction* must be 'horizontal' or 'vertical'
        If *minspan* is not *None*, ignore events smaller than *minspan*
        The span rectangle is drawn with *rectprops*; default::
          rectprops = dict(facecolor='red', alpha=0.5)
        Set the visible attribute to *False* if you want to turn off
        the functionality of the span selector
        If *span_stays* is True, the span stays visble after making
        a valid selection.
        *button* is a list of integers indicating which mouse buttons should
        be used for selection.  You can also specify a single
        integer if only a single button is desired.  Default is *None*,
        which does not limit which button can be used.
        Note, typically:
         1 = left mouse button
         2 = center mouse button (scroll wheel)
         3 = right mouse button
        """
        _my_SelectorWidget.__init__(self, ax, onselect, update_on_ext_event, button=button)

        if rectprops is None:
            self.rectprops = dict(facecolor='#f5f5f5', alpha=0.3)
        else:
            self.rectprops = rectprops
        if lineprops is None:
            self.lineprops = dict(color='#e66101', lw=2)  # bar color
        else:
            self.lineprops = lineprops

        if not isinstance(nrect, list):
            nrect = [nrect]
        if stay_rectprops is None:
            cc = ['#984ea3', '#ffff33', '#d8b365', '#5ab4ac']  # green yellow blue red
            color = [cc[i % len(cc)] for i in range(0, len(nrect))]
            self.stay_rectprops = [dict(facecolor=c, alpha=0.5) for c in color]
        else:
            assert (len(nrect) == len(stay_rectprops))
            self.stay_rectprops = stay_rectprops

        self.pressv = None
        self.onclick = onclick
        self.onmove_callback = onmove_callback
        self.minspan = minspan

        # 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, nrect)

    def new_axes(self, ax, nrect):
        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()
        # span
        trans = blended_transform_factory(self.ax.transData, self.ax.transAxes)
        w, h = 0, 1
        self.rect = Rectangle((0, 0), w, h, transform=trans, visible=False,
                              animated=True, **self.rectprops)
        self.ax.add_patch(self.rect)
        self.artists = [self.rect]
        # stay rect
        self.stay_rects = []
        for set in range(0, len(nrect)):
            self.stay_rects.append([])
            for n in range(0, nrect[set]):
                stay_rect = Rectangle((0, 0), w, h, transform=trans, visible=False,
                                      animated=True, **self.stay_rectprops[set])
                self.ax.add_patch(stay_rect)
                self.stay_rects[set].append(stay_rect)
            self.artists.extend(self.stay_rects[set])
        # bar
        self.bar = ax.axvline(0, w, h, visible=False, **self.lineprops)
        self.artists.append(self.bar)

    def set_bar_position(self, x):
        self.bar.set_xdata(x)
        self.bar.set_visible(True)

    def set_stay_rects_x_bounds(self, xarr, set=0):
        for n, stay_rect in enumerate(self.stay_rects[set]):
            try:
                xmin, xmax = xarr[n]
            except IndexError:
                stay_rect.set_visible(False)
            else:
                stay_rect.set_x(xmin)
                stay_rect.set_y(self.rect.get_y())
                stay_rect.set_width(abs(xmax - xmin))
                stay_rect.set_height(self.rect.get_height())
                stay_rect.set_visible(True)

    def set_stay_rect_visible(self, b=True, set=0):
        for stay_rect in self.stay_rects[set]:
            stay_rect.set_visible(b)

    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"""
        xdata, ydata = self._get_data(event)
        self.pressv = xdata
        return False

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

        self.rect.set_visible(False)
        vmin = self.pressv
        xdata, ydata = self._get_data(event)
        vmax = xdata or self.prev[0]

        if vmin > vmax:
            vmin, vmax = vmax, vmin
        span = vmax - vmin
        if span < self.minspan and event.button == 3:  # right click to remove span
            self.onclick(vmin)
            return
        elif span > self.minspan and event.button == 1:
            self.onselect(vmin, vmax)
            self.pressv = None
            return False
        elif span > self.minspan and event.button == 3:
            self.onselect(vmin, vmax, True)
            self.pressv = None
            return False

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

        self.prev = x, y
        v = x
        minv, maxv = v, self.pressv
        if minv > maxv:
            minv, maxv = maxv, minv
        self.rect.set_x(minv)
        self.rect.set_width(maxv - minv)

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

            if vmin > vmax:
                vmin, vmax = vmax, vmin
            self.onmove_callback(vmin, vmax)
        if not self.update_on_ext_event:
            self.update()
        return False
Ejemplo n.º 19
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)
Ejemplo n.º 20
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)
Ejemplo n.º 21
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
Ejemplo n.º 22
0
class identificationWidget(QMainWindow):
    def __init__(self):
        super().__init__()
        self.peakThreshold = 0.01
        self.peakNumber = 50
        self.peakDistance = 2
        self.pickDistance = 1
        self.wavelengthPixelList = pd.DataFrame({
            'Wavelength': [' '],
            'Pixel': [' ']
        })
        self.selfPeakPixs = []
        self.standardPeakWavelengths = []
        self.matchedPeakPixs = []
        self.isPressed = False
        self.isPicked = False
        self.isMatchFinished = True
        self.standardSpectrum = []
        self.selfSpectrum = []
        self.currentPickedPeakWavelength = 0
        self.selfFWHM = 2
        self.REIDYStep = 2
        self.selfImageY = [0, 0]
        self.selfData = []
        self.reidentificationWidget = reIdentificationWidget(
            matchList=self.wavelengthPixelList,
            flux=self.selfData,
            FWHM=self.selfFWHM)

        self.initUI()

    def initUI(self):
        self.layout = QGridLayout()
        self.mainWidget = QWidget()

        self.calibrationFileOpenAction = QAction('CalibrationFileOpen', self)
        self.calibrationFileOpenAction.setShortcut('Ctrl+C')
        self.calibrationFileOpenAction.triggered.connect(
            self.onCalibnationFileOpen)
        self.calibrationFileOpenAction.setStatusTip('Open calibration image')

        #직접 찍은 아이덴티피케이션 이미지의 스펙트럼을 보여주는 fig

        self.selfSpectrumCanvas = FigureCanvas(Figure(figsize=(13, 5)))

        self.selfSpectrumCanvas.figure.clear()

        self.peakNumberSlider = QSlider(Qt.Horizontal, self)
        self.peakNumberSlider.setValue(self.peakNumber)
        self.peakNumberSlider.setRange(1, 100)
        self.peakDistanceSlider = QSlider(Qt.Horizontal, self)
        self.peakDistanceSlider.setValue(self.peakDistance)
        self.peakNumberSlider.setRange(1, 10)
        self.peakThresholdSlider = QSlider(Qt.Horizontal, self)
        self.peakThresholdSlider.setValue(int(self.peakThreshold * 100))
        self.peakNumberSlider.setRange(1, 100)

        self.peakNumberLabel = QLabel(f'Number of Peak = {self.peakNumber}')
        self.peakDistanceLabel = QLabel(
            f'Distance between Peak = {self.peakDistance}')
        self.peakThresholdLabel = QLabel(
            f'Threshold of peak = {self.peakThreshold}')

        self.peakNumberSlider.valueChanged.connect(
            self.onPeakNumberValueChanged)
        self.peakDistanceSlider.valueChanged.connect(
            self.onPeakDistanceValueChanged)
        self.peakThresholdSlider.valueChanged.connect(
            self.onPeakThresholdValueChanged)

        self.selfPeakControl = QWidget()
        self.peakControlLayout = QVBoxLayout()
        self.peakControlLayout.addWidget(self.peakNumberLabel)
        self.peakControlLayout.addWidget(self.peakNumberSlider)
        self.peakControlLayout.addWidget(self.peakDistanceLabel)
        self.peakControlLayout.addWidget(self.peakDistanceSlider)
        self.peakControlLayout.addWidget(self.peakThresholdLabel)
        self.peakControlLayout.addWidget(self.peakThresholdSlider)

        self.selfPeakControl.setLayout(self.peakControlLayout)

        # 직접 찍은 아이덴티피케이션 이미지를 보여주는 fig

        self.selfImageCanvas = FigureCanvas(Figure(figsize=(5, 2)))

        self.selfImageCanvas.mpl_connect("button_press_event",
                                         self.onPressAtImage)
        self.selfImageCanvas.mpl_connect("motion_notify_event",
                                         self.onMoveAtImage)
        self.selfImageCanvas.mpl_connect("button_release_event",
                                         self.onReleaseAtImage)

        self.selfSpectrumCanvas.mpl_connect('scroll_event',
                                            self.onScrollAtSelfSpectrum)
        self.selfSpectrumCanvas.mpl_connect('pick_event',
                                            self.onPickPeakAtSelfSpectrum)
        self.selfSpectrumCanvas.mpl_connect("button_press_event",
                                            self.onPressAtSelfSpectrum)
        self.selfSpectrumCanvas.mpl_connect("motion_notify_event",
                                            self.onMoveAtSelfSpectrum)
        self.selfSpectrumCanvas.mpl_connect("button_release_event",
                                            self.onReleaseAtSelfSpectrum)

        self.selfSpectrumGaussFitCanvas = FigureCanvas(Figure(figsize=(7, 7)))
        self.gaussFitWidget = QWidget()
        self.gaussFitLayout = QVBoxLayout()
        self.gaussFitButton = QPushButton('&Yes')
        self.gaussFitButton.clicked.connect(self.onGaussFitButtonClicked)
        self.FWHMSlider = QSlider(Qt.Horizontal, self)
        self.FWHMSlider.setValue(self.selfFWHM * 10)
        self.FWHMSlider.setRange(1, 100)

        self.FWHMLabel = QLabel(f'FHWM for comp image = {self.selfFWHM}')
        self.FWHMSlider.valueChanged.connect(self.onFWHMChanged)
        self.gaussFitLayout.addWidget(self.selfSpectrumGaussFitCanvas)
        self.gaussFitLayout.addWidget(self.FWHMSlider)
        self.gaussFitLayout.addWidget(self.FWHMLabel)
        self.gaussFitLayout.addWidget(self.gaussFitButton)
        self.gaussFitWidget.setLayout(self.gaussFitLayout)

        self.NeonArcButton = QPushButton('&Neon')
        self.NeonArcButton.clicked.connect(self.neonSpectrumDraw)
        self.OpenArcButton = QPushButton('&Open')

        self.standardSpectrumButtonLayout = QVBoxLayout()
        self.standardSpectrumButtonLayout.addWidget(self.NeonArcButton)
        self.standardSpectrumButtonLayout.addWidget(self.OpenArcButton)

        self.standardSpectrumButton = QWidget()
        self.standardSpectrumButton.setLayout(
            self.standardSpectrumButtonLayout)

        #비교할 아이덴티피케이션의 스펙트럼을 보여주는 fig

        self.standardSpectrumCanvas = FigureCanvas(Figure(figsize=(13, 5)))

        self.standardSpectrumCanvas.mpl_connect(
            'scroll_event', self.onScrollAtStandardSpectrum)
        self.standardSpectrumCanvas.mpl_connect(
            'pick_event', self.onPickPeakAtStandardSpectrum)
        self.standardSpectrumCanvas.mpl_connect('button_press_event',
                                                self.onPressAtStandardSpectrum)

        self.wavelengthPixelTable = QTableView()
        self.wavelengthPixelModel = tableModel(self.wavelengthPixelList)
        self.wavelengthPixelTable.setModel(self.wavelengthPixelModel)
        self.wavelengthPixelTable.setSelectionBehavior(QTableView.SelectRows)
        self.wavelengthPixelTable.doubleClicked.connect(
            self.onWavelengthPixelTableDoubleClicked)

        self.gaussButton = QPushButton('&GuassFit')
        self.gaussButton.clicked.connect(self.selfSpectrumDrawWithGauss)

        self.matchButton = QPushButton('&Match')
        self.matchButton.clicked.connect(self.onMatch)
        self.abortButton = QPushButton('&Abort')
        self.abortButton.clicked.connect(self.onAbort)
        self.exportButton = QPushButton('&Export')
        self.exportButton.clicked.connect(self.onExport)
        self.importButton = QPushButton('&Import')
        self.importButton.clicked.connect(self.onImport)

        self.tableMatchingButtons = QWidget()
        self.tableMatchingButtonLayout = QVBoxLayout()
        self.tableMatchingButtonLayout.addWidget(self.matchButton)
        self.tableMatchingButtonLayout.addWidget(self.abortButton)
        self.tableMatchingButtonLayout.addWidget(self.exportButton)
        self.tableMatchingButtonLayout.addWidget(self.importButton)
        self.tableMatchingButtons.setLayout(self.tableMatchingButtonLayout)

        self.setCentralWidget(self.mainWidget)
        menubar = self.menuBar()
        menubar.setNativeMenuBar(False)

        filemenu = menubar.addMenu('&File')  #&는 File을 Alt F로 실행하게 해준다
        filemenu.addAction(self.calibrationFileOpenAction)

        self.splitter = QSplitter(Qt.Horizontal)
        self.tables = QWidget()
        self.tableLayout = QVBoxLayout()
        self.tableLayout.addWidget(self.gaussButton)
        self.tableLayout.addWidget(self.wavelengthPixelTable)
        self.tableLayout.addWidget(self.tableMatchingButtons)
        self.tables.setLayout(self.tableLayout)

        self.splitter.addWidget(self.tables)
        self.spectrums = QWidget()
        self.spectrumsLayout = QVBoxLayout()
        self.spectrumsLayout.addWidget(self.selfSpectrumCanvas)
        self.spectrumsLayout.addWidget(self.standardSpectrumCanvas)
        self.spectrums.setLayout(self.spectrumsLayout)

        self.splitter.addWidget(self.spectrums)

        self.reidentificationBtn = QPushButton('&Reidentification')
        self.reidentificationBtn.clicked.connect(self.onReidentification)

        self.layout.addWidget(self.splitter, 1, 0, 3, 1)
        self.layout.addWidget(self.selfImageCanvas, 1, 1, 1, 1)
        self.layout.addWidget(self.selfPeakControl, 2, 1, 1, 1)
        self.layout.addWidget(self.standardSpectrumButton, 3, 1)
        self.layout.addWidget(self.reidentificationBtn, 4, 0, 1, -1)

        self.mainWidget.setLayout(self.layout)
        self.setCentralWidget(self.mainWidget)
        self.resize(1500, 800)
        self.center()

    '''
    칼리브레이션 파일(comp 파일)을 열고 Identification에 사용될 Y방향(Wavelength에 수직한 방향) 구간을 결정하는 메소드    
    '''

    def onCalibnationFileOpen(self):
        filePath = QFileDialog.getOpenFileName(
            self, 'Open calibration file',
            './Spectroscopy_Example/20181023/combine/')[0]
        hdr, data = openFitData(filePath)
        self.selfImageCanvas.figure.clear()
        self.selfImageAx = self.selfImageCanvas.figure.add_subplot(111)
        zimshow(self.selfImageAx, data)
        self.selfImageCanvas.draw()

        self.imageWidth = int(data.shape[1])
        self.selfData = data

    '''
    칼리브레이션파일 스펙트럼에서 Peak을 찾는 과정에 관여하는 3가지 initial value(number of peaks, distance btw peaks,
    threshol of peaks)를 조정해서 적절히 Peak을 찾을 수 있게 하는 메소드 
    Slider의 값을 받아서 canvas에 적용한다.
    '''

    def onPeakNumberValueChanged(self, val):
        self.peakNumber = val
        self.peakNumberLabel.setText(f'Number of Peak = {self.peakNumber}')
        self.selfSpectrumDraw(
            ymin=self.selfImageY[0],
            ymax=self.selfImageY[1],
            data=self.selfData,
            args=[self.peakDistance, self.peakThreshold, self.peakNumber])

    def onPeakDistanceValueChanged(self, val):
        self.peakDistance = val
        self.peakDistanceLabel.setText(
            f'Distance between Peak = {self.peakDistance}')
        self.selfSpectrumDraw(
            ymin=self.selfImageY[0],
            ymax=self.selfImageY[1],
            data=self.selfData,
            args=[self.peakDistance, self.peakThreshold, self.peakNumber])

    def onPeakThresholdValueChanged(self, val):
        self.peakThreshold = val / 100
        self.peakThresholdLabel.setText(
            f'Threshold of peak = {self.peakThreshold}')
        self.selfSpectrumDraw(
            ymin=self.selfImageY[0],
            ymax=self.selfImageY[1],
            data=self.selfData,
            args=[self.peakDistance, self.peakThreshold, self.peakNumber])

    '''
    칼리브레이션 스펙트럼 그래프를  확대/축소하는 메소드. 
    스크롤을 내리면 마우스 위치를 중심으로 xlim이 4/5배가 되고,
    스크롤을 올리면 마우스 위치를 중심으로 xlim이 5/4배가 된다.
    '''

    def onScrollAtSelfSpectrum(self, event):
        xmin, xmax = self.selfSpectrumAx.get_xlim()
        xnow = event.xdata
        if (event.button == 'up'):
            xsize = int((xmax - xmin) * 0.40)
            xmin = xnow - xsize
            xmax = xnow + xsize
            self.selfSpectrumAx.set_xlim(xmin, xmax)
            self.selfSpectrumAx.figure.canvas.draw()
        elif (event.button == 'down'):
            xsize = int((xmax - xmin) * 0.625)
            xmin = xnow - xsize
            xmax = xnow + xsize
            self.selfSpectrumAx.set_xlim(xmin, xmax)
            self.selfSpectrumAx.figure.canvas.draw()

    '''
    칼리브레이션 스펙트럼 그래프에서 pickPeak 메소드를 통해 생성된 peakPicker를 움직이고 그 값을 table에 저장하는 메소드
    두 가지 방식으로 peakPicker를 움직일 수 있다. 
    1. Drag and Drop :
        peakPicker를 클릭한 채로 끌어서 움직일 수 있고 마우스를 놓으면 위치가 고정된다. 
        peak 근처 peakDistance 픽셀에서는 자동으로 peak에 붙고 이때 색깔이 연두색으로 바뀐다. 
        마우스가 이동할때 그 픽셀값이 저장된다. 
    2. Double Click :
        selfSpectrum의 Peak의 text를 더블클릭하면 peakPicker 그 text로 이동하고 그 픽셀값이 저장된다. 
    '''

    def onPickPeakAtSelfSpectrum(self, event):
        if self.isMatchFinished: return
        if self.isPicked: return
        if (event.mouseevent.dblclick and event.artist != self.peakPicker):
            val = round(float(event.artist.get_text()), 4)
            self.wavelengthPixelList.loc[self.wavelengthPixelList.Wavelength ==
                                         self.currentPickedPeakWavelength,
                                         'Pixel'] = val
            self.peakPicker.remove()
            self.peakPicker = self.selfSpectrumAx.axvline(
                val, color='green', picker=True, pickradius=self.pickDistance)
            self.selfSpectrumAx.figure.canvas.draw()
            self.onChangedList()
            return
        if not event.mouseevent.button == 1: return
        self.isPicked = True

    def onMoveAtSelfSpectrum(self, event):
        if not event.inaxes: return
        if event.inaxes != self.selfSpectrumAx: return
        if not self.isPicked: return
        self.peakPicker.remove()
        dist = np.min(np.abs(self.selfPeakPixs - event.xdata))
        val = self.selfPeakPixs[np.argmin(
            np.abs(self.selfPeakPixs - event.xdata))]

        if (dist < self.peakDistance):
            self.peakPicker = self.selfSpectrumAx.axvline(
                val, color='green', picker=True, pickradius=self.pickDistance)
            self.wavelengthPixelList.loc[self.wavelengthPixelList.Wavelength ==
                                         self.currentPickedPeakWavelength,
                                         'Pixel'] = val

        else:
            self.peakPicker = self.selfSpectrumAx.axvline(
                event.xdata,
                color='blue',
                picker=True,
                pickradius=self.pickDistance)
            self.wavelengthPixelList.loc[self.wavelengthPixelList.Wavelength ==
                                         self.currentPickedPeakWavelength,
                                         'Pixel'] = int(event.xdata)

        self.selfSpectrumAx.figure.canvas.draw()
        self.onChangedList()

    def onReleaseAtSelfSpectrum(self, event):
        if not event.inaxes: return
        if event.inaxes != self.selfSpectrumAx: return
        if not self.isPicked: return
        self.isPicked = False

    '''
    칼리브레이션 스펙트럼 그래프에서 우클릭을 하면 pickPeak과정을 취소하는 메소드
    우클릭을 하면 self.onPickDisable을 호출한다.     
    '''

    def onPressAtSelfSpectrum(self, event):
        if (event.button == 3):
            self.onPickDisable()

    '''
    스탠다드 스펙트럼 그래프를 확대/축소하는 메소드. 
    스크롤을 내리면 마우스 위치를 중심으로 xlim이 4/5배가 되고,
    스크롤을 올리면 마우스 위치를 중심으로 xlim이 5/4배가 된다.
     '''

    def onScrollAtStandardSpectrum(self, event):
        xmin, xmax = self.standardSpectrumAx.get_xlim()
        xnow = event.xdata
        if (event.button == 'up'):
            xsize = int((xmax - xmin) * 0.40)
            xmin = xnow - xsize
            xmax = xnow + xsize
            self.standardSpectrumAx.set_xlim(xmin, xmax)
            self.standardSpectrumAx.figure.canvas.draw()
        elif (event.button == 'down'):
            xsize = int((xmax - xmin) * 0.625)
            xmin = xnow - xsize
            xmax = xnow + xsize
            self.standardSpectrumAx.set_xlim(xmin, xmax)
            self.standardSpectrumAx.figure.canvas.draw()

    '''
    pickPeak을 시작하기 위한 조건을 나타낸 메소드들. 
    standard spectrum 그래프에서 peak wavelength text를 더블클릭하거나 왼쪽 테이블에서 wavelength를 더블클릭하면 그
    wavelength에 맞는 pickPeak이 실행된다. 
    '''

    def onPickPeakAtStandardSpectrum(self, event):
        if (event.mouseevent.dblclick):
            self.pickPeak(event.artist.get_position()[0],
                          self.standardSpectrum, event.mouseevent)

    def onWavelengthPixelTableDoubleClicked(self, index):
        row = index.row()
        wavelength = self.standardPeakWavelengths[row]
        self.pickPeak(wavelength, self.standardSpectrum)

    '''
    peak wavelength에 맞는 peak Pixel을 찾기 위한 메소드
    선택된 wavelength와 그 peak의 axvline를 파란색으로 강조해서 보여주고 칼리브레이션 스펙트럼 그래프의 중간이나 그래프상의 
    같은 위치에 움직일수 있는 peakPicker를 생성해 해당 wavelength에 맞는 peak Pixel을 찾을 수 있도록 한다. 
    '''

    def pickPeak(self, waveNow, spectrum, mouse=None):
        if not self.isMatchFinished: return
        wavelength = spectrum[0]
        flux = spectrum[1]
        fluxNow = flux[np.where(wavelength == waveNow)][0]
        self.pickedPeak = self.standardSpectrumAx.axvline(waveNow,
                                                          color='blue')
        if (fluxNow + max(flux) / 2.85 > max(flux)):
            self.pickedText = self.standardSpectrumAx.text(
                waveNow,
                fluxNow + max(flux) / 2000,
                waveNow,
                c='blue',
                bbox=dict(facecolor='white', ec='none'))
        else:
            self.pickedText = self.standardSpectrumAx.text(
                waveNow,
                fluxNow + max(flux) / 2.85,
                waveNow,
                ha='center',
                va='center',
                rotation=90,
                clip_on=True,
                c='blue',
                bbox=dict(facecolor='white', ec='none'))
        if (mouse is None):
            xshift = [(self.selfSpectrumAx.get_xlim()[1] -
                       self.selfSpectrumAx.get_xlim()[0]) / 2 +
                      self.selfSpectrumAx.get_xlim()[0], 0]
        else:
            xshift = self.selfSpectrumAx.transData.inverted().transform(
                (mouse.x, 0))
        self.peakPicker = self.selfSpectrumAx.axvline(
            xshift[0], color='blue', picker=True, pickradius=self.pickDistance)
        self.selfSpectrumAx.figure.canvas.draw()
        self.standardSpectrumAx.figure.canvas.draw()
        self.currentPickedPeakWavelength = waveNow
        self.isMatchFinished = False

    '''
    pickPeak을 완료하기 위한 메소드 
    매치가 완료되었으면(onMatch) 해당 내용을 리스트에 저장하고 강제종료시(onAbort) 저장하지 않는다. 
    테이블 아래있는 버튼 (Match, Abort)을 클릭하거나 selfSpectrumFigure 위에서 키(m on Match, a on Abort)를 누르면 완료된다. 

    '''

    def keyPressEvent(self, event):
        if self.isMatchFinished: return
        elif event.key() == Qt.Key_M:
            self.onMatch()
        elif event.key() == Qt.Key_A:
            self.onAatch()

    def onMatch(self):
        print('match')
        self.onPickDisable()
        self.onChangedList()

    def onAbort(self):
        self.wavelengthPixelList.loc[self.wavelengthPixelList.Wavelength ==
                                     self.currentPickedPeakWavelength,
                                     'Pixel'] = 0
        self.onPickDisable()
        self.onChangedList()

    def onExport(self):
        path = QFileDialog.getSaveFileName(
            self, 'Choose save file location and name ', './',
            "CSV files (*.csv)")[0]
        self.wavelengthPixelList.to_csv(path, index=False)

    def onImport(self):
        file = QFileDialog.getOpenFileName(self, 'Choose match file', './',
                                           "CSV files (*.csv)")[0]
        self.wavelengthPixelList = pd.read_csv(file)
        self.onChangedList()

    def onPressAtStandardSpectrum(self, event):
        if (event.button == 3):
            self.onPickDisable()

    def onPickDisable(self):
        self.pickedPeak.remove()
        self.pickedText.remove()
        self.peakPicker.remove()

        self.selfSpectrumAx.figure.canvas.draw()
        self.standardSpectrumAx.figure.canvas.draw()
        self.isMatchFinished = True

    '''
    onCalibrationOpen 메소드로 열린 comp 이미지 파일에서 사용할 이미지의 y 축 범위를 찾는 메소드.
    마우스 클릭후 끌어서 범위를 결정하면 selfSpectrumDraw에서 
    '''

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

    def onMoveAtImage(self, event):
        if not event.inaxes: return
        if event.inaxes != self.selfImageAx: return
        if not self.isPressed: return
        self.x1 = event.xdata
        self.y1 = event.ydata
        self.rect.set_width(self.imageWidth)
        self.rect.set_height(self.y1 - self.y0)
        self.rect.set_xy((0, self.y0))
        self.selfImageAx.figure.canvas.draw()

    def onReleaseAtImage(self, event):
        if not event.inaxes: return
        if event.inaxes != self.selfImageAx: return
        if not self.isPressed: return
        y = int(self.rect.get_y())
        height = int(self.rect.get_height())
        self.rect.remove()
        self.selfImageAx.figure.canvas.draw()
        if (height < 0):
            height = 0 - height
        self.selfImageY = np.array([y, y + height])
        self.selfSpectrumDraw(
            ymin=y,
            ymax=y + height,
            data=self.selfData,
            args=[self.peakDistance, self.peakThreshold, self.peakNumber])
        self.isPressed = False

    def selfSpectrumDrawWithGauss(self):
        self.selfSpectrumGaussShow(self.selfSpectrum, self.selfPeakPixs)

    def selfSpectrumDraw(self, ymin, ymax, data, args):

        MINSEP_PK = args[0]  # minimum separation of peaks
        MINAMP_PK = args[
            1]  # fraction of minimum amplitude (wrt maximum) to regard as peak
        NMAX_PK = args[2]

        self.selfSpectrumCanvas.figure.clear()
        self.selfSpectrumAx = self.selfSpectrumCanvas.figure.add_subplot(111)
        identify = np.average(data[ymin:ymax, :], axis=0)
        ground = np.median(identify[0:200])
        max_intens = np.max(identify)
        peakPixs = peak_local_max(identify,
                                  indices=True,
                                  num_peaks=NMAX_PK,
                                  min_distance=MINSEP_PK,
                                  threshold_abs=max_intens * MINAMP_PK +
                                  ground)
        newPeakPixs = []
        for peakPix in peakPixs:
            newPeakPixs.append(peakPix[0])
        peakPixs = newPeakPixs

        self.selfPeakPixs = np.array(peakPixs)
        self.selfSpectrum = np.array(identify)

        for i in peakPixs:
            self.selfSpectrumAx.axvline(i,
                                        identify[i] / max(identify) + 0.0003,
                                        identify[i] / max(identify) + 0.2,
                                        color='c')
            if (identify[i] + max(identify) / 2.85 > max(identify)):
                self.selfSpectrumAx.text(i,
                                         identify[i] + max(identify) / 2000,
                                         str(i),
                                         clip_on=False,
                                         picker=self.pickDistance)
            else:
                self.selfSpectrumAx.text(i,
                                         identify[i] + max(identify) / 2.85,
                                         str(i),
                                         ha='center',
                                         va='center',
                                         rotation=90,
                                         clip_on=True,
                                         picker=self.pickDistance)

        self.selfSpectrumAx.plot(identify, color='r')
        self.selfSpectrumAx.set_xlim(0, len(identify))
        self.selfSpectrumAx.set_ylim(0, )
        self.selfSpectrumAx.figure.canvas.draw()

    # 가우스핏을 통해 peak의 정확한 픽셀값을 찾는다.
    # 값이 제일 큰 3개의 peak 스펙트럼과 그 가우스핏을 예시로 보여주고 특히 FWHM값을 모르거나 추측해야 할 경우
    # FWHM값을 변경하면서 가우스핏이 제대로 되었는지

    def selfSpectrumGaussShow(self, identify, peakPixs):
        self.gaussFitWidget.show()
        self.gaussFitWidget.raise_()
        self.selfSpectrumGaussDraw(identify, peakPixs)


#Todo 이거 잘 빼는 방법(바닥값에 맞게 잘 빼는 방법)을 찾아보자.

    def selfSpectrumGaussDraw(self, identify, peakPixs):
        iterations = 3

        fitter = LevMarLSQFitter()
        self.selfSpectrumGaussFitCanvas.figure.clear()
        identify = identify - np.median(identify[0:100])  ##여기!
        for i in np.arange(iterations):
            a = int(iterations / 5)
            if iterations % 5 != 0: a = a + 1

            ax = self.selfSpectrumGaussFitCanvas.figure.add_subplot(
                a, 5, i + 1)
            peakPix = peakPixs[-i]
            xs = np.arange(peakPix - int(self.selfFWHM) * 5,
                           peakPix + int(self.selfFWHM) * 5 + 1)

            g_init = Gaussian1D(amplitude=identify[peakPix],
                                mean=peakPix,
                                stddev=self.selfFWHM * gaussian_fwhm_to_sigma,
                                bounds={
                                    'amplitude': (0, 2 * identify[peakPix]),
                                    'mean': (peakPix - self.selfFWHM,
                                             peakPix + self.selfFWHM),
                                    'stddev': (0, self.selfFWHM)
                                })

            ax.set_ylim(0, max(identify) * 1.1)
            fitted = fitter(g_init, xs, identify[xs])
            ax.set_xlim(peakPix - fitted.stddev / gaussian_fwhm_to_sigma * 2,
                        peakPix + fitted.stddev / gaussian_fwhm_to_sigma * 2)
            ax.plot(xs, identify[xs], 'b')
            xss = np.arange(peakPix - self.selfFWHM * 5,
                            peakPix + self.selfFWHM * 5 + 1, 0.01)
            ax.plot(xss, fitted(xss), 'r--')
            ax.figure.canvas.draw()

    def selfSpectrumGaussFit(self, identify, peakPixs):

        self.selfSpectrumCanvas.figure.clear()
        self.selfSpectrumAx = self.selfSpectrumCanvas.figure.add_subplot(111)

        fitter = LevMarLSQFitter()

        sortedPeakPixs = np.sort(peakPixs)
        ground = np.median(identify[0:100])
        identify_fit = identify - ground  ## 여기도!

        peak_gauss = []
        i = 0
        x_identify = np.arange(len(identify_fit))
        for peakPix in peakPixs:
            g_init = Gaussian1D(amplitude=identify_fit[peakPix],
                                mean=peakPix,
                                stddev=self.selfFWHM * gaussian_fwhm_to_sigma,
                                bounds={
                                    'amplitude': (identify_fit[peakPix],
                                                  2 * identify_fit[peakPix]),
                                    'mean': (peakPix - self.selfFWHM,
                                             peakPix + self.selfFWHM),
                                    'stddev': (0, self.selfFWHM)
                                })
            fitted = fitter(g_init, x_identify, identify_fit)
            xss = np.arange(peakPix - int(self.selfFWHM) * 3,
                            peakPix + int(self.selfFWHM) * 3 + 1, 0.01)
            self.selfSpectrumAx.plot(xss, fitted(xss) + ground, 'royalblue')

            peak_gauss.append(fitted.mean.value)
            identify_fit = identify_fit - fitted(np.arange(len(identify)))
        '''
        while i < len(sortedPeakPixs)-1:
            peakPix = sortedPeakPixs[i]
            try:
                peakPix2 = sortedPeakPixs[i + 1]
            except:
                peakPix2 = int(peakPix + self.selfFWHM * 10)

            xs = np.arange(peakPix - int(self.selfFWHM) * 3, peakPix2 + int(self.selfFWHM) * 3 + 1)
            g_init = Gaussian1D(amplitude=identify_fit[peakPix],
                                mean=peakPix,
                                stddev=self.selfFWHM * gaussian_fwhm_to_sigma,
                                bounds={'amplitude': (0, 2 * identify_fit[peakPix]),
                                        'mean': (peakPix - self.selfFWHM, peakPix + self.selfFWHM),
                                        'stddev': (0, self.selfFWHM)}
                                )+\
                     Gaussian1D(amplitude=identify_fit[peakPix2],
                                mean=peakPix2,
                                stddev=self.selfFWHM * gaussian_fwhm_to_sigma,
                                bounds={'amplitude': (0, 2 * identify_fit[peakPix2]),
                                        'mean': (peakPix2 - self.selfFWHM, peakPix2 + self.selfFWHM),
                                        'stddev': (0, self.selfFWHM)}
                                )
            fitted = fitter(g_init, xs, identify_fit[xs])



            # fit 한 두 값이 mean 차이가  시그마의 합의 3배 보다 크면 1D fit
            if (fitted.mean_1.value - fitted.mean_0.value > 3* ( fitted.stddev_1 + fitted.stddev_0) ) :
                xs = np.arange(peakPix - int(self.selfFWHM) * 3, peakPix + int(self.selfFWHM) * 3 + 1)
                g_init = Gaussian1D(amplitude=identify_fit[peakPix],
                                    mean=peakPix,
                                    stddev=self.selfFWHM * gaussian_fwhm_to_sigma,
                                    bounds={'amplitude': (0, 2 * identify_fit[peakPix]),
                                            'mean': (peakPix - self.selfFWHM, peakPix + self.selfFWHM),
                                            'stddev': (0, self.selfFWHM)}
                                    )
                fitted = fitter(g_init, xs, identify_fit[xs])
                xss = np.arange(peakPix - int(self.selfFWHM) * 3, peakPix + int(self.selfFWHM) * 3 + 1, 0.01)
                self.selfSpectrumAx.plot(np.arange(len(identify)), fitted(np.arange(len(identify))) + ground, 'royalblue')
                peak_gauss.append(fitted.mean.value)
                i+=1
            else:
                peak_gauss.append(fitted.mean_0.value)
                peak_gauss.append(fitted.mean_1.value)
                xss = np.arange(peakPix - int(self.selfFWHM) * 3, peakPix2 + int(self.selfFWHM) * 3 + 1, 0.01)
                self.selfSpectrumAx.plot(xss, fitted(xss) + ground, 'yellowgreen')
                i += 2

        print(len(peak_gauss))
        print(len(sortedPeakPixs))
        '''
        peak_gauss = np.round(peak_gauss, 4)

        for i, j in zip(peakPixs, peak_gauss):
            self.selfSpectrumAx.axvline(j,
                                        identify[i] / max(identify) + 0.0003,
                                        identify[i] / max(identify) + 0.2,
                                        color='c')
            if (identify[i] + max(identify) / 2.85 > max(identify)):
                self.selfSpectrumAx.text(j,
                                         identify[i] + max(identify) / 2000,
                                         str(j),
                                         clip_on=False,
                                         picker=self.pickDistance)
            else:
                self.selfSpectrumAx.text(j,
                                         identify[i] + max(identify) / 2.85,
                                         str(j),
                                         ha='center',
                                         va='center',
                                         rotation=90,
                                         clip_on=True,
                                         picker=self.pickDistance)

        self.selfSpectrumAx.plot(identify, 'r--')
        self.selfSpectrumAx.set_xlim(0, len(identify))
        self.selfSpectrumAx.set_ylim(0, )
        self.selfPeakPixs = np.array(peak_gauss)
        self.selfSpectrumAx.figure.canvas.draw()
        self.gaussFitWidget.close()

    def onFWHMChanged(self, val):
        self.selfFWHM = val / 10
        self.FWHMLabel.setText(f'FHWM for comp image = {self.selfFWHM}')
        self.selfSpectrumGaussDraw(self.selfSpectrum, self.selfPeakPixs)

    def onGaussFitButtonClicked(self):
        self.selfSpectrumGaussFit(self.selfSpectrum, self.selfPeakPixs)

    def neonSpectrumDraw(self):
        filePath = './NeonArcSpectrum.fit'
        hdr, data = openFitData(filePath)
        self.standardSpectrumDraw(data=data, arc='Neon')

    def standardSpectrumDraw(self, data, arc, peaks=[]):
        wavelength = data[0]
        flux = data[1]
        self.standardSpectrumCanvas.figure.clear()
        self.standardSpectrumAx = self.standardSpectrumCanvas.figure.add_subplot(
            111)
        if (arc == 'Neon'):
            peaks = [
                5330.8000, 5400.5620, 5764.4180, 5852.4878, 5944.8342,
                6029.9971, 6074.3377, 6096.1630, 6143.0623, 6163.5939,
                6217.2813, 6266.4950, 6304.7892, 6334.4279, 6382.9914,
                6402.2460, 6506.5279, 6532.8824, 6598.9529, 6717.0428,
                6929.4680, 7032.4127, 7173.9390, 7245.1670, 7438.8990,
                7488.8720, 7535.7750, 8082.4580, 8377.6070
            ]

        for i in peaks:
            self.standardSpectrumAx.axvline(
                i,
                flux[np.where(i == wavelength)][0] / max(flux) + 0.003,
                flux[np.where(i == wavelength)] / max(flux) + 0.2,
                color='c')
            if (flux[np.where(i == wavelength)][0] + max(flux) / 2.85 >
                    max(flux)):
                self.standardSpectrumAx.text(
                    i,
                    flux[np.where(i == wavelength)][0] + max(flux) / 2000,
                    str(i),
                    clip_on=False,
                    picker=self.pickDistance)
            else:
                self.standardSpectrumAx.text(
                    i,
                    flux[np.where(i == wavelength)][0] + max(flux) / 2.85,
                    str(i),
                    ha='center',
                    va='center',
                    rotation=90,
                    clip_on=True,
                    picker=self.pickDistance)
        self.standardSpectrumAx.plot(wavelength, flux, 'r--')
        self.standardSpectrumAx.set_ylim(0, )
        self.standardSpectrumAx.set_xlim(min(wavelength), max(wavelength))
        self.standardPeakWavelengths = np.array(peaks)
        self.standardSpectrum = data
        self.matchedPeakPixs = np.zeros(
            (self.standardPeakWavelengths.shape[0]))
        matchInfo = np.column_stack(
            (self.standardPeakWavelengths, self.matchedPeakPixs))
        self.wavelengthPixelList = pd.DataFrame(
            matchInfo, columns=['Wavelength', 'Pixel'])
        self.onChangedList()
        self.standardSpectrumAx.figure.canvas.draw()

    def onReidentification(self):
        self.reidentificationWidget.setReidentifier(
            matchList=self.wavelengthPixelList,
            flux=self.selfData,
            fitMethod='linear',
            FWHM=self.selfFWHM)
        self.reidentificationWidget.show()
        self.reidentificationWidget.raise_()

    def onChangedList(self):

        self.wavelengthPixelModel = tableModel(self.wavelengthPixelList)
        self.wavelengthPixelTable.setModel(self.wavelengthPixelModel)

    def onButtonClicked(self, status):
        self.bottonSinal.emit(status)

    def center(self):
        qr = self.frameGeometry()
        cp = QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())
Ejemplo n.º 23
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_()
Ejemplo n.º 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()
Ejemplo n.º 25
0
class ComboBox(LineEdit):
    """
    A ComboxBox, upon clicking button, drops down a list of items to choose.

    Items can be edited also.
    """


    def __init__(
            self,
            width,
            height,
            text_list,
            edit_notify = None,
            selection_notify = None,
            **kwargs
        ):

        if len(text_list) > 0:
            text = text_list[0]
        else:
            text = ''

        super(ComboBox,self).__init__(
            width, height, text, self._on_edit_notify, **kwargs)

        self._text_list = text_list

        self._edit_notify = edit_notify
        self._selection_notify = selection_notify

        if edit_notify and not callable(edit_notify):
            raise RuntimeError('edit_notify must be a callable function')

        if selection_notify and not callable(selection_notify):
            raise RuntimeError('selection_notify must be a callable function')

        #---------------------------------------------------------------------
        # additional items
        #
        # selection axes for showing the possible selections
        # a rectangle to highlight the selection row
        # a button to show the selection drop down

        self._select_axes = None
        self._select_highlight = None # just a rectangle
        self._select_posx = None
        self._select_entries = []

        self._cb_state = ComboState.IDLE

        self._n_lines = 5

        self._mouse_motion_cid = None

        self._ignore_edit_notify = False


    def _render(self, fig, x, y):

        super(ComboBox, self)._render(fig, x, y)

        self._render_dropdown_button(fig)
        self._render_dropdown_axis(fig, x, y)


    def _render_dropdown_axis(self, fig, x, y):

        W, H = fig.get_size_inches()

        h = self._n_lines * self._height

        y -= h

        x /= W
        y /= H

        # create the other gui assets but keep them hidden

        # selection axes, same width, by 10 times in height

        w = self._width / W
        h /= H

        ax = fig.add_axes([x, y, w, h], xticks=[], yticks=[])

        ax.set_xlim([0, self._width])
        ax.set_ylim([0, self._n_lines * self._height])

        ax.set_axis_bgcolor('white')

        ax.set_visible(False)

        ax.set_zorder(1000)

        self._select_axes = ax


    def _render_dropdown_button(self, fig):

        w, h = 0.25, 0.125

        hw = w / 2.0
        hh = h / 2.0

        x = self._width - w - 0.02
        y = (self._height - h) / 2.0

        self._select_posx = x + hw

        # Three point polygon:
        #
        #  2 O-----O 3
        #     \   /
        #      \ /
        #       O
        #       1
        #

        points = [
            [ x + hw, y    ],
            [ x,      y + h],
            [ x + w,  y + h],
        ]

        points = np.array(points)

        patch = Polygon(points, closed = True, ec = 'black', fc = 'black')

        self._axes.add_patch(patch)


    def _cb_change_state(self, new_state):

        if self._cb_state == new_state:
            raise RuntimeError("Already in state %s" % new_state)

        if self._cb_state == ComboState.IDLE:

            if new_state == ComboState.DROP_SELECT:
                self._select_axes.set_visible(True)

                x = self._pad_left
                y = ((self._n_lines - 1) * self._height)

                #--------------------------------------------------------------
                # create highlight

                if self._select_highlight is None:

                    self._select_highlight = Rectangle(
                        (0,y - self._height / 2.0),
                        self._width,
                        self._height,
                        ec = self._hl_color,
                        fc = self._hl_color
                    )

                    self._select_axes.add_patch(self._select_highlight)

                else:
                    self._select_highlight.set_visible(True)

                # delete existing text objects

                for t in self._select_entries:
                    t.remove()
                    del t

                self._select_entries = []

                for t in self._text_list:

                    txt = self._select_axes.text(
                        x,y,t, ha = 'left', va = 'center')

                    y -= self._height

                    self._select_entries.append(txt)

                self._mouse_motion_cid = self._select_axes.figure.canvas.mpl_connect(
                    'motion_notify_event', self._on_mouse_motion
                )

                self.canvas().draw()

            else: self._unhandled_state(new_state)

        elif self._cb_state == ComboState.DROP_SELECT:

            if new_state == ComboState.IDLE:
                self._select_axes.set_visible(False)
                self.canvas().draw()

            else: self._unhandled_state(new_state)

        else: self._unhandled_state(new_state)

        self._cb_state = new_state


    def _unhandled_state(self, new_state):
        if _DEV:
            print("unhandled %s --> %s" % (self._cb_state, new_state))


    def _on_mouse_down(self, event):

        if event.inaxes not in [self._axes, self._select_axes]:
            self._ignore_edit_notify = True
            if self._cb_state != ComboState.IDLE:
                self._cb_change_state(ComboState.IDLE)
            super(ComboBox, self)._on_mouse_down(event)
            return

        x, y = event.xdata, event.ydata

        if x is None or y is None:
            super(ComboBox, self)._on_mouse_down(event)
            return

        if self._cb_state == ComboState.IDLE:

            cx = self._select_posx

            d = np.sqrt( (x - cx) ** 2 )

            if d <= 0.16:
                self._cb_change_state(ComboState.DROP_SELECT)

            else:
                super(ComboBox, self)._on_mouse_down(event)

        elif self._cb_state == ComboState.DROP_SELECT:

            y = self._select_highlight.get_y()

            idx = self._find_text_entry(y)

            selection = self._text_list[idx]

            self._ignore_edit_notify = True

            self.text(selection)

            if self._selection_notify:
                self._selection_notify(idx, selection)

            self._cb_change_state(ComboState.IDLE)

        elif _DEV:
            print("on_mouse_down(): unhandled %s" % self._cb_state)


    def _on_mouse_motion(self, event):

        if event.inaxes != self._select_axes:
            return

        x, y = event.xdata, event.ydata

        if x is None or y is None:
            return

        if self._cb_state == ComboState.DROP_SELECT:

            idx = self._find_text_entry(y)

            _, y = self._select_entries[idx].get_position()

            self._select_highlight.set_y(y - self._height / 2.0)

            self.canvas().draw()


    def _on_key_press(self, event):

        if event.key == 'escape' and self._cb_state == ComboState.DROP_SELECT:
            self._cb_change_state(ComboState.IDLE)

        self._ignore_edit_notify = False

        super(ComboBox, self)._on_key_press(event)


    def _find_text_entry(self, y):

         # find nearest text

        dist = []

        for txt in self._select_entries:

            _, ydata = txt.get_position()

            d = np.abs(ydata - y)

            dist.append(d)

        return np.argmin(dist)


    def _on_edit_notify(self, text):

        if self._ignore_edit_notify:
            self._ignore_edit_notify = False
            return

        # add to the list

        self._text_list.append(text)

        if self._edit_notify:
            self._edit_notify(text)