Beispiel #1
0
    def __init__(self):
        super().__init__()
        self.resize(420, 400)
        self.show()

        plot = self.addPlot()  # title="non-interactive")

        # prepare demonstration data:
        data = np.fromfunction(
            lambda i, j: (1 + 0.3 * np.sin(i)) * (i)**2 + (j)**2, (100, 100))
        noisy_data = data * (1 + 0.2 * np.random.random(data.shape))

        # Example: False color image with interactive level adjustment
        img = pg.ImageItem(image=noisy_data
                           )  # create monochrome image from demonstration data
        plot.addItem(img)  # add to PlotItem 'plot'
        cm = pg.colormap.get('CET-L9')  # prepare a linear color map
        bar = pg.ColorBarItem(values=(0, 20_000),
                              cmap=cm)  # prepare interactive color bar
        # Have ColorBarItem control colors of img and appear in 'plot':
        bar.setImageItem(img, insert_in=plot)

        self.timer = pg.QtCore.QTimer(singleShot=True)
        self.timer.timeout.connect(self.export)
        self.timer.start(100)
Beispiel #2
0
    def __init__(self, parent=None, lockAspect=False, raw=False):
        super().__init__(parent)
        self.ui = Ui_Form_static()
        self.ui.setupUi(self)

        # Some options for Raw Images
        self.raw = raw

        # Image pane setup
        self.image_layout = Qt.QtWidgets.QHBoxLayout(self.ui.imageFrame)
        self.image_layout.setContentsMargins(0, 0, 0, 0)
        self.image_layout.setSpacing(0)

        self.image_win = pg.GraphicsLayoutWidget()
        self.image_layout.addWidget(self.image_win)
        self.imageViewBox = RectViewBox(lockAspect=lockAspect)
        self.image_plot = self.image_win.addPlot(viewBox=self.imageViewBox)
        self.imageItem = pgXDImageItem(raw=self.raw)
        self.image_plot.addItem(self.imageItem)

        # Make Label Item for showing position
        self.make_pos_label()

        self.histogram = pg.ColorBarItem(width=15)
        # Have ColorBarItem control colors of img and appear in 'plot':
        self.histogram.setImageItem(self.imageItem, insert_in=self.image_plot)

        self.raw_image = np.zeros(0)
        self.displayed_image = np.zeros(0)
        self.show()
Beispiel #3
0
    def __init__(self, parent: Optional[QtWidgets.QWidget] = None) -> None:
        super().__init__(parent)

        self.plot: pg.PlotItem = self.graphicsLayout.addPlot()

        cmap = pg.colormap.get('viridis', source='matplotlib')
        self.colorbar: pg.ColorBarItem = pg.ColorBarItem(interactive=True, values=(0, 1),
                                                         cmap=cmap, width=15)
        self.graphicsLayout.addItem(self.colorbar)

        self.img: Optional[pg.ImageItem] = None
        self.scatter: Optional[pg.ScatterPlotItem] = None
        self.scatterZVals: Optional[np.ndarray] = None
    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)
        gr_wid = pg.GraphicsLayoutWidget(show=True)
        self.setCentralWidget(gr_wid)
        self.setWindowTitle('pyqtgraph example: Correlation matrix display')
        self.resize(600, 500)
        self.show()

        corrMatrix = np.array([[1., 0.5184571, -0.70188642],
                               [0.5184571, 1., -0.86094096],
                               [-0.70188642, -0.86094096, 1.]])
        columns = ["A", "B", "C"]

        pg.setConfigOption('imageAxisOrder',
                           'row-major')  # Switch default order to Row-major

        correlogram = pg.ImageItem()
        # create transform to center the corner element on the origin, for any assigned image:
        tr = QtGui.QTransform().translate(-0.5, -0.5)
        correlogram.setTransform(tr)
        correlogram.setImage(corrMatrix)

        plotItem = gr_wid.addPlot(
        )  # add PlotItem to the main GraphicsLayoutWidget
        plotItem.invertY(True)  # orient y axis to run top-to-bottom
        plotItem.setDefaultPadding(0.0)  # plot without padding data range
        plotItem.addItem(correlogram)  # display correlogram

        # show full frame, label tick marks at top and left sides, with some extra space for labels:
        plotItem.showAxes(True, showValues=(True, True, False, False), size=20)

        # define major tick marks and labels:
        ticks = [(idx, label) for idx, label in enumerate(columns)]
        for side in ('left', 'top', 'right', 'bottom'):
            plotItem.getAxis(side).setTicks(
                (ticks, []))  # add list of major ticks; no minor ticks
        plotItem.getAxis('bottom').setHeight(
            10)  # include some additional space at bottom of figure

        colorMap = pg.colormap.get(
            "CET-D1")  # choose perceptually uniform, diverging color map
        # generate an adjustabled color bar, initially spanning -1 to 1:
        bar = pg.ColorBarItem(values=(-1, 1), colorMap=colorMap)
        # link color bar and color map to correlogram, and show it in plotItem:
        bar.setImageItem(correlogram, insert_in=plotItem)
