【问题标题】:QScrollArea in Qml: Flickable + QQuickPaintedItemQml 中的 QScrollArea:Flickable + QQuickPaintedItem
【发布时间】:2016-02-17 15:07:01
【问题描述】:

我试图在 Qml 的帮助下实现与QScrollArea(在小部件世界中)类似的东西。 我决定探测Flickable 加上QQuickPaintedItem 基于项目(在我的例子中命名为抽屉):

Flickable {
  ...
  onContentXChanged(): {
  drawer.update()
  }

Drawer {
  id: drawer
  ...
}

抽屉的渲染目标设置为FrameBufferObject。它的绘制函数如下所示:

void Drawer::paint(QPainter *painter)
{
   // Some function to compute rect which is needed to be redrawn
   QRect updateRect = computeUpdateRect();

   // How to shift contents of Frame buffer, e.g. to right, and draw only updateRect in this space?
}

想象一下我们如何在 QScrollArea 小部件中滚动,例如向左:视口的所有条目都向右移动,左侧唯一的小矩形被重绘。 我想对Flickable+QQuickPaintedItem 做同样的事情。但是有些东西我看不懂:

如何操作 QQuickPaintedItem 中的帧缓冲区对象? 也许有一些更正确的方法可以在 QML 中实现QScrollArea

顺便问一下,QQuickPaintedItem默认开启双缓冲了吗?

使用Flickable 实现: 提供有关任务的更多信息:我有一个非常大的“图片”。所以我不能将它全部加载到内存中,但我必须使用视口之类的东西来浏览它。

【问题讨论】:

  • QQuickPaintedItemQPainter 一起使用。就速度而言,这不是最好的主意,所以我建议您使用 QQuickItem 并直接使用 OpenGL。无论如何,如果您使用QQuickPaintedItem,您可以设置QQuickPaintedItem::setRenderTarget(QQuickPaintedItem::FramebufferObject),因此QPainter 使用GL 绘制引擎绘制到QOpenGLFramebufferObject(来自Qt 文档)
  • @folibis 感谢您的回复,您知道是否有一种手动操作此帧缓冲区的方法,或者只能通过 QPainter 接口访问?我在文档中没有找到有关它的信息。

标签: qt qml qt-quick qscrollarea


【解决方案1】:

当您想要将较大的内容封装在较小的区域中并在其周围导航时,可以使用滚动区域或可滑动区域。在您的情况下,情况并非如此。您实际上并没有使用滚动区域,因为您的图像永远不会大于滚动区域的大小,您只是想伪造它,这实际上很容易:

#include <QQuickPaintedItem>
#include <QImage>
#include <QPainter>

class PImage : public QQuickPaintedItem {
    Q_OBJECT
public:
    PImage(QQuickItem * p = 0) : QQuickPaintedItem(p), xpos(0), ypos(0) {}
    void paint(QPainter *painter) {
        if (!source.isNull()) painter->drawImage(QRect(0, 0, width(), height()), source, QRect(xpos, ypos, width(), height()));
        else painter->fillRect(QRect(0, 0, width(), height()), Qt::black);
    }
public slots:
    bool load(QString path) {
        source = QImage(path);
        return !source.isNull();
    }
    void moveBy(int x, int y) {
        int ox, oy;
        // don't go outside the image
        ox = x + xpos + width() <= source.width() ? x + xpos : source.width() - width();
        oy = y + ypos + height() <= source.height() ? y + ypos : source.height() - height();
        if (ox < 0) ox = 0;
        if (oy < 0) oy = 0;
        if (ox != xpos || oy != ypos) {
            xpos = ox;
            ypos = oy;
            update();
        }
    }
private:
    QImage source;
    int xpos, ypos;
};

在 QML 方面:

PImage {
    width: 300
    height: 300
    Component.onCompleted: load("d:/img.jpg") // a big image
    MouseArea {
        property int ix
        property int iy
        anchors.fill: parent
        onPressed: {
            ix = mouseX
            iy = mouseY
        }
        onPositionChanged: {
            parent.moveBy(ix - mouseX, iy - mouseY)
            ix = mouseX
            iy = mouseY
        }
    }
}

这只是一个简单的基本示例,还有很多改进和改进的空间。另请注意,如果源矩形与目标矩形大小不同,则可以轻松实现放大或缩小。您可以将其连接到一个 flickable 以获得“动态滚动”而不是鼠标区域。

【讨论】:

  • 添加了关于任务的信息:我有一张非常大的“图片”。所以我不能将它全部加载到内存中,但我必须使用视口之类的东西来浏览它。
  • 那么QScrollArea和它有什么关系呢?它不支持部分图像加载。图片有多大?图片是什么格式的?它甚至支持部分加载吗?
  • 感谢您的回复,我所说的“图片”是指可缩放的画布(尺寸可能超过 10 000 x 10 000 像素),其中部分可以动态绘制。
  • 一张 10k x 10k RGBA 图像只需要 380 MB 的内存。此外,如果它是画布,那么无论如何它都会在 ram 中。目前还不清楚你想做什么。如果您只需要绘制较大图像的矩形区域,则只需使用void QPainter::drawImage(const QRect &amp; rectangle, const QImage &amp; image)
  • @Sas - 不要担心过早的优化。仅在确定确实存在性能问题后才执行此操作。场景图本身可能使用双缓冲。考虑将操作从主线程卸载到工作线程,以保持主线程响应。
猜你喜欢
  • 2022-11-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-15
  • 2016-08-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多