Esempio n. 1
0
 def mousePressEvent(self, ev):
     if ev.button() == QtCore.Qt.RightButton:
         ev.accept()
         self.dragStarted = True
         self.sigPressed.emit(ev.pos())
     else:
         ImageItem.mousePressEvent(self, ev)
Esempio n. 2
0
    def __init__(self, *args, **kwargs):
        QtGui.QWidget.__init__(self)
        # variable that handles the manipulation of the spectrogram
        # create, recompute etc
        self.specgramHandler = Spectrogram()

        # create the histogram widget
        self.imageItem = ImageItem()
        self.__histogram = HorizontalHistogramWidget()
        self.__histogram.item.setImageItem(self.imageItem)

        # internal vieBox for image graph
        self.viewBox = ViewBox()
        self.viewBox.addItem(self.imageItem)
        self.viewBox.setMouseEnabled(x=False, y=False)
        self.viewBox.setMenuEnabled(False)
        self.viewBox.setAspectLocked(False)

        # set the X and Y axis for the graph
        self.xAxis = OscXAxis(self, orientation='bottom')
        self.yAxis = SpecYAxis(self, orientation='left')

        self.xAxis.linkToView(self.viewBox)
        self.yAxis.linkToView(self.viewBox)

        self.xAxis.setGrid(self.AXIS_LINES_OPACITY)
        self.yAxis.setGrid(self.AXIS_LINES_OPACITY)

        # adjust the graph zone in the control
        horizontal_histogram = kwargs[
            "horizontal_histogram"] if "horizontal_histogram" in kwargs else True
        visible_histogram = kwargs[
            "visible_histogram"] if "visible_histogram" in kwargs else False
        self.__updateLayout(histogramHorizontal=(
            horizontal_histogram if visible_histogram else None))
Esempio n. 3
0
 def mouseReleaseEvent(self, ev):
     if ev.button() == QtCore.Qt.RightButton:
         ev.accept()
         self.dragStarted = False
         self.sigReleased.emit(ev.pos())
     else:
         ImageItem.mouseMoveEvent(self, ev)
Esempio n. 4
0
    def set_image(self, image, emit=True, *args, **kwargs) :
        """ Expects either np.arrays or pg.ImageItems as input and sets them 
        correctly to this PlotWidget's Image with `addItem`. Also makes sure 
        there is only one Image by deleting the previous image.

        Emits :signal:`sig_image_changed`

        **Parameters**

        ========  ==============================================================
        image     np.ndarray or pyqtgraph.ImageItem instance; the image to be
                  displayed.
        emit      bool; whether or not to emit :signal:`sig_image_changed`
        (kw)args  positional and keyword arguments that are passed on to 
                  :class:`pyqtgraph.ImageItem`
        ========  ==============================================================
        """
        # Convert array to ImageItem
        if isinstance(image, ndarray) :
            if 0 not in image.shape :
                image_item = ImageItem(image, *args, **kwargs)
            else :
                logger.debug(('<{}>.set_image(): image.shape is {}. Not '
                              'setting image.').format(self.name, image.shape))
                return
        else :
            image_item = image
        # Throw an exception if image is not an ImageItem
        if not isinstance(image_item, ImageItem) :
            message = '''`image` should be a np.array or pg.ImageItem instance,
            not {}'''.format(type(image))
            raise TypeError(message)

        # Transpose if necessary
        if self.transposed.get_value() :
            image_item = ImageItem(image_item.image.T, *args, **kwargs)

        # Replace the image
        self.remove_image()
        self.image_item = image_item
        self.image_data = image_item.image
        logger.debug('<{}>Setting image.'.format(self.name))
        self.addItem(image_item)
        # Reset limits if necessary
        if self.xscale is not None and self.yscale is not None :
            axes_shape = (len(self.xscale), len(self.yscale))
            if axes_shape != self.image_data.shape :
                self.xlim = None
                self.ylim = None
        self._set_axes_scales(emit=emit)

        if emit :
            logger.info('<{}>Emitting sig_image_changed.'.format(self.name))
            self.sig_image_changed.emit()