Beispiel #5
0
    def loadAnImage(self, image, colormap, cmap_limits, opacity = 1):
        """ load single image and colorbar to the widget. This function will be looped for
        multiple images later
        """
        # get pg image item
        img = pg.ImageItem()
        # add image to the graphicsview widget
        self.canvas.addItem(img)
        # set the color map
        cmap = pg.ColorMap(pos=np.linspace(0, 1, len(colormap)), color=colormap)
        #image = np.squeeze(tf.imread(image_path))
        # set image to the image item with cmap
        img.setImage(np.array(image), lut=cmap.getLookupTable(), opacity = opacity)

        # set colorbar for thresholding
        bar = pg.ColorBarItem(
            values=cmap_limits,
            cmap=cmap,
            limits=(0, None),
            orientation='vertical'
        )
        bar.setImageItem(img)
        # set composition mode to plus for overlaying
        img.setCompositionMode(QtGui.QPainter.CompositionMode_Plus)
Beispiel #6
0
    def _replot(self):
        logger.info('')

        self.kymographPlot.clear()
        self.kymographPlot.show()

        if self.ba is None:
            return

        myTif = self.ba.tifData
        #self.myImageItem = pg.ImageItem(myTif, axisOrder='row-major')
        #  TODO: set height to micro-meters
        axisOrder = 'row-major'
        rect = [0, 0, self.ba.recordingDur,
                self.ba.tifData.shape[0]]  # x, y, w, h
        if self.myImageItem is None:
            # first time build
            self.myImageItem = kymographImage(myTif,
                                              axisOrder=axisOrder,
                                              rect=rect)
            # redirect hover to self (to display intensity
            self.myImageItem.hoverEvent = self.hoverEvent
        else:
            # second time update
            myTif = self.ba.tifData
            self.myImageItem.setImage(myTif, axisOrder=axisOrder, rect=rect)
        self.kymographPlot.addItem(self.myImageItem)

        # color bar with contrast !!!
        if myTif.dtype == np.dtype('uint8'):
            bitDepth = 8
        elif myTif.dtype == np.dtype('uint16'):
            bitDepth = 16
        else:
            bitDepth = 16
            logger.error(f'Did not recognize tif dtype: {myTif.dtype}')

        minTif = np.nanmin(myTif)
        maxTif = np.nanmax(myTif)
        #print(type(dtype), dtype)  # <class 'numpy.dtype[uint16]'> uint16

        cm = pg.colormap.get('Greens_r',
                             source='matplotlib')  # prepare a linear color map
        #values = (0, 2**bitDepth)
        values = (0, maxTif)
        limits = values
        logger.info(
            f'color bar bit depth is {bitDepth} with values is {values}')
        bar = pg.ColorBarItem(
            values=values, limits=limits, cmap=cm,
            orientation='horizontal')  # prepare interactive color bar
        # Have ColorBarItem control colors of img and appear in 'plot':
        bar.setImageItem(self.myImageItem, insert_in=self.kymographPlot)

        kymographRect = self.ba.getKymographRect()
        if kymographRect is not None:
            xRoiPos = kymographRect[0]
            yRoiPos = kymographRect[3]
            top = kymographRect[1]
            right = kymographRect[2]
            bottom = kymographRect[3]
            widthRoi = right - xRoiPos + 1
            #heightRoi = bottom - yRoiPos + 1
            heightRoi = top - yRoiPos + 1
        '''
		else:
			#  TODO: Put this logic into function in bAbfText
			pos, size = self.ba.defaultTifRoi()

			xRoiPos = 0  # startSeconds
			yRoiPos = 0  # pixels
			widthRoi = myTif.shape[1]
			heightRoi = myTif.shape[0]
			tifHeightPercent = myTif.shape[0] * 0.2
			#print('tifHeightPercent:', tifHeightPercent)
			yRoiPos += tifHeightPercent
			heightRoi -= 2 * tifHeightPercent
		'''
        # TODO: get this out of replot, recreating the ROI is causing runtime error
        pos = (xRoiPos, yRoiPos)
        size = (widthRoi, heightRoi)
        if self.myLineRoi is None:
            self.myLineRoi = pg.ROI(pos=pos,
                                    size=size,
                                    parent=self.myImageItem)
            self.myLineRoi.addScaleHandle((0, 0), (1, 1),
                                          name='topleft')  # at origin
            self.myLineRoi.addScaleHandle((0.5, 0), (0.5, 1))  # top center
            self.myLineRoi.addScaleHandle((0.5, 1), (0.5, 0))  # bottom center
            self.myLineRoi.addScaleHandle((0, 0.5), (1, 0.5))  # left center
            self.myLineRoi.addScaleHandle((1, 0.5), (0, 0.5))  # right center
            self.myLineRoi.addScaleHandle((1, 1), (0, 0),
                                          name='bottomright')  # bottom right
            self.myLineRoi.sigRegionChangeFinished.connect(
                self.kymographChanged)
        else:
            self.myLineRoi.setPos(pos, finish=False)
            self.myLineRoi.setSize(size, finish=False)

        # background kymograph ROI
        backgroundRect = self.ba.getKymographBackgroundRect(
        )  # keep this in the backend
        if backgroundRect is not None:
            xRoiPos = backgroundRect[0]
            yRoiPos = backgroundRect[1]
            right = backgroundRect[2]
            bottom = backgroundRect[3]
            widthRoi = right - xRoiPos + 1
            heightRoi = bottom - yRoiPos + 1

            # TODO: get this out of replot, recreating the ROI is causing runtime error
            self.myLineRoiBackground = pg.ROI(pos=(xRoiPos, yRoiPos),
                                              size=(widthRoi, heightRoi),
                                              parent=self.myImageItem)

        # update min/max labels
        # TODO: also display min max within roi ???
        self.tifMinLabel.setText(f'Min:{minTif}')
        self.tifMaxLabel.setText(f'Max:{maxTif}')

        self._updateRoiMinMax(kymographRect)
