Jack Huang's Blog


  • 首页

  • 标签

  • 归档

  • 搜索

QT核心机制与原理

发表于 2021-02-06 | 更新于 2023-08-17

要想学到QT的精髓,必须对QT的核心机制信号与槽、元对象系统、事件模型有充分的理解。

信号与槽

信号和槽是一种高级接口,它们被应用于对象之间的通信,它们是Qt 的核心特性,也是Qt不同于其它同类工具包的重要地方之一。

信号(signal)

当对象的状态发生改变时,信号被某一个对象发射( emit)。只有定义过这个信号的类或者其派生类能够发射这个信号。当一个信号被发射时,与其相关联的槽将被执行,就象一个正常的函数调用一样。信号-槽机制独立于任何GUI 事件循环。只有当所有的槽正确返回以后,发射函数(emit)才返回。

槽(slot)

槽是普通的C++成员函数,可以被正常调用,不同之处是它们可以与信号( signal)相关联。当与其关联的信号被发射时,这个槽就会被调用。槽可以有参数,但槽的参数不能有缺省值。

信号与槽的关联

槽和普通的C++成员函数几乎是一样的-可以是虚函数;可以被重载;可以是共有的、 保护的或是私有的,并且也可以被其它C++成员函数直接调用;还有,它们的参数可以是任意类型。唯一不同的是:槽还可以和信号连接在一起,在这种情况下,每当发射这个信号的时候,就会自动调用这个槽。

connect()语句看起来会是如下的样子:

1
connect(sender,SIGNAL(signal),receiver,SLOT(slot));

这里的sender 和receiver 是指向QObject 的指针,signal 和slot 是不带参数的函数名。实际上,SIGNAL()宏和SLOT()会把它们的参数转换成相应的字符串。

从QObject 或其子类(例如Qwidget)派生的类都能够使用信号和槽机制。这种机制本身是在QObject 中实现的,并不只局限于图形用户界面编程中:当对象的状态得到改变时, 它可以某种方式将信号发射(emit)出去,但它并不了解是谁在接收这个信号。

元对象系统

Qt 的元对象系统是一个基于标准C++的扩展,能够使C++更好的适应真正的组件GUI 编程。它为Qt 提供了支持对象间通信的信号与槽机制、实时类型信息和动态属性系统等方面的功能。

元对象系统在Qt 中主要有以下三部分构成:QObject 类、Q_OBJECT 宏和元对象编译器moc。

元对象系统机制

Qt 的主要成就之一是使用了一种机制对C++进行了扩展,并且使用这种机制创建了独立的软件组件。这些组件可以绑定在一起,但任何一个组件对于它所要连接的组件的情况事先都不了解。

这种机制称为元对象系统(meta-object system),它提供了关键的两项技术:信号-槽以及内省(introspection)。内省功能对于实现信号和槽是必需的,并且允许应用程序的开发人员在运行时获得有关QObject 子类的“元信息”(meta-information),包括一个含有对象的类名以及它所支持的信号和槽的列表。这一机制也支持属性(广泛用于Qt 设计师中)和文本翻译(用于国际化),并且它也为QtScirpt 模块奠定了基础。

标准C++没有对Qt 的元对象系统所需要的动态元信息提供支持。Qt 通过提供一个独立的moc 工具解决了这个问题,moc 解析Q_OBJECT 类的定义并且通过C++函数提供可供使用的信息。由于moc 使用纯C++来实现它的所有功能,所以Qt 的元对象系统可以在任意C++ 编译器上工作。

元对象工具(moc)

Qt 的信号和槽机制是采用标准C++ 来实现的。该实现使用C++ 预处理器和Qt 所包括的moc(元对象编译器)。元对象编译器读取应用程序的头文件,并生成必要的代码,以支持信号和槽机制。

事件模型

应用程序对象将系统消息接收为Qt 事件。应用程序可以按照不同的粒度对事件加以监控、过滤并做出响应。

在Qt 中,事件是指从QEvent 继承的对象。Qt 将事件发送给每个QObject 对象,这样对象便可对事件做出响应。也就是说, Qt 的事件处理机制主要是基于QEvent 类来实现的,QEvent 类是其他事件类的基类。当一个事件产生时, Qt 就会构造一个QEvent 子类的实例来表述该事件,然后将该事件发送到相应的对象上进行处理。

