
.. DO NOT EDIT.
.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY.
.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE:
.. "gallery/user_interfaces/fourier_demo_wx_sgskip.py"
.. LINE NUMBERS ARE GIVEN BELOW.

.. only:: html

    .. meta::
        :keywords: codex

    .. note::
        :class: sphx-glr-download-link-note

        :ref:`Go to the end <sphx_glr_download_gallery_user_interfaces_fourier_demo_wx_sgskip.py>`
        to download the full example code.

.. rst-class:: sphx-glr-example-title

.. _sphx_glr_gallery_user_interfaces_fourier_demo_wx_sgskip.py:


===============
Fourier Demo WX
===============

.. GENERATED FROM PYTHON SOURCE LINES 7-239

.. code-block:: Python


    import wx

    import numpy as np

    from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
    from matplotlib.figure import Figure


    class Knob:
        """
        Knob - simple class with a "setKnob" method.
        A Knob instance is attached to a Param instance, e.g., param.attach(knob)
        Base class is for documentation purposes.
        """

        def setKnob(self, value):
            pass


    class Param:
        """
        The idea of the "Param" class is that some parameter in the GUI may have
        several knobs that both control it and reflect the parameter's state, e.g.
        a slider, text, and dragging can all change the value of the frequency in
        the waveform of this example.
        The class allows a cleaner way to update/"feedback" to the other knobs when
        one is being changed.  Also, this class handles min/max constraints for all
        the knobs.
        Idea - knob list - in "set" method, knob object is passed as well
          - the other knobs in the knob list have a "set" method which gets
            called for the others.
        """

        def __init__(self, initialValue=None, minimum=0., maximum=1.):
            self.minimum = minimum
            self.maximum = maximum
            if initialValue != self.constrain(initialValue):
                raise ValueError('illegal initial value')
            self.value = initialValue
            self.knobs = []

        def attach(self, knob):
            self.knobs += [knob]

        def set(self, value, knob=None):
            self.value = value
            self.value = self.constrain(value)
            for feedbackKnob in self.knobs:
                if feedbackKnob != knob:
                    feedbackKnob.setKnob(self.value)
            return self.value

        def constrain(self, value):
            if value <= self.minimum:
                value = self.minimum
            if value >= self.maximum:
                value = self.maximum
            return value


    class SliderGroup(Knob):
        def __init__(self, parent, label, param):
            self.sliderLabel = wx.StaticText(parent, label=label)
            self.sliderText = wx.TextCtrl(parent, -1, style=wx.TE_PROCESS_ENTER)
            self.slider = wx.Slider(parent, -1)
            # self.slider.SetMax(param.maximum*1000)
            self.slider.SetRange(0, int(param.maximum * 1000))
            self.setKnob(param.value)

            sizer = wx.BoxSizer(wx.HORIZONTAL)
            sizer.Add(self.sliderLabel, 0,
                      wx.EXPAND | wx.ALL,
                      border=2)
            sizer.Add(self.sliderText, 0,
                      wx.EXPAND | wx.ALL,
                      border=2)
            sizer.Add(self.slider, 1, wx.EXPAND)
            self.sizer = sizer

            self.slider.Bind(wx.EVT_SLIDER, self.sliderHandler)
            self.sliderText.Bind(wx.EVT_TEXT_ENTER, self.sliderTextHandler)

            self.param = param
            self.param.attach(self)

        def sliderHandler(self, event):
            value = event.GetInt() / 1000.
            self.param.set(value)

        def sliderTextHandler(self, event):
            value = float(self.sliderText.GetValue())
            self.param.set(value)

        def setKnob(self, value):
            self.sliderText.SetValue(f'{value:g}')
            self.slider.SetValue(int(value * 1000))


    class FourierDemoFrame(wx.Frame):
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            panel = wx.Panel(self)

            # create the GUI elements
            self.createCanvas(panel)
            self.createSliders(panel)

            # place them in a sizer for the Layout
            sizer = wx.BoxSizer(wx.VERTICAL)
            sizer.Add(self.canvas, 1, wx.EXPAND)
            sizer.Add(self.frequencySliderGroup.sizer, 0,
                      wx.EXPAND | wx.ALL, border=5)
            sizer.Add(self.amplitudeSliderGroup.sizer, 0,
                      wx.EXPAND | wx.ALL, border=5)
            panel.SetSizer(sizer)

        def createCanvas(self, parent):
            self.lines = []
            self.figure = Figure()
            self.canvas = FigureCanvas(parent, -1, self.figure)
            self.canvas.callbacks.connect('button_press_event', self.mouseDown)
            self.canvas.callbacks.connect('motion_notify_event', self.mouseMotion)
            self.canvas.callbacks.connect('button_release_event', self.mouseUp)
            self.state = ''
            self.mouseInfo = (None, None, None, None)
            self.f0 = Param(2., minimum=0., maximum=6.)
            self.A = Param(1., minimum=0.01, maximum=2.)
            self.createPlots()

            # Not sure I like having two params attached to the same Knob,
            # but that is what we have here... it works but feels kludgy -
            # although maybe it's not too bad since the knob changes both params
            # at the same time (both f0 and A are affected during a drag)
            self.f0.attach(self)
            self.A.attach(self)

        def createSliders(self, panel):
            self.frequencySliderGroup = SliderGroup(
                panel,
                label='Frequency f0:',
                param=self.f0)
            self.amplitudeSliderGroup = SliderGroup(panel, label=' Amplitude a:',
                                                    param=self.A)

        def mouseDown(self, event):
            if self.lines[0].contains(event)[0]:
                self.state = 'frequency'
            elif self.lines[1].contains(event)[0]:
                self.state = 'time'
            else:
                self.state = ''
            self.mouseInfo = (event.xdata, event.ydata,
                              max(self.f0.value, .1),
                              self.A.value)

        def mouseMotion(self, event):
            if self.state == '':
                return
            x, y = event.xdata, event.ydata
            if x is None:  # outside the Axes
                return
            x0, y0, f0Init, AInit = self.mouseInfo
            self.A.set(AInit + (AInit * (y - y0) / y0), self)
            if self.state == 'frequency':
                self.f0.set(f0Init + (f0Init * (x - x0) / x0))
            elif self.state == 'time':
                if (x - x0) / x0 != -1.:
                    self.f0.set(1. / (1. / f0Init + (1. / f0Init * (x - x0) / x0)))

        def mouseUp(self, event):
            self.state = ''

        def createPlots(self):
            # This method creates the subplots, waveforms and labels.
            # Later, when the waveforms or sliders are dragged, only the
            # waveform data will be updated (not here, but below in setKnob).
            self.subplot1, self.subplot2 = self.figure.subplots(2)
            x1, y1, x2, y2 = self.compute(self.f0.value, self.A.value)
            color = (1., 0., 0.)
            self.lines += self.subplot1.plot(x1, y1, color=color, linewidth=2)
            self.lines += self.subplot2.plot(x2, y2, color=color, linewidth=2)
            # Set some plot attributes
            self.subplot1.set_title(
                "Click and drag waveforms to change frequency and amplitude",
                fontsize=12)
            self.subplot1.set_ylabel("Frequency Domain Waveform X(f)", fontsize=8)
            self.subplot1.set_xlabel("frequency f", fontsize=8)
            self.subplot2.set_ylabel("Time Domain Waveform x(t)", fontsize=8)
            self.subplot2.set_xlabel("time t", fontsize=8)
            self.subplot1.set_xlim([-6, 6])
            self.subplot1.set_ylim([0, 1])
            self.subplot2.set_xlim([-2, 2])
            self.subplot2.set_ylim([-2, 2])
            self.subplot1.text(0.05, .95,
                               r'$X(f) = \mathcal{F}\{x(t)\}$',
                               verticalalignment='top',
                               transform=self.subplot1.transAxes)
            self.subplot2.text(0.05, .95,
                               r'$x(t) = a \cdot \cos(2\pi f_0 t) e^{-\pi t^2}$',
                               verticalalignment='top',
                               transform=self.subplot2.transAxes)

        def compute(self, f0, A):
            f = np.arange(-6., 6., 0.02)
            t = np.arange(-2., 2., 0.01)
            x = A * np.cos(2 * np.pi * f0 * t) * np.exp(-np.pi * t ** 2)
            X = A / 2 * \
                (np.exp(-np.pi * (f - f0) ** 2) + np.exp(-np.pi * (f + f0) ** 2))
            return f, X, t, x

        def setKnob(self, value):
            # Note, we ignore value arg here and just go by state of the params
            x1, y1, x2, y2 = self.compute(self.f0.value, self.A.value)
            # update the data of the two waveforms
            self.lines[0].set(xdata=x1, ydata=y1)
            self.lines[1].set(xdata=x2, ydata=y2)
            # make the canvas draw its contents again with the new data
            self.canvas.draw()


    class App(wx.App):
        def OnInit(self):
            self.frame1 = FourierDemoFrame(parent=None, title="Fourier Demo",
                                           size=(640, 480))
            self.frame1.Show()
            return True


    if __name__ == "__main__":
        app = App()
        app.MainLoop()


.. _sphx_glr_download_gallery_user_interfaces_fourier_demo_wx_sgskip.py:

.. only:: html

  .. container:: sphx-glr-footer sphx-glr-footer-example

    .. container:: sphx-glr-download sphx-glr-download-jupyter

      :download:`Download Jupyter notebook: fourier_demo_wx_sgskip.ipynb <fourier_demo_wx_sgskip.ipynb>`

    .. container:: sphx-glr-download sphx-glr-download-python

      :download:`Download Python source code: fourier_demo_wx_sgskip.py <fourier_demo_wx_sgskip.py>`

    .. container:: sphx-glr-download sphx-glr-download-zip

      :download:`Download zipped: fourier_demo_wx_sgskip.zip <fourier_demo_wx_sgskip.zip>`


.. only:: html

 .. rst-class:: sphx-glr-signature

    `Gallery generated by Sphinx-Gallery <https://sphinx-gallery.github.io>`_
