QT-简单视觉框架代码

文章目录

  • 简介
  • 1. 整体架构
  • 2. 关键类功能概述
  • 3. 详细代码实现
  • hikcameraworker.h 和 hikcameraworker.cpp(海康相机工作线程类)
  • imageviewerwidget.h 和 imageviewerwidget.cpp(图像查看部件类)
      • 构造函数 `ImageViewerWidget`
      • 析构函数 `~ImageViewerWidget`
      • `updateImage`
      • `addToolAction`
      • `removeToolAction`
      • `mousePressEvent`
      • `mouseMoveEvent`
      • `mouseReleaseEvent`
      • `wheelEvent`
      • `drawRectangle`
      • `handleRightClickMenu`
      • `zoomIn`
      • `zoomOut`
      • `translateView`
      • `startLineMeasurement`
      • `continueLineMeasurement`
      • `finishLineMeasurement`
      • `updateLineGeometry`
      • `isNearLineEndpoint`
      • `getLineEndpointIndex`
      • `updateLineRotation`
      • `startCircleMeasurement`
      • `continueCircleMeasurement`
      • `finishCircleMeasurement`
      • `startRotatedRectMeasurement`
      • `continueRotatedRectMeasurement`
      • `finishRotatedRectMeasurement`
      • `isNearCorner`
      • `getCornerIndex`
      • `updateRectGeometry`
      • `updateRectRotation`
      • `showLineTooltip`
      • `showRectTooltip`
      • `showCircleTooltip`
  • cameramanager.h 和 cameramanager.cpp(相机管理类,负责相机业务逻辑)
  • mainwindow.h 和 mainwindow.cpp(主窗口类)
  • main.cpp(程序入口)
  • 4. 功能说明

简介

以下是一个满足需求的 Qt 程序示例,采用 C++ QT语言编写,通过合理的分层架构设计,实现了界面与业务逻辑的分离,具备对多台海康相机的高效控制以及丰富的交互功能:

1. 整体架构

程序分为三层:界面层(负责显示和用户交互)、业务逻辑层(处理相机相关操作)、数据模型层(存储相机数据及状态,本示例未详细展开,可按需扩展)。通过信号与槽机制在各层之间传递信息,保证模块的低耦合性。

2. 关键类功能概述

  • CameraInterface:定义相机操作的抽象接口,确保不同相机厂商实现的兼容性。
    HikCameraWorker:继承自CameraInterface,实现海康相机的具体业务逻辑,包括连接、取图、断线重连、手动触发等。
  • MainWindow:主窗口类,负责创建界面,集成菜单栏、工具栏、状态栏、图像显示区和日志区,响应用户操作并与业务逻辑层交互。
  • ImageViewerWidget:自定义图像显示部件,封装 QGraphicsView 和QGraphicsScene,负责接收并展示图像数据。

3. 详细代码实现

  • camerainterface.h(相机接口抽象类)
#ifndef CAMERAINTERFACE_H
#define CAMERAINTERFACE_H

#include <QObject>

class CameraInterface : public QObject
{
    Q_OBJECT
public:
    virtual ~CameraInterface() {}

    // 初始化相机,返回是否成功
    virtual bool initialize() = 0;
    // 启动相机图像采集
    virtual void startCapture() = 0;
    // 停止相机图像采集
    virtual void stopCapture() = 0;
    // 手动触发一次图像采集
    virtual void manualTrigger() = 0;
    // 断开相机连接
    virtual void disconnectCamera() = 0;
    // 获取相机当前连接状态
    virtual bool isConnected() const = 0;

signals:
    // 图像采集成功信号,携带图像数据、宽度、高度
    void imageCaptured(unsigned char* imageData, int width, int height);
    // 相机出错信号
    void cameraError();
};

#endif // CAMERAINTERFACE_H

hikcameraworker.h 和 hikcameraworker.cpp(海康相机工作线程类)

// hikcameraworker.h
#ifndef HIKCAMERAWORKER_H
#define HIKCAMERAWORKER_H

#include <QObject>
#include <QThread>
#include <MvCameraControl.h>
#include "camerainterface.h"

class HikCameraWorker : public CameraInterface
{
    Q_OBJECT
public:
    explicit HikCameraWorker(int cameraIndex, QObject *parent = nullptr);
    ~HikCameraWorker();

private:
    int m_cameraIndex;
    MV_CC_DEVICE_INFO_LIST m_deviceInfoList;
    MV_CC_DEVICE_INFO* m_pDeviceInfo;
    MV_CC_HANDLE m_cameraHandle;
    bool m_isCapturing;
    bool m_isConnected;

    bool connectCamera();
    void disconnectCamera();
    static void __stdcall imageCallback(MV_FRAME_OUT_INFO_EX* pFrameInfo, void* pUser);

    // CameraInterface 接口实现
    bool initialize() override;
    void startCapture() override;
    void stopCapture() override;
    void manualTrigger() override;
    void disconnectCamera() override;
    bool isConnected() const override;
};

// hikcameraworker.cpp
#include "hikcameraworker.h"

HikCameraWorker::HikCameraWorker(int cameraIndex, QObject *parent) :
    CameraInterface(parent),
    m_cameraIndex(cameraIndex),
    m_pDeviceInfo(null nullable),
    m_cameraHandle(null nullable),
    m_isCapturing(false),
    m_isConnected(false)
{
    // 枚举设备
    MV_CC_EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE, &m_deviceInfoList);
    if (m_deviceIdex >= 0 && m_deviceIdex < m_deviceInfoList.nDeviceNum) {
        m_pDeviceInfo = m_deviceInfoList.pDeviceInfo[m_deviceIndex];
    }
}

HikCameraWorker::~HikCameraWorker()
{
    stopCapture();
    disconnectCamera();
}

bool HikCameraWorker::initialize()
{
    return connectCamera();
}

void HikCameraWorker::startCapture()
{
    if (!m_isConnected &&!connectCamera()) {
        emit cameraError();
        return;
    }

    m_isCapturing = true;
    MV_CC_SetCallbackFunction(m_cameraHandle, imageCallback, this);
    MV_CC_StartGrabbing(m_cameraHandle);
}

void HikCameraWorker::stopCapture()
{
    m_isCapturing = false;
    if (m_cameraHandle) {
        MV_CC_StopGrabbing(m_cameraHandle);
        MV_CC_DestroyHandle(m_cameraHandle);
        m_cameraHandle = nullptr;
    }
}

void HikCameraWorker::manualTrigger()
{
    if (m_isConnected && m_cameraHandle) {
        MV_CC_SoftwareTriggerCommand(m_cameraHandle);
    }
}

bool HikCameraWorker::connectCamera()
{
    if (m_pDeviceInfo) {
        int nRet = MV_CC_CreateHandle(&m_cameraHandle, m_pDeviceInfo);
        if (nRet == MV_OK) {
            nRet = MV_CC_OpenDevice(m_cameraHandle);
            if (nRet == MV_OK) {
                m_isConnected = true;
                return true;
            } else {
                MV_CC_DestroyHandle(m_cameraHandle);
                char errorMsg[1024];
                MV_CC_GetLastErrorMsg(errorMsg, sizeof(errorMsg));
                qDebug() << "Open device error: " << errorMsg;
                m_cameraHandle = nullptr;
            }
        }
    }
    return false;
}