Qt 的主事件循环能够从事件队列中获取本地窗口系统事件,然后判断事件类型,并将事件分发给特定的接收对象。主事件循环通过调用QCoreApplication::exec() 启动, 随着QCoreApplication::exit()结束,本地的事件循环可用利用QEventLoop 构建。作为事件分发器的QAbstractEventDispatcher 管理着Qt 的事件队列,事件分发器从窗口系统或其他事件源接收事件,然后将他们发送给QCoreApplication 或QApplication 的实例进行处理或继续分发。QAbstractEventDispatcher 为事件分发提供了良好的保护措施。

事件与信号的区别

(1) 使用场合和时机不同一般情况下,在“使用”窗口部件时,我们经常需要使用信号,并且会遵循信号与槽的机制;而在“实现”窗口部件时,我们就不得不考虑如何处理事件了。举个例子,当使用QPushButton 时,我们对于它的clicked()信号往往更为关注,而很少关心促成发射该信号的底层的鼠标或者键盘事件。但是,如果要实现一个类似于QPushButton 的类,我们就需要编写一定的处理鼠标和键盘事件的代码,而且在必要的时候,仍然需要发射和接收clicked()信号。

(2)使用的机制和原理不同

事件类似于Windows 里的消息,它的发出者一般是窗口系统。相对信号和槽机制,它比较“底层”,它同时支持异步和同步的通信机制,一个事件产生时将被放到事件队列里,然后我们就可以继续执行该事件“后面”的代码。事件的机制是非阻塞的。

信号和槽机制相对而言比较“高层”,它的发出者一般是对象。从本质上看,它类似于传统的回调机制,是不支持异步调用的。

(3) 信号与槽在多线程时支持异步调用

在单线程应用时,你可以把信号与槽看成是一种对象间的同步通信机制,这是因为在这种情况下,信号的释放过程是阻塞的,一定要等到槽函数返回后这个过程才结束,也就是不支持异步调用。

参考链接

  1. 第13 章Qt 核心机制与原理,by wizardforcel.
  2. 如何才能学到Qt的精髓?,by zhihu.
  3. Qt中资源文件的使用及意义,by Stephan_zry.
  4. Qt:Qt资源系统,by qt技术开发老杰.

jsplumb学习笔记

发表于 2021-02-02 | 更新于 2021-11-27

jsPlumb 是一个用于在 html 元素之间生成连接线的 javascript 库。类似的工具还有 jointjs、GoJS、mxGraph 等。下面简单介绍jsplumb的相关内容。

基本概念

  • Souce 源节点
  • Target 目标节点
  • Anchor 锚点
  • Endpoint 端点
  • Connector 连接
  • Overlay 连接装饰

初始化

1
2
3
4
5
jsPlumb.ready(function() {
...
// your jsPlumb related init code goes here
...
});

创建实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
var firstInstance = jsPlumb.getInstance();

firstInstance.importDefaults({
Connector : [ "Bezier", { curviness: 150 } ],
Anchors : [ "TopCenter", "BottomCenter" ]
});

firstInstance.connect({
source:"element1",
target:"element2",
scope:"someScope"
});

var secondInstance = jsPlumb.getInstance({
PaintStyle:{
strokeWidth:6,
stroke:"#567567",
outlineStroke:"black",
outlineWidth:1
},
Connector:[ "Bezier", { curviness: 30 } ],
Endpoint:[ "Dot", { radius:5 } ],
EndpointStyle : { fill: "#567567" },
Anchor : [ 0.5, 0.5, 1, 1 ]
});

secondInstance.connect({
source:"element4",
target:"element3",
scope:"someScope"
});

参考链接

  1. jsplumb 中文基础教程,by wangduanduan.
  2. 记一次绘图框架技术选型: jsPlumb VS mxGraph,by yejinzhan.
  3. jsPlumb Community Edition,by jsplumbtoolkit.
  4. jsPlumb 文档翻译,by shawchen08.
  5. Vue.js 技术揭秘,by huangyi.
  6. jsPlumb Connection event is triggering more than once,by stackflow.

层次分析法与模糊综合评价法

发表于 2021-01-27 | 更新于 2021-07-12

层次分析法

层次分析法(英语:Analytic Hierarchy Process, AHP)为 1971 年Thomas L. Saaty (匹兹堡大学教授)所发展出来,主要应用在不确定情况下及具有多数个评估准则的决策问题上。 层次分析法发展的目的是将复杂的问题系统化,由不同层面给予层级分解,并透过量化的运算,找到脉络后加以综合评估。

