大佬的QT武林秘籍(整理)

根据大佬的武林秘籍,整理出来一些网的时候自己可以直接查看
大佬主页:https://blog.csdn.net/feiyangqingyun?type=blog

1.QTimer::singleShot单次定时器和QMetaObject::invokeMethod

定时器是个好东西,学会好使用它,有时候用QTimer::singleShot单次定时器和
QMetaObject::invokeMethod可以解决意想不到的问题。比如在窗体初始化的时候加载一个耗时的操
作,很容易卡主界面的显示,要在加载完以后才会显示界面,这就导致了体验很卡不友好的感觉,此时
你可以将耗时的加载(有时候这些加载又必须在主线程,比如用QStackWidget堆栈窗体加载一些子窗
体),延时或者异步进行加载,这样就会在界面显示后去执行,而不是卡住主界面。

//异步执行load函数
QMetaObject::invokeMethod(this, "load", Qt::QueuedConnection);
//延时10毫秒执行load函数
QTimer::singleShot(10, this, SLOT(load()));

2.可以在pro文件中写上版本号、程序图标、产品名称、版权所有、文件说明等信息(Qt5才支持)


实在windows上就是qmake的时候会自动将此信息转换成rc文件。对于早期的Qt4版本你可以手动写rc
文件实现。

#程序版本
VERSION = 2025.10.01
#程序图标
RC_ICONS = main.ico
#产品名称
QMAKE_TARGET_PRODUCT = quc
#版权所有
QMAKE_TARGET_COPYRIGHT = feiyangqingyun
#文件说明
QMAKE_TARGET_DESCRIPTION = QQ: 517216493 WX: feiyangqingyun

3. 管理员运行程序,限定在MSVC编译器,在项目pro文件中增加如下代码。

QMAKE_LFLAGS += /MANIFESTUAC:"level='requireAdministrator' uiAccess='false'" #以
管理员运行
QMAKE_LFLAGS += /SUBSYSTEM:WINDOWS,"5.01" #VS2013 在XP运行

4.运行文件附带调试输出窗口

这个非常有用,很多时候当我们发布程序阶段,我们会遇到程序双击无法
运行也不报错提示(开发机器上一切正常),都不知道发生了什么,甚至任务管理器可以看到运行了但
是没有界面弹出来,此时就需要在项目的pro文件中加上一行CONFIG += console,带界面的程序也会
自动弹出调试窗口打印输出信息,方便找问题,一般没法正常运行的程序都会打印一些提示信息缺啥之
类的。

TEMPLATE = app
MOC_DIR = temp/moc
RCC_DIR = temp/rcc
UI_DIR = temp/ui
OBJECTS_DIR = temp/obj
#就是下面这行用来设置运行文件附带调试输出窗口
CONFIG += console

5.获取类的属性和方法

//拿到控件元对象
const QMetaObject *metaObject = widget->metaObject();
//所有属性的数量
int propertyCount = metaObject->propertyCount();
//propertyOffset是自定义的属性开始的位置
int propertyOffset = metaObject->propertyOffset();
//循环取出控件的自定义属性, int i = 0 表示所有属性
for (int i = propertyOffset; i < propertyCount; ++i) {
QMetaProperty metaProperty = metaObject->property(i);
const char *name = metaProperty.name();
const char *type = metaProperty.typeName();
QVariant value = widget->property(name);
qDebug() << name << type << value;
}
//所有方法的数量
int methodCount = metaObject->methodCount();
//methodOffset是自定义的方法开始的位置
int methodOffset = metaObject->methodOffset();
//循环取出控件的自定义方法, int i = 0 表示所有方法
for (int i = methodOffset; i < methodCount; ++i) {
QMetaMethod metaMethod = metaObject->method(i);
const char *name = metaMethod.name();
const char *type = metaMethod.typeName();
qDebug() << name << type;
}

6. 巧妙的使用 findChildren 可以查找该控件下的所有子控件