void HikCameraWorker::disconnectCamera()
{
    if (m_cameraHandle) {
        MV_CC_CloseDevice(m_cameraHandle);
        MV_CC_DestroyHandle(m_cameraHandle);
        m_cameraHandle = nullptr;
    }
    m_isConnected = false;
}

void __stdcall HikCameraWorker::imageCallback(MV_FRAME_OUT_INFO_EX* pFrameInfo, void* pUser)
{
    HikCameraWorker* worker = static_cast<HikCameraWorker*>(pUser);
    if (worker && worker->m_isCapturing) {
        emit worker->imageCaptured(pFrameInfo->pBuf, pFrameInfo->nWidth, pFrameInfo->nHeight);
    }
}

bool HikCameraWorker::isConnected() const
{
    return m_isConnected;
}

imageviewerwidget.h 和 imageviewerwidget.cpp(图像查看部件类)

// imageviewerwidget.h
#ifndef IMAGEVIEWERWIDGET_H
#define IMAGEVIEWERWIDGET_H

#include <QWidget>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QMouseEvent>
#include <QWheelEvent>
#include <QGraphicsRectItem>
#include <QGraphicsItemGroup>
#include <QMenu>
#include <QAction>
#include <QDebug>
#include <QPainterPath>
#include <cmath>
#include <QToolTip>

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

    // 动态添加工具项到右键菜单,例如找线卡尺、圆形卡尺等
    void addToolAction(QAction* action);
    // 动态移除工具项
    void removeToolAction(QAction* action);

public slots:
    void updateImage(unsigned char* imageData, int width, int height);

signals:
    // 当绘制矩形等操作完成后,发出信号通知外界(例如主窗口),携带矩形信息
    void shapeDrawn(QRectF rect);
    // 当使用找线卡尺完成测量后,发出信号携带线的相关信息(起点、终点坐标等)
    void lineMeasured(QPointF start, QPointF end);
    // 当使用圆形卡尺完成测量后,发出信号携带圆的相关信息(圆心、半径)
    void circleMeasured(QPointF center, double radius);
    // 当绘制旋转矩形完成后,发出信号携带旋转矩形相关信息(矩形、旋转角度)
    void rotatedRectDrawn(QRectF rect, double angle);

protected:
    void mousePressEvent(QMouseEvent* event) override;
    void mouseMoveEvent(QMouseEvent* event) override;
    void mouseReleaseEvent(QMouseEvent* event) override;
    void wheelEvent(QWheelEvent* event) override;

private:
    QGraphicsScene* m_scene;
    QGraphicsView* m_view;
    QImage m_image;
    QGraphicsItemGroup* m_currentItemGroup;  // 用于管理当前选中的图形组合(如矩形、卡尺等)
    QMenu* m_rightClickMenu;
    double m_scaleFactor;  // 缩放因子

    // 记录旋转矩形相关状态
    bool m_isDrawingRotatedRect;
    QPointF m_rotatedRectStartPoint;
    QPointF m_rotatedRectLastPoint;
    double m_rotatedRectRotation;

    // 正在操作的矩形角索引,用于四个角拖拽, -1 表示无操作
    int m_activeCornerIndex;
    // 记录矩形中心初始位置,用于中心平移
    QPointF m_rectCenterInitialPos;

    // 绘制矩形相关函数
    void drawRectangle(QPointF startPoint, QPointF endPoint);
    // 处理右键菜单事件
    void handleRightClickMenu(QMouseEvent* event);
    // 放大图片
    void zoomIn();
    // 缩小图片
    void zoomOut();
    // 平移图片
    void translateView(QPointF offset);

    // 找线卡尺工具相关函数
    void startLineMeasurement(QPointF startPoint);
    // 继续线测量
    void continueLineMeasurement(QPointF currentPoint);
    // 完成线测量
    void finishLineMeasurement(QPointF endPoint);

    // 圆形卡尺工具相关函数
    void startCircleMeasurement(QPointF centerPoint);
    // 继续圆测量
    void continueCircleMeasurement(QPointF currentPoint);
    // 完成圆测量
    void finishCircleMeasurement(QPointF currentPoint);

    // 旋转矩形工具相关函数
    void startRotatedRectMeasurement(QPointF startPoint);
    // 继续旋转矩形测量
    void continueRotatedRectMeasurement(QPointF currentPoint);
    // 完成旋转矩形测量
    void finishRotatedRectMeasurement(QPointF currentPoint);

    // 辅助函数,判断鼠标点击位置是否靠近矩形角
    bool isNearCorner(QPointF point, QRectF rect, double tolerance = 5.0);
    // 辅助函数,更新矩形旋转状态
    void updateRectRotation(QPointF currentPoint);
    // 辅助函数,根据鼠标移动更新矩形位置和大小
    void updateRectGeometry(QPointF currentPoint);
    // 辅助函数,显示矩形提示信息
    void showRectTooltip(QPointF point, QRectF rect);
};

#endif // IMAGEVIEWERWIDGET_H





// imageviewerwidget.cpp
#include "imageviewerWidget.h"

ImageViewerWidget::ImageViewerWidget(QWidget *parent) :
    QWidget(parent),
    m_scaleFactor(1.0),
    m_isDrawingRotatedRect(false),
    m_rotatedRectRotation(0),
    m_activeLineEndpointIndex(-1),
    m_lineCenterInitialPos(),
    m_activeCornerIndex(-1),
    m_rectCenterInitialPos()
{
    m_scene = new QGraphicsScene(this);
    m_view = new QGraphicsView(m_scene);
    m_view->setAlignment(Qt::AlignCenter);
    m_view->setDragMode(QGraphicsView::ScrollHandDrag);  // 开启平移模式

    m_rightClickMenu = new QMenu(this);
    m_currentItemGroup = nullptr;

    QVBoxLayout* layout = new QVBoxLayout(this);
    layout->addWidget(m_view);
    setLayout(layout);
}

ImageViewerWidget::~ImageViewerWidget()
{
    delete m_scene;
    delete m_view;
    delete m_rightClickMenu;
}

void ImageViewerWidget::updateImage(unsigned char* imageData, int width, int height)
{
    m_image = QImage(imageData, width, height, QImage::Format_RGB888);
    m_scene->clear();
    m_scene->addPixmap(QPixmap::fromImage(m_image));
    m_view->resetTransform();  // 重置视图变换,避免缩放等影响新图像显示
    m_scaleFactor = 1.0;
    m_isDrawingRotatedRect = false;
}

void ImageViewerWidget::addToolAction(QAction* action)
{
    m_rightClickMenu->addAction(action);
}

void ImageViewerWidget::removeToolAction(QAction* action)
{
    m_rightClickMenu->removeAction(action);
}

