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

目录

  • 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的安装路径。

  #includesINCLUDEPATH   += "$$(HALCONROOT)/include"INCLUDEPATH   += "$$(HALCONROOT)/include/halconcpp"#libsQMAKE_LIBDIR  += "$$(HALCONROOT)/lib/$$(HALCONARCH)"unix:LIBS     += -lhalconcpp -lhalcon -lXext -lX11 -ldl -lpthreadwin32: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_OBJECTpublic: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()就是发信号者 QActionint 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);//转换句柄为HDrawingObjectHDrawingObject 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.mzph.cn/news/758340.shtml

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

相关文章

3.19作业

1、思维导图 2、模拟面试题 1&#xff09;TCP通信中的三次握手和四次挥手 答&#xff1a;三次握手 客户端向服务器发送连接请求 服务器向客户端回复应答并向客户端发送连接请求 客户端回复服务端&#xff0c;并建立联系 四次挥手 进程a向进程b发送断开连接请求…

3.20作业

1、思维导图 2、 1> 创建一个工人信息库&#xff0c;包含工号&#xff08;主键&#xff09;、姓名、年龄、薪资。 2> 添加三条工人信息&#xff08;可以完整信息&#xff0c;也可以非完整信息&#xff09; 3> 修改某一个工人的薪资&#xff08;确定的一个&am…

踏“时间”与“空间”前来探寻复杂度的奥妙(Java篇)

本篇会加入个人的所谓‘鱼式疯言’ ❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言 而是理解过并总结出来通俗易懂的大白话, 小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的. &#x1f92d;&#x1f92d;&#x1f92d;可能说的不是那么严谨.但小编初心是能让更多人…

校园综合能效平台建设的意义

彭姝麟 Acrelpsl 一 高校用能分析 当前高校用能普遍存在以下点问题&#xff1a; 一是用能需求日益增加&#xff1a;随着高校的快速发展&#xff0c;校园用能人数、用能设备、建筑面积等逐年增加&#xff0c;用能需求也相应攀升。日益增长的能耗需求与节能降耗任务之间的客观矛…

一文读懂什么是序列 (sequence)

