Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/153.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ QGraphicsView使用鼠标滚轮在鼠标位置下放大和缩小 我在屏幕的中间有一个应用程序,它有代码> qGraceCsVIEW/COD>窗口。我想能够放大和缩小使用鼠标滚轮滚动_C++_Qt_Qgraphicsview - Fatal编程技术网

C++ QGraphicsView使用鼠标滚轮在鼠标位置下放大和缩小 我在屏幕的中间有一个应用程序,它有代码> qGraceCsVIEW/COD>窗口。我想能够放大和缩小使用鼠标滚轮滚动

C++ QGraphicsView使用鼠标滚轮在鼠标位置下放大和缩小 我在屏幕的中间有一个应用程序,它有代码> qGraceCsVIEW/COD>窗口。我想能够放大和缩小使用鼠标滚轮滚动,c++,qt,qgraphicsview,C++,Qt,Qgraphicsview,目前,我已经重新实现了QGraphicsView,并覆盖了鼠标滚动功能,使其不会滚动图像(与默认情况相同) 所以当我滚动时,如果鼠标滚轮向前,我会发出一个信号true;如果鼠标滚轮向后,我会发出一个信号false 然后,我将此信号连接到类中处理GUI内容的插槽(缩放函数见下文)。现在基本上我认为我的缩放功能根本不是最好的方式,我见过一些人使用覆盖的wheelevent功能来设置比例的例子,但我真的找不到一个完整的答案 因此,我做了这个,但无论如何它都不是完美的,所以我想对它进行一些调整,或者在w

目前,我已经重新实现了
QGraphicsView
,并覆盖了鼠标滚动功能,使其不会滚动图像(与默认情况相同)

所以当我滚动时,如果鼠标滚轮向前,我会发出一个信号true;如果鼠标滚轮向后,我会发出一个信号false

然后,我将此信号连接到类中处理GUI内容的插槽(缩放函数见下文)。现在基本上我认为我的缩放功能根本不是最好的方式,我见过一些人使用覆盖的wheelevent功能来设置比例的例子,但我真的找不到一个完整的答案

因此,我做了这个,但无论如何它都不是完美的,所以我想对它进行一些调整,或者在wheel事件函数中使用scale作为一个工作示例

我在构造函数中将
m_zoom_level
初始化为
0

void Display::zoomfunction(bool zoom)
{
    QMatrix matrix;

    if(zoom && m_zoom_level < 500)
    {
        m_zoom_level = m_zoom_level + 10;
        ui->graphicsView->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
        matrix.scale(m_zoom_level, m_zoom_level);

        ui->graphicsView->setMatrix(matrix);
        ui->graphicsView->scale(1,-1);
    }
    else if(!zoom)
    {
        m_zoom_level = m_zoom_level - 10;
        ui->graphicsView->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
        matrix.scale(m_zoom_level, m_zoom_level);

        ui->graphicsView->setMatrix(matrix);
        ui->graphicsView->scale(1,-1);
    }
}
void Display::zoomfunction(布尔缩放)
{
q矩阵;
如果(缩放和m_缩放级别<500)
{
m_zoom_level=m_zoom_level+10;
ui->graphicsView->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
矩阵缩放(m_缩放级别,m_缩放级别);
用户界面->图形视图->设置矩阵(矩阵);
用户界面->图形视图->比例(1,-1);
}
否则如果(!缩放)
{
m_zoom_level=m_zoom_level-10;
ui->graphicsView->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
矩阵缩放(m_缩放级别,m_缩放级别);
用户界面->图形视图->设置矩阵(矩阵);
用户界面->图形视图->比例(1,-1);
}
}
正如你在上面看到的,我正在使用一个
QMatrix
并缩放它,将其设置为Graphicsview,并将转换锚点设置为鼠标下,但它并不完美,有时如果我滚动加载,它只会开始放大(我认为这与int循环或其他内容有关)


正如我所说的,这方面的帮助或鼠标缩放的一个好例子将非常好。

这样的缩放有点棘手。让我分享一下我自己的课程

标题:

#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();
};
class QGraphicsViewMap : public QGraphicsView
{
    Q_OBJECT

private:
    qreal _numScheduledScalings = 0;
    QPoint wheelEventMousePos;
public:
    explicit QGraphicsViewMap(QWidget *parent = 0);

signals:

public slots:
    void wheelEvent(QWheelEvent* event);
    void scalingTime(qreal x);
    void animFinished();
};