void ImageViewerWidget::mousePressEvent(QMouseEvent* event)
{
    if (event->button() == Qt::RightButton) {
        handleRightClickMenu(event);
    } else if (event->button() == Qt::LeftButton) {
        if (!m_currentItemGroup) {
            m_currentItemGroup = new QGraphicsItemGroup;
            m_scene->addItem(m_currentItemGroup);
        }
        if (m_rightClickMenu->actions().contains(findChild<QAction*>("lineMeasurementAction"))) {
            startLineMeasurement(mapToScene(event->pos()));
        } else if (m_rightClickMenu->actions().contains(findChild<QAction*>("circleMeasurementAction"))) {
            startCircleMeasurement(mapToScene(event->pos()));
        } else if (m_rightClickMenu->actions().contains(findChild<QAction*>("rotatedRectAction"))) {
            startRotatedRectMeasurement(mapToScene(event->pos()));
        } else {
            drawRectangle(mapToScene(event->pos()), mapToScene(event->pos()));
        }

        // 检查是否点击在线端点、旋转矩形角上
        if (m_currentItemGroup) {
            if (m_rightClickMenu->actions().contains(findChild<QAction*>("lineMeasurementAction"))) {
                QPainterPath linePath = static_cast<QGraphicsPathItem*>(m_currentItemGroup->childItems().front())->path();
                m_activeLineEndpointIndex = getLineEndpointIndex(mapToScene(event->pos()), linePath);
                if (m_activeLineEndpointIndex!= -1) {
                    m_lineCenterInitialPos = linePath.boundingRect().center();
                }
            }
            if (m_rightClickMenu->actions().contains(findChild<QAction*>("rotatedRectAction"))) {
                QRectF rect = m_currentItemGroup->boundingRect();
                m_activeCornerIndex = getCornerIndex(mapToScene(event->pos()), rect);
                if (m_activeCornerIndex!= -1) {
                    m_rectCenterInitialPos = rect.center();
                }
            }
        }
    }
    QWidget::mousePressEvent(event);
}

void ImageViewerWidget::mouseMoveEvent(QMouseEvent* event)
{
    if (m_currentItemGroup && event->buttons() == Qt::LeftButton) {
        QPointF newPos = mapToScene(event->pos());
        QPointF offset = newPos - m_currentItemGroup->pos();
        m_currentItemGroup->moveBy(offset.x(), offset.y());
        if (m_rightClickMenu->actions().contains(findChild<QAction*>("lineMeasurementAction"))) {
            continueLineMeasurement(newPos);
            if (m_activeLineEndpointIndex!= -1) {
                updateLineGeometry(newPos);
            } else {
                updateLineRotation(newPos);
            }
        } else if (m_rightClickMenu->actions().contains(findChild<QAction*>("circleMeasurementAction"))) {
            continueCircleMeasurement(newPos);
        } else if (m_rightClickMenu->actions().contains(findChild<QAction*>("rotatedRectAction"))) {
            continueRotatedRectMeasurement(newPos);
            if (m_activeCornerIndex!= -1) {
                updateRectGeometry(newPos);
            } else {
                updateRectRotation(newPos);
            }
        }
        // 显示线、矩形或圆的提示信息
        if (m_rightClickMenu->actions().contains(findChild<QAction*>("lineMeasurementAction"))) {
            QPainterPath linePath = static_cast<QGraphicsPathItem*>(m_currentItemGroup->childItems().front())->path();
            showLineTooltip(mapToScene(event->pos()), linePath);
        } else if (m_rightClickMenu->actions().contains(findChild<QAction*>("rotatedRectAction"))) {
            QRectF rect = m_currentItemGroup->boundingRect();
            showRectTooltip(mapToScene(event->pos()), rect);
        } else if (m_rightClickMenu->actions().contains(findChild<QAction*>("circleMeasurementAction"))) {
            QGraphicsPathItem* circleItem = static_cast<QGraphicsPathItem*>(m_currentItemGroup->childItems().front());
            QPainterPath circlePath = circleItem->path();
            showCircleTooltip(mapToScene(event->pos()), circlePath);
        }
    }
    QWidget::mouseMoveEvent(event);
}

void ImageViewerWidget::mouseReleaseEvent(QMouseEvent* event)
{
    if (m_currentItemGroup && event->button() == Qt::LeftButton) {
        if (m_rightClickMenu->actions().contains(findChild<QAction*>("lineMeasurementAction"))) {
            finishLineMeasurement(mapToScene(event->pos()));
            m_activeLineEndpointIndex = -1;
        } else if (m_rightClickMenu->actions().contains(findChild<QAction*>("circleMeasurementAction"))) {
            finishCircleMeasurement(mapToScene(event->pos()));
        } else if (m_rightClickMenu->actions().contains(findChild<QAction*>("rotatedRectAction"))) {
            finishRotatedRectMeasurement(mapToScene(event->pos()));
            m_activeCornerIndex = -1;
        } else {
            QRectF rect = m_currentItemGroup->boundingRect();
            emit shapeDrawn(rect);
        }
        m_currentItemGroup = nullptr;
    }
    QWidget::mouseReleaseEvent(event);
}

void ImageViewerWidget::wheelEvent(QWheelEvent* event)
{
    if (event->angleDelta().y() > 0) {
        zoomIn();
    } else {
        zoomOut();
    }
    QWidget::wheelEvent(event);
}

void ImageViewerWidget::drawRectangle(QPointF startPoint, QPointF endPoint)
{
    QGraphicsRectItem* rectItem = new QGraphicsRectItem(QRectF(startPoint, endPoint));
    m_currentItemGroup->addToGroup(rectItem);
}

void ImageViewerWidget::handleRightClickMenu(QMouseEvent* event)
{
    m_rightClickMenu->exec(mapToGlobal(event->pos()));
}

void ImageViewerWidget::zoomIn()
{
    m_scaleFactor *= 1.2;
    m_view->scale(m_scaleFactor, m_scaleFactor);
}

void ImageViewerWidget::zoomOut()
{
    m_scaleFactor /= 1.2;
    m_view->scale(m_scaleFactor, m_scaleFactor);
}

void ImageViewerWidget::translateView(QPointF offset)
{
    m_view->translate(offset.x(), offset.y());
}

void ImageViewerWidget::startLineMeasurement(QPointF startPoint)
{
    QGraphicsPathItem* lineItem = new QGraphicsPathItem;
    QPainterPath path;
    path.moveTo(startPoint);
    lineItem->setPath(path);
    m_currentItemGroup->addToGroup(lineItem);
}

void ImageViewerWidget::continueLineMeasurement(QPointF currentPoint)
{
    QGraphicsPathItem* lineItem = static_cast<QGraphicsPathItem*>(m_currentItemGroup->childItems().front());
    QPainterPath path = lineItem->path();
    path.lineTo(currentPoint);
    lineItem->setPath(path);
}

void ImageViewerWidget::finishLineMeasurement(QPointF endPoint)
{
    QGraphicsPathItem* lineItem = static_cast<QGraphicsPathItem*>(m_currentItemGroup->childItems().front());
    QPainterPath path = lineItem->path();
    path.lineTo(endPoint);
    lineItem->setPath(path);
    emit lineMeasured(path.elementAt(0).x, path.elementAt(0).y, path.elementAt(path.elementCount() - 1).x, path.elementAt(path.elementCount() - 1).y);
}

