【问题标题】:setRotation method for QGraphicsSimpleTextItem() slow zommig and pannig interactionQGraphicsSimpleTextItem() 慢速缩放和平移交互的 setRotation 方法
【发布时间】:2020-10-25 12:12:28
【问题描述】:

我有一个图形用户界面,显示与每一行对齐的行和文本,我使用 QGraphicsSimpleTextItem() 处理文本。

问题:

如果没有在 QGraphicsSimpleTextItem() 上使用 Rotation 方法,则缩放和平移(QGraphcisView 子类)交互运行很快,但是如果为这些项目分配了 Rotation,则文本缩放和平移交互变得非常慢。

问题:

我有 Line Profiler 来查找在课堂上花费更多时间的行,但没有什么特别突出的,如下所示。有什么理由会发生这种情况吗?我该如何改进?

设置文字旋转(ang)

注释掉文本旋转线(ang)

每次点击时间并没有显示出很大的增加或减少,但是评论行 # dict_Text[str(i)].setRotation(ang[i]) 时有关缩放和平移交互的用户体验非常不同。

重现问题:

下面是重现我遇到的问题的代码,首先按原样运行代码,您将有一个非常缓慢的缩放和平移交互,然后注释掉 dict_Text[str(i)].setRotation(ang[ i]) 并且缩放和平移交互会非常快。

代码:

from PyQt5 import QtWidgets, QtCore, QtGui
import numpy as np
import sys
print(QtCore.PYQT_VERSION_STR)

class GraphicsView(QtWidgets.QGraphicsView):
    @profile
    def __init__(self, scene, parent):
        super(GraphicsView, self).__init__(scene, parent)

        #Mouse Tracking
        self.setMouseTracking(True)
        #Zoom Anchor
        self.setTransformationAnchor(QtWidgets.QGraphicsView.AnchorUnderMouse)
        self.setResizeAnchor(QtWidgets.QGraphicsView.AnchorUnderMouse)
        #Antialiasing and indexing
        self.setRenderHints(QtGui.QPainter.Antialiasing | QtGui.QPainter.HighQualityAntialiasing | QtGui.QPainter.TextAntialiasing)
        self.setCacheMode(QtWidgets.QGraphicsView.CacheBackground)
        self.resetCachedContent()
        scene.setItemIndexMethod(QtWidgets.QGraphicsScene.NoIndex)

        #Pan variable
        self.pos_init_class = None

    @profile
    def mousePressEvent(self, event):
        pos = self.mapToScene(event.pos())
        #Mouse Pan
        if event.button() == QtCore.Qt.MiddleButton:
            self.pos_init_class = pos
        super(GraphicsView, self).mousePressEvent(event)

    @profile
    def mouseReleaseEvent(self, event):
        if self.pos_init_class and event.button() == QtCore.Qt.MiddleButton:
            #Mouse Pan
            self.pos_init_class = None
        super(GraphicsView, self).mouseReleaseEvent(event)

    @profile
    def mouseMoveEvent(self, event):
        if self.pos_init_class:
            #Mouse Pan
            delta = self.pos_init_class - self.mapToScene(event.pos())
            r = self.mapToScene(self.viewport().rect()).boundingRect()
            self.setSceneRect(r.translated(delta))
        super(GraphicsView, self).mouseMoveEvent(event)

    @profile
    def wheelEvent(self, event):
        #Mouse Zoom
        if event.angleDelta().y() > 0:
            self.scale(1.5, 1.5)
        else:
            self.scale(1 / 1.5, 1 / 1.5)


class Ui_MainWindow(object):
    def __init__(self):
        super(Ui_MainWindow, self).__init__()


    def plt_plot(self):
        #Create data set
        size = 200
        x = np.random.randint(0, high=1000, size=size, dtype=int)
        y = np.random.randint(0, high=1000, size=size, dtype=int)
        ang = np.random.randint(1, high=360, size=size, dtype=int)

        #Store Text in Dict
        dict_Text = {}

        for i in range(len(x)):
            #Create Text Item
            dict_Text[str(i)] = QtWidgets.QGraphicsSimpleTextItem()

            #Set text
            dict_Text[str(i)].setText('nn-mm \nL: 50.6 m \nD: 1500 mm')

            #Set Pos
            dict_Text[str(i)].setPos(x[i], y[i])

            #Set rotation angle
            dict_Text[str(i)].setRotation(ang[i])

            #Add to Scene
            self.graphicsView.scene().addItem(dict_Text[str(i)])


    def setupUi(self, MainWindow):
        #Central Widget
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        MainWindow.setCentralWidget(self.centralwidget)
        main_width, main_heigth = 1200, 800
        MainWindow.resize(main_width, main_heigth)

        #Create GraphicsView and Scene
        self.scene = QtWidgets.QGraphicsScene()
        self.graphicsView = GraphicsView(scene=self.scene, parent=self.centralwidget)

        #Set Geometry
        self.graphicsView.setGeometry(QtCore.QRect(0, 0, main_width, main_heigth))

        #plot dummy data set
        self.plt_plot()

