[Qt学习笔记]QT下获取Halcon图形窗口鼠标事件并执行相应操作

news2024/11/24 22:32:04

目录

  • 1、背景
  • 2、参考信息
  • 3、目标
  • 4、步骤
    • 4.1 Halcon库的配置
    • 4.2 读取图像,并实现图像自适应窗体控件大小
    • 4.3 主要的图形绘制和贴图操作见如下代码,其中重点为全局函数的创建来实现选择Select、拖拽Drag和尺寸Resize事件响应。
  • 5、总结

1、背景

在视觉项目开发过程中碰到了需要使用Halcon进行图像算法开发的需求,估计很多视觉工程师都用到过Halcon软件开发库,但是完成Halcon算法开发后就会遇到一个问题,就是图像的显示、读写、UI交互等问题,由于Halcon具有特殊的图像文件格式HObject和数据格式HTuple,所以说需要格式转换后才能实现相对应的操作,不过Halcon本身也有比较实用的显示、界面交互的功能,所以如何在C++或QT下使用这些功能成为了接下来需要去研究和实践的工作。

2、参考信息

Halcon针对不同的开发环境,给出了不同的开发例程,针对图形显示及界面操作这一块,Halcon只给出了C#的相关例程,其运行结果如下:
DrawingObject.png

Draw.png
其中可以实现在窗口界面创建矩形、圆、椭圆等形状的Region,并根据鼠标来选择、拖动和设置尺寸,并实现设置颜色,获取坐标,region区内二值化、轮廓化等一系列后续操作。

3、目标

实现在QT环境下,将Halcon窗口贴在QT的控件上,并实现上述创建和操作region的基本动作。

4、步骤

4.1 Halcon库的配置

本人使用的是Halcon12.0的破解版,目前调用Halcon的函数不会出错,但是项目中有调用新版本的Halcon库有出错状况,目前未查证是不是版本的问题,Halcon配置主要在PRO文件中添加Include和Lib的引用路径。其中HALCONROOT是环境变量中Halcon的安装路径。

  #includes
  INCLUDEPATH   += "$$(HALCONROOT)/include"
  INCLUDEPATH   += "$$(HALCONROOT)/include/halconcpp"

  #libs
  QMAKE_LIBDIR  += "$$(HALCONROOT)/lib/$$(HALCONARCH)"
  unix:LIBS     += -lhalconcpp -lhalcon -lXext -lX11 -ldl -lpthread
  win32:LIBS    += "$$(HALCONROOT)/lib/$$(HALCONARCH)/halconcpp.lib" \
                   "$$(HALCONROOT)/lib/$$(HALCONARCH)/halcon.lib"

4.2 读取图像,并实现图像自适应窗体控件大小

这里我首先创建了一个QHalconWindow类,然后在qt的ui界面将widget提升为QHalconWindow类,这样就免去了Halcon窗口句柄和ui句柄的绑定,直接通过QHalconWindow类来调用就行。

qhalconwindow.h文件

#include <QObject>
#include <QWidget>
#include "HalconCpp.h"

class QHalconWindow : public QWidget
{
    Q_OBJECT
public:
    explicit QHalconWindow(QWidget *parent = 0,long Width=0,long Height=0);
    virtual ~QHalconWindow(void);

    HalconCpp::HTuple WindowID(void) {return WinID;}   //f返回窗口句柄

protected:
    void resizeEvent(QResizeEvent*);                 //窗口大小尺寸调整事件
private:
    HalconCpp::HTuple WinID;
    void OpenWindow(void);
}

Cpp文件主要是关于窗口基本操作的实现函数

#include "qhalconwindow.h"

using namespace HalconCpp;

QHalconWindow::QHalconWindow(QWidget *parent,long Width,long Height)
    : QWidget(parent)
{
    resize(Width,Height);
    show();
    OpenWindow();

}

QHalconWindow::~QHalconWindow(void)
{
    CloseWindow(WindowID());
}

void QHalconWindow::OpenWindow(void)
{
    SetWindowAttr("border_width",0);
    SetCheck("~father");
    HalconCpp::OpenWindow(0,0,100,100,(Hlong)winId(),"visible","",&WinID);
    SetCheck("father");
}

//修改窗口尺寸
void QHalconWindow::resizeEvent(QResizeEvent *)
{
    SetWindowExtents(WindowID(),0,0,width(),height());
}