void ImageViewerWidget::updateLineGeometry(QPointF currentPoint)
{
    if (m_currentItemGroup) {
        QPainterPath linePath = static_cast<QGraphicsPathItem*>(m_currentItemGroup->childItems().front())->path();
        QPointF center = linePath.boundingRect().center();
        QPointF offset = currentPoint - m_lineCenterInitialPos;
        switch (m_activeLineEndpointIndex) {
        case 0: // 起点
            linePath.setElementPositionAt(0, linePath.elementAt(0).x + offset.x(), linePath.elementAt(0).y + offset.y());
            break;
        case 1: // 终点
            linePath.setElementPositionAt(linePath.elementCount() - 1, linePath.elementAt(linePath.elementCount() - 1).x + offset.x(), linePath.elementAt(linePath.elementCount() - 1).y + offset.y());
            break;
        }
        QGraphicsPathItem* lineItem = static_cast<QGraphicsPathItem*>(m_currentItemGroup->childItems().front());
        lineItem->setPath(linePath);
    }
}

bool ImageViewerWidget::isNearLineEndpoint(QPointF point, QPainterPath linePath, double tolerance = 5.0)
{
    const QPointF endpoints[2] = { QPointF(linePath.elementAt(0).x, linePath.elementAt(0).y), QPointF(linePath.elementAt(linePath.elementCount() - 1).x, linePath.elementAt(linePath.elementCount() - 1).y) };
    for (int i = 0; i < 2; i++) {
        if (QLineF(point, endpoints[i]).length() < tolerance) {
            return true;
        }
    }
    return false;
}

int ImageViewerWidget::getLineEndpointIndex(QPointF point, QPainterPath linePath)
{
    const QPointF endpoints[2] = { QPointF(linePath.elementAt(0).x, linePath.elementAt(0).y), QPointF(linePath.elementAt(linePath.elementCount() - 1).x, linePath.elementAt(linePath.elementCount() - 1).y) };
    for (int i = 0; i < 2; i++) {
        if (QLineF(point, endpoints[i]).length() < 5.0) {
            return i;
        }
    }
    return -1;
}

void ImageViewerWidget::updateLineRotation(QPointF currentPoint)
{
    if (m_currentItemGroup) {
        QPainterPath linePath = static_cast<QGraphicsPathItem*>(m_currentItemGroup->childItems().front())->path();
        double angle = std::atan2(currentPoint.y() - linePath.boundingRect().center().y(), currentPoint.x() - linePath.boundingRect().center().x());
        QGraphicsPathItem* lineItem = static_cast<QGraphicsPathItem*>(m_currentItemGroup->childItems().front());
        lineItem->setRotation(angle * 180 / M_PI);
    }
}

void ImageViewerWidget::startCircleMeasurement(QPointF centerPoint)
{
    QGraphicsPathItem* circleItem = new QGraphicsPathItem;
    QPainterPath path;
    path.addEllipse(centerPoint, 0, 0);
    circleItem->setPath(path);
    m_currentItemGroup->addToGroup(circleItem);
}

void ImageViewerWidget::continueCircleMeasurement(QPointF currentPoint)
{
    QGraphicsPathItem* circleItem = static_cast<QGraphicsPathItem*>(m_currentItemGroup->childItems().front());
    QPainterPath path = circleItem->path();
    double radius = std::sqrt(std::pow(currentPoint.x() - path.elementAt(0).x, 2) + std::pow(currentPoint.y() - path.elementAt(0).y, 2));
    path = QPainterPath();
    path.addEllipse(path.elementAt(0).x, path.elementAt(0).y, radius, radius);
    circleItem->setPath(path);
}

void ImageViewerWidget::finishCircleMeasurement(QPointF currentPoint)
{
    QGraphicsPathItem* circleItem = static_cast<QGraphicsPathItem*>(m_currentItemGroup->childItems().front());
    QPainterPath path = circleItem->path();
    double radius = std::sqrt(std::pow(currentPoint.x() - path.elementAt(0).x, 2) + std::pow(currentPoint.y() - path.elementAt(0).y, 2));
    emit circleMeasured(path.elementAt(0).x, path.elementAt(0).y, radius);
}

void ImageViewerWidget::startRotatedRectMeasurement(QPointF startPoint)
{
    m_isDrawingRotatedRect = true;
    m_rotatedRectStartPoint = startPoint;
    m_rotatedRectLastPoint = startPoint;
}

void ImageViewerWidget::continueRotatedRectMeasurement(QPointF currentPoint)
{
    if (m_isDrawingRotatedRect) {
        QGraphicsItemGroup* group = m_currentItemGroup;

        // 先移除之前可能存在的矩形,用于实时更新显示
        QList<QGraphicsItem*> items = group->childItems();
        for (QGraphicsItem* item : items) {
            if (QGraphicsRectItem* rectItem = qgraphicsitem_cast<QGraphicsRectItem*>(item)) {
                group->removeFromGroup(rectItem);
                delete rectItem;
            }
        }

        QRectF rect = QRectF(m_rotatedRectStartPoint, currentPoint).normalized();
        double angle = std::atan2(currentPoint.y() - m_rotatedRectStartPoint.y(), currentPoint.x() - m_rotatedRectStartPoint.x());

        QGraphicsRectItem* rectItem = new QGraphicsRectItem(rect);
        rectItem->setTransformOriginPoint(rect.center());
        rectItem->setRotation(angle * 180 / M_PI);
        group->addToGroup(rectItem);

        m_rotatedRectLastPoint = currentPoint;
        m_rotatedRectRotation = angle;
    }
}

void ImageViewerWidget::finishRotatedRectMeasurement(QPointF currentPoint)
{
    if (m_isDrawingRotatedRect) {
        continueRotatedRectMeasurement(currentPoint);
        QRectF rect = QRectF(m_rotatedRectStartPoint, currentPoint).normalized();
        emit rotatedRectDrawn(rect, m_rotatedRectRotation * 180 / M_PI);
        m_isDrawingRotatedRect = false;
    }
}

bool ImageViewerWidget::isNearCorner(QPointF point, QRectF rect, double tolerance = 5.0)
{
    const QPointF corners[4] = { rect.topLeft(), rect.topRight(), rect.bottomRight(), rect.bottomLeft() };
    for (int i = 0; i < 4; i++) {
        if (QLineF(point, corners[i]).length() < tolerance) {
            return true;
        }
    }
    return false;
}
int ImageViewerWidget::getCornerIndex(QPointF point, QRectF rect)
{
    const QPointF corners[4] = { rect.topLeft(), rect.topRight(), rect.bottomRight(), rect.bottomLeft() };
    for (int i = 0; i < 4; i++) {
        if (QLineF(point, corners[i]).length() < 5.0) {
            return i;
        }
    }
    return -1;
}

