Esempio n. 1
0
 def update_region(self, view_range):
     # region.setRegion appears to signal twice (once per side?),
     # so do this in two stages to prevent double signaling.
     orig_range = self.region.getRegion()
     # possibilities for region morphing:
     # 1) sliding / expanding right: right side increases, left side may or may not increase
     # 2) sliding / expanding left: left side decreases, right side may or may not decrease
     # 3) expanding in place: left side decreases and right side increases
     # 4) shrinking in place: left side increases and right side decreases
     #
     # (1), (3) and (4) move right side first
     # (2) move left side first: condition is that new_left < old_left and new_right <= old_right
     if view_range[0] < orig_range[0] and view_range[1] <= orig_range[1]:
         first_region = [view_range[0], orig_range[1]]
     else:
         first_region = [orig_range[0], view_range[1]]
     # do right-side while blocked (force block in case we're already in a blocking context)
     info = parallel_context.get_logger().info
     with block_signals(self.region, forced_state=True):
         info('setting first range with block: {} {}'.format(
             self.region.signalsBlocked(), timestamp()))
         self.region.setRegion(first_region)
     # do both sides while emitting (or not)
     info('setting both sides with block: {} {}'.format(
         self.region.signalsBlocked(), timestamp()))
     self.region.setRegion(view_range)
 def unset_external_data(self, redraw=False, visible=False):
     info('Unsetting external (visible {}) and updating curve data {}'.
          format(visible, timestamp()))
     if not visible:
         # Revert to raw slices and reset visible data
         self._use_raw_slice = True
     if redraw:
         self.updatePlotData()
Esempio n. 3
0
 def jump_nav(self, evt):
     if QtGui.QApplication.keyboardModifiers() != QtCore.Qt.ShiftModifier:
         return
     pos = evt.scenePos()
     if not self.p2.sceneBoundingRect().contains(pos):
         return
     newX = self.p2.vb.mapSceneToView(pos).x()
     minX, maxX = self.region.getRegion()
     rng = 0.5 * (maxX - minX)
     info('Jump-updating region box {}'.format(timestamp()))
     self.update_region([newX - rng, newX + rng])
Esempio n. 4
0
 def set_mean_image(self):
     if self.curve_manager.heatmap_curve.y_visible is None:
         return
     image = np.nanstd(self.curve_manager.heatmap_curve.y_visible, axis=1)
     chan_map = self.curve_manager.heatmap_curve.map_curves(self.chan_map)
     x_vis = self.curve_manager.heatmap_curve.x_visible[0]
     x_avg = 0.5 * (x_vis[0] + x_vis[-1])
     frame = embed_frame(chan_map, image)
     info('setting RMS frame {}'.format(timestamp()))
     # self.img.setImage(frame, autoLevels=False)
     self.img.setImage(frame, autoLevels=False)
     self.frame_text.setText('Time ~ {:.3f}s'.format(x_avg))
Esempio n. 5
0
 def set_image_frame(self, x=None, frame_vec=(), move_vline=True):
     if not len(frame_vec):
         if x is None:
             # can't do anything!
             return
         idx = self.curve_manager.heatmap_curve.x_visible[0].searchsorted(x)
         frame_vec = self.curve_manager.heatmap_curve.y_visible[:, idx]
     chan_map = self.curve_manager.heatmap_curve.map_curves(self.chan_map)
     frame = embed_frame(chan_map, frame_vec)
     if self.frame_filter:
         frame = self.frame_filter(frame)
     info('Setting voltage frame {}'.format(timestamp()))
     # self.img.setImage(frame, autoLevels=False)
     self.img.setImage(frame, autoLevels=False)
     self.frame_text.setText('Time {:.3f}s'.format(x))
     if move_vline and x is not None:
         self.vline.setPos(x)
Esempio n. 6
0
    def apply_callback(self, *emitting):
        """
        Apply the real callback

        Parameters
        ----------
        emitting: QtObject
            The emitting object

        """
        # if the signal time stack is only one deep, then that signal was already dispatched
        if len(self._signal_times) == 1:
            return
        info = parallel_context.get_logger().info
        info('applying callback {} {} {}'.format(self.callback, emitting,
                                                 timestamp()))
        self.callback(*emitting)
        self._signal_times = list()
    def _set_data(self):
        r = self._view_range()
        if not r:
            self.y_visible = None
            self.x_visible = None
            return
        start, stop = r

        if self._cslice is None:
            needs_slice = True
        else:
            cstart = self._cslice.start
            cstop = self._cslice.stop
            # If the any view limit is outside the old slice bounds, update the loaded data
            needs_slice = start < cstart or stop > cstop

        if needs_slice:
            # When this condition, automatically reset state to raw slices.
            # If anything is watching this data to change, it will need access to the raw slice.
            info('Slicing from HDF5 {} and emitting data_changed'.format(
                timestamp()))
            N = stop - start
            L = self.hdf_data.shape[1]
            # load this many points before and after the view limits so that small updates
            # can grab pre-loaded data
            extra = int(self.load_extra / 2 * N)
            sl = np.s_[max(0, start - extra):min(L, stop + extra)]
            self._use_raw_slice = True
            self._raw_slice = self.hdf_data[(self.plot_channels, sl)] * self.dy
            # self._raw_slice = self.hdf5[sl] * self._yscale
            self._cslice = sl
            self.data_changed.emit(self)

        # can retreive data within the pre-load
        x0 = self._cslice.start
        y_start = start - x0
        y_stop = y_start + (stop - start)
        self.y_visible = self.y_slice[:, y_start:y_stop]
        self.x_visible = np.tile(
            np.arange(start, stop) * self.dx, (len(self.plot_channels), 1))
        info('{}: x,y arrays set: {} {}'.format(type(self),
                                                self.y_visible.shape,
                                                self.x_visible.shape))
 def set_text_position(self, selection):
     # the number of rows in x- and y-visible is equal to the number of active channels
     x_visible = self.x_visible
     y_visible = self.y_visible
     if y_visible is not None:
         y_visible = y_visible + self.y_offset
     label_row = 0
     for i, text in enumerate(self.texts):
         text.setVisible(self._active_channels[i])
         if not text.isVisible():
             continue
         # put text 20% from the left
         x_pos = 0.8 * x_visible[label_row, 0] + 0.2 * x_visible[label_row,
                                                                 -1]
         # set height to the max in a 10% window around x_pos
         wid = max(1, int(0.05 * y_visible.shape[1]))
         n_pos = int(0.2 * y_visible.shape[1])
         y_pos = y_visible[label_row, n_pos - wid:n_pos + wid].max()
         info('Setting text {}, {}: {}'.format(x_pos, y_pos, timestamp()))
         # print('Setting text {}, {}: {}'.format(x_pos, y_pos, timestamp()))
         text.setPos(x_pos, y_pos)
         label_row += 1