参考Halcon中关于SetDrawingObjectCallback函数的描述,需要在c++下面调用时,调用C++格式的函数,即下图的Void的回调函数指针。
回调函数.PNG
但是这个回调函数在程序中需要定义为一个全局函数,主要依据是Halcon中介绍,如下:
回调函数要求.PNG
所以根据这些需求完成Halcon窗口中绘制矩形、圆形和直线的操作

4.3 主要的图形绘制和贴图操作见如下代码,其中重点为全局函数的创建来实现选择Select、拖拽Drag和尺寸Resize事件响应。

Widget.h文件

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QList>
#include <QStack>
#include <functional>
#include "HalconCpp.h"
//#include "qhalconwindow.h"

using namespace HalconCpp;

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();
    void InitWin(void);
    static Widget* getInstance();

protected:
    void resizeEvent (QResizeEvent*);
    void InitFg(void);

private slots:
    void on_HalconWinD_customContextMenuRequested(const QPoint &pos);
    void onTaskBoxContextMenuEvent();
    void  onTaskDeleteObj();
    void on_btn_DrawRectangle_clicked();
    void on_btn_DrawCircle_clicked();
    void on_btn_DrawLine_clicked();
    void on_btn_ClearAllObj_clicked();
    void AttachDrawObj(HDrawingObject obj);
    void slot_ReceiveData(long);

signals:
    void signal_data(long);
private:
    Ui::Widget *ui;
    //Halcon窗口的参数
    HTuple WindowIDBuf,FGHandle,Width,Height,Area;
    HTuple WindowWidth,WindowHeight;
    HObject Image;
    QStack<HObject> graphic_stack;
    QList<HDrawingObject> drawing_objects;
    HTuple Draw_Text;
    QList<HTuple>Drawing_Index;
};

#endif // WIDGET_H

主要实现代码

#pragma execution_character_set("utf-8")
#include "widget.h"
#include "ui_widget.h"
#include <QMessageBox>
#include <QDebug>
#include <QMenu>

void CallBackFunc_Set(long DrawID,long WindowHandle, char* type);
void CallBackFunc_DrawObj(long DrawID,long WindowHandle, char* type);
HTuple selected_drawing_object;
Widget* instance;

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    WindowIDBuf = -1;
    Draw_Text=HTuple();
    InitWin();
    instance = this;
    connect(this,SIGNAL(signal_data(long)),this,SLOT(slot_ReceiveData(long)));
}

Widget::~Widget()
{
    HalconCpp::CloseWindow(WindowIDBuf);
    delete ui;
}

//建立自身调用
Widget *Widget::getInstance()
{
    return instance;
}

void Widget::InitFg(void)
{
    Hlong disp_width, disp_height;
    //读取一张图像并获取图像大小
    ReadImage(&Image,"D:/Test_Image/kobe.jpg");
    GetImageSize(Image,&Width,&Height);
    
    //根据图像的大小修改界面的尺寸大小
    //    disp_width = ui->HalconWinD->width();
    //    disp_height = ui->HalconWinD->height();
    //    ui->HalconWinD->resize(Width[0].L(),Height[0].L());
    //    resize(width()+Width[0].L()-disp_width,height()+Height[0].L()-disp_height);
}

void Widget::InitWin(void)
{
    InitFg();
    //    HTuple hv_WindowHandleCurrent;
    Hlong WinIDcurrent = (Hlong)ui->HalconWinD->winId();
    WindowWidth = ui->HalconWinD->width();
    WindowHeight = ui->HalconWinD->height();
    OpenWindow(0,0,WindowWidth,WindowHeight,WinIDcurrent,"","",&WindowIDBuf);    
    AttachBackgroundToWindow(Image,WindowIDBuf);
    //    DispObj(Image,ui->HalconWinD->WindowID());
}

void Widget::resizeEvent(QResizeEvent *)
{
    if(WindowIDBuf>0 )
    {
        WindowWidth = ui->HalconWinD->width();
        WindowHeight = ui->HalconWinD->height();
        SetWindowExtents(WindowIDBuf,0,0,WindowWidth,WindowHeight);
        //       DispObj(Image,WindowIDBuf);
    }
}