void ImageViewerWidget::updateRectGeometry(QPointF currentPoint)
{
    if (m_currentItemGroup) {
        QRectF rect = m_currentItemGroup->boundingRect();
        QPointF center = rect.center();
        QPointF offset = currentPoint - m_rectCenterInitialPos;
        switch (m_activeCornerIndex) {
        case 0: // 左上角
            rect.setTopLeft(rect.topLeft() + offset);
            break;
        case 1: // 右上角
            rect.setTopRight(rect.topRight() + offset);
            break;
        case 2: // 右下角
            rect.setBottomRight(rect.bottomRight() + offset);
            break;
        case 3: // 左下角
            rect.setBottomLeft(rect.bottomLeft() + offset);
            break;
        }
        QGraphicsRectItem* rectItem = static_cast<QGraphicsRectItem*>(m_currentItemGroup->childItems().first());
        rectItem->setRect(rect);
    }
}

void ImageViewerWidget::updateRectRotation(QPointF currentPoint)
{
    if (m_currentItemGroup) {
        QRectF rect = m_currentItemGroup->boundingRect();
        double angle = std::atan2(currentPoint.y() - rect.center().y(), currentPoint.x() - rect.center().x());
        QGraphicsRectItem* rectItem = static_cast<QGraphicsRectItem*>(m_currentItemGroup->childItems().first());
        rectItem->setRotation(angle * 180 / M_PI);
        m_rotatedRectRotation = angle;
    }
}

void ImageViewerWidget::showLineTooltip(QPointF point, QPainterPath linePath)
{
    QPointF start = QPointF(linePath.elementAt(0).x, linePath.elementAt(0).y);
    QPointF end = QPointF(linePath.elementAt(linePath.elementCount() - 1).x, linePath.elementAt(linePath.elementCount() - 1).y);
    QString tooltipText = QString("线信息:\n起点坐标: (%1, %2)\n终点坐标: (%3, %4)")
                         .arg(start.x())
                         .arg(start.y())
                         .arg(end.x())
                         .arg(end.y());
    QToolTip::showText(mapToGlobal(mapFromScene(point)), tooltipText, this);
}

void ImageViewerWidget::showRectTooltip(QPointF point, QRectF rect)
{
    QString tooltipText = QString("矩形信息:\n左上角坐标: (%1, %2)\n右下角坐标: (%3, %4)\n旋转角度: %5°")
                         .arg(rect.topLeft().x())
                         .arg(rect.topLeft().y())
                         .arg(rect.bottomRight().x())
                         .arg(rect.bottomRight().y())
                         .arg(m_rotatedRectRotation * 180 / M_PI);
    QToolTip::showText(mapToGlobal(mapFromScene(point)), tooltipText, this);
}

void ImageViewerWidget::showCircleTooltip(QPointF point, QPainterPath circlePath)
{
    QPointF center = QPointF(circlePath.elementAt(0).x, circlePath.elementAt(0).y);
    double radius = std::sqrt(std::pow(point.x() - center.x, 2) + std::pow(point.y() - center.y, 2));
    QString tooltipText = QString("圆信息:\n圆心坐标: (%1, %2)\n半径: %3")
                         .arg(center.x())
                         .arg(center.y())
                         .arg(radius);
    QToolTip::showText(mapToGlobal(mapFromScene(point)), tooltipText, this);
}

以下是对 ImageViewerWidget.cpp 中各主要函数功能的简单介绍:

构造函数 ImageViewerWidget

  • 功能:初始化 ImageViewerWidget 类相关的成员变量、创建图形场景 m_scene、图形视图 m_view,设置视图平移模式等,还初始化右键菜单和用于管理图形组合的指针,搭建好界面布局,为后续操作做准备。

析构函数 ~ImageViewerWidget

  • 功能:释放之前构造函数中创建的 m_scenem_viewm_rightClickMenu 等资源,避免内存泄漏,进行清理工作。

updateImage

  • 功能:依据传入的图像数据更新显示的图像,包括清除原有场景内容、添加新图像、重置视图变换以及相关操作状态变量,确保后续操作基于新图像开展。

addToolAction

  • 功能:往右键菜单里添加一个工具操作选项(QAction),可动态扩充右键菜单功能。

removeToolAction

  • 功能:从右键菜单中移除指定的工具操作选项(QAction),实现动态调整菜单内容。

mousePressEvent

  • 功能:处理鼠标按下事件,右键按下弹出右键菜单,左键按下依据右键菜单中的工具选项启动相应图形绘制或测量操作,同时判断是否点击在图形关键位置(如线端点、矩形角)并记录相关信息。

mouseMoveEvent

  • 功能:响应鼠标移动且左键按下的情况,实现图形的平移,根据不同激活工具继续对应图形的绘制或修改操作(如更新线、圆、矩形的相关参数),还实时显示鼠标悬停处图形的提示信息。

mouseReleaseEvent

  • 功能:处理鼠标释放事件,针对不同激活工具完成相应操作、重置操作状态,并在普通矩形绘制时发送绘制完成信号,结束当前操作。

wheelEvent

  • 功能:依据鼠标滚轮滚动方向,调用相应函数实现图片放大或缩小功能。

drawRectangle

  • 功能:创建矩形图形项并添加到当前图形管理组,用于在场景中绘制矩形。

handleRightClickMenu

  • 功能:在鼠标右键点击位置弹出右键菜单,方便选择操作。

zoomIn

  • 功能:增大图片的缩放因子,实现图片放大显示效果。

zoomOut

  • 功能:减小图片的缩放因子,让图片缩小显示。

translateView

  • 功能:按照传入的偏移量对视图进行平移,便于浏览图像不同位置。

startLineMeasurement

  • 功能:开启找线卡尺测量,创建初始线图形项并添加到管理组,准备绘制线。

continueLineMeasurement

  • 功能:在找线卡尺测量中,随着鼠标移动持续更新线的终点位置,动态绘制线。

finishLineMeasurement

  • 功能:结束找线卡尺测量,确定线的最终形态,并发送测量完成信号携带线的起止坐标信息。

updateLineGeometry

  • 功能:根据鼠标操作情况,更新找线卡尺绘制线的端点位置,实现线的拖拽变形。

isNearLineEndpoint

  • 功能:判断鼠标位置是否靠近线的端点,辅助后续操作判断。

getLineEndpointIndex

  • 功能:获取鼠标位置靠近的线端点索引,确定操作的具体端点。

updateLineRotation

  • 功能:依据鼠标位置改变找线卡尺绘制线的旋转角度,实现线的旋转操作。

startCircleMeasurement

  • 功能:启动圆形卡尺测量,创建初始圆形图形项添加到管理组,准备后续绘制圆。

continueCircleMeasurement

  • 功能:在圆形卡尺测量中,随着鼠标移动更新圆的半径,动态绘制圆。

finishCircleMeasurement

  • 功能:结束圆形卡尺测量,确定圆的最终形态,并发送测量完成信号携带圆心和半径信息。

startRotatedRectMeasurement

  • 功能:开启旋转矩形绘制操作,记录起始点等初始信息。

continueRotatedRectMeasurement

  • 功能:在旋转矩形绘制中,根据鼠标位置实时更新矩形形状和旋转角度,动态展示旋转矩形。

finishRotatedRectMeasurement

  • 功能:结束旋转矩形绘制,确定最终矩形形态,并发送绘制完成信号携带矩形及旋转角度信息。

isNearCorner

  • 功能:判断鼠标位置是否靠近矩形的角,辅助后续矩形角操作判断。

