我正在尝试在可调整大小的
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