方法介绍

层次分析法可以利用树状的层级结构,将复杂的决策问题在一个层级中区分为数个简单的子问题,并且每个子问题可以独立进行分析,这个层级中的子问题可以包含是任何类型的子问题,无论是有形的还是无形的,仔细计算的或者粗略估计的,理解清晰或模糊的,只要是用于最终决策的子问题都可以包括于此。

模糊综合评价法

模糊综合评价法(Fuzzy Comprehension Evaluation Method)是一种基于模糊数学的综合评价方法。该综合评价法根据模糊数学的隶属度理论把定性评价转化为定量评价,即用模糊数学对受到多种因素制约的事物或对象做出一个总体的评价。它具有结果清晰,系统性强的特点,能较好地解决模糊的、难以量化的问题,适合各种非确定性问题的解决。

参考链接

  1. 层次分析法的一个应用案例及R语言实现,by 曾广宇.
  2. 模糊综合评价法原理及实现,by BetterManPeter.
  3. 结合层次分析法和模糊综合评价法的评价方法-利用yaahp,by Jeff.
  4. 层次分析法(Analytic Hierarchy Process),by SuPhoebe.
  5. 数模系列(3):模糊综合评价法,by 00木水.
  6. 层次分析法,by wikipeida.
  7. 数学建模笔记——评价类模型之模糊综合评价,by 小白.

FlightGear在Windows的编译安装教程

发表于 2021-01-27

本文记录在Windows10平台上编译安装FlightGear的过程。

前提条件

按顺序安装如下软件:

  1. CMake for Windows。
  2. Microsoft Visual Studio 2017。
  3. QT5.9.8。QT安装步骤请参考QT5.11下载与安装教程,注意需选择安装MSVC 2017 64-bit编译器。
  4. Git。

获取源代码

1
2
3
4
5
6
7
8
9
git clone https://git.code.sf.net/p/flightgear/simgear simgear

git clone https://git.code.sf.net/p/flightgear/flightgear flightgear

git clone https://git.code.sf.net/p/flightgear/fgdata fgdata

git clone https://git.code.sf.net/p/flightgear/windows-3rd-party windows-3rd-party

git clone -b fgfs-342-1 https://github.com/zakalawe/osg.git osg

目录结构

采用如下目录结构组织源代码:

1
2
3
4
5
6
7
8
9
10
FlightGearBuild / (Main root directory)
fgdata / (FlightGear data files)
flightgear / (FlightGear sources)
install / (Directory where you will install the built binaries to)
launch.bat / (launch script if desired, see below)
osg / (OSG sources)
simgear / (SimGear sources)
windows-3rd-party /
build.bat / (build script, see below)
update.bat / (update script, see below)

编译链接

使用build.bat批处理脚本配置编译OSG, SG 和 FG。其内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
SET PATH=%PATH%;%ProgramFiles%\CMake\bin
SET QT5SDK64=C:\Qt\5.15.0\msvc2019_64
SET CMAKE_TOOLCHAIN="Visual Studio 16 2019"
SET ROOT_DIR=C:\path\to\FlightGearBuild

md osg-build
md simgear-build
md flightgear-build

cd %ROOT_DIR%\osg-build

cmake %ROOT_DIR%\osg -G %CMAKE_TOOLCHAIN% -A x64 ^
-DACTUAL_3RDPARTY_DIR=%ROOT_DIR%\windows-3rd-party/msvc140/3rdParty.x64 ^
-DCMAKE_RELWITHDEBINFO_POSTFIX:STRING= ^
-DOSG_USE_UTF8_FILENAME:BOOL=ON ^
-DWIN32_USE_MP:BOOL=ON ^
-DCMAKE_INSTALL_PREFIX:PATH=%ROOT_DIR%\install
cmake --build . --config RelWithDebInfo --target INSTALL

cd %ROOT_DIR%\simgear-build
cmake %ROOT_DIR%\simgear -G %CMAKE_TOOLCHAIN% -A x64 ^
-DOSG_FSTREAM_EXPORT_FIXED:BOOL=ON ^
-DCMAKE_INSTALL_PREFIX:PATH=%ROOT_DIR%\install
cmake --build . --config RelWithDebInfo --target INSTALL

