首页 > 其他分享 >使用Qt实现自定义GraphicsView

使用Qt实现自定义GraphicsView

时间:2024-08-09 10:09:39浏览次数:14  
标签:Qt 自定义 void GraphicsView ROIRectItem QRectF GraphView include imageRect

在本文中,我们将介绍如何使用Qt实现一个自定义的GraphicsView,主要是作为笔记使用QGraphicsView框架方面的使用手法、套路,对代码就不做过多的解释了,它具有以下功能:

  1. 显示图像
  2. 可拖动的十字标记(CrossMarkItem)
  3. 可调整大小的ROI(Region of Interest)矩形
  4. FPS和日期时间显示
  5. 保存和加载十字标记和ROI矩形的位置

1. 项目结构

我们的项目包含以下主要文件:

  • MainWindow.h / MainWindow.cpp
  • GraphView.h / GraphView.cpp
  • CrossMarkItem.h / CrossMarkItem.cpp
  • ROIRectItem.h / ROIRectItem.cpp
  • CMakeLists.txt

2. 实现CrossMarkItem

首先,我们实现一个可拖动的十字标记:

CrossMarkItem.h:

#pragma once
#include <QGraphicsItem>
#include <QRectF>

class CrossMarkItem : public QObject, public QGraphicsItem
{
    Q_OBJECT
    Q_INTERFACES(QGraphicsItem)

public:
    CrossMarkItem(QGraphicsItem *parent = nullptr);
    ~CrossMarkItem();

    QRectF boundingRect() const override;
    void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = nullptr) override;
    void setSize(qreal size);

protected:
    QVariant itemChange(GraphicsItemChange change, const QVariant& value) override;

signals:
    void positionChanged(const QPointF& pos);

private:
    qreal m_size;
};

CrossMarkItem.cpp:

#include <QPainter>
#include "CrossMarkItem.h"

CrossMarkItem::CrossMarkItem(QGraphicsItem* parent)
    : QGraphicsItem(parent), m_size(10)
{
    setFlag(QGraphicsItem::ItemIsMovable);
    setFlag(QGraphicsItem::ItemSendsGeometryChanges);
}

CrossMarkItem::~CrossMarkItem()
{
}

QRectF CrossMarkItem::boundingRect() const
{
    return QRectF(-m_size / 2, -m_size / 2, m_size, m_size);
}

void CrossMarkItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
{
    Q_UNUSED(option);
    Q_UNUSED(widget);
    painter->setPen(QPen(Qt::green, 2));
    painter->drawLine(QPointF(-m_size / 2, 0), QPointF(m_size / 2, 0));
    painter->drawLine(QPointF(0, -m_size / 2), QPointF(0, m_size / 2));
}

void CrossMarkItem::setSize(qreal size)
{
    m_size = size;
    update();
}

QVariant CrossMarkItem::itemChange(GraphicsItemChange change, const QVariant& value)
{
    if (change == GraphicsItemChange::ItemPositionChange && scene()) 
        emit positionChanged(value.toPointF());
    return QGraphicsItem::itemChange(change, value);
}

3. 实现ROIRectItem

接下来,我们实现一个可调整大小的ROI矩形:

ROIRectItem.h:

#pragma once
#include <QGraphicsItem>
#include <QGraphicsRectItem>

class ROIRectItem : public QGraphicsRectItem
{
public:
    explicit ROIRectItem(QGraphicsItem *parent = nullptr);
    ~ROIRectItem();

    enum ResizeHandle { None, TopLeft, TopRight, BottomLeft, BottomRight };

    void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = nullptr) override;
    QRectF boundingRect() const override;
    void setRelativeRect(const QRectF& relativeRect);
    QRectF relativeRect() const { return m_relativeRect; }
    void updateAbsoluteRect(const QRectF& imageRect);

protected:
    void mousePressEvent(QGraphicsSceneMouseEvent* event) override;
    void mouseMoveEvent(QGraphicsSceneMouseEvent* event) override;
    void mouseReleaseEvent(QGraphicsSceneMouseEvent* event) override;
    void hoverMoveEvent(QGraphicsSceneHoverEvent* event) override;

private:
    ResizeHandle getResizeHandle(const QPointF& pos) const;
    void updateCursor(ResizeHandle handle);
    void resizeRect(const QPointF& delta);

