PyQwtを使ってグラフを描く。今回はマウスでデータを動かすプログラムを作成。
マウスの座標を取得するにはQwtPlotのinvTransformを使えば良いことが分かった。
ただ、この関数にはキャンパス(QwtPlot.canvas())の座標を与えなければならないので注意。
僕はずっとQwtPlotのウィジットの座標を与えていたようで、少し値がずれていました。
イベントの取得方法はPyQwtのHPの例を参考にしました。
イベントフィルタについてはここを読むとわかる。
今回のようにクラス外部にフィルタを持つことで、柔軟にcanvas()でのイベントを取得できます。
(ただ原理をきちんと理解していないので冗長さはありますが…どうなんでしょう?)
クリックした座標に一番近いデータはQwt.PlotCurve.closestPointで取得できます。
あとはドラッグしている間にそのデータを書き換えるだけです。
ソースも載せときます。
とりあえずグラフでやりたいことはここまでなので、あとはQtでUIを作成していきます。
import sys from PyQt4.QtGui import QApplication, QWidget, QHBoxLayout from PyQt4.Qt import Qt, QPen, QSize, QBrush, QObject, SIGNAL, QEvent from PyQt4.Qwt5.Qwt import QwtPlot, QwtPlotGrid, QwtPlotCurve, QwtSymbol import numpy as np from numpy import pi class Spy(QObject): def __init__(self, parent): QObject.__init__(self, parent) parent.setMouseTracking(True) parent.installEventFilter(self) # __init__() def eventFilter(self, _, event): if event.type() == QEvent.MouseMove: self.emit(SIGNAL("MouseMove"), event.pos()) if event.type() == QEvent.MouseButtonPress: self.emit(SIGNAL("MouseButtonPress"), event.pos()) if event.type() == QEvent.MouseButtonRelease: self.emit(SIGNAL("MouseButtonRelease"), event.pos()) return False # eventFilter() # class Spy class PlotWidget(QwtPlot): def __init__(self, title, *args): QwtPlot.__init__(self, *args) self.setTitle(title) self.setCanvasBackground(Qt.white) self.setMouseTracking(True) #grid grid = QwtPlotGrid() grid.attach(self) grid.setPen(QPen(Qt.black, 0, Qt.DotLine)) #make grid dotted-line self.curve = None self.picked_id = None #picked index by mouse self.pick_tol = 5.0 #picking tollerence #connect signal and slot self.connect(Spy(self.canvas()), SIGNAL("MouseMove"), self.on_move) self.connect(Spy(self.canvas()), SIGNAL("MouseButtonPress"), self.on_click) self.connect(Spy(self.canvas()), SIGNAL("MouseButtonRelease"), self.on_release) def on_move(self, pos): if self.picked_id is not None: #drag the picked data by mouse x = self.invTransform(QwtPlot.xBottom, pos.x()) y = self.invTransform(QwtPlot.yLeft, pos.y()) self.x[self.picked_id]=x self.y[self.picked_id]=y self.curve.setData(self.x, self.y) self.replot() def on_click(self, pos): if self.curve is not None: picked = self.curve.closestPoint(pos) if picked[1] > self.pick_tol: self.picked_id = None else: self.picked_id = picked[0] def on_release(self, pos): self.picked_id = None def setCurveData(self, title, x, y): self.curve = QwtPlotCurve(title) self.curve.setPen(QPen(Qt.black, 0)) self.curve.setSymbol(QwtSymbol(QwtSymbol.Ellipse, QBrush(Qt.red), QPen(Qt.red), QSize(10,10))) self.curve.setData(x,y) self.x = x self.y = y self.curve.attach(self) class MultiPlotWidget(QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent) self.setWindowTitle("multiple graphs") hlayout = QHBoxLayout() self.setLayout(hlayout) #data creation x = np.linspace(-2*pi, 2*pi, 100) y1 = np.cos(x) #create plots self.plot1 = PlotWidget("plot1") self.plot1.setCurveData("y=cos(x)", x, y1) #add plots to the main widget self.layout().addWidget(self.plot1) def main(): app = QApplication(sys.argv) plot = MultiPlotWidget() plot.setMinimumSize(500,300) plot.show() sys.exit(app.exec_()) if __name__ == "__main__": main()