//右键选取后的Menu的对应操作函数
void Widget::onTaskBoxContextMenuEvent()
{
    QAction *pEven = qobject_cast<QAction *>(this->sender()); //this->sender()就是发信号者 QAction
    
    int iType = pEven->data().toInt();
    HTuple position;
    GetDrawingObjectParams(selected_drawing_object,(HTuple("row1").Append("column1")),&position);    
    switch (iType)
    {
    case 1:
    {
        SetDrawingObjectParams(selected_drawing_object,"color","green");
        QMessageBox::about(this, "tip", pEven->text());
        break;
    }
    case 2:
    {
        SetDrawingObjectParams(selected_drawing_object,"color","blue");
        QMessageBox::about(this, "tip", pEven->text());
        break;
    }
    case 3:
    {
        SetDrawingObjectParams(selected_drawing_object,"color","yellow");
        QMessageBox::about(this, "tip", pEven->text());
        break;
    }
    case 4:
    {
        SetDrawingObjectParams(selected_drawing_object,"color","black");
        QMessageBox::about(this, "tip", pEven->text());
        break;
    }
    default:
        break;
    }
    int Select_DrawID;
    for(int i=0;i!=Drawing_Index.size();++i)
    {
        if(Drawing_Index.at(i) == selected_drawing_object)
        {
            Select_DrawID=i;
            qDebug()<<"select ID:"<<i<<endl;
        }
    }
    for(int i=0;i!=Drawing_Index.size();++i)
    {
        if(Drawing_Index.at(i) == selected_drawing_object)
        {
            Select_DrawID=i;
        }
    }
    QString Message_test = pEven->text();
    QByteArray ba = Message_test.toLocal8Bit();
    const char *str = ba.data();
    HTuple Draw_Message(str);
    HTuple Draw_MesObj;    
    CreateDrawingObjectText(position[0],position[1], Draw_Message,&Draw_MesObj);
    AttachDrawingObjectToWindow(WindowIDBuf,Draw_MesObj);
    Draw_Text[Select_DrawID]=Draw_MesObj;    
}

//右键选取删除操作对应函数
void Widget::onTaskDeleteObj()
{
    int Select_DrawID;
    for(int i=0;i!=Drawing_Index.size();++i)
    {
        if(Drawing_Index.at(i) == selected_drawing_object)
        {
            Select_DrawID=i;
            qDebug()<<"select ID:"<<i<<endl;
        }
    }
    if(Draw_Text.Length() >Select_DrawID)
    {
        DetachDrawingObjectFromWindow(WindowIDBuf,Draw_Text[Select_DrawID]);
        DetachDrawingObjectFromWindow(WindowIDBuf,selected_drawing_object);
    }
    else
    {
        DetachDrawingObjectFromWindow(WindowIDBuf,selected_drawing_object);
    }
}

