当前位置: 首页 > news >正文

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 槽函数中停止当前相机取图

相关文章:

QT-简单视觉框架代码

文章目录 简介1. 整体架构2. 关键类功能概述3. 详细代码实现hikcameraworker.h 和 hikcameraworker.cpp&#xff08;海康相机工作线程类&#xff09;imageviewerwidget.h 和 imageviewerwidget.cpp&#xff08;图像查看部件类&#xff09;构造函数 ImageViewerWidget析构函数 ~…...

AI新书推荐:深度学习和大模型原理与实践(清华社)

本书简介 在这个信息爆炸、技术革新日新月异的时代&#xff0c;深度学习作为人工智能领域的重要分支&#xff0c;正引领着新一轮的技术革命。《深度学习和大模型原理与实践》一书&#xff0c;旨在为读者提供深度学习及其大模型技术的全面知识和实践应用的指南。 本书特色在于…...

[spring]处理器

我们可以通过spring来管理我们的类&#xff0c;之后我们可以通过spring的容器来获取我们所需要的Bean类对象。Spring的处理器是Spring对外开发的重要扩展点&#xff0c;它允许我们介入到Bean的整个实例化流程中来&#xff0c;可以动态添加、修改BeanDefinition、动态修改Bean 首…...

重温设计模式--中介者模式

中介者模式介绍 定义&#xff1a;中介者模式是一种行为设计模式&#xff0c;它通过引入一个中介者对象来封装一系列对象之间的交互。中介者使得各个对象之间不需要显式地相互引用&#xff0c;从而降低了它们之间的耦合度&#xff0c;并且可以更方便地对它们的交互进行管理和协调…...

重温设计模式--设计模式七大原则

文章目录 1、开闭原则&#xff08;Open - Closed Principle&#xff0c;OCP&#xff09;定义&#xff1a;示例&#xff1a;好处&#xff1a; 2、里氏替换原则&#xff08;Liskov Substitution Principle&#xff0c;LSP&#xff09;定义&#xff1a;示例&#xff1a;好处&#…...

LeetCode429周赛T4

最小化二进制字符串中最长相同子字符串的长度 在处理二进制字符串问题时&#xff0c;优化字符串结构以满足特定条件是一项常见的挑战。本文将探讨一个具体的问题&#xff1a;给定一个长度为 n 的二进制字符串 s 和一个整数 numOps&#xff0c;通过最多 numOps 次位翻转操作&am…...

详解MySQL在Windows上的安装

目录 查看电脑上是否安装了MySQL 下载安装MySQL 打开MySQL官网&#xff0c;找到DOWNLOADS 然后往下翻&#xff0c;找到MySQL Community(GPL) Downloads>> 然后找到MySQL Community Server 然后下载&#xff0c;选择No thanks,just start my download. 然后双击进行…...

【Python使用】嘿马python高级进阶全体系教程第10篇:静态Web服务器-返回固定页面数据,1. 开发自己的静态Web服务器【附代码文档】

本教程的知识点为&#xff1a;操作系统 1. 常见的操作系统 4. 小结 ls命令选项 2. 小结 mkdir和rm命令选项 1. mkdir命令选项 压缩和解压缩命令 1. 压缩格式的介绍 2. tar命令及选项的使用 3. zip和unzip命令及选项的使用 4. 小结 编辑器 vim 1. vim 的介绍 2. vim 的工作模式 …...

软件测试面试题和简历模板(面试前准备篇)

一、问题预测 1、让简单介绍下自己&#xff08;这个不用说了每次面试开场&#xff09; 面试官&#xff0c;你好&#xff0c;我叫xxx&#xff0c;xx年本科毕业&#xff0c;从事软件测试将近3年的时间。在此期间做过一些项目也积累过一些经验&#xff0c;能够独立地完成软件测试…...

Linux 基本使用和程序部署

1. Linux 环境搭建 1.1 环境搭建方式 主要有 4 种&#xff1a; 直接安装在物理机上。但是Linux桌面使用起来非常不友好&#xff0c;所以不建议。[不推荐]。使用虚拟机软件&#xff0c;将Linux搭建在虚拟机上。但是由于当前的虚拟机软件(如VMWare之类的)存在一些bug&#xff…...

