当图像小于视图时,QGraphicsView 将图像缩放到光标

问题描述

我正在尝试实现可以​​放大图像并将图像移向光标的代码,但不会完全以光标为中心(类似于 Google 地图的工作方式,或完全类似于 Picasa 照片查看器的工作方式)如果有人看到的话,曾经可以工作)。关键是它需要放大图像,并具有与鼠标光标下相同的像素,仍然在其下。

this question 的已接受答案中提供的示例代码很接近,但不能完全解决问题,因为这是一个关于如何放大 光标并避开 QGraphicsView 固有的问题。

问题是:如标题中示例代码的注释所示,如果视图大于图像,则无法执行此功能。换句话说,如果你把图片缩小到没有滚动条,然后再放大,图片会放大到中心,而不是鼠标光标;只有通过放大到有水平垂直滚动条,它才会放大光标并保持光标下方相同的像素。

这似乎是 QGraphicsView 的一个基本问题: QGraphicsView::setTransformationAnchor(QGraphicsView::AnchorUnderMouse) 可以达到几乎完全相同的结果,但 Qt 文档报告了同样的问题:

请注意,当只有一部分场景可见时(即有滚动条时),此属性效果很明显。否则,如果整个场景适合视图,QGraphicsScene 使用视图对齐来定位视图中的场景。

我怀疑这是因为这两种方法滚动视口,但如果图像与视口相比太小,则无法实际滚动它。

我怀疑该解决方案可能需要放弃滚动并实际移动图像,但我无法弄清楚如何移动图像以使相同的像素停留在鼠标光标下方。

这是我已经实现但不起作用的一些代码

Graphics_view_zoom.h:

#include <QObject>
#include <QGraphicsView>

/*!
 * This class adds ability to zoom QGraphicsView using mouse wheel. The point under cursor
 * remains motionless while it's possible.
 *
 * Note that it becomes not possible when the scene's
 * size is not large enough comparing to the viewport size. QGraphicsView centers the picture
 * when it's smaller than the view. And QGraphicsView's scrolls boundaries don't allow to
 * put any picture point at any viewport position.
 *
 * When the user starts scrolling,this class remembers original scene position and
 * keeps it until scrolling is completed. It's better than getting original scene position at
 * each scrolling step because that approach leads to position errors due to before-mentioned
 * positioning restrictions.
 *
 * When zommed using scroll,this class emits zoomed() signal.
 *
 * Usage:
 *
 *   new Graphics_view_zoom(view);
 *
 * The object will be deleted automatically when the view is deleted.
 *
 * You can set keyboard modifiers used for zooming using set_modified(). Zooming will be
 * performed only on exact match of modifiers combination. The default modifier is Ctrl.
 *
 * You can change zoom veLocity by calling set_zoom_factor_base().
 * Zoom coefficient is calculated as zoom_factor_base^angle_delta
 * (see QWheelEvent::angleDelta).
 * The default zoom factor base is 1.0015.
 */
class Graphics_view_zoom : public QObject {
  Q_OBJECT
public:
  Graphics_view_zoom(QGraphicsView* view);
  void gentle_zoom(double factor);
  void set_modifiers(Qt::KeyboardModifiers modifiers);
  void set_zoom_factor_base(double value);

private:
  QGraphicsView* _view;
  Qt::KeyboardModifiers _modifiers;
  double _zoom_factor_base;
  QPointF target_scene_pos,target_viewport_pos;
  bool eventFilter(QObject* object,QEvent* event);

signals:
  void zoomed();
};

Graphics_view_zoom.cpp:

#include "Graphics_view_zoom.h"
#include <QMouseEvent>
#include <QApplication>
#include <QScrollBar>
#include <qmath.h>

Graphics_view_zoom::Graphics_view_zoom(QGraphicsView* view)
  : QObject(view),_view(view)
{
  _view->viewport()->installEventFilter(this);
  _view->setMouseTracking(true);
  _modifiers = Qt::ControlModifier;
  _zoom_factor_base = 1.0015;
}

void Graphics_view_zoom::gentle_zoom(double factor) {
  _view->scale(factor,factor);
  _view->centerOn(target_scene_pos);
  QPointF delta_viewport_pos = target_viewport_pos - QPointF(_view->viewport()->width() / 2.0,_view->viewport()->height() / 2.0);
  QPointF viewport_center = _view->mapFromScene(target_scene_pos) - delta_viewport_pos;
  _view->centerOn(_view->mapToScene(viewport_center.toPoint()));
  emit zoomed();
}

void Graphics_view_zoom::set_modifiers(Qt::KeyboardModifiers modifiers) {
  _modifiers = modifiers;

}

void Graphics_view_zoom::set_zoom_factor_base(double value) {
  _zoom_factor_base = value;
}

bool Graphics_view_zoom::eventFilter(QObject *object,QEvent *event) {
  if (event->type() == QEvent::MouseMove) {
    QMouseEvent* mouse_event = static_cast<QMouseEvent*>(event);
    QPointF delta = target_viewport_pos - mouse_event->pos();
    if (qAbs(delta.x()) > 5 || qAbs(delta.y()) > 5) {
      target_viewport_pos = mouse_event->pos();
      target_scene_pos = _view->mapToScene(mouse_event->pos());
    }
  } else if (event->type() == QEvent::Wheel) {
    QWheelEvent* wheel_event = static_cast<QWheelEvent*>(event);
    if (QApplication::keyboardModifiers() == _modifiers) {
      if (wheel_event->orientation() == Qt::Vertical) {
        double angle = wheel_event->angleDelta().y();
        double factor = qPow(_zoom_factor_base,angle);
        gentle_zoom(factor);
        return true;
      }
    }
  }
  Q_UNUSED(object)
  return false;
}

main.cpp

#include "mainwindow.h"

#include <QApplication>

int main(int argc,char *argv[])
{
    QApplication a(argc,argv);
    MainWindow w;
    w.show();
    return a.exec();
}

mainwindow.h:

#pragma once

#include <QMainWindow>

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

mainwindow.cpp:

#include "mainwindow.h"

#include <QGraphicsScene>
#include <QGraphicsView>
#include <Qpixmap>
#include <Graphics_view_zoom.h>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    this->setFixedSize(1000,1000);

    QGraphicsScene* s = new QGraphicsScene(this);
    QGraphicsView*  v = new QGraphicsView(this);
    Graphics_view_zoom* z = new Graphics_view_zoom(v);
    z->set_modifiers(Qt::NoModifier);

    v->setScene(s);
    v->setFixedSize(1000,1000);

    Qpixmap pix;
    if(pix.load("C:\\full\\path\\to_an_image_file.jpg"))
    {
        s->addpixmap(pix);
    }
}

我也尝试了找到 here 的修复,但这也有完全相同的问题:如果图像放大到足够远,像素将保持在光标下提供垂直和水平滚动选项。

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)