getCornerIndex

  • 功能:获取鼠标位置靠近的矩形角索引,确定操作的具体角。

updateRectGeometry

  • 功能:依据鼠标对矩形角的操作,更新旋转矩形的大小和位置,实现角的拖拽改变矩形形态。

updateRectRotation

  • 功能:根据鼠标位置更新旋转矩形的旋转角度,实现旋转操作。

showLineTooltip

  • 功能:在鼠标悬停在线上时,显示包含线相关参数(起止坐标)的提示信息。

showRectTooltip

  • 功能:当鼠标悬停在矩形上时,展示包含矩形关键参数(坐标、旋转角度)的提示信息。

showCircleTooltip

  • 功能:鼠标悬停在圆上时,显示包含圆的圆心和半径等参数的提示信息。

cameramanager.h 和 cameramanager.cpp(相机管理类,负责相机业务逻辑)

// cameramanager.h
#ifndef CAMERAMANAGER_H
#define CAMERAMANAGER_H

#include <QObject>
#include <QTimer>
#include <vector>
#include "camerainterface.h"

class CameraManager : public QObject
{
    Q_OBJECT
public:
    CameraManager(QObject *parent = nullptr);
    ~CameraManager();

    // 初始化相机列表,根据实际连接的相机数量
    void initializeCameras();
    // 启动所有相机的图像采集
    void startCapture();
    // 停止所有相机的图像采集
    void stopCapture();
    // 手动触发所有相机采集一次图像
    void manualTrigger();
    // 关闭所有相机连接
    void closeAllCameras();

signals:
    // 图像采集成功信号,携带图像数据、宽度、高度以及相机索引
    void imageCaptured(unsigned char* imageData, int width, int height, int cameraIndex);
    // 相机出错信号,携带相机索引
    void cameraError(int cameraIndex);

private:
    std::vector<CameraInterface*> m_cameraWorkers;
    QTimer* m_reconnectionTimer;
    void setupCameraWorkers(int numCameras);
    void handleCameraError(int cameraIndex);
    void checkForReconnection();
};

// cameramanager.cpp
#include "cameramanager.h"

CameraManager::CameraManager(QObject *parent) :
    QObject(parent),
    m_reconnectionTimer(new QTimer(this))
{
    connect(m_reconnectionTimer, &QTimer::timeout, this, &CameraManager::checkForReconnection);
}

CameraManager::~CameraManager()
{
    closeAllCameras();
    delete m_reconnectionTimer;
    for (CameraInterface* worker : m_cameraWorkers) {
        delete worker;
    }
}

void CameraManager::initializeCameras()
{
    MV_CC_EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE, &m_deviceInfoList);
    int numCameras = m_deviceInfoList.nDeviceNum;
    setupCameraWorkers(numCameras);
}

void CameraManager::startCapture()
{
    for (CameraInterface* worker : m_cameraWorkers) {
        QThread* thread = new QThread;
        worker->moveToThread(thread);
        connect(thread, &QThread::started, worker, &CameraInterface::startCapture);
        connect(worker, &CameraInterface::destroyed, thread, &QThread::quit);
        connect(thread, &QThread::finished, thread, &QThread::deleteLater);
        thread->start();
    }
}

void CameraManager::stopCapture()
{
    for (CameraInterface* worker : m_cameraWorkers) {
        worker->stopCapture();
    }
}

void CameraManager::manualTrigger()
{
    for (CameraInterface* worker : m_cameraWorkers) {
        worker->manualTrigger();
    }
}

void CameraManager::closeAllCameras()
{
    for (CameraInterface* worker : m_cameraWorkers) {
        worker->disconnectCamera();
    }
}

void CameraManager::setupCameraWorkers(int numCameras)
{
    m_cameraWorkers.resize(numCameras);
    for (int i = 0; i < numCameras; ++i) {
        m_cameraWorkers[i] = new HikCameraWorker(i);
        connect(m_cameraWorkers[i], &CameraInterface::imageCaptured, this, [this, i](unsigned char* imageData, int width, int height) {
            emit imageCaptured(imageData, width, height, i);
        });
        connect(m_cameraWorkers[i], &CameraInterface::cameraError, this, &CameraManager::handleCameraError);
    }
}

void CameraManager::handleCameraError(int cameraIndex)
{
    m_cameraWorkers[cameraIndex]->stopCapture();
    m_cameraWorkers[cameraIndex]->disconnectCamera();
    m_reconnectionTimer->start(5000);
    emit cameraError(cameraIndex);
}

void CameraManager::checkForReconnection()
{
    m_reconnectionTimer->stop();
    for (int i = 0; i < m_cameraWorkers.size(); ++i) {
        if (!m_cameraWorkers[i]->isConnected()) {
            QThread* thread = new QThread;
            m_cameraWorkers[i]->moveToThread(thread);
            connect(thread, &QThread::started, m_cameraWorkers[i], &CameraInterface::startCapture);
            connect(m_cameraWorkers[i], &CameraInterface::destroyed, thread, &QThread::quit);
            connect(thread, &QThread::finished, thread, &QThread::deleteLater);
            thread->start();
        }
    }
}

mainwindow.h 和 mainwindow.cpp(主窗口类)

// mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QMenuBar>
#include <QToolBar>
#include <QStatusBar>
#include <QAction>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QTextEdit>
#include <vector>
#include "imageviewerwidget.h"

class CameraManager;

class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void onStartCaptureClicked();
    void onStopCaptureClicked();
    void updateImage(unsigned char* imageData, int width, int height, int cameraIndex);
    void handleCameraError(int cameraIndex);
    void onManualTriggerClicked();
    void onOpenCameraClicked();
    void onCloseCameraClicked();
    void onToggleCameraClicked();

private:
    CameraManager* m_cameraManager;
    std::vector<ImageViewerWidget*> m_imageViewers;
    QMenuBar* m_menuBar;
    QToolBar* m_toolBar;
    QStatusBar* m_statusBar;
    QAction* m_startCaptureAction;
    QAction* m_stopCaptureAction;
    QAction* m_manualTriggerAction;
    QAction* m_openCameraAction;
    QAction* m_closeCameraAction;
    QAction* m_toggleCameraAction;
    QWidget* m_centralWidget;
    QVBoxLayout* m_layout;
    QHBoxLayout* m_imageLayout;
    QTextEdit* m_logTextEdit;

    void setupMenuBar();
    void setupToolBar();
    void setupStatusBar();
    void setupCameraUI();
    void addImageViewerWidget();
    void removeImageViewerWidget();
};

#endif // MAINWINDOW_H
#endif // MAINWINDOW_H


// mainwindow.cpp
#include "mainwindow.h"
#include "cameramanager.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent)
{
    m_cameraManager = new CameraManager();
    m_cameraManager->initializeCameras();

    setupCameraUI();

    connect(m_cameraManager, &CameraManager::imageCaptured, this, &MainWindow::updateImage);
    connect(m_cameraManager, &CameraManager::cameraError, this, &MainWindow::handleCameraError);
}

MainWindow::~MainWindow()
{
    delete m_cameraManager;
    for (ImageViewerWidget* viewer : m_imageViewers) {
        delete viewer;
    }
}