uniapp微信小程序,使用fastadmin完成一个一键获取微信手机号的功能

前端部分 点击按钮&#xff0c;获取手机号 <button open-type"getPhoneNumber" getphonenumber"bindGetPhoneNumber" hover-class"none"class"btn-purity">一键获取</button> 传入openid和code bindGetPhoneNumber(e) …...

CSS系列(27)- 图形与滤镜详解

前端技术探索系列&#xff1a;CSS 图形与滤镜详解 &#x1f3a8; 致读者&#xff1a;探索CSS的艺术表现力 &#x1f44b; 前端开发者们&#xff0c; 今天我们将深入探讨 CSS 图形和滤镜效果&#xff0c;学习如何创建引人注目的视觉效果。 基础图形 &#x1f680; 几何形状…...

Docker 技术系列之安装多版本Mysql5.6和Mysql5.7

image 大家好&#xff0c;后面的就不是关于MAC专有的内容&#xff0c;基本是跟Java环境&#xff0c;基础技术方面有关。所以这个教程对于在linux系统还是macOS都是通用的&#xff0c;不用担心。 上一篇&#xff0c;我们安装好对应的Docker之后&#xff0c;感受到了它的便利。接…...

理解并使用Linux 内核中的 Tracepoint

理解并使用Linux 内核中的 Tracepoint 1. 引言 1.1 为什么需要 Tracepoint&#xff1f; 在内核调试与性能分析中&#xff0c;传统的 printk 方法虽然简单直接&#xff0c;但存在几个显著的局限性&#xff1a; 日志噪音&#xff1a;printk 会将所有输出无差别地记录到系统日…...

centos7中Gbase8s数据库安装,以及数据导入遇到的一系列问题

centos7中Gbase8s数据库安装&#xff0c;以及遇到的一系列问题 以下是我在centos7上安装gbase8s数据库遇到的一系列问题&#xff0c;包括数据库安装&#xff0c;数据导入&#xff0c;数据连接&#xff0c;不能完全作为标准&#xff0c;只可作为类似问题参考&#xff0c;有问题…...

AW36518芯片手册解读(3)

接前一篇文章&#xff1a;AW36518芯片手册解读&#xff08;2&#xff09; 二、详述 3. 功能描述 &#xff08;1&#xff09;上电复位 当电源电压VIN降至预定义电压VPOR&#xff08;典型值为2.0V&#xff09;以下时&#xff0c;该设备会产生复位信号以执行上电复位操作&#x…...

MySQL的REPEATABLE READ事务隔离级别

本文隔离级别&#xff1a; T1内读T2的update数据 首先开两个事务&#xff08;左二&#xff09; 事务1修改成李四,提交 事务2再读还是张三&#xff0c;也就是说&#xff0c;记录的数据从事务开始时一直到结束&#xff0c;读的都是同一个版本,读不到T2未提交的此条记录修改&…...

sqoop的参数有哪些?

Sqoop 是一款用于在 Hadoop 与关系型数据库之间进行数据传输的工具&#xff0c;它有很多参数&#xff0c;可分为通用参数、导入参数和导出参数等&#xff0c;以下是一些常见的参数介绍&#xff1a; 通用参数 --connect 说明&#xff1a;指定要连接的关系型数据库的 JDBC URL。…...

动态规划<四> 回文串问题(含对应LeetcodeOJ题)

目录 引例 其余经典OJ题 1.第一题 2.第二题 3.第三题 4.第四题 5.第五题 引例 OJ 传送门Leetcode<647>回文子串 画图分析&#xff1a; 使用动态规划解决 原理&#xff1a;能够将所有子串是否是回文的信息保存在dp表中 在使用暴力方法枚举出所有子串&#xff0c;是…...

跨模态知识迁移:基于预训练语言模型的时序数据建模