findChild 为查找单个

//查找指定类名objectName的控件
QList<QWidget *> widgets = fatherWidget.findChildren<QWidget *>("widgetname");
//查找所有QPushButton
QList<QPushButton *> allPButtons = fatherWidget.findChildren<QPushButton *>();
//查找一级子控件,不然会一直遍历所有子控件
QList<QPushButton *> childButtons = fatherWidget.findChildren<QPushButton *>
(QString(), Qt::FindDirectChildrenOnly);

7.判断编译器类型、编译器版本、操作系统

//GCC编译器
#ifdef __GNUC__
#if __GNUC__ >= 3 // GCC3.0 以上
//MSVC编译器
#ifdef _MSC_VER
#if _MSC_VER >=1000 // VC++4.0 以上
#if _MSC_VER >=1100 // VC++5.0 以上
#if _MSC_VER >=1200 // VC++6.0 以上
#if _MSC_VER >=1300 // VC2003 以上
#if _MSC_VER >=1400 // VC2005 以上
#if _MSC_VER >=1500 // VC2008 以上
#if _MSC_VER >=1600 // VC2010 以上
#if _MSC_VER >=1700 // VC2012 以上
#if _MSC_VER >=1800 // VC2013 以上
#if _MSC_VER >=1900 // VC2015 以上
//Visual Studio版本与MSVC版本号的对应关系
MSC 1.0 _MSC_VER == 100
MSC 2.0 _MSC_VER == 200
MSC 3.0 _MSC_VER == 300
MSC 4.0 _MSC_VER == 400
MSC 5.0 _MSC_VER == 500
MSC 6.0 _MSC_VER == 600
MSC 7.0 _MSC_VER == 700
MSVC++ 1.0 _MSC_VER == 800
MSVC++ 2.0 _MSC_VER == 900
MSVC++ 4.0 _MSC_VER == 1000 (Developer Studio 4.0)
MSVC++ 4.2 _MSC_VER == 1020 (Developer Studio 4.2)
MSVC++ 5.0 _MSC_VER == 1100 (Visual Studio 97 version 5.0)
MSVC++ 6.0 _MSC_VER == 1200 (Visual Studio 6.0 version 6.0)
MSVC++ 7.0 _MSC_VER == 1300 (Visual Studio .NET 2002 version 7.0)
MSVC++ 7.1 _MSC_VER == 1310 (Visual Studio .NET 2003 version 7.1)
MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005 version 8.0)
MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008 version 9.0)
MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010 version 10.0)
MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012 version 11.0)
MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013 version 12.0)
MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015 version 14.0)
MSVC++ 14.1 _MSC_VER == 1910 (Visual Studio 2017 version 15.0)
MSVC++ 14.11 _MSC_VER == 1911 (Visual Studio 2017 version 15.3)
MSVC++ 14.12 _MSC_VER == 1912 (Visual Studio 2017 version 15.5)
MSVC++ 14.13 _MSC_VER == 1913 (Visual Studio 2017 version 15.6)
MSVC++ 14.14 _MSC_VER == 1914 (Visual Studio 2017 version 15.7)
MSVC++ 14.15 _MSC_VER == 1915 (Visual Studio 2017 version 15.8)
MSVC++ 14.16 _MSC_VER == 1916 (Visual Studio 2017 version 15.9)
MSVC++ 14.2 _MSC_VER == 1920 (Visual Studio 2019 Version 16.0)
MSVC++ 14.21 _MSC_VER == 1921 (Visual Studio 2019 Version 16.1)
MSVC++ 14.22 _MSC_VER == 1922 (Visual Studio 2019 Version 16.2)
MSVC++ 14.30 _MSC_VER == 1930 (Visual Studio 2022 Version 17.0)
MSVC++ 14.31 _MSC_VER == 1931 (Visual Studio 2022 Version 17.1)
MSVC++ 14.32 _MSC_VER == 1932 (Visual Studio 2022 Version 17.2)
//Borland C++
#ifdef __BORLANDC__
//Cygwin
#ifdef __CYGWIN__
#ifdef __CYGWIN32__
//mingw
#ifdef __MINGW32__
//windows
#ifdef _WIN32 //32bit
#ifdef _WIN64 //64bit
#ifdef _WINDOWS //图形界面程序
#ifdef _CONSOLE //控制台程序
//Windows(95/98/Me/NT/2000/XP/Vista)和Windows CE都定义了
#if (WINVER >= 0x030a) // Windows 3.1以上
#if (WINVER >= 0x0400) // Windows 95/NT4.0以上
#if (WINVER >= 0x0410) // Windows 98以上
#if (WINVER >= 0x0500) // Windows Me/2000以上
#if (WINVER >= 0x0501) // Windows XP以上
#if (WINVER >= 0x0600) // Windows Vista以上
//_WIN32_WINNT 内核版本
#if (_WIN32_WINNT >= 0x0500) // Windows 2000以上
#if (_WIN32_WINNT >= 0x0501) // Windows XP以上
#if (_WIN32_WINNT >= 0x0600) // Windows Vista以上