Esempio n. 9
0
 def update_region_callback(self, window, view_range, *args):
     info = parallel_context.get_logger().info
     with block_signals(self.region):
         info('region callback called with block: {} {}'.format(
             self.region.signalsBlocked(), timestamp()))
         self.update_region(view_range)
Esempio n. 10
0
 def update_zoom_callback(self, *args):
     # in callback mode, disable the signal from the zoom plot
     with block_signals(self.p1):
         info('zoom callback called with block: {} {}'.format(
             self.p1.signalsBlocked(), timestamp()))
         self.update_zoom_from_region()
    def updatePlotData(self, data_ready=False):
        # Use data_ready=True to indicate that there's pre-defined x- and y-visible data ready to plot
        if self.hdf_data is None or not len(self.plot_channels):
            self.setData([])
            self.y_visible = None
            self.x_visible = None
            return

        if not data_ready:
            self._set_data()
        r = self._view_range()
        if not r:
            return
        start, stop = r
        # Decide by how much we should downsample
        ds = int((stop - start) / self.limit) + 1
        if ds == 1:
            # Small enough to display with no intervention.
            visible = self.y_visible
            x_visible = self.x_visible
        else:
            # Here convert data into a down-sampled
            # array suitable for visualizing.

            N = self.y_visible.shape[1]

            # This downsample does min/max interleaving
            # -- does not squash noise
            # 1st axis: number of display points
            # 2nd axis: number of raw points per display point
            # ds = ds * 2
            # Nsub = max(5, N // ds)
            # y = self.y_visible[:Nsub * ds].reshape(Nsub, ds)
            # visible = np.empty(Nsub * 2, dtype='d')
            # visible[0::2] = y.min(axis=1)
            # visible[1::2] = y.max(axis=1)

            # This downsample picks 2 points regularly spaced in the segment
            ds = ds * 2
            Nsub = max(5, N // ds)
            p1 = ds // 4
            p2 = (3 * ds) // 4
            n_rows = self.y_visible.shape[0]
            y = self.y_visible[:, :Nsub * ds].reshape(n_rows, Nsub, ds)
            visible = np.empty((n_rows, Nsub * 2), dtype='d')
            visible[:, 0::2] = y[..., p1]
            visible[:, 1::2] = y[..., p2]

            # This downsample does block averaging
            # -- this flattens noisy segments
            # Nsub = max(5, N // ds)
            # y = self.y_visible[:Nsub * ds].reshape(Nsub, ds)
            # visible = y.mean(axis=1)

            x_samp = np.linspace(ds // 2, self.x_visible.shape[1] - ds // 2,
                                 Nsub * 2).astype('i')
            if x_samp[-1] >= self.x_visible.shape[1]:
                x_samp[-1] = N - 1
            x_visible = np.take(self.x_visible, x_samp, axis=1)

        info('{}: Update hdf5 lines called: external data {} {}'.format(
            type(self), data_ready, timestamp()))
        visible = visible + self.y_offset
        connects = np.ones(visible.shape, dtype='>i4')
        connects[:, -1] = 0
        # Set the data as one block without connections from row to row
        info('{}: x shape {}, y shape {}, mask shape {}'.format(
            type(self), x_visible.shape, visible.shape, connects.shape))
        self.setData(x=x_visible.ravel(),
                     y=visible.ravel(),
                     connect=connects.ravel())  # update_zoom_callback the plot
        # TODO: is this needed?
        self.resetTransform()
        # self.set_text_position()
        # mark these start, stop positions to match against future view-change signals
        self._cx1 = start
        self._cx2 = stop
        vis_rate = round(1 / (x_visible[0, 1] - x_visible[0, 0]))
        if vis_rate != self._current_vis_rate:
            self._current_vis_rate = vis_rate
            self.vis_rate_changed.emit(float(vis_rate))
        self.plot_changed.emit(self)
 def viewRangeChanged(self):
     # only look for x-range changes that actually require loading different data
     if self._view_really_changed():
         info('Zoom plot ranged actually changed, redrawing: {} {}'.format(
             self._view_range(), timestamp()))
         self.updatePlotData()