Beispiel #7
0
    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)
        gr_wid = pg.GraphicsLayoutWidget(show=True)
        self.setCentralWidget(gr_wid)
        self.setWindowTitle('pyqtgraph example: Interactive color bar')
        self.resize(800, 700)
        self.show()

        ## Create image items
        data = np.fromfunction(
            lambda i, j: (1 + 0.3 * np.sin(i)) * (i)**2 + (j)**2, (100, 100))
        noisy_data = data * (1 + 0.2 * np.random.random(data.shape))
        noisy_transposed = noisy_data.transpose()

        #--- add non-interactive image with integrated color ------------------
        p1 = gr_wid.addPlot(title="non-interactive")
        # Basic steps to create a false color image with color bar:
        i1 = pg.ImageItem(image=data)
        p1.addItem(i1)
        p1.addColorBar(i1, colorMap='CET-L9',
                       values=(0, 30_000))  # , interactive=False)
        #
        p1.setMouseEnabled(x=False, y=False)
        p1.disableAutoRange()
        p1.hideButtons()
        p1.setRange(xRange=(0, 100), yRange=(0, 100), padding=0)
        p1.showAxes(True, showValues=(True, False, False, True))

        #--- add interactive image with integrated horizontal color bar -------
        i2 = pg.ImageItem(image=noisy_data)
        p2 = gr_wid.addPlot(1, 0, 1, 1, title="interactive")
        p2.addItem(i2, title='')
        # inserted color bar also works with labels on the right.
        p2.showAxis('right')
        p2.getAxis('left').setStyle(showValues=False)
        p2.getAxis('bottom').setLabel('bottom axis label')
        p2.getAxis('right').setLabel('right axis label')

        bar = pg.ColorBarItem(values=(0, 30_000),
                              colorMap='CET-L4',
                              label='horizontal color bar',
                              limits=(0, None),
                              rounding=1000,
                              orientation='h',
                              pen='#8888FF',
                              hoverPen='#EEEEFF',
                              hoverBrush='#EEEEFF80')
        bar.setImageItem(i2, insert_in=p2)

        #--- multiple images adjusted by a separate color bar ------------------------
        i3 = pg.ImageItem(image=noisy_data)
        p3 = gr_wid.addPlot(0, 1, 1, 1, title="shared 1")
        p3.addItem(i3)

        i4 = pg.ImageItem(image=noisy_transposed)
        p4 = gr_wid.addPlot(1, 1, 1, 1, title="shared 2")
        p4.addItem(i4)

        cmap = pg.colormap.get('CET-L8')
        bar = pg.ColorBarItem(
            # values = (-15_000, 15_000),
            limits=(-30_000, 30_000),  # start with full range...
            rounding=1000,
            width=10,
            colorMap=cmap)
        bar.setImageItem([i3, i4])
        bar.setLevels(low=-5_000,
                      high=15_000)  # ... then adjust to retro sunset.

        # manually adjust reserved space at top and bottom to align with plot
        bar.getAxis('bottom').setHeight(21)
        bar.getAxis('top').setHeight(31)
        gr_wid.addItem(bar, 0, 2, 2, 1)  # large bar spanning both rows
    def __init__(self, *args, **kwargs):
        super(self.__class__, self).__init__(*args, **kwargs)

        args, _ = parser.parse_known_args()

        if args.samplerate == None:
            self.samplerate = \
                int(sd.query_devices(args.input_device)['default_samplerate'])
        else:
            self.samplerate = int(args.samplerate)
        print(f"INFO -- Sampling rate at {self.samplerate} Hz")

        self.threadpool = QtCore.QThreadPool()

        self.q = queue.Queue()

        self.setFixedSize(args.width, args.height)
        self.mainbox = QtWidgets.QWidget()
        self.setCentralWidget(self.mainbox)
        self.layout = QtWidgets.QGridLayout()
        self.mainbox.setLayout(self.layout)

        # Widgets
        self.spec_plot = SpectrogramWidget()
        self.wave_plot = WaveFormWidget()

        for i, widget in enumerate([self.spec_plot, self.wave_plot]):
            self.layout.addWidget(widget, i, 0)

        # Initialize x and y
        self.length = self.samplerate * args.duration
        self.y = np.random.rand(self.length, len(args.channels))
        self.x = np.linspace(0, args.duration, num=self.length)

        self.zcr = librosa.feature.zero_crossing_rate(self.y.mean(axis=1))[0]

        # Wave Plot
        self.waveline_1 = self.wave_plot.plot(x=self.x,
                                              y=self.y[:, 0],
                                              pen=pg.mkPen('g', width=0.5),
                                              name='channel_1')
        self.waveline_2 = self.wave_plot.plot(x=self.x,
                                              y=self.y[:, 1],
                                              pen=pg.mkPen('y', width=0.5),
                                              name='channel_2')
        self.waveline_3 = self.wave_plot.plot(x=np.linspace(
            0, args.duration, self.zcr.shape[0]),
                                              y=self.zcr,
                                              pen=pg.mkPen('r', width=2),
                                              name='zcr')

        # Spectrogram
        self.fmax = int(
            librosa.core.fft_frequencies(sr=self.samplerate,
                                         n_fft=args.n_fft)[-1])
        D = librosa.stft(y=self.y.mean(axis=1), n_fft=args.n_fft, center=False)
        self.specdata = librosa.amplitude_to_db(np.abs(D), ref=np.max)

        # M = librosa.feature.melspectrogram(
        #             y=self.y.mean(axis=1),
        #             sr=self.samplerate,
        #             n_fft=args.n_fft,
        #             n_mels=args.n_mels)
        # self.specdata = librosa.power_to_db(S=M, ref=np.max)

        self.F0 = librosa.yin(y=self.y.mean(axis=1),
                              sr=self.samplerate,
                              frame_length=2048,
                              fmin=librosa.note_to_hz('C2'),
                              fmax=librosa.note_to_hz('C5'),
                              center=False)
        self.spec_image = pg.ImageItem(item=self.specdata.T)
        self.spec_plot.addItem(item=self.spec_image)
        self.f0_line = self.spec_plot.plot(x=np.linspace(
            0, args.duration, self.F0.shape[0]),
                                           y=self.F0,
                                           pen=pg.mkPen('r', width=2),
                                           name='f0')
        self.bar = pg.ColorBarItem(values=(librosa.note_to_hz('C2'),
                                           librosa.note_to_hz('C5')),
                                   cmap=pg.colormap.get('CET-L9'))
        self.bar.setImageItem(self.spec_image)

        # Start audio stream and animations
        self.start_stream()
        if args.input_device == 'Virtual Input (VB-Audio Virtual Cable), Windows DirectSound':
            self.play_media(media_url=args.media_url,
                            type='stream',
                            volume=100)
        self.animate()
        self.show()
