WPF 程序内嵌 QT 窗体

1、目标:将QT控件(Qwiget)(或则基于QWiget的控件)(或则任何第三方C++控件)封装为WPF可调用的用户控件。简单来说就是WPF程序调用QT窗体控件。

本人需要使用3D控件显示一些3D点云等功能。但是又找不到好的兼容WPF的控件(大点云和效率原因)。最后选用CloudConpare作为点云显示控件(该控件基于QT的QWiget)。
目前网络上这方面的内容并不是太多,而且多数还都是雷同。
个人感觉 C# 跨平台并不是太友好(或则我水平不够),特别是3D方面。

本人对QT不是很熟悉,但是CloudCompare是基于QT写的界面,所以没办法只能慢慢摸索,好在以前有MFC编程经验,对C++还有点积累,过程中关于配置方面的问题我尽量详细说明(对于C#编程者来说还是挺恶心的,QT的配置和CloudCompare的配置挺麻烦的)。

实现过程如下(可能需要稍微有点基础才能看懂,我认为有意思的会写多点 ,写的不是太系统),可能有点乱,有点啰嗦,想到哪里写到哪,见谅。

心路:
一开始查询资料想通过中间库 CLR链接C#和C++,简单demo也测试OK,后期碰到控件句柄传递问题调试也比较麻烦,遂放弃(现在想来应该也是可以实现的)。
最终直接使用 C形式函数导出 的形式来创建DLL,也挺方便的。专业点叫P/Invoke(推荐大家一本书《精通.NET互操作:P/Invoke,C++Interop和COM Interop》)。

大概逻辑:WPF控件句柄–>传递给C++生成的QT库–>动态库根据传递过来的句柄创建一个QWiget–>根据这个QWiget生成一个GLWindow(真实的3D显示窗体)–>C#保存这个指针方便下次访问。
数据传逻辑:主要是C#这边发给C++ Dll数据,主要涉及到托管和非托管资源的问题、字节对齐等。

实现过程中可能有几个主要问题:
1、C#和C++混合编程;
2、QT程序(QT事件循环QApplication.exec())如何封装为Dll;
3、QT的UI线程和WPF的UI线程冲突问题(目前本人也没完全弄懂,欢迎沟通);

工具:VS2019(Qt Visual Studio Tool 2.7.0)、QT5.14.2、CloudCompare2.6.12源码。。。。

上代码:
1、创建一个VS-QT的动态库。
选择Qtwidgets application,然后修改配置生成为dll。网上资料很多,不详细描述(大概流程:修改输出类型为dll,将没必要的文件删除即可(Main)(所有文件都可以删除,然后新增一个QtWidgets class也行))。如下:

我们这里界面就不需要设置(因为我们不是直接用QT界面),只需要一个空白的Qwidget。(同志们只需要用Qwiget就可以在这里绘制了)
右键 编译这个.ui文件。会生成一个ui_xxxx.h文件。添加这个文件到项目中,(可能在生成目录的uic文件夹下,如有必要还需要将这个文件路径加到包含目录中。若是不添加可能会生成失败)。
首次生成失败很正常,各种QT环境问题。不要心急。

这里我们就获得了一个带QT的界面的C++库。
2、WPF中引用它:
若是你们用的是Winform,那就比较方便了,直接拿到控件句柄(我这里使用的panel控件)传递给动态库就行了。
若是你用的是WPF,会麻烦一点(因为WPF控件获取不到“控件”句柄),这里在下试了很多,获得的都是窗体句柄。所以只能使用WindowsFormsHost。上代码

这里是本人在WPF创建了一个用户控件的xaml文件。上代码

public partial class Window3DControl : UserControl{public Window3DControl(){InitializeComponent();}/// <summary>/// 3D显示 窗口/// </summary>public Window3DControl_Net Control3D = new Window3DControl_Net();/// <summary>/// 创建真实的3D窗体,GlWindow/// </summary>/// <returns></returns>public bool Create3DWindow(){int res = Control3D.Creat3DWindow_Net((long)Panel3D.Handle, Panel3D.Width, Panel3D.Height);return res == 0;}/// <summary>/// 控件发生变化/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void Window3DControl_SizeChanged(object sender, SizeChangedEventArgs e){if(Control3D.IsInitWindow){int width = (int)(WindowForm3D.ActualWidth * 1.25);int Heigth = (int)(WindowForm3D.ActualHeight * 1.25);int res = Control3D.WindowSizeChanged_Net(width, Heigth);}}/// <summary>/// 3D窗体切换/// </summary>public void ChangeWindowModel(){if(WindowForm3D.Visibility == Visibility.Visible){WindowForm3D.Visibility = Visibility.Collapsed;Button2D.Visibility = Visibility.Visible;}else{WindowForm3D.Visibility = Visibility.Visible;Button2D.Visibility = Visibility.Collapsed;}}}

上面是用户控件的代码部分。也很简单只有几个简单功能。其中Control3D对象是我封装的一个调用动态库的接口类(只是封装了一层),功能上直接调用C形式的函数接口也是可以的。上代码

这里是封装的3D对象的部分接口,C#这边就很简单了,都封装成用户控件了,那不是想怎么玩怎么玩。(关于C#定义函数接口就不描述了,C++那边导出函数接口也不描述了)。上代码

还有一点很重要(我感觉)如何显示QT窗口正好覆盖WPF中句柄对应的控件(可能会碰到直接显示QT窗口在主界面的左上角)。有两步很重要,通过如上图,可以看到我们将控件句柄和对应的尺寸传递过去,并且将生成的QT窗口的实例对应的指针传回来,为了下次能够直接拿到这个QT窗口。可以看下我的创建窗口函数是如何实现的,上代码

图中标记部分,很重要 。(忘记从哪个大神哪里偷学来的)
另外为了C# 那边能拿到C++这边的对象指针。使用了二级指针概念(这个不太好解释,要自己细细理解,这个不是偷学的,在下耗尽脑子想的)。
还有一个问题QT事件循环,(若是你调用过QT的dll,可能就会发现这个问题),我这边解决办法就是,在程序开始(你认为的合适的地方)调用InitQTEnvironment接口,上代码。

#include "qmfcapp.h"
int InitQtEnvironment()
{try{//if (QApplication::instance() == NULL)//{// int argc = 0;//    g_app = new QApplication(argc, NULL);//    //QWidget* GroudWidget = new QWidget(NULL);//  //GroudWidget->setGeometry(0, 0,0, 1);//设置widget的大小//    //GroudWidget->showMinimized();//    //g_app->exec();//}if (QApplication::instance() == NULL){return  QMfcApp::pluginInstance();}return 0;}catch(QException ex ){printf("Init Error");printf(ex.what());return -1;}}

两种方式都行,目的是创建出来QApplication这个静态对象即可。(其中QMfcApp是偷学某个大佬)。
OK,其实到这里整个流程已经基本打通了,路走通了,其余的都好办了。
关于数据传递问题,这里也说一下。C#和C++的基础数据类型在内存中占位不尽相同,有兴趣可以看下对照表(网上一大堆)。
若是使用CLR包装C++dll,可以解决类型不一致问题(个人感觉CLR就是一次形参类型转换而已,可能是我太浅薄了)。
若是我们这种直接调用C形式的函数接口。需要传递参数需要注意几点。
1、基础类型也需要注意内存占位是否一致。
2、结构体类型,需要考虑字节对齐和托管资源问题。思路:两边定义相同类型的结构体。直接传递结构体指针(首地址)本质上还是内存要一样,不能乱。先上代码(C++)

C#中数据结构

仔细对比(字节对齐,还要考虑编译器可能优化)。
数组必须确定传递长度(内存分配才能连续),并且C#这边申请的内存必须是非托管内存(使用Marsh申请)。关于C++传递数据到C#,上文也提到了二级指针,基础类型或确定内存长度的可以直接传递,不确定的对象使用二级指针。关于内存释放问题。大家可以网上找下,有几种常用方法,此处不赘述。

按照惯例,上效果图。

遗留问题:
玩过WPF的都知道,WPF有一个隐藏的UI线程,我们的属性绑定控件和刷线界面都是
这个线程来更新的。QT也有一个类似的界面线程。因为我这边主程序是WPF,QWiget只是作为一个控件来用的。
目前发现他们两个界面线程会出现抢资源情况(我也是猜的,因为出现WPF的源已经改变了但是目标控件上没有刷新(及时刷新)的现象)。各位大佬若有这方面的见解,请一定联系我。抱拳!!!

写的不妥的地方,请大佬们不吝赐教,再次抱拳!!!!

C# WPF调用 QT窗口相关推荐

  1. QT 在子窗口中调用主窗口的UI

    在QT中,我们时常会需要在主窗口里添加一些子窗口,比如添加一个新的对话框,或者在TabWidget中添加tab页面.通常添加子窗口后,如果在子窗口中做了一些操作,我们需要在主窗口中通过UI控件反映出来 ...

  2. [WPF疑难]避免窗口最大化时遮盖任务栏

    [WPF疑难]避免窗口最大化时遮盖任务栏 周银辉 WPF窗口最大化时有个很不好的现象是:如果窗口的WindowStyle被直接或间接地设置为None后(比如很多情况下你会覆盖默认的窗体样式,即不采用W ...

  3. 【Qt】Qt窗口程序

    00. 目录 文章目录 00. 目录 01. 概述 02. 开发环境 03. 程序设计 04. 程序执行 05. 预留 06. 附录 01. 概述 程序要实现的功能是:运行开始出现一个对话框,按下登录 ...

  4. 深度探索QT窗口系统——几何篇

    深度探索QT窗口系统--几何篇 窗口作为界面编程中最重要的部分,没有窗口就没有界面,是窗口让我们摆脱了DOS时代,按钮是一个窗口,文本框是一个窗口,标签页是一个窗口.一个窗口可以由多个窗口组成,每天我 ...

  5. Linux下Qt窗口半透明,Qt实现嵌入桌面的半透明窗口 good

    一.将Qt窗口嵌入到桌面中. 声明一个最简单的类: class Dialog : public QDialog {         Q_OBJECT public:         Dialog(QW ...

  6. 2021-10-18 WPF调用dll出现异常

    问题:WPF调用其他托管dll时,提示System.Windows.Markup.XamlParseException异常 用VS2019新建了一个wpf程序,调用已经写好的类库dll时,提示异常,见 ...

  7. OsgEarth —— 笔记2 - Qt窗口加载earth地球(附源码)

    效果         相关文章      OSG -- 笔记1 - 指令调用模型      OSG -- 笔记2 - 加载模型(附源码)      OSG -- 笔记3 - 绘制矩形(附源码)     ...

  8. Uiautomation 在Windows WPF和Qt 产品上的应用

    前面使用Pywinauto 对公司自研的Windows WPF和Qt进行了应用. 最近用Uiautomation 进行了试验,发现Uiautomation 比Pywinauto更加易用,更好上手. 如 ...

  9. WPF绘制自定义窗口

    原文:WPF绘制自定义窗口 WPF是制作界面的一大利器,下面就用WPF模拟一下360的软件管理界面,360软件管理界面如下: 界面不难,主要有如下几个要素: 窗体的圆角 自定义标题栏及按钮 自定义状态 ...

最新文章

  1. 【译】Swift算法俱乐部-查找最大/最小值
  2. uart接口_UART串行总线舵机转接板规格、接线说明 amp; 驱动安装
  3. 51nod 1557 两个集合 (严谨的逻辑题)
  4. junit junit_JUnit理论简介
  5. koa中上传文件到阿里云oss实现点击在线预览和下载
  6. 李宏毅的可解释模型——三个任务
  7. fiddler工具条、状态栏、请求信息栏各按钮的作用
  8. 《How to Generate a Good Word Embedding?》导读(转)
  9. 数据结构课程设计--平衡二叉树
  10. Redis 核心知识点总结
  11. idea maven repositories为空
  12. Mysql中修改字段类型、长度以及添加删除列
  13. 苹果激活锁功能可被长字符串溢出
  14. 《YUI 3 Cookbook 中文版》
  15. 研究生学习生活日记——第五次组会
  16. 24个关键词致敬科比系列之——凌晨四点
  17. watch蜂窝开通服务器中断,原因找到了!Apple Watch Series 3为何无法连接蜂窝网络...
  18. daydream手柄
  19. canopen 报文格式_CANopen协议报文处理
  20. 1100 1149C语言答案,C语言程序设计(何钦铭)课后习题作业

热门文章

  1. windows商店_Windows平台官方免费文件恢复软件
  2. 单片机 ADC0809模数转换实验
  3. 该怎样在KeyShot中进行贴图
  4. 萧毅舟;3.1今日黄金走势分析,反弹空,黄金白银操作建议
  5. python不等长数组_长度不等的数组中的Numpy数组
  6. 微型计算机接口2018年4月,全国2018年4月自考微型计算机及接口技术04732试题和答案(最后)...
  7. Node.js+Socket.io实现双人在线五子棋对战
  8. 研究生复试《软件工程导论》的资料及学习计划
  9. 【简单易懂】Java字符串应用场景:数字金额转换成大写汉字金额【金额转换】
  10. python怎么修改默认路径_修改默认python