使用QGraphicsView和QGraphicsItem进行编程滚动?

时间:2016-01-28 09:03:08

标签: qt scroll graphics

我想以编程方式向左/向右滚动一个场景,但我不确定如何正确地做到这一点。请注意,我想要(可见)滚动条。

我使用标准QGraphicsView + QGraphicsScene + QGraphicsItem设置。我已将其缩小到最小,在场景中只有一个QGraphicsItem(一个QGraphicsRectItem)。

通过设置我的视图,我设法实现了程序化滚动:

// view setup
view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

然后,在代码的另一部分:

// programmatic scrolling
QScrollBar* const sb = view->horizontalScrollBar();
sb->setRange(0, 1000); // some values for experimenting
sb->setValue(sb->value() + 100 or -100); // some increment for experimenting

这很有效,但是......滚动浏览隐形滚动条感觉不对。

我尝试了这种更简单的方法:

// programmatic scrolling - doesn't quite work
view->viewport()->scroll(100 or -100, 0); // some increment for experimenting

此代码会滚动,但当矩形离开视图的左边缘时,我会反转滚动方向(在调用100时,增量从-100更改为scroll() }),矩形的未覆盖部分不重新绘制。原因是在这种情况下不调用QGraphicsRectItem::paint()(使用滚动条方法时调用它)。

那么,有没有办法让viewport()->scroll()工作?或者其他一些简单的方法来实现程序化滚动?或者人工滚动条方法是否可行?

3 个答案:

答案 0 :(得分:0)

尝试使用QGraphicsView::translate()QGraphicsView::setTransform()更改视图转换。

但请记住,您无法将视口移动到场景“外部”,因此请确保您的场景矩形足够大。

答案 1 :(得分:0)

如果我的问题正确,那么有一个dojo类库,其中包含PanWebView这样的类,它允许QWebView使用鼠标平滑滚动而不需要任何滚动条。 Take a look at sources。它支持平移,可以适用于移动应用程序,但也许它也可以为您提供帮助。

PanWebView类看起来像这样

#include <QWebView>
#include <QWebFrame>
#include <QMouseEvent>
#include <QApplication>

class PanWebView : public QWebView
{
    Q_OBJECT

private:
    bool pressed;
    bool scrolling;
    QPoint position;
    QPoint offset;
    QList<QEvent*> ignored;

public:
    PanWebView(QWidget *parent = 0): QWebView(parent), pressed(false),     scrolling(false) {
    QWebFrame *frame = page()->mainFrame();
    frame->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff);
    frame->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff);
}

protected:

void mousePressEvent(QMouseEvent *mouseEvent) {

    if (ignored.removeAll(mouseEvent))
        return QWebView::mousePressEvent(mouseEvent);

    if (!pressed && !scrolling && mouseEvent->modifiers() == Qt::NoModifier)
        if (mouseEvent->buttons() == Qt::LeftButton) {
            pressed = true;
            scrolling = false;
            position = mouseEvent->pos();
            QWebFrame *frame = page()->mainFrame();
            int x = frame->evaluateJavaScript("window.scrollX").toInt();
            int y = frame->evaluateJavaScript("window.scrollY").toInt();
            offset = QPoint(x, y);
            QApplication::setOverrideCursor(Qt::OpenHandCursor);
            return;
        }

    return QWebView::mousePressEvent(mouseEvent);
}

void mouseReleaseEvent(QMouseEvent *mouseEvent) {

    if (ignored.removeAll(mouseEvent))
        return QWebView::mouseReleaseEvent(mouseEvent);

    if (scrolling) {
        pressed = false;
        scrolling = false;
        QApplication::restoreOverrideCursor();
        return;
    }

    if (pressed) {
        pressed = false;
        scrolling = false;

        QMouseEvent *event1 = new QMouseEvent(QEvent::MouseButtonPress,
                                              position, Qt::LeftButton,
                                              Qt::LeftButton, Qt::NoModifier);
        QMouseEvent *event2 = new QMouseEvent(*mouseEvent);

        ignored << event1;
        ignored << event2;
        QApplication::postEvent(this, event1);
        QApplication::postEvent(this, event2);
        QApplication::restoreOverrideCursor();
        return;
    }

    return QWebView::mouseReleaseEvent(mouseEvent);
}

void mouseMoveEvent(QMouseEvent *mouseEvent) {

    if (scrolling) {
        QPoint delta = mouseEvent->pos() - position;
        QPoint p = offset - delta;
        QWebFrame *frame = page()->mainFrame();
        frame- >evaluateJavaScript(QString("window.scrollTo(%1,%2);").arg(p.x()).arg(p.y()));
        return;
    }

    if (pressed) {
        pressed = false;
        scrolling = true;
        return;
    }

    return QWebView::mouseMoveEvent(mouseEvent);
}

};

用法:

PanWebView web;
web.setUrl(QUrl("http://news.google.com"));
web.setWindowTitle("Web View - use mouse to drag and pan around");
web.show();

您还检查了thisthis主题吗?我认为这可能很有用。

答案 2 :(得分:0)

移动视图假定它比场景小。如果它们的大小相同,则不会移动。

QGraphicsView可以设置为场景坐标中任意位置的centerOn。使用计时器调用centerOn一次移动视图一次。

这是一个有效的例子: -

#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsRectItem>
#include <QTimer>

class MyView : public QGraphicsView
{
private:

public:
    MyView(QGraphicsScene* pScene)
        : QGraphicsView(pScene, NULL)
    {}

    void AnimateBy(int x)
    {
        float updateFrequency = (1000/30.0); // ~30 frames per second

        QPointF currScenePos = sceneRect().center();

        int curX = currScenePos.x();
        int endPos = curX + x;

        int distanceToAnimate = (endPos - curX);

        // speed = dist / time
        float updatePosInterval = (float)distanceToAnimate / updateFrequency;

        printf("updatePosInterval: %f \n", updatePosInterval);

        static float newXPos = sceneRect().center().x();

        QTimer* pTimer = new QTimer;
        QObject::connect(pTimer, &QTimer::timeout, [=](){

            newXPos += updatePosInterval;
            centerOn(newXPos, sceneRect().center().y());   

            // check for end position or time, then....
            if(newXPos >= endPos)
            {
                pTimer->stop();
                pTimer->deleteLater();
            }

        });
        pTimer->start(updateFrequency);
    }
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QGraphicsScene scene(0, 0, 10000, 20000);
    MyView* view = new MyView(&scene);

    QGraphicsRectItem* pRect = new QGraphicsRectItem(0, 0, 100, 100);
    pRect->setPos(scene.width()/2, scene.height()/2);
    scene.addItem(pRect);

    // timer to wait for the window to appear, before starting to move
    QTimer* pTimer = new QTimer;
    pTimer->setSingleShot(true);

    QObject::connect(pTimer, &QTimer::timeout,[=](){

        view->centerOn(pRect); // centre on the rectangle
        view->AnimateBy(100);
        pTimer->deleteLater();
    });

    pTimer->start(1000);
    view->show();

    return a.exec();
}

因此,我们通过使用centerOn的调用逐帧移动视图来创建动画。

为简单起见,代码只处理在一个轴上移动。要在2轴中移动,请使用2D矢量数学计算间隔位置。