45088


pyqt5で描いた3次元グラフを、マウスでドラッグしたときに動くようにしたいです。下は試しに書いたグラフです。

[code]

    import sys
    from PyQt5.QtWidgets import QDialog, QApplication, QVBoxLayout
    
    
    from matplotlib.figure import Figure
    import numpy as np
    import matplotlib.pyplot as plt
    from PIL import Image, ImageOps
    from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
    from matplotlib.backends.backend_qt5 import NavigationToolbar2QT as NavigationToolbar
    from mpl_toolkits.mplot3d import Axes3D
    
    class Main(QDialog):
        def __init__(self, parent=None):
            super(Main, self).__init__(parent)
    
            x = np.arange(-3, 3, 0.25)
            y = np.arange(-3, 3, 0.25)
            X, Y = np.meshgrid(x, y)
            Z = np.sin(X)+ np.cos(Y)
    
            self.figure1 = plt.figure()
            #self.axes1 = self.figure1.add_subplot(111, projection='3d')
            self.axes1 = self.figure1.gca(projection="3d")
            self.canvas1 = FigureCanvas(self.figure1)
            self.canvas1.setFixedSize(600,450)
            self.toolbar1 = NavigationToolbar(self.canvas1, self)
    
            self.axes1.plot_surface(X,Y,Z,cmap='jet')
    
            layout1=QVBoxLayout()
            layout1.addWidget(self.toolbar1)
            layout1.addWidget(self.canvas1)
            self.setLayout(layout1)
    
            self.show()
    
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        win = Main()
        sys.exit(app.exec_())
    
[/code]

これを実行すると次のようなGUIが出来ます。  
[![画像](https://i.stack.imgur.com/NVdHC.png)](https://i.stack.imgur.com/NVdHC.png)

しかし、これではグラフをドラッグしてもグラフの視点は変わりませんでした。ここでグラフの視点が変化するように表示することは出来るのでしょうか。


CC BY-SA 4.0
2018-06-26T10:33:36.267
45087
2018-06-27T03:01:54.583
2018-06-26T14:02:23.590
19110
26529
question
python pyqt pyqt5
pyqt5で描いた3次元グラフの視点をマウスで動かしたい


[QTimer](http://pyqt.sourceforge.net/Docs/PyQt5/api/qtimer.html)
で定期的に関数を呼び出し、[QCursor](http://pyqt.sourceforge.net/Docs/PyQt5/api/qcursor.html)
から得た位置情報を元にグラフを回転させ、再描画すれば出来ました。グラフの回転には [`view_init(elevation,
azimuth)`](https://matplotlib.org/api/_as_gen/mpl_toolkits.mplot3d.axes3d.Axes3D.html?highlight=view_init#mpl_toolkits.mplot3d.axes3d.Axes3D.view_init)
が使えます。マウスがクリックされたかどうかは、FigureCanvas を
[`mpl_connect()`](https://matplotlib.org/users/event_handling.html) しておけば
`mousePressEvent` や `mouseReleaseEvent` で検知できます。

具体的には以下のようにします。まずいくつか追加で `import` します。

[code]

    from PyQt5.QtCore import Qt, QTimer, QPoint
    from PyQt5.QtGui import QCursor
    
[/code]

質問文のソースコードの `__init__()` にタイマーを起動するコードを加えます。

[code]

    self.timer = QTimer(self)
    self.timer.timeout.connect(self.update)
    self.timer.start(100)  # ここが更新速度 (ミリ秒) になります
    
[/code]

更に `__init__()` で `mpl_connect()` すると共にドラッグ用の変数を初期化しておきます。

[code]

    self.canvas1.mpl_connect('button_press_event', self.mousePressEvent)
    self.canvas1.mpl_connect('button_release_event', self.mouseReleaseEvent)
    
    self.startCursorPos = QPoint(0, 0)
    self.isDragging = False
    self.elev = 0
    self.azim = 0
    
[/code]

マウスが左クリックされているか検知するためのメソッドを用意し、

[code]

    def mousePressEvent(self, event):
        if event.button == Qt.LeftButton:
            self.startCursorPos = QCursor.pos()
            self.isDragging = True
    
    def mouseReleaseEvent(self, event):
        if event.button == Qt.LeftButton:
            diff = QCursor.pos() - self.startCursorPos
            self.elev += diff.y()
            self.azim -= diff.x()
            self.update()
            self.isDragging = False
    
[/code]

最後に、再描画するメソッドを定義しておきます。

[code]

    def paintEvent(self, event):
        if self.isDragging:
            diff = QCursor.pos() - self.startCursorPos
            self.axes1.view_init(self.elev + diff.y(), self.azim - diff.x())
            self.canvas1.draw()
    
[/code]

とりあえずこれでグラフが回るようになります。

## 参考

  * [how to set “camera position” for 3d plots using python/matplotlib?](https://stackoverflow.com/q/12904912/5989200) \-- Stack Overflow
  * [mplot3d example code: rotate_axes3d_demo.py](https://matplotlib.org/2.0.2/examples/mplot3d/rotate_axes3d_demo.html) \-- matplotlib
  * [Qt 5, get the mouse position in a screen](https://stackoverflow.com/q/32040202/5989200) \-- Stack Overflow


CC BY-SA 4.0
2018-06-26T12:08:41.637
45088
2018-06-27T03:01:54.583
2018-06-27T03:01:54.583
19110
19110
45087
answer