8.在pro中判断Qt版本及构建套件位数

#打印版本信息
message(qt version: $$QT_VERSION)
#判断当前qt版本号
QT_VERSION = $$[QT_VERSION]
QT_VERSION = $$split(QT_VERSION, ".")
QT_VER_MAJ = $$member(QT_VERSION, 0)
QT_VER_MIN = $$member(QT_VERSION, 1)
#下面是表示 Qt5.5及以上版本
greaterThan(QT_VER_MAJ, 4) {
greaterThan(QT_VER_MIN, 4) {
#自己根据需要做一些处理
}}
#QT_ARCH是Qt5新增的,在Qt4上没效果
#打印当前Qt构建套件的信息
message($$QT_ARCH)
#表示arm平台构建套件
contains(QT_ARCH, arm) {}
#表示32位的构建套件
contains(QT_ARCH, i386) {}
#表示64位的构建套件
contains(QT_ARCH, x86_64) {}
#其实Qt内置了主版本号和子版本号变量
#判断当前qt版本号
message($$QT_ARCH : $$QT_VERSION -> $$QT_MAJOR_VERSION . $$QT_MINOR_VERSION)
#下面的含义是如果版本 < 4.8
lessThan(QT_MAJOR_VERSION, 5) {
lessThan(QT_MINOR_VERSION, 8) {
#这里放要做的处理
}}
#下面的含义是如果版本 < 5.12.0
REQ_QT_MAJOR = 5
REQ_QT_MINOR = 12
REQ_QT_PATCH = 0
lessThan(QT_MAJOR_VERSION, $$REQ_QT_MAJOR)|lessThan(QT_MINOR_VERSION,
$$REQ_QT_MINOR)|lessThan(QT_MINOR_VERSION, $$REQ_QT_PATCH) {
#这里放要做的处理
}
#下面的含义是如果版本 >= 5.5
greaterThan(QT_MAJOR_VERSION, 4) {
greaterThan(QT_MINOR_VERSION, 4) {
#这里放要做的处理
}}
//代码中判断版本不要太简单
#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0))
//这里放要做的处理
#endif
//下面表示 >= 5.0.0
#if QT_VERSION >= 0x050000
...
#endif
//下面表示 < 5.12.10
#if QT_VERSION < 0x050C0A
...
#endif

9.Qt最小化后恢复界面可能会出现假死冻结现象,加上代码

void showEvent(QShowEvent *e)
{
setAttribute(Qt::WA_Mapped);
QWidget::showEvent(e);
}

10.巧妙的用QEventLoop开启事件循环

可以使得很多同步获取返回结果而不阻塞界面。查看源码得知,
原来QEventLoop内部新建了线程执行。

