在QScrollArea中缩放和平移图片

6
我创建了一个预览,显示了一个渲染的图像。我使用了图片查看器示例来实现缩放功能 - 所以我有一个继承了QScrollArea的类,能够在QLabel中显示图像,并具有特定限制的缩放/放大/适应大小功能。我将滚动条设置为“按需显示”。
作为新的要求,我必须能够进行平移,并且不显示滚动条
我一直在寻找方法 - 并发现示例中的人们使用鼠标按下、移动和释放事件来将图像上的点与滚动条相关联。
问题:
1)如果滚动条不可见,则移动的方向是意外的 - 在平移对象时,对象向鼠标移动(保持在鼠标下方),而滚动条则向相反方向移动
2)我认为移动受到滚动条大小的限制,因此...如果我计算反向移动,我会在仍有一定空间可以朝一个方向移动时撞到墙壁
3)这在缩放时将无法工作,而这正是需要平移的时候;需要进行更复杂的计算。
我可以选择使用QGraphicsView,并且...
setDragMode(ScrollHandDrag);

它也可以与缩放很好地配合使用,我不需要自己实现它。原因是,我需要添加一个QGraphicsScene,和包含我想要的图像的QGraphicsPixmapItem - 然后找到如何禁用除平移之外的所有鼠标事件 - 并仍然使用QScrollArea来容纳QGraphicsView;似乎这太费事了(并且这是为一个嵌入式设备设计的,速度和内存都很小,应该非常轻量化)。最好的选择是什么?有没有一种尽可能轻量级的方法来在查看器中平移缩放的图像?
1个回答

8

考虑到自定义可缩放和平移的像素图查看器的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();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接