FrontPage / Python / PySide / plotter.py

[[FrontPage/Python/PySide]]


https://gist.github.com/peace098beat/c61a781712cab34f05ae

 class Plotter(QtGui.QWidget):

    def __init__(self, parent=None):
        super(Plotter, self).__init__(parent)

        ##########################################
        # member
        ##########################################
        self.Margin = 50
        self.curveMap = {}  # QMap<int, QVector<QPointF>>
        self.zoomStack = []  # QVector<PlotSettings>
        self.curZoom = 0  # int
        self.rubberBandIsShown = True
        self.rubberBandRect = QtCore.QRect()
        self.pixmap = QtGui.QPixmap()

        ##########################################
        # Widget初期設定
        ##########################################
        # self.setBackgroundRole(QtGui.QPalette.Dark)
        self.setBackgroundRole(QtGui.QPalette.Shadow)
        self.setAutoFillBackground(True)
        self.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
        self.setFocusPolicy(QtCore.Qt.StrongFocus)
        self.rubberBandIsShown = False
        ##########################################
        # Setup UI
        ##########################################
        # Zoom In Button セッティング
        self.zoomInButton = QtGui.QToolButton(self)
        self.zoomInButton.setIcon(QtGui.QIcon(":/images/zoomin.png"))
        self.zoomInButton.adjustSize()
        self.connect(self.zoomInButton, QtCore.SIGNAL("clicked()"), self, QtCore.SLOT("zoomIn()"))

        # Zoom Out Button セッティング
        self.zoomOutButton = QtGui.QToolButton(self)
        self.zoomOutButton.setIcon(QtGui.QIcon(":/images/zoomout.png"))
        self.zoomOutButton.adjustSize()
        self.connect(self.zoomOutButton, QtCore.SIGNAL("clicked()"), self, QtCore.SLOT("zoomOut()"))

        # 描画設定
        iniSetting = PlotSettings()
        self.setPlotSettings(iniSetting)


    def setPlotSettings(self, settings):
        self.zoomStack = []
        self.zoomStack.append(settings)
        self.curZoom = 0
        self.zoomInButton.hide()
        self.zoomOutButton.hide()
        self.refreshPixmap()
        pass

    def setCurveData(self, id, data):
        # 辞書要素に追加
        self.curveMap[id] = data
        self.refreshPixmap()
        print self.curveMap
        pass

    def clearCurve(self, id):
        ''' 曲線データを削除する
        id:曲線データのインデックス
        '''
        if self.curveMap.has_key(id):
            self.curveMap.pop(id)
        self.refreshPixmap()
        print self.curveMap
        pass

    def minimumSizeHint(self):
        '''ウィジェットの理想的な最小サイズ
        '''
        return QtCore.QSize( 6 * self.Margin, 4 * self.Margin)
        pass

    def sizeHint(self):
        '''ウィジェットの理想的なサイズ
        '''
        return QtCore.QSize( 12 * self.Margin, 8 * self.Margin)
        pass

    ##########################################
    # Slot
    ##########################################
    @QtCore.Slot()
    def zoomIn(self):
        print 'Call zoomIn()'
        if (self.curZoom < len(self.zoomStack) - 1 ):
            self.curZoom = self.curZoom + 1
            self.zoomInButton.setEnabled(self.curZoom < len(self.zoomStack) - 1)
            self.zoomOutButton.setEnabled(True)
            self.zoomOutButton.show()
            self.refreshPixmap()
        pass

    @QtCore.Slot()
    def zoomOut(self):
        print 'Call zoomOut()'
        if (self.curZoom > 0):
            self.curZoom = self.curZoom - 1
            self.zoomOutButton.setEnabled(self.curZoom > 0)
            self.zoomInButton.setEnabled(True)
            self.zoomInButton.show()
            self.refreshPixmap()
        pass

    ##########################################
    # protected
    # 再実装の必要なQWidgetのイベントハンドラを宣言
    ##########################################
    def paintEvent(self, event):
        print 'paintEvent()'
        # 1. イメージ(グラフ)の描画
        painter = QtGui.QStylePainter(self)
        painter.drawPixmap(0,0,self.pixmap)

        # 2. ラバーバンドの描画
        if self.rubberBandIsShown:
            print 'draw to rubberBand'
            # ライトカラーの指定(QStyleに依存している)
            painter.setPen(self.palette().light().color())
            painter.setPen(QtCore.Qt.red)
            painter.drawRect(self.rubberBandRect.normalized().adjusted(0,0,-1,-1))

        # 3. ウィジェットがフォーカスされているときの色処理
        if self.hasFocus():
            option = QtGui.QStyleOptionFocusRect()
            option.initFrom(self)
            option.backgroundColor = self.palette().dark().color()
            option.backgroundColor = self.palette().shadow().color()
            painter.drawPrimitive(QtGui.QStyle.PE_FrameFocusRect, option)
        pass

    def resizeEvent(self, event):
        x = self.width() - (self.zoomInButton.width() + self.zoomOutButton.width() + 10)
        self.zoomInButton.move(x, 5)
        self.zoomOutButton.move(x + self.zoomInButton.width() + 5, 5)
        self.refreshPixmap()
        pass

    def mousePressEvent(self, event):
        rect = QtCore.QRect(self.Margin, self.Margin,
                            self.width()-2*self.Margin,
                            self.height()-2*self.Margin)
        if event.button() == QtCore.Qt.LeftButton:
            print 'push left button'
            if rect.contains(event.pos()):
                self.rubberBandIsShown = True
                self.rubberBandRect.setTopLeft(event.pos())
                self.rubberBandRect.setBottomRight(event.pos())
                self.updateRubberBandRegion()
                self.setCursor(QtCore.Qt.CrossCursor)
        pass

    def mouseMoveEnvent(self, event):
        if self.rubberBandIsShown:
            self.updateRubberBandRegion()
            self.rubberBandRect.setBottomRight(event.pos())
            self.updateRubberBandRegion()
        pass

    def mouseReleaseEvent(self, event):
        if (event.button() == QtCore.Qt.LeftButton) and self.rubberBandIsShown:
            self.rubberBandIsShown = False
            self.updateRubberBandRegion()
            self.unsetCursor()

            rect = self.rubberBandRect.normalized()
            if rect.width()<4 or rect.height()<4:
                return
            rect.translate(-self.Margin, -self.Margin)

            prevSettings = self.zoomStack[self.curZoom]
            settings = PlotSettings()
            dx = prevSettings.spanX() / (self.width() - 2*self.Margin)
            dy = prevSettings.spanY() / (self.height() - 2*self.Margin)
            settings.minX = prevSettings.minX + dx * rect.left()
            settings.maxX = prevSettings.maxX + dx * rect.right()
            settings.minY = prevSettings.minY - dy * rect.bottom()
            settings.maxY = prevSettings.maxY - dy * rect.top()
            settings.adjust()

            self.zoomStack.append(settings)
            self.zoomIn()
        pass

    def keyPressEvent(self, event):
        e =event.key()
        if e == QtCore.Qt.Key_Plus:
            self.zoomIn()
        elif e == QtCore.Qt.Key_Minus:
            self.zoomOut()
        elif e == QtCore.Qt.Key_Left:
            self.zoomStack[self.curZoom].scroll(-1, 0)
            self.refreshPixmap()
        elif e == QtCore.Qt.Key_Right:
            self.zoomStack[self.curZoom].scroll(+1, 0)
            self.refreshPixmap()
        elif e == QtCore.Qt.Key_Down:
            self.zoomStack[self.curZoom].scroll(0, -1)
            self.refreshPixmap()
        elif e == QtCore.Qt.Key_Up:
            self.zoomStack[self.curZoom].scroll(0, +1)
            self.refreshPixmap()
        else:
            QtGui.QWidget.keyPressEvent(event)

    def wheelEvent(self, event):
        pass



    ##########################################
    # private
    # 描画に必要な関数、定数、メンバ変数を宣言
    ##########################################
    def updateRubberBandRegion(self):
        print 'updateRubberBandRegion'
        rect = self.rubberBandRect.normalized()
        self.update(rect.left(), rect.top(), rect.width(), 1)
        self.update(rect.left(), rect.top(), 1, rect.height())
        self.update(rect.left(), rect.bottom(), rect.width(), 1)
        self.update(rect.right(), rect.top(), 1, rect.height())
        pass

    def refreshPixmap(self):
        '''オフスクリーンピックスマップ上に曲線をプロットし、画面を更新する
        '''
        self.pixmap = QtGui.QPixmap(self.size())
        self.pixmap.fill(self, 0, 0)

        painter = QtGui.QPainter(self.pixmap)
        painter.initFrom(self)
        self.drawGrid(painter)
        self.drawCurves(painter)
        self.update()
        pass

    def drawGrid(self, painter):
        rect = QtCore.QRect(self.Margin,
                            self.Margin,
                            self.width() - 2*self.Margin,
                            self.height() - 2*self.Margin)
        if not rect.isValid():
            return

        # グラフプロットの設定
        settings = self.zoomStack[self.curZoom]
        # ペンの色の指定
        # quiteDark = self.palette().dark().color()
        quiteDark = self.palette().dark().color()
        light = self.palette().light().color()

        # 横軸の目盛線の描画
        for i in range(settings.numXTicks+1):
            x = rect.left() + (i * (rect.width() - 1)/settings.numXTicks)
            label = settings.minX + (i * settings.spanX()/settings.numXTicks)
            painter.setPen(quiteDark)
            painter.drawLine(x, rect.top(), x, rect.bottom())
            painter.setPen(light)
            painter.drawLine(x, rect.bottom(), x, rect.bottom() + 5)
            painter.drawText(x-50, rect.bottom()+5, 100, 15,
                             QtCore.Qt.AlignCenter|QtCore.Qt.AlignTop,
                             unicode('%0.2f' % label))

        # 縦軸の目盛線の描画
        for j in range(settings.numYTicks+1):
            y = rect.bottom() - (j * (rect.height() - 1)/settings.numYTicks)
            label = settings.minY + (j * settings.spanY()/settings.numYTicks)
            painter.setPen(quiteDark)
            painter.drawLine(rect.left(), y, rect.right(), y)
            painter.setPen(light)
            painter.drawLine(rect.left()-5, y, rect.left(), y)
            painter.drawText(rect.left()-self.Margin, y-10, self.Margin-5, 20,
                             QtCore.Qt.AlignRight|QtCore.Qt.AlignVCenter,
                             unicode('%0.2f' % label))
        painter.drawRect(rect.adjusted(0,0,-1,-1))

    def drawCurves(self, painter):
        '''グリッドの上に曲線を描画する'''
        colorForIds = [QtCore.Qt.red, QtCore.Qt.green, QtCore.Qt.blue, QtCore.Qt.cyan, QtCore.Qt.magenta, QtCore.Qt.yellow]
        settings = self.zoomStack[self.curZoom]
        rect = QtCore.QRect(self.Margin, self.Margin, self.width()-2*self.Margin, self.height()-2*self.Margin)
        if not rect.isValid():
            return

        # 再描画する範囲を、曲線が含まれる矩形内のみに指定
        painter.setClipRect(rect.adjusted(+1, +1, -1, -1))

        for id, data in self.curveMap.iteritems():
            polyline = QtGui.QPolygonF(data.length())
            for j in range(data.length()):
                dx = data.x[j] - settings.minX
                dy = data.y[j] - settings.minY
                x = rect.left() + (dx * (rect.width()-1)/settings.spanX())
                y = rect.bottom() - (dy * (rect.height()-1)/settings.spanY())
                polyline.append(QtCore.QPointF(x,y))

            painter.setPen( colorForIds[id % 6] )
            painter.drawPolyline(polyline)

        pass
トップ   編集 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS