QGraphicsView Zoom avanti e indietro sotto la posizione del mouse usando la rotellina del mouse

Ho un’applicazione con una finestra QGraphicsView nel mezzo dello schermo. Voglio essere in grado di ingrandire e rimpicciolire usando la rotellina del mouse.

Attualmente ho reimplementato QGraphicsView e QGraphicsView la funzione di scroll del mouse in modo che non QGraphicsView l’immagine (come per default).

 void MyQGraphicsView::wheelEvent(QWheelEvent *event) { if(event->delta() > 0) { emit mouseWheelZoom(true); } else { emit mouseWheelZoom(false); } } 

quindi quando faccio scorrere, sto emettendo un segnale vero se il mouse ruota in avanti falso se il mouse ruota indietro.

Ho quindi collegato questo segnale a uno slot (funzione zoom vedi sotto ) nella class che gestisce la mia roba GUI. Ora, in sostanza, penso che la mia funzione di zoom non sia il modo migliore per farlo, ho visto alcuni esempi di persone che usano la funzione override wheelevent per impostare le scale ma non sono riuscito a trovare una risposta completa.

Così, invece, l’ho fatto, ma non è perfetto in alcun modo, quindi sto cercando questo per essere ottimizzato un po ‘o per un esempio di lavoro utilizzando la scala nella funzione evento ruota.

Inizializzo m_zoom_level a 0 nel costruttore.

 void Display::zoomfunction(bool zoom) { QMatrix matrix; if(zoom && m_zoom_level 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); } } 

Come puoi vedere sopra, sto usando una QMatrix e lo ridimensiono e impostandolo su Graphicsview e impostando l’ancora di trasformazione sotto il mouse, ma non funziona perfettamente a volte se sto scorrendo i carichi inizierà solo a ingrandire (che penso abbia a che fare con l’int looping o qualcosa del genere).

Come ho detto, l’aiuto con questo o un buon esempio di scala sotto il mouse sarebbe fantastico.

Tale zoom è un po ‘complicato. Lascia che condivida la mia stessa class per farlo.

Intestazione:

 #include  #include  /*! * 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 * performsd 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(); }; 

Fonte:

 #include "Graphics_view_zoom.h" #include  #include  #include  #include  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(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(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; } 

Esempio di utilizzo:

 Graphics_view_zoom* z = new Graphics_view_zoom(ui->graphicsView); z->set_modifiers(Qt::NoModifier); 

Ecco una soluzione che utilizza 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()) 

Ecco la versione Python funziona per me. Deriva dalla combinazione di risposte di @Stefan Reinhardt e @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()) 

Dopo molte frustrazioni, sembra funzionare. Il problema sembra essere che la transform QGraphicsView non ha nulla a che fare con la sua posizione di scorrimento, quindi il comportamento di QGraphicsView::mapToScene(const QPoint&) const dipende sia dalla posizione di scorrimento che dalla trasformazione. Ho dovuto guardare la fonte per mapToScene per capire questo.

Con questo in mente, ecco cosa ha funzionato: ricorda il punto della scena in cui punta il mouse, scala, mappa quella scena punta a coordinate del mouse, quindi regola le barre di scorrimento per far si che il punto si fermi sotto il mouse:

 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()); } 

È un po ‘tardi, ma ho camminato lo stesso oggi solo con Pyside, ma dovrebbe essere lo stesso …

L’approccio è “molto semplice”, ma mi è costato un po ‘di tempo … Per prima cosa imposta tutte le ancore su NoAnchor, poi prendi il punto della ruota, mappalo sulla scena, traduci la scena con questo valore, ridimensiona e infine tradurla indietro:

 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()) 

Questa era l’unica soluzione che ha funzionato per il mio scopo. IMHO è anche la soluzione più logica …

Puoi semplicemente utilizzare la funzionalità AnchorUnderMouse o AnchorViewCenter per mantenere lo stato AnchorViewCenter sotto il mouse o al centro. Questo funziona per me in Qt 5.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); } } 

Ecco una versione condensata della soluzione sopra; con solo il codice che devi inserire nell’evento wheel. Funziona con / senza barre di scorrimento nei miei test, perfettamente;)

 void MyGraphicsView::wheelEvent(QWheelEvent* pWheelEvent) { if (pWheelEvent->modifiers() & Qt::ControlModifier) { // Do a wheel-based zoom about the cursor position double angle = pWheelEvent->angleDelta().y(); double factor = qPow(1.0015, angle); auto targetViewportPos = pWheelEvent->pos(); auto targetScenePos = mapToScene(pWheelEvent->pos()); scale(factor, factor); centerOn(targetScenePos); QPointF deltaViewportPos = targetViewportPos - QPointF(viewport()->width() / 2.0, viewport()->height() / 2.0); QPointF viewportCenter = mapFromScene(targetScenePos) - deltaViewportPos; centerOn(mapToScene(viewportCenter.toPoint())); return; } 

Zoom più fluido

 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()); } 

Combinare la soluzione di @veslam: s con il codice Smooth Zoom da QT Wiki ( https://wiki.qt.io/Smooth_Zoom_In_QGraphicsView ) sembra funzionare molto bene:

Fonte:

 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(); } 

Intestazione:

 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(); };