在经历了许多挫折之后,这似乎奏效了。问题似乎是
QGraphicsView
转换
与其滚动位置无关,因此
QGraphicsView::maptosene(const QPoint&)const
的行为取决于滚动位置和转换。我必须查看
maptosene
的源代码才能理解这一点

记住这一点,下面是有效的方法:记住鼠标指向的场景点,缩放,将该场景点映射到鼠标坐标,然后调整滚动条,使该点在鼠标下方结束:

void ZoomGraphicsView::wheelEvent(QWheelEvent* event)
{
   const QPointF p0scene = mapToScene(event->pos());

   qreal factor = std::pow(1.01, event->delta());
   scale(factor, factor);

   const QPointF p1mouse = mapFromScene(p0scene);
   const QPointF move = p1mouse - event->pos(); // The move
   horizontalScrollBar()->setValue(move.x() + horizontalScrollBar()->value());
   verticalScrollBar()->setValue(move.y() + verticalScrollBar()->value());
}
有点晚了 但我今天只和Pyside一起走过同样的路,但应该是一样的

方法“非常简单”,虽然花费了我一点时间。。。 首先将所有锚点设置为NoAnchor,然后获取wheelevent的点,将其映射到场景, 按此值平移场景,缩放并最终将其平移回:

def wheelEvent(self, evt):
    #Remove possible Anchors
    self.widget.setTransformationAnchor(QtGui.QGraphicsView.NoAnchor)
    self.widget.setResizeAnchor(QtGui.QGraphicsView.NoAnchor)
    #Get Scene Pos
    target_viewport_pos = self.widget.mapToScene(evt.pos())
    #Translate Scene
    self.widget.translate(target_viewport_pos.x(),target_viewport_pos.y())
    # ZOOM
    if evt.delta() > 0:
        self._eventHandler.zoom_ctrl(1.2)
    else:
        self._eventHandler.zoom_ctrl(0.83333)
    # Translate back
    self.widget.translate(-target_viewport_pos.x(),-target_viewport_pos.y())
这是唯一符合我目的的解决办法。
IMHO它也是最符合逻辑的解决方案…

这里有一个使用PyQt的解决方案:

def wheelEvent(self, event):
    """
    Zoom in or out of the view.
    """
    zoomInFactor = 1.25
    zoomOutFactor = 1 / zoomInFactor

    # Save the scene pos
    oldPos = self.mapToScene(event.pos())

    # Zoom
    if event.angleDelta().y() > 0:
        zoomFactor = zoomInFactor
    else:
        zoomFactor = zoomOutFactor
    self.scale(zoomFactor, zoomFactor)

    # Get the new position
    newPos = self.mapToScene(event.pos())

    # Move scene to old position
    delta = newPos - oldPos
    self.translate(delta.x(), delta.y())

这是python版本适合我的。来自@Stefan Reinhardt和@rengel的答案组合

class MyQGraphicsView(QtGui.QGraphicsView):

def __init__ (self, parent=None):
    super(MyQGraphicsView, self).__init__ (parent)

def wheelEvent(self, event):
    # Zoom Factor
    zoomInFactor = 1.25
    zoomOutFactor = 1 / zoomInFactor

    # Set Anchors
    self.setTransformationAnchor(QtGui.QGraphicsView.NoAnchor)
    self.setResizeAnchor(QtGui.QGraphicsView.NoAnchor)

    # Save the scene pos
    oldPos = self.mapToScene(event.pos())

    # Zoom
    if event.delta() > 0:
        zoomFactor = zoomInFactor
    else:
        zoomFactor = zoomOutFactor
    self.scale(zoomFactor, zoomFactor)

    # Get the new position
    newPos = self.mapToScene(event.pos())

    # Move scene to old position
    delta = newPos - oldPos
    self.translate(delta.x(), delta.y())
平滑变焦

void StatusView::wheelEvent(QWheelEvent * event)
{
    const QPointF p0scene = mapToScene(event->pos());

    qreal factor = qPow(1.2, event->delta() / 240.0);
    scale(factor, factor);

    const QPointF p1mouse = mapFromScene(p0scene);
    const QPointF move = p1mouse - event->pos(); // The move
    horizontalScrollBar()->setValue(move.x() + horizontalScrollBar()->value());
    verticalScrollBar()->setValue(move.y() + verticalScrollBar()->value());

}

