1、一念、一瞬、一弹指
古代的梵典《僧祗律》中有这样的记载:一刹那者为一念,二十念为一瞬,二十瞬为一弹指,二十弹指为一罗预,二十罗预为一须臾,一日一夜有三十须臾。根据时间推算,一须臾为48分钟,一弹指为7.2秒,一瞬为0.36秒,一念为0.018秒。
而我们在使用Qt做应用程序中,同步请求是必不可少的操作,但当同步请求耗时比较久呢?
理想状态下,页面之间的跳转需要在瞬间解决,而页面内的操作需要在刹那间完成。实际情况之下呢?似乎都没有那么在意,慢慢的,会给我们造成一种潜意识,软件,慢一点是正常。
是的,慢一点,也无可厚非,但这只是在普通的领域。如果涉及到交易呢?慢一点可能会造成不可估量的损失,毕竟只要涉及到钱的事,都会比较敏感。
为了给用户良好的体验,超过一弹指的请求需要有进度提示,并且能够随时终止或者取消。是的,曾经我也是追求请求的极致,现在,制造业软件中,总是觉得,时间久一点,也会心安理得。
2、解决卡死
同步请求呢,存在的意义就是必须要等这个请求完成才能进行下一步操作,否则那就死等。。。等的不耐烦了或者手贱一下,多点了几次,恐怖的事情就会发生,轻则卡死转圈圈,重则直接崩溃或者闪退。
别着急,经历的多了,也就麻木了。话虽这么说,该解决的问题还是要解决。
手边有一个任务,导入一份比较大的数据,需求呢:文件读取,3个服务器,串行导入,每个服务器都自己的db,并且共用一个SQLserver。
这个请求的耗时就会有点恐怖,为了防止操作人员不耐烦的点击和给他一个显眼的提示,巧妙的使用了Qt的多线程和一个交互,虽然没有提升性能,至少能防止界面卡死,并且能告诉操作人员需要等待。
交互是比较简单的交互,发起该请求之后,界面给一层蒙版,防止点击,再给一个不断循环的圈圈。
首先看下圈圈的实现:
#include <QDialog>
#include <QMovie>
namespace Ui {
class LoadingWidget;
}
class LoadingWidget : public QDialog
{
Q_OBJECT
public:
static LoadingWidget* instance(); //获取单实例
static void release(); //释放单实例;
void start();
void stop();
private:
explicit LoadingWidget(QWidget *parent = Q_NULLPTR);
~LoadingWidget();
void initPage();
private:
Ui::LoadingWidget* ui;
static LoadingWidget* m_pSelf;
QMovie* m_pMovie{ Q_NULLPTR };
};
实现呢,比较简单,就是找一个动画,做成动画。
void LoadingWidget::initPage()
{
setWindowOpacity(0.2);
setWindowFlags(Qt::FramelessWindowHint/* | Qt::WindowStaysOnTopHint */| Qt::WindowSystemMenuHint | Qt::SubWindow);
m_pMovie = new QMovie(":/LoadingWidget.gif");
ui->label->setMovie(m_pMovie);
}
void LoadingWidget::start()
{
m_pMovie->start();
exec(); // 启动之后阻塞
}
void LoadingWidget::stop()
{
m_pMovie->stop();
reject();
}
下面看下实际应用的过程:
一个同步请求,调用完成之后就不再需要占用资源,所以我们现在Qt中临时启动线程的方式来做。
#include <QtConcurrent/QtConcurrent>
#include <QFuture>
实际的调用过程:
QFuture<void> func = QtConcurrent::run(this, &TestWidget::importPointThread, path);
LoadingWidget::instance()->start();
上面我们就完成了防止界面卡死的操作,其中,&TestWidget::importPointThread
是需要执行的函数地址,path
是其入参,之后调用LoadingWidget::Instance()->start();
实现界面交互。