<<<<<<< HEAD
介绍…

下载地址:https://download.qt.io/archive/qt/

QT三大核心机制

信号槽,原对象系统以及事件模型。
重载函数
connect(this, QOverload<>::of(&classname::signal), this, QOverload<>::of(&classname::slot))

信号-槽:
QueuedConntection,DirectionConnection, autoConnection, uniqueConnection, blockingQueue

元对象系统:
QObject, Q_OBJECT, moc元对象编译器。
事件模型:
事件的创建:鼠标事件,键盘事件,窗口调整事件,模拟事件;
事件的交付:QObject::event()交付事件;
事件循环模型:QCoreApplication::exec()启动,exit()结束;
QEventLoop

信号-槽

signal没有public,private以及protected之分,slot有。

要使用signal-slot的先决条件是继承QObject类,并在类声明中添加Q_OBJECT宏。

声明与实现

信号和槽的本质都是函数。

信号只有声明,不需要实现,moc编译器会自动生成实现。

触发信号时,不写emit关键字,直接调用信号函数,也是没有问题的。因为emit是个空的宏:

1
#define emit

Q_OBJECT宏

Q_OBJECT宏的展开如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public: \
QT_WARNINIG_PUSH \
Q_OBJECT_NO_OVERRIDE_WARNING \
static const QMetaObject staticMetaObject; \
virtual const QMetaObject *metaObject() const; \
virtual void *qt_metacast(const char *); \
virtual int qt_metacall(QMetaObject::Call, int, void **); \
QT_TR_FUNCTIONS \
private: \
Q_OBJECT_NO_ATTRIBUTES_WARNING \
Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **);
QT_WARNING_POP \
struct QPrivateSignal {}; \
QT_ANNOTATE_CLASS(qt_qobject, "")

声明了一个只读的静态成员变量staticMetaObject,以及3个public的成员函数。以及private的静态成员函数qt_static_metacall。

这些成员变量和函数的实现在moc生成的moc_***.cpp文件中。

staticMetaObject是一个结构体,用来存储类的信号,槽等元信息,并把qt_static_metacall静态函数作为指针存储起来。

qt_static_metacall函数提供了两种”元调用的实现”:

  • 如果是InvokeMetaMethod类型的调用,则直接把参数中的QObject对象,转换成类然后调用对应函数。
  • 如果是IndexOfMethod类型的调用,即获取元函数的索引号,计算调用函数的偏移并返回。

信号的触发

信号的实现,直接调用QMetaObject::activate函数,第三个参数表示函数的索引号。

QMetaObject::active函数的实现在QObject.cpp文件中,不同版本的实现,差异比较大,大致流程:

  1. 先找出与当前信号连接的所有的对象-槽函数,逐个处理:
    处理方式分为三种:
    1
    2
    3
    4
    5
    6
    7
    8
    if ((c->connectionType == Qt::AutoConnection && !receiverInSameThread) || (c->connectioinType == Qt::QueuecConnection)) {
    // 队列处理
    } else if (c->connectionType == Qt::BlockingQueuedConnection) {
    // 阻塞处理
    // 如果同线程,打印潜在的死锁
    } else {
    // 直接调用槽函数或会掉函数
    }
    receiverInSameThread表示当前线程id和接受信号的对象所在线程id是否相等。
  • 如果信号-槽的连接方式为QueuedConnection,不论是否在同一个线程,按队列处理。
  • 如果信号-槽的连接方式为Auto,且不在同一个线程,按队列处理。
  • 如果信号-槽的连接方式BlockingQueuedConnection,按阻塞处理。同一个线程,不要按阻塞处理,直接调用。如果走阻塞队列,则多了加锁的过程。如果槽中又发了同样的信号,就会出现死锁:加锁之后还未解锁,又来申请加锁。

队列处理,就是把槽函数的调用,转成QMetaCallEvent事件,通过QCoreApplication::postEvent放进事件循环。

槽和moc生成

slot函数我们自己实现了,moc不会做额外的处理,所以自动生成的moc_***.cpp文件中,只有Q_OBJECT宏的展开。

