前言
有时在窗体初始化的时候加载一个耗时的操作,很容易卡主界面的显示,要在加载完以后才会显示界面,这就导致了体验很卡不友好的感觉,此时你可以将耗时的加载延时或者异步进行加载,这样就会在界面显示后去执行,而不是卡住主界面。
博客转载至:https://blog.csdn.net/ligare/article/details/124406743
延时:可以考虑QTimer的单次触发静态函数QTimer::singleShot()
函数原型:void QTimer::singleShot ( int msec, QObject * receiver, const char * member ) [static]
示例1:时间到了之后,只会触发一次
#include <QApplication>
#include <QTimer>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QTimer::singleShot(600000, &app, SLOT(quit()));
...
return app.exec();
}
示例2:非阻塞方式延时
QEventLoop loop;
QTimer::singleShot(msec, &loop, SLOT(quit()));
loop.exec();
继续执行代码
//如果不是QTimer的单次触发事件,是其他的执行代码的话,可以用
loop.exit(); //退出事件循环
异步进行load函数:QMetaObject::invokeMethod(this, "load", Qt::QueuedConnection);
让耗时操作可以异步进行,不至于卡界面
使用方法,都是把耗时的操作放在对应的槽函数里执行。
补充说明:QMetaObject::invokeMethod静态函数可以同来调用一个Q_OBJECT对象的信号、槽、方法,其原型如下所示:(如果可以调用该成员,则返回True。如果没有这样的成员或参数不匹配,则返回FALSE。)
bool QMetaObject::invokeMethod (
QObject * obj,
const char * member,
Qt::ConnectionType type,
QGenericReturnArgument ret,
QGenericArgument val0 = QGenericArgument( 0 ),
QGenericArgument val1 = QGenericArgument(),
QGenericArgument val2 = QGenericArgument(),
QGenericArgument val3 = QGenericArgument(),
QGenericArgument val4 = QGenericArgument(),
QGenericArgument val5 = QGenericArgument(),
QGenericArgument val6 = QGenericArgument(),
QGenericArgument val7 = QGenericArgument(),
QGenericArgument val8 = QGenericArgument(),
QGenericArgument val9 = QGenericArgument() ) [static]
参数说明:
☺第一个参数是被调用对象的指针;
☺第二个参数是方法的名字;
☺第三个参数是连接类型。可以指定连接类型,来决定是同步还是异步调用。
☺第四个参数接收被调用函数的返回值;注意,如果调用是异步的,则无法计算返回值。
注意:传入的参数是有个数限制的,可以向成员函数传递最多十个参数(val0,val1,val2,val3,val4,val5,val6,val7,val8和val9)。
第三个参数类型区别:
Qt :: DirectConnection 会立即调用该成员
Qt :: QueuedConnection 将发送QEvent,并在应用程序进入主事件循环时立即调用该成员(异步)
Qt :: BlockingQueuedConnection 将以与Qt :: QueuedConnection相同的方式调用该方法,除了当前线程将阻塞直到事件被传递。使用此连接类型在同一线程中的对象之间进行通信将导致死锁。
Qt :: AutoConnection 则如果obj与调用者位于同一个线程中,则会同步调用该成员; 否则它将异步调用该成员。
QGenericArgument和QGenericReturnArgument是内部帮助器类。因为可以动态调用信号和槽,所以必须使用Q_ARG()和Q_RETURN_ARG()宏将参数括起来。
Q_ARG()接受类型名和该类型的常量引用;
Q_RETURN_ARG()接受类型名和非常数引用。您只需要将信号或槽的名称传递给此函数,而不是整个签名。例如,若要异步调用QPushButton上的AnimateClick()槽,请使用以下代码:
QMetaObject::InvokeMethod(pushButton,“AnimateClick”,Qt::QueuedConnection);
如对于带参数的槽函数调用示例,compute(QString, int, double):
QString retVal;
QMetaObject::invokeMethod(object, "compute", Qt::DirectConnection,
Q_RETURN_ARG(QString, retVal),
Q_ARG(QString, "sqrt"),
Q_ARG(int, 42),
Q_ARG(double, 9.7));
常用的形式
QMetaObject::invokeMethod(object, "methodName", Qt::QueuedConnection,
Q_ARG(type1, arg1),
Q_ARG(type2, arg2));
还有一种是没有第三个参数,也就是不指明信号与槽的连接方式。
注:因为上面所示的参数需要被在构建事件时进行硬拷贝,参数的自定义型别所对应的类需要提供一个共有的构造函数、析构函数以及拷贝构造函数。而且必须使用注册Qt型别系统所提供的qRegisterMetaType() 方法来注册这一自定义型别。
对于异步方法调用,参数必须是Qt的元对象系统已知的类型,因为Qt需要复制参数以将它们存储在幕后的事件中。
注:QMetaObject::InvokeMethod:无法处理未注册的数据类型,在调用InvokeMethod()之前,应该调用qRegisterMetaType()注册数据类型,示例:
typedef QString CustomString;
qRegisterMetaType<CustomString>("CustomString");
大佬经验补充:如果要执行private(protected/public)下的函数,需要函数前面加上 Q_INVOKABLE 关键字,private: Q_INVOKABLE void fun_test(int type, double value);
QMetaObject::invokeMethod(this, "fun_test", Q_ARG(int, 99), Q_ARG(double, 99.99));