Esempio n. 5
0
    def __init__(self, parent=None, res=(640, 480)):
        super(FrameBufferView, self).__init__(parent)

        #self.enableMouse()
        #self.setAspectLocked(True)

        self.scene = QtGui.QGraphicsScene()
        self.setScene(self.scene)

        self.imgArray = np.zeros(res)
        self.imgBuffer = ImageItem(border='w')
        self.scene.addItem(self.imgBuffer)

        self.fitInView(
            QtCore.QRectF(0, 0, self.imgArray.shape[1],
                          self.imgArray.shape[1]))
Esempio n. 6
0
def class_density_image(min_x, max_x, min_y, max_y, resolution, x_data, y_data, rgb_data):
    x_sz = (max_x-min_x)/(resolution-1)
    y_sz = (max_y-min_y)/(resolution-1)
    x_grid = [min_x+i*x_sz for i in range(resolution)]
    y_grid = [min_y+i*y_sz for i in range(resolution)]
    n_points = len(x_data)
    sample = range(n_points)
    if n_points > 1000:
        sample = grid_sample(x_data, y_data, 1000)
    x_data_norm = (np.array(x_data)-min_x)/(max_x-min_x)
    y_data_norm = (np.array(y_data)-min_y)/(max_y-min_y)
    x_grid_norm = (np.array(x_grid)-min_x)/(max_x-min_x)
    y_grid_norm = (np.array(y_grid)-min_y)/(max_y-min_y)
    img = compute_density(x_grid_norm, y_grid_norm,
                          x_data_norm[sample], y_data_norm[sample], np.array(rgb_data)[sample])
    density_img = ImageItem(img.astype(np.uint8), autoLevels=False)
    density_img.setRect(QRectF(min_x-x_sz/2, min_y-y_sz/2,
                               max_x-min_x+x_sz, max_y-min_y+y_sz))
    density_img.setZValue(-1)
    return density_img
Esempio n. 7
0
class FrameBufferView(QtGui.QGraphicsView):
    '''
    Object to display and update the image.
    '''
    def __init__(self, parent=None, res=(640, 480)):
        super(FrameBufferView, self).__init__(parent)

        #self.enableMouse()
        #self.setAspectLocked(True)

        self.scene = QtGui.QGraphicsScene()
        self.setScene(self.scene)

        self.imgArray = np.zeros(res)
        self.imgBuffer = ImageItem(border='w')
        self.scene.addItem(self.imgBuffer)

        self.fitInView(
            QtCore.QRectF(0, 0, self.imgArray.shape[1],
                          self.imgArray.shape[1]))

        #TODO - line update Q
        #Fills up when it changes, passes to the dispatcher

    def setResolution(res=(640, 480)):
        '''
        '''
        self.imgArray.resize(res)

    def viewedRect(self):
        '''
        Return the pixel coordinates of the image visible in the viewport.
        '''
        pass

    def updateImage(self, data):
        '''
        Update the image with data.
        '''
        self.imgBuffer.setImage(data)
Esempio n. 8
0
def class_density_image(min_x, max_x, min_y, max_y, resolution, x_data, y_data,
                        rgb_data):
    x_sz = (max_x - min_x) / (resolution - 1)
    y_sz = (max_y - min_y) / (resolution - 1)
    x_grid = [min_x + i * x_sz for i in range(resolution)]
    y_grid = [min_y + i * y_sz for i in range(resolution)]
    n_points = len(x_data)
    sample = range(n_points)
    if n_points > 1000:
        sample = grid_sample(x_data, y_data, 1000)
    x_data_norm = (np.array(x_data) - min_x) / (max_x - min_x)
    y_data_norm = (np.array(y_data) - min_y) / (max_y - min_y)
    x_grid_norm = (np.array(x_grid) - min_x) / (max_x - min_x)
    y_grid_norm = (np.array(y_grid) - min_y) / (max_y - min_y)
    img = compute_density(
        x_grid_norm,
        y_grid_norm,
        x_data_norm[sample],
        y_data_norm[sample],
        np.array(rgb_data)[sample],
    )
    density_img = ImageItem(img.astype(np.uint8), autoLevels=False)
    density_img.setRect(
        QRectF(
            min_x - x_sz / 2,
            min_y - y_sz / 2,
            max_x - min_x + x_sz,
            max_y - min_y + y_sz,
        ))
    density_img.setZValue(-1)
    return density_img