private:    
    ResizeHandle m_activeHandle;
    QPointF m_lastPos;
    QRectF m_imageRect;
    QRectF m_relativeRect;
};

ROIRectItem.cpp:

#include <QPainter>
#include <QCursor>
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsSceneHoverEvent>
#include "ROIRectItem.h"

ROIRectItem::ROIRectItem(QGraphicsItem* parent):
    QGraphicsRectItem(parent), m_activeHandle(None)
{
    setFlag(QGraphicsItem::ItemIsMovable);
    setFlag(QGraphicsItem::ItemSendsGeometryChanges);
    setAcceptHoverEvents(true);
}

ROIRectItem::~ROIRectItem()
{
}

void ROIRectItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
{
    Q_UNUSED(option);
    Q_UNUSED(widget);
    painter->setPen(QPen(Qt::green, 2));
    painter->drawRect(rect());
}

QRectF ROIRectItem::boundingRect() const
{
    return rect();
}

void ROIRectItem::setRelativeRect(const QRectF& relativeRect)
{
    m_relativeRect = relativeRect;
}

void ROIRectItem::updateAbsoluteRect(const QRectF& imageRect)
{
    m_imageRect = imageRect;
    QRectF r(
        m_imageRect.x() + m_relativeRect.x() * m_imageRect.width(),
        m_imageRect.y() + m_relativeRect.y() * m_imageRect.height(),
        m_relativeRect.width() * m_imageRect.width(),
        m_relativeRect.height() * m_imageRect.height()
    );
    setRect(r);
}

void ROIRectItem::mousePressEvent(QGraphicsSceneMouseEvent* event)
{
    m_activeHandle = getResizeHandle(event->pos());
    m_lastPos = event->pos();
    QGraphicsItem::mousePressEvent(event);
}

void ROIRectItem::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
{
    QPointF delta = event->pos() - m_lastPos;
    QRectF r;
    if (m_activeHandle != None)
    {
        resizeRect(delta);
        r = rect();
    }
    else
    {
        r = rect().translated(delta);
        setRect(r);
    }

    // Ensure the rect is within the bounds of m_imageRect
    if (r.left() < m_imageRect.left()) r.moveLeft(m_imageRect.left() + 10);
    if (r.top() < m_imageRect.top()) r.moveTop(m_imageRect.top() + 10);
    if (r.right() > m_imageRect.right()) r.moveRight(m_imageRect.right() - 10);
    if (r.bottom() > m_imageRect.bottom()) r.moveBottom(m_imageRect.bottom() - 10);

    m_lastPos = event->pos();
    // Update relative rect
    m_relativeRect = QRectF(
        (r.x() - m_imageRect.x()) / m_imageRect.width(),
        (r.y() - m_imageRect.y()) / m_imageRect.height(),
        r.width() / m_imageRect.width(),
        r.height() / m_imageRect.height()
    );
    update();
}

void ROIRectItem::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
{
    m_activeHandle = None;
    QGraphicsItem::mouseReleaseEvent(event);
}

void ROIRectItem::hoverMoveEvent(QGraphicsSceneHoverEvent* event)
{
    updateCursor(getResizeHandle(event->pos()));
    QGraphicsItem::hoverMoveEvent(event);
}

ROIRectItem::ResizeHandle ROIRectItem::getResizeHandle(const QPointF& pos) const
{
    QRectF r = rect();
    qreal handleSize = 12;
    if (QRectF(r.topLeft() - QPointF(handleSize / 2, handleSize / 2), QSizeF(handleSize, handleSize)).contains(pos)) return TopLeft;
    if (QRectF(r.topRight() - QPointF(handleSize / 2, handleSize / 2), QSizeF(handleSize, handleSize)).contains(pos)) return TopRight;
    if (QRectF(r.bottomLeft() - QPointF(handleSize / 2, handleSize / 2), QSizeF(handleSize, handleSize)).contains(pos)) return BottomLeft;
    if (QRectF(r.bottomRight() - QPointF(handleSize / 2, handleSize / 2), QSizeF(handleSize, handleSize)).contains(pos)) return BottomRight;
    return None;
}

