QGraphicsView::setSceneRect() 滚动视口而不是平移

问题描述 投票:0回答:0

我正在尝试在可调整大小的

QGraphicsScene
中创建一个可平移的
QGraphicsView
。调整大小时,我需要保持场景大小固定,而不是在视图上使用变换以确保场景始终“覆盖”视口。结果是一个很好的缩放场景,保持居中。

问题是当视图小于场景矩形时,平移场景会导致它滚动。尽管我已经以我能想到的所有方式禁用滚动,但对

setSceneRect()
的调用似乎优先考虑滚动。这是在正确平移场景之前限制沿该轴的移动。

这是问题的动图。矩形是我为可见性添加的项目,它不可交互:

重申一下,目标是允许场景矩形扩展到视口的边界之外,但不能有任何可能的滚动行为。一个示例用例是渲染地图视图,其中渲染的图块可能会超出视口边界,并且所有平移都是逻辑上完成的,而不是使用滚动条。我希望这是有道理的。

我创建了一个最小可复制的例子:

主窗口.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QGraphicsScene>
#include <QGraphicsItem>
#include <QDebug>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    MyGraphicsView *graphicsView = new MyGraphicsView();

    //These have no effect------

    //graphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    //graphicsView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    graphicsView->setDragMode(QGraphicsView::NoDrag);
    graphicsView->setResizeAnchor(QGraphicsView::NoAnchor);

    //-------

    setCentralWidget(graphicsView);

    const qreal sceneWidth = 1920;
    const qreal sceneHeight = 1080;
    const QRectF sceneRect = QRectF(0, 0, sceneWidth, sceneHeight);

    QGraphicsScene *scene = new QGraphicsScene();
    scene->setSceneRect(sceneRect.translated(-sceneWidth / 2, -sceneHeight / 2));

    //Rectangle for visibility (same size as scene, doesnt move)
    QGraphicsRectItem* rectItem = new QGraphicsRectItem(sceneRect);
    rectItem->setPos(-sceneWidth / 2, -sceneHeight / 2);
    rectItem->setPen(QPen(Qt::black, 8));
    QLinearGradient gradient(sceneRect.topLeft(), sceneRect.bottomRight());
    gradient.setColorAt(0, Qt::blue);
    gradient.setColorAt(0.5, Qt::green);
    gradient.setColorAt(1, Qt::yellow);
    QBrush brush(gradient);
    rectItem->setBrush(brush);
    scene->addItem(rectItem);

    graphicsView->setScene(scene);
    graphicsView->setRenderHint(QPainter::Antialiasing);
    graphicsView->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);

    // Center the scene in the view
    graphicsView->centerOn(0, 0);

    connect(graphicsView, &MyGraphicsView::resized, this, &MainWindow::resizeView);
}

void MainWindow::resizeView()
{
    MyGraphicsView *view = qobject_cast<MyGraphicsView*>(sender());
    
    // Get the new viewport size
    QSizeF viewportSize = view->viewport()->size();

    QSizeF sceneSize = view->sceneRect().size();

    // Calculate the scaling factor to fill the viewport with the scene
    qreal scaleX = viewportSize.width() / sceneSize.width();
    qreal scaleY = viewportSize.height() / sceneSize.height();
    qreal scale = qMax(scaleX, scaleY);

    // Set the new transformation matrix
    QTransform transform;
    transform.scale(scale, scale);
    view->setTransform(transform);

    // Center the scene on the center point
    QPointF centerPoint = view->sceneRect().center();
    view->centerOn(centerPoint);
}


MainWindow::~MainWindow()
{
    delete ui;
}

主窗口.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QGraphicsView>
#include <QMouseEvent>
#include <QDebug>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MyGraphicsView : public QGraphicsView
{
    Q_OBJECT
public:
    explicit MyGraphicsView(QWidget *parent = nullptr): m_isPanning(false)
    {
    }
signals:
    void resized();
protected:

protected:
    void mousePressEvent(QMouseEvent* event) override
    {
        if (event->button() == Qt::LeftButton)
        {
            m_isPanning = true;
            m_lastPanPos = mapToScene(event->pos());
            setCursor(Qt::ClosedHandCursor);
        }

        QGraphicsView::mousePressEvent(event);
    }

    void mouseMoveEvent(QMouseEvent* event) override
    {
        if (m_isPanning)
        {
            QPointF delta = m_lastPanPos.toPoint() - mapToScene(event->pos());

            //The culprit
            setSceneRect(sceneRect().translated(delta.x(), delta.y()));

            m_lastPanPos = mapToScene(event->pos());
        }

        QGraphicsView::mouseMoveEvent(event);
    }

    void mouseReleaseEvent(QMouseEvent* event) override
    {
        if (event->button() == Qt::LeftButton && m_isPanning)
        {
            m_isPanning = false;
            setCursor(Qt::ArrowCursor);
        }

        QGraphicsView::mouseReleaseEvent(event);
    }

    void scrollContentsBy(int, int) override
    {
            // Don't call the base implementation, effectively preventing scrolling
    }

    void resizeEvent(QResizeEvent *event) override
    {
        QGraphicsView::resizeEvent(event);
        emit resized();
    }

private:
    bool m_isPanning;
    QPointF m_lastPanPos;
};


class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

public slots:
    void resizeView();

private:
    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H

我试过禁用 scrollContentsBy,关闭滚动条可见性,将视图设置为 NoDrag,但问题似乎与 setSceneRect() 的功能直接相关。如果我在 mouseMoveEvent 函数中注释该行,则根本没有平移或滚动能力,这是正确的。一旦我取消注释,它就会再次开始滚动。

我坚持使用 Qt 5.6.2

c++ qt qgraphicsview qgraphicsscene
© www.soinside.com 2019 - 2024. All rights reserved.