您只需使用内置功能
AnchorUnderMouse
AnchorViewCenter
即可在鼠标下方或中间保持焦点。 这在Qt5.7中适用于我

void SceneView::wheelEvent(QWheelEvent *event)
    {
        if (event->modifiers() & Qt::ControlModifier) {
            // zoom
            const ViewportAnchor anchor = transformationAnchor();
            setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
            int angle = event->angleDelta().y();
            qreal factor;
            if (angle > 0) {
                factor = 1.1;
            } else {
                factor = 0.9;
            }
            scale(factor, factor);
            setTransformationAnchor(anchor);
        } else {
            QGraphicsView::wheelEvent(event);
        }
    }

这是上述解决方案的浓缩版本;只需要将代码放入wheel事件中。这在我的测试中可以使用/不使用滚动条,非常好;)


将@veslam:s解决方案与来自QT Wiki()的平滑缩放代码结合起来似乎效果很好:

资料来源:

#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;
}
QGraphicsViewMap::QGraphicsViewMap(QWidget *parent) : QGraphicsView(parent)
{
    setTransformationAnchor(QGraphicsView::NoAnchor);
    setResizeAnchor(QGraphicsView::NoAnchor);
}

void QGraphicsViewMap::wheelEvent(QWheelEvent* event)
{
    wheelEventMousePos = event->pos();

    int numDegrees = event->delta() / 8;
    int numSteps = numDegrees / 15; // see QWheelEvent documentation
    _numScheduledScalings += numSteps;
    if (_numScheduledScalings * numSteps < 0) // if user moved the wheel in another direction, we reset previously scheduled scalings
        _numScheduledScalings = numSteps;

    QTimeLine *anim = new QTimeLine(350, this);
    anim->setUpdateInterval(20);

    connect(anim, SIGNAL (valueChanged(qreal)), SLOT (scalingTime(qreal)));
    connect(anim, SIGNAL (finished()), SLOT (animFinished()));
    anim->start();
 }

void QGraphicsViewMap::scalingTime(qreal x)
{
    QPointF oldPos = mapToScene(wheelEventMousePos);

    qreal factor = 1.0+ qreal(_numScheduledScalings) / 300.0;
    scale(factor, factor);

    QPointF newPos = mapToScene(wheelEventMousePos);
    QPointF delta = newPos - oldPos;
    this->translate(delta.x(), delta.y());
}

void QGraphicsViewMap::animFinished()
{
    if (_numScheduledScalings > 0)
        _numScheduledScalings--;
    else
        _numScheduledScalings++;

    sender()->~QObject();
}

在Mac OS上,使用QGraphicsView::setTransformationAnchor(AnchorUnderMouse)时,此处引用的解决方案有时会失败:

1-当windows没有焦点时,Qt不会更新LastMouseMovescenPoint。因为缩放是在失去焦点时使用鼠标位置执行的,而不是当前位置。()

2-Qt有时会在使用任务控制切换窗口时停止传播鼠标移动事件,因此zoom也会出现类似于#1的错误行为。(). 我在第二次单击窗口时没有突出显示芯片,因为没有调用mouseMoveEvent。我知道这在我的应用程序中不是一个bug,因为这是Qt提供的40000个芯片示例。我发布了这个问题的解决方法

3-setInteractive(false)不能与AnchorUnderMouse一起使用,因为用作转换中心的鼠标位置未更新:

Qt SDK似乎没有在不常见的场景(如使用鼠标滚轮缩放)中对鼠标移动事件进行很好的测试。

简单示例:

Graphics_view_zoom* z = new Graphics_view_zoom(ui->graphicsView);
z->set_modifiers(Qt::NoModifier);
class CGraphicsVew : public QGraphicsView
{
    Q_OBJECT

protected:
    void wheelEvent(QWheelEvent *event)
    {
        qreal deltaScale = 1;
        deltaScale += event->delta() > 0 ? 0.1 : -0.1;
        setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
        scale(deltaScale, deltaScale);
    }
};


<代码>

< P> QYQT回答得很好,这里提供C++函数,以防将来有人需要。

