1. 程式人生 > >QGraphicsView 如何實現百度地圖按照滑鼠點進行放大縮小效果

QGraphicsView 如何實現百度地圖按照滑鼠點進行放大縮小效果

一、簡述

前段時間用了QGraphicsView做了一些工作,然而如何實現QGraphicsView的放大縮小的效果也很簡單,直接重寫QGraphicsViewwheelEvent事件即可,上一篇文章中也提到了,但是僅僅通過以下程式碼實現放大縮小的效果並不是很完美。

雖然達到了放大縮小的效果,但是並沒有像百度地圖一樣能夠按照滑鼠某一點進行縮放,僅靠以下程式碼對view進行縮放會導致view上的item在放大縮小的過程中跑偏了。


void CustomView::wheelEvent(QWheelEvent *event)
{
    // 當前放縮倍數;
    qreal scaleFactor = this
->matrix().m11(); int wheelDeltaValue = event->delta(); // 向上滾動,放大; if (wheelDeltaValue > 0) { this->scale(1.2, 1.2); } // 向下滾動,縮小; else { this->scale(1.0 / 1.2, 1.0 / 1.2); } }

我們看一下僅靠以上程式碼實現的放大縮小的效果。

從下圖中我們看到把圖中小矩形放到螢幕中央進行放大縮小時,效果還是可以的,但是當我們吧小矩形拖到螢幕靠左位置時(或者靠右,只要不是中央位置),我們發現放大所需時小矩形位置偏移較為嚴重,為了就解決這個問題,閉關修煉了三天,終於解決了。期間各種百度,看助手文件,後來有位小夥伴推薦了一篇文章用MFC實現了圖片按照滑鼠點進行放大縮小效果

,雖然達到了類似的效果,但是和QGraphicsView的原理不一樣。那篇文章中通過放大後重新計算圖片的位置,然後在對應位置進行重繪。

最後的最後,在QGraphicsView的原始碼中找到了方法的思路。

這裡寫圖片描述

我們再看一下百度地圖放大縮小的效果(因為受到了圖片大小的限制,就截了一小塊)。
大家可以看到滑鼠分別放到兩個綠色矩形區域進行放大縮小的效果,可以看到都是按照滑鼠點進行縮放的。

這裡寫圖片描述

二、程式碼之路

好了,上面說明了問題,下面就開始針對這個問題進行解決。話不多說,直接上程式碼。

void CustomView::wheelEvent(QWheelEvent *event)
{
    // 獲取當前滑鼠相對於view的位置;
QPointF cursorPoint = event->pos(); // 獲取當前滑鼠相對於scene的位置; QPointF scenePos = this->mapToScene(QPoint(cursorPoint.x(), cursorPoint.y())); // 獲取view的寬高; qreal viewWidth = this->viewport()->width(); qreal viewHeight = this->viewport()->height(); // 獲取當前滑鼠位置相當於view大小的橫縱比例; qreal hScale = cursorPoint.x() / viewWidth; qreal vScale = cursorPoint.y() / viewHeight; // 當前放縮倍數; qreal scaleFactor = this->matrix().m11(); int wheelDeltaValue = event->delta(); // 向上滾動,放大; if (wheelDeltaValue > 0) { this->scale(1.2, 1.2); } // 向下滾動,縮小; else { this->scale(1.0 / 1.2, 1.0 / 1.2); } // 將scene座標轉換為放大縮小後的座標; QPointF viewPoint = this->matrix().map(scenePos); // 通過滾動條控制view放大縮小後的展示scene的位置; horizontalScrollBar()->setValue(int(viewPoint.x() - viewWidth * hScale)); verticalScrollBar()->setValue(int(viewPoint.y() - viewHeight * vScale)); }

通過上面的程式碼即可實現QGraphicsView按照滑鼠點進行放大縮小的效果。

我們看一下效果,圖一中我分別將將小矩形拖到螢幕的上、下、左、右四個方位,我們發現都是按照滑鼠點進行了放縮(gif圖錄制軟體在拖到小矩形時生成的圖片有點陰影,大家可以忽略)。

圖二中,分別將滑鼠放置在螢幕的四角,我們可以看到很明確的效果。

圖一

這裡寫圖片描述

圖二

這裡寫圖片描述

附上一張效果圖(可能需要放大網頁看,相對清楚一點)

這裡寫圖片描述

下面是QGraphicsView::centerOn(const QPointF &pos)方法的原始碼。以上程式碼也是參考了這個方法後得到的結果。這個方法也就是通過滑動滾動條的方法將所給的點(該點是相對於scene的)放置在view 的中央位置。而我們想要實現按照滑鼠點進行放大縮小效果,只需要計算當前滑鼠位置相對於view大小的比例,centerOn方法中是 1/2 ,我們只要替換為相對應的比例即可。

這裡寫圖片描述

/*!
    Scrolls the contents of the viewport to ensure that the scene
    coordinate \a pos, is centered in the view.

    Because \a pos is a floating point coordinate, and the scroll bars operate
    on integer coordinates, the centering is only an approximation.

    \note If the item is close to or outside the border, it will be visible
    in the view, but not centered.

    \sa ensureVisible()
*/
void QGraphicsView::centerOn(const QPointF &pos)
{
    Q_D(QGraphicsView);
    qreal width = viewport()->width();
    qreal height = viewport()->height();
    QPointF viewPoint = d->matrix.map(pos);
    QPointF oldCenterPoint = pos;

    if (!d->leftIndent) {
        if (isRightToLeft()) {
            qint64 horizontal = 0;
            horizontal += horizontalScrollBar()->minimum();
            horizontal += horizontalScrollBar()->maximum();
            horizontal -= int(viewPoint.x() - width / 2.0);
            horizontalScrollBar()->setValue(horizontal);
        } else {
            horizontalScrollBar()->setValue(int(viewPoint.x() - width / 2.0));
        }
    }
    if (!d->topIndent)
        verticalScrollBar()->setValue(int(viewPoint.y() - height / 2.0));
    d->lastCenterPoint = oldCenterPoint;
}