void ROIRectItem::updateCursor(ResizeHandle handle)
{
    switch (handle)
    {
    case ROIRectItem::TopLeft:
    case ROIRectItem::BottomRight:
        setCursor(Qt::SizeFDiagCursor);
        break;
    case ROIRectItem::TopRight:
    case ROIRectItem::BottomLeft:
        setCursor(Qt::SizeBDiagCursor);
        break;     
    default:
        setCursor(Qt::ArrowCursor);
        break;
    }
}

void ROIRectItem::resizeRect(const QPointF& delta)
{
    QRectF r = rect();
    switch (m_activeHandle)
    {
    case ROIRectItem::TopLeft:
        r.setTopLeft(r.topLeft() + delta);
        break;
    case ROIRectItem::TopRight:
        r.setTopRight(r.topRight() + delta);
        break;
    case ROIRectItem::BottomLeft:
        r.setBottomLeft(r.bottomLeft() + delta);
        break;
    case ROIRectItem::BottomRight:
        r.setBottomRight(r.bottomRight() + delta);
        break;
    default:
        break;
    }
    
    QRectF normalizedRect = r.normalized();
    qreal minWidth = 50; 
    qreal minHeight = 50; 
    if (normalizedRect.width() < minWidth) 
    {
        if (m_activeHandle == ROIRectItem::TopLeft || m_activeHandle == ROIRectItem::BottomLeft) normalizedRect.setLeft(normalizedRect.right() - minWidth);
        else normalizedRect.setRight(normalizedRect.left() + minWidth);
    }
    if (normalizedRect.height() < minHeight) {
        if (m_activeHandle == ROIRectItem::TopLeft || m_activeHandle == ROIRectItem::TopRight) normalizedRect.setTop(normalizedRect.bottom() - minHeight);
        else normalizedRect.setBottom(normalizedRect.top() + minHeight);
    }
    setRect(normalizedRect);
}

4. 实现GraphView

现在,我们来实现主要的GraphView类:

GraphView.h:

#pragma once
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsPixmapItem>
#include <QTimer>
#include <QDateTime>
#include "CrossMarkItem.h"
#include "ROIRectItem.h"

class GraphView : public QGraphicsView
{
    Q_OBJECT
public:
    explicit GraphView(QWidget *parent = nullptr);
    ~GraphView();

    void setImage(const QImage& image);
    void setKeepAspectRatio(bool keep);
    
    QPointF getMarkItemPositionInImage();
    void setMarkItemPositionInImage(const QPointF& relativePos = QPointF(0.5, 0.5));
    QRectF getROIRectInfoInImage();
    void setROIRectInfoInImage(const QRectF& relativeRect = QRectF(0.25, 0.25, 0.5, 0.5));

protected:
    void resizeEvent(QResizeEvent* event) override;
    void mousePressEvent(QMouseEvent* event) override;
    void mouseMoveEvent(QMouseEvent* event) override;
    void mouseReleaseEvent(QMouseEvent* event) override;

private:
    void loadMarkItemPositionInImage();
    void saveMarkItemPositionInImage();
    void loadROIRectInfoInImage();
    void saveROIRectInfoInImage();

public slots:
    void setMarkItemMovable(bool movable) { m_markItem->setFlag(QGraphicsItem::ItemIsMovable, movable); }
    void setMarkItemVisible(bool visible) { m_markItem->setVisible(visible); }
    
    void setROIRectMovable(bool movable) { m_roiRectItem->setFlag(QGraphicsItem::ItemIsMovable, movable); }
    void setROIRectVisible(bool visible) { m_roiRectItem->setVisible(visible); }

private slots:
    void updateFPS();
    void updateImageScale();
    void updateTextItems();
    void updateMarkItem();
    void updateROIRect();
    
    void onMarkItemPositionChanged(const QPointF& pos) 
    {
        Q_UNUSED(pos);
        saveMarkItemPositionInImage(); 
    }

private:
    QGraphicsScene* m_scene;
    QGraphicsPixmapItem* m_imageItem;
    QGraphicsTextItem* m_fpsItem;
    QGraphicsTextItem* m_dateTimeItem;
    CrossMarkItem* m_markItem;
    ROIRectItem* m_roiRectItem;
    
    QTimer* m_fpsTimer;
    QTimer* m_dateTimeTimer;
    float m_fps;
    int m_frameCount;
    bool m_keepAspectRatio;
    bool m_drawingROIRect;
    QPointF m_roiRectStartPos;
};

