关于python:在PySide中嵌入交互式3D绘图

Embed an interactive 3D plot in PySide

在Pyside GUI中嵌入交互式3D绘图的最佳方法是什么?我看了一些嵌入在Pyside GUI中的二维图的示例:

让Pyside与Matplotlib合作嵌入PYQT的Matplotlib交互图python/matplotlib/pyside快速时间跟踪滚动

但是,我所寻找的功能并不完全相同。图形需要根据用户的鼠标输入进行旋转和缩放,方式与在单独窗口中绘制图形的方式相同。

我尽量避免手动输入和编写将鼠标点击+移动转换为图形旋转和画布重新绘制的函数——即使这是唯一的方法,我甚至不知道该怎么做。但我认为(没有双关语)应该有一种方法可以重用已经存在的功能,在自己的窗口中创建三维绘图。

这是我的密码。它按预期工作,但情节不是交互式的。感谢您的任何建议!

编辑:我根据tcaswell的修正修正了figureCanvas的使用。我还从Matplotlib事件处理和选择文档中添加了一些内容,以显示这个数字似乎是在鼠标单击时获取事件。

最终编辑:下面的代码现在根据需要生成绘图。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# -*- coding: utf-8 -*-

from PySide import QtCore, QtGui
import numpy as np

import matplotlib
import sys
# specify the use of PySide
matplotlib.rcParams['backend.qt4'] ="PySide"

# import the figure canvas for interfacing with the backend
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg \
                                                                as FigureCanvas

# import 3D plotting
from mpl_toolkits.mplot3d import Axes3D    # @UnusedImport
from matplotlib.figure import Figure


# Auto-generated code from QT Designer ----------------------------------------

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(750, 497)
        self.centralwidget = QtGui.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.horizontalLayout_2 = QtGui.QHBoxLayout(self.centralwidget)
        self.horizontalLayout_2.setObjectName("horizontalLayout_2")
        self.frame_2 = QtGui.QFrame(self.centralwidget)
        self.frame_2.setFrameShape(QtGui.QFrame.StyledPanel)
        self.frame_2.setFrameShadow(QtGui.QFrame.Raised)
        self.frame_2.setObjectName("frame_2")
        self.verticalLayout = QtGui.QVBoxLayout(self.frame_2)
        self.verticalLayout.setObjectName("verticalLayout")
        self.label = QtGui.QLabel(self.frame_2)
        self.label.setObjectName("label")
        self.verticalLayout.addWidget(self.label)
        self.label_2 = QtGui.QLabel(self.frame_2)
        self.label_2.setObjectName("label_2")
        self.verticalLayout.addWidget(self.label_2)
        self.lineEdit = QtGui.QLineEdit(self.frame_2)
        sizePolicy = QtGui.QSizePolicy(
                            QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
                                self.lineEdit.sizePolicy().hasHeightForWidth())
        self.lineEdit.setSizePolicy(sizePolicy)
        self.lineEdit.setObjectName("lineEdit")
        self.verticalLayout.addWidget(self.lineEdit)
        spacerItem = QtGui.QSpacerItem(
                20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
        self.verticalLayout.addItem(spacerItem)
        self.horizontalLayout_2.addWidget(self.frame_2)
        self.frame_plot = QtGui.QFrame(self.centralwidget)
        self.frame_plot.setMinimumSize(QtCore.QSize(500, 0))
        self.frame_plot.setFrameShape(QtGui.QFrame.StyledPanel)
        self.frame_plot.setFrameShadow(QtGui.QFrame.Raised)
        self.frame_plot.setObjectName("frame_plot")
        self.horizontalLayout_2.addWidget(self.frame_plot)
        MainWindow.setCentralWidget(self.centralwidget)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        MainWindow.setWindowTitle(QtGui.QApplication.translate(
           "MainWindow","MainWindow", None, QtGui.QApplication.UnicodeUTF8))
        self.label.setText(QtGui.QApplication.translate("MainWindow",
                   "This is a qlabel.", None, QtGui.QApplication.UnicodeUTF8))
        self.label_2.setText(QtGui.QApplication.translate("MainWindow",
           "And this is another one.", None, QtGui.QApplication.UnicodeUTF8))
        self.lineEdit.setText(QtGui.QApplication.translate("MainWindow",
                   "Text goes here.", None, QtGui.QApplication.UnicodeUTF8))

# Auto-generated code from QT Designer ----------------------------------------

class MainWindow(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        # intialize the window
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        # create the matplotlib widget and put it in the frame on the right
        self.ui.plotWidget = Mpwidget(parent=self.ui.frame_plot)

class Mpwidget(FigureCanvas):
    def __init__(self, parent=None):

        self.figure = Figure(facecolor=(0, 0, 0))
        super(Mpwidget, self).__init__(self.figure)
        self.setParent(parent)

        # plot random 3D data
        self.axes = self.figure.add_subplot(111, projection='3d')
        self.data = np.random.random((3, 100))
        self.axes.plot(self.data[0, :], self.data[1, :], self.data[2, :])

if __name__ =="__main__":
    app = QtGui.QApplication(sys.argv)
    mw = MainWindow()
    mw.show()

    # adjust the frame size so that it fits right after the window is shown
    s = mw.ui.frame_plot.size()
    mw.ui.plotWidget.setGeometry(1, 1, s.width() - 2, s.height() - 2)

    sys.exit(app.exec_())


你没有使用FigureCanvas对:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Mpwidget(FigureCanvas):
    def __init__(self, parent=None):
        self.figure = Figure(facecolor=(0, 0, 0))
        super(Mpwidget, self).__init__(self.figure) # this object _is_ your canvas
        self.setParent(parent)

        # plot random 3D data
        self.axes = self.figure.add_subplot(111, projection='3d')
        self.data = np.random.random((3, 100))
        self.axes.plot(self.data[0, :], self.data[1, :], self.data[2, :])

if __name__ =="__main__":
    app = QtGui.QApplication(sys.argv)
    mw = MainWindow()
    mw.show()

    # adjust the frame size so that it fits right after the window is shown
    s = mw.ui.frame_plot.size()
    mw.ui.plotWidget.setGeometry(1, 1, s.width() - 2, s.height() - 2)

    sys.exit(app.exec_())

每次你给FigureCanvas(..)打电话时,你都会把这个数字贴到一张新的画布上(你看到的不是FigureCanvas),所以回电都不会响(因为他们在听你看不见的FigureCanvas)。


1)在添加轴之前创建图形转换器。请参阅https://stackoverflow.com/a/9007892/3962328

1
2
canvas = FigureCanvas(fig)
ax = figure.add_subplot(111, projection='3d')

1
2
3
4
5
class MyFigureCanvas(FigureCanvas):
    def __init__(self):
        self.figure = Figure()
        super(FigureCanvas, self).__init__(self.figure)
        self.axes = self.figure.add_subplot(111, projection='3d')

2)尝试ax.mouse_init()恢复连接:

1
2
3
4
5
...
ax = fig.gca(projection="3d")
...
canvas = FigureCanvas(fig)
ax.mouse_init()