一、项目构成
- 概况
- 开源协议:lgpl和gplv2
- qt生态链:商业版需要付费,否则只能开源。qss自由度有,
- 但是组件库收费:一个模仿WPF UI的国内库(GitHub - zhiyiYo/PyQt-Fluent-Widgets at Qt5)
- 跨平台:已经不是优势,音视频处理领域有一定优势。
- 嵌入式:没优势,低端的用mingui、lgvl;高端的用Android。
- mfc和.net的生态链:需要微软的Visual studio授权,如果是.net framework甚至要购买Window Server。
- ui组件库:WPF UI、Win UI都是微软主推的,主要给的就是vs授权。
- electron:开源,qt开发者都在转。
- Linux平台:开源核心优势在那,但也细分领域。
- Android内核:是最高级的,也是最流行的,上个好的arm芯片要不了多少钱。例子:收银机、车机
- 自研:基本上还是二次开发,例如3d打印领域的klipper。纯自研可以用qt,最近出现了lgvl。
- IDE:qt creator
- 设计器 qt designer:非常类似于winform
- 构建器:qmake、极度相似与Cmake(makefile)
- 编译器:gcc 32位、msvc等都行
- 设置msvc:本意是在命令行中设置编译环境,即独立于vs之外进行命令行编译开发。现在qt直接提供入口即可。
- 帮助文档:
- qt core:核心库
- qt gui:基于openGL的组件库
- 快捷键:
- widgets app:
-
- sources源码窗口:QmainWindow类,体现为头文件
- QApplication:是qt应用程序,执行exec()即阻塞生成桌面。底层就是基于消息循环的概念
- MyWindow:主要窗口,生成ui界面,和qmainwindow的区别是有无菜单,执行show()方法
- Forms:.ui文件——运行时编译成cpp文件
- 对应的cpp文件为:mywindow、mywidget等里面的UI子对象
- 本质:designer来设计,自动生成代码放进容器类里,和winform逻辑一致。
- Headers:专门放头文件
- .pro文件:项目配置相当于qmake的Makefile文件
- 编译:模块、源文件、ui文件
- sources源码窗口:QmainWindow类,体现为头文件
- 编译和构建:
- 项目结构:只能ide里调试
- 完整的dll项目:
- event filter:全局事件拦截器
- 作用:让一些不常规的事件触发
二、q widget页面
- 界面:和安卓高度相似,有特色的cpp库
- 对象树:Qlist记录子对象树,方便析构
- gc:系统根据parent指针会调用父类的析构,自动析构所有对象。
QObject
对象通过setParent()
方法设置了其父对象后,就会被添加到父对象的对象树中。当父对象被销毁时,它会自动递归销毁其所有子对象,从而避免了内存泄漏问题。
- Q_OBJECT宏:qobject的特点,信号槽函数都在此
- 设计器:只能说和winform没区别
- 查看继承结构:
- 生成文件:ui_wight/ui_mainwindows
- 在窗口类里被定义在Ui命名空间里引用
- cpp里初始化指针:
- 初始化成员列表:这里用到了ui(new Ui::Widget),一个快速赋值的技巧语法糖,相当于=赋值。这种写法也有助于避免潜在的资源泄漏问题,因为如果在构造函数初始化列表中出现了异常,构造函数会在对象被完全创建之前终止,从而避免资源泄漏。
- 底层原理:
- setupUI():创建组件和属性、
- 关联槽函数:
- 类定义:意思就是ui::Widget被包含了,之前已经声明,实现了可视化设计功能。
- 不用设计器:就没有ui指针,直接在窗口widget文件下定义和操作:
- 窗口开发:
- 1.先加layout(div):里面的组件自动垂直或水平划分
- 2.菜单栏:设置tab顺序、快捷键获取焦点等
- 3.手动创建组件实例:一般用iniUI()函数,是手动创建ui。
- 对比Android:是用xml设计居多,而非代码。
- Qdialog:
- 与mainwindows区别:exec()以模态显示就会导致聚焦中心不会发生变化,show()才能同时多个对话框窗口。
- QMessagebox:直接使用information()函数
- 资源文件:Resource目录,十分类似于Android。
- 菜单栏:
- 菜单-工具-状态栏:得QmainWindow才具有
- action:拖动到菜单分组下的按钮控件,触发信号triggered()
- 组成:图标-文字
- 事件:
- 图标:
- RC_ICONS =APPIcon.cio
- 控件
- qtextEdit:典型的对象绑定数据、
- 与前端的区别:dom树双绑到js对象是不同的。
- qtextEdit:典型的对象绑定数据、
- qss:类似于css
三、事件和模块
- 信号槽:
- 本质:回调函数,但从性能角度来看,信号槽机制相对于回调函数可能略微慢一些,因为信号槽机制涉及到一些附加的元数据和运行时信息。但是,这种差异通常不太明显,并且由于信号槽机制带来的灵活性和可维护性,一般情况下使用信号槽机制比使用回调函数更好。
- 实际开发:还是用回调函数on_click(),这样便于维护。
- 与mfc的消息区别:Qt的信号槽机制更加灵活、易于使用,而MFC的消息机制则更加底层,且不支持多对多。
- 使用:
- 由父类对象进行connect(对象,信号,对象,槽)函数,一般写在窗口的构造函数调用initSIGNALSlots()函数中。
- 例如:connect(ui->rbtn,SIGNAL(clicked()),this,SLOT(on_xx_clicked()))
- 可视化:
- 关闭连接(少用):disconnect()
- 自定义:1对多的灵活性体现,跨对象进行通信
- signals:是一个宏,声明自定义信号例如:love(),在c#中类似于事件,最好加emit标志
- slots:是实现,当信号发出时自动调用的回调函数。
- 带参数:得声明函数指针来指向特定的函数,void (boy::*xprt)(Qstring)
- 使用lambda:
- js中:(参数)=>{}
- qt中:[外面的参数](信号传的参数类型)muteble->返回值{}(实际传参),[]中的参数主要是传入this指针
- 常见应用:ret=[&ret]()mutable->int{}相当于js中修改全局数据
- qtcore:
- 底层:qt的预编译机制完成了
四、mfc附录
1.谈泛前端界面的渲染框架
- 谈客户端
- 核心:HMI(人机交互)
- 页面渲染引擎:引擎是webgl、还是xml解释器、还是其他。
- 数据绑定:mvvm自动绑定、命令式手动绑定mvc。
- MFC(Microsoft Foundation Classes):
- 渲染引擎:MFC使用GDI(Graphics Device Interface)作为其底层渲染引擎,用于2D图形绘制和界面渲染。
- 维护者:MFC由Microsoft维护和支持。
- Qt:
- 渲染引擎:Qt使用自己的绘图引擎,称为Qt Graphics View Framework,它是基于OpenGL的。
- 维护者:Qt是由Qt公司(前身是Nokia,现在是Digia的一部分)维护的,同时也是开源社区参与的项目。
- WinForms(Windows Forms):
- 渲染引擎:WinForms使用GDI+(Graphics Device Interface Plus)作为其底层渲染引擎,用于2D图形绘制和界面渲染。
- 维护者:WinForms由Microsoft维护和支持。
- WPF(Windows Presentation Foundation):
- 渲染引擎:WPF使用DirectX作为其底层渲染引擎,具有强大的3D图形和高级图形特性。
- 维护者:WPF由Microsoft维护和支持。
- Electron:
- 渲染引擎:Electron使用Chromium作为其底层渲染引擎,Chromium是开源的Web浏览器引擎,它支持HTML、CSS、JavaScript等Web技术。
- 维护者:Electron是一个开源项目,由社区维护,但也得到了一些大公司的支持,如GitHub、Microsoft等。
- 四大端:
- browser:基于浏览器的w3c标准,main.js的入口、vue提供了组件化、mvvm数据流。原生函数式编程支持(事件)。
- Windows:
- mfc:基于win32,有InitInstance虚函数入口
- qt:有main.app加载QApplication应用程序入口,
- 运行时c#:也有入口,结构化ui和控件组件对象化,委托支持事件,
- Android:基于java虚拟机的,manifest文件入口读取,四大组件。
- 跨平台应用:基于混合编译的,基于特定语言,还有调用特定平台api接口。
- 驱动程序和硬件支持:
- 一般来讲是Android rom开发或者上位机控制需要的驱动程序。
- 消费电子:液晶显示屏
2.工程化构成
- 概念:微软维护的c++类库
- 初版本:1992年,提出的 GDI渲染技术。
- MFC 4.0:1995年发布,引入了Doc/View架构,这是一种用于管理文档和视图的模型-视图-控制器(MVC)设计模式。
- MFC 16.0 / Visual Studio 2019:继续对Windows 10进行改进,并提供了更多的现代化界面控件和工具。
- 基本库:三大声明:afx.h和afxwin.h、afxext窗口类声明
- 全局对象:cwinapp,提供了消息循环的应用程序类
- cobject:提供机制的支持——序列化、动态创建
- 为什么mfc:微软提供了一个c++的应用程序框架,用于快速开发桌面应用。
- 三大机制:
- 程序启动机制:总结就是围绕cwinapp的构造和生命周期InitInstance - 程序的初始化函数和Run - 消息循环
- 窗口创建机制:与消息紧密相关,
- 消息映射机制:
- 类内必须添加声明宏:DECLARE_MESSAGE_MAP()
- 类外必须添加实现宏:就是在cpp中,
BEGIN_MESSAGE_MAP(
theClass, //想要实现消息映射的类名称
baseClass //theClass中的基类名称
)
END_MESSAGE_MAP()
上述两个实现宏需要配对使用,也就是两个一起
- 对话框应用程序:
- h文件:
- winapp:应用程序类负责了
- xxdlg:窗体,类似于form
- cpp文件:
- CDialog:
- h文件:
- 单文档视图架构程序:
- CFrameWnd:窗口类
- CDocument:文档类,负责管理数据
- Cview:视图窗口类,负责显示数据
- 多文档视图框架程序:
- CMDIFrameWnd:多文档的窗口类
- CMDIChildWnd:子窗口类
- CView:显示数据
- 基本类:
- 类的阶层:
- Cviews:视图
- CMenu:菜单
- Ccontrols:控件类
- Cfile:系统文件io
- CDialogEx:简单的对话框
- 概括:
- 关于程序启动流程:
- 入口函数:与Win32窗口程序相同,都是从WinMain函数开始,但是MFC库已经实现了WinMain函数,所以在程序中不需要实现由于MFC库实现了WinMain,也就意味着MFC负责安排程序的流程。且C++ 规定,全局对象的构造将比程序进入点(在 DOS 环境为main,在 Windows 环境为WinMain)更早。所以theApp 的构造函数将更早于main。换句话说你所看到的执行结果中的那些构造函数输出动作全都是在main函数之前完成的。main 函数调用全局函数 AfxGetApp 以取得theApp 的对象指针。这完全是仿真 MFC 程序的手法。
- 每一个窗口的cpp必执行InitInstance():生命周期内的数据处理呗。
3.实际开发
- 消息映射开发:一对一映射
- 声明:在类中声明 afx_msg类型
- 实现:在类外使用宏实现
- 视图(view+control):
- 作用:覆盖在主框架窗口客户区上的另一个窗口,这个窗口没有标题栏也没有边框,背景是白色的,将主框架窗口客户区遮挡起来,它可以提供一个用于专门显示数据的窗口
- 重写:void OnDraw(CDC* pDC)
- 文档(model):覆盖在主框架窗口客户区上的另一个窗口,这个窗口没有标题栏也没有边框,背景是白色的,将主框架窗口客户区遮挡起来,它可以提供一个用于专门显示数据的窗口。
- 窗口切分:
- 使用类:CSplitterWnd - 不规则框架窗口类,封装了不规则框架窗口的操作
- 生命周期:
- 1.写在虚函数OnCreateClient()中
- 2.调用CSplitterWnd::CreateStatic()
- 实际开发:以一个数据列表展示应用为例。
- 1.如果你想创建一个带有菜单、窗口内分成多个区域(
CView
)并展示一些数据列表的应用,我建议你选择多文档视图架构。这个框架允许你创建多个文档和视图,每个视图可以在主窗口中的不同区域显示不同的内容,同时还可以轻松地管理菜单和工具栏。 - 2.然后走流程cwinapp-cframewnd-cdocument+cview-控件
- 1.如果你想创建一个带有菜单、窗口内分成多个区域(
4.基本的Window原理和api
- Window api
- 总结:mfc就是对Window api的高级封装。
- 库文件GDI32.LIB 和 USER32.LIB 和 KERNEL32.LIB。
- USER32.LIB:掌控外围设备监控和巡侦。
- GDI32.LIB:至于窗口的产生与显示,十分简单,有专门的API函数负责。
- 声明头文件:Window.h涵盖了所有api,windef、winusr等等。
- 编译:
- 资源:
resource.h
:这个头文件通常包含了资源标识符的定义,例如图标、位图、对话框模板等。这些资源可以在你的应用程序中使用,例如作为窗口图标或对话框的布局。 targetver.h
:这个头文件通常包含了一些宏定义,用于指定你的应用程序要针对的最低Windows版本,以便在编译时进行条件编译- main函数入口:当Windows的「外壳」(shell,例如 Windows 3.1的程序管理员或Windows 95的文件总管)侦测到使用者意欲执行一个 Windows 程序,于是调用加载器把该程序加载,然后调用C startup code,后者再调用WinMain,开始执进程序。WinMain 的四个参数由作业系统传递进来。
- 消息驱动机制:程序不断等待(利用一个 while 循环),等待任何可能的输入,然后做判断,然后再做适当的处理。
- 消息循环需要先创建一个MSG结构,其中包含以下的信息
1、窗口句柄
2、消息ID
3、消息的两个参数(两个附带消息)
4、消息产生的时间
5、消息产生时的鼠标位置 - GetMessage()获取信息到结构体lpMsg,并指定句柄hWnd的抓,
- DispatchMessage(const MSG* lpMsg)函数会调用窗口处理函数,首先调用窗口处理的
- SendMessage()阻塞式函数,PostMessage()非阻塞式。
- 系统消息:就是相当于事件e传参。
- 用户消息:
- #define WM_MYMESSAGE WM_USER + 1001 //消息的宏名称,可以随意定义
- SendMessage - 直接将消息发送给窗口的处理函数,并等候处理结果,是非消息队列的
- 常见消息:WM_CREATE、WM_SIZE等
- 总结流程:消息发送后,首先放入系统队列,再转发到各程序队列,然后通过消息循环,从本进程队列中获取
- GetMessage - 从消息队列中获取消息
- PostMessage - 将消息投递到消息队列
- 常见队列消息:WM_PAINT、键盘、鼠标、定时器
- 消息循环需要先创建一个MSG结构,其中包含以下的信息
- 消息的应用:
- WM_CREATE:常用于初始化窗口的参数、资源等等,包括创建子窗口等
- WM_SIZE:常用于窗口大小变化后,调整窗口内各个部分的布局
- WM_SYSCOMMAND:关闭确认
- GDI绘图:可以说相当于opengl的绘图,晦涩难懂。而且只有2d,
- 性能:强于java的swing库但弱于directx(WPF)
- 编译问题:
- 静态库是将代码嵌入到使用程序中,当多个程序使用时,会有多份代码,所以代码的体积会增大
动态库的代码只需要存在一份,其他程序通过函数地址可以直接使用,所以代码体积小 - 静态库发生变化后,新的代码需要重新链接并嵌入到执行程序中
动态库发生变化后,如果库中函数的定义(或地址)没有变化,其他使用DLL的程序不需要重新链接
- 静态库是将代码嵌入到使用程序中,当多个程序使用时,会有多份代码,所以代码的体积会增大
6.console程序
- visual c++:其实就是Windows c++
- 我在BBS论坛上看到很多程序设计初学者,还没有学习C/C++,就想直接学习Visual C++。并不是他们好高骛远,而是他们以为Visual C++是一种特殊的C++语言。吃过苦头的过来人以为初学所说的 Visual C++ programming 是指MFC programming,所以大吃一惊(没有一点C++ 基础就要学习MFC programming,当然是大吃一惊)。
- console程序和gui程序:
- 共同:Console 程序的格式则和所有的Win32 程序一样,是所谓的PE(Portable Executable)格式
- 区别:能调用的api有限,仅限于与绘窗口无关的。例如多线程支持的、cstring。
本文作者为抱一只橘,转载请注明。