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)
Exemple #2
0
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
Exemple #3
0
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'])