如何在QGraphicsView中平移图像

25

我目前可以将图像加载到图形场景中,然后再加载到QGraphicsViewer中。

我可以通过检测QEvent :: Wheel事件并调用graphicsView的scale()函数来实现缩放功能。

但是,我似乎无法弄清楚如何使平移功能工作。 我基本上想要检测鼠标是否在图像上单击,然后随着鼠标而将图像向左、右、上或下移动。

截至目前,我基本上有一个MouseFilter类来检测事件,并根据事件类型执行不同的操作。我将该侦听器附加到了QGraphicsView对象上。

3个回答

57

如果有人想知道如何自己完成,实际上非常简单。这是我应用程序的代码:

class ImageView : public QGraphicsView
{
public:
    ImageView(QWidget *parent);
    ~ImageView();

private:
    virtual void mouseMoveEvent(QMouseEvent *event);
    virtual void mousePressEvent(QMouseEvent *event);
    virtual void mouseReleaseEvent(QMouseEvent *event);

    bool _pan;
    int _panStartX, _panStartY;
};

你需要储存拖动的起始位置,例如像这样(我使用了右键):

void ImageView::mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::RightButton)
    {
        _pan = true;
        _panStartX = event->x();
        _panStartY = event->y();
        setCursor(Qt::ClosedHandCursor);
        event->accept();
        return;
    }
    event->ignore();
}

还有,当释放按钮时,您需要清除标志并恢复光标:

void ImageView::mouseReleaseEvent(QMouseEvent *event)
{
    if (event->button() == Qt::RightButton)
    {
        _pan = false;
        setCursor(Qt::ArrowCursor);
        event->accept();
        return;
    }
    event->ignore();
}

要实际管理拖拽,您需要重写鼠标移动事件。QGraphicsView继承自QAbstractScrollArea,并且其滚动条很容易访问。您还需要更新平移位置:

void ImageView::mouseMoveEvent(QMouseEvent *event)
{
    if (_pan)
    {
        horizontalScrollBar()->setValue(horizontalScrollBar()->value() - (event->x() - _panStartX));
        verticalScrollBar()->setValue(verticalScrollBar()->value() - (event->y() - _panStartY));
        _panStartX = event->x();
        _panStartY = event->y();
        event->accept();
        return;
    }
    event->ignore();

}

是的,这正是我一直在寻找的...即使过了几年,也非常感谢! - Captain GouLash
请考虑使用上面的示例,但不要操作滚动条,而是使用QGraphicsView::scrollContentsBy。它接受一个偏移量dx、dy来调整您的内容(场景)。 - Mark
2
请忽略使用 ::scrollContentsBy 函数,根据文档,你应该仅使用滚动条。"为了进行程序滚动而调用此函数是错误的,应改用滚动条(例如直接调用 QScrollBar::setValue() 函数)来实现。" - Mark
有人能告诉我这是否会干扰右键单击以拉出上下文菜单吗? - pbreach

18

QGraphicsView内置了鼠标平移支持。设置正确的DragMode,其余操作会被自动处理。但你需要启用滚动条才能使其生效。


你认为我能够自己实现这个吗?我想在鼠标右键按下时执行此操作。我该如何从代码中滚动视图? - neuviemeporte
@neuviemeporte:我不知道如何通过右键单击来完成它。你可能想把它作为一个单独的问题发布,这样其他人就可以回答了。 - Stephen Chu
在Qt 5.0中,您不再需要启用滚动条。 - vsz

8

neuviemeporte解决方案需要对QGraphicsView进行子类化。

如果不需要自定义QGraphicsView的其他行为,可以使用eventFilter而无需对视图进行子类化来实现拖动。这种技术可以节省一些工作量。

假设您的GUI逻辑由一个QMainWindow子类维护,并且QGraphicsView和QGraphicsScene被声明为此子类的私有成员。您需要按照以下方式实现eventFilter函数:

bool MyMainWindow::eventFilter(QObject *obj, QEvent *event)
{
    if (obj == scene && event->type() == Event::GraphicsSceneMouseMove)
    {
        QGraphicsSceneMouseEvent *m = static_cast<QGraphicsSceneMouseEvent*>(event);
        if (m->buttons() & Qt::MiddleButton)
        {
            QPointF delta = m->lastScreenPos() - m->screenPos();
            int newX = view->horizontalScrollBar()->value() + delta.x();
            int newY = view->verticalScrollBar()->value() + delta.y();
            view->horizontalScrollBar()->setValue(newX);
            view->verticalScrollBar()->setValue(newY);
            return true;
        }
    }

    return QMainWindow::eventFilter(obj, event);
}

要从QGraphicsScene过滤事件,您需要将MyMainWindow安装为场景的事件过滤器。也许您可以在设置GUI的同一函数中完成此操作。

void MyMainWindow::setupGUI()
{ 
    // along with other GUI stuff...

    scene->installEventFilter(this);
}

您可以将这个想法扩展到使用之前展示的拖动“手”来替换光标。

谢谢,很棒的解决方法 - 对于那些不想重新实现QGraphicsView的人来说 :) - Maxime Franchot

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接