Beispiel #9
0
    def __init__(self,
                 array,
                 y_scale,
                 y_spacing,
                 chan_map,
                 nav_trace,
                 x_scale=1,
                 load_channels='all',
                 max_zoom=120.0,
                 units='V'):
        """
        Scroller GUI loading a minimal amount of timeseries to display.

        X-units are currently assumed to be seconds.

        Parameters
        ----------
        array : array-like
            Array-like access to a mapped array (i.e. can index with
            array[i, range] slices).
        y_scale : float
            Scale the raw samples to units.
        y_spacing : float
            Spacing for the channel traces (in units)
        chan_map : ChannelMap
            Matrix map of the channels.
        nav_trace : array-like (1D)
            Navigation trace that is the same length as the channel
            timeseries. If not given, then mean of channels is used.
            **THIS MAY BE A COSTLY COMPUTATION**
        x_scale : float (optional)
            X-axis scale (e.g. sampling interval)
        load_channels : sequence | 'all'
            Order of (subset of) channels to load. Otherwise load all in array.
        units : str
            Y-axis units

        """

        nchan = array.shape[0]
        if isinstance(load_channels, str) and load_channels.lower() == 'all':
            load_channels = range(nchan)
            self.chan_map = chan_map
        elif len(load_channels) == len(chan_map):
            self.chan_map = chan_map
        elif len(load_channels) < len(chan_map):
            # "load_channels" indexes an array of recording channels
            # that may include grounded channels or other junk.
            raise NotImplementedError('cannot yet subset data channels')
            # new_cm = ChannelMap( [chan_map[i] for i in load_channels],
            #                       chan_map.geometry,
            #                       col_major=chan_map.col_major )
            # self.chan_map = new_cm
        else:
            raise ValueError('cannot map the listed channels')

        self.array = array
        self.y_scale = y_scale
        if isinstance(load_channels, str) and load_channels == 'all':
            load_channels = list(range(len(array)))
        self.load_channels = load_channels

        # The main window + layout
        self.win = pg.GraphicsLayoutWidget(border=(10, 10, 10))
        layout = self.win.ci
        # Adding columns to layout: just titles on the top row
        layout.addLabel('Array heatmap')
        layout.addLabel('Zoomed plot')
        layout.addLabel('|')
        # Next row has 1) the heatmap image with the colorbar widget
        layout.nextRow()
        sub_layout = layout.addLayout(colspan=1)
        self.img = pg.ImageItem(
            image=np.random.randn(*self.chan_map.geometry) *
            y_spacing)  # * 1e6 / 2)
        cmap = pg.colormap.get('coolwarm', source='matplotlib')
        p_img = sub_layout.addPlot()
        self.p_img = p_img
        p_img.getViewBox().setAspectLocked()
        p_img.addItem(self.img)
        p_img.hideAxis('bottom')
        p_img.hideAxis('left')
        mid_x, top_y = self.chan_map.geometry[1] / 2.0, self.chan_map.geometry[
            0] + 2.0

        # add a text label on top of the box ("anchor" has text box is centered on its x, y position)
        self.frame_text = pg.TextItem('empty',
                                      anchor=(0.5, 0.5),
                                      color=(255, 255, 255))
        self.frame_text.setPos(mid_x, top_y)
        # self.vb_img.addItem(self.frame_text)
        p_img.getViewBox().addItem(self.frame_text)
        p_img.getViewBox().autoRange()

        # colorbar
        self.cb = pg.ColorBarItem(limits=None,
                                  cmap=cmap,
                                  hoverBrush='#EEEEFF80',
                                  rounding=10e-6,
                                  values=(-y_spacing, y_spacing))
        self.cb.getAxis('left').setLabel('')
        self.cb.getAxis('right').setLabel('Voltage', units='V')
        self.cb.setImageItem(self.img)
        sub_layout.addItem(self.cb)

        # 2) the stacked traces plot (colspan 2)
        axis = PlainSecAxis(orientation='bottom')
        self.p1 = layout.addPlot(colspan=2,
                                 row=1,
                                 col=1,
                                 axisItems={'bottom': axis})

        self.p1.enableAutoRange(axis='y', enable=True)
        self.p1.setAutoVisible(y=False)
        self.p1.setLabel('left', 'Amplitude', units=units)
        self.p1.setLabel('bottom', 'Time')

        # Next layout row has the navigator plot (colspan 3)
        layout.nextRow()

        # The navigator plot
        axis = HMSAxis(orientation='bottom')
        self.p2 = layout.addPlot(row=2,
                                 col=0,
                                 colspan=3,
                                 axisItems={'bottom': axis})
        self.p2.setLabel('left', 'Amplitude', units=units)
        self.p2.setLabel('bottom', 'Time')
        self.region = pg.LinearRegionItem()
        self.region.setZValue(10)
        initial_pts = 5000
        self.region.setRegion([0, initial_pts * x_scale])

        # Add the LinearRegionItem to the ViewBox,
        # but tell the ViewBox to exclude this
        # item when doing auto-range calculations.
        self.p2.addItem(self.region, ignoreBounds=True)

        self.p1.setAutoVisible(y=True)
        self.p1.setXRange(0, initial_pts * x_scale)
        self.p1.vb.setLimits(maxXRange=max_zoom)

        # Multiple curve set that calls up data on-demand
        self.curve_manager = CurveManager(plot=self.p1)
        curves = PlotCurveCollection(array, load_channels, x_scale, y_scale,
                                     y_spacing)
        curves.setPen(color='w', width=1)
        self.curve_manager.add_new_curves(curves, 'all', set_source=True)
        # Set the heatmap to track these curves
        self.curve_manager.heatmap_name = 'all'

        # Selected curve & label set that calls up data on-demand
        labels = ['({}, {})'.format(i, j) for i, j in zip(*chan_map.to_mat())]
        selected_curves = LabeledCurveCollection(curves,
                                                 labels,
                                                 clickable=True)
        self.curve_manager.add_new_curves(selected_curves, 'selected')
        for text in selected_curves.texts:
            self.p1.addItem(text)

        # Add mean trace to bottom plot
        self.nav_trace = pg.PlotCurveItem(x=np.arange(len(nav_trace)) *
                                          x_scale,
                                          y=nav_trace)
        self.p2.addItem(self.nav_trace)
        self.p2.setXRange(0, min(5e4, len(nav_trace)) * x_scale)
        self.p2.setYRange(*np.percentile(nav_trace, [1, 99]))

        # Set bidirectional plot interaction
        # need to hang onto references?
        self._db_cnx1 = DebounceCallback.connect(self.region.sigRegionChanged,
                                                 self.update_zoom_callback)
        self._db_cnx2 = DebounceCallback.connect(self.p1.sigXRangeChanged,
                                                 self.update_region_callback)

        # Do navigation jumps (if shift key is down)
        self.p2.scene().sigMouseClicked.connect(self.jump_nav)

        # Do fine interaction in zoomed plot with vertical line
        self.vline = pg.InfiniteLine(angle=90, movable=False)
        self.p1.addItem(self.vline)
        self.p1.scene().sigMouseMoved.connect(self.fine_nav)

        # final adjustments to rows: args are (row, stretch)
        self.win.centralWidget.layout.setRowStretchFactor(0, 0.5)
        self.win.centralWidget.layout.setRowStretchFactor(1, 5)
        self.win.centralWidget.layout.setRowStretchFactor(2, 2.5)

        # a callable frame filter may be set on this object to affect frame display
        self.frame_filter = None

        # set up initial frame
        self.set_mean_image()