Esempio n. 9
0
class FrameBufferView(QtGui.QGraphicsView):
    '''
    Object to display and update the image.
    '''
    def __init__(self, parent=None, res=(640, 480)):
        super(FrameBufferView, self).__init__(parent)

        #self.enableMouse()
        #self.setAspectLocked(True)

        self.scene = QtGui.QGraphicsScene()
        self.setScene(self.scene)

        self.imgArray = np.zeros(res)
        self.imgBuffer = ImageItem(border='w')
        self.scene.addItem(self.imgBuffer)

        self.fitInView(QtCore.QRectF(0, 0, self.imgArray.shape[1], self.imgArray.shape[1]))

        #TODO - line update Q
        #Fills up when it changes, passes to the dispatcher

    def setResolution(res=(640, 480)):
        '''
        '''
        self.imgArray.resize(res)

    def viewedRect(self):
        '''
        Return the pixel coordinates of the image visible in the viewport.
        '''
        pass

    def updateImage(self, data):
        '''
        Update the image with data.
        '''
        self.imgBuffer.setImage(data)
Esempio n. 10
0
    def __init__(self, parent=None, res=(640, 480)):
        super(FrameBufferView, self).__init__(parent)

        #self.enableMouse()
        #self.setAspectLocked(True)

        self.scene = QtGui.QGraphicsScene()
        self.setScene(self.scene)

        self.imgArray = np.zeros(res)
        self.imgBuffer = ImageItem(border='w')
        self.scene.addItem(self.imgBuffer)

        self.fitInView(QtCore.QRectF(0, 0, self.imgArray.shape[1], self.imgArray.shape[1]))
Esempio n. 11
0
    def setup_pixel_mask(self, image, mask):

        image_item = ImageItem()
        lookup_table = pg.ColorMap([0, 1],
                                   np.array([[0, 0, 0, 0], [0, 0, 255, 255]
                                             ])).getLookupTable(alpha=True)
        image_item.setLookupTable(lookup_table)

        image.addItem(image_item)
        image_item.setImage(mask)

        return image_item
Esempio n. 12
0
    def set_image(self, image, emit=True, *args, **kwargs):
        """ Expects both, np.arrays and pg.ImageItems as input and sets them 
        correctly to this PlotWidget's Image with `addItem`. Also makes sure 
        there is only one Image by deleting the previous image.

        Emits :signal: `sig_image_changed`

        ======  ================================================================
        image   np.ndarray or pyqtgraph.ImageItem instance; the image to be
                displayed.
        emit    bool; whether or not to emit :signal: `sig_image_changed`
        args    positional and keyword arguments that are passed on to :class:
        kwargs  `ImageItem <pyqtgraph.graphicsItems.ImageItem.ImageItem>`
        ======  ================================================================
        """
        self.image_data = image

        # Convert array to ImageItem
        if isinstance(image, ndarray):
            if 0 not in image.shape:
                image = ImageItem(image, *args, **kwargs)
            else:
                logger.debug(('<{}>.set_image(): image.shape is {}. Not '
                              'setting image.').format(self.name, image.shape))
                return
        # Throw an exception if image is not an ImageItem
        if not isinstance(image, ImageItem):
            message = '''`image` should be a np.array or pg.ImageItem instance,
            not {}'''.format(type(image))
            raise TypeError(message)

        # Replace the image
        self.remove_image()
        self.image_item = image
        logger.debug('<{}>Setting image.'.format(self.name))
        self.addItem(image)
        self._set_axes_scales(emit=emit)

        if emit:
            logger.info('<{}>Emitting sig_image_changed.'.format(self.name))
            self.sig_image_changed.emit()
Esempio n. 13
0
    def set_image(self, image, *args, **kwargs) :
        """ Expects both, np.arrays and pg.ImageItems as input and sets them 
        correctly to this PlotWidget's Image with `addItem`. Also makes sure 
        there is only one Image by deleting the previous image.

        Emits :signal: `sig_image_changed`

        ======  ================================================================
        image   np.ndarray or pyqtgraph.ImageItem instance; the image to be
                displayed.
        args    positional and keyword arguments that are passed on to :class:
        kwargs  `ImageItem <pyqtgraph.graphicsItems.ImageItem.ImageItem>`
        ======  ================================================================
        """
        # Convert array to ImageItem
        if isinstance(image, ndarray) :
            image = ImageItem(image, *args, **kwargs)
        # Throw an exception if image is not an ImageItem
        if not isinstance(image, ImageItem) :
            message = '''`image` should be a np.array or pg.ImageItem instance,
            not {}'''.format(type(image))
            raise TypeError(message)

        # Replace the image
        self.remove_image()
        self.image_item = image
        logger.debug('<{}>Setting image.'.format(self.name))
        self.addItem(image)
        self._set_axes_scales(emit=False)
        # We suppressed emittance of sig_axes_changed to avoid external 
        # listeners thinking the axes are different now. Thus, have to call 
        # self.fix_viewrange manually.
        self.fix_viewrange()

        logger.info('<{}>Emitting sig_image_changed.'.format(self.name))
        self.sig_image_changed.emit()