GraphView.cpp:

#include <QResizeEvent>
#include <QOPenGLWidget>
#include <QSettings>
#include <QApplication>
#include <QDir>
#include <QDebug>
#include "GraphView.h"

GraphView::GraphView(QWidget *parent)
    : QGraphicsView(parent), m_scene(new QGraphicsScene(this)), m_frameCount(0), m_fps(0.0f), 
    m_keepAspectRatio(false), m_drawingROIRect(false)
{
#ifdef WITH_GPU_RENDER
    // 使用 OpenGL
    QOpenGLWidget* glWidget = new QOpenGLWidget(this);
    QSurfaceFormat format;
    format.setSwapInterval(1); // 启用 VSync
    glWidget->setFormat(format);
    setViewport(glWidget);  
#endif // WITH_GPU_RENDER
    
    // 优化设置
    setViewportUpdateMode(QGraphicsView::SmartViewportUpdate);
    setCacheMode(QGraphicsView::CacheBackground);
    setOptimizationFlags(QGraphicsView::DontSavePainterState);
    setRenderHint(QPainter::Antialiasing, true);
    setRenderHint(QPainter::SmoothPixmapTransform, true);

    setScene(m_scene);

    m_imageItem = new QGraphicsPixmapItem();
    m_scene->addItem(m_imageItem);

    m_markItem = new CrossMarkItem();
    m_markItem->setZValue(100);
    m_scene->addItem(m_markItem);

    m_roiRectItem = new ROIRectItem();
    m_scene->addItem(m_roiRectItem);

    m_fpsItem = new QGraphicsTextItem();
    m_fpsItem->setDefaultTextColor(Qt::white);
    m_scene->addItem(m_fpsItem);

    m_dateTimeItem = new QGraphicsTextItem();
    m_dateTimeItem->setDefaultTextColor(Qt::white);
    m_scene->addItem(m_dateTimeItem);

    // Update FPS every second
    m_fpsTimer = new QTimer(this);
    connect(m_fpsTimer, &QTimer::timeout, this, &GraphView::updateFPS);
    m_fpsTimer->start(1000);

    // Update date time every second
    m_dateTimeTimer = new QTimer(this);
    connect(m_dateTimeTimer, &QTimer::timeout, this, &GraphView::updateTextItems);
    m_dateTimeTimer->start(1000);

    setBackgroundBrush(Qt::black);
    setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
}

GraphView::~GraphView()
{
    saveMarkItemPositionInImage();
    saveROIRectInfoInImage();
}

void GraphView::setImage(const QImage& image)
{
    if (image.isNull()) return;
    m_imageItem->setPixmap(QPixmap::fromImage(image));
    updateImageScale();
    static bool firstImageArrival = true;
    if (firstImageArrival)
    {
        firstImageArrival = false;
        // 首次设置图像时载入坐标
        loadMarkItemPositionInImage();
        loadROIRectInfoInImage();
    }
    m_frameCount++;
}

void GraphView::setKeepAspectRatio(bool keep)
{
    m_keepAspectRatio = keep;
    updateImageScale();
}

void GraphView::resizeEvent(QResizeEvent* event)
{
    QGraphicsView::resizeEvent(event);
    updateImageScale();
    updateTextItems();
}

void GraphView::mousePressEvent(QMouseEvent* event)
{
    QGraphicsView::mousePressEvent(event);
}

void GraphView::mouseMoveEvent(QMouseEvent* event)
{
    QGraphicsView::mouseMoveEvent(event);
}

void GraphView::mouseReleaseEvent(QMouseEvent* event)
{
    QGraphicsView::mouseReleaseEvent(event);
}

void GraphView::loadMarkItemPositionInImage()
{
    // 获取执行文件目录并构建自定义路径
    QString settingsDir = qApp->applicationDirPath() + "/settings";
    QString settingsFile = settingsDir + "/" + this->metaObject()->className() + ".ini";
    
    QSettings settings(settingsFile, QSettings::IniFormat);
    qreal x = settings.value("markItem/X", 0.5).toDouble();
    qreal y = settings.value("markItem/Y", 0.5).toDouble();
    setMarkItemPositionInImage(QPointF(x, y));
}

