Масштабировать изображение прямо под мышью в Qt 4

Подобно google.com/maps, я хотел бы, чтобы, если моя мышь находится над QGraphicsItem, то, когда я перемещаю колесико вперед и назад, область изображения под мышью была в центре моего QGraphicsView.

Могу ли я выполнить это в методе wheelEvent пользовательского QGraphicsIte, который я создал?

У меня есть такой код

update();
qreal factor = 1.2;
if (event->delta() < 0)
  factor = 1.0 / factor;
scale(factor, factor);
scaleFactor *=factor;
this->scene()->setSceneRect(0,0,this->boundingRect().width(), this->boundingRect().height());

Что я могу добавить к этому, чтобы дать желаемый эффект?

Например, в картах Google, если вы наведете указатель мыши на штат Юта и продолжите увеличивать масштаб с помощью колесика мыши, в конечном итоге в окне просмотра останется только Юта.


person Derek    schedule 24.02.2011    source источник


Ответы (3)


См. также setTransformationAnchor( QGraphicsView::AnchorUnderMouse );

person handle    schedule 15.07.2013

Давайте перефразируем вашу цель более прямо. Событие на колесе:

  1. Сцена должна увеличиваться/уменьшаться
  2. Расположение сцены под мышью должно оставаться под мышью

Вы реализовали (1) с вызовом масштаба. Остается (2). Чтобы прокрутить вид до определенного места, вам нужно сделать:

horizontalScrollbar().setValue(x);
verticalScrollbar().setValue(y);

Это прокручивает сцену так, что (x, y) находится в верхнем левом углу представления. Но вы хотите, чтобы (x, y) находились в позиции мыши (относительно QGraphicsView). Поэтому вам нужно прокрутить до (x-mx, y-my), где (mx, my) — положение мыши относительно QGraphicsView, а (x, y) — положение сцены, которое было под мышью до события колеса.

Это не проверено и может быть неправильным в деталях (например, в точном поведении QScrollBar::setValue), но математическая концепция верна, поэтому вам должно быть достаточно, чтобы заставить ее работать.

person Stefan Monov    schedule 24.02.2011
comment
Могу ли я сделать это из события колеса QGraphicsItem? Item - это то, что имеет в нем WheelEvent, а не QGraphicsView - person Derek; 24.02.2011
comment
Ах, я предположил, что ваше колесное событие было в QGV. Если это в QGI, вы все равно сможете это сделать. Единственное различие заключается в том, как вы определяете (x, y) (используйте QGraphicsSceneMouseEvent::screenPos) и (mx, my) (используйте QGraphicsSceneMouseEvent::scenePos). В любом случае, я думаю, что чище реализовать свой wheelEvent в QGV, а не в QGI. - person Stefan Monov; 25.02.2011

Уравнение похоже на ,,,

margin = -half_overflow + (((center_offset - click_offset) * resize_ratio) - (center_offset - click_offset));

... и вы должны сделать это для x и y.

Ниже я вставлю некоторый код, который это делает, но имейте в виду, что этот код работает с квадратными изображениями, поэтому я использую ширину как для ширины, так и для высоты. Чтобы использовать это на прямоугольных изображениях, вам также нужно будет захватить высоту и использовать ее для всех вычислений Y, где у меня есть ширина, а также установить все переменные, которые я использую, такие как content_width и т. д. Но алгоритм все там и можно узнать из кода...

        zoomChart: function(e)
        {
                var chart_max = 2048;
                var zoom_max = egapp.user.options.zoom_multiplier || 2; // >= 2
                var chart_bounds = $('#egapp_chart_bounds');
                var chart_imgs = chart_bounds.find('img');
                var width = chart_imgs.width();
                if (width == chart_bounds.width()) { // zoom in
                        // margin = -half_overflow + (((center_offset - click_offset) * resize_ratio) - (center_offset - click_offset));
                        chart_bounds.removeClass('egapp_zoom_in');
                        if (width == chart_max)
                                return;
                        chart_bounds.addClass('egapp_zoom_out');
                        var new_width = parseInt(width * zoom_max);
                        if (new_width > chart_max)
                                new_width = chart_max;
                        var ratio = new_width / width;
                        var moveX = moveY = -((new_width - width) / 2);
                        var chart_offset = chart_bounds.offset();
                        var offsetX = (chart_offset.left + (width / 2)) - e.pageX;
                        var offsetY = (chart_offset.top + (width / 2)) - e.pageY;
                        moveX += parseInt((offsetX * ratio) - offsetX);
                        moveY += parseInt((offsetY * ratio) - offsetY);
                        chart_imgs.animate({width: new_width+'px', height: new_width+'px', marginLeft: moveX+'px', marginTop: moveY+'px'}, 'fast');
                        chart_bounds.addClass('egapp_zoom_out');
                }
                else { // zoom out
                        var new_width = egapp.content_width - 10;
                        chart_imgs.animate({width: new_width+'px', height: new_width+'px', marginLeft: 0, marginTop: 0}, 'fast');
                        chart_bounds.removeClass('egapp_zoom_out').addClass('egapp_zoom_in');
                }
        },

HTML - это что-то вроде...

<div id="egapp_chart_bounds" style="width: 608px; height: 608px;" class="egapp_zoom_in">
    <img id="egapp_timeline_chart" src="some.image" class="egapp_chart" style="width: 608px; height: 608px; margin-left: 0px; margin-top: 0px;">
    <img id="egapp_expression_chart_9" src="overlay.image" class="egapp_chart_overlay" style="width: 608px; height: 608px;">
</div>

А CSS такой...

#egapp_chart_bounds{
        overflow: hidden;
}
.egapp_chart, .egapp_chart_overlay{
        position: absolute;
}

.egapp_zoom_in{
        cursor: -moz-zoom-in;
        cursor: -webkit-zoom-in;
        cursor: zoom-in;
}
.egapp_zoom_out{
        cursor: -moz-zoom-out;
        cursor: -webkit-zoom-out;
        cursor: zoom-out;
}
person ekerner    schedule 29.08.2014