基于Qt实现图片查看器

一、简介

基于Qt实现的图片查看器。支持如下功能:

  • 图像放大、缩小、拖动
  • 矩形标注框显示,在放大缩小时,标注框的线宽始终保持固定宽度。

二、源码

  1. ImgViewWidget.hpp
// ImgViewWidget.hpp
#pragma once
#include <QImage>
#include <QPixmap>
#include <QWidget>

class ImgViewWidget final : public QWidget {
    Q_OBJECT
public:
    // 标注框(矩形区域和描述文字)
    using MarkBox = QPair<QRect, QString>;

    explicit ImgViewWidget(QWidget* parent = nullptr);
    ~ImgViewWidget() override = default;
    
    // 设置图像
    void set_image(const QImage& img);
    void set_pixmap(const QPixmap& img);
    // 复位图像(复位缩放和移动)
    void reset_transform();
    // 清除图像
    void clear();
    // 设置缩放是否启用(默认启用)
    void set_zoom_enable(bool enable);
    // 设置标注框显示(默认显示)
    void set_mark_visible(bool visible);
    // 设置标注框颜色,默认红色
    void set_mark_color(const QColor& color);
    // 设置标注框
    void set_mark_box(const QVector<MarkBox>& marks);

    [[nodiscard]] QPixmap original_pix() const;
    [[nodiscard]] bool zoom_enable() const;
    [[nodiscard]] bool mark_visible() const;
    [[nodiscard]] QColor mark_color() const;
    [[nodiscard]] const QVector<MarkBox>& mark_box() const;

protected:
    void paintEvent(QPaintEvent* event) override;
    void wheelEvent(QWheelEvent* event) override;
    void mousePressEvent(QMouseEvent* event) override;
    void mouseMoveEvent(QMouseEvent* event) override;
    void mouseReleaseEvent(QMouseEvent* event) override;
    void resizeEvent(QResizeEvent* event) override;

private:
    QPixmap original_pix_;
    QPixmap display_pix_;
    QPointF prev_point_;
    QPointF move_step_;
    float zoom_scale_;
    bool move_start_;
    bool zoom_enable_;
    bool mark_visible_;
    QColor mark_color_;
    QVector<MarkBox> marks_;
};

  1. ImgViewWidget.cpp
// ImgViewWidget.cpp
#include "ImgViewWidget.hpp"

#include <QPainter>
#include <QWheelEvent>

ImgViewWidget::ImgViewWidget(QWidget* parent)
    : QWidget(parent), zoom_scale_{1.0F}, move_start_{}, zoom_enable_{true}, mark_visible_{true}, mark_color_{Qt::red} {
    setWindowTitle(tr("Image View"));
}

void ImgViewWidget::set_image(const QImage& img) {
    set_pixmap(QPixmap::fromImage(img));
}

void ImgViewWidget::set_pixmap(const QPixmap& img) {
    original_pix_ = img;
    display_pix_ = original_pix_.scaled(zoom_scale_ * size(), Qt::KeepAspectRatio);
    update();
}

void ImgViewWidget::reset_transform() {
    zoom_scale_ = 1.0f;
    move_step_ = QPoint(0, 0);
    display_pix_ = original_pix_.scaled(zoom_scale_ * size(), Qt::KeepAspectRatio);
    update();
}

void ImgViewWidget::clear() {
    set_pixmap(QPixmap{});
}

QPixmap ImgViewWidget::original_pix() const {
    return original_pix_;
}

void ImgViewWidget::set_zoom_enable(bool enable) {
    zoom_enable_ = enable;
}

void ImgViewWidget::paintEvent(QPaintEvent* event) {
    QPainter painter(this);
    const QPoint top_left(
        move_step_.x() + (width() - display_pix_.width()) / 2, move_step_.y() + (height() - display_pix_.height()) / 2);
    painter.drawPixmap(top_left, display_pix_);

    if (!mark_visible_) {
        return;
    }
    const float factor = static_cast<float>(display_pix_.width()) / static_cast<float>(original_pix_.width());
    for (const auto& [box, label] : marks_) {
        const QRect adjust_box(top_left.x() + box.x() * factor, top_left.y() + box.y() * factor, box.width() * factor,
            box.height() * factor);
        painter.setPen(QPen(mark_color_, 1));
        painter.drawRect(adjust_box);
        painter.drawText(adjust_box.x(), adjust_box.y() - 2, label);
    }
}

void ImgViewWidget::wheelEvent(QWheelEvent* event) {
    if (!zoom_enable_) {
        return;
    }
    if (event->angleDelta().y() > 0) {
        zoom_scale_ *= 1.1;
    } else {
        zoom_scale_ *= 0.9;
    }
    display_pix_ = original_pix_.scaled(zoom_scale_ * size(), Qt::KeepAspectRatio);
    update();
}

void ImgViewWidget::mousePressEvent(QMouseEvent* event) {
    if (!zoom_enable_) {
        event->ignore();
        return;
    }
    if (event->button() == Qt::LeftButton) {
        if (!move_start_) {
            move_start_ = true;
            prev_point_ = event->globalPosition();
        }
    } else if (event->button() == Qt::RightButton) {
        reset_transform();
    }
}

void ImgViewWidget::mouseMoveEvent(QMouseEvent* event) {
    if (!zoom_enable_) {
        event->ignore();
        return;
    }
    if (move_start_) {
        const QPointF point = event->globalPosition();
        move_step_ += point - prev_point_;
        prev_point_ = point;
        update();
    }
}

void ImgViewWidget::mouseReleaseEvent(QMouseEvent* event) {
    if (!zoom_enable_) {
        event->ignore();
        return;
    }
    if (event->button() == Qt::LeftButton) {
        move_start_ = false;
    }
}

void ImgViewWidget::resizeEvent(QResizeEvent* event) {
    if (!original_pix_.isNull()) {
        display_pix_ = original_pix_.scaled(zoom_scale_ * size(), Qt::KeepAspectRatio);
    }
    update();
}

void ImgViewWidget::set_mark_visible(bool visible) {
    mark_visible_ = visible;
    update();
}

void ImgViewWidget::set_mark_color(const QColor& color) {
    mark_color_ = color;
    update();
}

void ImgViewWidget::set_mark_box(const QVector<ImgViewWidget::MarkBox>& marks) {
    marks_ = marks;
    update();
}

bool ImgViewWidget::zoom_enable() const {
    return zoom_enable_;
}

bool ImgViewWidget::mark_visible() const {
    return mark_visible_;
}

QColor ImgViewWidget::mark_color() const {
    return mark_color_;
}

const QVector<ImgViewWidget::MarkBox>& ImgViewWidget::mark_box() const {
    return marks_;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值