sequence 序列 sequence(序列)是一组有顺序的元素的集合 (严格的说&#xff0c;是对象的集合&#xff0c;但鉴于我们还没有引入“对象”概念&#xff0c;暂时说元素) 序列可以包含一个或多个元素&#xff0c;也可以没有任何元素。 我们之前所说的基本数据类型&#xff0c;都…

蓝桥杯练习03个人博客

个人博客 介绍 很多人都有自己的博客&#xff0c;在博客上面用自己的方式去书写文章&#xff0c;用来记录生活&#xff0c;分享技术等。下面是蓝桥云课的博客&#xff0c;但是上面还缺少一些样式&#xff0c;需要大家去完善。 准备 开始答题前&#xff0c;需要先打开本题的…

物业社区人行通道闸如何选择,这6点一定要考虑!

社区是居民的共同家园&#xff0c;一个安全、便捷且和谐的社区环境对于提升居民的生活质量至关重要。人行通道闸不仅仅是一道简单的进出关卡&#xff0c;它是守护社区人员通行安全的坚实屏障&#xff0c;是提升社区管理效率的智能工具&#xff0c;更是增强业主满意度的关键因素…

C# 部署ICE框架以及用例(VS2019)

使用Windows 10环境&#xff0c;VS2019进行ICE用例开发 用例结构&#xff1a;客户端和服务端 关键技术&#xff1a;集成ICE环境&#xff0c;可以创建ice文件并自动生成对应的cs文件 1.环境安装 ICE Build插件安装。安装以后&#xff0c;就可以在项目中插入ice文件 2.代码实…

放大镜效果

放大镜效果 摘要 利用css和js来实现图片放大效果 HTML <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><titl…

Go——切片

1. 特点 slice并不是数组或数组指针。它通过内部指针和相关属性引用数组片段&#xff0c;以实现变长方案。 切片&#xff1a;切片是数组的一个引用&#xff0c;因此切片是引用类型。但自身是结构体&#xff0c;值拷贝传递。切片的长度可以改变&#xff0c;因此&#xff0c;切片…

Elasticsearch实战:索引阻塞 —— 数据保护的终极武器

文章目录 1、索引阻塞的种类2、什么时候使用阻塞&#xff1f;场景1&#xff1a;进行系统维护场景。场景2&#xff1a;保护数据不被随意更改场景。场景3&#xff1a;优化资源使用的场景。场景4&#xff1a;遵守安全规则场景。 3、添加索引阻塞API4、解除设置 API5、小结6、参考 …

Transformer位置编码(Position Embedding)理解

本文主要介绍4种位置编码&#xff0c;分别是NLP发源的transformer、ViT、Sw-Transformer、MAE的Position Embedding 一、NLP transformer 使用的是1d的绝对位置编码&#xff0c;使用sincos将每个token编码为一个向量【硬编码】 Attention Is All You Need 在语言中&#xff0…

RPG Maker MV 踩坑八 仿新仙剑战斗物品指令菜单

仿新仙剑战斗物品指令菜单 遇到的坑坑一坑二解决方法 遇到的坑 上次做的额外战斗指令菜单和物品战斗指令菜单&#xff0c;突然发现一个大问题&#xff0c;漏风了&#xff01;&#xff01;&#xff01; 其实就是将底部漏出来了&#xff0c;这样整个UI就不完整了&#xff0c;算是…

微服务day04(上)-- RabbitMQ学习与入门

1.初识MQ 1.1.同步和异步通讯 微服务间通讯有同步和异步两种方式&#xff1a; 同步通讯&#xff1a;就像打电话&#xff0c;需要实时响应。 异步通讯&#xff1a;就像发邮件&#xff0c;不需要马上回复。 两种方式各有优劣&#xff0c;打电话可以立即得到响应&#xff0c;但…

深度学习 | 神经网络

一、神经网络原理 1、神经元模型 虽然叫个神经元&#xff0c;但骨子里还是线性模型。 2、神经网络结构 顾名思义就是由很多个神经元结点前后相连组成的一个网络。虽然长相上是个网络&#xff0c;但是本质上是多个线性模型的模块化组合。 在早期也被称为 多层感知机 Multi-Layer…

Visual Studio 2013 - 调试模式下根据内存地址查看内存

Visual Studio 2013 - 调试模式下根据内存地址查看内存 1. 查看内存References 1. 查看内存 调试 -> 窗口 -> 内存 -> 内存1-4 References [1] Yongqiang Cheng, https://yongqiang.blog.csdn.net/

【质押空投】公链Zkasino

据深潮TechFlow报道&#xff0c;游戏公链Zkasino HyperChain宣布上线质押挖矿活动&#xff0c;参与者可通过将ETH跨链到Zkasino链的方式来获取ZKAS代币。本次活动总共将分配25%的总代币供应量给参与者。质押挖矿时间将通过倒计时的方式来限制参与人数&#xff0c;以保护早期质押…

Datawhale 零基础入门数据挖掘-Task1 赛题理解

一、 赛题理解 Tip:此部分为零基础入门数据挖掘的 Task1 赛题理解 部分&#xff0c;为大家入门数据挖掘比赛提供一个基本的赛题入门讲解&#xff0c;欢迎后续大家多多交流。 赛题&#xff1a;零基础入门数据挖掘 - 二手车交易价格预测 地址&#xff1a;零基础入门数据挖掘 -…

【代码分享】四十七种测试函数(关注可免费获取)

智能优化算法测试函数简介 智能优化算法测试函数是为了在优化算法研究和开发中测试算法性能的规范问题集合。这些测试函数模拟了真实世界优化问题的不同方面,包括局部最小值、最大值、全局最优解,以及多种复杂性如高维度、非线性、不连续等。优化算法,如遗传算法、粒子群优…

蓝桥杯之动态规划冲刺

文章目录 动态规划01背包小练一下01背包网格图上的DP完全背包 最长公共字符串最长递增子序列 动态规划 动态规划&#xff1a;确定好状态方程&#xff0c;我们常常是确定前 当状态来到 i 时&#xff0c;前 i 个物体的状态是怎么样的&#xff0c;我们并不是从一个点去考虑&#x…