Esempio n. 14
0
class SpectrogramWidget(QtGui.QWidget):
    """
    Widget that visualize and interacts with a spectrogram graph
    of an audio signal.
    """
    # SIGNALS

    # CONSTANTS
    # Value of the axis lines opacity
    AXIS_LINES_OPACITY = 255

    # Max amount of columns to show on the visual widget.
    MAX_SPECGRAM_COLUMNS = 800

    def __init__(self, *args, **kwargs):
        QtGui.QWidget.__init__(self)
        # variable that handles the manipulation of the spectrogram
        # create, recompute etc
        self.specgramHandler = Spectrogram()

        # create the histogram widget
        self.imageItem = ImageItem()
        self.__histogram = HorizontalHistogramWidget()
        self.__histogram.item.setImageItem(self.imageItem)

        # internal vieBox for image graph
        self.viewBox = ViewBox()
        self.viewBox.addItem(self.imageItem)
        self.viewBox.setMouseEnabled(x=False, y=False)
        self.viewBox.setMenuEnabled(False)
        self.viewBox.setAspectLocked(False)

        # set the X and Y axis for the graph
        self.xAxis = OscXAxis(self, orientation='bottom')
        self.yAxis = SpecYAxis(self, orientation='left')

        self.xAxis.linkToView(self.viewBox)
        self.yAxis.linkToView(self.viewBox)

        self.xAxis.setGrid(self.AXIS_LINES_OPACITY)
        self.yAxis.setGrid(self.AXIS_LINES_OPACITY)

        # adjust the graph zone in the control
        horizontal_histogram = kwargs[
            "horizontal_histogram"] if "horizontal_histogram" in kwargs else True
        visible_histogram = kwargs[
            "visible_histogram"] if "visible_histogram" in kwargs else False
        self.__updateLayout(histogramHorizontal=(
            horizontal_histogram if visible_histogram else None))

    def __updateLayout(self, histogramHorizontal=None):
        """
        Updates the layout of the widget to change the histogram
        position (horizontal-vertical) and visibility
        :param histogramHorizontal: None if invisible, true if horizontal
        false if vertical
        :return:
        """
        # graph_control_layout

        self.graphics_view = pg.GraphicsView()

        # set the layout of the graphics view internal
        # that contains the axis and the view box to graph the spectrogram
        graphics_view_grid_layout = QtGui.QGraphicsGridLayout()
        graphics_view_grid_layout.setContentsMargins(0, 0, 0, 0)
        graphics_view_grid_layout.setHorizontalSpacing(0)
        graphics_view_grid_layout.setVerticalSpacing(0)

        graphics_view_grid_layout.addItem(self.xAxis, 1, 1)
        graphics_view_grid_layout.addItem(self.yAxis, 0, 0)
        graphics_view_grid_layout.addItem(self.viewBox, 0, 1)

        self.graphics_view.centralWidget.setLayout(graphics_view_grid_layout)
        layout = QtGui.QGridLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(self.graphics_view, 0, 0)
        # add the elements in row,col

        if histogramHorizontal is not None:
            row = 1 if histogramHorizontal else 0
            col = 1 - row
            self.__histogram = HorizontalHistogramWidget() if histogramHorizontal else\
                               HistogramLUTWidget()
            self.__histogram.item.gradient.loadPreset('thermal')
            self.__histogram.item.setImageItem(self.imageItem)
            layout.addWidget(self.histogram, row, col)

        self.setLayout(layout)

    # region Signal Property
    @property
    def signal(self):
        return self.specgramHandler.signal

    @signal.setter
    def signal(self, signal):
        if signal is None or not isinstance(signal, AudioSignal):
            raise ValueError(
                "Invalid assignation. Must be of type AudioSignal")

        self.specgramHandler.signal = signal

        max_columns = max(self.viewBox.width(), self.MAX_SPECGRAM_COLUMNS)

        self.specgramHandler.recomputeSpectrogram(maxCol=max_columns)
        # updating the axis scales
        self.xAxis.scale = self.specgramHandler.signal.samplingRate

        # updating the frequencies present in the signal
        # samplingRate/2 by Nyquist theorem. After update the spectrogram handler
        # there is an array in the specgramHandler with the freqs returned by the specgram
        # freqs is an array with the values of the frequency of each row of the specgram matriz
        self.yAxis.frequencies = self.specgramHandler.freqs

    # endregion

    # region Histogram Prop
    @property
    def histogram(self):
        """
        Histogram widget with the values of the image.
        interacts with the image graph to change its color,
        threshold etc. Allow to visualize it outside the spectrogram widget
        :return:
        """
        return self.__histogram

    # endregion

    def getInfo(self, x, y):
        """
        Gets the values at x, y in the spectrogram
        :param x: the time position in rows of the spectrogram matriz
        :param y: the freq position in columns of the spectrogram matriz
        :return: a tuple with (time, freq, intensity)
        """
        return self.specgramHandler.getInfo(x, y)

    def graph(self, indexFrom=0, indexTo=-1, padding=0):

        indexTo = indexTo if (indexTo >= 0
                              and indexTo > indexFrom) else self.signal.length

        # avoid to set the max columns too high that the spectrogram cant be computed
        # and do not consume more resources that necessary
        # show at most as many columns as pixels in the widget's width
        max_columns = max(self.viewBox.width(), self.MAX_SPECGRAM_COLUMNS)

        self.specgramHandler.recomputeSpectrogram(indexFrom, indexTo,
                                                  max_columns)
        self.yAxis.frequencies = self.specgramHandler.freqs

        # set the new spectrogram image computed
        self.imageItem.setImage(self.specgramHandler.matriz)
        self.viewBox.setRange(
            xRange=(self.specgramHandler.from_osc_to_spec(indexFrom),
                    self.specgramHandler.from_osc_to_spec(indexTo - 1)),
            padding=padding)

        # update the histogram colors of the spectrogram
        self.histogram.item.region.lineMoved()
        self.histogram.item.region.lineMoveFinished()
        self.xAxis.setRange(indexFrom, indexTo)

        self.update()
