【Qt 导出生成并使用第三方库】03:导出供MFC调用且带QT界面的DLL

前言

这边和大家分享一个最近做的项目,来演示如何生成供MFC调用的带QT界面的DLL
项目功能:读取本地视频,并判断每帧图片的水流方向以及角度,并在界面上绘制罗盘,和定位辅助线(ps:关于视觉部分代码,在下面开源的项目中我删除掉了)

程序功能效果展示

在这里插入图片描述

引言

在Windows下,可能要用MFC或C#或Java,python调用一个插件,这个插件是一个dll,可以弄一个Qt的界面出来,官方已经给出了一个API了,为qtwinmigrate。大家可以参考这位博主的调用方式实现。

关于qtwinmigrate大家可以参考下面这两篇文章
使用C++控制台程序或Python调用Dll创建Qt界面(dll中创建QApplication,qtwinmigrate的使用)
Qt生成带界面的dll给c#调用的例程

下载qtwinmigrate源码

下载 qtwinmigrate:https://github.com/qtproject/qt-solutions
如果无法访问可以在文章下方自行下载 文件名为:qt-solutions-master.rar

项目简介

导出供MFC调用且带QT界面的DLL

因为提供源码了大家可以自己下载学习
我这边就简单介绍下几个类的功能,以及调用方法
在这里插入图片描述
SaveLog类 是日志记录用的,将程序的打印信息保存到本地方便定位问题
使用方法
在实例化manwindow类之前 我们就安装日志钩子
在这里插入图片描述
testAngle类主要负责调用opencv接口进行本地视频的读取并通过算法计算图像中水流方向并返回角度 通过paintEvent将角度信息指南针绘制在界面上。(PS计算水流方向功能,在源码中被我删除掉了所以找不到哈~)
核心绘制代码

void testAngle::paintEvent(QPaintEvent *event)
{
    QPainter p(this);
    float angle = m_angle;      //旋转角度
    p.setRenderHint(QPainter::Antialiasing, true); //抗锯齿

    //绘制图像作为背景
    p.drawPixmap(0,0,width(),height(), QPixmap::fromImage(m_backGround));

    int width = this->width();
    int height = this->height();
    int maxLen = width*1.5;
    if(height > width)
        maxLen = height*1.5;
    int radius = 20;

    QFont font;
    font.setPointSize(12);
    font.setBold(true);
    p.setFont(font);
    QFontMetrics fm = p.fontMetrics();
    int width_text = fm.width("北");

    //绘制指南
    if(m_bshowCompass_virtual && !std::isnan(angle))
    {
        QPolygon pts;
        //N
        p.save();
        p.translate(width*0.92,height*0.15);
        p.setOpacity(0.9);
        p.setPen(Qt::NoPen);
        p.setBrush(Qt::red);
        pts.setPoints(3, -5, 0, 5, 0, 0, radius);
        p.rotate(angle + 180 + 180);
        p.drawConvexPolygon(pts);
        //N text

        p.setPen(QPen(Qt::red,4));
        p.drawText(-width_text/2,40,"北");
        p.restore();
        //S
        p.save();
        p.translate(width*0.92,height*0.15);
        p.setOpacity(0.9);
        p.setPen(Qt::NoPen);
        p.setBrush(Qt::black);
        pts.setPoints(3, -5, 0, 5, 0, 0, radius);
        p.rotate(180+angle);
        p.drawConvexPolygon(pts);
        p.restore();
    }

    //绘制十字线
    p.translate(width/2,height/2);
    if(m_breticle && !std::isnan(angle))
    {
        //下
        p.save();
        p.setPen(QPen(Qt::red,3));
        p.rotate(angle);
        p.drawLine(0,0,0,maxLen/2*0.25);
        //N text
        p.setFont(font);
        p.setPen(QPen(Qt::red,4));
        p.drawText(-width_text/2,maxLen/2*0.25+width_text,"北");
        p.setPen(QPen(Qt::red,2));
        p.setBrush(Qt::NoBrush);
        width_text += 5;
        p.drawRect(-width_text/2,maxLen/2*0.25,width_text,width_text);
        p.setPen(QPen(Qt::red,3));
        p.drawLine(0,maxLen/2*0.25+width_text,0,maxLen-(maxLen/2*0.25+width_text));
        p.restore();

        //左
        p.save();
        p.setPen(QPen(Qt::white,3));
        p.rotate(angle+90);
        p.drawLine(0,0,0,maxLen/2);
        p.restore();
        //上
        p.save();
        p.setPen(QPen(Qt::white,3));
        p.rotate(angle+180);
        p.drawLine(0,0,0,maxLen/2);
        p.restore();
        //右
        p.save();
        p.setPen(QPen(Qt::white,3));
        p.rotate(angle+270);
        p.drawLine(0,0,0,maxLen/2);
        p.restore();
    }
    //绘制定位圆点
    if(m_bredPoint && !std::isnan(angle))
    {
        p.setPen(QColor(255, 20, 15));
        //圆点
        p.setPen(QPen(Qt::red,2));
        p.setBrush(QColor(255,0,0));//设置画刷,如果不画实现的直接把Brush设置为setBrush(Qt::NoBrush);
        p.drawEllipse(-4,-height*0.45,8,8);
    }
}