void GraphView::saveMarkItemPositionInImage()
{
    // 获取执行文件目录并构建自定义路径
    QString settingsDir = qApp->applicationDirPath() + "/settings";
    // 确保路径存在
    QDir().mkpath(settingsDir);
    QString settingsFile = settingsDir + "/" + this->metaObject()->className() + ".ini";

    QSettings settings(settingsFile, QSettings::IniFormat);
    QPointF relativePos = getMarkItemPositionInImage();
    settings.setValue("markItem/X", relativePos.x());
    settings.setValue("markItem/Y", relativePos.y());
}

void GraphView::loadROIRectInfoInImage()
{
    QString settingsDir = qApp->applicationDirPath() + "/settings";
    QString settingsFile = settingsDir + "/" + this->metaObject()->className() + ".ini";

    QSettings settings(settingsFile, QSettings::IniFormat);
    QRectF rect = settings.value("ROIRect/Rect", QRectF(0.25, 0.25, 0.5, 0.5)).toRectF();
    setROIRectInfoInImage(rect);
}

void GraphView::saveROIRectInfoInImage()
{
    QString settingsDir = qApp->applicationDirPath() + "/settings";
    QDir().mkpath(settingsDir);
    QString settingsFile = settingsDir + "/" + this->metaObject()->className() + ".ini";

    QSettings settings(settingsFile, QSettings::IniFormat);
    settings.setValue("ROIRect/Rect", getROIRectInfoInImage());
}

void GraphView::updateFPS()
{
    m_fps = m_frameCount;
    m_frameCount = 0;
    updateTextItems();
}

void GraphView::updateImageScale()
{
    if (m_imageItem->pixmap().isNull()) return;
    QRectF imageRect = m_imageItem->boundingRect();
    QRectF viewRect = this->rect();
    qreal scaleX = viewRect.width() / imageRect.width();
    qreal scaleY = viewRect.height() / imageRect.height();
    
    // 保存当前的相对位置
    QPointF relativeMarkPos = getMarkItemPositionInImage();
    QRectF relatvieROIRect = getROIRectInfoInImage();
    
    if (m_keepAspectRatio)
    {
        qreal scale = qMin(scaleX, scaleY);
        m_imageItem->setScale(scale);
        // 居中位置显示
        m_imageItem->setPos((viewRect.width() - imageRect.width() * scale) / 2,
            (viewRect.height() - imageRect.height() * scale) / 2);
    }
    // 拉伸填充
    else 
    {
        m_imageItem->setScale(1.0);                                 // 重置缩放
        m_imageItem->setPos(0, 0);                                  // 设置到左上角
        QTransform trans = QTransform::fromScale(scaleX, scaleY);   // 创建缩放矩阵
        m_imageItem->setTransform(trans);                           // 应用缩放矩阵
    }
    m_scene->setSceneRect(viewRect);
    
    // 更新信息
    setMarkItemPositionInImage(relativeMarkPos);
    setROIRectInfoInImage(relatvieROIRect);
}

void GraphView::updateTextItems()
{
    QFont font = m_fpsItem->font();
    font.setPointSize(12);
    m_fpsItem->setFont(font);
    m_dateTimeItem->setFont(font);

    m_fpsItem->setPlainText(QString::fromLocal8Bit("帧率:%1").arg(m_fps, 0, 'f', 1));
    m_fpsItem->setPos(10, 0);

    QString currentDateTime = QDateTime::currentDateTime().toString(QString::fromLocal8Bit("yyyy/MM/dd hh:mm:ss"));
    m_dateTimeItem->setPlainText(currentDateTime);
    m_dateTimeItem->setPos(this->rect().width() - m_dateTimeItem->boundingRect().width() - 10, 0);
}

void GraphView::updateMarkItem()
{
    if (m_imageItem->pixmap().isNull()) return;
    QRectF imageRect = m_imageItem->sceneBoundingRect();
    // 10% of the smaller dimension
    m_markItem->setSize(qMin(imageRect.width(), imageRect.height()) * 0.1);
}

void GraphView::updateROIRect()
{
    if (m_imageItem->pixmap().isNull()) return;
    QRectF imageRect = m_imageItem->sceneBoundingRect();
    m_roiRectItem->updateAbsoluteRect(imageRect);
}

