今天用QT写了个扫雷小游戏,界面和功能都是按照WindowsXp下面的winmine来做的,因为需要Press,Release和Click这些事件,索性我就用QPushButton来做的那些小方块,把它们的Text设为空,只显示Icon,并用QGridLayout来对它们布局.但我想在QGridLayout对Button进行Resize后重新得到Button的Size,问题就出现了,我获取到了640*480这个大小.
一开始,我把setIcon放在了QPushButton的resizeEvent事件里,在Button的大小发生变化后重新Scale图片然后setIcon.
后来我发现因为所有的Button大小都一样,这样写每个Button都有一个独立的QPixmap,内存占用很大,于是我就准备把图片在Button Resize之后处理,然后一个个Blit到Button上,也就是所有Button只用了同一张图片.然后就有了下面这样的代码:
在更新完所有控件之后,我准备获取0号Button的Size来Scale图片,但是我发现虽然界面上看起来Button已经被布局管理器调整了大小,但如果此时我获取Button的属性,得到的却是640*480这个大小.
之后,我在qDebug()函数前面加了1秒的延时,这时再调用0号Button的Width()和Height()就能得到正确的大小了,为了进一步验证,我自己写了个类继承QPushButton,重写了resizeEvent函数,发现resizeEvent确实被调用了,而且确实是在qDebug()之后被调用的.也就是说,当执行到qDebug()这里时,QGridLayout其实还没有对其中的控件进行布局.由此可以猜测QGridLayout对控件的布局可能是异步进行的.
于是我Google了各种关键词,查了好多QT文档都没有发现什么结果(而且发现Nokia的QT网站进不去了).
什么也找不到,只能自己慢慢查了,下载了QT的源代码,我先查了QGridLayout的代码:
发现在它的底层调用了addChildWidget和addItem这两个方法.addItem是QGridLayout中的,所以应该是网格布局特有的一些东西,addChildWidget是继承来的,而且没有被重写.于是我又查了它的父类QLayout,找到了addChildWidget这个方法的实现:
addChildWidget会先把原来的对象重新设置父控件,并且根据它的isVisiable和isHidden等属性确定这个控件是否能显示出来,如果需要显示,会调用QMetaObject::invokeMethod这个方法.看到后面那个调皮的//show later,我就知道这应该就是异步调整控件的地方了.
查询了QMetaObject::invokeMethod这个方法,它可以调用一个object对象的SIGNAL或者SLOT方法(object是QT中所有对象的祖先类),第二个参数是方法名,第三个为调用方式,Qt:: QueuedConnection便是将调用事件加入到主事件队列中(后面还有一些可省略参数,具体大家可以查文档),到此总算是破案了,可以说QT所有基类是QLayout的布局管理器都是异步实现的控件调整.
由此我想到了之前用到的QT的HTTP请求也是异步进行的,弄成异步的好处当然是可以提升运行效率,但由于QT的文档各种不全(感觉不光是QT,所有C++库文档都不如Java来的好),有些细节的遗漏总是会造成奇怪的问题:)