//右键响应事件
void Widget::on_HalconWinD_customContextMenuRequested(const QPoint &pos)
{    
    HTuple Row_Mouse,Column_Mouse,Button,position;
    GetMposition(WindowIDBuf,&Row_Mouse,&Column_Mouse,&Button);
    GetDrawingObjectParams(selected_drawing_object,(HTuple("column1").Append("column2").Append("row1").Append("row2")),&position);
    qDebug()<<Column_Mouse.D()<<Row_Mouse.D()<<position[0].D()<<position[1].D()<<position[2].D()<<position[3].D()<<endl;
    
    if(Column_Mouse>position[0] && Column_Mouse<position[1])
    {
        if(Row_Mouse>position[2] && Row_Mouse<position[3])
        {           
            //创建菜单对象
            QMenu *pMenu = new QMenu(this);
            
            QAction *pTask1 = new QAction(tr("得分王"), this);
            QAction *pTask2 = new QAction(tr("总冠军"), this);
            QAction *pTask3 = new QAction(tr("MVP"), this);
            QAction *pTask4 = new QAction(tr("单场81分"), this);
            QAction *action=new QAction(this);
            QAction *pDelete = new QAction(tr("追随黑曼巴!"), this);
            
            pTask1->setData(1);
            pTask2->setData(2);
            pTask3 ->setData(3);
            pTask4->setData(4);
            action->setSeparator(true);
            pDelete ->setData(5);
            
            //把QAction对象添加到菜单上
            pMenu->addAction(pTask1);
            pMenu->addAction(pTask2);
            pMenu->addAction(pTask3);
            pMenu->addAction(pTask4);
            pMenu->addAction(action);
            pMenu->addAction(pDelete);
                       
            //连接鼠标右键点击信号
            connect(pTask1, SIGNAL(triggered()), this, SLOT(onTaskBoxContextMenuEvent()));
            connect(pTask2, SIGNAL(triggered()), this, SLOT(onTaskBoxContextMenuEvent()));
            connect(pTask3, SIGNAL(triggered()),this, SLOT(onTaskBoxContextMenuEvent()));
            connect(pTask4, SIGNAL(triggered()), this, SLOT(onTaskBoxContextMenuEvent()));
            connect(pDelete, SIGNAL(triggered()),this, SLOT(onTaskDeleteObj()));
            
            //在鼠标右键点击的地方显示菜单
            pMenu->exec(cursor().pos());
            qDebug()<<cursor().pos().x()<<cursor().pos().y()<<endl;
            
            //释放内存
            QList<QAction*> list = pMenu->actions();
            foreach (QAction* pAction, list) delete pAction;
            delete pMenu;
        }
    }
}
//画矩形框
void Widget::on_btn_DrawRectangle_clicked()
{   
    HTuple Rect_ID;
    CreateDrawingObjectRectangle1(100,100,200,200,&Rect_ID);
    SetDrawingObjectParams(Rect_ID,"color","red");
    qDebug()<<"Rect_ID"<<Rect_ID.D()<<endl;
    Drawing_Index.append(Rect_ID);
    //转换句柄为HDrawingObject
    HDrawingObject draw=HDrawingObject(Rect_ID);
    AttachDrawingObjectToWindow(WindowIDBuf,Rect_ID);
    AttachDrawObj(draw);
}


void Widget::AttachDrawObj(HDrawingObject obj)
{
    drawing_objects.append(obj);
    obj.SetDrawingObjectCallback("on_resize",(void*)CallBackFunc_DrawObj);
    obj.SetDrawingObjectCallback("on_drag",(void*)CallBackFunc_DrawObj);
    //    obj.SetDrawingObjectCallback("on_attach",CallBackFunc_Set);
    obj.SetDrawingObjectCallback("on_select",(void*)CallBackFunc_Set);
    //    AttachDrawingObjectToWindow(ui->HalconWinD->WindowID(),obj);
}

//Drag和Resize对应的回调函数,这里用UI的一个自身指针将全局函数的变量传递给UI,从而调用UI下的函数
void CallBackFunc_DrawObj(long DrawID,long WindowHandle, char* type)
{
    Widget::getInstance()->signal_data(DrawID);
}

//Drag和Resize对应的UI中的处理函数
void Widget::slot_ReceiveData(long DrawID)
{
    int Select_DrawID;
    for(int i=0;i!=Drawing_Index.size();++i)
    {
        if(Drawing_Index.at(i) == (HTuple)DrawID)
        {
            Select_DrawID=i;
            qDebug()<<"delete ID:"<<i<<endl;
        }
    }
    if(Draw_Text.Length() >Select_DrawID)
    {
        DetachDrawingObjectFromWindow(WindowIDBuf,Draw_Text[Select_DrawID]);
    }
}

//选取矩形框对应的回调函数
void CallBackFunc_Set(long DrawID,long WindowHandle, char* type)
{  
    selected_drawing_object=DrawID;
    SetDrawingObjectParams(DrawID,"color","blue");
    HObject Region;
    HTuple Area,row,column;
    GetDrawingObjectIconic(&Region,DrawID);
    AreaCenter(Region,&Area,&row,&column);
}

//清除窗口所有图形
void Widget::on_btn_ClearAllObj_clicked()
{
    for(int i=0;i!=Drawing_Index.size();++i)
    {
        ClearDrawingObject(Drawing_Index.at(i));       
    }
    for(int j=0;j<Draw_Text.Length();++j)
    {
        ClearDrawingObject(Draw_Text[j]);
    }    
    Drawing_Index.clear();
    Draw_Text=HTuple();
}

//画圆形
void Widget::on_btn_DrawCircle_clicked()
{
    HTuple Circle_ID;
    CreateDrawingObjectCircle(300,300,200,&Circle_ID);
    SetDrawingObjectParams(Circle_ID,"color","red");
    AttachDrawingObjectToWindow(WindowIDBuf,Circle_ID);
}