void CanvasView::zoomAt(const QPoint &centerPos, double factor)
{
    //QGraphicsView::AnchorUnderMouse uses ::centerOn() in it's implement, which must need scroll.
    //transformationAnchor() default is AnchorViewCenter, you need set NoAnchor while change transform, 
    //and combine all transform change will work more effective
    QPointF targetScenePos = mapToScene(centerPos);
    ViewportAnchor oldAnchor = this->transformationAnchor();
    setTransformationAnchor(QGraphicsView::NoAnchor);

    QTransform matrix = transform();
    matrix.translate(targetScenePos.x(), targetScenePos.y())
            .scale(factor, factor)
            .translate(-targetScenePos.x(), -targetScenePos.y());
    setTransform(matrix);

    setTransformationAnchor(oldAnchor);
}

void CanvasView::wheelEvent(QWheelEvent *event)
{
    if(event->modifiers().testFlag(Qt::ControlModifier))
    {
        double angle = event->angleDelta().y();

        double factor = qPow(1.0015, angle);    //smoother zoom
        zoomAt(event->pos(), factor);
        return;
    }

    QGraphicsView::wheelEvent(event);
}

围绕点矩阵公式进行缩放:,这与缩放相同。

最好能得到这个问题的明确答案,因为像这样的问题太多了,所有问题都有不同的答案,而且大多数问题在单独讨论之前似乎只解决了一半。。。当然,这并不太复杂,更重要的是正确的缩放和如何应用它..欢呼这真的很好,当我试图编译它时,有一些错误被标记了
QMouseEvent*mouse\u event=static\u cast(event)我不得不更改为
QMouseEvent*鼠标事件=(QMouseEvent*)事件
;除此之外,一切都好,谢谢。你介意解释一些硬编码的值是用来做什么的吗?这样我就可以确切地理解它的作用和原因了,再次感谢。我很快就接受你@Pavel!D
class CGraphicsVew : public QGraphicsView
{
    Q_OBJECT

protected:
    void wheelEvent(QWheelEvent *event)
    {
        qreal deltaScale = 1;
        deltaScale += event->delta() > 0 ? 0.1 : -0.1;
        setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
        scale(deltaScale, deltaScale);
    }
};
void GraphicsView::wheelEvent(QWheelEvent* event)
{
    switch (event->modifiers()) {
    case Qt::ControlModifier:
        if (event->angleDelta().x() != 0)
            QAbstractScrollArea::horizontalScrollBar()->setValue(QAbstractScrollArea::horizontalScrollBar()->value() - (event->delta()));
        else
            QAbstractScrollArea::verticalScrollBar()->setValue(QAbstractScrollArea::verticalScrollBar()->value() - (event->delta()));
        break;
    case Qt::ShiftModifier:
        QAbstractScrollArea::horizontalScrollBar()->setValue(QAbstractScrollArea::horizontalScrollBar()->value() - (event->delta()));
        break;
    case Qt::NoModifier:
        if (abs(event->delta()) == 120) {
            if (event->delta() > 0)
                zoomIn();
            else
                zoomOut();
        }
        break;
    default:
        QGraphicsView::wheelEvent(event);
        return;
    }
    event->accept();
}

const double zoomFactor = 1.5;

void GraphicsView::zoomIn()
{
    scale(zoomFactor, zoomFactor);
}

void GraphicsView::zoomOut()
{
    scale(1.0 / zoomFactor, 1.0 / zoomFactor);
}
void CanvasView::zoomAt(const QPoint &centerPos, double factor)
{
    //QGraphicsView::AnchorUnderMouse uses ::centerOn() in it's implement, which must need scroll.
    //transformationAnchor() default is AnchorViewCenter, you need set NoAnchor while change transform, 
    //and combine all transform change will work more effective
    QPointF targetScenePos = mapToScene(centerPos);
    ViewportAnchor oldAnchor = this->transformationAnchor();
    setTransformationAnchor(QGraphicsView::NoAnchor);

    QTransform matrix = transform();
    matrix.translate(targetScenePos.x(), targetScenePos.y())
            .scale(factor, factor)
            .translate(-targetScenePos.x(), -targetScenePos.y());
    setTransform(matrix);

    setTransformationAnchor(oldAnchor);
}

void CanvasView::wheelEvent(QWheelEvent *event)
{
    if(event->modifiers().testFlag(Qt::ControlModifier))
    {
        double angle = event->angleDelta().y();

        double factor = qPow(1.0015, angle);    //smoother zoom
        zoomAt(event->pos(), factor);
        return;
    }

    QGraphicsView::wheelEvent(event);
}