BodeDemo.py

#!/usr/bin/env python
# The Python version of Qwt-5.1.1/examples/bode
# To get an impression of the expressive power of NumPy,
# compare the Python and C++ versions of setDamp()
import sys
from PyQt4.Qt import *
from PyQt4.Qwt5 import *
import PyQt4.Qwt5.anynumpy as np
import sip; sip.settracemask(0xff)
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#..........',
 '..................###...........']
zoom_xpm = ['32 32 8 1',
 '# c #000000',
 'b c #c0c0c0',
 'a c #ffffff',
 'e c #585858',
 'd c #a0a0a4',
 'c c #0000ff',
 'f c #00ffff',
 '. c None',
 '..######################........',
 '.#a#baaaaaaaaaaaaaaaaaa#........',
 '#aa#baaaaaaaaaaaaaccaca#........',
 '####baaaaaaaaaaaaaaaaca####.....',
 '#bbbbaaaaaaaaaaaacccaaa#da#.....',
 '#aaaaaaaaaaaaaaaacccaca#da#.....',
 '#aaaaaaaaaaaaaaaaaccaca#da#.....',
 '#aaaaaaaaaabe###ebaaaaa#da#.....',
 '#aaaaaaaaa#########aaaa#da#.....',
 '#aaaaaaaa###dbbbb###aaa#da#.....',
 '#aaaaaaa###aaaaffb###aa#da#.....',
 '#aaaaaab##aaccaaafb##ba#da#.....',
 '#aaaaaae#daaccaccaad#ea#da#.....',
 '#aaaaaa##aaaaaaccaab##a#da#.....',
 '#aaaaaa##aacccaaaaab##a#da#.....',
 '#aaaaaa##aaccccaccab##a#da#.....',
 '#aaaaaae#daccccaccad#ea#da#.....',
 '#aaaaaab##aacccaaaa##da#da#.....',
 '#aaccacd###aaaaaaa###da#da#.....',
 '#aaaaacad###daaad#####a#da#.....',
 '#acccaaaad##########da##da#.....',
 '#acccacaaadde###edd#eda#da#.....',
 '#aaccacaaaabdddddbdd#eda#a#.....',
 '#aaaaaaaaaaaaaaaaaadd#eda##.....',
 '#aaaaaaaaaaaaaaaaaaadd#eda#.....',
 '#aaaaaaaccacaaaaaaaaadd#eda#....',
 '#aaaaaaaaaacaaaaaaaaaad##eda#...',
 '#aaaaaacccaaaaaaaaaaaaa#d#eda#..',
 '########################dd#eda#.',
 '...#dddddddddddddddddddddd##eda#',
 '...#aaaaaaaaaaaaaaaaaaaaaa#.####',
 '...########################..##.']
class PrintFilter(QwtPlotPrintFilter):
 def __init__(self):
 QwtPlotPrintFilter.__init__(self)
 # __init___()
 
 def color(self, c, item):
 if not (self.options() & QwtPlotPrintFilter.CanvasBackground):
 if item == QwtPlotPrintFilter.MajorGrid:
 return Qt.darkGray
 elif item == QwtPlotPrintFilter.MinorGrid:
 return Qt.gray
 if item == QwtPlotPrintFilter.Title:
 return Qt.red
 elif item == QwtPlotPrintFilter.AxisScale:
 return Qt.green
 elif item == QwtPlotPrintFilter.AxisTitle:
 return Qt.blue
 return c
 # color()
 def font(self, f, _):
 result = QFont(f)
 result.setPointSize(int(f.pointSize()*1.25))
 return result
 # font()