//画直线
void Widget::on_btn_DrawLine_clicked()
{
    HTuple Line_ID;
    CreateDrawingObjectLine(300,300,600,600,&Line_ID);
    SetDrawingObjectParams(Line_ID,"color","yellow");
    AttachDrawingObjectToWindow(WindowIDBuf,Line_ID);
}

5、总结

这个知识点本身并不难,而且Halcon也带有c#的例程,主要当初碰到的难点是无法理解其回调函数的Draw_ID是如何传递的,最后查到Halcon的帮助资料才发现,按照全局回调函数的样子去定义,回调会自动返回你当前所选择的Draw_ID,从而可以使用该Draw_ID进行你所需要的操作。
最后放上最终的效果:
缅怀.png

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

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

相关文章

春暖花开,一起来看看2024年品牌春分海报吧!

春分&#xff08;Vernal equinox&#xff09;已至&#xff0c;春花烂漫、燕子归来、百草回芽。 今天我们要分享的是2024年品牌发布的春分节气海报合集&#xff0c;快来随我们一起感受这昂扬、蓬勃的春意吧! &#xff08;1&#xff09;泸州老窖 &#xff08;2&#xff09;BD…

C语言——自定义类型——结构体(从零到一的跨越)

目录 前言 1.什么是结构体 2.结构体类型的声明 2.1结构体的声明 2.2结构体的创建和初始化 2.3结构成员访问操作符 2.3.1结构体成员直接访问 2.3.2结构体成员的间接访问 2.4结构体变量的重命名 2.5结构体的特殊声明 2.6结构的自引用 3.结构体内存对齐 3.1对齐规则 3…

docker harbor.v2.9.2搭建镜像无法下载问题解决

在通过部署docker harbor时&#xff0c;采用的是离线包的方式&#xff0c;当解压压缩包后&#xff0c;执行prepare脚本步骤中有一步是要获取prepare:v2.9.2版本镜像 结果执行脚本时报如下错误&#xff1a; Unable to find image goharbor/prepare:v2.9.2 locally 这时候我们就…

网络: DHCP 协议简介

文章目录 1. 前言2. DHCP 协议简介2.1 DHCP 客户端广播 DHCPDISCOVER 消息2.2 DHCP 服务器回复 DHCPOFFER 消息2.3 DHCP 客户端广播 DHCPREQUEST 消息2.4 DHCP 服务器回复 DHCPACK 消息2.5 剩余的工作 3. 参考资料 1. 前言 限于作者能力水平&#xff0c;本文可能存在谬误&…

【Linux】shell命令运行原理---认识Linux基本指令

主页&#xff1a;醋溜马桶圈-CSDN博客 专栏&#xff1a;Linux_醋溜马桶圈的博客-CSDN博客 gitee&#xff1a;mnxcc (mnxcc) - Gitee.com 目录 1.shell命令以及运行原理 1.1 shell命令 1.2 Linux内核权限 1.3 图示Linux shell和bash的区别 2.认识Linux基本指令 2.1 指令的…

Vue.js前端开发零基础教学(一)

目录 第一章 初识Vue.js 前言 开发的好处 一.前端技术的发展 什么是单页Web应用&#xff1f; 二. Vue的简介 三. Vue的特性 四. Vue的版本 五.常见的包管理 六.安装node环境 第一章 初识Vue.js 学习目标&#xff1a; 了解前端技术的发展 了解什么是Vue掌握使用方…

Zero-Shot Learning with Joint Generative Adversarial Networks 中文版

目录 摘要介绍1.研究背景和意义2.先前的模型提出了什么方法&#xff1f;解决了什么问题&#xff1f;有什么不足&#xff1f;3.最近的研究提出了什么方法&#xff1f;解决了什么问题&#xff1f;4.最新的研究提出了什么方法&#xff1f;解决了什么问题&#xff1f;有什么不足&am…

关于自己Nginx的使用(ant design pro 部署)

一 原因 工作需要部署 ant design pro 框架开发的前端程序&#xff0c;并且需要有用到代理。就选择了nginx部署。 二 使用nginx部署 ant design pro 框架程序 1. 前端项目打包 &#xff08;1&#xff09;打包命令&#xff1a;npm run build 或者 yarn bulid &#…

SAP STMS请求重复传输