Beispiel #10
0
 def updateplot(self):
     self.mainloop()
     if not self.ui.drawingCheck.checkState() == QtCore.Qt.Checked: return
     for li in range(self.nlines):
         maxchan=li-HaasoscopeLibQt.num_chan_per_board*HaasoscopeLibQt.num_board
         if maxchan>=0: # these are the slow ADC channels
             self.lines[li].setData(d.xydataslow[maxchan][0],d.xydataslow[maxchan][1])
         else:
             self.lines[li].setData(d.xydata[li][0],d.xydata[li][1])
     if d.dologicanalyzer:
         for li in np.arange(d.num_logic_inputs):
             self.lines[d.logicline1+li].setData(d.xydatalogic[li][0],d.xydatalogic[li][1])
     if d.dofft:
         self.fftui.fftline.setPen(self.linepens[d.fftchan])
         self.fftui.fftline.setData(d.fftfreqplot_xdata,d.fftfreqplot_ydata)
         self.fftui.ui.plot.setTitle("FFT plot of channel "+str(d.fftchan))
         self.fftui.ui.plot.setLabel('bottom', d.fftax_xlabel)
         self.fftui.ui.plot.setRange(xRange=(0.0, d.fftax_xlim))
         now = time.time()
         dt = now - self.fftui.fftlastTime
         if dt>3.0 or self.fftyrange<d.fftfreqplot_ydatamax*1.1:
             self.fftui.fftlastTime = now
             self.fftui.ui.plot.setRange(yRange=(0.0, d.fftfreqplot_ydatamax*1.1))
             self.fftyrange = d.fftfreqplot_ydatamax * 1.1
         if not self.fftui.isVisible(): # closed the fft window
             d.dofft = False
             self.ui.fftCheck.setCheckState(QtCore.Qt.Unchecked)
     if d.recorddata:
         if self.firstpersist:
             self.image1 = pg.ImageItem(image=d.recorded2d,opacity=0.5)
             self.persistui.ui.plot.addItem(self.image1)
             self.persistui.ui.plot.setTitle("Persist plot of channel "+str(d.recorddatachan))
             self.cmap = pg.colormap.get('CET-D8')
             self.firstpersist=False
         else:
             self.image1.setImage(image=d.recorded2d,opacity=0.5)
         self.image1.setRect(QtCore.QRectF(d.min_x,d.min_y,d.max_x-d.min_x,d.max_y-d.min_y))
         self.persistui.ui.plot.setLabel('bottom', d.xlabel)
         self.bar = pg.ColorBarItem(interactive=False, values= (0, np.max(d.recorded2d)), cmap=self.cmap)
         self.bar.setImageItem(self.image1)
         if not self.persistui.isVisible(): # closed the fft window
             d.recorddata = False
             self.ui.persistCheck.setCheckState(QtCore.Qt.Unchecked)
     if self.ui.decodeCheck.checkState() == QtCore.Qt.Checked:
         # delete all previous labels
         for label in self.decodelabels:
             self.ui.plot.removeItem(label)
         if d.downsample>=0:
             resulttext,resultstart,resultend = d.decode()
             #print(resulttext,resultstart, d.min_x, d.max_x, d.xscale, d.xscaling)
             item=0
             while item<len(resulttext):
                 label = pg.TextItem(resulttext[item])
                 label.setColor(self.linepens[d.selectedchannel].color())
                 x=d.min_x+resultstart[item]*1e9/d.xscaling
                 label.setPos( QtCore.QPointF(x, -1) )
                 #print(x)
                 self.ui.plot.addItem(label)
                 self.decodelabels.append(label)
                 item=item+1
         else:
             print("can't decode when downsample is <0... go slower!")
     now = time.time()
     dt = now - self.lastTime + 0.00001
     self.lastTime = now
     if self.fps is None:
         self.fps = 1.0/dt
     else:
         s = np.clip(dt*3., 0, 1)
         self.fps = self.fps * (1-s) + (1.0/dt) * s
     self.ui.plot.setTitle('%0.2f fps' % self.fps)
     app.processEvents()