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:三維歐式距離 √(dx2 + dy2 + dz2)

? 圖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
繁體地址,請注明出處:http://hk.pswp.cn/diannao/85167.shtml
英文地址,請注明出處:http://en.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…