一、简介
基于Qt实现的图片查看器。支持如下功能:
- 图像放大、缩小、拖动
- 矩形标注框显示,在放大缩小时,标注框的线宽始终保持固定宽度。
二、源码
- 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_;
};
- 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_;
}