VS+Qt应用开发-Qt+Halcon显示图片,实现鼠标缩放、移动图片
关于显示图像以及前后浏览的功能请参照我的另一篇博客:
VS+Qt应用开发-实现从文件夹打开图片,前后浏览
关于控件提升请参照我的另一篇博客:
VS+Qt应用开发-控件提升-以Label为例
效果展示
- 动态展示
-
显示图像
-
鼠标滚轮缩放图像
-
鼠标按下拖动平移图像
-
实时显示坐标和灰度值
-
恢复图像
界面设计
在文章VS+Qt应用开发-实现从文件夹打开图片,前后浏览
的界面基础上进行以下增改:
- 添加一个Push Button,对象名设为btn_resetPic,用于恢复图片;
- 添加一个Label,对象名设为label_status,用于实时显示坐标和灰度值;
- 将label_show控件提升为CMyLabel类,用于接收鼠标事件。
代码例程
完整代码
首先贴上完整代码:
CMyLabel.h
#pragma once
#include <QLabel>
#include <QWheelEvent>
#include <HalconCpp.h>
using namespace Halcon;
class CMyLabel : public QLabel
{
Q_OBJECT
public:
CMyLabel(QWidget *parent = Q_NULLPTR);
~CMyLabel();
//设置Halcon图像和Halcon窗口句柄,用户响应鼠标事件后实时更新图像
void setHalconWnd(Hobject img, HTuple hHalconID, QLabel *label);
//鼠标滚轮缩放事件
void wheelEvent(QWheelEvent *ev);
//鼠标按下事件
void mousePressEvent(QMouseEvent *ev);
//鼠标释放事件
void mouseReleaseEvent(QMouseEvent *ev);
//鼠标移动事件
void mouseMoveEvent(QMouseEvent *ev);
public:
HTuple m_labelID; //Qt标签句柄
HTuple m_hHalconID; //Halcon窗口句柄
Hobject m_currentImg; //当前的图像
//主界面显示坐标的标签
QLabel *m_label;
//鼠标按下的位置
HTuple m_tMouseDownRow;
HTuple m_tMouseDownCol;
bool m_bIsMove; //是否移动图像标识
};
CMyLabel.cpp
#include "CMyLabel.h"
//定义单步放大倍率
#define ZOOMRATIO 2.0
CMyLabel::CMyLabel(QWidget *parent)
: QLabel(parent)
{
m_bIsMove = false;
}
CMyLabel::~CMyLabel()
{
}
//设置Halcon图像和Halcon窗口句柄,用户响应鼠标事件后实时更新图像
void CMyLabel::setHalconWnd(Hobject img, HTuple hHalconID, QLabel *label)
{
m_hHalconID = hHalconID;
m_currentImg = img;
m_label = label;
}
//鼠标滚轮缩放事件,用于缩放图像
void CMyLabel::wheelEvent(QWheelEvent *ev)
{
double Zoom; //放大或缩小倍率
HTuple mouseRow, mouseCol, Button;
HTuple startRowBf, startColBf, endRowBf, endColBf, Ht, Wt, startRowAft, startColAft, endRowAft, endColAft;
//滚轮前滑,放大
if (ev->delta() > 0)
{
Zoom = ZOOMRATIO;
}
else
{
Zoom = 1 / ZOOMRATIO;
}
//获取光标在原图上的位置,注意是原图坐标,不是Label下的坐标
set_check("~give_error");
Herror ret = get_mposition(m_hHalconID, &mouseRow, &mouseCol, &Button);
set_check("give_error");
//当光标不在Halcon窗口内时返回,否则会报错
if (ret != H_MSG_TRUE)
{
return;
}
//获取原图显示的部分,注意也是原图坐标
get_part(m_hHalconID, &startRowBf, &startColBf, &endRowBf, &endColBf);
//缩放前显示的图像宽高
Ht = endRowBf - startRowBf;
Wt = endColBf - startColBf;
//普通版halcon能处理的图像最大尺寸是32K*32K。如果无限缩小原图像,导致显示的图像超出限制,则会造成程序崩溃
if ((Ht * Wt < 20000 * 20000 || Zoom == ZOOMRATIO))
{
//计算缩放后的图像区域
startRowAft = mouseRow - ((mouseRow - startRowBf) / Zoom);
startColAft = mouseCol - ((mouseCol - startColBf) / Zoom);
endRowAft = startRowAft + (Ht / Zoom);
endColAft = startColAft + (Wt / Zoom);
//如果放大过大,则返回
if (endRowAft - startRowAft < 2)
{
return;
}
if (m_hHalconID != NULL)
{
//如果有图像,则先清空图像
detach_background_from_window(m_hHalconID);
}
set_part(m_hHalconID, startRowAft, startColAft, endRowAft, endColAft);
attach_background_to_window(m_currentImg, m_hHalconID);
}
}
//鼠标按下事件
void CMyLabel::mousePressEvent(QMouseEvent *ev)
{
HTuple mouseRow, mouseCol, Button;
set_check("~give_error");
Herror ret = get_mposition(m_hHalconID, &mouseRow, &mouseCol, &Button);
set_check("give_error");
//当光标不在Halcon窗口内时返回,否则会报错
if (ret != H_MSG_TRUE)
{
return;
}
//鼠标按下时的行列坐标
m_tMouseDownRow = mouseRow;
m_tMouseDownCol = mouseCol;
m_bIsMove = true;
}
//鼠标释放事件
void CMyLabel::mouseReleaseEvent(QMouseEvent *ev)
{
m_bIsMove = false;
}
//鼠标移动事件
void CMyLabel::mouseMoveEvent(QMouseEvent *ev)
{
HTuple startRowBf, startColBf, endRowBf, endColBf, mouseRow, mouseCol, Button;
//获取当前鼠标在原图的位置
set_check("~give_error");
Herror ret= get_mposition(m_hHalconID, &mouseRow, &mouseCol, &Button);
set_check("give_error");
//当光标不在Halcon窗口内时返回,否则会报错
if (ret != H_MSG_TRUE)
{
return;
}
//鼠标按下并移动时,移动图像,否则只显示坐标
if (m_bIsMove)
{
//计算移动值
double RowMove = mouseRow[0].D() - m_tMouseDownRow[0].D();
double ColMove = mouseCol[0].D() - m_tMouseDownCol[0].D();
//得到当前的窗口坐标
get_part(m_hHalconID, &startRowBf, &startColBf, &endRowBf, &endColBf);
//移动图像
if (m_hHalconID != NULL)
{
//如果有图像,则先清空图像
detach_background_from_window(m_hHalconID);
}
set_part(m_hHalconID, startRowBf - RowMove, startColBf - ColMove, endRowBf - RowMove, endColBf - ColMove);
attach_background_to_window(m_currentImg, m_hHalconID);
}
//获取灰度值
HTuple pointGray;
set_check("~give_error");
ret = get_grayval(m_currentImg, mouseRow, mouseCol, &pointGray);
set_check("give_error");
//当光标不在Halcon窗口内时返回,否则会报错
if (ret != H_MSG_TRUE)
{
m_label->setText(QString::fromLocal8Bit("X坐标:- Y坐标:- 灰度值:-"));
return;
}
//设置坐标
m_label->setText(QString::fromLocal8Bit("X坐标:%1 Y坐标:%2 灰度值:%3").arg(mouseCol[0].D()).arg(mouseRow[0].D()).arg(pointGray[0].D()));
}
BrowsePic.h
#pragma once
#include <QtWidgets/QWidget>
#include "ui_BrowsePic.h"
#include <HalconCpp.h>
#include <QToolBar>
using namespace Halcon;
#pragma execution_character_set("utf-8");
class BrowsePic : public QWidget
{
Q_OBJECT
public:
BrowsePic(QWidget *parent = Q_NULLPTR);
~BrowsePic();
//初始化
void init();
//打开窗口
void showImg();
//窗体大小改变事件
void resizeEvent(QResizeEvent *ev);
public:
//显示图像的控件id
HTuple m_hLabelID; //QLabel控件句柄
HTuple m_hHalconID; //Halcon显示窗口句柄
//原始图像的尺寸
HTuple m_imgWidth, m_imgHeight;
//图片路径列表
HTuple m_imgFiles;
//当前图像
Hobject m_hCurrentImg;
//当前图像的下标
HTuple index;
//是否等比缩放
bool m_bIsEqualScale;
//是否居中显示
bool m_bIsShowCenter;
//缩放后的图像
Hobject m_hResizedImg;
//缩放系数
HTuple m_hvScaledRate;
//缩放后图像的大小
HTuple m_scaledHeight, m_scaledWidth;
QToolBar *m_toolBar;
public slots:
//打开图片
void on_btn_openPic_clicked();
//浏览前一张
void on_btn_prePic_clicked();
//浏览后一张
void on_btn_nextPic_clicked();
//恢复图片
void on_btn_resetPic_clicked();
//等比缩放复选框状态改变
void on_checkBox_equalScale_stateChanged(int);
//居中显示复选框状态改变
void on_checkBox_showCenter_stateChanged(int);
private:
Ui::BrowsePicClass ui;
};
BrowsePic.cpp
#include "BrowsePic.h"
#include <QFileDialog>
#include <QFileInfo>
#include <QLabel>
BrowsePic::BrowsePic(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
//初始化
init();
}
BrowsePic::~BrowsePic()
{
}
//初始化
void BrowsePic::init()
{
//设置halcon的文件路径为utf8,解决中文乱码
set_system("filename_encoding", "utf8");
//生成空图像
gen_empty_obj(&m_hCurrentImg);
//获取图像显示控件的句柄
m_hHalconID = NULL;
m_hLabelID = (Hlong)ui.label_show->winId();
m_bIsEqualScale = false;
m_bIsShowCenter = true;
ui.checkBox_equalScale->setChecked(m_bIsEqualScale);
ui.checkBox_showCenter->setChecked(m_bIsShowCenter);
ui.checkBox_showCenter->setEnabled(false);
}
//打开窗口
void BrowsePic::showImg()
{
//判断图像是否为空
Hlong isEqual;
Hobject emptyObj;
gen_empty_obj(&emptyObj);
test_equal_obj(emptyObj, m_hCurrentImg, &isEqual);
if (isEqual)
{
return;
}
if (m_hHalconID != NULL)
{
//如果有图像,则先清空图像
detach_background_from_window(m_hHalconID);
}
else
{
//打开窗口
open_window(0, 0, ui.label_show->width(), ui.label_show->height(), m_hLabelID, "visible", "", &m_hHalconID);
}
ui.label_show->setHalconWnd(m_hCurrentImg, m_hHalconID, ui.label_status);
get_image_size(m_hCurrentImg, &m_imgWidth, &m_imgHeight);
//判断是否等比缩放
if (m_bIsEqualScale)
{
//获取缩放系数
tuple_min2(1.0 * ui.label_show->width() / m_imgWidth, 1.0 * ui.label_show->height() / m_imgHeight, &m_hvScaledRate);
//进行图像缩放
zoom_image_factor(m_hCurrentImg, &m_hResizedImg, m_hvScaledRate, m_hvScaledRate, "constant");
get_image_size(m_hResizedImg, &m_scaledWidth, &m_scaledHeight);
set_window_extents(m_hHalconID, 0, 0, m_scaledWidth, m_scaledHeight);
if (m_bIsShowCenter)
{
if (1.0*ui.label_show->width() / m_imgWidth < 1.0*ui.label_show->height() / m_imgHeight)
{
set_window_extents(m_hHalconID, ui.label_show->height() / 2.0 - m_scaledHeight / 2.0, 0, ui.label_show->width(), m_scaledHeight);
}
else
{
set_window_extents(m_hHalconID, 0, ui.label_show->width() / 2.0 - m_scaledWidth / 2.0, m_scaledWidth, ui.label_show->height());
}
}
}
else
{
set_window_extents(m_hHalconID, 0, 0, ui.label_show->width(), ui.label_show->height());
}
set_part(m_hHalconID, 0, 0, m_imgHeight - 1, m_imgWidth - 1);
attach_background_to_window(m_hCurrentImg, m_hHalconID);
}
//窗体大小改变事件
void BrowsePic::resizeEvent(QResizeEvent *ev)
{
if (m_hHalconID != NULL)
{
//防止窗口闪烁
set_system("flush_graphic","false");
showImg();
set_system("flush_graphic", "true");
detach_background_from_window(m_hHalconID);
attach_background_to_window(m_hCurrentImg, m_hHalconID);
}
}
//打开图片
void BrowsePic::on_btn_openPic_clicked()
{
//打开选择文件对话框,选择文件
QString qtFilePath = QFileDialog::getOpenFileName(this, "选择图片", "./", "图像文件(*.jpg *.png *.bmp *.tif)");
if (qtFilePath.isEmpty())
{
return;
}
QFileInfo fileInfo(qtFilePath);
qtFilePath.replace("/", "\\");
QString hPath = fileInfo.path().replace("/", "\\");
//由halcon算子获取选中的文件夹中的所有文件
list_files(hPath.toStdString().c_str(), HTuple("files").Append("follow_links"), &m_imgFiles);
//筛选符合图片格式的文件
tuple_regexp_select(m_imgFiles, HTuple("\\.(jpg|png|bmp|tif)$").Append("ignore_case"), &m_imgFiles);
//找到选择的文件
tuple_find(m_imgFiles, qtFilePath.toStdString().c_str(), &index);
//读取图像
read_image(&m_hCurrentImg, HTuple(m_imgFiles[index]));
showImg();
}
//浏览前一张
void BrowsePic::on_btn_prePic_clicked()
{
if (index.Num()>0&&index[0].I() > 0)
{
index[0] = index[0].I() - 1;
read_image(&m_hCurrentImg, HTuple(m_imgFiles[index]));
showImg();
}
}
//浏览后一张
void BrowsePic::on_btn_nextPic_clicked()
{
if (index.Num()>0 && index[0].I() < m_imgFiles.Num() - 1)
{
index[0] = index[0].I() + 1;
read_image(&m_hCurrentImg, HTuple(m_imgFiles[index]));
showImg();
}
}
//恢复图片
void BrowsePic::on_btn_resetPic_clicked()
{
showImg();
}
//等比缩放复选框状态改变
void BrowsePic::on_checkBox_equalScale_stateChanged(int state)
{
ui.checkBox_showCenter->setEnabled(state);
m_bIsEqualScale = state;
showImg();
}
//居中显示复选框状态改变
void BrowsePic::on_checkBox_showCenter_stateChanged(int state)
{
m_bIsShowCenter = state;
showImg();
}