summaryrefslogtreecommitdiff
path: root/examples/fftPlotAction.py
diff options
context:
space:
mode:
authorPicca Frédéric-Emmanuel <picca@synchrotron-soleil.fr>2017-08-18 14:48:52 +0200
committerPicca Frédéric-Emmanuel <picca@synchrotron-soleil.fr>2017-08-18 14:48:52 +0200
commitf7bdc2acff3c13a6d632c28c4569690ab106eed7 (patch)
tree9d67cdb7152ee4e711379e03fe0546c7c3b97303 /examples/fftPlotAction.py
Import Upstream version 0.5.0+dfsg
Diffstat (limited to 'examples/fftPlotAction.py')
-rwxr-xr-xexamples/fftPlotAction.py194
1 files changed, 194 insertions, 0 deletions
diff --git a/examples/fftPlotAction.py b/examples/fftPlotAction.py
new file mode 100755
index 0000000..e4d4081
--- /dev/null
+++ b/examples/fftPlotAction.py
@@ -0,0 +1,194 @@
+#!/usr/bin/env python
+# coding: utf-8
+# /*##########################################################################
+#
+# Copyright (c) 2016-2017 European Synchrotron Radiation Facility
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+# ###########################################################################*/
+"""This script is a simple example of how to create a PlotWindow with a custom
+PlotAction added to the toolbar.
+
+The action computes the FFT of all curves and plots their amplitude spectrum.
+It also performs the reverse transform.
+
+This example illustrates:
+ - how to create a checkable action
+ - how to store user info with a curve in a PlotWindow
+ - how to modify the graph title and axes labels
+ - how to add your own icon as a PNG file
+
+See shiftPlotAction.py for a simpler example with more basic comments.
+
+"""
+__authors__ = ["P. Knobel"]
+__license__ = "MIT"
+__date__ = "12/01/2017"
+
+import numpy
+import os
+import sys
+
+from silx.gui import qt
+from silx.gui.plot import PlotWindow
+from silx.gui.plot.PlotActions import PlotAction
+
+# Custom icon
+# make sure there is a "fft.png" file saved in the same folder as this script
+scriptdir = os.path.dirname(os.path.realpath(__file__))
+my_icon = os.path.join(scriptdir, "fft.png")
+
+
+class FftAction(PlotAction):
+ """QAction performing a Fourier transform on all curves when checked,
+ and reverse transform when unchecked.
+
+ :param plot: PlotWindow on which to operate
+ :param parent: See documentation of :class:`QAction`
+ """
+ def __init__(self, plot, parent=None):
+ PlotAction.__init__(
+ self,
+ plot,
+ icon=qt.QIcon(my_icon),
+ text='FFT',
+ tooltip='Perform Fast Fourier Transform on all curves',
+ triggered=self.fftAllCurves,
+ checkable=True,
+ parent=parent)
+
+ def _rememberGraphLabels(self):
+ """Store labels and title as attributes"""
+ self.original_title = self.plot.getGraphTitle()
+ self.original_xlabel = self.plot.getGraphXLabel()
+ self.original_ylabel = self.plot.getGraphYLabel()
+
+ def fftAllCurves(self, checked=False):
+ """Get all curves from our PlotWindow, compute the amplitude spectrum
+ using a Fast Fourier Transform, replace all curves with their
+ amplitude spectra.
+
+ When un-checking the button, do the reverse transform.
+
+ :param checked: Boolean parameter signaling whether the action
+ has been checked or unchecked.
+ """
+ allCurves = self.plot.getAllCurves(withhidden=True)
+
+ if checked:
+ # remember original labels
+ self._rememberGraphLabels()
+ # change them
+ self.plot.setGraphTitle("Amplitude spectrum")
+ self.plot.setGraphXLabel("Frequency")
+ self.plot.setGraphYLabel("Amplitude")
+ else:
+ # restore original labels
+ self.plot.setGraphTitle(self.original_title)
+ self.plot.setGraphXLabel(self.original_xlabel)
+ self.plot.setGraphYLabel(self.original_ylabel)
+
+ self.plot.clearCurves()
+
+ for curve in allCurves:
+ x = curve.getXData()
+ y = curve.getYData()
+ legend = curve.getLegend()
+ info = curve.getInfo()
+ if info is None:
+ info = {}
+
+ if checked:
+ # FAST FOURIER TRANSFORM
+ fft_y = numpy.fft.fft(y)
+ # amplitude spectrum
+ A = numpy.abs(fft_y)
+
+ # sampling frequency (samples per X unit)
+ Fs = len(x) / (max(x) - min(x))
+ # frequency array (abscissa of new curve)
+ F = [k * Fs / len(x) for k in range(len(A))]
+
+ # we need to store the complete transform (complex data) to be
+ # able to perform the reverse transform.
+ info["complex fft"] = fft_y
+ info["original x"] = x
+
+ # plot the amplitude spectrum
+ self.plot.addCurve(F, A, legend="FFT of " + legend,
+ info=info)
+
+ else:
+ # INVERSE FFT
+ fft_y = info["complex fft"]
+ # we keep only the real part because we know the imaginary
+ # part is 0 (our original data was real numbers)
+ y1 = numpy.real(numpy.fft.ifft(fft_y))
+
+ # recover original info
+ x1 = info["original x"]
+ legend1 = legend[7:] # remove "FFT of "
+
+ # remove restored data from info dict
+ for key in ["complex fft", "original x"]:
+ del info[key]
+
+ # plot the original data
+ self.plot.addCurve(x1, y1, legend=legend1,
+ info=info)
+
+ self.plot.resetZoom()
+
+
+app = qt.QApplication([])
+
+sys.excepthook = qt.exceptionHandler
+
+plotwin = PlotWindow(control=True)
+toolbar = qt.QToolBar("My toolbar")
+plotwin.addToolBar(toolbar)
+
+myaction = FftAction(plotwin)
+toolbar.addAction(myaction)
+
+# x range: 0 -- 10 (1000 points)
+x = numpy.arange(1000) * 0.01
+
+twopi = 2 * numpy.pi
+# Sum of sine functions with frequencies 3, 20 and 42 Hz
+y1 = numpy.sin(twopi * 3 * x) + 1.5 * numpy.sin(twopi * 20 * x) + 2 * numpy.sin(twopi * 42 * x)
+# Cosine with frequency 7 Hz and phase pi / 3
+y2 = numpy.cos(twopi * 7 * (x - numpy.pi / 3))
+# 5 periods of square wave, amplitude 2
+y3 = numpy.zeros_like(x)
+for i in [0, 2, 4, 6, 8]:
+ y3[i * len(x) / 10:(i + 1) * len(x) / 10] = 2
+
+plotwin.addCurve(x, y1, legend="sin")
+plotwin.addCurve(x, y2, legend="cos")
+plotwin.addCurve(x, y3, legend="square wave")
+
+plotwin.setGraphTitle("Original data")
+plotwin.setGraphYLabel("amplitude")
+plotwin.setGraphXLabel("time")
+
+plotwin.show()
+app.exec_()
+sys.excepthook = sys.__excepthook__