63870
matplotlibとPyQt5を使って3dグラフが表示されるGUIを作りたいです。

その際、x、y軸の比率を揃えたいです。ここではx、y軸の比率を見やすくするためにグラフを上から見た様子が表示されるようにしています。set_aspectを使って書いて見ましたが、比率が揃いません。

どのようにすればよいのでしょうか。

* * *

**表示されるGUI(本当は軸の表示範囲に則って、y軸がx軸の2倍の長さになるようにしたい)**  
[![画像](https://i.stack.imgur.com/gvQMx.png)](https://i.stack.imgur.com/gvQMx.png)

**ソースコード**

```

    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
    #from PyQt5.QtCore import Qt, QTimer, QPoint
    #from PyQt5.QtGui import QCursor
    
    
    class Main(QDialog):
        def __init__(self, parent=None):
            super(Main, self).__init__(parent)
    
            self.figure = plt.figure()
            self.canvas = FigureCanvas(self.figure)
            self.axes = Axes3D(self.figure)
            self.axes.set_aspect("equal")
    
            self.plot()      
            self.change_view_int(90,0)
    
            layout1=QVBoxLayout()
            layout1.addWidget(self.canvas)
            self.setLayout(layout1)
    
            self.show()
    
        def plot(self):
            x = np.arange(-3, 3, 0.25)
            y = np.arange(-6, 6, 0.25)
            X, Y = np.meshgrid(x, y)
            Z = np.sin(X)+ np.cos(Y)
    
            self.axes.plot_surface(X,Y,Z,cmap='jet')
            self.axes.set_xlabel("x")
            self.axes.set_ylabel("y")
            self.axes.set_zlabel("z")
    
            self.canvas.draw()
    
        def change_view_int(self,elev,azim):
            self.axes.view_init(elev,azim)
            self.canvas.draw()
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        win = Main()
        app.exec_()
    
```
CC BY-SA 4.0
2020-03-15T07:27:17.690
63859
2020-03-16T04:13:46.330
26529
question
python pyqt5
PyQt5で3dグラフを描くときにxy軸のアスペクト比を揃えたい
以下の記事に情報が載っていました。

[[matplotlib 3D] 50.
3Dグラフにおけるアスペクト比の設定](https://sabopy.com/py/matplotlib-3d-50/)

> matplotlib
> mplot3dで作成する3Dグラフでアスペクト比を揃えるために、ax.set_aspect(‘equal’)とすると、NotImplementedErrorが出るようになった(matplotlib
> 3.1.0から)。下記サイトにその理由が書いてある。

**元々のIssue** とそれからの抜粋  
[3d plots with aspect='equal'
#1077](https://github.com/matplotlib/matplotlib/issues/1077/)

> `aspect='equal'` argument is completely misused by axes3D.
>
> `aspect='equal'`引数はaxes3Dによって完全に誤用されています。
>
> an "aspect" kwarg doesn't make a lot of sense for 3d plots, which is why
> even matlab doesn't do it.
>
> 「アスペクト」のkwargは3Dプロットではあまり意味をなさないため、matlabでもそれができません。
>
> Actually there are several ways of showing a 3D model on a 2D screen, mainly
> including axonometric projection and perspective projection. There are also
> many kinds of axonometric projections. So even the unit length of three
> axises are the same, they look different on the screen on many
> circumstances. A cubic box may look not that cubic if drawn in different
> ways. So if the 3D plot class is to be improved, projection method should be
> determined beforehand.
>
>
> 実際、2Dスクリーンに3Dモデルを表示する方法はいくつかあり、主に軸測投影と透視投影が含まれます。さまざまな種類の不等角投影もあります。そのため、3つの軸の単位長が同じでも、多くの状況で画面上で異なって見えます。立方体ボックスは、さまざまな方法で描かれた場合、その立方体ではないように見える場合があります。したがって、3Dプロットクラスを改善する場合は、事前に投影方法を決定する必要があります。

等々

* * *

ということで、以下2点の対処を行うことでアスペクト比の制御を行えるようです。

  * matplotlib 3.1.0 以上を使う
  * `plt.figure()`呼び出し時に`figsize=plt.figaspect()`でアスペクト比を指定する

質問のソースで言えば、`self.figure = plt.figure()`を`self.figure =
plt.figure(figsize=plt.figaspect(0.5))`に変更し、`self.axes.set_aspect("equal")`をコメントアウトすることで以下の図になりました。  
使ったものの版数はそれぞれ Windows10, Python 64bit 3.7.6, PyQt5 5.14.1, matplotlib 3.2.0,
numpy 1.18.1 になります。

[![正方形アスペクト比画像](https://i.stack.imgur.com/aYXwk.png)](https://i.stack.imgur.com/aYXwk.png)
CC BY-SA 4.0
2020-03-15T16:23:06.647
63870
2020-03-16T00:42:55.507
2020-06-17T08:14:45.997
-1
26370
63859
answer
x、y軸の長さが異なると見る角度によっては表示される図形が歪んで見えてしまうようです。以下のコードの様にx、y軸の長さを大きい方に合わせるのが無難なようです。

```

    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
    #from PyQt5.QtCore import Qt, QTimer, QPoint
    #from PyQt5.QtGui import QCursor
    
    
    class Main(QDialog):
        def __init__(self, parent=None):
            super(Main, self).__init__(parent)
    
            #self.figure = plt.figure()
            self.figure = plt.figure(figsize=plt.figaspect(1))
            self.canvas = FigureCanvas(self.figure)
            self.axes = Axes3D(self.figure)
            #self.axes.set_aspect("equal")
    
            self.plot()      
            self.change_view_int(90,0)
    
            layout1=QVBoxLayout()
            layout1.addWidget(self.canvas)
            self.setLayout(layout1)
    
            self.show()
    
        def plot(self):
            x = np.arange(-3, 3, 0.25)
            y = np.arange(-6, 6, 0.25)
            X, Y = np.meshgrid(x, y)
            Z = np.sin(X)+ np.cos(Y)
    
            self.axes.plot_surface(X,Y,Z,cmap='jet')
            self.axes.set_xlabel("x")
            self.axes.set_ylabel("y")
            self.axes.set_zlabel("z")
            self.axes.auto_scale_xyz([-6,6], [-6, 6],[-2,2])
    
            self.canvas.draw()
    
        def change_view_int(self,elev,azim):
            self.axes.view_init(elev,azim)
            self.canvas.draw()
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        win = Main()
        app.exec_()
    
```

[![画像](https://i.stack.imgur.com/NJAme.png)](https://i.stack.imgur.com/NJAme.png)
CC BY-SA 4.0
2020-03-16T04:13:46.330
63882
2020-03-16T04:13:46.330
26529
63859
answer