VTK|实现类似CloundCompare的测量功能

文章目录

  • CloundCompare在点、线、面三种模式下的显示内容
      • ✅ 图1:点模式
      • ✅ 图2:线模式
      • ✅ 图3:面模式
  • 增加控制菜单栏
  • 实现测量功能类
  • 如何调用
  • 项目git链接

CloundCompare在点、线、面三种模式下的显示内容


在这里插入图片描述
线
在这里插入图片描述

在这里插入图片描述
三张图展示了 CloudCompare 式测量工具浮窗在点、线、面三种模式下的显示内容,它们都包括:


✅ 图1:点模式

Point@Tri#15242
X1 532.893433    XE 258532.893433    R 254
Y1 -126.423424   YE 3356873.576576   G 0
Z1 0.000000      ZE 0.000000         B 0
  • X1/Y1/Z1:局部坐标(例如:模型内部坐标)
  • XE/YE/ZE:全局坐标(世界/地理参考系坐标)
  • R/G/B:该点的颜色信息(RGB)
  • @Tri#15242:三角面片索引号中该点所属的 triangle ID

✅ 图2:线模式

Distance: 159.394958
△X 158.730469   △XY 159.394958
△Y 14.539291    △XZ 158.730469
△Z 0.000000     △ZY 14.539291
  • △X/△Y/△Z:两点之间的坐标差
  • △XY/△XZ/△ZY:平面投影差(XY平面距离等)
  • Distance:三维欧式距离 √(dx² + dy² + dz²)

✅ 图3:面模式

Area: 6427.653320
index.A 15242     AB 159.394958
index.B 14731     BC 126.297385
index.C 12000     CA 101.850845angle.A 52.358784   Nx 0.000000
angle.B 39.685806   Ny 0.000000
angle.C 87.955391   Nz 1.000000
  • index.A/B/C:三个点的 ID
  • AB/BC/CA:边长
  • angle.A/B/C:夹角(角A 是 ∠BAC)
  • Nx/Ny/Nz:面法向量的分量

增加控制菜单栏

头文件:

/*** @file MeasurementMenuWidget.h* @brief 该头文件定义了 MeasurementMenuWidget 类,用于创建测量功能的菜单界面。* @author qtree* @date 2025年5月29日*/
#pragma once#include <QWidget>
#include <QPushButton>
#include <QVBoxLayout>
#include <QEvent>
#include <QMoveEvent>/*** @class MeasurementMenuWidget* @brief 继承自 QWidget,用于创建并管理测量功能的菜单界面。* 该菜单包含点测量、线测量、三角形测量和关闭测量等功能按钮。*/
class MeasurementMenuWidget : public QWidget
{Q_OBJECTpublic:/*** @brief 构造函数,初始化测量菜单窗口。* @param parent 父窗口指针,默认为 nullptr。*/explicit MeasurementMenuWidget(QWidget *parent = nullptr);/*** @brief 在指定位置显示测量菜单。* @param position 菜单显示的位置。*/void showMenu(const QPoint &position); // 显示菜单/*** @brief 隐藏测量菜单。*/void hideMenu(); // 隐藏菜单protected:/*** @brief 事件过滤器,用于处理特定对象的事件。* @param watched 被监视的对象。* @param event 发生的事件。* @return 如果事件已被处理则返回 true,否则返回 false。*/bool eventFilter(QObject *watched, QEvent *event);signals:/*** @brief 发出点测量请求信号。*/void pointMeasureRequested();/*** @brief 发出线测量请求信号。*/void lineMeasureRequested();/*** @brief 发出三角形测量请求信号。*/void triangleMeasureRequested();/*** @brief 发出关闭测量请求信号。*/void closeMeasureRequested();private:/*** @brief 点测量功能按钮。*/QPushButton *pointBtn_;/*** @brief 线测量功能按钮。*/QPushButton *lineBtn_;/*** @brief 三角形测量功能按钮。*/QPushButton *triangleBtn_;/*** @brief 关闭测量功能按钮。*/QPushButton *closeBtn_;/*** @brief 菜单相对于父窗口的位置偏移。*/QPoint anchorOffset_; // 相对于父窗口的位置偏移/*** @brief 更新按钮的高亮状态。* @param activeBtn 当前激活的按钮。*/void updateHighlight(QPushButton *activeBtn);
};

