class HistPlot(QtWidgets.QWidget): def __init__(self, parent, logger): super(HistPlot, self).__init__() # store the logger instance self.logger = logger self.verticalScaleDivision = ScaleDivision(-140, 0, 100) self.verticalScaleTransform = CoordinateTransform(-140, 0, 100, 0, 0) self.verticalScale = VerticalScaleWidget(self, self.verticalScaleDivision, self.verticalScaleTransform) self.verticalScale.setTitle("PSD (dB A)") self.horizontalScaleDivision = ScaleDivision(44, 22000, 100) self.horizontalScaleTransform = CoordinateTransform(44, 22000, 100, 0, 0) self.horizontalScale = HorizontalScaleWidget(self, self.horizontalScaleDivision, self.horizontalScaleTransform) self.horizontalScale.setTitle("Frequency (Hz)") self.canvasWidget = CanvasWidget(self, self.verticalScaleTransform, self.horizontalScaleTransform) self.canvasWidget.setTrackerFormatter(lambda x, y: "%d Hz, %.1f dB" % (x, y)) plot_layout = QtWidgets.QGridLayout() plot_layout.setSpacing(0) plot_layout.setContentsMargins(0, 0, 0, 0) plot_layout.addWidget(self.verticalScale, 0, 0) plot_layout.addWidget(self.canvasWidget, 0, 1) plot_layout.addWidget(self.horizontalScale, 1, 1) self.setLayout(plot_layout) self.needfullreplot = False self.horizontalScaleTransform.setLogarithmic() self.horizontalScaleDivision.setLogarithmic() # insert an additional plot item for the peak bar self.bar_peak = HistogramPeakBarItem() self.canvasWidget.attach(self.bar_peak) self.peak = zeros((1,)) self.peak_int = 0 self.peak_decay = PEAK_DECAY_RATE self.histogram = HistogramItem() self.histogram.set_color(Qt.Qt.darkGreen) self.canvasWidget.attach(self.histogram) # need to replot here for the size Hints to be computed correctly (depending on axis scales...) self.update() def setdata(self, fl, fh, fc, y): self.histogram.setData(fl, fh, fc, y) self.compute_peaks(y) self.bar_peak.setData(fl, fh, self.peak, self.peak_int, y) # only draw on demand # self.draw() def draw(self): if self.needfullreplot: self.needfullreplot = False self.verticalScaleDivision.setLength(self.canvasWidget.height()) self.verticalScaleTransform.setLength(self.canvasWidget.height()) start_border, end_border = self.verticalScale.spacingBorders() self.verticalScaleTransform.setBorders(start_border, end_border) self.verticalScale.update() self.horizontalScaleDivision.setLength(self.canvasWidget.width()) self.horizontalScaleTransform.setLength(self.canvasWidget.width()) start_border, end_border = self.horizontalScale.spacingBorders() self.horizontalScaleTransform.setBorders(start_border, end_border) self.horizontalScale.update() x_major_tick = self.horizontalScaleDivision.majorTicks() x_minor_tick = self.horizontalScaleDivision.minorTicks() y_major_tick = self.verticalScaleDivision.majorTicks() y_minor_tick = self.verticalScaleDivision.minorTicks() self.canvasWidget.setGrid(array(x_major_tick), array(x_minor_tick), array(y_major_tick), array(y_minor_tick)) self.canvasWidget.update() # redraw when the widget is resized to update coordinates transformations def resizeEvent(self, event): self.needfullreplot = True self.draw() def compute_peaks(self, y): if len(self.peak) != len(y): y_ones = ones(y.shape) self.peak = y_ones * (-500.) self.peak_int = zeros(y.shape) self.peak_decay = y_ones * 20. * log10(PEAK_DECAY_RATE) * 5000 mask1 = (self.peak < y) mask2 = (~mask1) mask2_a = mask2 * (self.peak_int < 0.2) mask2_b = mask2 * (self.peak_int >= 0.2) self.peak[mask1] = y[mask1] self.peak[mask2_a] = self.peak[mask2_a] + self.peak_decay[mask2_a] self.peak_decay[mask1] = 20. * log10(PEAK_DECAY_RATE) * 5000 self.peak_decay[mask2_a] += 20. * log10(PEAK_DECAY_RATE) * 5000 self.peak_int[mask1] = 1. self.peak_int[mask2_b] *= 0.975 def setspecrange(self, spec_min, spec_max): self.verticalScaleTransform.setRange(spec_min, spec_max) self.verticalScaleDivision.setRange(spec_min, spec_max) # notify that sizeHint has changed (this should be done with a signal emitted from the scale division to the scale bar) self.verticalScale.scaleBar.updateGeometry() self.needfullreplot = True self.update() def setweighting(self, weighting): if weighting is 0: title = "PSD (dB)" elif weighting is 1: title = "PSD (dB A)" elif weighting is 2: title = "PSD (dB B)" else: title = "PSD (dB C)" self.verticalScale.setTitle(title)
class TimePlot(QtWidgets.QWidget): def __init__(self, parent): super(TimePlot, self).__init__() self.verticalScaleDivision = ScaleDivision(-1, 1, 100) self.verticalScaleTransform = CoordinateTransform(-1, 1, 100, 0, 0) self.verticalScale = VerticalScaleWidget(self, self.verticalScaleDivision, self.verticalScaleTransform) self.verticalScale.setTitle("Signal") self.horizontalScaleDivision = ScaleDivision(-1, 1, 100) self.horizontalScaleTransform = CoordinateTransform(-1, 1, 100, 0, 0) self.horizontalScale = HorizontalScaleWidget(self, self.horizontalScaleDivision, self.horizontalScaleTransform) self.horizontalScale.setTitle("Time (ms)") self.canvasWidget = GlCanvasWidget(self, self.verticalScaleTransform, self.horizontalScaleTransform) self.canvasWidget.setTrackerFormatter(lambda x, y: "%.3g ms, %.3g" % (x, y)) self.legendWidget = LegendWidget(self, self.canvasWidget) plotLayout = QtWidgets.QGridLayout() plotLayout.setSpacing(0) plotLayout.setContentsMargins(0, 0, 0, 0) plotLayout.addWidget(self.verticalScale, 0, 0) plotLayout.addWidget(self.canvasWidget, 0, 1) plotLayout.addWidget(self.horizontalScale, 1, 1) plotLayout.addWidget(self.legendWidget, 0, 2) self.setLayout(plotLayout) self.legendWidget.hide() self.needfullreplot = False self.curve = CurveItem() self.curve.setColor(QtGui.QColor(Qt.Qt.red)) # gives a title to the curve for the legend self.curve.setTitle("Ch1") self.canvasWidget.attach(self.curve) self.curve2 = CurveItem() self.curve2.setColor(QtGui.QColor(Qt.Qt.blue)) # gives a title to the curve for the legend self.curve2.setTitle("Ch2") # self.curve2 will be attached when needed # need to replot here for the size Hints to be computed correctly (depending on axis scales...) self.update() self.xmin = 0. self.xmax = 1. self.canvas_width = 0 self.dual_channel = False self.paused = False self.canvasWidget.resized.connect(self.canvasResized) def setdata(self, x, y): if self.canvas_width != self.canvasWidget.width(): Logger().push("timeplot : changed canvas width") self.canvas_width = self.canvasWidget.width() self.update_xscale() if self.dual_channel: self.dual_channel = False self.canvasWidget.detach(self.curve2) # disable the legend, useless when one channel is active self.legendWidget.hide() # the canvas reisze event will trigger a full replot if self.xmax != x[-1]: Logger().push("timeplot : changing x max") self.xmax = x[-1] self.settimerange(self.xmin, self.xmax) self.update_xscale() self.needfullreplot = True if self.xmin != x[0]: Logger().push("timeplot : changing x min") self.xmin = x[0] self.settimerange(self.xmin, self.xmax) self.update_xscale() self.needfullreplot = True if not self.paused: y_interp = np.interp(self.xscaled, x, y) self.curve.setData(self.xscaled, y_interp) self.draw() def draw(self): if self.needfullreplot: self.needfullreplot = False self.verticalScaleDivision.setLength(self.canvasWidget.height()) self.verticalScaleTransform.setLength(self.canvasWidget.height()) startBorder, endBorder = self.verticalScale.spacingBorders() self.verticalScaleTransform.setBorders(startBorder, endBorder) self.verticalScale.update() self.horizontalScaleDivision.setLength(self.canvasWidget.width()) self.horizontalScaleTransform.setLength(self.canvasWidget.width()) startBorder, endBorder = self.horizontalScale.spacingBorders() self.horizontalScaleTransform.setBorders(startBorder, endBorder) self.horizontalScale.update() self.canvasWidget.setGrid(np.array(self.horizontalScaleDivision.majorTicks()), np.array(self.horizontalScaleDivision.minorTicks()), np.array(self.verticalScaleDivision.majorTicks()), np.array(self.verticalScaleDivision.minorTicks())) def pause(self): self.paused = True self.canvasWidget.pause() def restart(self): self.paused = False self.canvasWidget.restart() # redraw when the widget is resized to update coordinates transformations # this is done instead of resizeEvent because the canvas can be resized independently of the whole plot (because the legend can disappear) def canvasResized(self, canvasWidth, canvasHeight): self.needfullreplot = True self.draw() def setdataTwoChannels(self, x, y, y2): if self.canvas_width != self.canvasWidget.width(): Logger().push("timeplot : changed canvas width") self.canvas_width = self.canvasWidget.width() self.update_xscale() if not self.dual_channel: self.dual_channel = True self.canvasWidget.attach(self.curve2) # enable the legend to discrimate between the two channels self.legendWidget.show() # the canvas reisze event will trigger a full replot if self.xmax != x[-1]: Logger().push("timeplot : changing x max") self.xmax = x[-1] self.settimerange(self.xmin, self.xmax) self.update_xscale() self.needfullreplot = True if self.xmin != x[0]: Logger().push("timeplot : changing x min") self.xmin = x[0] self.settimerange(self.xmin, self.xmax) self.update_xscale() self.needfullreplot = True if not self.paused: # y_interp = np.interp(self.xscaled, x, y) # y_interp2 = np.interp(self.xscaled, x, y2) # ClassPlot.setdata(self, self.xscaled, y_interp) # self.curve2.setData(self.xscaled, y_interp2) self.curve.setData(x, y) self.curve2.setData(x, y2) self.draw() def update_xscale(self): self.xscaled = np.linspace(self.xmin, self.xmax, self.canvas_width) def settimerange(self, time_min, time_max): self.horizontalScaleTransform.setRange(time_min, time_max) self.horizontalScaleDivision.setRange(time_min, time_max) # notify that sizeHint has changed (this should be done with a signal emitted from the scale division to the scale bar) self.horizontalScale.scaleBar.updateGeometry() self.needfullreplot = True self.draw() def setverticalrange(self, v_min, v_max): self.verticalScaleTransform.setRange(v_min, v_max) self.verticalScaleDivision.setRange(v_min, v_max) # notify that sizeHint has changed (this should be done with a signal emitted from the scale division to the scale bar) self.verticalScale.scaleBar.updateGeometry() self.needfullreplot = True self.draw() def setverticaltitle(self, title): self.verticalScale.setTitle(title) def sethorizontaltitle(self, title): self.horizontalScale.setTitle(title) def setTrackerFormatter(self, formatter): self.canvasWidget.setTrackerFormatter(formatter)
class SpectrumPlotWidget(QtWidgets.QWidget): def __init__(self, parent, logger=None): super(SpectrumPlotWidget, self).__init__() self.x1 = array([0.1, 0.5, 1.]) self.x2 = array([0.5, 1., 2.]) self.needtransform = False self.paused = False self.verticalScaleDivision = ScaleDivision(0, 1, 100) self.verticalScaleTransform = CoordinateTransform(0, 1, 100, 0, 0) self.verticalScale = VerticalScaleWidget(self, self.verticalScaleDivision, self.verticalScaleTransform) self.verticalScale.setTitle("PSD (dB)") self.horizontalScaleDivision = ScaleDivision(0, 22000, 100) self.horizontalScaleTransform = CoordinateTransform(0, 22000, 100, 0, 0) self.horizontalScale = HorizontalScaleWidget(self, self.horizontalScaleDivision, self.horizontalScaleTransform) self.horizontalScale.setTitle("Frequency (Hz)") self.canvasWidget = GlCanvasWidget(self, self.verticalScaleTransform, self.horizontalScaleTransform) self.canvasWidget.setTrackerFormatter(lambda x, y: "%d Hz, %.1f dB" %(x, y)) self.quadsItem = QuadsItem() self.canvasWidget.attach(self.quadsItem) plotLayout = QtWidgets.QGridLayout() plotLayout.setSpacing(0) plotLayout.setContentsMargins(0, 0, 0, 0) plotLayout.addWidget(self.verticalScale, 0, 0) plotLayout.addWidget(self.canvasWidget, 0, 1) plotLayout.addWidget(self.horizontalScale, 1, 1) self.setLayout(plotLayout) def setlinfreqscale(self): self.horizontalScaleTransform.setLinear() self.horizontalScaleDivision.setLinear() self.needtransform = True self.draw() def setlogfreqscale(self): self.horizontalScaleTransform.setLogarithmic() self.horizontalScaleDivision.setLogarithmic() self.needtransform = True self.draw() def setfreqrange(self, minfreq, maxfreq): self.xmin = minfreq self.xmax = maxfreq self.horizontalScaleTransform.setRange(minfreq, maxfreq) self.horizontalScaleDivision.setRange(minfreq, maxfreq) # notify that sizeHint has changed (this should be done with a signal emitted from the scale division to the scale bar) self.horizontalScale.scaleBar.updateGeometry() self.needtransform = True self.draw() def setspecrange(self, spec_min, spec_max): self.verticalScaleTransform.setRange(spec_min, spec_max) self.verticalScaleDivision.setRange(spec_min, spec_max) # notify that sizeHint has changed (this should be done with a signal emitted from the scale division to the scale bar) self.verticalScale.scaleBar.updateGeometry() self.needtransform = True self.draw() def setweighting(self, weighting): if weighting is 0: title = "PSD (dB)" elif weighting is 1: title = "PSD (dB A)" elif weighting is 2: title = "PSD (dB B)" else: title = "PSD (dB C)" self.verticalScale.setTitle(title) self.needtransform = True self.draw() def setShowFreqLabel(self, showFreqLabel): self.canvasWidget.setShowFreqLabel(showFreqLabel) def set_peaks_enabled(self, enabled): self.quadsItem.set_peaks_enabled(enabled) def set_baseline_displayUnits(self, baseline): self.quadsItem.set_baseline_displayUnits(baseline) def set_baseline_dataUnits(self, baseline): self.quadsItem.set_baseline_dataUnits(baseline) def setdata(self, x, y, fmax): x1 = zeros(x.shape) x2 = zeros(x.shape) x1[0] = 1e-10 x1[1:] = (x[1:] + x[:-1])/2. x2[:-1] = x1[1:] x2[-1] = float(SAMPLING_RATE/2) if len(x1) != len(self.x1): self.needtransform = True self.x1 = x1 self.x2 = x2 if not self.paused: self.canvasWidget.setfmax(fmax) self.quadsItem.setData(x1, x2, y) # TODO : # - Fix peaks loss when resizing # - optimize if further needed def draw(self): if self.needtransform: self.verticalScaleDivision.setLength(self.canvasWidget.height()) self.verticalScaleTransform.setLength(self.canvasWidget.height()) startBorder, endBorder = self.verticalScale.spacingBorders() self.verticalScaleTransform.setBorders(startBorder, endBorder) self.verticalScale.update() self.horizontalScaleDivision.setLength(self.canvasWidget.width()) self.horizontalScaleTransform.setLength(self.canvasWidget.width()) startBorder, endBorder = self.horizontalScale.spacingBorders() self.horizontalScaleTransform.setBorders(startBorder, endBorder) self.horizontalScale.update() self.canvasWidget.setGrid(array(self.horizontalScaleDivision.majorTicks()), array(self.horizontalScaleDivision.minorTicks()), array(self.verticalScaleDivision.majorTicks()), array(self.verticalScaleDivision.minorTicks()) ) self.quadsItem.transformUpdate() self.needtransform = False def pause(self): self.paused = True self.canvasWidget.pause() def restart(self): self.paused = False self.canvasWidget.restart() # redraw when the widget is resized to update coordinates transformations def resizeEvent(self, event): self.needtransform = True self.draw()
class ImagePlot(QtWidgets.QWidget): def __init__(self, parent): super(ImagePlot, self).__init__(parent) self.verticalScaleDivision = ScaleDivision(20, 20000, 100) self.verticalScaleTransform = CoordinateTransform(20, 20000, 100, 0, 0) self.verticalScale = VerticalScaleWidget(self, self.verticalScaleDivision, self.verticalScaleTransform) self.verticalScale.setTitle("Frequency (Hz)") self.verticalScale.scaleBar.setTickFormatter(tickFormatter) self.horizontalScaleDivision = ScaleDivision(0, 10, 100) self.horizontalScaleTransform = CoordinateTransform(0, 10, 100, 0, 0) self.horizontalScale = HorizontalScaleWidget(self, self.horizontalScaleDivision, self.horizontalScaleTransform) self.horizontalScale.setTitle("Time (s)") self.colorScaleDivision = ScaleDivision(-140, 0, 100) self.colorScaleTransform = CoordinateTransform(-140, 0, 100, 0, 0) self.colorScale = ColorScaleWidget(self, self.colorScaleDivision, self.colorScaleTransform) self.colorScale.setTitle("PSD (dB A)") self.canvasWidget = CanvasWidget(self, self.verticalScaleTransform, self.horizontalScaleTransform) self.canvasWidget.setTrackerFormatter(lambda x, y: "%.2f s, %d Hz" % (x, y)) plotLayout = QtWidgets.QGridLayout() plotLayout.setSpacing(0) plotLayout.setContentsMargins(0, 0, 0, 0) plotLayout.addWidget(self.verticalScale, 0, 0) plotLayout.addWidget(self.canvasWidget, 0, 1) plotLayout.addWidget(self.colorScale, 0, 2) plotLayout.addWidget(self.horizontalScale, 1, 1) self.setLayout(plotLayout) self.needfullreplot = False # attach a plot image self.plotImage = PlotImage() self.canvasWidget.attach(self.plotImage) self.setlinfreqscale() self.setspecrange(-140., 0.) # need to replot here for the size Hints to be computed correctly (depending on axis scales...) self.update() def addData(self, freq, xyzs, last_data_time): self.plotImage.addData(freq, xyzs, self.logfreqscale, last_data_time) def draw(self): if self.needfullreplot: self.needfullreplot = False self.verticalScaleDivision.setLength(self.canvasWidget.height()) self.verticalScaleTransform.setLength(self.canvasWidget.height()) startBorder, endBorder = self.verticalScale.spacingBorders() self.verticalScaleTransform.setBorders(startBorder, endBorder) self.verticalScale.update() self.horizontalScaleDivision.setLength(self.canvasWidget.width()) self.horizontalScaleTransform.setLength(self.canvasWidget.width()) startBorder, endBorder = self.horizontalScale.spacingBorders() self.horizontalScaleTransform.setBorders(startBorder, endBorder) self.horizontalScale.update() self.colorScaleDivision.setLength(self.canvasWidget.height()) self.colorScaleTransform.setLength(self.canvasWidget.height()) startBorder, endBorder = self.colorScale.spacingBorders() self.colorScaleTransform.setBorders(startBorder, endBorder) self.colorScale.update() self.canvasWidget.update() # redraw when the widget is resized to update coordinates transformations def resizeEvent(self, event): self.needfullreplot = True self.draw() def pause(self): self.plotImage.pause() def restart(self): self.plotImage.restart() def setlinfreqscale(self): self.plotImage.erase() self.logfreqscale = 0 self.plotImage.setlogfreqscale(False) self.verticalScaleTransform.setLinear() self.verticalScaleDivision.setLinear() # notify that sizeHint has changed (this should be done with a signal emitted from the scale division to the scale bar) self.verticalScale.scaleBar.updateGeometry() self.needfullreplot = True self.update() def setlog10freqscale(self): self.plotImage.erase() self.logfreqscale = 1 self.plotImage.setlogfreqscale(True) self.verticalScaleTransform.setLogarithmic() self.verticalScaleDivision.setLogarithmic() # notify that sizeHint has changed (this should be done with a signal emitted from the scale division to the scale bar) self.verticalScale.scaleBar.updateGeometry() self.needfullreplot = True self.update() def settimerange(self, timerange_seconds, dT_seconds): self.plotImage.settimerange(timerange_seconds, dT_seconds) self.horizontalScaleTransform.setRange(0, timerange_seconds) self.horizontalScaleDivision.setRange(0, timerange_seconds) # notify that sizeHint has changed (this should be done with a signal emitted from the scale division to the scale bar) self.horizontalScale.scaleBar.updateGeometry() self.needfullreplot = True self.update() def set_sfft_rate(self, rate_frac): self.plotImage.set_sfft_rate(rate_frac) def setfreqrange(self, minfreq, maxfreq): self.plotImage.setfreqrange(minfreq, maxfreq) self.verticalScaleTransform.setRange(minfreq, maxfreq) self.verticalScaleDivision.setRange(minfreq, maxfreq) # notify that sizeHint has changed (this should be done with a signal emitted from the scale division to the scale bar) self.verticalScale.scaleBar.updateGeometry() self.needfullreplot = True self.update() def setspecrange(self, spec_min, spec_max): self.colorScaleTransform.setRange(spec_min, spec_max) self.colorScaleDivision.setRange(spec_min, spec_max) # notify that sizeHint has changed (this should be done with a signal emitted from the scale division to the scale bar) self.colorScale.scaleBar.updateGeometry() self.needfullreplot = True self.update() def setweighting(self, weighting): if weighting is 0: title = "PSD (dB)" elif weighting is 1: title = "PSD (dB A)" elif weighting is 2: title = "PSD (dB B)" else: title = "PSD (dB C)" self.colorScale.setTitle(title)
class SpectrumPlotWidget(QtWidgets.QWidget): def __init__(self, parent, logger=None): super(SpectrumPlotWidget, self).__init__(parent) self.x1 = array([0.1, 0.5, 1.]) self.x2 = array([0.5, 1., 2.]) self.needtransform = False self.paused = False self.peaks_enabled = True self.peak = zeros((3, )) self.peak_int = zeros((3, )) self.peak_decay = ones((3, )) * PEAK_DECAY_RATE self.verticalScaleDivision = ScaleDivision(0, 1, 100) self.verticalScaleTransform = CoordinateTransform(0, 1, 100, 0, 0) self.verticalScale = VerticalScaleWidget(self, self.verticalScaleDivision, self.verticalScaleTransform) self.verticalScale.setTitle("PSD (dB)") self.horizontalScaleDivision = ScaleDivision(0, 22000, 100) self.horizontalScaleTransform = CoordinateTransform( 0, 22000, 100, 0, 0) self.horizontalScale = HorizontalScaleWidget( self, self.horizontalScaleDivision, self.horizontalScaleTransform) self.horizontalScale.setTitle("Frequency (Hz)") self.canvasWidget = GlCanvasWidget(self, self.verticalScaleTransform, self.horizontalScaleTransform) self.canvasWidget.setTrackerFormatter(lambda x, y: "%d Hz, %.1f dB" % (x, y)) self.canvasWidget.resized.connect(self.canvasResized) r_peak = lambda p: 1. + 0. * p g_peak = lambda p: 1. - p b_peak = lambda p: 1. - p self.peakQuadsItem = QuadsItem(r_peak, g_peak, b_peak) self.canvasWidget.attach(self.peakQuadsItem) r_signal = lambda p: 0. * p g_signal = lambda p: 0.3 + 0.5 * p b_signal = lambda p: 0. * p self.quadsItem = QuadsItem(r_signal, g_signal, b_signal) self.canvasWidget.attach(self.quadsItem) plotLayout = QtWidgets.QGridLayout() plotLayout.setSpacing(0) plotLayout.setContentsMargins(0, 0, 0, 0) plotLayout.addWidget(self.verticalScale, 0, 0) plotLayout.addWidget(self.canvasWidget, 0, 1) plotLayout.addWidget(self.horizontalScale, 1, 1) self.setLayout(plotLayout) def setlinfreqscale(self): self.horizontalScaleTransform.setLinear() self.horizontalScaleDivision.setLinear() self.needtransform = True self.draw() def setlogfreqscale(self): self.horizontalScaleTransform.setLogarithmic() self.horizontalScaleDivision.setLogarithmic() self.needtransform = True self.draw() def setfreqrange(self, minfreq, maxfreq): self.xmin = minfreq self.xmax = maxfreq self.horizontalScaleTransform.setRange(minfreq, maxfreq) self.horizontalScaleDivision.setRange(minfreq, maxfreq) # notify that sizeHint has changed (this should be done with a signal emitted from the scale division to the scale bar) self.horizontalScale.scaleBar.updateGeometry() self.needtransform = True self.draw() def setspecrange(self, spec_min, spec_max): if spec_min > spec_max: spec_min, spec_max = spec_max, spec_min self.verticalScaleTransform.setRange(spec_min, spec_max) self.verticalScaleDivision.setRange(spec_min, spec_max) # notify that sizeHint has changed (this should be done with a signal emitted from the scale division to the scale bar) self.verticalScale.scaleBar.updateGeometry() self.needtransform = True self.draw() def setweighting(self, weighting): if weighting is 0: title = "PSD (dB)" elif weighting is 1: title = "PSD (dB A)" elif weighting is 2: title = "PSD (dB B)" else: title = "PSD (dB C)" self.verticalScale.setTitle(title) self.needtransform = True self.draw() def setShowFreqLabel(self, showFreqLabel): self.canvasWidget.setShowFreqLabel(showFreqLabel) def set_peaks_enabled(self, enabled): self.peaks_enabled = enabled self.canvasWidget.detachAll() if enabled: self.canvasWidget.attach(self.peakQuadsItem) self.canvasWidget.attach(self.quadsItem) def set_baseline_displayUnits(self, baseline): self.quadsItem.set_baseline_displayUnits(baseline) def set_baseline_dataUnits(self, baseline): self.quadsItem.set_baseline_dataUnits(baseline) def setdata(self, x, y, fmax): x1 = zeros(x.shape) x2 = zeros(x.shape) x1[0] = 1e-10 x1[1:] = (x[1:] + x[:-1]) / 2. x2[:-1] = x1[1:] x2[-1] = float(SAMPLING_RATE / 2) if len(x1) != len(self.x1): self.needtransform = True self.x1 = x1 self.x2 = x2 if not self.paused: self.canvasWidget.setfmax(fmax) M = max(y) m = self.verticalScaleTransform.coord_min y_int = (y - m) / (np.abs(M - m) + 1e-3) self.quadsItem.setData(x1, x2, y, y_int) if self.peaks_enabled: self.compute_peaks(y) self.peakQuadsItem.setData(x1, x2, self.peak, self.peak_int) def draw(self): if self.needtransform: self.verticalScaleDivision.setLength(self.canvasWidget.height()) self.verticalScaleTransform.setLength(self.canvasWidget.height()) startBorder, endBorder = self.verticalScale.spacingBorders() self.verticalScaleTransform.setBorders(startBorder, endBorder) self.verticalScale.update() self.horizontalScaleDivision.setLength(self.canvasWidget.width()) self.horizontalScaleTransform.setLength(self.canvasWidget.width()) startBorder, endBorder = self.horizontalScale.spacingBorders() self.horizontalScaleTransform.setBorders(startBorder, endBorder) self.horizontalScale.update() self.canvasWidget.setGrid( array(self.horizontalScaleDivision.majorTicks()), array(self.horizontalScaleDivision.minorTicks()), array(self.verticalScaleDivision.majorTicks()), array(self.verticalScaleDivision.minorTicks())) self.quadsItem.transformUpdate() self.peakQuadsItem.transformUpdate() self.needtransform = False def pause(self): self.paused = True self.canvasWidget.pause() def restart(self): self.paused = False self.canvasWidget.restart() # redraw when the widget is resized to update coordinates transformations # QOpenGlWidget does not like that we override resizeEvent def canvasResized(self, canvasWidth, canvasHeight): self.needtransform = True self.draw() def canvasUpdate(self): self.canvasWidget.update() def compute_peaks(self, y): if len(self.peak) != len(y): y_ones = ones(y.shape) self.peak = y_ones * (-500.) self.peak_int = zeros(y.shape) self.peak_decay = y_ones * 20. * log10(PEAK_DECAY_RATE) * 5000 mask1 = (self.peak < y) mask2 = (-mask1) mask2_a = mask2 * (self.peak_int < 0.2) mask2_b = mask2 * (self.peak_int >= 0.2) self.peak[mask1] = y[mask1] self.peak[mask2_a] = self.peak[mask2_a] + self.peak_decay[mask2_a] self.peak_decay[mask1] = 20. * log10(PEAK_DECAY_RATE) * 5000 self.peak_decay[mask2_a] += 20. * log10(PEAK_DECAY_RATE) * 5000 self.peak_int[mask1] = 1. self.peak_int[mask2_b] *= 0.975
class TimePlot(QtWidgets.QWidget): def __init__(self, parent, logger): super(TimePlot, self).__init__() # store the _logger instance self.logger = logger self.verticalScaleDivision = ScaleDivision(-1, 1, 100) self.verticalScaleTransform = CoordinateTransform(-1, 1, 100, 0, 0) self.verticalScale = VerticalScaleWidget(self, self.verticalScaleDivision, self.verticalScaleTransform) self.verticalScale.setTitle("Signal") self.horizontalScaleDivision = ScaleDivision(-1, 1, 100) self.horizontalScaleTransform = CoordinateTransform(-1, 1, 100, 0, 0) self.horizontalScale = HorizontalScaleWidget(self, self.horizontalScaleDivision, self.horizontalScaleTransform) self.horizontalScale.setTitle("Time (ms)") self.canvasWidget = GlCanvasWidget(self, self.verticalScaleTransform, self.horizontalScaleTransform) self.canvasWidget.setTrackerFormatter(lambda x, y: "%.3g ms, %.3g" % (x, y)) self.legendWidget = LegendWidget(self, self.canvasWidget) plotLayout = QtWidgets.QGridLayout() plotLayout.setSpacing(0) plotLayout.setContentsMargins(0, 0, 0, 0) plotLayout.addWidget(self.verticalScale, 0, 0) plotLayout.addWidget(self.canvasWidget, 0, 1) plotLayout.addWidget(self.horizontalScale, 1, 1) plotLayout.addWidget(self.legendWidget, 0, 2) self.setLayout(plotLayout) self.legendWidget.hide() self.needfullreplot = False self.curve = CurveItem() self.curve.setColor(QtGui.QColor(Qt.Qt.red)) # gives a title to the curve for the legend self.curve.setTitle("Ch1") self.canvasWidget.attach(self.curve) self.curve2 = CurveItem() self.curve2.setColor(QtGui.QColor(Qt.Qt.blue)) # gives a title to the curve for the legend self.curve2.setTitle("Ch2") # self.curve2 will be attached when needed # need to replot here for the size Hints to be computed correctly (depending on axis scales...) self.update() self.xmin = 0. self.xmax = 1. self.canvas_width = 0 self.dual_channel = False self.paused = False self.canvasWidget.resized.connect(self.canvasResized) def setdata(self, x, y): if self.canvas_width != self.canvasWidget.width(): self.logger.push("timeplot : changed canvas width") self.canvas_width = self.canvasWidget.width() self.update_xscale() if self.dual_channel: self.dual_channel = False self.canvasWidget.detach(self.curve2) # disable the legend, useless when one channel is active self.legendWidget.hide() # the canvas reisze event will trigger a full replot if self.xmax != x[-1]: self.logger.push("timeplot : changing x max") self.xmax = x[-1] self.settimerange(self.xmin, self.xmax) self.update_xscale() self.needfullreplot = True if self.xmin != x[0]: self.logger.push("timeplot : changing x min") self.xmin = x[0] self.settimerange(self.xmin, self.xmax) self.update_xscale() self.needfullreplot = True if not self.paused: y_interp = interp(self.xscaled, x, y) self.curve.setData(self.xscaled, y_interp) self.draw() def draw(self): if self.needfullreplot: self.needfullreplot = False self.verticalScaleDivision.setLength(self.canvasWidget.height()) self.verticalScaleTransform.setLength(self.canvasWidget.height()) startBorder, endBorder = self.verticalScale.spacingBorders() self.verticalScaleTransform.setBorders(startBorder, endBorder) self.verticalScale.update() self.horizontalScaleDivision.setLength(self.canvasWidget.width()) self.horizontalScaleTransform.setLength(self.canvasWidget.width()) startBorder, endBorder = self.horizontalScale.spacingBorders() self.horizontalScaleTransform.setBorders(startBorder, endBorder) self.horizontalScale.update() self.canvasWidget.setGrid(array(self.horizontalScaleDivision.majorTicks()), array(self.horizontalScaleDivision.minorTicks()), array(self.verticalScaleDivision.majorTicks()), array(self.verticalScaleDivision.minorTicks())) def pause(self): self.paused = True self.canvasWidget.pause() def restart(self): self.paused = False self.canvasWidget.restart() # redraw when the time_plot is resized to update coordinates transformations # this is done instead of resizeEvent because the canvas can be resized independently of the whole plot (because the legend can disappear) def canvasResized(self, canvasWidth, canvasHeight): self.needfullreplot = True self.draw() def setdataTwoChannels(self, x, y, y2): if self.canvas_width != self.canvasWidget.width(): self.logger.push("timeplot : changed canvas width") self.canvas_width = self.canvasWidget.width() self.update_xscale() if not self.dual_channel: self.dual_channel = True self.canvasWidget.attach(self.curve2) # enable the legend to discrimate between the two channels self.legendWidget.show() # the canvas reisze event will trigger a full replot if self.xmax != x[-1]: self.logger.push("timeplot : changing x max") self.xmax = x[-1] self.settimerange(self.xmin, self.xmax) self.update_xscale() self.needfullreplot = True if self.xmin != x[0]: self.logger.push("timeplot : changing x min") self.xmin = x[0] self.settimerange(self.xmin, self.xmax) self.update_xscale() self.needfullreplot = True if not self.paused: # y_interp = interp(self.xscaled, x, y) # y_interp2 = interp(self.xscaled, x, y2) # ClassPlot.setdata(self, self.xscaled, y_interp) # self.curve2.setData(self.xscaled, y_interp2) self.curve.setData(x, y) self.curve2.setData(x, y2) self.draw() def update_xscale(self): self.xscaled = linspace(self.xmin, self.xmax, self.canvas_width) def settimerange(self, time_min, time_max): self.horizontalScaleTransform.setRange(time_min, time_max) self.horizontalScaleDivision.setRange(time_min, time_max) # notify that sizeHint has changed (this should be done with a signal emitted from the scale division to the scale bar) self.horizontalScale.scaleBar.updateGeometry() self.needfullreplot = True self.draw() def setverticalrange(self, v_min, v_max): self.verticalScaleTransform.setRange(v_min, v_max) self.verticalScaleDivision.setRange(v_min, v_max) # notify that sizeHint has changed (this should be done with a signal emitted from the scale division to the scale bar) self.verticalScale.scaleBar.updateGeometry() self.needfullreplot = True self.draw() def setverticaltitle(self, title): self.verticalScale.setTitle(title) def sethorizontaltitle(self, title): self.horizontalScale.setTitle(title) def setTrackerFormatter(self, formatter): self.canvasWidget.setTrackerFormatter(formatter)
class SpectrumPlotWidget(QtWidgets.QWidget): def __init__(self, parent, sharedGLWidget, logger=None): super(SpectrumPlotWidget, self).__init__() self.peaks_enabled = True self.peak = zeros((3, )) self.peak_int = zeros((3, )) self.peak_decay = ones((3, )) * PEAK_DECAY_RATE self.x1 = array([0.1, 0.5, 1.]) self.x2 = array([0.5, 1., 2.]) self.y = array([0., 0., 0.]) self.fmax = 1e3 self.transformed_x1 = self.x1 self.transformed_x2 = self.x2 self.baseline_transformed = False self.baseline = 0. self.needtransform = False self.verticalScaleDivision = ScaleDivision(0, 1, 100) self.verticalScaleTransform = CoordinateTransform(0, 1, 100, 0, 0) self.verticalScale = VerticalScaleWidget(self, self.verticalScaleDivision, self.verticalScaleTransform) self.verticalScale.setTitle("PSD (dB)") self.horizontalScaleDivision = ScaleDivision(0, 22000, 100) self.horizontalScaleTransform = CoordinateTransform( 0, 22000, 100, 0, 0) self.horizontalScale = HorizontalScaleWidget( self, self.horizontalScaleDivision, self.horizontalScaleTransform) self.horizontalScale.setTitle("Frequency (Hz)") self.canvasWidget = GlCanvasWidget(self, sharedGLWidget, self.verticalScaleTransform, self.horizontalScaleTransform) self.canvasWidget.setTrackerFormatter(lambda x, y: "%d Hz, %.1f dB" % (x, y)) self.quadsItem = QuadsItem() self.canvasWidget.attach(self.quadsItem) plotLayout = QtWidgets.QGridLayout() plotLayout.setSpacing(0) plotLayout.setContentsMargins(0, 0, 0, 0) plotLayout.addWidget(self.verticalScale, 0, 0) plotLayout.addWidget(self.canvasWidget, 0, 1) plotLayout.addWidget(self.horizontalScale, 1, 1) self.setLayout(plotLayout) def setlinfreqscale(self): self.logx = False self.horizontalScaleTransform.setLinear() self.horizontalScaleDivision.setLinear() self.needtransform = True self.draw() def setlogfreqscale(self): self.logx = True self.horizontalScaleTransform.setLogarithmic() self.horizontalScaleDivision.setLogarithmic() self.needtransform = True self.draw() def setfreqrange(self, minfreq, maxfreq): self.xmin = minfreq self.xmax = maxfreq self.horizontalScaleTransform.setRange(minfreq, maxfreq) self.horizontalScaleDivision.setRange(minfreq, maxfreq) # notify that sizeHint has changed (this should be done with a signal emitted from the scale division to the scale bar) self.horizontalScale.scaleBar.updateGeometry() self.needtransform = True self.draw() def setspecrange(self, spec_min, spec_max): self.verticalScaleTransform.setRange(spec_min, spec_max) self.verticalScaleDivision.setRange(spec_min, spec_max) # notify that sizeHint has changed (this should be done with a signal emitted from the scale division to the scale bar) self.verticalScale.scaleBar.updateGeometry() self.needtransform = True self.draw() def setweighting(self, weighting): if weighting is 0: title = "PSD (dB)" elif weighting is 1: title = "PSD (dB A)" elif weighting is 2: title = "PSD (dB B)" else: title = "PSD (dB C)" self.verticalScale.setTitle(title) self.needtransform = True self.draw() def setShowFreqLabel(self, showFreqLabel): self.canvasWidget.setShowFreqLabel(showFreqLabel) def set_peaks_enabled(self, enabled): self.peaks_enabled = enabled def set_baseline_displayUnits(self, baseline): self.baseline_transformed = False self.baseline = baseline def set_baseline_dataUnits(self, baseline): self.baseline_transformed = True self.baseline = baseline def setdata(self, x, y, fmax): x1 = zeros(x.shape) x2 = zeros(x.shape) x1[0] = 1e-10 x1[1:] = (x[1:] + x[:-1]) / 2. x2[:-1] = x1[1:] x2[-1] = float(SAMPLING_RATE / 2) if len(x1) != len(self.x1): self.needtransform = True # save data for resizing self.x1 = x1 self.x2 = x2 # save data for resizing self.y = y self.fmax = fmax self.draw() # TODO : # - Fix peaks loss when resizing # - optimize if further needed def pre_tree_rebin(self, x1, x2): if len(x2) == 0: # enf of recursion ! return x1, x2, 0 n0 = max(where(x2 - x1 >= 0.5)[0]) # leave untouched the frequency bins that span more than half a pixel # and first make sure that what will be left can be decimated by two rest = len(x2) - n0 - ((len(x2) - n0) // 2) * 2 n0 += rest x1_0 = x1[:n0] x2_0 = x2[:n0] # decimate the rest x1_2 = x1[n0::2] x2_2 = x2[n0 + 1::2] # recursive !! x1_2, x2_2, n2 = self.pre_tree_rebin(x1_2, x2_2) if n2 == 0.: n = [n0] else: n = [n0] + [i * 2 + n0 for i in n2] x1 = hstack((x1_0, x1_2)) x2 = hstack((x2_0, x2_2)) return x1, x2, n def tree_rebin(self, y, ns, N): y2 = zeros(N) n = 0 for i in range(len(ns) - 1): y3 = y[ns[i]:ns[i + 1]] d = 2**i l = len(y3) / d y3.shape = (l, d) # Note: the FFT spectrum is mostly used to identify frequency content # ans _peaks_ are particularly interesting (e.g. feedback frequencies) # so we display the _max_ instead of the mean of each bin #y3 = mean(y3, axis=1) #y3 = (y3[::2] + y3[1::2])*0.5 y3 = np.max(y3, axis=1) y2[n:n + len(y3)] = y3 n += l return y2 def draw(self): if self.needtransform: self.verticalScaleDivision.setLength(self.canvasWidget.height()) self.verticalScaleTransform.setLength(self.canvasWidget.height()) startBorder, endBorder = self.verticalScale.spacingBorders() self.verticalScaleTransform.setBorders(startBorder, endBorder) self.verticalScale.update() self.horizontalScaleDivision.setLength(self.canvasWidget.width()) self.horizontalScaleTransform.setLength(self.canvasWidget.width()) startBorder, endBorder = self.horizontalScale.spacingBorders() self.horizontalScaleTransform.setBorders(startBorder, endBorder) self.horizontalScale.update() # transform the coordinates only when needed x1 = self.horizontalScaleTransform.toScreen(self.x1) x2 = self.horizontalScaleTransform.toScreen(self.x2) if self.logx: self.transformed_x1, self.transformed_x2, n = self.pre_tree_rebin( x1, x2) self.n = [0] + n self.N = 0 for i in range(len(self.n) - 1): self.N += (self.n[i + 1] - self.n[i]) / 2**i else: self.transformed_x1 = x1 self.transformed_x2 = x2 self.canvasWidget.setGrid( array(self.horizontalScaleDivision.majorTicks()), array(self.horizontalScaleDivision.minorTicks()), array(self.verticalScaleDivision.majorTicks()), array(self.verticalScaleDivision.minorTicks())) self.needtransform = False # for easier reading x1 = self.transformed_x1 x2 = self.transformed_x2 if self.logx: y = self.tree_rebin(self.y, self.n, self.N) else: n = floor(1. / (x2[2] - x1[1])) if n > 0: new_len = len(self.y) // n rest = len(self.y) - new_len * n new_y = self.y[:-rest] new_y.shape = (new_len, n) y = mean(new_y, axis=1) x1 = x1[:-rest:n] x2 = x2[n::n] else: y = self.y if self.peaks_enabled: self.compute_peaks(y) transformed_y = self.verticalScaleTransform.toScreen(y) Ones = ones(x1.shape) Ones_shaded = Ones #.copy() # FIXME : the following would give a satisfying result if the # bins were one pixel wide at minimum => Need to to a rounding # to pixels #w = x2 - x1 #i = where(w<1.)[0] #if len(i)>0: # Ones_shaded[:i[0]:2] = 1.2 if self.peaks_enabled: transformed_peak = self.verticalScaleTransform.toScreen(self.peak) n = x1.size # FIXME should be done conditionally to need_transform x1_with_peaks = zeros((2 * n)) x2_with_peaks = zeros((2 * n)) y_with_peaks = zeros((2 * n)) r_with_peaks = zeros((2 * n)) g_with_peaks = zeros((2 * n)) b_with_peaks = zeros((2 * n)) x1_with_peaks[:n] = x1 x1_with_peaks[n:] = x1 x2_with_peaks[:n] = x2 x2_with_peaks[n:] = x2 y_with_peaks[:n] = transformed_peak y_with_peaks[n:] = transformed_y r_with_peaks[:n] = 1. * Ones r_with_peaks[n:] = 0. * Ones g_with_peaks[:n] = 1. - self.peak_int g_with_peaks[n:] = 0.5 * Ones_shaded b_with_peaks[:n] = 1. - self.peak_int b_with_peaks[n:] = 0. * Ones else: x1_with_peaks = x1 x2_with_peaks = x2 y_with_peaks = transformed_y r_with_peaks = 0. * Ones g_with_peaks = 0.5 * Ones_shaded b_with_peaks = 0. * Ones if self.baseline_transformed: # used for dual channel response measurement baseline = self.verticalScaleTransform.toScreen(self.baseline) else: # used for single channel analysis baseline = self.baseline xmax = self.horizontalScaleTransform.toScreen(self.fmax) self.canvasWidget.setfmax(xmax, self.fmax) self.quadsItem.setData(x1_with_peaks, y_with_peaks, x2_with_peaks - x1_with_peaks, baseline, r_with_peaks, g_with_peaks, b_with_peaks) self.canvasWidget.update() # redraw when the widget is resized to update coordinates transformations def resizeEvent(self, event): self.needtransform = True self.draw() def compute_peaks(self, y): if len(self.peak) != len(y): y_ones = ones(y.shape) self.peak = y_ones * (-500.) self.peak_int = zeros(y.shape) self.peak_decay = y_ones * 20. * log10(PEAK_DECAY_RATE) * 5000 mask1 = (self.peak < y) mask2 = (-mask1) mask2_a = mask2 * (self.peak_int < 0.2) mask2_b = mask2 * (self.peak_int >= 0.2) self.peak[mask1] = y[mask1] self.peak[mask2_a] = self.peak[mask2_a] + self.peak_decay[mask2_a] self.peak_decay[mask1] = 20. * log10(PEAK_DECAY_RATE) * 5000 self.peak_decay[mask2_a] += 20. * log10(PEAK_DECAY_RATE) * 5000 self.peak_int[mask1] = 1. self.peak_int[mask2_b] *= 0.975
class HistPlot(QtWidgets.QWidget): def __init__(self, parent, logger): super(HistPlot, self).__init__() # store the _logger instance self.logger = logger self.verticalScaleDivision = ScaleDivision(-140, 0, 100) self.verticalScaleTransform = CoordinateTransform(-140, 0, 100, 0, 0) self.verticalScale = VerticalScaleWidget(self, self.verticalScaleDivision, self.verticalScaleTransform) self.verticalScale.setTitle("PSD (dB A)") self.horizontalScaleDivision = ScaleDivision(44, 22000, 100) self.horizontalScaleTransform = CoordinateTransform(44, 22000, 100, 0, 0) self.horizontalScale = HorizontalScaleWidget(self, self.horizontalScaleDivision, self.horizontalScaleTransform) self.horizontalScale.setTitle("Frequency (Hz)") self.canvasWidget = CanvasWidget(self, self.verticalScaleTransform, self.horizontalScaleTransform) self.canvasWidget.setTrackerFormatter(lambda x, y: "%d Hz, %.1f dB" % (x, y)) plot_layout = QtWidgets.QGridLayout() plot_layout.setSpacing(0) plot_layout.setContentsMargins(0, 0, 0, 0) plot_layout.addWidget(self.verticalScale, 0, 0) plot_layout.addWidget(self.canvasWidget, 0, 1) plot_layout.addWidget(self.horizontalScale, 1, 1) self.setLayout(plot_layout) self.needfullreplot = False self.horizontalScaleTransform.setLogarithmic() self.horizontalScaleDivision.setLogarithmic() # insert an additional plot item for the peak bar self.bar_peak = HistogramPeakBarItem() self.canvasWidget.attach(self.bar_peak) self.peak = zeros((1,)) self.peak_int = 0 self.peak_decay = PEAK_DECAY_RATE self.histogram = HistogramItem() self.histogram.set_color(Qt.Qt.darkGreen) self.canvasWidget.attach(self.histogram) # need to replot here for the size Hints to be computed correctly (depending on axis scales...) self.update() def setdata(self, fl, fh, fc, y): self.histogram.setData(fl, fh, fc, y) self.compute_peaks(y) self.bar_peak.setData(fl, fh, self.peak, self.peak_int, y) # only draw on demand # self.draw() def draw(self): if self.needfullreplot: self.needfullreplot = False self.verticalScaleDivision.setLength(self.canvasWidget.height()) self.verticalScaleTransform.setLength(self.canvasWidget.height()) start_border, end_border = self.verticalScale.spacingBorders() self.verticalScaleTransform.setBorders(start_border, end_border) self.verticalScale.update() self.horizontalScaleDivision.setLength(self.canvasWidget.width()) self.horizontalScaleTransform.setLength(self.canvasWidget.width()) start_border, end_border = self.horizontalScale.spacingBorders() self.horizontalScaleTransform.setBorders(start_border, end_border) self.horizontalScale.update() x_major_tick = self.horizontalScaleDivision.majorTicks() x_minor_tick = self.horizontalScaleDivision.minorTicks() y_major_tick = self.verticalScaleDivision.majorTicks() y_minor_tick = self.verticalScaleDivision.minorTicks() self.canvasWidget.setGrid(array(x_major_tick), array(x_minor_tick), array(y_major_tick), array(y_minor_tick)) self.canvasWidget.update() # redraw when the time_plot is resized to update coordinates transformations def resizeEvent(self, event): self.needfullreplot = True self.draw() def compute_peaks(self, y): if len(self.peak) != len(y): y_ones = ones(y.shape) self.peak = y_ones * (-500.) self.peak_int = zeros(y.shape) self.peak_decay = y_ones * 20. * log10(PEAK_DECAY_RATE) * 5000 mask1 = (self.peak < y) mask2 = (-mask1) mask2_a = mask2 * (self.peak_int < 0.2) mask2_b = mask2 * (self.peak_int >= 0.2) self.peak[mask1] = y[mask1] self.peak[mask2_a] = self.peak[mask2_a] + self.peak_decay[mask2_a] self.peak_decay[mask1] = 20. * log10(PEAK_DECAY_RATE) * 5000 self.peak_decay[mask2_a] += 20. * log10(PEAK_DECAY_RATE) * 5000 self.peak_int[mask1] = 1. self.peak_int[mask2_b] *= 0.975 def setspecrange(self, spec_min, spec_max): self.verticalScaleTransform.setRange(spec_min, spec_max) self.verticalScaleDivision.setRange(spec_min, spec_max) # notify that sizeHint has changed (this should be done with a signal emitted from the scale division to the scale bar) self.verticalScale.scaleBar.updateGeometry() self.needfullreplot = True self.update() def setweighting(self, weighting): if weighting is 0: title = "PSD (dB)" elif weighting is 1: title = "PSD (dB A)" elif weighting is 2: title = "PSD (dB B)" else: title = "PSD (dB C)" self.verticalScale.setTitle(title)
class ImagePlot(QtWidgets.QWidget): def __init__(self, parent, logger, audiobackend): super(ImagePlot, self).__init__(parent) self.verticalScaleDivision = ScaleDivision(20, 20000, 100) self.verticalScaleTransform = CoordinateTransform(20, 20000, 100, 0, 0) self.verticalScale = VerticalScaleWidget(self, self.verticalScaleDivision, self.verticalScaleTransform) self.verticalScale.setTitle("Frequency (Hz)") self.verticalScale.scaleBar.setTickFormatter(tickFormatter) self.horizontalScaleDivision = ScaleDivision(0, 10, 100) self.horizontalScaleTransform = CoordinateTransform(0, 10, 100, 0, 0) self.horizontalScale = HorizontalScaleWidget(self, self.horizontalScaleDivision, self.horizontalScaleTransform) self.horizontalScale.setTitle("Time (s)") self.colorScaleDivision = ScaleDivision(-140, 0, 100) self.colorScaleTransform = CoordinateTransform(-140, 0, 100, 0, 0) self.colorScale = ColorScaleWidget(self, self.colorScaleDivision, self.colorScaleTransform) self.colorScale.setTitle("PSD (dB A)") self.canvasWidget = CanvasWidget(self, self.verticalScaleTransform, self.horizontalScaleTransform) self.canvasWidget.setTrackerFormatter(lambda x, y: "%.2f s, %d Hz" % (x, y)) plotLayout = QtWidgets.QGridLayout() plotLayout.setSpacing(0) plotLayout.setContentsMargins(0, 0, 0, 0) plotLayout.addWidget(self.verticalScale, 0, 0) plotLayout.addWidget(self.canvasWidget, 0, 1) plotLayout.addWidget(self.colorScale, 0, 2) plotLayout.addWidget(self.horizontalScale, 1, 1) self.setLayout(plotLayout) self.needfullreplot = False # attach a plot image self.plotImage = PlotImage(logger, audiobackend) self.canvasWidget.attach(self.plotImage) self.setlinfreqscale() self.setspecrange(-140., 0.) # need to replot here for the size Hints to be computed correctly (depending on axis scales...) self.update() def addData(self, freq, xyzs, last_data_time): self.plotImage.addData(freq, xyzs, self.logfreqscale, last_data_time) def draw(self): if self.needfullreplot: self.needfullreplot = False self.verticalScaleDivision.setLength(self.canvasWidget.height()) self.verticalScaleTransform.setLength(self.canvasWidget.height()) startBorder, endBorder = self.verticalScale.spacingBorders() self.verticalScaleTransform.setBorders(startBorder, endBorder) self.verticalScale.update() self.horizontalScaleDivision.setLength(self.canvasWidget.width()) self.horizontalScaleTransform.setLength(self.canvasWidget.width()) startBorder, endBorder = self.horizontalScale.spacingBorders() self.horizontalScaleTransform.setBorders(startBorder, endBorder) self.horizontalScale.update() self.colorScaleDivision.setLength(self.canvasWidget.height()) self.colorScaleTransform.setLength(self.canvasWidget.height()) startBorder, endBorder = self.colorScale.spacingBorders() self.colorScaleTransform.setBorders(startBorder, endBorder) self.colorScale.update() self.canvasWidget.update() # redraw when the widget is resized to update coordinates transformations def resizeEvent(self, event): self.needfullreplot = True self.draw() def pause(self): self.plotImage.pause() def restart(self): self.plotImage.restart() def setlinfreqscale(self): self.plotImage.erase() self.logfreqscale = 0 self.plotImage.setlogfreqscale(False) self.verticalScaleTransform.setLinear() self.verticalScaleDivision.setLinear() # notify that sizeHint has changed (this should be done with a signal emitted from the scale division to the scale bar) self.verticalScale.scaleBar.updateGeometry() self.needfullreplot = True self.update() def setlog10freqscale(self): self.plotImage.erase() self.logfreqscale = 1 self.plotImage.setlogfreqscale(True) self.verticalScaleTransform.setLogarithmic() self.verticalScaleDivision.setLogarithmic() # notify that sizeHint has changed (this should be done with a signal emitted from the scale division to the scale bar) self.verticalScale.scaleBar.updateGeometry() self.needfullreplot = True self.update() def settimerange(self, timerange_seconds, dT_seconds): self.plotImage.settimerange(timerange_seconds, dT_seconds) self.horizontalScaleTransform.setRange(0, timerange_seconds) self.horizontalScaleDivision.setRange(0, timerange_seconds) # notify that sizeHint has changed (this should be done with a signal emitted from the scale division to the scale bar) self.horizontalScale.scaleBar.updateGeometry() self.needfullreplot = True self.update() def set_sfft_rate(self, rate_frac): self.plotImage.set_sfft_rate(rate_frac) def setfreqrange(self, minfreq, maxfreq): self.plotImage.setfreqrange(minfreq, maxfreq) self.verticalScaleTransform.setRange(minfreq, maxfreq) self.verticalScaleDivision.setRange(minfreq, maxfreq) # notify that sizeHint has changed (this should be done with a signal emitted from the scale division to the scale bar) self.verticalScale.scaleBar.updateGeometry() self.needfullreplot = True self.update() def setspecrange(self, spec_min, spec_max): self.colorScaleTransform.setRange(spec_min, spec_max) self.colorScaleDivision.setRange(spec_min, spec_max) # notify that sizeHint has changed (this should be done with a signal emitted from the scale division to the scale bar) self.colorScale.scaleBar.updateGeometry() self.needfullreplot = True self.update() def setweighting(self, weighting): if weighting is 0: title = "PSD (dB)" elif weighting is 1: title = "PSD (dB A)" elif weighting is 2: title = "PSD (dB B)" else: title = "PSD (dB C)" self.colorScale.setTitle(title)
class SpectrumPlotWidget(QtWidgets.QWidget): def __init__(self, parent, logger=None): super(SpectrumPlotWidget, self).__init__(parent) self.x1 = array([0.1, 0.5, 1.]) self.x2 = array([0.5, 1., 2.]) self.needtransform = False self.paused = False self.peaks_enabled = True self.peak = zeros((3,)) self.peak_int = zeros((3,)) self.peak_decay = ones((3,)) * PEAK_DECAY_RATE self.verticalScaleDivision = ScaleDivision(0, 1, 100) self.verticalScaleTransform = CoordinateTransform(0, 1, 100, 0, 0) self.verticalScale = VerticalScaleWidget(self, self.verticalScaleDivision, self.verticalScaleTransform) self.verticalScale.setTitle("PSD (dB)") self.horizontalScaleDivision = ScaleDivision(0, 22000, 100) self.horizontalScaleTransform = CoordinateTransform(0, 22000, 100, 0, 0) self.horizontalScale = HorizontalScaleWidget(self, self.horizontalScaleDivision, self.horizontalScaleTransform) self.horizontalScale.setTitle("Frequency (Hz)") self.canvasWidget = GlCanvasWidget(self, self.verticalScaleTransform, self.horizontalScaleTransform) self.canvasWidget.setTrackerFormatter(lambda x, y: "%d Hz, %.1f dB" % (x, y)) self.canvasWidget.resized.connect(self.canvasResized) r_peak = lambda p: 1. + 0.*p g_peak = lambda p: 1. - p b_peak = lambda p: 1. - p self.peakQuadsItem = QuadsItem(r_peak, g_peak, b_peak) self.canvasWidget.attach(self.peakQuadsItem) r_signal = lambda p: 0.*p g_signal = lambda p: 0.3 + 0.5*p b_signal = lambda p: 0.*p self.quadsItem = QuadsItem(r_signal, g_signal, b_signal) self.canvasWidget.attach(self.quadsItem) plotLayout = QtWidgets.QGridLayout() plotLayout.setSpacing(0) plotLayout.setContentsMargins(0, 0, 0, 0) plotLayout.addWidget(self.verticalScale, 0, 0) plotLayout.addWidget(self.canvasWidget, 0, 1) plotLayout.addWidget(self.horizontalScale, 1, 1) self.setLayout(plotLayout) def setlinfreqscale(self): self.horizontalScaleTransform.setLinear() self.horizontalScaleDivision.setLinear() self.needtransform = True self.draw() def setlogfreqscale(self): self.horizontalScaleTransform.setLogarithmic() self.horizontalScaleDivision.setLogarithmic() self.needtransform = True self.draw() def setfreqrange(self, minfreq, maxfreq): self.xmin = minfreq self.xmax = maxfreq self.horizontalScaleTransform.setRange(minfreq, maxfreq) self.horizontalScaleDivision.setRange(minfreq, maxfreq) # notify that sizeHint has changed (this should be done with a # signal emitted from the scale division to the scale bar) self.horizontalScale.scaleBar.updateGeometry() self.needtransform = True self.draw() def setspecrange(self, spec_min, spec_max): if spec_min > spec_max: spec_min, spec_max = spec_max, spec_min self.verticalScaleTransform.setRange(spec_min, spec_max) self.verticalScaleDivision.setRange(spec_min, spec_max) # notify that sizeHint has changed (this should be done with a # signal emitted from the scale division to the scale bar) self.verticalScale.scaleBar.updateGeometry() self.needtransform = True self.draw() def setweighting(self, weighting): if weighting is 0: title = "PSD (dB)" elif weighting is 1: title = "PSD (dB A)" elif weighting is 2: title = "PSD (dB B)" else: title = "PSD (dB C)" self.verticalScale.setTitle(title) self.needtransform = True self.draw() def setShowFreqLabel(self, showFreqLabel): self.canvasWidget.setShowFreqLabel(showFreqLabel) def set_peaks_enabled(self, enabled): self.peaks_enabled = enabled self.canvasWidget.detachAll() if enabled: self.canvasWidget.attach(self.peakQuadsItem) self.canvasWidget.attach(self.quadsItem) def set_baseline_displayUnits(self, baseline): self.quadsItem.set_baseline_displayUnits(baseline) def set_baseline_dataUnits(self, baseline): self.quadsItem.set_baseline_dataUnits(baseline) def setdata(self, x, y, fmax): x1 = zeros(x.shape) x2 = zeros(x.shape) x1[0] = 1e-10 x1[1:] = (x[1:] + x[:-1]) / 2. x2[:-1] = x1[1:] x2[-1] = float(SAMPLING_RATE / 2) if len(x1) != len(self.x1): self.needtransform = True self.x1 = x1 self.x2 = x2 if not self.paused: self.canvasWidget.setfmax(fmax) M = max(y) m = self.verticalScaleTransform.coord_min y_int = (y-m)/(np.abs(M-m)+1e-3) self.quadsItem.setData(x1, x2, y, y_int) if self.peaks_enabled: self.compute_peaks(y) self.peakQuadsItem.setData(x1, x2, self.peak, self.peak_int) def draw(self): if self.needtransform: self.verticalScaleDivision.setLength(self.canvasWidget.height()) self.verticalScaleTransform.setLength(self.canvasWidget.height()) startBorder, endBorder = self.verticalScale.spacingBorders() self.verticalScaleTransform.setBorders(startBorder, endBorder) self.verticalScale.update() self.horizontalScaleDivision.setLength(self.canvasWidget.width()) self.horizontalScaleTransform.setLength(self.canvasWidget.width()) startBorder, endBorder = self.horizontalScale.spacingBorders() self.horizontalScaleTransform.setBorders(startBorder, endBorder) self.horizontalScale.update() self.canvasWidget.setGrid(array(self.horizontalScaleDivision.majorTicks()), array(self.horizontalScaleDivision.minorTicks()), array(self.verticalScaleDivision.majorTicks()), array(self.verticalScaleDivision.minorTicks())) self.quadsItem.transformUpdate() self.peakQuadsItem.transformUpdate() self.needtransform = False def pause(self): self.paused = True self.canvasWidget.pause() def restart(self): self.paused = False self.canvasWidget.restart() # redraw when the time_plot is resized to update coordinates transformations # QOpenGlWidget does not like that we override resizeEvent def canvasResized(self, canvasWidth, canvasHeight): self.needtransform = True self.draw() def canvasUpdate(self): self.canvasWidget.update() def compute_peaks(self, y): if len(self.peak) != len(y): y_ones = ones(y.shape) self.peak = y_ones * (-500.) self.peak_int = zeros(y.shape) self.peak_decay = y_ones * 20. * log10(PEAK_DECAY_RATE) * 5000 mask1 = (self.peak < y) mask2 = (-mask1) mask2_a = mask2 * (abs(self.peak_int) < 0.2) mask2_b = mask2 * (abs(self.peak_int) >= 0.2) self.peak[mask1] = y[mask1] self.peak[mask2_a] = self.peak[mask2_a] + self.peak_decay[mask2_a] self.peak_decay[mask1] = 20. * log10(PEAK_DECAY_RATE) * 5000 self.peak_decay[mask2_a] += 20. * log10(PEAK_DECAY_RATE) * 5000 self.peak_int[mask1] = 1. self.peak_int[mask2_b] *= 0.975