void GraphView::setMarkItemPositionInImage(const QPointF& relativePos)
{
    if (m_imageItem->pixmap().isNull()) return;
    QRectF imageRect = m_imageItem->sceneBoundingRect();
    qreal x = imageRect.left() + relativePos.x() * imageRect.width();
    qreal y = imageRect.top() + relativePos.y() * imageRect.height();
    m_markItem->setPos(x, y);

    updateMarkItem();
}

QPointF GraphView::getMarkItemPositionInImage()
{
    // 默认居中
    if (m_imageItem->pixmap().isNull()) return QPointF(0.5, 0.5);
    QRectF imageRect = m_imageItem->sceneBoundingRect();
    QPointF markItemPos = m_markItem->pos();
    qreal x = (markItemPos.x() - imageRect.left()) / imageRect.width();
    qreal y = (markItemPos.y() - imageRect.top()) / imageRect.height();
    // 确保值在 0.1~0.99 范围内
    x = qBound(0.01, x, 0.99);
    y = qBound(0.01, y, 0.99);
    return QPointF(x, y);
}

void GraphView::setROIRectInfoInImage(const QRectF& relativeRect)
{
    m_roiRectItem->setRelativeRect(relativeRect);
    
    updateROIRect();
}

QRectF GraphView::getROIRectInfoInImage()
{
    // 默认居中矩形
    if (m_imageItem->pixmap().isNull()) return QRectF(0.25, 0.25, 0.5, 0.5);
    // relativeRect的更新在 ROIRectItem 内容处理
    return m_roiRectItem->relativeRect();
}

5. 实现MainWindow

最后,我们实现MainWindow类来使用我们的GraphView:

MainWindow.h:

#pragma once

#include <QMainWindow>
#include <QImage>

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    QImage m_image;
};

MainWindow.cpp:

#include <QVBoxLayout>
#include <QTimer>
#include "MainWindow.h"
#include "GraphView.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    resize(800, 600);
    m_image.load("snipaste.png");
    GraphView* imageView = new GraphView();
    imageView->setMarkItemMovable(true);
    imageView->setMarkItemVisible(true);
    setCentralWidget(imageView);
    QTimer* timer = new QTimer(this);
    connect(timer, &QTimer::timeout, [=] {
        imageView->setImage(m_image);
    });
    timer->start(15);
}

MainWindow::~MainWindow()
{
}

6. CMakeLists.txt

为了编译我们的项目,我们需要一个CMakeLists.txt文件:

# CMakeList.txt: GraphicsViewGettingStarted 的 CMake 项目,在此处包括源代码并定义
# 项目特定的逻辑。
#
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)

find_package(QT NAMES Qt5 Qt6)
message(STATUS "FOUND QT_VERSION_MAJOR IS ${QT_VERSION_MAJOR}")
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Widgets REQUIRED)

# 定义源图片文件和目标目录
set(SOURCE_IMAGE "${CMAKE_CURRENT_LIST_DIR}/snipaste.png")

# 设置执行文件的输出目录
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")

# 复制图片文件
file(COPY ${SOURCE_IMAGE} DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})

file(GLOB SAMPLE1_HEADER "${CMAKE_CURRENT_LIST_DIR}/*.h")
file(GLOB SAMPLE1_SOURCE "${CMAKE_CURRENT_LIST_DIR}/*.cpp")

# 将源代码添加到此项目的可执行文件。
add_executable (sample1 ${SAMPLE1_HEADER} ${SAMPLE1_SOURCE})

if (CMAKE_VERSION VERSION_GREATER 3.12)
  set_property(TARGET sample1 PROPERTY CXX_STANDARD 20)
endif()

# TODO: 如有需要,请添加测试并安装目标。
target_link_libraries(sample1 Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Widgets)

在这个项目中,我们实现了一个自定义的GraphView类,它能够:

  1. 显示图像并支持保持宽高比或拉伸填充
  2. 显示一个可拖动的十字标记(CrossMarkItem)
  3. 显示一个可调整大小的ROI矩形(ROIRectItem)
  4. 显示FPS和当前日期时间
  5. 保存和加载十字标记和ROI矩形的位置

这个例子展示了如何使用Qt的Graphics View Framework来创建复杂的自定义图形界面。通过使用QGraphicsScene和QGraphicsItem,我们可以轻松地在场景中添加和操作各种图形元素。

