文章目录
- 效果
- QHalconWind 类
- 回调函数刷新窗口
- 构造函数
- 保证窗口大小和Halcon 窗口大小一致
- 绘制图片
- 获取坐标点
- 设置坐标点
- 鼠标拖动图片
- 鼠标按下
- 鼠标抬起
- 鼠标双击
- 滚轮放大缩小图片
效果
QHalconWind 类
// HALCON/Qt pattern matching and measure example
//
// (c) 2004-2017 MVTec Software GmbH
// QHalconWindow.h : Class used for opening HALCON windows in Qt
//
#include <QWidget>
#include <QPainter>
#include <QScopedPointer>
#ifndef __APPLE__
# include "HalconCpp.h"
#else
# ifndef HC_LARGE_IMAGES
# include <HALCONCpp/HalconCpp.h>
# else
# include <HALCONCppxl/HalconCpp.h>
# endif
#endif
class QHalconWindow : public QWidget
{
Q_OBJECT
public:
// 构造函数,初始化一个新的 HALCON 窗口。接受一个可选的父窗口部件、宽度和高度参数。
// parent 参数允许将这个窗口作为另一个窗口的小部件嵌入。
// Width 和 Height 参数用于设置窗口的初始大小。
QHalconWindow(QWidget *parent = nullptr, long Width = 0, long Height = 0);
// 提供对 HALCON 图形缓冲区的访问。返回指向 HALCON HWindow 对象的指针,
// 允许外部代码与 HALCON 图像处理环境交互。
HalconCpp::HWindow* GetHalconBuffer(void) { return halconBuffer.data(); }
protected:
// 当窗口大小改变时被调用。这里可以处理窗口大小变化后的逻辑,
// 例如调整 HALCON 缓冲区的大小。
void resizeEvent(QResizeEvent* event) override;
// 当需要更新窗口内容时被调用。在这里实现实际的绘制逻辑,
// 比如从 HALCON 缓冲区获取图像并绘制到窗口上。
void paintEvent(QPaintEvent* event) override;
// 鼠标移动事件处理函数,响应用户的鼠标移动操作。
void mouseMoveEvent(QMouseEvent* event) override;
// 鼠标按下事件处理函数,响应用户按下鼠标按键的操作。
void mousePressEvent(QMouseEvent* event) override;
// 鼠标释放事件处理函数,响应用户释放鼠标按键的操作。
void mouseReleaseEvent(QMouseEvent* event) override;
// 鼠标双击事件处理函数,响应用户的鼠标双击操作。
void mouseDoubleClickEvent(QMouseEvent* event) override;
// 鼠标滚轮事件处理函数,响应用户的鼠标滚轮滚动操作。
void wheelEvent(QWheelEvent* event) override;
private:
// 获取 HALCON 窗口中的某个区域坐标
void GetPartFloat(double* row1, double* col1, double* row2, double* col2);
// 设置 HALCON 窗口中的某个区域坐标
void SetPartFloat(double row1, double col1, double row2, double col2);
// 智能指针,管理 HALCON 的 HWindow 对象,确保其生命周期与 QHalconWindow 实例一致。
// halconBuffer 是 HALCON 图像处理的图形缓冲区。
QScopedPointer<HalconCpp::HWindow> halconBuffer;
// 成员变量,用于保存最近一次鼠标位置和 HALCON 窗口的某些区域坐标。
// 可能在处理鼠标事件或更新显示时使用这些值。
QPoint lastMousePos;
double lastRow1, lastCol1, lastRow2, lastCol2;
};
回调函数刷新窗口
Herror __stdcall ContentUpdateCallback(void *context)
{
// 定义一个名为 ContentUpdateCallback 的回调函数,
// 使用 Windows API 标准调用约定 (__stdcall)。
// 函数接收一个指向用户定义数据的指针 (void *context),该数据将被用于回调操作。
// 注释解释了这个回调会在何时被调用:
// 当缓冲区刷新时会调用此回调;如果启用了自动刷新(默认情况下是启用的),则每次图形缓冲区更改后都会调用。
QHalconWindow* hwindow = (QHalconWindow*)context;
// 将传入的 context 参数强制转换为 QHalconWindow 类型的指针,
// 并赋值给局部变量 hwindow。这假设传递给回调的上下文是指向 QHalconWindow 实例的指针。
// schedule redraw in Qt thread
hwindow->update();
// 调用 hwindow 指向的对象的 update() 方法来安排重绘。
// 这个方法不会立即导致窗口重绘,而是告诉 Qt 应用程序在下一次事件循环迭代时重绘窗口。
return H_MSG_OK;
// 返回 H_MSG_OK 值,表示回调成功完成。
// H_MSG_OK 可能是一个预定义的宏或常量,用来表示消息处理状态。
}
构造函数
QHalconWindow::QHalconWindow(QWidget *parent, long Width, long Height)
: QWidget(parent), lastMousePos(-1, -1)
{
// 显示窗口。这确保窗口在创建后立即可见。
show();
// 设置窗口的大小为指定的宽度和高度。如果 Width 和 Height 为 0,则使用默认大小。
resize(Width, Height);
//打开图形窗口
halconBuffer.reset(new HalconCpp::HWindow(0, 0, 100, 100, 0, "buffer", ""));
// 启用图形堆栈功能,使得图像和区域在缩放或调整窗口大小后仍然保持不变。
halconBuffer->SetWindowParam("graphics_stack", "true");
// 关闭自动刷新(flush),启用显式刷新模式。
halconBuffer->SetWindowParam("flush", "false");
// 注册一个回调函数 ContentUpdateCallback,用于响应缓冲区内容更新事件。
// 第二个参数 this 表示当前对象实例作为回调函数的上下文,允许回调函数访问 QHalconWindow 的成员。
//刷新窗口
halconBuffer->SetContentUpdateCallback((void*)&ContentUpdateCallback, this);
}
保证窗口大小和Halcon 窗口大小一致
// 当 QHalconWindow 小部件的大小发生变化时调整 HALCON 窗口的大小。
void QHalconWindow::resizeEvent(QResizeEvent* event)
{
Q_UNUSED(event);
//设置窗口的坐标和宽高
halconBuffer->SetWindowExtents(0, 0, width(), height());
// 触发缓冲区刷新,以确保 HALCON 窗口内容根据新的尺寸重新绘制。
halconBuffer->FlushBuffer();
}
绘制图片
void QHalconWindow::paintEvent(QPaintEvent *event)
{
// 使用命名空间以避免每次调用 HALCON 函数时都需要前缀。
using namespace HalconCpp;
Q_UNUSED(event);
HString type;
Hlong width, height;
// 获取 HALCON 缓冲区的内容。
// 将图片加载进窗口
HImage image = halconBuffer->DumpWindowImage();
// 将 HALCON 图像转换为适合 Qt 使用的格式。
// InterleaveChannels() 方法将图像通道重新排列为 ARGB 格式,并确保与 Qt 的颜色匹配。
HImage imageInterleaved = image.InterleaveChannels("argb", "match", 0);
// 获取转换后图像的原始数据指针。
// GetImagePointer1() 返回指向图像数据的指针,并提供有关图像类型和尺寸的信息。
unsigned char* pointer = (unsigned char*)imageInterleaved.GetImagePointer1(&type, &width, &height);
// 使用原始图像数据创建一个 QImage 对象。
// 注意:宽度除以 4 是因为每个像素包含 4 个字节(ARGB),而 QImage::Format_RGB32 每个像素占用 4 字节。
QImage qimage(pointer, width / 4, height, QImage::Format_RGB32);
// 创建 QPainter 对象并开始绘制。
// 将 QImage 绘制到小部件上,从左上角 (0, 0) 开始。
QPainter painter(this);
painter.drawImage(QPoint(0, 0), qimage);
}
获取坐标点
void QHalconWindow::GetPartFloat(double *row1, double *col1, double *row2, double *col2)
{
// 为了从 get_part 获取浮点值,使用 HTuple 参数。
// HTuple 是 HALCON 中用于处理多个数值或不同类型数据的类,
// 它可以存储和操作任意数量的元素,并且支持多种数据类型。
HalconCpp::HTuple trow1, tcol1, trow2, tcol2;
// 调用 halconBuffer 的 GetPart 方法来获取当前窗口显示区域的边界坐标。
// 这些坐标将被存储在 HTuple 对象中。
halconBuffer->GetPart(&trow1, &tcol1, &trow2, &tcol2);
// 将 HTuple 对象中的第一个元素(即浮点数)转换为 double 类型,并赋值给输出参数。
// .D() 方法用于将 HTuple 中的第一个元素转换为双精度浮点数。
*row1 = trow1.D();
*col1 = tcol1.D();
*row2 = trow2.D();
*col2 = tcol2.D();
}
设置坐标点
void QHalconWindow::SetPartFloat(double row1, double col1, double row2, double col2)
{
// 将 double 类型的值转换为 HTuple。如果不进行转换,将会调用 SetPart 的整数版本。
// 使用 HTuple 确保可以传递浮点数值,从而实现平滑的移动和缩放,即使在放大时也能保持精度。
// 调用 halconBuffer 的 SetPart 方法来设置 HALCON 窗口显示区域的边界坐标。
// 通过将 double 参数包装在 HTuple 中,确保 HALCON 接收到的是浮点数而不是整数。
// 这样可以保证窗口边界坐标的精确性,支持更加精细和平滑的操作。
halconBuffer->SetPart(
HalconCpp::HTuple(row1), // 左上角行坐标 (row1)
HalconCpp::HTuple(col1), // 左上角列坐标 (col1)
HalconCpp::HTuple(row2), // 右下角行坐标 (row2)
HalconCpp::HTuple(col2) // 右下角列坐标 (col2)
);
}
鼠标拖动图片
void QHalconWindow::mouseMoveEvent(QMouseEvent *event)
{
// 检查是否按下了鼠标左键,并且 lastMousePos 已经被初始化(即 x() 不等于 -1)。
if ((event->buttons() == Qt::LeftButton) && lastMousePos.x() != -1)
{
// 计算鼠标移动的距离(delta),从上一次的位置到当前事件位置。
// 使用全局坐标系以确保即使窗口移动,计算仍然准确。
QPoint delta = lastMousePos - event->globalPos();
// 根据当前窗口的缩放比例调整 delta 的大小。
// scalex 和 scaley 分别是水平和垂直方向上的缩放因子,
// 它们基于图像的显示区域与窗口尺寸的比例。
double scalex = (lastCol2 - lastCol1 + 1) / (double)width();
double scaley = (lastRow2 - lastRow1 + 1) / (double)height();
try
{
// 设置新的可见区域。根据鼠标移动的距离(delta)调整显示区域的边界坐标。
// 这里使用了缩放因子来确保平移量与当前缩放级别匹配。
SetPartFloat(lastRow1 + (delta.y() * scaley),
lastCol1 + (delta.x() * scalex),
lastRow2 + (delta.y() * scaley),
lastCol2 + (delta.x() * scalex));
// 触发缓冲区刷新,以更新 HALCON 窗口的内容。
// 这将导致重新绘制,显示新的可见区域。
halconBuffer->FlushBuffer();
}
catch (HalconCpp::HOperatorException &e)
{
// 如果设置新可见区域失败(例如,图像部分移出窗口),捕获异常并忽略错误。
// 这种情况可能发生在用户尝试将图像拖动到窗口外时。
// 可选:可以在这里添加日志记录或用户通知。
// qDebug() << "Failed to set new visible part: " << e.what();
}
// 更新 lastMousePos 为当前事件的位置,以便下次计算 delta。
lastMousePos = event->globalPos();
}
}
鼠标按下
void QHalconWindow::mousePressEvent(QMouseEvent *event)
{
// 保存当前鼠标位置和图像显示区域。
// 这些信息将在后续的鼠标移动事件中用于计算平移量。
// 获取当前 HALCON 窗口显示区域的边界坐标,并保存到成员变量中。
// lastRow1, lastCol1 是显示区域的左上角坐标,
// lastRow2, lastCol2 是显示区域的右下角坐标。
GetPartFloat(&lastRow1, &lastCol1, &lastRow2, &lastCol2);
// 记录当前鼠标按下时的全局位置(屏幕坐标)。
// 这个位置将用于后续的鼠标移动事件中计算鼠标移动的距离。
lastMousePos = event->globalPos();
}
鼠标抬起
void QHalconWindow::mouseReleaseEvent(QMouseEvent *event)
{
// 使用 Q_UNUSED 宏来避免编译器对未使用的参数发出警告。
// 在这个方法中,event 参数实际上没有被使用。
Q_UNUSED(event);
// 取消设置参考鼠标位置。
// 将 lastMousePos 设置为一个无效值 (-1, -1),表示不再有有效的参考点。
// 这样可以确保在下次鼠标按下事件之前,不会有旧的鼠标位置影响新的拖动操作。
lastMousePos = QPoint(-1, -1);
}
鼠标双击
void QHalconWindow::mouseDoubleClickEvent(QMouseEvent *event)
{
// 使用 Q_UNUSED 宏来避免编译器对未使用的参数发出警告。
// 在这个方法中,event 参数实际上没有被使用。
Q_UNUSED(event);
// 检查是否是左键双击事件。
if (event->buttons() == Qt::LeftButton)
{
// 重置图像显示区域为默认值。
// SetPart 的参数 (0, 0, -1, -1) 表示将显示区域设置为整个图像,
// 即取消任何缩放或平移效果,恢复到原始视图。
halconBuffer->SetPart(0, 0, -1, -1);
// 刷新 HALCON 缓冲区以应用更改并更新显示。
// 这会触发窗口内容的重新绘制,显示重置后的图像。
halconBuffer->FlushBuffer();
}
}
滚轮放大缩小图片
void QHalconWindow::wheelEvent(QWheelEvent *event)
{
// event->delta() 是 120 的倍数。较大的倍数表示用户滚动了多个刻度。
int num_notch = std::abs(event->delta()) / 120;
// 计算缩放因子。每次滚轮向上滚动时(正 delta),图像放大;向下滚动时(负 delta),图像缩小。
double factor = (event->delta() > 0) ? std::sqrt(2.0) : 1.0 / std::sqrt(2.0);
// 如果用户滚动了多个刻度,调整缩放因子以匹配滚动次数。
while (num_notch > 1)
{
factor = factor * ((event->delta() > 0) ? std::sqrt(2.0) : 1.0 / std::sqrt(2.0));
num_notch--;
}
// 获取缩放中心点。将鼠标指针位置从窗口坐标转换为图像坐标。
double centerRow, centerCol;
halconBuffer->ConvertCoordinatesWindowToImage(event->y(), event->x(), ¢erRow, ¢erCol);
// 获取当前图像显示区域的边界坐标。
double row1, col1, row2, col2;
GetPartFloat(&row1, &col1, &row2, &col2);
// 计算相对于中心点的四个边界的距离。
double left = centerRow - row1;
double right = row2 - centerRow;
double top = centerCol - col1;
double bottom = col2 - centerCol;
// 根据缩放因子调整新的显示区域边界。
double newRow1 = centerRow - left * factor;
double newRow2 = centerRow + right * factor;
double newCol1 = centerCol - top * factor;
double newCol2 = centerCol + bottom * factor;
try
{
// 设置新的显示区域。
SetPartFloat(newRow1, newCol1, newRow2, newCol2);
// 刷新 HALCON 缓冲区以应用更改并更新显示。
halconBuffer->FlushBuffer();
}
catch (HalconCpp::HOperatorException &e)
{
// 如果设置新显示区域失败(例如,部分区域过小或过大),捕获异常并忽略错误。
// 这种情况可能发生在用户尝试过度缩放时。
// 可选:可以在这里添加日志记录或用户通知。
// qDebug() << "Failed to set new visible part: " << e.what();
}
}