QT UI缩放

调用qputenv,如下:

1
2
qputenv("QT_SCALE_FACTOR", "0.6");
QGuiApplication app(argc, argv);

QT 关闭控制台

在CMakeLists.txt的工程下,添加WIN32选项,编译64位程序,也是用改项

1
2
3
4
5
6
7
add_executable(${PROJECT_NAME} WIN32
${SRCS}
resource/qml.qrc
resource/image.qrc
${CMAKE_CURRENT_SOURCE_DIR}/icon.rc
${QM_FILES}
)

QT 翻译

在CMakeLists.txt的工程下,添加以下,用于build ts文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
find_package(Qt5 COMPONENTS Core Quick Widgets LinguistTools Concurrent REQUIRED)

# compile language .ts to .qm
set(TS_FILES language/zh_CN.ts language/en.ts)
#qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES} OPTIONS -no-obsolete)
option(CREATE_TS_FILE "option for create ts files" OFF)
if (CREATE_TS_FILE)
qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES} OPTIONS -no-obsolete)
else()
qt5_add_translation(QM_FILES ${TS_FILES})
endif()

message("language qm: ${QM_FILES}")

更新翻译时使用:

1
lupdate ./ -ts language/en.ts -no-obsolete

./表示源码搜索目录,默认递归搜索,-ts指定输出的ts翻译文件.

-no-obsolete 放弃无效翻译。

QT文件注释设置

Tools->Options->Text Editor->Snippets,选择Text或者其他,输入框中输入,添加一个fileheader,选中fileheader,最小边的输入框中根据需要添加:

1
2
3
4
5
6
7
8
9
10
/****************************************************************************
* @project %{CurrentProject:Name}
* @file %{CurrentDocument:FileName}
* @brief
* @author
* @email
* @version
* @date %{CurrentDate:yyyy-MM-dd}
* Copyright (c) 2022 *** All rights reserved.
****************************************************************************/

QT CMakeLists.txt

Debug和Release模式选择拷贝dll

运行环境需要的dll和文件,可以在编译的时候使用cmake拷贝到exe相关的位置,在打包的时候直接打包整个Release文件夹即可,再CMakeLists.txt文件中添加:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
set(TARGET_NAME TestProject)
#set(CMAKE_PREFIX_PATH "C:/Qt/Qt5.14.1/5.14.1/msvc2017_64/lib/cmake/Qt5;${CMAKE_PREFIX_PATH}")
# 设置自定义的Qt路径
set(CMAKE_PREFIX_PATH "${QT_QML_DIR};${CMAKE_PREFIX_PATH}")

# 使用qt的windeployqt.exe工具拷贝相关的文件
add_custom_command(
TARGET ${TARGET_NAME}
POST_BUILD
COMMAND $<TARGET_FILE_DIR:Qt5::qmake>/windeployqt.exe $<TARGET_FILE:${TARGET_NAME}>
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/bin/${CMAKE_BUILD_TYPE}"
COMMENT "Start running windeployqt."
)

# 添加缺失的文件或者第三方的dll
add_custom_command(
TARGET ${TARGET_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory ${QT_QML_DIR}/qml $<TARGET_FILE_DIR:${TARGET_NAME}>/qml

COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_FILE_DIR:Qt5::qmake>/Qt5QuickControls2$<$<CONFIG:Debug>:d>.dll
$<TARGET_FILE_DIR:Qt5::qmake>/Qt5QuickTemplates2$<$<CONFIG:Debug>:d>.dll
$<TARGET_FILE_DIR:${TARGET_NAME}>
)

QDesktopServices::openUrl中文路径

1
QDesktopServices::openUrl(QUrl::fromLocalFile(save_file));

QPainter保存图像

利用QPixmap作为画板,并保存。

1
2
3
4
5
6
7
QPainter painter;
QPixmap map(400, 400);
map.fill(Qt::transparent);
painter.begin(&map);
painter.drawRect(10, 10, 50, 80);
painter.end();
map.save("output.png");