Python 在PySide中嵌入交互式3D绘图

Python 在PySide中嵌入交互式3D绘图,python,matplotlib,pyside,Python,Matplotlib,Pyside,在PySide GUI中嵌入交互式3D绘图的最佳方式是什么?我在这里看到了一些嵌入PySide GUI中的2D绘图示例: 然而,我正在寻找的功能并不完全相同。图形需要根据用户的鼠标输入进行旋转和缩放,就像在单独的窗口中绘制一样 我试图避免手动编写将鼠标单击+移动转换为图形旋转和画布重新绘制的函数——即使这是唯一的方法,我甚至不知道如何做到这一点。但我认为(并非双关语)应该有一种方法可以重用已经存在的功能,在他们自己的窗口中创建3D绘图 这是我的密码。它按预期工作,但情节不是交互式的。任何建

在PySide GUI中嵌入交互式3D绘图的最佳方式是什么?我在这里看到了一些嵌入PySide GUI中的2D绘图示例:



然而,我正在寻找的功能并不完全相同。图形需要根据用户的鼠标输入进行旋转和缩放,就像在单独的窗口中绘制一样

我试图避免手动编写将鼠标单击+移动转换为图形旋转和画布重新绘制的函数——即使这是唯一的方法,我甚至不知道如何做到这一点。但我认为(并非双关语)应该有一种方法可以重用已经存在的功能,在他们自己的窗口中创建3D绘图

这是我的密码。它按预期工作,但情节不是交互式的。任何建议都将不胜感激

编辑:我根据的更正修复了FigureCanvas的使用。我还从matplotlib文档中添加了一些内容,以显示该图似乎是在鼠标单击时获取事件的

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

# -*- 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
右侧:

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)在添加轴之前创建FigureCanvas。看

2) 尝试ax.mouse_init()还原连接:

...
ax = fig.gca(projection="3d")
...
canvas = FigureCanvas(fig)
ax.mouse_init()

你看过维普顿吗?我知道它能满足您的需求,但我以前从未尝试过将其嵌入PySide GUI。是的,我尝试过。我对它的力量和简单印象深刻——五分钟,我有一个球在重力作用下上下弹跳的动画。但是,在进一步研究之后,我找不到任何有人尝试将其嵌入PySide的案例。请检查
qt4agg
后端代码是如何做到这一点的。因此,基本上我得到的是,后端是为渲染目的而设计的,而不是为输入而设计的。因此,获得我想要的功能的唯一方法是点击绘图的父窗口小部件,定义处理鼠标单击+移动事件的函数,并根据这些事件重新绘制画布。听起来对吗?还是我仍然缺少它?后端
qt4agg
只是
matplotlib
嵌入到
Qt
中。它可以做你想要的交互式工作->它可以在
Qt
中或多或少地开箱即用。旋转是通过
mpl
回拨系统完成的,您应该能够在不接触
Qt
层的情况下完成。谢谢,伙计。我感谢你的帮助。编辑原始帖子以包含您的更改;它现在正在打印鼠标单击事件的信息,以显示事件确实已注册。但是,它似乎不支持旋转和缩小方框。这是你期望的吗?还是我又把事情搞砸了?如果这解决了您在OP中提出的问题,您应该接受答案(左侧的灰色大复选框)。如果你发现了新问题,你应该问一个新问题。而打印是因为你点击了你注册的
功能。取下它,打印就会消失away@Bryant我怀疑你点击时的
会干扰事件,这样轮换代码就不会因为错误的沟通而感到担忧——我把片段打印出来,然后点击
事件,显示确实发生了一些事情,因为它仍然没有旋转。我不认为它窃取了事件,因为在我添加连接之前它不会旋转。它确实解决了一个问题,但它仍然不会旋转,所以它并没有完全回答这个问题:/如果我发现了什么,我会发帖的!再次感谢你的帮助。
class MyFigureCanvas(FigureCanvas):
    def __init__(self):
        self.figure = Figure()
        super(FigureCanvas, self).__init__(self.figure)
        self.axes = self.figure.add_subplot(111, projection='3d')
...
ax = fig.gca(projection="3d")
...
canvas = FigureCanvas(fig)
ax.mouse_init()