cd %ROOT_DIR%\flightgear-build
cmake %ROOT_DIR%\flightgear -G %CMAKE_TOOLCHAIN% -A x64 ^
-DCMAKE_INSTALL_PREFIX:PATH=%ROOT_DIR%\install ^
-DCMAKE_PREFIX_PATH=%QT5SDK64% ^
-DOSG_FSTREAM_EXPORT_FIXED:BOOL=ON ^
-DENABLE_COMPOSITOR:BOOL=OFF

cmake --build . --config RelWithDebInfo --target INSTALL

pause

启动FlightGear

构建launch.bat批处理文件,启动FlightGear,其内容如下:

1
2
SET PATH=C:\path\to\FlightGearBuild\install\bin;C:\path\to\FlightGearBuild\windows-3rd-party\msvc140\3rdParty.x64\bin;C:\Qt\5.15.0\msvc2019_64\bin;%PATH%
fgfs.exe --launcher

参考链接

  1. Building using CMake - Windows,by flightgear.
  2. Visual Studio之RelWithDebInfo模式,“被忽视”的编译模式,by inter_peng.
  3. QT5.11下载与安装教程,by 杨书航.
  4. VS2017+Qt5.12环境搭建完美教程分享,by 小豆君的干货铺.

Protobuf学习笔记

发表于 2021-01-19 | 更新于 2024-09-26

Protocol Buffers是一种序列化数据结构的协议。对于透过管道(pipeline)或存储资料进行通信的程序开发上是很有用的。这个方法包含一个接口描述语言,描述一些数据结构,并提供程序工具根据这些描述产生代码,用于将这些数据结构产生或解析资料流。

安装配置

protobuf的github发布地址: https://github.com/protocolbuffers/protobuf/releases

protobuf的编译器叫protoc,在上面的网址中找到最新版本的安装包,下载安装。

这里下载的是:protoc-3.9.1-win64.zip , windows 64位系统版本的编译器,下载后,解压到你想要的安装目录即可。

提示:解压完成后,将 [protoc安装目录]/bin 路径添加到PATH环境变量中

打开cmd,命令窗口执行protoc命令,没有报错的话,就已经安装成功。

源码编译

因为需要protobuf的header、lib、dll用于测试,需要编译protobuf的源代码。请参考:

  • protobuf的编译和使用,在windows平台上
  • 如何在Qt下编译以及使用protobuf?【C++培训】
  • 解决protobuf的Undefined reference to google::protobuf cxx11

如果使用qt进行开发,请参考:

  • Qt MinGW版编译使用protobuf
  • QT中使用MinGW 编译的protobuf库–包含库的生成和使用

注意:项目依赖库使用的编译链接工具应与项目使用的编译链接工具相同。例如:都使用 msvc2017 64编译链接。或者考虑跨平台使用mingw 64编译链接。

创建 .proto 文件,定义数据结构

proto3 的语法请参考:

  • Protocol Buffers V3中文语法指南[翻译]

proto3 与 proto2 的区别请参考:

  • Protobuf 的 proto3 与 proto2 的区别

下面是一个proto简单示例:

1
2
3
4
5
6
7
8
9
10
11
// 指定protobuf的版本,proto3是最新的语法版本
syntax = "proto3";

package dream;

message helloworld
{
int32 id = 1; // ID
string str = 2; // str
int32 opt = 3; //optional field
}

将.proto文件,编译成指定语言类库

1
protoc -I=$SRC_DIR --cpp_out=$DST_DIR $SRC_DIR/addressbook.proto

在生成的头文件中添加如下宏定义:

1
#define PROTOBUF_USE_DLLS

测试程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// TestProtoBuffer.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <fstream>
#include "MessageStruct.pb.h"

int main()
{
Msg_Header msgHeader;
msgHeader.set_msgtype(1);
msgHeader.set_msgsubtype(2);

// Write the new address book back to disk.
fstream output("./log", ios::out | ios::trunc | ios::binary);

if (!msgHeader.SerializeToOstream(&output)) {
cerr << "Failed to write msg." << endl;
return -1;
}

Msg_Header msgHeaderFromFile1;
try {
string work_dir = "E:\\Target";
fstream input(work_dir+"\\Debug\\log", ios::in | ios::binary);
if (!msgHeaderFromFile1.ParseFromIstream(&input)) {
cerr << "Failed to parse address book." << endl;
return -1;
}
}
catch (exception ex) {
cout << ex.what() << endl;
}

std::cout << "文件反序列化!\n";
cout << msgHeaderFromFile1.msgtype() << endl;
cout << msgHeaderFromFile1.msgsubtype() << endl;

string testStr;
msgHeader.SerializeToString(&testStr);
Msg_Header msgHeaderFromFile2;
msgHeaderFromFile2.ParseFromString(testStr);

std::cout << "\n字符串反序列化!\n";
cout << msgHeaderFromFile2.msgtype() << endl;
cout << msgHeaderFromFile2.msgsubtype() << endl;
}