Esempio n. 15
0
    def __init__(self,
                 parent=None,
                 name="ImageView",
                 view=None,
                 imageItem=None,
                 *args):
        QtGui.QWidget.__init__(self, parent, *args)
        self.levelMax = 4096
        self.levelMin = 0
        self.name = name
        self.image = None
        self.axes = {}
        self.imageDisp = None
        self.ui = Ui_Form()
        self.ui.setupUi(self)
        self.scene = self.ui.graphicsView.scene()

        self.ignoreTimeLine = False

        self.wcsAxes = WCSAxes()
        if view is None:
            self.view = self.wcsAxes.plotItem
        else:
            self.view = view
        self.ui.graphicsView.setCentralItem(self.view)
        self.view.setAspectLocked(True)
        self.view.invertY()

        if imageItem is None:
            self.imageItem = ImageItem()
        else:
            self.imageItem = imageItem
        self.view.addItem(self.imageItem)
        self.currentIndex = 0

        self.ui.histogram.setImageItem(self.imageItem)

        self.menu = None

        self._hideSlider()

        self.roiEnabled = False
        self.roi = MultiROI()
        self.roi.setZValue(20)
        self.view.addItem(self.roi)
        self.roi.hide()
        self.normRoi = MultiROI()
        self.normRoi.setPen('y')
        self.normRoi.setZValue(20)
        self.view.addItem(self.normRoi)
        self.normRoi.hide()
        self.roiCurve = self.ui.roiPlot.plot()
        self.timeLine = InfiniteLine(0, movable=True)
        self.timeLine.setPen((255, 255, 0, 200))
        self.timeLine.setZValue(1)
        self.ui.roiPlot.addItem(self.timeLine)
        self.ui.splitter.setSizes([self.height() - 35, 35])
        self.ui.roiPlot.hideAxis('left')

        self.keysPressed = {}
        self.playTimer = QtCore.QTimer()
        self.playRate = 0
        self.lastPlayTime = 0

        self.normRgn = LinearRegionItem()
        self.normRgn.setZValue(0)
        self.ui.roiPlot.addItem(self.normRgn)
        self.normRgn.hide()

        # wrap functions from view box
        for fn in ['addItem', 'removeItem']:
            setattr(self, fn, getattr(self.view, fn))

        # wrap functions from histogram
        for fn in [
                'setHistogramRange', 'autoHistogramRange', 'getLookupTable',
                'getLevels'
        ]:
            setattr(self, fn, getattr(self.ui.histogram, fn))

        self.ui.horizontalSlider.valueChanged.connect(self._sliderValueChanged)
        self.ui.sliderValueSpinBox.valueChanged.connect(self._spinBoxChanged)
        self.timeLine.sigPositionChanged.connect(self.timeLineChanged)
        self.roi.sigRegionChanged.connect(self.roiChanged)

        self.playTimer.timeout.connect(self.timeout)

        self.normProxy = pg.SignalProxy(self.normRgn.sigRegionChanged,
                                        slot=self.updateNorm)
        self.normRoi.sigRegionChangeFinished.connect(self.updateNorm)

        self.ui.roiPlot.registerPlot(self.name + '_ROI')
        self.view.register(self.name)

        self.noRepeatKeys = [
            QtCore.Qt.Key_Right, QtCore.Qt.Key_Left, QtCore.Qt.Key_Up,
            QtCore.Qt.Key_Down, QtCore.Qt.Key_PageUp, QtCore.Qt.Key_PageDown
        ]

        # initialize roi plot to correct shape / visibility
        self.setROIType(None)