源文件:

#include "MeasurementMenuWidget.h"MeasurementMenuWidget::MeasurementMenuWidget(QWidget *parent): QWidget(parent)
{if (parent)parent->installEventFilter(this);setWindowFlags(Qt::FramelessWindowHint | Qt::Tool); // 无边框悬浮窗setAttribute(Qt::WA_ShowWithoutActivating);         // 不抢焦点setStyleSheet("QPushButton { min-width: 80px; }");QVBoxLayout *layout = new QVBoxLayout(this);layout->setContentsMargins(5, 5, 5, 5);layout->setSpacing(5);pointBtn_ = new QPushButton("Point");lineBtn_ = new QPushButton("Line");triangleBtn_ = new QPushButton("Triangle");closeBtn_ = new QPushButton("Close");layout->addWidget(pointBtn_);layout->addWidget(lineBtn_);layout->addWidget(triangleBtn_);layout->addWidget(closeBtn_);connect(pointBtn_, &QPushButton::clicked, this, [=](){emit pointMeasureRequested();updateHighlight(pointBtn_); });connect(lineBtn_, &QPushButton::clicked, this, [=](){emit lineMeasureRequested();updateHighlight(lineBtn_); });connect(triangleBtn_, &QPushButton::clicked, this, [=](){emit triangleMeasureRequested();updateHighlight(triangleBtn_); });connect(closeBtn_, &QPushButton::clicked, this, [=](){emit closeMeasureRequested();hideMenu(); });
}void MeasurementMenuWidget::showMenu(const QPoint &position)
{if (parentWidget())anchorOffset_ = position - parentWidget()->mapToGlobal(QPoint(0, 0));move(position);show();
}void MeasurementMenuWidget::hideMenu()
{hide();
}bool MeasurementMenuWidget::eventFilter(QObject *watched, QEvent *event)
{if (watched == parentWidget() && event->type() == QEvent::Move){if (isVisible()){QPoint newGlobalPos = parentWidget()->mapToGlobal(QPoint(0, 0)) + anchorOffset_;move(newGlobalPos);}}return QWidget::eventFilter(watched, event);
}void MeasurementMenuWidget::updateHighlight(QPushButton *activeBtn)
{QList<QPushButton *> buttons = {pointBtn_, lineBtn_, triangleBtn_};for (auto btn : buttons)btn->setStyleSheet(btn == activeBtn ? "background-color: lightblue;" : "");
}

实现测量功能类

头文件:

