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

article/2025/6/18 21:46:21

文章目录

  • 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.hkcw.cn/article/cuSRxkJUOH.shtml

相关文章

Android15 userdebug版本不能remount

背景描述&#xff1a; 最近调试Android Vendor Hal的时候发现一个奇怪的现象: android userdebug版本刷到设备中&#xff0c;执行adb root没提示错误&#xff0c;但是没有获取到root权限。 Android设备运行的系统版本有三种情况&#xff1a;user版本、userdebug版本和eng版本…

伊朗外长:将适当回应美方核谈判提案

△伊朗外交部长阿拉格齐(资料图)当地时间5月31日,伊朗外交部长阿拉格齐在社交平台表示,当天阿曼外交大臣巴德尔访问伊朗并向其介绍了美方有关核谈判的提案。阿拉格齐表示,伊朗将根据原则、国家利益和伊朗人民的权利对此作出适当的回应。白宫新闻秘书莱维特当地时间31日表示…

27 C 语言编程核心:main 主函数(基本形式、返回值、参数、命令行传参)、多文件编程实践

1 main 主函数 1.1 主函数的作用 在 C 语言中&#xff0c;main 主函数是程序的入口函数&#xff0c;所有 C 程序必须包含一个名为 main 的函数。程序总是从该函数开始执行&#xff0c;没有它程序就无法启动。 主函数可以调用其他函数。其他函数不能调用主函数。主函数不能调用…

GIS常见数据及主要应用综述:类型解析、应用案例与未来趋势全景解读

&#x1f30f; GIS常见数据及主要应用综述&#xff1a;类型解析、应用案例与未来趋势全景解读 地理信息系统&#xff08;GIS&#xff09;是支撑空间决策、资源管理、城市治理的重要技术体系。本文从常见数据类型入手&#xff0c;结合中国及国际资源&#xff0c;梳理典型GIS应用…

系统性学习C语言-第十二讲-深入理解指针(2)

系统性学习C语言-第十二讲-深入理解指针&#xff08;2&#xff09; 1. const 修饰指针1.1 const 修饰变量1.2 const 修饰指针变量 2. 野指针2.1 野指针成因2.2 如何规避野指针2.2.1 指针初始化2.2.2 小心指针越界2.2.3 指针变量不再使用时&#xff0c;及时置 NULL &…

Linux安装redis

Linux redis路径 https://download.redis.io/releases/解压安装Redis 解压 tar -zvxf redis-6.0.5.tar.gz 由于redis是c语言编写的&#xff0c;所以我们需要先安装gcc&#xff0c;安装的命令如下&#xff1a; yum install gcc-c安装 输入命令&#xff1a; make PREFIX/usr/…

NumPy 数组计算:广播机制

文章目录 NumPy 数组计算&#xff1a;广播机制一、广播机制简介二、广播机制的规则1. 广播机制示例 12. 广播机制示例 23. 广播机制示例 3 三、广播机制实战1. 数组的中心化2. 绘制二维函数 NumPy 数组计算&#xff1a;广播机制 我们在NumPy数组的计算&#xff1a;通用函数中看…

Codesys FOR 循环之轴控

关于多伺服的轴控,不管怎么写都会很复杂,要么编程的时候代码行数多,要么是后期检查时非常麻烦,目前还未找到一个两全其美的方法,今天介绍的是通过FOR循环的轴控,就属于后者,代码行数较少,控制的轴数也没有限制,不需要一个轴一个的复制FB块,但是想在调试的时候实时查看…

欧冠决赛杜埃梅开二度 新星闪耀赛场

北京时间6月1日,本赛季的欧冠决赛中,19岁的杜埃表现出色,梅开二度并送出一次助攻,帮助巴黎圣日耳曼在比赛进行到73分钟时以4-0领先国际米兰。据统计,杜埃成为自1964年国际米兰名宿桑德罗-马佐拉以来,首位在欧冠决赛中完成梅开二度并且送出助攻的球员。本赛季,杜埃代表巴…

使用VSCode在WSL和Docker中开发

通过WSL&#xff0c;开发人员可以安装 Linux 发行版&#xff08;例如 Ubuntu、OpenSUSE、Kali、Debian、Arch Linux 等&#xff09;&#xff0c;并直接在 Windows 上使用 Linux 应用程序、实用程序和 Bash 命令行工具&#xff0c;不用进行任何修改&#xff0c;也无需使用传统虚…