Esempio n. 16
0
 def __init__(self, *args, **kwargs):
     ImageItem.__init__(self, *args, **kwargs)
     self.dragStarted = False
Esempio n. 17
0
 def mouseMoveEvent(self, ev):
     if self.dragStarted:
         self.sigDragged.emit(ev.pos())
         ev.accept()
     else:
         ImageItem.mouseMoveEvent(self, ev)
Esempio n. 18
0
class AstroImageView(pg.ImageView):
    """
    Extends ImageView class.
     - Replace default HistogramLUTWidget with our AstroHistogramLUTWidget.
     - Replace PlotWidget for roiPlot (defined inside UI class) by
       AstroWaveFormPlotWidget
     - Delete roiBtn
    """
    def __init__(self,
                 parent=None,
                 name="ImageView",
                 view=None,
                 imageItem=None,
                 *args):
        QtGui.QWidget.__init__(self, parent, *args)
        self.levelMax = 4096
        self.levelMin = 0
        self.name = name
        self.image = None
        self.axes = {}
        self.imageDisp = None
        self.ui = Ui_Form()
        self.ui.setupUi(self)
        self.scene = self.ui.graphicsView.scene()

        self.ignoreTimeLine = False

        self.wcsAxes = WCSAxes()
        if view is None:
            self.view = self.wcsAxes.plotItem
        else:
            self.view = view
        self.ui.graphicsView.setCentralItem(self.view)
        self.view.setAspectLocked(True)
        self.view.invertY()

        if imageItem is None:
            self.imageItem = ImageItem()
        else:
            self.imageItem = imageItem
        self.view.addItem(self.imageItem)
        self.currentIndex = 0

        self.ui.histogram.setImageItem(self.imageItem)

        self.menu = None

        self._hideSlider()

        self.roiEnabled = False
        self.roi = MultiROI()
        self.roi.setZValue(20)
        self.view.addItem(self.roi)
        self.roi.hide()
        self.normRoi = MultiROI()
        self.normRoi.setPen('y')
        self.normRoi.setZValue(20)
        self.view.addItem(self.normRoi)
        self.normRoi.hide()
        self.roiCurve = self.ui.roiPlot.plot()
        self.timeLine = InfiniteLine(0, movable=True)
        self.timeLine.setPen((255, 255, 0, 200))
        self.timeLine.setZValue(1)
        self.ui.roiPlot.addItem(self.timeLine)
        self.ui.splitter.setSizes([self.height() - 35, 35])
        self.ui.roiPlot.hideAxis('left')

        self.keysPressed = {}
        self.playTimer = QtCore.QTimer()
        self.playRate = 0
        self.lastPlayTime = 0

        self.normRgn = LinearRegionItem()
        self.normRgn.setZValue(0)
        self.ui.roiPlot.addItem(self.normRgn)
        self.normRgn.hide()

        # wrap functions from view box
        for fn in ['addItem', 'removeItem']:
            setattr(self, fn, getattr(self.view, fn))

        # wrap functions from histogram
        for fn in [
                'setHistogramRange', 'autoHistogramRange', 'getLookupTable',
                'getLevels'
        ]:
            setattr(self, fn, getattr(self.ui.histogram, fn))

        self.ui.horizontalSlider.valueChanged.connect(self._sliderValueChanged)
        self.ui.sliderValueSpinBox.valueChanged.connect(self._spinBoxChanged)
        self.timeLine.sigPositionChanged.connect(self.timeLineChanged)
        self.roi.sigRegionChanged.connect(self.roiChanged)

        self.playTimer.timeout.connect(self.timeout)

        self.normProxy = pg.SignalProxy(self.normRgn.sigRegionChanged,
                                        slot=self.updateNorm)
        self.normRoi.sigRegionChangeFinished.connect(self.updateNorm)

        self.ui.roiPlot.registerPlot(self.name + '_ROI')
        self.view.register(self.name)

        self.noRepeatKeys = [
            QtCore.Qt.Key_Right, QtCore.Qt.Key_Left, QtCore.Qt.Key_Up,
            QtCore.Qt.Key_Down, QtCore.Qt.Key_PageUp, QtCore.Qt.Key_PageDown
        ]

        # initialize roi plot to correct shape / visibility
        self.setROIType(None)

    def set_fits_file(self, fitsFile):
        self.wcsAxes.setFitsFile(fitsFile)
        flux = fitsFile.flux()
        wave = fitsFile.wave()
        self.setImage(flux, xvals=wave)
        self.ui.roiPlot.setFitsFile(fitsFile)
        # TODO: Autorange the image after it is drawn.
        # This is a work around for what seems to be a bug in the
        # *pg.ArrowItem*: When arrows are shown in the canvas, the default
        # range of the axis becomes larger. Arrows are included from *WCSAxis*.
        timer = threading.Timer(0.1, lambda: self.view.vb.menu.autoRange())
        timer.start()

    def setImage(self,
                 img,
                 autoRange=True,
                 autoLevels=True,
                 levels=None,
                 axes=None,
                 xvals=None,
                 pos=None,
                 scale=None,
                 transform=None,
                 autoHistogramRange=True):
        """
        Reimplement setImage without using roiBtn
        """
        if hasattr(img, 'implements') and img.implements('MetaArray'):
            img = img.asarray()

        if not isinstance(img, np.ndarray):
            required = ['dtype', 'max', 'min', 'ndim', 'shape', 'size']
            if not all([hasattr(img, attr) for attr in required]):
                raise TypeError("Image must be NumPy array or any object "
                                "that provides compatible attributes/methods:"
                                "\n  %s" % str(required))

        self.image = img
        self.imageDisp = None

        if axes is None:
            if self.imageItem.axisOrder == 'col-major':
                x, y = (0, 1)
            else:
                x, y = (1, 0)

            if img.ndim == 2:
                self.axes = {'t': None, 'x': x, 'y': y, 'c': None}
            elif img.ndim == 3:
                # Ambiguous case; make a guess
                if img.shape[2] <= 4:
                    self.axes = {'t': None, 'x': x, 'y': y, 'c': 2}
                else:
                    self.axes = {'t': 0, 'x': x + 1, 'y': y + 1, 'c': None}
            elif img.ndim == 4:
                # Even more ambiguous; just assume the default
                self.axes = {'t': 0, 'x': x + 1, 'y': y + 1, 'c': 3}
            else:
                raise Exception("Can not interpret image with dimensions %s" %
                                str(img.shape))
        elif isinstance(axes, dict):
            self.axes = axes.copy()
        elif isinstance(axes, list) or isinstance(axes, tuple):
            self.axes = {}
            for i in range(len(axes)):
                self.axes[axes[i]] = i
        else:
            raise Exception("Can not interpret axis specification %s. "
                            "Must be like {'t': 2, 'x': 0, 'y': 1} or "
                            "('t', 'x', 'y', 'c')" % str(axes))

        for x in ['t', 'x', 'y', 'c']:
            self.axes[x] = self.axes.get(x, None)
        axes = self.axes

        if xvals is not None:
            self.tVals = xvals
        elif axes['t'] is not None:
            if hasattr(img, 'xvals'):
                try:
                    self.tVals = img.xvals(axes['t'])
                except Exception:
                    self.tVals = np.arange(img.shape[axes['t']])
            else:
                self.tVals = np.arange(img.shape[axes['t']])

        self.currentIndex = 0
        self.updateImage(autoHistogramRange=autoHistogramRange)
        if levels is None and autoLevels:
            self.autoLevels()
        # this does nothing since getProcessedImage sets these values again.
        if levels is not None:
            self.setLevels(*levels)

        if self.roiEnabled:
            self.roiChanged()

        if self.axes['t'] is not None:
            self.ui.roiPlot.setXRange(self.tVals.min(), self.tVals.max())
            self.timeLine.setValue(0)
            if len(self.tVals) > 1:
                start = self.tVals.min()
                stop = (self.tVals.max() +
                        abs(self.tVals[-1] - self.tVals[0]) * 0.02)
            elif len(self.tVals) == 1:
                start = self.tVals[0] - 0.5
                stop = self.tVals[0] + 0.5
            else:
                start = 0
                stop = 1
            for s in [self.timeLine, self.normRgn]:
                s.setBounds([start, stop])

        self.imageItem.resetTransform()
        if scale is not None:
            self.imageItem.scale(*scale)
        if pos is not None:
            self.imageItem.setPos(*pos)
        if transform is not None:
            self.imageItem.setTransform(transform)

        if autoRange:
            self.autoRange()
        self._updateRoiPlot()

    # Override normalization functions to just return the image as is
    # We do not use ImageView's normalization features (yet)
    def normalize(self, image):
        return image.view(np.ndarray).copy()

    # Override as well
    def updateNorm(self):
        pass

    def setROIType(self, roiType):
        if roiType is None:
            self.roiEnabled = False
        else:
            self.roiEnabled = True
            if roiType == ROIType.SEMIAUTOMATIC:
                self._showSlider()
            else:
                self._hideSlider()
            self.roi.setROIType(roiType)
        self._updateRoiPlot()

    def _updateRoiPlot(self):
        showRoiPlot = False
        if self.roiEnabled:
            showRoiPlot = True
            self.roi.show()
            self.ui.roiPlot.setMouseEnabled(True, True)
            self.ui.splitter.setSizes(
                [self.height() * 0.6, self.height() * 0.4])
            self.roiCurve.show()
            self.roiChanged()
            self.ui.roiPlot.showAxis('left')
        else:
            self.roi.hide()
            self.ui.roiPlot.setMouseEnabled(False, False)
            self.roiCurve.hide()
            self.ui.roiPlot.hideAxis('left')

        if self.hasTimeAxis():
            showRoiPlot = True
            mn = self.tVals.min()
            mx = self.tVals.max()
            self.ui.roiPlot.setXRange(mn, mx, padding=0.01)
            self.timeLine.show()
            self.timeLine.setBounds([mn, mx])
            self.ui.roiPlot.show()
            if not self.roiEnabled:
                self.ui.splitter.setSizes([self.height() - 35, 35])
        else:
            self.timeLine.hide()

        self.ui.roiPlot.setVisible(showRoiPlot)

    def _hideSlider(self):
        self.ui.horizontalSliderLabel.hide()
        self.ui.horizontalSlider.hide()
        self.ui.sliderValueSpinBox.hide()

    def _showSlider(self):
        self.ui.horizontalSliderLabel.show()
        self.ui.horizontalSlider.show()
        self.ui.sliderValueSpinBox.show()

    def _sliderValueChanged(self, value):
        spinBoxVal = value / 100.0
        self.ui.sliderValueSpinBox.setValue(spinBoxVal)
        self.roi.setSliderValue(spinBoxVal)

    def _spinBoxChanged(self, value):
        self.ui.horizontalSlider.setValue(int(value * 100))
        self.roi.setSliderValue(value)