#pragma once#include <QObject>
#include <vtkSmartPointer.h>
#include <vtkRenderer.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkTextActor.h>
#include <vtkActor.h>
#include <vtkLineSource.h>
#include <vtkPolyDataMapper.h>
#include <vtkSphereSource.h>
#include <vtkCaptionActor2D.h>
#include <vector>
#include <array>/*** @enum MeasurementMode* @brief 定义测量模式的枚举类型,用于指定当前的测量操作类型。*/
enum class MeasurementMode
{None,    ///< 无测量模式,不进行任何测量操作。Point,   ///< 点测量模式,用于选择和测量单个点。Line,    ///< 线测量模式,用于选择和测量两点之间的距离。Triangle ///< 三角形测量模式,用于选择和测量三角形的面积和角度。
};/*** @class MeasurementController* @brief 测量控制器类,继承自 QObject,负责处理点、线、面的选择和测量显示。** 该类提供了设置测量模式、清除测量数据、处理鼠标点击事件等功能,* 并能根据用户选择的测量模式在渲染窗口中显示相应的测量结果。*/
class MeasurementController : public QObject
{Q_OBJECTpublic:/*** @brief 构造函数,初始化测量控制器。** @param renderer 指向 vtkRenderer 的指针,用于在渲染窗口中显示测量结果。* @param interactor 指向 vtkRenderWindowInteractor 的指针,用于处理用户交互事件。*/MeasurementController(vtkRenderer *renderer, vtkRenderWindowInteractor *interactor);/*** @brief 设置当前的测量模式。** @param mode 要设置的测量模式,为 MeasurementMode 枚举类型。*/void setMode(MeasurementMode mode);/*** @brief 清除当前所有的测量数据和显示的图形。*/void clearMeasurements();/*** @brief 处理鼠标左键点击事件。** 该函数需要在外部与鼠标左键点击事件连接,用于响应鼠标点击操作。*/void onLeftButtonPressed();/*** @brief 重新将文本框和其他图形添加到渲染场景中。*/void ReAddActorsToRenderer();private:/*** @brief 在指定位置添加一个球体标记点。** @param pos 标记点的三维坐标数组。*/void addPointMarker(const double pos[3]);/*** @brief 根据已选的测量点更新测量图形和文本显示。*/void updateMeasurementDisplay();/*** @brief 在渲染窗口中绘制一条直线。** @param p1 直线起点的三维坐标数组。* @param p2 直线终点的三维坐标数组。*/void renderLine(const double p1[3], const double p2[3]);/*** @brief 在渲染窗口中绘制一个三角形。** @param p1 三角形第一个顶点的三维坐标数组。* @param p2 三角形第二个顶点的三维坐标数组。* @param p3 三角形第三个顶点的三维坐标数组。*/void renderTriangle(const double p1[3], const double p2[3], const double p3[3]);/*** @brief 请求刷新渲染窗口。*/void render();/*** @brief 更新文本信息框的显示内容。*/void updateTextActor();/*** @brief 清除所有的标记点和测量图形。*/void clearAllMarkers();vtkRenderer *renderer_;                               ///< 指向 vtkRenderer 的指针,用于渲染测量结果。vtkRenderWindowInteractor *interactor_;               ///< 指向 vtkRenderWindowInteractor 的指针,用于处理用户交互。MeasurementMode mode_ = MeasurementMode::None;        ///< 当前的测量模式。std::vector<std::array<double, 3>> pickedPoints_;     ///< 已选的测量点的列表。std::vector<vtkSmartPointer<vtkActor>> pointMarkers_; ///< 所有绘制的 actor(点、线)的列表。vtkSmartPointer<vtkTextActor> textActor_;             ///< 用于显示测量信息的文本 actor。
};

