Масштабирование и панорамирование изображения в QScrollArea

Я создал предварительный просмотр, который показывает визуализированное изображение. Я использовал пример средства просмотра изображений для масштабирования. поэтому у меня есть класс, наследующий QScrollArea, способный отображать изображение в QLabel и увеличивать/уменьшать/соответствовать определенным ограничениям. Я показывал полосы прокрутки «по мере необходимости».

В качестве нового требования я должен иметь возможность выполнять панорамирование и не показывать полосы прокрутки.

Я искал способы сделать это и нашел примеры людей, использующих события нажатия, перемещения и отпускания мыши, чтобы связать точку на изображении с полосами прокрутки.
Проблемы:
1) направление движения, если полосы прокрутки невидимы, является неожиданным - при панорамировании объект перемещается в направлении мыши (остается под мышью), в то время как полосы прокрутки перемещаются в противоположном направлении
2) Я думаю, что движение ограничено размером полосы прокрутки, поэтому... если я вычислю обратный ход, я ударюсь о стену, пока все еще есть место для перемещения в одном направлении
3) Это не будет работать с масштабированием, а именно тогда, когда необходимо панорамирование; потребуются более сложные расчеты.

Я мог бы попеременно использовать QGraphicsView и

setDragMode(ScrollHandDrag);

Это также хорошо работало бы с масштабированием, и мне не пришлось бы реализовывать это самостоятельно.
Причина, по которой я еще не сделал этого, заключается в том, что мне нужно было бы добавить QGraphicsScene и QGraphicsPixmapItem, содержащие изображение, которое я хочу - затем найдите, как отключить все события мыши, кроме панорамирования - и по-прежнему используйте QScrollArea для удержания QGraphicsView;
Кажется, это слишком много накладных расходов (и это должно быть чрезвычайно легким для встроенного устройства с мало скорости и памяти).

Какой лучший вариант? есть ли способ панорамировать увеличенное изображение в средстве просмотра, как можно более легкое?


person Thalia    schedule 18.11.2016    source источник


Ответы (1)


Учитывая, что реализация paintEvent для пользовательского средства просмотра растровых изображений с возможностью масштабирования и панорамирования состоит из 5 строк, ее можно было бы также реализовать с нуля:

// https://github.com/KubaO/stackoverflown/tree/master/questions/image-panzoom-40683840
#include <QtWidgets>
#include <QtNetwork>

class ImageViewer : public QWidget {
    QPixmap m_pixmap;
    QRectF m_rect;
    QPointF m_reference;
    QPointF m_delta;
    qreal m_scale = 1.0;
    void paintEvent(QPaintEvent *) override {
        QPainter p{this};
        p.translate(rect().center());
        p.scale(m_scale, m_scale);
        p.translate(m_delta);
        p.drawPixmap(m_rect.topLeft(), m_pixmap);
    }
    void mousePressEvent(QMouseEvent *event) override {
        m_reference = event->pos();
        qApp->setOverrideCursor(Qt::ClosedHandCursor);
        setMouseTracking(true);
    }
    void mouseMoveEvent(QMouseEvent *event) override {
        m_delta += (event->pos() - m_reference) * 1.0/m_scale;
        m_reference = event->pos();
        update();
    }
    void mouseReleaseEvent(QMouseEvent *) override {
        qApp->restoreOverrideCursor();
        setMouseTracking(false);
    }
public:
    void setPixmap(const QPixmap &pix) {
        m_pixmap = pix;
        m_rect = m_pixmap.rect();
        m_rect.translate(-m_rect.center());
        update();
    }
    void scale(qreal s) {
        m_scale *= s;
        update();
    }
    QSize sizeHint() const override { return {400, 400}; }
};

Сопоставимый виджет на основе QGraphicsView был бы лишь немного короче и имел бы немного больше накладных расходов, если бы растровое изображение было очень маленьким. Для больших растровых изображений время, затрачиваемое на визуализацию растрового изображения, значительно перевешивает любые накладные расходы из-за механизма QGraphicsScene/QGraphicsView. В конце концов, сама сцена статична, и это идеальная рабочая точка для исполнения QGraphicsView.

class SceneImageViewer : public QGraphicsView {
    QGraphicsScene m_scene;
    QGraphicsPixmapItem m_item;
public:
    SceneImageViewer() {
        setScene(&m_scene);
        m_scene.addItem(&m_item);
        setDragMode(QGraphicsView::ScrollHandDrag);
        setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
        setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
        setResizeAnchor(QGraphicsView::AnchorViewCenter);
    }
    void setPixmap(const QPixmap &pixmap) {
        m_item.setPixmap(pixmap);
        auto offset = -QRectF(pixmap.rect()).center();
        m_item.setOffset(offset);
        setSceneRect(offset.x()*4, offset.y()*4, -offset.x()*8, -offset.y()*8);
        translate(1, 1);
    }
    void scale(qreal s) { QGraphicsView::scale(s, s); }
    QSize sizeHint() const override { return {400, 400}; }
};

И тестовый жгут:

int main(int argc, char *argv[])
{
    QApplication a{argc, argv};
    QWidget ui;
    QGridLayout layout{&ui};
    ImageViewer viewer1;
    SceneImageViewer viewer2;
    QPushButton zoomOut{"Zoom Out"}, zoomIn{"Zoom In"};
    layout.addWidget(&viewer1, 0, 0);
    layout.addWidget(&viewer2, 0, 1);
    layout.addWidget(&zoomOut, 1, 0, 1, 1, Qt::AlignLeft);
    layout.addWidget(&zoomIn, 1, 1, 1, 1, Qt::AlignRight);

    QNetworkAccessManager mgr;
    QScopedPointer<QNetworkReply> rsp(
                mgr.get(QNetworkRequest({"http://i.imgur.com/ikwUmUV.jpg"})));
    QObject::connect(rsp.data(), &QNetworkReply::finished, [&]{
        if (rsp->error() == QNetworkReply::NoError) {
            QPixmap pixmap;
            pixmap.loadFromData(rsp->readAll());
            viewer1.setPixmap(pixmap);
            viewer2.setPixmap(pixmap);
        }
        rsp.reset();
    });
    QObject::connect(&zoomIn, &QPushButton::clicked, [&]{
        viewer1.scale(1.1); viewer2.scale(1.1);
    });
    QObject::connect(&zoomOut, &QPushButton::clicked, [&]{
        viewer1.scale(1.0/1.1); viewer2.scale(1.0/1.1);
    });
    ui.show();
    return a.exec();
}
person Kuba hasn't forgotten Monica    schedule 18.11.2016