变量赋值

repeated类型变量赋值

  • protobuf中repeated类型变量与C++ vector类型变量的相互赋值方法

使用问题

Several shared object using same proto leading the the error: file already exists in database

解决方案请参考:

  • Protobuf 使用过程遇到的一个问题
  • vcpkg安装静态库/mtd/mt
  • Vcpkg使用MD运行时静态库——如何设置?

protobuf 解析错误 ParseFromArray 时返回false

可能的原因有:

  • 消息没有序列化,因此消息反序列化失败

解决方案请参考:

  • protobuf 解析错误 ParseFromArray 时返回false
  • protobuf的ParseFromArray 解析失败的问题

参考链接

  1. Protobuf 终极教程,by 鸟窝.
  2. Protobuf通信协议详解:代码演示、详细原理介绍等,by CPP加油站.
  3. Protocol Buffers V3中文语法指南[翻译],by 李文周.
  4. Qt中使用Protobuf简单案例(Windows + msvc),by WindSun.
  5. Protobuf 的 proto3 与 proto2 的区别,by huanggang982.
  6. 长文图解Google的protobuf思考、设计、应用,by 嵌入式客栈.
  7. protobuf优缺点及编码原理,by 牛奔.
  8. Protobuf 使用过程遇到的一个问题,by jashwang.

Win7下MongoDB数据库的安装方法

发表于 2021-01-19

在Windows7操作系统下安装MongoDB数据库,需要注意MongoDB数据库版本不能超过4.2。在安装过程中应选择“Complete”安装,并取消勾选“Install MongoDB Compass”。在安装MongoDB数据库之前,应先安装VC_redist程序,否则安装MongoDB数据库时将报如图1所示错误。

安装过程中MongoDB服务无法启动

图1 安装过程中MongoDB服务无法启动

参考链接

  1. Service ‘MongoDB Server’ (MongoDB) failed to start,by stackoverflow.
  2. 解决 Win10 安装 MongoDB 4.0 无法启动服务的问题( 踩了个大坑),by 蓝三金.

常用C++库记录

发表于 2021-01-17

常用的C/C++库可参考awesome-cpp。这里主要记录本人曾使用的C/C++库。

数学库

  • GSL

文件解析

  • Rapidcsv

图像处理

  • bitmap
  • FreeGlut

网络通信

  • clsocket

软件测试

  • Catch2

参考链接

  1. awesome-cpp,by fffaraz.

采用序列号保护机制的软件保护技术

发表于 2021-01-16

软件序列号保护是一种常用的软件保护机制,其具体流程如下图所示。

软件序列号保护流程

图1 软件序列号保护流程

参考链接

  1. 加密与解密,by 段钢.
  2. 软件保护技术概述,by wxam.
  3. 目前常见软件保护技术概述,by 顺其自然~.
  4. A simple software key useful to protect software components,by Manuele Sicuteri.

飞行仿真中飞行器控制器的设计方法

发表于 2021-01-03 | 更新于 2021-01-17

飞行仿真中飞行器控制器的设计方法可以参考Matlab中的示例。具体步骤如下:

  • 建立飞机动力学模型
  • 配平和线性化
  • 调参

参考链接

  1. Trimming and Linearization of the HL-20 Airframe,by mathworks.
  2. Angular Rate Control in the HL-20 Autopilot,by mathworks.

大文件上传与断点续传

发表于 2021-01-03

大文件上传与断点续传的原理和实现方法可参考字节跳动面试官:请你实现一个大文件上传和断点续传。

参考链接

  1. 字节跳动面试官:请你实现一个大文件上传和断点续传,by yeyan1996.
上一页1…242526…53下一页

Jack Huang

521 日志
67 标签
© 2025 Jack Huang
由 Hexo 强力驱动
|
主题 — NexT.Muse