源文件:

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif#include "MeasurementController.h"
#include <vtkPointPicker.h>
#include <vtkTextProperty.h>
#include <vtkProperty.h>
#include <vtkMath.h>
#include <cmath>
#include <vtkRenderWindow.h>
#include <vtkSphereSource.h>
#include <vtkProperty2D.h>
#include <QDebug>MeasurementController::MeasurementController(vtkRenderer *renderer, vtkRenderWindowInteractor *interactor): renderer_(renderer), interactor_(interactor)
{qDebug() << "[MeasurementController] Entering constructor";textActor_ = vtkSmartPointer<vtkTextActor>::New();textActor_->SetDisplayPosition(20, 20);textActor_->GetTextProperty()->SetFontSize(16);textActor_->GetTextProperty()->SetColor(0.0, 0.0, 0.0);           // 黑字textActor_->GetTextProperty()->SetBackgroundColor(1.0, 1.0, 1.0); // 白底textActor_->GetTextProperty()->SetBackgroundOpacity(0.8);         // 半透明背景textActor_->GetTextProperty()->SetFrame(1);                       // 开启边框textActor_->GetTextProperty()->SetFrameColor(1.0, 0.0, 0.0);      // 红框textActor_->SetVisibility(0);                                     // 初始隐藏renderer_->AddActor2D(textActor_);
}void MeasurementController::setMode(MeasurementMode mode)
{// clearMeasurements();mode_ = mode;if (mode_ == MeasurementMode::None){clearMeasurements();}
}void MeasurementController::clearMeasurements()
{pickedPoints_.clear();for (auto &actor : pointMarkers_){renderer_->RemoveActor(actor);}pointMarkers_.clear();textActor_->SetInput("");render();
}void MeasurementController::onLeftButtonPressed()
{if (mode_ == MeasurementMode::None){qDebug() << "[MeasurementController] Current mode is None. Click ignored.";return;}int x, y;interactor_->GetEventPosition(x, y);qDebug() << "[MeasurementController] Mouse clicked at: (" << x << "," << y << ")";auto picker = vtkSmartPointer<vtkPointPicker>::New();if (!picker->Pick(x, y, 0, renderer_)){qDebug() << "[MeasurementController] Point picking failed. No valid geometry hit.";return;}double pos[3];picker->GetPickPosition(pos);qDebug() << "[MeasurementController] Point picked at: ("<< pos[0] << "," << pos[1] << "," << pos[2] << ")";// 清除逻辑根据当前点的数量判断switch (mode_){case MeasurementMode::Point:pickedPoints_.clear();clearAllMarkers(); // 清除之前的可视化标记break;case MeasurementMode::Line:if (pickedPoints_.size() >= 2){pickedPoints_.clear();clearAllMarkers();qDebug() << "[MeasurementController] Line mode - previous 2 points cleared.";}break;case MeasurementMode::Triangle:if (pickedPoints_.size() >= 3){pickedPoints_.clear();clearAllMarkers();qDebug() << "[MeasurementController] Triangle mode - previous 3 points cleared.";}break;default:break;}// 添加当前点击的点pickedPoints_.emplace_back(std::array<double, 3>{pos[0], pos[1], pos[2]});qDebug() << "[MeasurementController] Picked point count:" << pickedPoints_.size();// 添加可视化标记addPointMarker(pos);// 当达到点数要求时,执行测量逻辑if ((mode_ == MeasurementMode::Line && pickedPoints_.size() == 2) ||(mode_ == MeasurementMode::Triangle && pickedPoints_.size() == 3)){qDebug() << "[MeasurementController] Required number of points reached. Updating measurement display.";updateMeasurementDisplay();}updateTextActor();
}void MeasurementController::addPointMarker(const double pos[3])
{auto sphere = vtkSmartPointer<vtkSphereSource>::New();double center[3] = {pos[0], pos[1], pos[2]};sphere->SetCenter(center);sphere->SetRadius(1.0);auto mapper = vtkSmartPointer<vtkPolyDataMapper>::New();mapper->SetInputConnection(sphere->GetOutputPort());auto actor = vtkSmartPointer<vtkActor>::New();actor->SetMapper(mapper);actor->GetProperty()->SetColor(1, 0, 0); // 红色球体pointMarkers_.push_back(actor);renderer_->AddActor(actor);render();
}void MeasurementController::updateMeasurementDisplay()
{if (mode_ == MeasurementMode::Line && pickedPoints_.size() >= 2){renderLine(pickedPoints_[0].data(), pickedPoints_[1].data());}else if (mode_ == MeasurementMode::Triangle && pickedPoints_.size() >= 3){renderTriangle(pickedPoints_[0].data(), pickedPoints_[1].data(), pickedPoints_[2].data());}render();
}void MeasurementController::renderLine(const double p1[3], const double p2[3])
{qDebug() << "[MeasurementController] Entering renderLine function";auto line = vtkSmartPointer<vtkLineSource>::New();double pt1[3] = {p1[0], p1[1], p1[2]}; // 非 const 拷贝double pt2[3] = {p2[0], p2[1], p2[2]}; // 非 const 拷贝line->SetPoint1(pt1);line->SetPoint2(pt2);auto mapper = vtkSmartPointer<vtkPolyDataMapper>::New();mapper->SetInputConnection(line->GetOutputPort());// ⭐关键:启用拓扑偏移,让线绘制时偏移一点点,避免被遮挡(VTK 8.2 的做法)vtkMapper::SetResolveCoincidentTopologyToPolygonOffset();mapper->SetResolveCoincidentTopology(true);mapper->SetResolveCoincidentTopologyPolygonOffsetParameters(1.0, 1.0); // 偏移强度auto actor = vtkSmartPointer<vtkActor>::New();actor->SetMapper(mapper);actor->GetProperty()->SetColor(1, 0, 0);actor->GetProperty()->SetLineWidth(2.0);actor->GetProperty()->SetColor(1, 0, 0);  // 红色actor->GetProperty()->SetLineWidth(2.0);  // 线宽actor->GetProperty()->SetLighting(false); // 可选,关闭光照影响// 可选:强制不透明,避免透明影响排序actor->GetProperty()->SetOpacity(1.0);pointMarkers_.push_back(actor);renderer_->AddActor(actor);
}void MeasurementController::renderTriangle(const double p1[3], const double p2[3], const double p3[3])
{renderLine(p1, p2);renderLine(p2, p3);renderLine(p3, p1);
}void MeasurementController::render()
{if (renderer_ && renderer_->GetRenderWindow()){renderer_->GetRenderWindow()->Render();}
}void MeasurementController::updateTextActor()
{if (mode_ == MeasurementMode::None){textActor_->SetVisibility(0);return;}QString text;if (pickedPoints_.size() == 1){const auto &p = pickedPoints_[0];text = QString("Point@Local\nX1:%1    Y1:%2    Z1:%3").arg(p[0], 0, 'f', 6).arg(p[1], 0, 'f', 6).arg(p[2], 0, 'f', 6);}else if (pickedPoints_.size() == 2){const auto &p1 = pickedPoints_[0];const auto &p2 = pickedPoints_[1];double dx = p2[0] - p1[0];double dy = p2[1] - p1[1];double dz = p2[2] - p1[2];double dxy = std::sqrt(dx * dx + dy * dy);double dxz = std::sqrt(dx * dx + dz * dz);double dyz = std::sqrt(dy * dy + dz * dz);double dist = std::sqrt(dx * dx + dy * dy + dz * dz);text = QString("Distance: %1\n""△X:%2   △Y:%3   △Z:%4\n""△XY:%5  △XZ:%6  △YZ:%7").arg(QString::number(dist, 'f', 6).rightJustified(12, ' ')).arg(QString::number(dx, 'f', 6).rightJustified(12, ' ')).arg(QString::number(dy, 'f', 6).rightJustified(12, ' ')).arg(QString::number(dz, 'f', 6).rightJustified(12, ' ')).arg(QString::number(dxy, 'f', 6).rightJustified(12, ' ')).arg(QString::number(dxz, 'f', 6).rightJustified(12, ' ')).arg(QString::number(dyz, 'f', 6).rightJustified(12, ' '));}else if (pickedPoints_.size() == 3){const auto &A = pickedPoints_[0];const auto &B = pickedPoints_[1];const auto &C = pickedPoints_[2];// 向量 AB, BC, CAdouble AB[3] = {B[0] - A[0], B[1] - A[1], B[2] - A[2]};double BC[3] = {C[0] - B[0], C[1] - B[1], C[2] - B[2]};double CA[3] = {A[0] - C[0], A[1] - C[1], A[2] - C[2]};// 边长double lenAB = std::sqrt(AB[0] * AB[0] + AB[1] * AB[1] + AB[2] * AB[2]);double lenBC = std::sqrt(BC[0] * BC[0] + BC[1] * BC[1] + BC[2] * BC[2]);double lenCA = std::sqrt(CA[0] * CA[0] + CA[1] * CA[1] + CA[2] * CA[2]);// 向量 AC(用于法线)double AC[3] = {C[0] - A[0], C[1] - A[1], C[2] - A[2]};double N[3] = {AB[1] * AC[2] - AB[2] * AC[1],AB[2] * AC[0] - AB[0] * AC[2],AB[0] * AC[1] - AB[1] * AC[0]};double normN = std::sqrt(N[0] * N[0] + N[1] * N[1] + N[2] * N[2]);double area = 0.5 * normN;// 单位法向量if (normN > 1e-6){N[0] /= normN;N[1] /= normN;N[2] /= normN;}// 角度(夹角):使用余弦定理auto angle = [](const double *u, const double *v) -> double{double dot = u[0] * v[0] + u[1] * v[1] + u[2] * v[2];double lenU = std::sqrt(u[0] * u[0] + u[1] * u[1] + u[2] * u[2]);double lenV = std::sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);double cosTheta = dot / (lenU * lenV);cosTheta = std::clamp(cosTheta, -1.0, 1.0);return std::acos(cosTheta) * 180.0 / M_PI;};double angleA = angle(CA, AB); // ∠CABdouble angleB = angle(AB, BC); // ∠ABCdouble angleC = angle(BC, CA); // ∠BCAtext = QString("Area:%1\n").arg(QString::number(area, 'f', 6).rightJustified(12, ' '));text += QString("AB:%1  BC:%2  CA:%3\n").arg(QString::number(lenAB, 'f', 6).rightJustified(12, ' ')).arg(QString::number(lenBC, 'f', 6).rightJustified(12, ' ')).arg(QString::number(lenCA, 'f', 6).rightJustified(12, ' '));text += QString("angle.A:%1°  angle.B:%2°  angle.C:%3°\n").arg(QString::number(angleA, 'f', 3).rightJustified(8, ' ')).arg(QString::number(angleB, 'f', 3).rightJustified(8, ' ')).arg(QString::number(angleC, 'f', 3).rightJustified(8, ' '));text += QString("Nx:%1  Ny:%2  Nz:%3").arg(QString::number(N[0], 'f', 6).rightJustified(12, ' ')).arg(QString::number(N[1], 'f', 6).rightJustified(12, ' ')).arg(QString::number(N[2], 'f', 6).rightJustified(12, ' '));}else{text = ""; // 超过3个点暂不支持}textActor_->SetInput(text.toUtf8().data());textActor_->SetDisplayPosition(20, 20);textActor_->SetVisibility(!text.isEmpty());
}void MeasurementController::clearAllMarkers()
{for (auto actor : pointMarkers_){renderer_->RemoveActor(actor);}pointMarkers_.clear();if (textActor_){textActor_->SetVisibility(0); // 不删除,只隐藏}interactor_->GetRenderWindow()->Render();
}void MeasurementController::ReAddActorsToRenderer()
{if (textActor_ && renderer_){renderer_->AddActor2D(textActor_);}
}

如何调用

定义调用类内全局变量

// 初始化测量菜单
void initMeasurementMenu();protected:bool eventFilter(QObject *obj, QEvent *event);//  测量功能
MeasurementMenuWidget *measurementMenuWidget_;
std::unique_ptr<MeasurementController> measurementController_;
QPushButton *measurement_btn_;

实现类


// 初始化测量控制器
measurementController_ = std::make_unique<MeasurementController>(renderer_, interactor_);
initMeasurementMenu();// 测量按钮
measurement_btn_ = new QPushButton("measurement");
control_btn_layout_2->addWidget(measurement_btn_);
// 测量按钮点击后显示菜单(放在合适位置,如右上角)
connect(measurement_btn_, &QPushButton::clicked, this, [=](){QPoint globalPos = mapToGlobal(QPoint(width() - 150, 50)); // 控制右上角偏移measurementMenuWidget_->showMenu(globalPos); });void ThreeDimensionalDisplayPage::initMeasurementMenu()
{measurementMenuWidget_ = new MeasurementMenuWidget(this);// 连接槽函数(你已有的 measurementController_)connect(measurementMenuWidget_, &MeasurementMenuWidget::pointMeasureRequested, this, [=](){ measurementController_->setMode(MeasurementMode::Point); });connect(measurementMenuWidget_, &MeasurementMenuWidget::lineMeasureRequested, this, [=](){ measurementController_->setMode(MeasurementMode::Line); });connect(measurementMenuWidget_, &MeasurementMenuWidget::triangleMeasureRequested, this, [=](){ measurementController_->setMode(MeasurementMode::Triangle); });connect(measurementMenuWidget_, &MeasurementMenuWidget::closeMeasureRequested, this, [=](){ measurementController_->setMode(MeasurementMode::None); });// 测量按钮点击后显示菜单(放在合适位置,如右上角)connect(measurement_btn_, &QPushButton::clicked, this, [=](){QPoint globalPos = mapToGlobal(QPoint(width() - 150, 50)); // 控制右上角偏移measurementMenuWidget_->showMenu(globalPos); });
}// 重新添加测量控件的 2D actor
if (measurementController_)
{measurementController_->ReAddActorsToRenderer();
}bool ThreeDimensionalDisplayPage::eventFilter(QObject *obj, QEvent *event)
{if (obj == m_pScene && event->type() == QEvent::MouseButtonPress){QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);if (mouseEvent->button() == Qt::LeftButton){if (measurementController_)measurementController_->onLeftButtonPressed();return true; // 拦截事件}}return QWidget::eventFilter(obj, event); // 交给默认处理
}

项目git链接

gitee:https://gitee.com/strange-tree-qian/vtktest
github:https://github.com/qishuqian666/project-vtk-test

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

4000万日订单背后,饿了么再掀即时零售的“效率革命”

当即时零售转向价值深耕&#xff0c;赢面就是综合实力的强弱。 文&#xff5c;郭梦仪 编&#xff5c;王一粟 在硝烟弥漫的外卖行业“三国杀”中&#xff0c;饿了么与淘宝闪购的日订单量竟然突破了4000万单。 而距淘宝闪购正式上线&#xff0c;还不到一个月。 在大额福利优惠…

vedio.ontimeupdate()和video.onloadeddata()

video.onloadeddata &#xff08;&#xff09; video.onloadeddata 是 JavaScript 中用于监听 HTML <video> 元素 「当前帧数据已加载」 的事件处理器。当视频的第一帧画面数据加载完成&#xff08;足以开始播放&#xff09;时&#xff0c;会触发此事件。 1. 基本用法 …

Baklib内容中台革新企业知识实践

Baklib智能知识中枢构建 作为现代企业知识管理的核心架构&#xff0c;Baklib内容中台通过整合多源异构数据形成智能化知识中枢&#xff0c;实现从信息采集到价值转化的全链路管理。其底层采用跨平台数据贯通技术&#xff0c;支持API接口与企业现有CRM、ERP系统无缝对接&#x…

用不太严谨的文字介绍遥测自跟踪天线的基本原理

前两天跟一个客户见面的时候&#xff0c;客户问我&#xff1a;遥测自跟踪天线能够跟踪目标&#xff0c;是什么原理&#xff1f;不需要目标的位置&#xff0c;怎么做到自跟踪的&#xff1f; 突然一瞬间&#xff0c;有点语塞。 难道要介绍天线、馈源、极化、左旋、右旋、和差网…

VS配置redis环境、redis简单封装

一、安装redis数据库 1.下载redis的压缩包 wget https://download.redis.io/releases/redis-6.0.5.tar.g 2.解压缩redis压缩包&#xff0c;一般就在当前路径 tar -zvxf redis-6.0.5.tar.gz -C /usr/local/redis 方便找我把它解压缩在/usr/local/redis&#xff0c;如果没有r…

C++23 已移除特性解析

文章目录 引言C23 已移除特性介绍1. 垃圾收集的支持和基于可达性的泄漏检测&#xff08;P2186R2&#xff09;背景与原理存在的问题移除的影响 2. 混合宽字符串字面量拼接非良构&#xff08;P2201R1&#xff09;宽字符串编码概述混合拼接的问题示例分析移除的意义 3. 不可编码宽…

Cloudflare

Cloudflare 是一个网络基础设施和网站安全服务提供商&#xff0c;它的主要作用是让网站 更快、更安全、更可靠。简单来说&#xff0c;它是一个“护盾 加速器”。 &#x1f9e9; Cloudflare 的主要功能&#xff1a; 1. &#x1f680; 加速网站访问&#xff08;CDN&#xff09…

Spring Boot启动慢?Redis缓存击穿?Kafka消费堆积?——Java后端常见问题排查实战

Spring Boot启动慢&#xff1f;Redis缓存击穿&#xff1f;Kafka消费堆积&#xff1f;——Java后端常见问题排查实战 引言 Java后端系统因其丰富的技术栈和复杂的业务逻辑&#xff0c;常常面临启动延迟、性能瓶颈、异常错误等多种挑战。从核心语言、Web框架到分布式微服务及缓…

数字人引领政务新风尚:智能设备助力政务服务

在信息技术飞速发展的今天&#xff0c;政府机构不断探索提升服务效率和改善服务质量的新途径。实时交互数字人在政务服务中的应用正成为一大亮点&#xff0c;通过将“数字公务员”植入各种横屏智能设备中&#xff0c;为民众办理业务提供全程辅助。这种创新不仅优化了政务大厅的…

ToolsSet之:十六进制及二进制编辑运算工具

ToolsSet是微软商店中的一款包含数十种实用工具数百种细分功能的工具集合应用&#xff0c;应用基本功能介绍可以查看以下文章&#xff1a; Windows应用ToolsSet介绍https://blog.csdn.net/BinField/article/details/145898264 ToolsSet中Number菜单下的Hex Operate工具可以进…

DSP处理数字信号做什么用的?

DSP&#xff08;数字信号处理器&#xff09;的核心任务是高效、实时地处理数字信号&#xff0c;通过专用硬件架构和算法优化&#xff0c;完成对信号的转换、增强、分析和控制。以下是DSP处理数字信号的主要用途及典型场景&#xff1a; 1. 信号增强与优化 降噪&#xff08;Noise…

电脑如何保养才能用得更久

在这个数字化的时代&#xff0c;电脑已经成为了我们生活和工作中不可或缺的伙伴。无论是处理工作文档、追剧娱乐&#xff0c;还是进行创意设计&#xff0c;电脑都发挥着至关重要的作用。那么&#xff0c;如何让我们的电脑“健康长寿”&#xff0c;陪伴我们更久呢&#xff1f;今…

设计模式-监听者模式

文章目录 监听者模式 监听者模式 监听器模式指的是事件源经过事件的封装传给监听器&#xff0c;当事件源触发事件之后&#xff0c;监听器收到事件的通知并执行事件回调方法。 -监听者观察者概念定义当范围对象的状态发生变化时&#xff0c;服务器自动调用监听器对象中的方法来…

小程序33-列表渲染

列表渲染 就是指通过循环遍历一个数组或对象&#xff0c;将其中的每个元素渲染到页面上 在组件上使用 wx:for 属性绑定一个数组或对象&#xff0c;既可使用每一项数据重复渲染当前组件 每一项的变量名默认为item&#xff0c;下标变量名默认为index 在使用 wx:for进行遍历的时候…

[ Qt ] | QRadioButton和QCheckBox的使用

目录 QRadioButton 常用属性 clicked(bool)信号、pressed信号、released信号 小项目 QRadioButton QRadioButton是一个单选按钮&#xff0c;也是继承自QAbstractButton(继承自QWidget) 常用属性 checkable 是否能选中 checked 是否已经被选中 autoExclusive 是否排…

[网页五子棋][匹配模式]创建房间类、房间管理器、验证匹配功能,匹配模式小结

文章目录 创建房间类创建房间类实现房间管理器 实现匹配器(3)验证匹配功能问题&#xff1a;匹配按钮不改变验证多开 小结 创建房间类 LOL&#xff0c;通过匹配的方式&#xff0c;自动给你加入到一个房间&#xff0c;也可手动创建游戏房间 这一局游戏&#xff0c;进行的“场所…

Apifox 5 月产品更新|数据模型支持查看「引用资源」、调试 AI 接口可实时预览 Markdown、性能优化

Apifox 新版本上线啦&#xff01; 看看本次版本更新主要涵盖的重点内容&#xff0c;有没有你所关注的功能特性&#xff1a; 自动解析 JSON 参数名和参数值调试 AI 接口时&#xff0c;可预览 Markdown 格式的内容性能优化&#xff1a;新增「实验性功能」选项 使用独立进程执行…

Spring MVC 框架

目录 1.MVC的定义 2.SpringMVC的实际应用 &#xff08;1&#xff09;建立连接 1.RequestMapping注解介绍 2.RequestMapping注解的请求方式 GET请求&#xff1a; POST请求&#xff1a; 指定GET/POST方法类型&#xff1a; &#xff08;2&#xff09;请求 传递参数 1.传…

基于RK3568/RK3588/全志H3/飞腾芯片/音视频通话程序/语音对讲/视频对讲/实时性好/极低延迟

一、前言说明 近期收到几个需求都是做音视频通话&#xff0c;很多人会选择用webrtc的方案&#xff0c;这个当然是个不错的方案&#xff0c;但是依赖的东西太多&#xff0c;而且相关组件代码量很大&#xff0c;开发难度大。所以最终选择自己属性的方案&#xff0c;那就是推流拉…

AI+爆款文案,提示词脚本 ——卫朋

目录 简介 提示词 作者简介 简介 用好AI的前提是脑子里面要有框架。 AI就像是一个刚出生的小孩&#xff0c;没有判断力&#xff0c;疯狂接收世界上的各类信息。 如果没有从小的规则框架约束、没有道德约束&#xff0c;最终的结果就一定是混乱无序的。 AI也是一样&#x…