1

我正在尝试为这样的时间线制定规则:

时间线

我有一个 QGraphicsView,我在其中放置了一个 QGraphicsScene,我在其中添加了 QGraphicsItems,例如线条和一些标签。

我仅在缩放更改时(而不是在引发paintEvent 时)将元素添加到场景中。

为了添加时间标签,我使用:

    QString label = "00:14"; // For example
    int posX = ... // Here I calculate the position of the label
    scene->addText(label,QFont("Arial",8))->setPos(posX,-1);

当我必须重新绘制规则时,我会:

    qDeleteAll(scene->items());

在开始,然后再次添加标签和线条。

我意识到我的表现很糟糕。我的场景有 8k 元素(在线条和标签之间),所以我使用 Valgrind 来检查问题。

它表明“可能”我在向场景中添加文本的行中存在内存泄漏。我有一些看起来像这样的消息:

    2,165,760 bytes in 470 blocks are possibly lost in loss record 9,922 of 9,923
      in TimelineWidget::drawRule() in Timeline/timelinewidget.cpp:166
      1: realloc in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so
      2: /usr/lib/x86_64-linux-gnu/libQtGui.so.4.8.3
      3: /usr/lib/x86_64-linux-gnu/libQtGui.so.4.8.3
      4: QTextDocument::setPlainText(QString const&) in /usr/lib/x86_64-linux-gnu/libQtGui.so.4.8.3
      5: /usr/lib/x86_64-linux-gnu/libQtGui.so.4.8.3
      6: QGraphicsTextItem::QGraphicsTextItem(QString const&, QGraphicsItem*, QGraphicsScene*) in /usr/lib/x86_64-linux-gnu/libQtGui.so.4.8.3
      7: QGraphicsScene::addText(QString const&, QFont const&) in /usr/lib/x86_64-linux-gnu/libQtGui.so.4.8.3
      8: TimelineWidget::drawRule() 

这只发生在 addText 调用中,而不是 addLine 调用。

我正在使用Qt 4.8,我的问题是:

  • 这些内存泄漏是真的吗?

  • 难道我做错了什么?

  • 是否有另一种/更好的方法来实施我的规则?

提前致谢!

4

1 回答 1

2

我建议以下所有内容:

  1. 而不是qDeleteAll(scene->items()),只需清除场景:

    scene->clear();
    
  2. scene->addSimpleText如果您不显示富文本,请使用。

  3. 对于简单、重复的项目,例如一组具有固定间距的行,创建可重复使用的自定义项目。与为每条线创建图形场景项目相比,这些将更快并且开销更低。就绘制线的数量而言,内存、索引、树遍历和查找开销将从 O(N) 下降到 O(1)

    假设您创建了一个自定义项,它是一个水平行数组:| | | | | | | | |. 该项目只是绘制固定数量的行。您可以调整项目的大小以更改线条的高度和它们跨越的水平区域。请记住在您的paint()方法中重置笔,因为默认笔将重新缩放,因此可能非常宽。

    一个简单的带有主要和次要刻度的未标记标尺可以通过叠加三个项目来获得:两个线阵列和一条水平线。

您可能还想重新设计处理场景的方式。到目前为止,您将场景视为整个时间轴的模型。相反,您应该至少将其中的一部分视为视图模型。

您的时间线有两个主要元素:规则和剪辑预览。

当您以给定的缩放级别滚动规则时,您会注意到始终有相同数量的线条和标签可见。唯一改变的是线条的轻微水平偏移,当然标签的值也会改变。

因此,与其在场景中拥有所有线条和所有标签,不如在当前视图中设置尽可能多的线条,调整它们在场景中的水平位置,使它们在正确的偏移处可见,并修复标签文本。这就是使它成为视图模型的原因——它是与特定视图紧密相关的模型。底层的抽象模型是一把很长的尺子,但我们只需要它是一个抽象模型,而不是一个占用内存和其他资源的真实模型。

当您滚动时,剪辑预览也可以从场景中动态添加和删除。这可能会为您节省大量内存,因为我认为您有很多图像,其中大部分是不可见的。

您显示的示例屏幕截图存在一些用户设计问题:显示水平截断的重叠剪辑预览毫无意义。在任何给定的缩放级别,您都应该有一个您愿意显示的最小预览尺寸。显示那些狭窄的垂直像素条非常昂贵。下面是一个可能的算法来处理它。

假设预览项目是从左到右排序的矩形。

  1. 从与视口矩形相交的最左边的项目开始(当然是场景坐标中的所有内容)。使其成为current_item.

  2. 显示current_item(将其添加到场景中)。

  3. 在它们与current_item. 存储last_overlapping_item(如果没有项目重叠,它可以是空的)。

  4. 将当前项目保存在previous_item. 拿下一个项目,把它变成current_item.

  5. 如果 和 之间没有间隙current_itemprevious_item请清除 ,last_overlapping_item因为它与相邻的当前项和以前的项完全重叠。

  6. 显示last_overlapping_item如果有的话(将其添加到场景中)。

  7. 如果current_item完全在视口矩形之外,你就完成了。否则转到2。

于 2013-10-03T01:19:59.603 回答