if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

【问题讨论】:

  • 使用提供的代码无法重现您的问题,请提供minimal, reproducible example。此外,cmets 应使用适当的 # 字符,并且 in 也应为英文。
  • musicamante,我已经用足够的代码和参考来编辑我的问题来重现问题。感谢您的宝贵时间。

标签: pyqt5 qgraphicsview


【解决方案1】:

虽然 QGraphicsView 框架文档声明它非常快,但这并不意味着它总是快。

渲染速度取决于许多因素,单个项目转换会大大降低整体性能。

考虑到所有项的绘制是作为个别光栅绘制完成的(后端几乎都是Qt自己的渲染:它的优化虽然通常很好,但并不完美)。
对于具有单独转换的每个项目,画家将需要根据那个转换进行绘画。
如果您有 200 个项目,每个项目都有自己的转换,这意味着 很多 计算。

注意:转换是可以转换绘画的matrix(这意味着一切都需要特殊和额外的计算)。
Qt 转换非常标准:

  • 翻译
  • 规模
  • shear
  • [投影]
  • 旋转(通过结合剪切和缩放来完成,因此很复杂)
  • 透视(通过结合投影和缩放来完成)

然后您必须添加一个事实,即您不是在绘制简单的项目,而是 基于文本的项目。尽管 Qt 和底层系统提供了所有优化,但文本绘制需要大量计算。

我不会深入探讨如何完成文字绘制,但您必须考虑很多方面;让我们只考虑其中的极少数

  • 每个字母由许多复杂的多边形组成(其中许多使用贝塞尔曲线)
  • 每个字母都有不同的大小和间距,包括每个字母(和每个字母对)的间距,也称为字距调整
  • 有些字体有更高级的功能,比如ligature
  • 即使是简单的对齐也必须考虑在内,可能根据systemwidget 甚至text option 布局方向
  • 很多其他的东西......

考虑这一点(它的工作原理并不完全像这样,但这只是为了示例):在您的文本中,您有大约 20 个可绘制字符。
将每个角色想象成一个创建的 QPainterPath 实例,包含 很多 线条和贝塞尔曲线(几乎所有角色都如此)。这大约是 4000 条具有自己曲线的单独路径,每条路径每次在绘制时创建。
然后由于旋转,您还需要应用变换矩阵(如前所述,两者剪切翻译)。

我需要说明的是,以上是对文本绘制方式的over-简化(因为 Qt 也部分依赖于底层系统字体渲染)。

那么,有解决办法吗?
嗯,“不是真的”和“不总是”。

首先,不使用setSceneRect(),您可以通过滚动场景的内容来获得一些轻微的改进。这是通过设置(非常)更大的sceneRect并使用set<Orientation>ScrollBarPolicy隐藏滚动条到ScrollBarAlwaysOff,然后通过设置滚动条值上的增量位置来移动可见区域。移动滚动条只会导致重新绘制视口,而setSceneRect() 还需要基于变换滚动条大小来(递归)计算可见区域。

然后,有一个 OpenGL 替代方案,它可能会改进performance

为了准确快速地对项目应用变换和效果,Graphics View 是在假设用户的硬件能够为浮点指令提供合理性能的情况下构建的。 [...] 因此,某些类型的效果在某些设备上可能比预期的要慢。可以通过在其他领域进行优化来弥补这种性能损失;例如,使用 OpenGL 渲染场景。

请参阅OpenGL Rendering 了解这一点,但请考虑它并不总能保证更好的性能。

最后,如果您需要显示许多单独的文本项,每个文本项都有自己的轮换,您必须预计性能会急剧下降。唯一可能的替代方法是尝试将这些文本项呈现为(更大)图像,然后使用 QGraphicsPixmapItem,但为了获得可靠的结果(因为基于位图的对象在转换时容易出现锯齿),您需要使用更大的尺寸每个项目。

【讨论】:

  • 谢谢,musicamante 我会试一试,如果我得到一些结果,我会发回来以防其他人可以使用它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-19
  • 1970-01-01
  • 2015-08-08
相关资源
最近更新 更多