# class PrintFilter
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)
 legend.setItemMode(QwtLegend.ClickableItem)
 self.insertLegend(legend, QwtPlot.BottomLegend)
 # grid
 self.grid = QwtPlotGrid()
 self.grid.enableXMin(True)
 self.grid.setMajPen(QPen(Qt.white, 0, Qt.DotLine))
 self.grid.setMinPen(QPen(Qt.gray, 0 , Qt.DotLine))
 self.grid.attach(self)
 # axes
 self.enableAxis(QwtPlot.yRight)
 self.setAxisTitle(QwtPlot.xBottom, u'\u03c9/\u03c9<sub>0</sub>')
 self.setAxisTitle(QwtPlot.yLeft, 'Amplitude [dB]')
 self.setAxisTitle(QwtPlot.yRight, u'Phase [\u00b0]')
 self.setAxisMaxMajor(QwtPlot.xBottom, 6)
 self.setAxisMaxMinor(QwtPlot.xBottom, 10)
 self.setAxisScaleEngine(QwtPlot.xBottom, QwtLog10ScaleEngine())
 # curves
 self.curve1 = QwtPlotCurve('Amplitude')
 self.curve1.setRenderHint(QwtPlotItem.RenderAntialiased);
 self.curve1.setPen(QPen(Qt.yellow))
 self.curve1.setYAxis(QwtPlot.yLeft)
 self.curve1.attach(self)
 
 self.curve2 = QwtPlotCurve('Phase')
 self.curve2.setRenderHint(QwtPlotItem.RenderAntialiased);
 self.curve2.setPen(QPen(Qt.cyan))
 self.curve2.setYAxis(QwtPlot.yRight)
 self.curve2.attach(self)
 # alias
 fn = self.fontInfo().family()
 # marker
 self.dB3Marker = m = QwtPlotMarker()
 m.setValue(0.0, 0.0)
 m.setLineStyle(QwtPlotMarker.VLine)
 m.setLabelAlignment(Qt.AlignRight | Qt.AlignBottom)
 m.setLinePen(QPen(Qt.green, 2, Qt.DashDotLine))
 text = QwtText('')
 text.setColor(Qt.green)
 text.setBackgroundBrush(Qt.red)
 text.setFont(QFont(fn, 12, QFont.Bold))
 m.setLabel(text)
 m.attach(self)
 self.peakMarker = m = QwtPlotMarker()
 m.setLineStyle(QwtPlotMarker.HLine)
 m.setLabelAlignment(Qt.AlignRight | Qt.AlignBottom)
 m.setLinePen(QPen(Qt.red, 2, Qt.DashDotLine))
 text = QwtText('')
 text.setColor(Qt.red)
 text.setBackgroundBrush(QBrush(self.canvasBackground()))
 text.setFont(QFont(fn, 12, QFont.Bold))
 
 m.setLabel(text)
 m.setSymbol(QwtSymbol(QwtSymbol.Diamond,
 QBrush(Qt.yellow),
 QPen(Qt.green),
 QSize(7,7)))
 m.attach(self)
 # text marker
 m = QwtPlotMarker()
 m.setValue(0.1, -20.0)
 m.setLabelAlignment(Qt.AlignRight | Qt.AlignBottom)
 text = QwtText(
 u'[1-(\u03c9/\u03c9<sub>0</sub>)<sup>2</sup>+2j\u03c9/Q]'
 '<sup>-1</sup>'
 )
 text.setFont(QFont(fn, 12, QFont.Bold))
 text.setColor(Qt.blue)
 text.setBackgroundBrush(QBrush(Qt.yellow))
 text.setBackgroundPen(QPen(Qt.red, 2))
 m.setLabel(text)
 m.attach(self)
 self.setDamp(0.01)
 # __init__()
 def showData(self, frequency, amplitude, phase):
 self.curve1.setData(frequency, amplitude)
 self.curve2.setData(frequency, phase)
 # showData()
 def showPeak(self, frequency, amplitude):
 self.peakMarker.setValue(frequency, amplitude)
 label = self.peakMarker.label()
 label.setText('Peak: %4g dB' % amplitude)
 self.peakMarker.setLabel(label)
 # showPeak()
 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)
 # show3dB()
 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()
 # setDamp()
