【问题标题】:How do I use Qt's "Graphics View" framework from python?如何从 python 中使用 Qt 的“图形视图”框架?
【发布时间】:2019-11-18 13:50:15
【问题描述】:

背景:我正在尝试使用 Qt Graphics View 框架在 python 中制作 VR 演示,该框架允许自定义呈现的 GUI。我可以选择包装 Qt 的两个不同的 Python 模块:PyQt5PySide2。不幸的是,这些模块中的每一个似乎都缺少图形视图框架中的不同关键要素。

PyQt5 似乎缺少QGraphicsSceneMouseEvent 的构造函数,这是从 VR 控制器手势创建合成鼠标事件所必需的。这个简短的 PyQt5 程序...

from PyQt5.QtCore import QEvent, QPointF, Qt
from PyQt5.QtGui import QMouseEvent
from PyQt5.QtWidgets import QGraphicsSceneMouseEvent
from PyQt5.QtGui import QOpenGLPaintDevice  # No problem for PyQt5

pos = QPointF(20, 20)
event1 = QMouseEvent(QEvent.MouseMove, pos, Qt.LeftButton, Qt.LeftButton, Qt.NoModifier)
device = QOpenGLPaintDevice(100, 100)

# Problem: TypeError: PyQt5.QtWidgets.QGraphicsSceneMouseEvent cannot be instantiated or sub-classed
event2 = QGraphicsSceneMouseEvent(QEvent.GraphicsSceneMouseMove)

...结果为TypeError: PyQt5.QtWidgets.QGraphicsSceneMouseEvent cannot be instantiated or sub-classed

看似好消息是替代Qt绑定PySide2模块可以毫无怨言地构造QGraphicsSceneMouseEvent。但是PySide2 缺少 QOpenGLPaintDevice 类,我需要它来实际绘制小部件。这个非常相似的程序,它使用 PySide2...

from PySide2.QtCore import QEvent, QPointF, Qt
from PySide2.QtGui import QMouseEvent
from PySide2.QtWidgets import QGraphicsSceneMouseEvent

pos = QPointF(20, 20)
event1 = QMouseEvent(QEvent.MouseMove, pos, Qt.LeftButton, Qt.LeftButton, Qt.NoModifier)
event2 = QGraphicsSceneMouseEvent(QEvent.GraphicsSceneMouseMove)  # No Problem for PySide2

from PySide2.QtGui import QOpenGLPaintDevice  # Problem: PySide2 does not have this class
device = QOpenGLPaintDevice(100, 100)

结果为@​​987654331@

我不确定是否有人从 python 中成功使用过 Qt Graphics View 框架。如果没有,我想成为第一个。