STMS 在接请求的导入的时候&#xff0c;第一次发生了错误&#xff0c;在修复了错误之后&#xff0c; 该请求二次导入显示已经该请求已全部导入 可以按如下操作进行再次导入 附加--》其他请求--》添加 输入请求号并勾选再次导入 然后点选需要重复导入的请求号即可再次导入

Redisinsight默认端口改成5540了!网上的8001都是错误的

Redisinsight 打开白屏解决方法 最近发现一个很讨厌的bug&#xff0c;就是redisinsight运行之后&#xff0c;不行了&#xff0c;在网上找到的所有资料里面&#xff0c;redis insight都是运行在8001端口&#xff0c;但是我现在发现&#xff0c;变成了5540 所以对应的docker-com…

华为ensp中rip动态路由协议原理及配置命令(详解)

CSDN 成就一亿技术人&#xff01; 作者主页&#xff1a;点击&#xff01; ENSP专栏&#xff1a;点击&#xff01; CSDN 成就一亿技术人&#xff01; ————前言————— RIP&#xff08;Routing Information Protocol&#xff0c;路由信息协议&#xff09;是一种距离矢…

什么样才叫计算机?

我和小宇早恋了&#xff0c;我们家住隔壁。 一、编码与电路——信号的转换 晚上父母会把手机没收&#xff0c;但我们还想继续聊天&#xff0c;又不敢发出声音&#xff0c;于是我们想到了这个办法... 我们把所有的中文都用灯泡的亮灭组合来表示&#xff0c;同时约定好每隔一秒读…

IDA反汇编工具详解之工程和窗口

文章目录 什么是反汇编反汇编的目的ID介绍打开创建工程IDA的基本规则窗口介绍反汇编窗口Names窗口Strings窗口十六进制窗口导出窗口导入窗口函数窗口结构体窗口枚举窗口段窗口签名窗口类型库窗口函数调用窗口问题窗口 什么是反汇编 程序员使用编译器、汇编器和链接器中的一个或…

位图与布隆过滤器

目录 一、位图 1、问题用位图来解决&#xff1a; 二、 布隆过滤器 1、将哈希与位图结合&#xff0c;即布隆过滤器 2.布隆过滤器的查找 3.布隆过滤器的删除 4.布隆过滤器优点 5、布隆过滤器缺陷 三、海量数据处理问题&#xff1a; 一、位图 问题1&#xff1a;给40亿个不…

【C++】详解 INT_MAX 和 INT_MIN(INT_MAX 和 INT_MIN是什么?它们的用途是什么?如何防止溢出?)

目录 一、前言 二、什么是 INT_MAX 和 INT_MIN &#xff1f; 三、INT_MAX 和 INT_MIN 的用途 四、如何避免溢出问题出现 &#xff1f; 五、 INT_MAX 和 INT_MIN 的运算 六、leetcode 常考面试题 七、共勉 一、前言 大家在平时刷 leetcode 的时候&#xff0c;肯定会碰到 溢出…

谷歌seo网络营销哪家好?

对于一个好的服务商的评判标准其实不难&#xff0c;保证结果&#xff0c;服务透明化&#xff0c;专业的服务&#xff0c;专业的指导&#xff0c;但怕就怕在你什么都不懂&#xff0c;只看重短期的结果&#xff0c;不懂谷歌seo的基础 一些做谷歌seo的反面例子也是需要了解的&…

上位机图像处理和嵌入式模块部署(qmacvisual三维测量)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 在qmacvisual软件里面&#xff0c;关于三维测量方面的内容讲的比较少。目前来说只有一个插件完成这个功能。这可能也和作者自己当时的开发环境有关…

springcloud-Nacos 更强大的注册中心组件

Nacos 实际上从设计思想来说 Eureka 和 nacos 是一样的。 后者是Alibaba推出的 一款更强大 功能更丰富的注册中心 你可以理解为Eureka的高配版 技多不压身既然了解了 Eureka, nacos也来学习一下吧&#xff01; 安装 首先nacos不像eureka 直接pom里面引个依赖就搞定了&#…

查看angular版本的问题The Angular CLI requires a minimum Node.js version of v18.13.

angular版本与node.js版本不匹配的问题 下载安装angular 查看版本&#xff0c;发现不匹配 安装指定版本即可 查看版本并运行