MainWindow类主要负责窗口焦点监管和testAngle实例化显示
因为在C#中隐藏和显示Qt的窗口有可能导致,Qt窗口焦点丢失导致界面刷新无效等问题,因此需要对Qt窗口的焦点进行监管处理。
负责监管焦点的函数主要有

	void init();
	void saveFocus();
	void resetFocus();
	void childEvent( QChildEvent *e );
	bool eventFilter( QObject *o, QEvent *e );
	
	bool focusNextPrevChild(bool next);
	void focusInEvent(QFocusEvent *e);
	#if QT_VERSION >= 0x050000
	bool nativeEvent(const QByteArray &eventType, void *message, long *result);
	void show();

ok完成了程序架构的搭建,我们在main.cpp中,我们需要指定函数接口来给C#使用。
提供给C#调用的接口有

//初始化窗口 实例化QApplication 和 mainwindow 
extern "C" __declspec(dllexport) void InitialDll(HWND parent ,int x,int y,int w,int h)
//显示窗口
extern "C" __declspec(dllexport) bool showDialog(int x,int y,int w,int h )
//隐藏窗口
extern "C" __declspec(dllexport) void hideDialog( )
//选择视频文件位置并加载播放视频 
extern "C" __declspec(dllexport) void initViedoTest( )
//删除窗口
extern "C" __declspec(dllexport) void deleteAll()
//显示指南针
extern "C" __declspec(dllexport) void showCompass()
//隐藏指南针
extern "C" __declspec(dllexport) void hideCompass()
//设置角度
extern "C" __declspec(dllexport) void setAngle(float angle)
//显示十字线
extern "C" __declspec(dllexport) void showReticle()
//隐藏十字线
extern "C" __declspec(dllexport) void hideReticle()
//显示定位红点
extern "C" __declspec(dllexport) void showRedPoint()
//隐藏定位红点
extern "C" __declspec(dllexport) void hideRedPoint()

特别说明
1.在实例化我们的Qt窗口前我们需要先实例化QApplication

    int argc = 0;
    (void)new QApplication(argc, 0);

2.MainWindow *a 这个实例化对象我是做的全局变量,以便其他函数可以直接调用。
在这里插入图片描述

3.为了设置我们的DLL的使用时效 我们可以通过下面的代码来实现

	const long long endline = QDateTime(QDate(2022,2,04),QTime(23,59,59)).toTime_t();//2018.06.30 23:59
    struct stat buffer;
    QString DLLName = QCoreApplication::applicationDirPath()+"/qtdialog.dll";
    stat(DLLName.toUtf8().data(), &buffer);//读取文件的信息
    time_t now = time(NULL);//获取当前时间
    //qDebug() << buffer.st_ctime << buffer.st_mtime << endline <<QApplication::applicationFilePath()<< QCoreApplication::applicationDirPath(); // 文件的创建时间、修改时间分别与期限相减
    if (now - endline > 0 || buffer.st_ctime - endline > 0 || buffer.st_mtime - endline > 0){//当前时间、文件的创建时间、修改时间分别与期限相减
        QMessageBox::information(NULL,"Title","DLL 文件过期");
        return;
    }

4.要想C#能调用Qt生成DLL文件还需要生成qtdialog.dll所依赖的库操作如下

以管理员身份打开Qt 5.9.7 64-bit forDesktop(MSVC 2015)
ps这里根据你生成DLL文件是什么编译器生成的选择用哪个命令行工具
在这里插入图片描述

