# Bode demo

    import os

    import numpy as np
    from qtpy.QtCore import Qt
    from qtpy.QtGui import QFont, QIcon, QPageLayout, QPen, QPixmap
    from qtpy.QtPrintSupport import QPrintDialog, QPrinter
    from qtpy.QtWidgets import (
        QFrame,
        QHBoxLayout,
        QLabel,
        QMainWindow,
        QToolBar,
        QToolButton,
        QWidget,
    )

    from qwt import (
        QwtLegend,
        QwtLogScaleEngine,
        QwtPlot,
        QwtPlotCurve,
        QwtPlotGrid,
        QwtPlotMarker,
        QwtPlotRenderer,
        QwtSymbol,
        QwtText,
    )
    from qwt.tests import utils

    print_xpm = [
        "32 32 12 1",
        "a c #ffffff",
        "h c #ffff00",
        "c c #ffffff",
        "f c #dcdcdc",
        "b c #c0c0c0",
        "j c #a0a0a4",
        "e c #808080",
        "g c #808000",
        "d c #585858",
        "i c #00ff00",
        "# c #000000",
        ". c None",
        "................................",
        "................................",
        "...........###..................",
        "..........#abb###...............",
        ".........#aabbbbb###............",
        ".........#ddaaabbbbb###.........",
        "........#ddddddaaabbbbb###......",
        ".......#deffddddddaaabbbbb###...",
        "......#deaaabbbddddddaaabbbbb###",
        ".....#deaaaaaaabbbddddddaaabbbb#",
        "....#deaaabbbaaaa#ddedddfggaaad#",
        "...#deaaaaaaaaaa#ddeeeeafgggfdd#",
        "..#deaaabbbaaaa#ddeeeeabbbbgfdd#",
        ".#deeefaaaaaaa#ddeeeeabbhhbbadd#",
        "#aabbbeeefaaa#ddeeeeabbbbbbaddd#",
        "#bbaaabbbeee#ddeeeeabbiibbadddd#",
        "#bbbbbaaabbbeeeeeeabbbbbbaddddd#",
        "#bjbbbbbbaaabbbbeabbbbbbadddddd#",
        "#bjjjjbbbbbbaaaeabbbbbbaddddddd#",
        "#bjaaajjjbbbbbbaaabbbbadddddddd#",
        "#bbbbbaaajjjbbbbbbaaaaddddddddd#",
        "#bjbbbbbbaaajjjbbbbbbddddddddd#.",
        "#bjjjjbbbbbbaaajjjbbbdddddddd#..",
        "#bjaaajjjbbbbbbjaajjbddddddd#...",
        "#bbbbbaaajjjbbbjbbaabdddddd#....",
        "###bbbbbbaaajjjjbbbbbddddd#.....",
        "...###bbbbbbaaajbbbbbdddd#......",
        "......###bbbbbbjbbbbbddd#.......",
        ".........###bbbbbbbbbdd#........",
        "............###bbbbbbd#.........",
        "...............###bbb#..........",
        "..................###...........",
    ]

    class BodePlot(QwtPlot):
        def __init__(self, *args):
            QwtPlot.__init__(self, *args)

            self.setTitle("Frequency Response of a 2<sup>nd</sup>-order System")
            self.setCanvasBackground(Qt.darkBlue)

            # legend
            legend = QwtLegend()
            legend.setFrameStyle(QFrame.Box | QFrame.Sunken)
            self.insertLegend(legend, QwtPlot.BottomLegend)

            # grid
            QwtPlotGrid.make(plot=self, enableminor=(True, False), color=Qt.darkGray)

            # axes
            self.enableAxis(QwtPlot.yRight)
            self.setAxisTitle(QwtPlot.xBottom, "\u03c9/\u03c9<sub>0</sub>")
            self.setAxisTitle(QwtPlot.yLeft, "Amplitude [dB]")
            self.setAxisTitle(QwtPlot.yRight, "Phase [\u00b0]")

            self.setAxisMaxMajor(QwtPlot.xBottom, 6)
            self.setAxisMaxMinor(QwtPlot.xBottom, 10)
            self.setAxisScaleEngine(QwtPlot.xBottom, QwtLogScaleEngine())

            # curves
            self.curve1 = QwtPlotCurve.make(
                title="Amplitude", linecolor=Qt.yellow, plot=self, antialiased=True
            )
            self.curve2 = QwtPlotCurve.make(
                title="Phase", linecolor=Qt.cyan, plot=self, antialiased=True
            )
            self.dB3Marker = QwtPlotMarker.make(
                label=QwtText.make(color=Qt.white, brush=Qt.red, weight=QFont.Light),
                linestyle=QwtPlotMarker.VLine,
                align=Qt.AlignRight | Qt.AlignBottom,
                color=Qt.green,
                width=2,
                style=Qt.DashDotLine,
                plot=self,
            )
            self.peakMarker = QwtPlotMarker.make(
                label=QwtText.make(
                    color=Qt.red, brush=self.canvasBackground(), weight=QFont.Bold
                ),
                symbol=QwtSymbol.make(QwtSymbol.Diamond, Qt.yellow, Qt.green, (7, 7)),
                linestyle=QwtPlotMarker.HLine,
                align=Qt.AlignRight | Qt.AlignBottom,
                color=Qt.red,
                width=2,
                style=Qt.DashDotLine,
                plot=self,
            )
            QwtPlotMarker.make(
                xvalue=0.1,
                yvalue=-20.0,
                align=Qt.AlignRight | Qt.AlignBottom,
                label=QwtText.make(
                    "[1-(\u03c9/\u03c9<sub>0</sub>)<sup>2</sup>+2j\u03c9/Q]"
                    "<sup>-1</sup>",
                    color=Qt.white,
                    borderradius=2,
                    borderpen=QPen(Qt.lightGray, 5),
                    brush=Qt.lightGray,
                    weight=QFont.Bold,
                ),
                plot=self,
            )

            self.setDamp(0.01)

        def showData(self, frequency, amplitude, phase):
            self.curve1.setData(frequency, amplitude)
            self.curve2.setData(frequency, phase)

        def showPeak(self, frequency, amplitude):
            self.peakMarker.setValue(frequency, amplitude)
            label = self.peakMarker.label()
            label.setText("Peak: %4g dB" % amplitude)
            self.peakMarker.setLabel(label)

        def show3dB(self, frequency):
            self.dB3Marker.setValue(frequency, 0.0)
            label = self.dB3Marker.label()
            label.setText("-3dB at f = %4g" % frequency)
            self.dB3Marker.setLabel(label)

        def setDamp(self, d):
            self.damping = d
            # Numerical Python: f, g, a and p are NumPy arrays!
            f = np.exp(np.log(10.0) * np.arange(-2, 2.02, 0.04))
            g = 1.0 / (1.0 - f * f + 2j * self.damping * f)
            a = 20.0 * np.log10(abs(g))
            p = 180 * np.arctan2(g.imag, g.real) / np.pi
            # for show3dB
            i3 = np.argmax(np.where(np.less(a, -3.0), a, -100.0))
            f3 = f[i3] - (a[i3] + 3.0) * (f[i3] - f[i3 - 1]) / (a[i3] - a[i3 - 1])
            # for showPeak
            imax = np.argmax(a)

            self.showPeak(f[imax], a[imax])
            self.show3dB(f3)
            self.showData(f, a, p)

            self.replot()

    FNAME_PDF = "bode.pdf"

    class BodeDemo(QMainWindow):
        def __init__(self, *args):
            QMainWindow.__init__(self, *args)

            self.plot = BodePlot(self)
            self.plot.setContentsMargins(5, 5, 5, 0)

            self.setContextMenuPolicy(Qt.NoContextMenu)

            self.setCentralWidget(self.plot)

            toolBar = QToolBar(self)
            self.addToolBar(toolBar)

            btnPrint = QToolButton(toolBar)
            btnPrint.setText("Print")
            btnPrint.setIcon(QIcon(QPixmap(print_xpm)))
            btnPrint.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
            toolBar.addWidget(btnPrint)
            btnPrint.clicked.connect(self.print_)

            btnExport = QToolButton(toolBar)
            btnExport.setText("Export")
            btnExport.setIcon(QIcon(QPixmap(print_xpm)))
            btnExport.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
            toolBar.addWidget(btnExport)
            btnExport.clicked.connect(self.exportDocument)

            toolBar.addSeparator()

            dampBox = QWidget(toolBar)
            dampLayout = QHBoxLayout(dampBox)
            dampLayout.setSpacing(0)
            dampLayout.addWidget(QWidget(dampBox), 10)  # spacer
            dampLayout.addWidget(QLabel("Damping Factor", dampBox), 0)
            dampLayout.addSpacing(10)

            toolBar.addWidget(dampBox)

            self.statusBar()

            self.showInfo()

            if utils.TestEnvironment().unattended:
                self.print_(unattended=True)

        def print_(self, unattended=False):
            try:
                mode = QPrinter.HighResolution
                printer = QPrinter(mode)
            except AttributeError:
                # Some PySide6 / PyQt6 versions do not have this attribute on Linux
                printer = QPrinter()

            printer.setCreator("Bode example")
            printer.setPageOrientation(QPageLayout.Landscape)
            try:
                printer.setColorMode(QPrinter.Color)
            except AttributeError:
                pass

            docName = str(self.plot.title().text())
            if not docName:
                docName.replace("\n", " -- ")
                printer.setDocName(docName)

            dialog = QPrintDialog(printer)
            if unattended:
                # Configure QPrinter object to print to PDF file
                printer.setPrinterName("")
                printer.setOutputFileName(FNAME_PDF)
                dialog.accept()
                ok = True
            else:
                ok = dialog.exec_()
            if ok:
                renderer = QwtPlotRenderer()
                renderer.renderTo(self.plot, printer)

        def exportDocument(self):
            renderer = QwtPlotRenderer(self.plot)
            renderer.exportTo(self.plot, "bode")

        def showInfo(self, text=""):
            self.statusBar().showMessage(text)

        def moved(self, point):
            info = "Freq=%g, Ampl=%g, Phase=%g" % (
                self.plot.invTransform(QwtPlot.xBottom, point.x()),
                self.plot.invTransform(QwtPlot.yLeft, point.y()),
                self.plot.invTransform(QwtPlot.yRight, point.y()),
            )
            self.showInfo(info)

        def selected(self, _):
            self.showInfo()

    def test_bodedemo():
        """Bode demo"""
        utils.test_widget(BodeDemo, (640, 480))
        if os.path.isfile(FNAME_PDF):
            os.remove(FNAME_PDF)

    if __name__ == "__main__":
        test_bodedemo()