2012年7月26日木曜日

PyQtでグラフ 完了

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()


0 件のコメント:

コメントを投稿