void MainWindow::setupMenuBar()
{
    m_menuBar = new QMenuBar(this);

    QMenu* cameraMenu = m_menuBar->addMenu("相机");
    m_startCaptureAction = cameraMenu->addAction("开始采集");
    connect(m_startCaptureAction, &QAction::triggered, this, &MainWindow::onStartCaptureClicked);
    m_stopCaptureAction = cameraMenu->addAction("停止采集");
    connect(m_stopCaptureAction, &QAction::triggered, this, &MainWindow::onStopCaptureClicked);
    m_manualTriggerAction = cameraMenu->addAction("手动触发");
    connect(m_manualTriggerAction, &QAction::triggered, this, &MainWindow::onManualTriggerClicked);
    m_openCameraAction = cameraMenu->addAction("打开相机");
    connect(m_openCameraAction, &QAction::triggered, this, &MainWindow::onOpenCameraClicked);
    m_closeCameraAction = cameraMenu->addAction("关闭相机");
    connect(m_closeCameraAction, &QAction::triggered, this, &MainWindow::onCloseCameraClicked);
    m_toggleCameraAction = cameraMenu->addAction("动态开关相机");
    connect(m_toggleCameraAction, &QAction::triggered, this, &MainWindow::onToggleCameraClicked);

    setMenuBar(m_menuBar);
}

void MainWindow::setupToolBar()
{
    m_toolBar = new QToolBar(this);
    m_toolBar->addAction(m_startCaptureAction);
    m_toolBar->addAction(m_stopCaptureAction);
    m_toolBar->addAction(m_manualTriggerAction);
    m_toolBar->addAction(m_openCameraAction);
    m_toolBar->addAction(m_closeCameraAction);
    m_toolBar->addAction(m_toggleCameraAction);
    addToolBar(m_toolBar);
}

void MainWindow::setupStatusBar()
{
    m_statusBar = new QStatusBar(this);
    setStatusBar(m_statusBar);
}

void MainWindow::setupCameraUI()
{
    int numCameras = m_cameraManager->getCameraCount();

    m_imageViewers.resize(numCameras);

    m_centralWidget = new QWidget(this);
    setCentralWidget(m_centralWidget);

    m_layout = new QVBoxLayout(m_centralWidget);

    m_imageLayout = new QHBoxLayout();
    m_layout->addLayout(m_imageLayout);

    m_logTextEdit = new QTextEdit(this);
    m_layout->addWidget(m_logTextEdit);

    for (int i = 0; i < numCameras; ++i) {
        m_imageViewers[i] = new ImageViewerWidget(this);
        m_imageLayout->addWidget(m_imageViewers[i]);
    }

    setupMenuBar();
    setupToolBar();
    setupStatusBar();
}

void MainWindow::onStartCaptureClicked()
{
    m_cameraManager->startCapture();
}

void MainWindow::onStopCaptureClicked()
{
    m_cameraManager->stopCapture();
}

void MainWindow::updateImage(unsigned char* imageData, int width, int height, int cameraIndex)
{
    if ( cameraIndex >= 0 && cameraIndex < m_imageViewers.size()) {
        m_imageViewers[cameraIndex]->updateImage(imageData, width, height);
    }
}

void MainWindow::handleCameraError(int cameraIndex)
{
    m_logTextEdit->append("相机 " + QString::number(cameraIndex) + " 出错");
}

void MainWindow::onManualTriggerClicked()
{
    m_cameraManager->manualTrigger();
}

void MainWindow::onOpenCameraClicked()
{
    m_logTextEdit->append("打开相机操作被触发");
}

void MainWindow::onCloseCameraClicked()
{
    m_cameraManager->closeAllCameras();
    m_logTextEdit->append("关闭相机操作被触发");
}

void MainWindow::onToggleCameraClicked()
{
    if (m_imageViewers.size() < m_cameraManager->getCameraCount()) {
        addImageViewerWidget();
    } else if (m_imageViewers.size() > 0) {
        removeImageViewerWidget();
    }
}

void MainWindow::addImageViewerWidget()
{
    int newIndex = m_imageViewers.size();
    ImageViewerWidget* newWidget = new ImageViewerWidget(this);
    m_imageViewers.push_back(newWidget);
    m_imageLayout->addWidget(newWidget);
}

void MainWindow::removeImageViewerWidget()
{
    if (!m_imageViewers.empty()) {
        int lastIndex = m_imageViewers.size() - 1;
        ImageViewerWidget* widgetToRemove = m_imageViewers.back();
        m_imageViewers.pop_back();
        m_imageLayout->removeWidget(widgetToRemove);
        delete widgetToRemove;
    }
}

在上述代码中:

  • 新增了一个 QAction 用于表示动态开关相机按钮,以及对应的槽函数 onToggleCameraClicked。
  • 在 onToggleCameraClicked 函数中,根据当前显示图片控件数量与相机总数的比较,决定是添加还是移除一个 ImageViewerWidget。
    addImageViewerWidget 函数用于创建并添加新的图片显示控件到布局中,removeImageViewerWidget 函数用于移除最后一个图片显示控件并释放相关资源。
  • 通过这些修改,实现了动态调整显示图片控件的功能,以适配相机的开启与关闭操作。
    请注意,上述代码基于之前提供的代码框架基础上修改,运行时需要确保 CameraManager 类及其他相关依赖正确实现并链接。

main.cpp(程序入口)

#include <QApplication>
#include "mainwindow.h"

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    MainWindow window;
    window.show();

    return app.exec();
}

4. 功能说明

  • 多线程取图:每个 HikCameraWorker 运行在独立线程中,通过 startCapture 启动相机采集,在 imageCallback 回调中发送采集到的图像信号,避免阻塞主线程,保证界面响应流畅。
  • 断线重连:当相机出现错误(如连接断开),触发 cameraError 信号,在 handleCameraError 槽函数中停止当前相机取图

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/942503.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

uniapp 项目基础搭建(vue2)

一 .创建项目 创建项目可以通过工具创建&#xff0c;也可以通过脚手架下载 1.通过工具创建 2.通过脚手架下载 安装脚手架 ​​npm install -g vue/cli 下载项目模板 vue create -p dcloudio/uni-preset-vue 项目名称 二. 下载相关依赖 1. 项目默认是没有package.json文件的&…

使用vcpkg安装opencv>=4.9后#include<opencv2/opencv.hpp>#include<opencv2/core.hpp>无效

使用vcpkg安装opencv>4.9后#include<opencv2/opencv.hpp>#include<opencv2/core.hpp>无效\无法查找或打开 至少从2024年开始&#xff0c;发布的vcpkg默认安装的opencv版本都是4.x版。4.8版本及以前&#xff0c;vcpkg编译后的opencv头文件目录是*/vcpkg/x64-win…

Kubernetes PV及PVC的使用

前提条件 拥有Kubernetes集群环境&#xff0c;可参考&#xff1a;Kubernetes集群搭建理解Kubernetes部署知识&#xff0c;可参考&#xff1a;使用Kubernetes部署第一个应用 、Deloyment控制器拥有NFS服务&#xff0c;可参考&#xff1a;Linux环境搭建NFS服务 概述 Persistent…

