class GraphicWidgetLogic(Ui_GraphicWindow): def __init__(self, GraphicWidgetLogic): Ui_GraphicWindow.__init__(self) self.FS = 48000 self.x = 0 self.y = 0 self.freq = 0 self.amp = 0 self.flag = "PURE" # se inicializa el sistema de visualizado avanzado def initializeBinds(self): # añadir plots self.pushButtonPlay.clicked.connect(self.play) self.pushButton.clicked.connect(self.showFFT) setConfigOption('leftButtonPan', False) self.x = 0 self.y = 0 self.zoomedPlot = self.graphicsView.addPlot(row=1, col=0) self.fullPlot = self.graphicsView.addPlot(row=2, col=0) self.graphicsView.setBackground(background="w") # self.zoomedPlot.vb.setBackgroundColor("w") # self.fullPlot.vb.setBackgroundColor("w") self.penB = mkPen('b') self.penR = mkPen('r') self.region = LinearRegionItem() self.region.setZValue(10) self.vb = self.zoomedPlot.vb self.region.setRegion([1000, 2000]) self.fullPlot.addItem(self.region, ignoreBounds=True) # pg.dbg() self.zoomedPlot.setAutoVisible(y=True) self.vLine = InfiniteLine(angle=90, movable=False) self.hLine = InfiniteLine(angle=0, movable=False) self.zoomedPlot.addItem(self.vLine, ignoreBounds=True) self.zoomedPlot.addItem(self.hLine, ignoreBounds=True) # signal para capturar evento de raton # proxy = SignalProxy(self.zoomedPlot.scene().sigMouseMoved, rateLimit=60, slot=self.mouseMoved) self.zoomedPlot.scene().sigMouseMoved.connect(self.mouseMoved) self.region.sigRegionChanged.connect(self.update) self.zoomedPlot.sigRangeChanged.connect(self.updateRegion) def play(self): sd.play(self.y, self.FS, blocking=True) def showFFT(self): self.FFTwindow = QtWidgets.QWidget() self.ui = GraphicWidgetLogicSpectrumLogic(self) self.ui.setupUi(self.FFTwindow) self.ui.initializeBinds() self.ui.PlotFFT(self.x, self.y, self.flag, self.amp) self.FFTwindow.activateWindow() self.FFTwindow.show() self.FFTwindow.raise_() def updateRegion(self, window, viewRange): rgn = viewRange[0] self.region.setRegion(rgn) def update(self): self.region.setZValue(10) minX, maxX = self.region.getRegion() self.zoomedPlot.setXRange(minX, maxX, padding=0) def mouseMoved(self, evt): pos = Point(evt.x(), evt.y()) if self.zoomedPlot.sceneBoundingRect().contains(pos): mousePoint = self.vb.mapSceneToView(pos) index = float(evt.x()) # if index > 0 : # self.dataLabel.setText("<span style='font-size: 12pt'>x=%0.1f, <span style='color: red'>y1=%0.1f</span>, <span style='color: green'>y2=%0.1f</span>" % int(index), self.x[index], self.y[index]) self.vLine.setPos(mousePoint.x()) self.hLine.setPos(mousePoint.y()) def PlotSin(self, amp, freq, phase, flag="PURE"): print(self.flag) self.flag = flag self.amp = amp self.freq = freq self.x = arange(0, 1, 1 / self.FS) self.y = (amp / 10) * sin(2 * pi * freq * self.x + (phase * pi)) self.fullPlot.plot(self.x, self.y, pen=self.penR) self.zoomedPlot.plot(self.x, self.y, pen=self.penB) def plotSawtooth(self, amp, freq): self.amp = amp self.freq = freq self.Fs: int = 44100 self.x = arange(0, 1, 1 / self.Fs) self.y = (amp / 10) * sawtooth(2 * pi * freq * self.x) self.fullPlot.plot(self.x, self.y, pen=self.penR) self.zoomedPlot.plot(self.x, self.y, pen=self.penB) def plotSquare(self, amp, freq): self.amp = amp self.freq = freq self.Fs: int = 44100 self.x = arange(0, 1, 1 / self.Fs) self.y = (amp / 10) * square(2 * pi * freq * self.x) self.fullPlot.plot(self.x, self.y, pen=self.penR) self.zoomedPlot.plot(self.x, self.y, pen=self.penB) def plotGpulse(self, amp, freq): self.amp = amp self.freq = freq self.Fs: int = 44100 self.x = arange(0, 1, 1 / self.Fs) self.y = (amp / 10) * gausspulse((self.x, freq)) self.fullPlot.plot(self.x, self.y, pen=self.penR) self.zoomedPlot.plot(self.x, self.y, pen=self.penB) def plotHarmonic(self, x_array, y_array): self.x = x_array self.y = y_array self.fullPlot.plot(x_array, y_array, pen=self.penR) self.zoomedPlot.plot(x_array, y_array, pen=self.penB)
class FindPeak(QWidget): def __init__(self, parent, x_data, y_data): super(FindPeak, self).__init__() self.parent = parent self.x = x_data self.y = y_data self.lower = np.min(x_data) self.upper = np.max(x_data) self.range = self.upper - self.lower self.renderWindow() self.initPlotView() self.drawCurve() self.setUpProcessUI() self.bindEvents() self.integral(x_data, y_data, self.lower, self.upper) def bindEvents(self): self.bindBoundEvent() self.bindAlgorithmEvent() self.bindFindEvent() def bindBoundEvent(self): def leftBoundEvent(x): self.lower = x upper = self.upper self.plotRegion.setRegion([x, upper]) self.rightBound.setMinimum(x) self.peakCenter.setMinimum(x) self.peakCenter.setValue((x + upper) / 2) self.integral(self.x, self.y, x, upper) def rightBoundEvent(x): self.upper = x lower = self.lower self.plotRegion.setRegion([lower, x]) self.leftBound.setMaximum(x) self.peakCenter.setMaximum(x) self.peakCenter.setValue((x + lower) / 2) self.integral(self.x, self.y, lower, x) def regionChangeEvent(): lower, upper = self.plotRegion.getRegion() self.lower = lower self.upper = upper self.leftBound.setValue(lower) self.leftBound.setMaximum(upper) self.rightBound.setValue(upper) self.rightBound.setMinimum(lower) self.peakCenter.setMinimum(lower) self.peakCenter.setMaximum(upper) self.peakCenter.setValue((lower + upper) / 2) self.integral(self.x, self.y, lower, upper) self.leftBound.valueChanged.connect(leftBoundEvent) self.rightBound.valueChanged.connect(rightBoundEvent) self.plotRegion.sigRegionChanged.connect(regionChangeEvent) def bindAlgorithmEvent(self): def updateInput(a, b, c, d, e, f): self.peakWidth.setEnabled(a) self.detectDis.setEnabled(b) self.noisePrt.setEnabled(c) self.amplitude.setEnabled(d) self.threshold.setEnabled(e) self.findBtn.setEnabled(f) def changeAlgorithm(algorithm): if algorithm == "Extremum": updateInput(False, False, False, False, False, True) pass elif algorithm == "Matlab Like": updateInput(True, True, False, True, True, True) pass elif algorithm == "Gaussian": updateInput(False, False, False, False, False, False) pass elif algorithm == "Lorentzian": updateInput(False, False, False, False, False, False) pass elif algorithm == "Pseudo-Voigt": updateInput(False, False, False, False, False, False) pass elif algorithm == "Wavelet Transform": updateInput(True, True, True, False, False, False) pass self.algorithm.currentTextChanged.connect(changeAlgorithm) updateInput(False, False, False, False, False, True) def integral(self, x_data, y_data, lower, upper): idx = np.where((x_data >= lower) & (x_data <= upper)) x = x_data[idx] y = y_data[idx] self.integralArea.setValue(simps(y, x)) def bindFindEvent(self): x_data = self.x y_data = self.y def findPeak(): region = np.where((x_data >= self.lower) & (x_data <= self.upper)) sub_data = y_data[region] sub_region = x_data[region] algorithm = self.algorithm.currentText() shape = self.shape.currentText() if shape == "Peak": const = 1 else: const = -1 sub_data = sub_data * const if algorithm == "Extremum": peak = np.max(sub_data) idx = np.where(sub_data == peak) x = sub_region[idx][0] y = sub_data[idx][0] * const self.peakCenter.setValue(x) return self.renderPeakPoint([x, y]) elif algorithm == "Matlab Like": indexes = find_peaks( sub_data, height=self.amplitude.value(), #低于指定高度忽略 threshold=self.threshold.value(), #相邻两点高度差 distance=self.detectDis.value(), #两峰间距 width=self.peakWidth.value() #峰宽 )[0] if np.size(indexes) == 0: return idx = np.where(sub_data == np.max(sub_data[indexes])) x = sub_region[idx][0] y = sub_data[idx][0] * const self.peakCenter.setValue(x) return self.renderPeakPoint([x, y]) elif algorithm == "Wavelet Transform": indexes = find_peaks_cwt( sub_data, widths=self.peakWidth.value(), #峰宽 max_distances=self.detectDis.value(), #两峰间距 noise_perc=self.noisePrt.value())[0] if np.size(indexes) == 0: return idx = np.where(sub_data == np.max(sub_data[indexes])) x = sub_region[idx][0] y = sub_data[idx][0] * const self.peakCenter.setValue(x) return self.renderPeakPoint([x, y]) self.noisePrt pass self.findBtn.clicked.connect(findPeak) def renderPeakPoint(self, pos): self.peakPoint.clear() self.peakPoint.addPoints([{'pos': pos, 'data': 1}]) def renderWindow(self): #边框结构 self.setGeometry(80, 80, 800, 420) size = self.geometry() screen = QDesktopWidget().screenGeometry() posX = (screen.width() - size.width()) / 2 posY = (screen.height() - size.height()) / 2 self.move(posX, posY) #标题 self.setWindowTitle('Find Peak') self.setWindowIcon(QIcon('resource/curve.ico')) #布局 layout = QGridLayout() self.graphicsView = QGridLayout() layout.addLayout(self.graphicsView, 0, 0, 1, 1) self.Process_Box = QGroupBox() self.Process_Box.setMinimumSize(200, 420) self.Process_Box.setFlat(True) layout.addWidget(self.Process_Box, 0, 1, 1, 1) self.setLayout(layout) def setUpProcessUI(self): layout = QGridLayout() layout.setContentsMargins(10, 10, 10, 10) layout.setSpacing(10) self.Process_Box.setLayout(layout) layout.addWidget(QLabel(self.translate('Left Boundary')), 0, 0, 1, 1) layout.addWidget(QLabel(self.translate('Right Boundary')), 1, 0, 1, 1) layout.addWidget(QLabel(self.translate("Integral Area")), 2, 0, 1, 1) layout.addWidget(QLabel(self.translate('Peak Center')), 3, 0, 1, 1) layout.addWidget(QLabel(self.translate('Peak Shape')), 4, 0, 1, 1) layout.addWidget(QLabel(self.translate('Find Peak Algorithm')), 5, 0, 1, 1) layout.addWidget(QLabel(self.translate('Minimum Peak Width')), 6, 0, 1, 1) layout.addWidget(QLabel(self.translate('Minimum Detect Distance')), 7, 0, 1, 1) layout.addWidget(QLabel(self.translate('Noise Percent')), 8, 0, 1, 1) layout.addWidget(QLabel(self.translate("Minimum Amplitude")), 9, 0, 1, 1) layout.addWidget(QLabel(self.translate("Relative Threshold")), 10, 0, 1, 1) self.leftBound = SpinBox(lower=self.lower, dec=4, val=self.lower) self.rightBound = SpinBox(upper=self.upper, dec=4, val=self.upper) self.peakCenter = SpinBox(lower=self.lower, upper=self.upper, dec=4) self.peakWidth = SpinBox(lower=1, upper=10000, val=5) self.noisePrt = SpinBox(lower=0, upper=100, step=1, val=10) self.detectDis = SpinBox(lower=1, val=3) self.amplitude = SpinBox(lower=-1E5, upper=1E5, dec=4, val=-1) self.threshold = SpinBox(lower=0, upper=100, dec=4, val=0.001) self.integralArea = SpinBox(upper=1E8, dec=4) self.integralArea.setReadOnly(True) self.integralArea.setButtonSymbols(QAbstractSpinBox.NoButtons) self.shape = QComboBox() self.shape.addItems(["Peak", "Valley"]) #self.shape.currentTextChanged.connect() self.algorithm = QComboBox() self.algorithm.addItems([ 'Extremum', 'Matlab Like', 'Wavelet Transform', 'Gaussian', 'Lorentzian', 'Pseudo-Voigt' ]) #self.algorithm.currentTextChanged.connect() #https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.find_peaks_cwt.html layout.addWidget(self.leftBound, 0, 1, 1, 1) layout.addWidget(self.rightBound, 1, 1, 1, 1) layout.addWidget(self.integralArea, 2, 1, 1, 1) layout.addWidget(self.peakCenter, 3, 1, 1, 1) layout.addWidget(self.shape, 4, 1, 1, 1) layout.addWidget(self.algorithm, 5, 1, 1, 1) layout.addWidget(self.peakWidth, 6, 1, 1, 1) layout.addWidget(self.detectDis, 7, 1, 1, 1) layout.addWidget(self.noisePrt, 8, 1, 1, 1) layout.addWidget(self.amplitude, 9, 1, 1, 1) layout.addWidget(self.threshold, 10, 1, 1, 1) self.findBtn = QPushButton(self.translate('Find Peak')) layout.addWidget(self.findBtn, 11, 0, 1, 2) pass def initPlotView(self): self.plot = PlotWidget(enableAutoRange=True) self.plot.setXRange(self.lower - self.range * 0.05, self.upper + self.range * 0.05) self.plotLegand = self.plot.addLegend() self.graphicsView.addWidget(self.plot) self.plotRegion = LinearRegionItem() self.plotRegion.setZValue(10) self.peakPoint = ScatterPlotItem(size=8, pen=mkPen(color='0000FF', width=2), symbol="+", brush=mkBrush(255, 255, 255, 240)) self.plot.addItem(self.plotRegion, ignoreBounds=True) self.plot.addItem(self.peakPoint) self.setGraphViewStyle() def setGraphViewStyle(self): self.plot.setAutoVisible(y=True) self.plot.setBackground('#ffffff') self.plot.showGrid(x=True, y=True, alpha=0.25) self.plot.getAxis('bottom').setPen(color='#000000', width=1.5) self.plot.getAxis('left').setPen(color='#000000', width=1.5) self.plotRegion.setRegion([self.lower, self.upper]) self.plotRegion.setBounds([self.lower, self.upper]) def drawCurve(self): pen = mkPen(color='FF0000', width=2) self.plot.plot(self.x, self.y, pen=pen) self.plot.show() def translate(self, text): if self.parent: self.langText = self.parent.langText else: self.langText = load(open('SCN.translation', encoding='utf-8')) if text in self.langText: return self.langText[text] return text
class HistogramItem(GraphicsWidget): """ This is a graphicsWidget which provides controls for adjusting the display of an image. Includes: - Image histogram - Movable region over histogram to select black/white levels Parameters ---------- image : ImageItem or None If *image* is provided, then the control will be automatically linked to the image and changes to the control will be immediately reflected in the image's appearance. fillHistogram : bool By default, the histogram is rendered with a fill. For performance, set *fillHistogram* = False. """ sigLevelsChanged = pyqtSignal(object) sigLevelChangeFinished = pyqtSignal(object) def __init__(self, image=None, fillHistogram=True, bounds: tuple = None): GraphicsWidget.__init__(self) self.imageItem = lambda: None # fake a dead weakref self.layout = QGraphicsGridLayout() self.setLayout(self.layout) self.layout.setContentsMargins(1, 1, 1, 1) self.layout.setSpacing(0) self.vb = ViewBox(parent=self) # self.vb.setMaximumHeight(152) # self.vb.setMinimumWidth(45) self.vb.setMouseEnabled(x=True, y=False) self.region = LinearRegionItem([0, 1], 'vertical', swapMode='block', bounds=bounds) self.region.setZValue(1000) self.vb.addItem(self.region) self.region.lines[0].addMarker('<|', 0.5) self.region.lines[1].addMarker('|>', 0.5) self.region.sigRegionChanged.connect(self.regionChanging) self.region.sigRegionChangeFinished.connect(self.regionChanged) self.axis = AxisItem('bottom', linkView=self.vb, maxTickLength=-10, parent=self) self.layout.addItem(self.axis, 1, 0) self.layout.addItem(self.vb, 0, 0) self.range = None self.vb.sigRangeChanged.connect(self.viewRangeChanged) self.plot = PlotCurveItem(pen=(200, 200, 200, 100)) # self.plot.rotate(90) self.vb.addItem(self.plot) self.fillHistogram(fillHistogram) self._showRegions() self.autoHistogramRange() if image is not None: self.setImageItem(image) def fillHistogram(self, fill=True, level=0.0, color=(100, 100, 200)): if fill: self.plot.setFillLevel(level) self.plot.setBrush(color) else: self.plot.setFillLevel(None) def paint(self, p, *args): rgn = self.getLevels() self.vb.mapFromViewToItem(self, Point(self.vb.viewRect().center().x(), rgn[0])) self.vb.mapFromViewToItem(self, Point(self.vb.viewRect().center().x(), rgn[1])) def setHistogramRange(self, mn, mx, padding=0.1): """Set the Y range on the histogram plot. This disables auto-scaling.""" self.vb.enableAutoRange(self.vb.XAxis, False) self.vb.setYRange(mn, mx, padding) def autoHistogramRange(self): """Enable auto-scaling on the histogram plot.""" self.vb.enableAutoRange(self.vb.XYAxes) def setImageItem(self, img): """Set an ImageItem to have its levels and LUT automatically controlled by this HistogramLUTItem. """ self.imageItem = weakref.ref(img) img.sigImageChanged.connect(self.imageChanged) self.regionChanged() self.imageChanged(autoLevel=True) def viewRangeChanged(self): self.update() def regionChanged(self): if self.imageItem() is not None: self.imageItem().setLevels(self.getLevels()) self.sigLevelChangeFinished.emit(self) def regionChanging(self): if self.imageItem() is not None: self.imageItem().setLevels(self.getLevels()) self.sigLevelsChanged.emit(self) self.update() def imageChanged(self, autoLevel=False): if self.imageItem() is None: return self.plot.setVisible(True) # plot one histogram for all image data h = self.imageItem().getHistogram() if h[0] is None: return self.plot.setData(*h) if autoLevel: mn = h[0][0] mx = h[0][-1] self.region.setRegion([mn, mx]) else: mn, mx = self.imageItem().levels self.region.setRegion([mn, mx]) def getLevels(self): """ Return the min and max levels. """ return self.region.getRegion() def setLevels(self, min=None, max=None): """ Set the min/max (bright and dark) levels. """ assert None not in (min, max) self.region.setRegion((min, max)) def _showRegions(self): self.region.setVisible(True) def saveState(self): return { 'levels': self.getLevels(), } def restoreState(self, state): self.setLevels(*state['levels'])