《汇编语言》第12章 内中断——实验12 编写0号中断的处理程序

编写0号中断的处理程序&#xff0c;使得在除法溢出发生时&#xff0c;在屏幕中间显示字符串"divide error&#xff01;"&#xff0c;然后返回到DOS。 要求&#xff1a;仔细跟踪调试&#xff0c;在理解整个过程之前&#xff0c;不要进行后面课程的学习。 ;sy12.asm …

黑马k8s(十八)

一&#xff1a;安全认证 1.安全认证-概述 2.安全认证-认证方式 认证管理 3.安全认证-授权管理 因为没有授予角色deployment的权限&#xff0c;所以不能查看 4.安全认证-准入控制 二&#xff1a;DashBoard 之前在kubernetes中完成的所有操作都是通过命令行工具kubectl完成的…

python:PyMOL 使用教程 及实用示例

安装参阅&#xff1a;开源版PyMol安装保姆级教程 百度网盘下载 提取码&#xff1a;csub 简介: PyMOL是一个Python增强的分子图形工具。它擅长蛋白质、小分子、密度、表面和轨迹的3D可视化。它还包括分子编辑、射线追踪和动画。 PyMol的名字来源于“Py”表示该软件基于Python这…

第十二节:第三部分:集合框架:List系列集合:特点、方法、遍历方式、ArrayList集合的底层原理

List系列集合特点 List集合的特有方法 List集合支持的遍历方式 ArrayList集合的底层原理 ArrayList集合适合的应用场景 代码&#xff1a;List系列集合遍历方式 package com.itheima.day19_Collection_List;import java.util.ArrayList; import java.util.Iterator; import jav…

ZC-OFDM雷达通信一体化减小PAPR——SC-FDMA技术

文章目录 前言一、SC-FDMA 技术1、简介2、原理 二、MATLAB 仿真1、核心代码2、仿真结果 三、资源自取 前言 在 OFDM 雷达通信一体化系统中&#xff0c;信号的传输由多个子载波协同完成&#xff0c;多个载波信号相互叠加形成最终的发射信号。此叠加过程可能导致信号峰值显著高于…

【算法】贪心算法

一、贪心算法基本思想 贪心算法总是作出在当前看来最好的选择。也就是说贪心算法并不从 整体最优考虑&#xff0c;它所作出的选择只是在某种意义上的局部最优选择。 我们希望贪心算法得到的最终结果也是整体最优的。虽然贪心算法不 能对所有问题都得到整体最优解&#xff08;O…

通义灵码深度实战测评:从零构建智能家居控制中枢,体验AI编程新范式

一、项目背景&#xff1a;零基础挑战全栈智能家居系统 目标&#xff1a;开发具备设备控制、环境感知、用户习惯学习的智能家居控制中枢&#xff08;PythonFlaskMQTTReact&#xff09; 挑战点&#xff1a; 需集成硬件通信(MQTT)、Web服务(Flask)、前端交互(React) 调用天气AP…

C 语言开发中常见的开发环境

目录 1.Dev-C 2.Visual Studio Code 3.虚拟机 Linux 环境 4.嵌入式 MCU 专用开发环境 1.Dev-C 使用集成的 C/C 开发环境&#xff08;注&#xff1a;较老旧方案&#xff0c;适合基础学习&#xff09; 2.Visual Studio Code 结合 C/C 扩展 GCC/MinGW 编译器&#xff0c;配置…

关于用Cloudflare的Zero Trust实现绕过备案访问国内站点说明

cloudflare 是一个可免费的CDN&#xff0c;CDN&#xff08;Content Delivery Network&#xff0c;内容分发网络&#xff09;加速国内网站&#xff0c;通常是已备案的。Zero Trust类似FRP&#xff0c;可以将请求转发到目标服务器。在使用Zero Trust绕过备案访问国内网站需要&…

火语言UI组件--播放器

【组件功能】&#xff1a;引用网络播放地址的视频播放器。 样式预览 设置 基础设置 属性名称属性释义输入值类型网络资源地址(url)播放视频的网络地址字符串类型音量(volume)播放视频的音量&#xff08;参考值&#xff1a;0 ~ 1)浮点型(Float)自动播放(autoplay)视频是否自动…