# class BodePlot
class BodeDemo(QMainWindow):
 def __init__(self, *args):
 QMainWindow.__init__(self, *args)
 self.plot = BodePlot(self)
 self.plot.setMargin(5)
 self.setContextMenuPolicy(Qt.NoContextMenu)
 
 self.zoomers = []
 zoomer = QwtPlotZoomer(
 QwtPlot.xBottom,
 QwtPlot.yLeft,
 QwtPicker.DragSelection,
 QwtPicker.AlwaysOff,
 self.plot.canvas())
 zoomer.setRubberBandPen(QPen(Qt.green))
 self.zoomers.append(zoomer)
 zoomer = QwtPlotZoomer(
 QwtPlot.xTop,
 QwtPlot.yRight,
 QwtPicker.PointSelection | QwtPicker.DragSelection,
 QwtPicker.AlwaysOff,
 self.plot.canvas())
 zoomer.setRubberBand(QwtPicker.NoRubberBand)
 self.zoomers.append(zoomer)
 self.picker = QwtPlotPicker(
 QwtPlot.xBottom,
 QwtPlot.yLeft,
 QwtPicker.PointSelection | QwtPicker.DragSelection,
 QwtPlotPicker.CrossRubberBand,
 QwtPicker.AlwaysOn,
 self.plot.canvas())
 self.picker.setRubberBandPen(QPen(Qt.green))
 self.picker.setTrackerPen(QPen(Qt.cyan))
 
 self.setCentralWidget(self.plot)
 toolBar = QToolBar(self)
 self.addToolBar(toolBar)
 
 btnZoom = QToolButton(toolBar)
 btnZoom.setText("Zoom")
 btnZoom.setIcon(QIcon(QPixmap(zoom_xpm)))
 btnZoom.setCheckable(True)
 btnZoom.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
 toolBar.addWidget(btnZoom)
 btnPrint = QToolButton(toolBar)
 btnPrint.setText("Print")
 btnPrint.setIcon(QIcon(QPixmap(print_xpm)))
 btnPrint.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
 toolBar.addWidget(btnPrint)
 self.connect(btnPrint, SIGNAL('clicked()'), self.print_)
 if QT_VERSION >= 0X040100:
 btnPDF = QToolButton(toolBar)
 btnPDF.setText("PDF")
 btnPDF.setIcon(QIcon(QPixmap(print_xpm)))
 btnPDF.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
 toolBar.addWidget(btnPDF)
 self.connect(btnPDF, SIGNAL('clicked()'), self.exportPDF)
 if QT_VERSION >= 0x040300:
 btnSVG = QToolButton(toolBar)
 btnSVG.setText("SVG")
 btnSVG.setIcon(QIcon(QPixmap(print_xpm)))
 btnSVG.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
 toolBar.addWidget(btnSVG) 
 self.connect(btnSVG, SIGNAL('clicked()'), self.exportSVG)
 
 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)
 self.cntDamp = QwtCounter(dampBox)
 self.cntDamp.setRange(0.01, 5.0, 0.01)
 self.cntDamp.setValue(0.01)
 dampLayout.addWidget(self.cntDamp, 10)
 toolBar.addWidget(dampBox)
 self.statusBar()
 
 self.zoom(False)
 self.showInfo()
 
 self.connect(self.cntDamp,
 SIGNAL('valueChanged(double)'),
 self.plot.setDamp)
 self.connect(btnZoom,
 SIGNAL('toggled(bool)'),
 self.zoom)
 self.connect(self.picker,
 SIGNAL('moved(const QPoint &)'),
 self.moved)
 self.connect(self.picker,
 SIGNAL('selected(const QaPolygon &)'),
 self.selected)
 # __init__()
 def print_(self):
 printer = QPrinter(QPrinter.HighResolution)
 printer.setOutputFileName('bode-example-%s.ps' % qVersion())
 printer.setCreator('Bode example')
 printer.setOrientation(QPrinter.Landscape)
 printer.setColorMode(QPrinter.Color)
 docName = self.plot.title().text()
 if not docName.isEmpty():
 docName.replace(QRegExp(QString.fromLatin1('\n')), self.tr(' -- '))
 printer.setDocName(docName)
 dialog = QPrintDialog(printer)
 if dialog.exec_():
 filter = PrintFilter()
 if (QPrinter.GrayScale == printer.colorMode()):
 filter.setOptions(
 QwtPlotPrintFilter.PrintAll
 & ~QwtPlotPrintFilter.PrintBackground
 | QwtPlotPrintFilter.PrintFrameWithScales)
 self.plot.print_(printer, filter)
 # print_()
 
 def exportPDF(self):
 if QT_VERSION > 0x040100:
 fileName = QFileDialog.getSaveFileName(
 self,
 'Export File Name',
 'bode-example-%s.pdf' % qVersion(),
 'PDF Documents (*.pdf)')
 if not fileName.isEmpty():
 printer = QPrinter()
 printer.setOutputFormat(QPrinter.PdfFormat)
 printer.setOrientation(QPrinter.Landscape)
 printer.setOutputFileName(fileName)
 printer.setCreator('Bode example')
 self.plot.print_(printer)
 # exportPDF()
 def exportSVG(self):
 if QT_VERSION >= 0x040300:
 fileName = QFileDialog.getSaveFileName(
 self,
 'Export File Name',
 'bode-example-%s.svg' % qVersion(),
 'SVG Documents (*.svg)')
 if not fileName.isEmpty():
 generator = QSvgGenerator()
 generator.setFileName(fileName)
 generator.setSize(QSize(800, 600))
 self.plot.print_(generator)
 # exportSVG()
 def zoom(self, on):
 self.zoomers[0].setEnabled(on)
 self.zoomers[0].zoom(0)
 
 self.zoomers[1].setEnabled(on)
 self.zoomers[1].zoom(0)
 if on:
 self.picker.setRubberBand(Qwt.QwtPicker.NoRubberBand)
 else:
 self.picker.setRubberBand(Qwt.QwtPicker.CrossRubberBand)
 self.showInfo()
 # zoom()
 
 def showInfo(self, text=None):
 if not text:
 if self.picker.rubberBand():
 text = 'Cursor Pos: Press left mouse button in plot region'
 else:
 text = 'Zoom: Press mouse button and drag'
 
 self.statusBar().showMessage(text)
 
 # showInfo()
 
 def moved(self, point):
 info = "Freq=%g, Ampl=%g, Phase=%g" % (
 self.plot.invTransform(Qwt.QwtPlot.xBottom, point.x()),
 self.plot.invTransform(Qwt.QwtPlot.yLeft, point.y()),
 self.plot.invTransform(Qwt.QwtPlot.yRight, point.y()))
 self.showInfo(info)
 # moved()
 def selected(self, _):
 self.showInfo()
 # selected()
# class BodeDemo
 
def make():
 demo = BodeDemo()
 demo.resize(540, 400)
 demo.show()
 return demo
# make()
def main(args):
 app = QApplication(args)
 fonts = QFontDatabase()
 for name in ('Verdana', 'STIXGeneral'):
 if QString(name) in fonts.families():
 app.setFont(QFont(name))
 break
 demo = make()
 sys.exit(app.exec_())
# main()
# Admire!
if __name__ == '__main__':
 main(sys.argv)
# Local Variables: ***
# mode: python ***
# End: ***

AltStyle によって変換されたページ (->オリジナル) /