`
qimo601
  • 浏览: 3454586 次
  • 性别: Icon_minigender_1
  • 来自: 苏州
社区版块
存档分类
最新评论

乱谈Qt事件循环嵌套

    博客分类:
  • Qt
阅读更多

 转载:http://blog.csdn.net/dbzhang800/article/details/6889291

 

 

  • 本文旨在说明:QDialog::exec()、QMenu::exec()等开启的局部事件循环,易用的背后,还有很多的陷阱...

引子

Qt 是事件驱动的,基本上,每一个Qt程序我们都会通过QCoreApplication或其派生类的exec()函数来开启事件循环(QEventLoop):

int main(int argc, char**argv)
{
    QApplication a(argc, argv);
    return a.exec(); 
}

但是在同一个线程内,我们可以开启多个事件循环,比如通过:

  • QDialog::exec()
  • QDrag::exec()
  • QMenu::exec()
  • ...

这些东西都很常用,不是么?它们每一个里面都在执行这样的语句:

QEventLoop loop;     //事件循环
loop.exec();

既然是同一线程内,这些显然是无法并行运行的,那么只能是嵌套运行。

如何演示?

如何用最小的例子来直观说明这个问题呢?

利用定时器来演示应该是最方便的。于是,很容易写出来这样的代码:

#include <QtCore>

class Object : public QObject
{
public:
    Object() {startTimer(200); }

protected:
    void timerEvent(QTimerEvent *) {
        static int level = 0;
        qDebug()<<"Enter: <<"++level;
        QEventLoop loop;     //事件循环
        loop.exec();
        qDebug()<<"Leave: "<<level;
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    Object w;
    return a.exec();
}

然后我们可以期待看到:

Enter: 1
Enter: 2
Enter: 3
...

但是,很让人失望,这并不会工作。因为Qt对Timer事件派发时进行了处理:

  • 如果当前在处理Timer事件,新的Timer将不会被派发。

演示

我们对这个例子进行一点改进:

  • 收到Timer事件后,我们立即post一个自定义事件(然后我们在对自定义事件的相应中开启局部的事件循环)。这样Timer事件的相应可以立即返回,新的Timer事件可以持续被派发。

另外,为了友好一点,使用了 QPlainTextEdit 来显示结果:

#include <QtGui>
#include <QtCore>

class Widget : public  QPlainTextEdit
{
public:
    Widget() {startTimer(200); }

protected:
    bool event(QEvent * evt)
    {
        if (evt->type() == QEvent::Timer) {
            qApp->postEvent(this, new QEvent(QEvent::User));
        } else if (evt->type() == QEvent::User) {
            static int level = 0;
            level++;
            this->appendPlainText(QString("Enter : %1").arg(++level));
            QEventLoop loop;
            loop.exec();
            this->appendPlainText(QString("Leave: %1").arg(level));
        }
        return QPlainTextEdit::event(evt);
    }
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

有什么用?

这个例子确实没有什么,因为似乎没人会写这样的代码。

但是,当你调用

  • QDialog::exec()
  • QMenu::exec()
  • QDrag::exec()
  • ...

等函数时,实际上就启动了嵌套的事件循环,而如果不小心的话,还有遇到各种怪异的问题!

== QDialog::exec() vs QDialog::open()== 在 QDialog 模态对话框与事件循环 以及 漫谈QWidget及其派生类(四) 我们解释过QDialog::exec()。它最终就是调用QEventLoop::exec()。

QDialog::exec()这个东西是这么常用,以至于我们很少考虑这个东西的不利因素。QDialog::open()尽管被官方所推荐,但是似乎很少有人用,很多人可能还不知道它的存在。

但是Qt官方blog中:

一文介绍了 exec() 可能造成的危害,并鼓励大家使用 QDialog::open()

在Qt官方的Qt Quarterly中: * QtQuarterly30 之 New Ways of Using Dialogs 对QDialog::open()有详细的介绍

QDialog::open()劣势与优势

看个例子:我们通过颜色对话框选择一个颜色,

  • 使用静态函数,写法很简介(内部调用exec()启动事件循环)

 

void Widget::onXXXClicked()
{
    QColor c = QColorDialog::getColor();
}
  • 对话框的常见用法,使用exec()启动事件循环,很直观

 

void Widget::onXXXClicked()
{
    QColorDialog dlg(this);
    dlg.exec();
    QColor c = dlg.currentColor();
}
  • 使用open()函数,比较不直观(因为是异步,需要链接一个槽)

 

void Widget::onXXXClicked()
{
    QColorDialog *dialog = new QColorDialog;
    dialog->open(this, SLOT(dialogClosed(QColor)));
}
void Widget::dialogClosed(const QColor &color)
{
    QColor = color;
}

好处嘛(就摘录Andreas Aardal Hanssen的话吧):

  • By using open() instead of exec(), you need to write a few more lines of code (implementing the target slot). But what you gain is very significant: complete control over execution. Now, the event loop is no longer nested/reentered, you’re not blocking inside a magic exec() function

局部事件循环导致崩溃

Kde开发者官方blog中描述这个问题:

在某个槽函数中,我们通过QDialog::exec() 弹出一个对话框。

void ParentWidget::slotDoSomething() 
{
    SomeDialog dlg( this ); //分配在栈上的对话框
    if (dlg.exec() == QDialog::Accepted ) {
        const QString str = dlg.someUserInput();
        //do something with with str
    }
}

如果这时ParentWidget或者通过其他方式(比如dbus)得到通知,需要被关闭。会怎么样?

程序将崩溃:ParentWidget析构时,将会delete这个对话框,而这个对话框却在栈上。

简单模拟一下(在上面代码中加一句即可):

void ParentWidget::slotDoSomething() 
{
    QTimer::singleShot(1000, this, SLOT(deleteLater()));
...

这篇blog最终给出的结论是:将对话框分配到堆上,并使用QPointer来保存对话框指针。

上面的代码,大概要写成这样:

void ParentWidget::slotDoSomething() 
{
    QWeakPointer<SomeDialog> dlg = new SomeDialog(this);
    if (dlg.data()->exec() == QDialog::Accepted ) {
        const QString str = dlg.data()->someUserInput();
        //do something with with str
    } else if(!dlg) {
        //....
    }
    if (!dlg) {
        delete dlg.data();
    }
}

感兴趣的可以去看看原文。比较起来 QDialog::open() 应该更值得考虑。

QCoreApplication::sendPostedEvents()

当程序做繁重的操作时,而又不愿意开启一个新线程时,我们都会选择调用

 QCoreApplication::sendPostedEvents ()

来使得程序保持相应。这和前面提到的哪些exec()开启局部事件循环的效果其实是完全一样的。

无论是这个,还是QEventLoop::exec()最终都是调用:

QAbstractEventDispatcher::processEvents()

来进行事件派发。前面的问题也都是由它派发的事件引起的。

参考

分享到:
评论

相关推荐

    MTPA数值求解:双法探究,MTPA数值求解详解:两种方法的比较与应用探索,MTPA数值求解两种方法 ,MTPA数值求解; 方法一; 方法二;,MTPA数值求解的两种高效方法

    MTPA数值求解:双法探究,MTPA数值求解详解:两种方法的比较与应用探索,MTPA数值求解两种方法 ,MTPA数值求解; 方法一; 方法二;,MTPA数值求解的两种高效方法

    为什么你的switch总出bug?90%新手不知道的break语句隐藏规则.pdf

    # 踏入C语言的奇妙编程世界 在编程的广阔宇宙中,C语言宛如一颗璀璨恒星,以其独特魅力与强大功能,始终占据着不可替代的地位。无论你是编程小白,还是有一定基础想进一步提升的开发者,C语言都值得深入探索。 C语言的高效性与可移植性令人瞩目。它能直接操控硬件,执行速度快,是系统软件、嵌入式开发的首选。同时,代码可在不同操作系统和硬件平台间轻松移植,极大节省开发成本。 学习C语言,能让你深入理解计算机底层原理,培养逻辑思维和问题解决能力。掌握C语言后,再学习其他编程语言也会事半功倍。 现在,让我们一起开启C语言学习之旅。这里有丰富教程、实用案例、详细代码解析,助你逐步掌握C语言核心知识和编程技巧。别再犹豫,加入我们,在C语言的海洋中尽情遨游,挖掘无限可能,为未来的编程之路打下坚实基础!

    避坑指南:DeepSeekAPI常见错误代码解析与解决方案.pdf

    在日常的工作和学习中,你是否常常为处理复杂的数据、生成高质量的文本或者进行精准的图像识别而烦恼?DeepSeek 或许就是你一直在寻找的解决方案!它以其高效、智能的特点,在各个行业都展现出了巨大的应用价值。然而,想要充分发挥 DeepSeek 的优势,掌握从入门到精通的知识和技能至关重要。本文将从实际应用的角度出发,为你详细介绍 DeepSeek 的基本原理、操作方法以及高级技巧。通过系统的学习,你将能够轻松地运用 DeepSeek 解决实际问题,提升工作效率和质量,让自己在职场和学术领域脱颖而出。现在,就让我们一起开启这场实用又高效的学习之旅吧!

    视频缩略图生成组件,图像格式转换另存为jpg, png, gif, bmp格式

    'Function 生成视频缩略图(ByVal 视频文件 As String, ByVal 保存缩略图的文件路径 As String, Optional ByVal jpg图像品质 As Long = 80, _ ' Optional ByVal 缩略图宽度 As Long = 500, Optional ByVal 缩略图高度 As Long = 500 _ ' , Optional 返回图像实际宽度 As Long, Optional 返回图像实际高度 As Long) As Boolean Public Function SaveImageAs(LoadImgFile As String, ByVal SaveAsImgFile As String, _ Optional ByVal JpgQuality As Long = 80, Optional hPal As Long, Optional Resolution As Single) As Boolean

    浏览器插件开发:基于DeepSeekAPI的沉浸式翻译工具实战.pdf

    在日常的工作和学习中,你是否常常为处理复杂的数据、生成高质量的文本或者进行精准的图像识别而烦恼?DeepSeek 或许就是你一直在寻找的解决方案!它以其高效、智能的特点,在各个行业都展现出了巨大的应用价值。然而,想要充分发挥 DeepSeek 的优势,掌握从入门到精通的知识和技能至关重要。本文将从实际应用的角度出发,为你详细介绍 DeepSeek 的基本原理、操作方法以及高级技巧。通过系统的学习,你将能够轻松地运用 DeepSeek 解决实际问题,提升工作效率和质量,让自己在职场和学术领域脱颖而出。现在,就让我们一起开启这场实用又高效的学习之旅吧!

    bkall_answers(2).json

    bkall_answers(2).json

    从零实现多轮对话:DeepSeek上下文推理模式开发手册.pdf

    在日常的工作和学习中,你是否常常为处理复杂的数据、生成高质量的文本或者进行精准的图像识别而烦恼?DeepSeek 或许就是你一直在寻找的解决方案!它以其高效、智能的特点,在各个行业都展现出了巨大的应用价值。然而,想要充分发挥 DeepSeek 的优势,掌握从入门到精通的知识和技能至关重要。本文将从实际应用的角度出发,为你详细介绍 DeepSeek 的基本原理、操作方法以及高级技巧。通过系统的学习,你将能够轻松地运用 DeepSeek 解决实际问题,提升工作效率和质量,让自己在职场和学术领域脱颖而出。现在,就让我们一起开启这场实用又高效的学习之旅吧!

    安全防护全攻略:DeepSeekAPI密钥管理与请求限流最佳实践.pdf

    在日常的工作和学习中,你是否常常为处理复杂的数据、生成高质量的文本或者进行精准的图像识别而烦恼?DeepSeek 或许就是你一直在寻找的解决方案!它以其高效、智能的特点,在各个行业都展现出了巨大的应用价值。然而,想要充分发挥 DeepSeek 的优势,掌握从入门到精通的知识和技能至关重要。本文将从实际应用的角度出发,为你详细介绍 DeepSeek 的基本原理、操作方法以及高级技巧。通过系统的学习,你将能够轻松地运用 DeepSeek 解决实际问题,提升工作效率和质量,让自己在职场和学术领域脱颖而出。现在,就让我们一起开启这场实用又高效的学习之旅吧!

    实时对话系统构建:WebSocket长连接下的API稳定性保障方案.pdf

    在日常的工作和学习中,你是否常常为处理复杂的数据、生成高质量的文本或者进行精准的图像识别而烦恼?DeepSeek 或许就是你一直在寻找的解决方案!它以其高效、智能的特点,在各个行业都展现出了巨大的应用价值。然而,想要充分发挥 DeepSeek 的优势,掌握从入门到精通的知识和技能至关重要。本文将从实际应用的角度出发,为你详细介绍 DeepSeek 的基本原理、操作方法以及高级技巧。通过系统的学习,你将能够轻松地运用 DeepSeek 解决实际问题,提升工作效率和质量,让自己在职场和学术领域脱颖而出。现在,就让我们一起开启这场实用又高效的学习之旅吧!

    结构化输出进阶指南:深度解析DeepSeekAPI响应数据处理技巧.pdf

    在日常的工作和学习中,你是否常常为处理复杂的数据、生成高质量的文本或者进行精准的图像识别而烦恼?DeepSeek 或许就是你一直在寻找的解决方案!它以其高效、智能的特点,在各个行业都展现出了巨大的应用价值。然而,想要充分发挥 DeepSeek 的优势,掌握从入门到精通的知识和技能至关重要。本文将从实际应用的角度出发,为你详细介绍 DeepSeek 的基本原理、操作方法以及高级技巧。通过系统的学习,你将能够轻松地运用 DeepSeek 解决实际问题,提升工作效率和质量,让自己在职场和学术领域脱颖而出。现在,就让我们一起开启这场实用又高效的学习之旅吧!

    清华出品AI教程(15天熟练掌握DeepSeek)

    近日,一份由清华大学团队发布《DeepSeek:从入门到精通》的AI学习教程冲上了热搜,它是由清华大学新闻与传播学院新媒体研究中心元宇宙文化实验室的余梦珑博士后及其团队倾力打造,从三个方面深入剖析了DeepSeek,DeepSeek是什么?有什么用?怎么使用? 详细论述了其应用场景与使用方法,并讲解了如何通过设计精妙的提示语来提升AI的使用效率,以及丰富的实例干货。 全部104页,完整版资料已经帮大家整理好了,免费领取 资料链接: https://pan.quark.cn/s/be3b500c539c

    异常处理大全:DeepSeekAPI调用中的10个常见错误与修复方案.pdf

    在日常的工作和学习中,你是否常常为处理复杂的数据、生成高质量的文本或者进行精准的图像识别而烦恼?DeepSeek 或许就是你一直在寻找的解决方案!它以其高效、智能的特点,在各个行业都展现出了巨大的应用价值。然而,想要充分发挥 DeepSeek 的优势,掌握从入门到精通的知识和技能至关重要。本文将从实际应用的角度出发,为你详细介绍 DeepSeek 的基本原理、操作方法以及高级技巧。通过系统的学习,你将能够轻松地运用 DeepSeek 解决实际问题,提升工作效率和质量,让自己在职场和学术领域脱颖而出。现在,就让我们一起开启这场实用又高效的学习之旅吧!

    冰点下载器珍藏版.zip

    冰点下载器珍藏版.zip

    Wallpaper Engine 壁纸一键提取

    Wallpaper Engine 是一款广受欢迎的动态壁纸软件,允许用户将各种动态、交互式壁纸应用到桌面上。其丰富的创意工坊内容让用户可以轻松下载和分享个性化的壁纸。而“一键提取”功能则是 Wallpaper Engine 中一个非常实用的工具,能够帮助用户快速提取和保存壁纸资源,方便后续使用或分享。

    基于碳达峰碳中和战略目标的城市园林绿化碳汇系统建设.pdf

    科研人员

    成本降低90%!DeepSeek与LangChain联动的API调用效率优化实验.pdf

    在日常的工作和学习中,你是否常常为处理复杂的数据、生成高质量的文本或者进行精准的图像识别而烦恼?DeepSeek 或许就是你一直在寻找的解决方案!它以其高效、智能的特点,在各个行业都展现出了巨大的应用价值。然而,想要充分发挥 DeepSeek 的优势,掌握从入门到精通的知识和技能至关重要。本文将从实际应用的角度出发,为你详细介绍 DeepSeek 的基本原理、操作方法以及高级技巧。通过系统的学习,你将能够轻松地运用 DeepSeek 解决实际问题,提升工作效率和质量,让自己在职场和学术领域脱颖而出。现在,就让我们一起开启这场实用又高效的学习之旅吧!

    13考试真题最近的t66.txt

    13考试真题最近的t66.txt

    对外承包项目借款合同2[示范文本].doc

    对外承包项目借款合同2[示范文本].doc

    性能对比测评:DeepSeek-V3与GPT-4接口的响应速度实测.pdf

    在日常的工作和学习中,你是否常常为处理复杂的数据、生成高质量的文本或者进行精准的图像识别而烦恼?DeepSeek 或许就是你一直在寻找的解决方案!它以其高效、智能的特点,在各个行业都展现出了巨大的应用价值。然而,想要充分发挥 DeepSeek 的优势,掌握从入门到精通的知识和技能至关重要。本文将从实际应用的角度出发,为你详细介绍 DeepSeek 的基本原理、操作方法以及高级技巧。通过系统的学习,你将能够轻松地运用 DeepSeek 解决实际问题,提升工作效率和质量,让自己在职场和学术领域脱颖而出。现在,就让我们一起开启这场实用又高效的学习之旅吧!

    基于java捷邻小程序.zip

    借助java编程语言和MySQL数据库等实现系统的全部功能,接下来对系统进行测试,测试系统是否有漏洞和测试用户权限来完善系统,最终系统完成达到相关标准。 本系统主要包括管理员和用户两个角色;主要包括首页、个人中心、用户管理、商品分类管理、商品信息管理、促销产品管理、系统管理、订单管理等功能的管理系统。 系统权限按管理员和用户这两类涉及用户划分。 (1)管理员功能需求 管理员登陆后,主要包括首页、个人中心、用户管理、商品分类管理、商品信息管理、促销产品管理、系统管理、订单管理等功能。 2)用户功能需求 用户登陆后进入小程序首页,可以实现首页、商品信息、促销产品、购物车、我的等,在我的页面可以对个人中心、我的收藏管理、用户充值、购物车、我的订单等功能进行详细操作。

Global site tag (gtag.js) - Google Analytics