按下回车后就会在该文件夹下生成qtdialog.dll所依赖的库,如下图:
可以参考下Qt程序打包发布方法(使用官方提供的windeployqt工具)
在这里插入图片描述
因为我们这边还用到了opencv 所以还需要加上opencv的DLL文件
opencv的DLL在QtDll4CSharp\myQtMainWindowDll\OPENCV-X64-4.5\x64\vc15\bin 这个路径下面
在这里插入图片描述
另外一些电脑可能没有以下文件会导致程序异常崩溃
在这里插入图片描述

在MFC中使用Qt的导出DLL

声明qtdialog.dll里面的接口

        [DllImport(@".\qtdialog.dll", CallingConvention = CallingConvention.Cdecl)]//导入qtdialog.dll
        public static extern void InitialDll(IntPtr parent, int x, int y, int w, int h);//声明qtdialog.dll里面的一个接口
        [DllImport(@".\qtdialog.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]//qtdialog.dll
        public static extern bool showDialog(int x, int y, int w, int h);//声明qtdialog.dll里面的另一个接口
        [DllImport(@".\qtdialog.dll", CallingConvention = CallingConvention.Cdecl)]//导入qtdialog.dll
        public static extern void deleteAll();//声明qtdialog.dll里面的一个接口
        [DllImport(@".\qtdialog.dll", CallingConvention = CallingConvention.Cdecl)]//导入qtdialog.dll
        public static extern void initViedoTest();//声明qtdialog.dll里面的一个接口
        [DllImport(@".\qtdialog.dll", CallingConvention = CallingConvention.Cdecl)]//导入qtdialog.dll
        public static extern void showCompass();//声明qtdialog.dll里面的一个接口
        [DllImport(@".\qtdialog.dll", CallingConvention = CallingConvention.Cdecl)]//导入qtdialog.dll
        public static extern void hideCompass();//声明qtdialog.dll里面的一个接口
        [DllImport(@".\qtdialog.dll", CallingConvention = CallingConvention.Cdecl)]//导入qtdialog.dll
        public static extern void setAngle(float angle);//声明qtdialog.dll里面的一个接口
        [DllImport(@".\qtdialog.dll", CallingConvention = CallingConvention.Cdecl)]//导入qtdialog.dll
        public static extern void showReticle();//声明qtdialog.dll里面的一个接口
        [DllImport(@".\qtdialog.dll", CallingConvention = CallingConvention.Cdecl)]//导入qtdialog.dll
        public static extern void hideReticle();//声明qtdialog.dll里面的一个接口
        [DllImport(@".\qtdialog.dll", CallingConvention = CallingConvention.Cdecl)]//导入qtdialog.dll
        public static extern void showRedPoint();//声明qtdialog.dll里面的一个接口
        [DllImport(@".\qtdialog.dll", CallingConvention = CallingConvention.Cdecl)]//导入qtdialog.dll
        public static extern void hideRedPoint();//声明qtdialog.dll里面的一个接口

在显示Qt界面前需要先调用InitialDll启动QApplication并传入父对象,和指定窗口大小
注意:c#在调用的时候必须先执行一次InitialDll,因为qt界面类程序的运行必须依赖于QApplication(argc, 0):

        public Form1()
        {
            InitializeComponent();
            InitialDll(this.Handle, 0, 0, 1240, 600);//启动QApplication
        }

通过C#中Form的按钮触发调用Qt的接口

        //显示qt窗口界面
        private void button1_Click(object sender, EventArgs e)
        {
            //this.WindowState = FormWindowState.Maximized;
            showDialog(0, 0, 1240, 600);
        }
        //选择视频文件位置并加载播放视频 
        private void abc123_Click(object sender, EventArgs e)
        {
            initViedoTest();
        }
        //删除Qt窗口
        private void button2_Click(object sender, EventArgs e)
        {
            deleteAll();
        }
        //显示罗盘
        private void button3_Click(object sender, EventArgs e)
        {
            showCompass();
        }

然后我们把之前qtdialog.dll生成的一拖依赖库文件添加到C#项目的输出路径里面就行了
在这里插入图片描述
本篇源码
https://github.com/jbyyy/QtDllLearing/releases/tag/QtDllLearingTEST3.0

Qt 导出生成并使用第三方库

【Qt 导出生成并使用第三方库】01:导出C++内容的DLL 并使用

【Qt 导出生成并使用第三方库】02:导出带QT界面且依赖opencv库的DLL]

【Qt 导出生成并使用第三方库】03:导出供MFC调用且带QT界面的DLL

  • 3
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jbyyy、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值