QEventLoop loop;
connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();

11.使用QFile的过程中

不建议频繁的打开文件写入然后再关闭文件,比如间隔5ms输出日志,IO性能
瓶颈很大,这种情况建议先打开文件不要关闭,等待合适的时机比如析构函数中或者日期变了需要重新
变换日志文件的时候关闭文件。不然短时间内大量的打开关闭文件会很卡,文件越大越卡。

12.非常不建议tr中包含中文

尽管现在的新版Qt支持中文到其他语言的翻译,但是很不规范,也不知道
TMD是谁教的(后面发现我在刚学Qt的时候也发布了一些demo到网上也是tr包含中文的,当时就狠狠
的打了自己一巴掌),tr的本意是包含英文,然后翻译到其他语言比如中文,现在大量的初学者滥用
tr,如果没有翻译的需求,禁用tr,tr需要开销的,Qt默认会认为他需要翻译,会额外进行特殊处理。

13.使用QList、QStringList、QByteArray等链表或者数组的过程中

如果只需要取值,而不是赋
值,强烈建议使用 at() 取值而不是 [] 操作符,在官方书籍《C++ GUI Qt 4编程(第二版)》的书中有特
别的强调说明,此教材的原作者据说是Qt开发的核心人员编写的,所以还是比较权威,至于使用 at() 与
使用 [] 操作符速度效率的比较,网上也有网友做过此类对比。原文在书的212页,这样描述的:Qt对所
有的容器和许多其他类都使用隐含共享,隐含共享是Qt对不希望修改的数据决不进行复制的保证,为了
使隐含共享的作用发挥得最好,可以采用两个新的编程习惯。第一种习惯是对于一个(非常量的)向量
或者列表进行只读存取时,使用 at() 函数而不用 [] 操作符,因为Qt的容器类不能辨别 [] 操作符是否将
出现在一个赋值的左边还是右边,他假设最坏的情况出现并且强制执行深层赋值,而 at() 函数则不被允
许出现在一个赋值的左边。

14.安全的删除Qt的对象类

强烈建议使用deleteLater而不是delete,因为deleteLater会选择在合适的时
机进行释放,而delete会立即释放,很可能会出错崩溃。如果要批量删除对象集合,可以用
qDeleteAll,比如 qDeleteAll(btns);

15.打印double数值的时候发现精确少了

其实原始数据还是完整的精确度的,只是打印的时候优化成了三位,
如果要保证完整的精确度,可以调用 qSetRealNumberPrecision 函数设置精确度位数即可。

QString s1, s2;
s1 = "666.5567124";
s2.setNum(888.5632123, 'f', 7);
qDebug() << qSetRealNumberPrecision(10) << s1.toDouble() << s2.toDouble();

16. QString的replace函数

切记,他在返回替换后的新字符串的同时也会改变原字符串,
我的乖乖!

17.在不同的平台上文件路径的斜杠也是不一样的

比如linux系统一般都是 / 斜杠,而在windows上都是
两个反斜杠,Qt本身程序内部无论在win还是linux都支持 / 斜杠的路径,但是一些第三方库的话可能需
要转换成对应系统的路径,这就需要用到斜杠转换,Qt当然内置类方法。

QString path = "C:/temp/test.txt";
path = QDir::toNativeSeparators(path);
//输出 C:\\temp\\test.txt
QString path = "C:\\temp\\test.txt";
path = QDir::toNativeSeparators(path);
//输出 C:/temp/test.txt

18.巧用QMetaObject::invokeMethod方法可以实现很多效果