flink sink kafka

接上文&#xff1a;一文说清flink从编码到部署上线 之前写了kafka source&#xff0c;现在补充kafka sink。完善kafka相关操作。 环境说明&#xff1a;MySQL&#xff1a;5.7&#xff1b;flink&#xff1a;1.14.0&#xff1b;hadoop&#xff1a;3.0.0&#xff1b;操作系统&#…

【安全编码】Web平台如何设计防止重放攻击

我们先来做一道关于防重放的题&#xff0c;答案在文末 防止重放攻击最有效的方法是&#xff08; &#xff09;。 A.对用户密码进行加密存储使用 B.使用一次一密的加密方式 C.强制用户经常修改用户密码 D.强制用户设置复杂度高的密码 如果这道题目自己拿不准&#xff0c;或者…

Diagramming AI: 使用自然语言来生成各种工具图

前言 在画一些工具图时&#xff08;流程图、UML图、架构图&#xff09;&#xff0c;你还在往画布上一个个的拖拽组件来进行绘制么&#xff1f;今天介绍一款更有效率的画图工具&#xff0c;它能够通过简单的自然语言描述来完成一个个复杂的图。 首页 进入官网之后&#xff0c;我…

黑马Java面试教程_P9_MySQL

系列博客目录 文章目录 系列博客目录前言1. 优化1.1 MySQL中&#xff0c;如何定位慢查询&#xff1f;面试文稿 1.2 面试官接着问&#xff1a;那这个SQL语句执行很慢,如何分析 ( 如何优化&#xff09;呢?面试文稿 1.3 了解过索引吗?(什么是索引)1.4 继续问 索引的底层数据结构…

Windows11家庭版启动Hyper-V

Hyper-V 是微软的硬件虚拟化产品&#xff0c;允许在 Windows 上以虚拟机形式运行多个操作系统。每个虚拟机都在虚拟硬件上运行&#xff0c;可以创建虚拟硬盘驱动器、虚拟交换机等虚拟设备。使用虚拟化可以运行需要较旧版本的 Windows 或非 Windows 操作系统的软件&#xff0c;以…

第6章 图论

2024年12月25日一稿 &#x1f430;6.1 图的基本概念 6.1.1 图的定义和表示 6.1.2 图的同构 6.1.3 完全图与正则图 6.1.4 子图与补图 6.1.5 通路与回路 6.2 图的连通性 6.2.1 无向图的连通性 6.2.2 有向图的连通性 6.3 图的矩阵表示 6.3.1 关联矩阵 6.3.2 有向图的邻接矩阵…

智能网关在电力物联网中的应用

摘要 随着电力技术的快速发展&#xff0c;断路器从传统的单一保护功能演变为具备智能监控和远程管理能力的多功能设备。智能断路器作为配电系统的重要组成部分&#xff0c;集成了实时监测、远程控制和多层保护功能&#xff0c;显著提升了配电系统的安全性、稳定性和管理效率…

【论文阅读】Comprehensive Review of End-to-End Video Compression

摘要&#xff1a; 近年来&#xff0c;端到端视频压缩作为一种新兴且有前景的解决方案开始在视频压缩领域受到关注。本文对端到端视频编码和解码技术的发展与现状进行了全面的综述&#xff0c;详细介绍了传统混合编码器和端到端编码器的基本原理。本研究深入探讨了从传统视频压…

系统架构师考试 常错题记录 01

1.按照《中华人民共和国著作权法》的权利保护期&#xff08; &#xff09;受到永久保护。 A.发表权 B.修改权 C.复制权 D.发行权 正确答案&#xff1a;B 解析&#xff1a;本题考查知识产权法中的《中华人民共和著作权法》保护期限知识点。 《中华人民共和著作权法》中约定署名权…

Redis-十大数据类型

Reids数据类型指的是value的类型&#xff0c;key都是字符串 redis-server:启动redis服务 redis-cli:进入redis交互式终端 常用的key的操作 redis的命令和参数不区分大小写 &#xff0c;key和value区分 1、查看当前库所有的key keys * 2、判断某个key是否存在 exists key 3、查…

IIC驱动EEPROM

代码参考正点原子 i2c_dri:主要是三段式状态机的编写 module iic_dri#(parameter SLAVE_ADDR 7b1010000 , //EEPROM从机地址parameter CLK_FREQ 26d50_000_000, //模块输入的时钟频率parameter I2C_FREQ 18d250_000 //IIC_SCL的时钟频率)( …

webrtc获取IceCandidate流程

在WebRTC(Web Real-Time Communication)中,ICECandidate是一个关键概念,它用于描述在建立点对点(P2P)连接时可以考虑的潜在通信端点。以下是关于WebRTC中ICECandidate的详细解释: 一、ICECandidate的定义 ICECandidate对象通常包含以下关键属性: foundation:用于唯一…

一文彻底拿捏DevEco Studio的使用小技巧

程序员Feri一名12年的程序员,做过开发带过团队创过业,擅长Java相关开发、鸿蒙开发、人工智能等,专注于程序员搞钱那点儿事,希望在搞钱的路上有你相伴&#xff01;君志所向,一往无前&#xff01; 0.安装DevEco Studio DevEco Studio面向HarmonyOS应用及元服务开发者提供的集成开…

基于openEuler22.09部署OpenStack Yoga云平台(一)

OpenStack Yoga部署 安装OpenStack 一、基础准备 基于OpenStack经典的三节点环境进行部署&#xff0c;三个节点分别是控制节点&#xff08;controller&#xff09;、计算节点&#xff08;compute&#xff09;、存储节点&#xff08;storage&#xff09;&#xff0c;其中存储…

新服务器ubuntu系统相关操作

1、查看驱动:驱动版本535.216.01能够支持cuda12.2,下面直接使用默认安装的cuda。 2、赋予用户管理员权限。 首先有超级用户(root)权限来编辑 /etc/sudoers 文件,visudo 是一个命令,用于安全地编辑 /etc/sudoers 文件。运行: sudo visudo 在 visudo 编辑器中,找到类似…

微机接口课设——基于Proteus和8086的打地鼠设计(8255、8253、8259)

原理图设计 汇编代码 ; I/O 端口地址定义 IOY0 EQU 0600H IOY1 EQU 0640H IOY2 EQU 0680HMY8255_A EQU IOY000H*2 ; 8255 A 口端口地址 MY8255_B EQU IOY001H*2 ; 8255 B 口端口地址 MY8255_C EQU IOY002H*2 ; 8255 C 口端口地址 MY8255_MODE EQU IOY003H*2 ; …

【C++数据结构——树】二叉树的遍历算法(头歌教学实验平台习题) 【合集】

目录&#x1f60b; 任务描述 相关知识 1. 二叉树的基本概念与结构定义 2. 建立二叉树 3. 先序遍历 4. 中序遍历 5. 后序遍历 6. 层次遍历 测试说明 通关代码 测试结果 任务描述 本关任务&#xff1a;实现二叉树的遍历 相关知识 为了完成本关任务&#xff0c;你需要掌…