要运行这个项目,确保你有Qt环境,然后使用CMake生成项目文件,编译并运行。你应该能看到一个窗口,显示一张图片,上面有一个可拖动的十字标记和一个可调整大小的绿色矩形,以及FPS和日期时间显示。

这个项目为进一步开发图像处理或计算机视觉应用提供了一个良好的起点。你可以在此基础上添加更多功能,如图像滤镜、目标检测等。

标签:Qt,自定义,void,GraphicsView,ROIRectItem,QRectF,GraphView,include,imageRect
From: https://www.cnblogs.com/linxmouse/p/18350224

相关文章

  • PC端文字过多展开收起跟在...后面(可自定义行数)
    话不多说直接上代码,可直接复制根据需求修改TextMore.vue<template> <div>  <divclass="yuanqu-con":style="{'--beforeHeight':beforeHeight}">   <div:style="{'-webkit-line-clamp':row}"clas......
  • C#中的foreach和自定义比较
    在C#中foreach不能修改集合里面的值在C#中,使用foreach循环遍历集合时,通常不建议修改集合中的元素,因为foreach循环是针对集合的枚举器进行操作的,而枚举器通常不支持修改集合中的元素。如果尝试在foreach循环中修改集合,可能会抛出异常或导致不可预测的行为。然而,如果你......
  • qt之QTableWidget按列遍历数据
    QObject::connect(ui->tableWidget_4,&QTableWidget::itemSelectionChanged,[=](){QList<QTableWidgetItem*>selectedItems=ui->tableWidget_4->selectedItems();if(!selectedItems.isEmpty()){in......
  • 基于YOLOv10深度学习的交通信号灯检测识别系统【python源码+Pyqt5界面+数据集+训练代
    《博主简介》小伙伴们好,我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。✌更多学习资源,可关注公-仲-hao:【阿旭算法与机器学习】,共同学习交流~......
  • 鹏哥C语言自定义笔记重点
    1.浮点数在内存中不能精确保存。2.sizeof这个操作符计算返回的结果是size_t类型的,是无符号整数型的,当遇见负数会被认为是非常大的数。3.strcpy在拷贝字符串时,会把源字符串中的\0也拷贝过去。assert是断言,可以防止NULL,需要头文件#include<assert.h>。const修饰指针变量放在*......
  • 关于Qt使用msvc时安装了Windows SDK后还显示警告
    如果我们在安装Qt时没有选择SDK或者其他原因,你的套件前面就会有一个黄色的感叹号。但是当我们安装SDK之后,而且电脑重启之后,会发现还是这个鸟样当我点击编译器时,我发现是有的所以我想在kit这里是不是要自己配置呢?然后就大胆进行配置了下点击ok,然后创建一个项目发现......
  • qt 输入一张图片,在图片上绘制后,再另存为图片
    boolDdrawCircleOnImage(constQString&inputImagePath,constQString&outputImagePath,QVector<QPoint>dotData){if(inputImagePath.isEmpty()||outputImagePath.isEmpty()){qWarning("输入图片路径无效!");retur......
  • 实现qt页面cpp
    在Qt中实现一个具体的界面,首先需要确定你的界面需求,包括需要哪些控件(如按钮、文本框、标签等)、布局方式(如垂直布局、水平布局、网格布局等)以及可能的交互逻辑。下面是一个简单的步骤,用于在Qt中实现一个基本的界面:创建Qt项目:使用QtCreator创建一个新的QtWidgetsApplicat......
  • 22.python自定义函数(format,zip)
    python自定义函数一、常见的自定义函数已经学过的函数:list、print、set、str、type、tuple、dict、range、input等今天学的函数:format二、实战讲解(一)format函数1、默认显示案例:hz="{}{}".format("dcs","43")print(hz)#dcs43hz="{}".format("dcs","43"......
  • 博客园自定义皮肤工具推荐:awescnb
    简介awescnb是一个用于博客园(Cnblogs)的自定义皮肤和功能增强插件。它允许用户通过简单的配置来自定义其博客的外观和增加一些额外的功能。下面是对awescnb的简要介绍:功能特点:自定义皮肤:用户可以选择不同的皮肤主题,包括背景颜色、字体样式等,以个性化他们的博客页面。扩展功能:除......