包括同步和异步执行,很大程度上解决了
跨线程处理信号槽的问题。比如有个应用场景是在回调中,需要异步调用一个public函数,如果直接调
用的话会发现不成功,此时需要使用 QMetaObject::invokeMethod(obj, “fun”,
Qt::QueuedConnection); 这种方式来就可以。
invokeMethod函数有很多重载参数,可以传入返回值和执行方法的参数等。
invokeMethod函数不仅支持槽函数还支持信号,而且这逼居然是线程安全的,可以在线程中放心使
用,牛逼!
测试下来发现只能执行signals或者slots标识的方法。
默认可以执行private(protected/public) slots下的函数,但是不能执行private(protected/public)下的
函数。
毛总补充:前提必须是slots或者signals标注的函数,不是标注的函数不在元信息导致无法查找,执行
之后会提示No such method。
2021-11-06补充:如果要执行private(protected/public)下的函数,需要函数前面加上 Q_INVOKABLE
关键字,今天又学到了,必须加鸡腿。
其实这样看下来,就是任何方法函数都能执行了,这就超越了private(protected/public)的权限限定
了,相当于一个类的私有函数用了 Q_INVOKABLE 关键字修饰也可以被 invokeMethod 执行,哇咔
咔。

//头文件声明信号和槽函数
signals:
void sig_test(int type,double value);
private slots:
void slot_test(int type, double value);
private:
Q_INVOKABLE void fun_test(int type, double value);
//构造函数关联信号槽
connect(this, SIGNAL(sig_test(int, double)), this, SLOT(slot_test(int,
double)));
//单击按钮触发信号和槽,这里是同时举例信号槽都可以
void MainWindow::on_pushButton_clicked()
{
QMetaObject::invokeMethod(this, "sig_test", Q_ARG(int, 66), Q_ARG(double,
66.66));
QMetaObject::invokeMethod(this, "slot_test", Q_ARG(int, 88), Q_ARG(double,
88.88));
QMetaObject::invokeMethod(this, "fun_test", Q_ARG(int, 99), Q_ARG(double,
99.99));
}
//会打印 66 66.66、88 88.88
void MainWindow::slot_test(int type, double value)
{
qDebug() << type << value;
}
//会打印 99 99.99
void MainWindow::fun_test(int type, double value)
{
qDebug() << type << value;
}

19.Qt的pro文件可以添加各种处理来使得配置更方便

比如指定输出文件路径等,这样就不会全部在一堆
编译生成的临时文件中找来找去。

#禁用qdebug打印输出
DEFINES += QT_NO_DEBUG_OUTPUT
#自定义define变量 可以在整个项目中使用
#pro文件可以这样判断 contains(DEFINES, videovlc) {}
#代码文件可以这样判断 #ifdef videovlc
DEFINES += videovlc1 videoffmpeg
#关闭编译警告提示 眼不见为净
CONFIG += warn_off
#指定编译生成的文件到temp目录 分门别类存储
MOC_DIR = temp/moc
RCC_DIR = temp/rcc
UI_DIR = temp/ui
OBJECTS_DIR = temp/obj
#指定编译生成的可执行文件到bin目录
DESTDIR = bin

20. 其他几点常规小经验,本人在这几个地方摔跤过很多次。

20.1.有返回值的函数,一定要主动return返回值,有部分编译器在没有返回值的情况下也能正常编译通过,

20.2.但是运行的时候会出问题,得不到想要的结果,因为没有return对应的值。

20.3.定义的局部变量,主动给定个初始值,是个必须养成的好习惯,不然编译器给的初始值很可能不是你想要的,比如int变量默认0,有时候随机变成一个很大的数值,bool变量的初始值不同编译器不同值,有些是true有些是false,主动给一个初始值更可靠。

20.4.某些函数参数很多,而且后期可能还会修改和增加,这就导致了源头修改以后,关联信号槽的地方也要修改,参数类型和位置必须保持完全一致,对应槽函数处理也要修改等,改动的工作量非常大而且极不友好,所以对于非固定参数的函数,建议用结构体,这样非常容易增加其他的参数,而且不用修改信号槽关联和信号槽函数定义等,比如学生信息表、商品信息表作为参数传输,最佳方案就是结构体。

你可能感兴趣的:(qt,开发语言)