【问题讨论】:

    标签: python pyqt pyqt5 qgraphicsview pyside2


    【解决方案1】:

    在我看来,在 PyQt5 中未启用 QGraphicsSceneMouseEvent 并不是一个错误,而是在 PySide2 中启用了一个错误,因为如果您查看the docs,会发现 QGraphicsSceneMouseEvent 没有公共构造函数。

    如果您想在 Qt 图形框架中模拟鼠标移动,则必须将 QMouseEvent 发送到 QGraphicsView 的viewport()

    在下面的示例中,我展示了如何通过使用 QMouseEvent 模拟鼠标来移动项目:

    from functools import partial
    from PySide2 import QtCore, QtGui, QtWidgets
    # from PyQt5 import QtCore, QtGui, QtWidgets
    
    
    class MainWindow(QtWidgets.QMainWindow):
        def __init__(self, parent=None):
            super(MainWindow, self).__init__(parent)
            self.m_scene = QtWidgets.QGraphicsScene(QtCore.QRectF(0, 0, 400, 400), self)
            self.m_graphicsview = QtWidgets.QGraphicsView(self.m_scene)
            self.setCentralWidget(self.m_graphicsview)
            self.resize(640, 480)
    
            self.m_item = QtWidgets.QGraphicsRectItem(QtCore.QRectF(-50, -50, 100, 100))
            self.m_item.setFlags(
                self.m_item.flags() | QtWidgets.QGraphicsItem.ItemIsMovable
            )
            self.m_item.setBrush(QtGui.QColor("salmon"))
            self.m_item.setPos(100, 100)
            self.m_scene.addItem(self.m_item)
    
            QtCore.QTimer.singleShot(1000, self.emulate_move_item)
    
        def emulate_move_item(self):
            sp = self.m_item.mapToScene(self.m_item.boundingRect().center())
            lp = self.m_graphicsview.mapFromScene(sp)
            end_pos = lp + QtCore.QPoint(100, 100)
    
            self.press(lp)
    
            animation = QtCore.QVariantAnimation(
                self,
                startValue=lp,
                endValue=end_pos
            )
            animation.valueChanged.connect(self.moveTo)
            animation.finished.connect(partial(self.release, end_pos))
            animation.start(QtCore.QAbstractAnimation.DeleteWhenStopped)
    
        def press(self, pos):
            event = QtGui.QMouseEvent(
                QtCore.QEvent.MouseButtonPress,
                pos,
                self.m_graphicsview.mapToGlobal(pos),
                QtCore.Qt.LeftButton,
                QtCore.Qt.LeftButton,
                QtCore.Qt.NoModifier,
            )
            QtCore.QCoreApplication.postEvent(self.m_graphicsview.viewport(), event)
    
        def moveTo(self, pos):
            event = QtGui.QMouseEvent(
                QtCore.QEvent.MouseMove,
                pos,
                self.m_graphicsview.viewport().mapToGlobal(pos),
                QtCore.Qt.LeftButton,
                QtCore.Qt.LeftButton,
                QtCore.Qt.NoModifier,
            )
            QtCore.QCoreApplication.postEvent(self.m_graphicsview.viewport(), event)
    
        def release(self, pos):
            event = QtGui.QMouseEvent(
                QtCore.QEvent.MouseButtonRelease,
                pos,
                self.m_graphicsview.viewport().mapToGlobal(pos),
                QtCore.Qt.LeftButton,
                QtCore.Qt.LeftButton,
                QtCore.Qt.NoModifier,
            )
            QtCore.QCoreApplication.postEvent(self.m_graphicsview.viewport(), event)
    
        def double_click(self, pos):
            event = QtGui.QMouseEvent(
                QtCore.QEvent.MouseButtonDblClick,
                pos,
                self.m_graphicsview.viewport().mapToGlobal(pos),
                QtCore.Qt.LeftButton,
                QtCore.Qt.LeftButton,
                QtCore.Qt.NoModifier,
            )
            QtCore.QCoreApplication.postEvent(self.m_graphicsview.viewport(), event)
    
    
    if __name__ == "__main__":
        import sys
    
        app = QtWidgets.QApplication(sys.argv)
        w = MainWindow()
        w.show()
        sys.exit(app.exec_())
    

    总结:

    • 不能在 python 中启用 QGraphicsSceneMouseEvent 构造函数,因为它不能在 C++ 中的 Qt 的公共 API 中访问。所以PyQt5的行为是正确的,但是PySide2有bug。

    • PySide2 没有实现 QOpenGLPaintDevice 类是一个可能的错误。

    【讨论】:

    • QGraphicsSceneMouseEvent 构造函数在我的 C++ 版本的演示中工作,所以它的构造函数必须是公共的。还是您对“公共”一词的使用方式与我不同?
    • 谢谢你的例子。我可能需要几天的时间才能完全评估它。
    • @ChristopherBruns 你可以给我看你所指的 C++ 代码,我已经查看了文档:i.imgur.com/Y8NzpsK.png,我没有看到构造函数方法,所以我认为它是一个私有构造函数。你了解 C++ 中公共、保护和私有的概念吗?绑定只公开公共和受保护的方法。
    • @ellyanesc QGraphicsSceneMouseEvent 构造函数的公共声明:github.com/qt/qtbase/blob/… 使用构造函数的工作 C++ 代码:github.com/ValveSoftware/openvr/blob/master/samples/…
    • @ChristopherBruns 我觉得很奇怪,可能是文档中的错误,我要进行调查。
    猜你喜欢
    • 2011-02-13
    • 2011-03-12
    • 2011-05-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多