在NLP和CV领域&#xff0c;通常通过在统一的预训练模型上进行微调&#xff0c;能够在各自领域的下游任务中实现SOTA&#xff08;最先进&#xff09;的结果。然而&#xff0c;在时序预测领域&#xff0c;由于数据量相对较少&#xff0c;难以训练出一个统一的预训练模型来覆盖所有…...

重温设计模式--职责链模式

文章目录 职责链模式的详细介绍C 代码示例C示例代码2 职责链模式的详细介绍 定义与概念 职责链模式&#xff08;Chain of Responsibility Pattern&#xff09;是一种行为型设计模式&#xff0c;它旨在将请求的发送者和多个接收者解耦&#xff0c;让多个对象都有机会处理请求&a…...

git冲突解决

git冲突解决 最近遇到了一次git冲突的问题 起因是因为最近公司数据推送部分重构&#xff0c;负责重构的同事就改动了我的一小部分推送的代码&#xff0c;然后等我开发完合并到远程master的时候&#xff0c;报了merge冲突。我对于git工具确实不是很熟练&#xff0c;只是学习了…...

Java学习笔记(14)--面向对象编程

面向对象基础 学习资料来自多态 - Java教程 - 廖雪峰的官方网站 目录 面向对象基础 Override 多态 举个例子 覆写Object方法 调用super final 练习 小结 Override 在继承关系中&#xff0c;子类如果定义了一个与父类方法签名完全相同的方法&#xff0c;被称为覆写&…...

《Swift 字面量》

《Swift 字面量》 介绍 在 Swift 编程语言中&#xff0c;字面量是一种表示源代码中固定值的表达方式。字面量可以直接表示数字、字符串、布尔值等基本数据类型&#xff0c;为编程提供了简洁和直观的方式。Swift 支持多种类型的字面量&#xff0c;包括整数字面量、浮点数字面量…...

数据库 SQL 常用语句全解析

数据库 SQL 常用语句全解析 在数据库领域&#xff0c;SQL&#xff08;Structured Query Language&#xff09;作为标准语言&#xff0c;掌控着数据的查询、插入、更新与删除等关键操作。无论是新手入门数据库&#xff0c;还是经验丰富的开发者日常工作&#xff0c;熟练掌握 SQ…...

SQLite 命令

关于《SQLite 命令》的文章&#xff0c;我可以为您概述一些关键点。SQLite是一个轻量级的数据库管理系统&#xff0c;它被广泛用于各种应用程序中。SQLite命令主要分为两类&#xff1a;一类是SQL命令&#xff0c;另一类是SQLite特定的点命令。 SQL命令&#xff1a;这些命令用于…...

本地如何启动casdoor

1、下载代码 GitHub - casdoor/casdoor at v1.777.0 下载对应tag的代码&#xff0c;我这里选择的时v1.777.0版本 通过网盘分享的文件&#xff1a;casdoor-1.777.0.zip 链接: https://pan.baidu.com/s/1fPNqyJYeyfZnem_LtEc0hw 提取码: avpd 2、启动后端 1、使用goland编译…...

目标检测-R-CNN

R-CNN在2014年被提出&#xff0c;算法流程可以概括如下&#xff1a; 候选区域生成&#xff1a;利用选择性搜索(selective search)方法找出图片中可能存在目标的候选区域(region proposal) CNN网络提取特征&#xff1a;对候选区域进行特征提取(可以使用AlexNet、VGG等网络) 目…...

【持续更新】Github实用命令

Intro 最近高强度使用github&#xff0c;遂小计于此作为备忘。 Basic github是一个代码管理软件&#xff0c;能够track文件变动并且管理版本&#xff0c;是当代coding必不可少的工具。当你安装好github在本地以后&#xff0c;你可以通过以下命令初始化当前文件夹&#xff08…...

docker 容器的基本使用

docker 容器 一、docker是什么&#xff1f; 软件的打包技术&#xff0c;就是将算乱的多个文件打包为一个整体&#xff0c;打包技术在没有docker容器之前&#xff0c;一直是有这种需求的&#xff0c;比如上节课我把我安装的虚拟机给你们打包了&#xff0c;前面的这种打包方式是…...