Qt中QEvent和信号槽的区别
仔细来看,事件与信号其实并无多大差别,从我们对其需求上来说,都只要能注册事件或信号响应函数,在事件或信号产生时能够被通知到即可。但有一项区别在 于,事件处理函数的返回值是有意义的,我们要根据这个返回值来确定是否还要继续事件的处理,比如在QT中,事件处理函数如果返回true,则这个事件处理 已完成,QApplication会接着处理下一个事件,而如果返回false,那么事件分派函数会继续向上寻找下一个可以处理该事件的注册方法。信号处 理函数的返回值对信号分派器来说是无意义的。
评价:实际上信号-槽也可以异步的,这个bool connect ( const QObject * sender, const char * signal, const QObject * receiver, const char * method, Qt::ConnectionType type = Qt::AutoCompatConnection )
里面的Qt::AutoCompatConnection,可以根据实际情况确定
所有使用了信号和槽的类都必须包含 Q_OBJECT 宏,而且这个类必须从QObject类派生(直接或者间接派生)出来,
当一个signal被emit出来的时候,链接到这个signal的slot会立刻被调用,就好像是一个函数调用一样。当这件事情发生的时
候,signal和slot机制与GUI的事件循环完全没有关系,当所有链接到这个signal的slot执行完成之后,在 emit
代码行之后的代码会立刻被执行。当有多个slot链接到一个signal的时候,这些slot会一个接着一个的、以随机的顺序被执行。
Signal 代码会由 moc 自动生成,开发人员一定不能在自己的C++代码中实现它,并且,它永远都不能有返回值。
Slot其实就是一个普通的类函数,并且可以被直接调用,唯一特殊的地方是它可以与signal相链接。
C++的预处理器更改或者删除 signal, slot, emit 关键字,所以,对于C++编译器来说,它处理的是标准的C++源文件。
signal/slot
在底层会使用三种方式传递消息。bool QObject::connect ( const QObject * sender, const
char * signal, const QObject * receiver, const char * method,
Qt::ConnectionType type = Qt::AutoCompatConnection )
最后一个参数是就是传递消息的方式了,有四个取值:
1.Qt::DirectConnection
When emitted, the signal is immediately delivered to the slot.
假设当前有4个slot连接到QPushButton::clicked(bool),当按钮被按下时,QT就把这4个slot按连接的时间顺序调用一遍。显然这种方式不能跨线程(传递消息)。
When emitted, the signal is queued until the event loop is able to deliver it to the slot.
假 设当前有4个slot连接到QPushButton::clicked(bool),当按钮被按下时,QT就把这个signal包装成一个 QEvent,放到消息队列里。QApplication::exec()或者线程的QThread::exec()会从消息队列里取消息,然后调用 signal关联的几个slot。这种方式既可以在线程内传递消息,也可以跨线程传递消息。
Same as QueuedConnection, except that the current thread blocks until the slot has been delivered. This connection type should only be used for receivers in a different thread. Note that misuse of this type can lead to dead locks in your application.
与Qt::QueuedConnection类似,但是会阻塞等到关联的slot都被执行。这里出现了阻塞这个词,说明它是专门用来多线程间传递消息的。
Qt::AutoConnection
If
the signal is emitted from the thread in which the receiving object
lives, the slot is invoked directly, as with Qt::DirectConnection;
otherwise the signal is queued, as with Qt::QueuedConnection.
这种连接类型根据signal和slot是否在同一个线程里自动选择Qt::DirectConnection或Qt::QueuedConnection
这样看来,第一种类型的效率肯定比第二种高,毕竟第二种方式需要将消息存储到队列,而且可能会涉及到大对象的复制(考虑sig_produced(BigObject bo),bo需要复制到队列里)。
Qt的线程和 signal-slot
QThread 现在默认状态下就是有一个消息循环的,而且鼓励使用线程独立的消息循环 1.主线程会被次线程block吗? 不会,QObject::connectd的最后一个参数默认使用Qt::AutoConnection, 在多线程环境下,会自动选择Qt::QueuedConnection方式,除非强制使用Qt::BlockingQueuedConnection或者 Qt::DirectConnection, 否则线程不会block. 2.如果在单cpu里会被block吗,如果次线程里不断循环,低层会自动分配时间片给主线程吗? 线程和CPU个数没有关系,当然CPU多了,运行会更加流程,线程的资源分配(时间片)是由操作系统管理的,在各个系统上都是如此 3.这种方法和直接调用次线程中的方法,用mutex之类的来实现类似的循环,效率差别大吗? 执行效率上很难讲,但是mutex肯定是带来了开发效率的降低,你得小心翼翼地处理好各种临界资源的锁问题 4. 记得QT里讲过signal-slot只是多几个函数调用的差别 是的,signal-slot的这种轻微的效率损失在绝大多数情况下不是问题,除非过度使用,这一般是设计上的错误导致的 分享两篇文章,讲QThread和Qt的signal-slot的: 1篇是qt labs的博客上一篇 You are doing it wrong...(http://labs.trolltech.com/blogs/2010/06/17/youre-doing-it-wrong/), 这篇文章很有意思,其实很多人不知道原来QThread还可以这样用。 另外一篇是关于怎么利用Qt的signal-slot机制和QThread进行完全的lock free编程(http://thesmithfam.org/blog/2009 ... ti-threading-in-qt/)
多谢多谢,再学习去。 之所以问,是因为,我做了一个下载googlemap的程序,开始下载线程后,GUI就不动了,直到下载结束。虽然操作系统会分配时间,但是如果下载线程循环,而且优先级相同的话,是不是还是有影响?
我再看看哪里出问题了。 刚看了你上面的两个链接,真是切中要害阿,呵呵,一直就在琢磨这个一段时间了。 总结一下理解,看看对不对。 第一篇里,QThread里默认的run()已经启动了消息循环exec(). 当需要新线程时,应该,直接生成QThread, start()。至于功能,应该放在另外的类里,在调用start前设置好signal-slot,再moveToThread到 QThread中。 不用sunbclass QThread。 我一直或者重新实现run(),在run()里生成所有object, signal-slot, 或者如文中所说的QThread.moveToThread(QThread). 这两种都有不少问题,破坏OO的概念。 很清楚。
第二篇里讲了用线程间的signal-slot来同步,正好回答了之前的疑问,不过他使用qthread的方法恰恰违背了第一篇的思想。
引用:
原帖由 GPS 于 12-7-2010 12:40 发表 我再看看哪里出问题了。 刚看了你上面的两个链接,真是切中要害阿,呵呵,一直就在琢磨这个一段时间了。 总结一下理解,看看对不对。 第一篇里,QThread里默认的run()已经启动了消息循环exec(). 当需要新线程时 ...
引用:
To set the record straight: I’m not saying sub-classing QThread is wrong. This is also how Java does it (sort of), and many people have been doing this with Qt for a long time. What I’m saying is “wrong” is subclassing QThread, adding signal and slot code to that subclass, and then calling moveToThread(this); in the constructor of that subclass. This causes people lots of confusion from my experience and observation on the #qt channel and the qt-interest mailing list. And yes, the current docs are still a bit lacking, something that I am fully aware of (and take responsibility for). I’m planning on sitting down and fleshing them out, showing both ways of using QThread (with and without subclassing).
signal由具体对象发出,然后会马上交给由connect函数连接的slot进行处理;而对于事件,Qt使用一个事件队列对所有发出的事件进行维护,当新的事件产生时,会被追加到事件队列的尾部,前一个事件完成后,取出后面的事件进行处理。但是,必要的时候,Qt的事件也是可以不进入事件队列,而是直接处理的。并且,事件还可以使用“事件过滤器”进行过滤。总的来说,如果我们使用组件,我们关心的是信号槽;如果我们自定义组件,我们关心的是事件。因为我们可以通过事件来改变组件的默认操作。比如,如果我们要自定义一个QPushButton,那么我们就需要重写它的鼠标点击事件和键盘处理事件,并且在恰当的时候发出clicked()信号。
还记得我们在main函数里面创建了一个QApplication对象,然后调用了它的exec()函数吗?其实,这个函数就是开始Qt的事件循环。在执行exec()函数之后,程序将进入事件循环来监听应用程序的事件。当事件发生时,Qt将创建一个事件对象。Qt的所有事件都继承于 QEvent类。在事件对象创建完毕后,Qt将这个事件对象传递给QObject的event()函数。event()函数并不直接处理事件,而是按照事件对象的类型分派给特定的事件处理函数(event handler)。
在所有组件的父类QWidget中,定义了很多事件处理函数,如keyPressEvent()、
keyReleaseEvent()、mouseDoubleClickEvent()、mouseMoveEvent ()、mousePressEvent()、mouseReleaseEvent()等。这些函数都是protected virtual的,也就是说,我们应该在子类中重定义这些函数。
http://www.360doc.com/content/13/0313/11/9200790_271203586.shtml