一、详细描述
QWidget类是所有用户界面对象的基类。通俗的来讲,Qt基本上所有的UI类都是由QWidget继承出来的,而QWidget继承于QObject,大家可以查阅Qt source 即可发现一些微妙的写法,如这篇文章有详细介绍:Qt 库对象数据的声明和使用
窗口层次
窗口部件是用户界面的一个原子:它从窗口系统接收鼠标、键盘和其它事件,并且在屏幕上绘制自己的表现。每一个窗口部件都是矩形,并且它们按Z轴顺序排列的。一个窗口部件可以被它的父窗口部件或者它前面的窗口部件盖住一部分。
QDialog是最普通的***窗口。不被嵌入到一个父窗口部件的窗口部件被叫做***窗口部件。通常情况下,***窗口部件是有框架和标题栏的窗口(尽管如果使用了一定的窗口部件标记,创建***窗口部件时也可能没有这些装饰。)在Qt中,QMainWindow和和不同的QDialog的子类是最普通的***窗口。一个没有父窗口部件的窗口部件一直是***窗口部件。非***窗口部件是子窗口部件。它们是它们的父窗口部件中的子窗口。你通常不能在视觉角度从它们的父窗口部件中辨别一个子窗口部件。在Qt中的绝大多数其它窗口部件仅仅作为子窗口部件才是有用的。(当然把一个按钮作为或者叫做***窗口部件也是可能的,但绝大多数人喜欢把他们的按钮放到其它按钮当中,比如 QDialog。)
QWidget有很多成员函数,但是它们中的一些有少量的直接功能:例如,QWidget有一个字体属性,但是它自己从来不用。有很多继承它的子类提供了实际的功能,比如QPushButton、QListBox和QTabDialog等等。
每一个窗口部件构造函数接受一个或两个标准参数:
1. QWidget *parent = 0是新窗口部件的父窗口部件。如果为0(默认),新的窗口部件将是一个***窗口部件。如果不是,它将会使parent的一个孩子,并且被parent的几何形状所强迫(除非你指定WType_TopLevel作为窗口部件标记)。
2. WFlags f = 0(在可用的情况下)设置窗口部件标记,默认设置对于几乎所有窗口部件都是适用的,但是,举例来说,一个没有窗口系统框架的***窗口部件,你必须使用特定的标记。
二、Top-Level 属性以及孩子构件
A widget without a parent widget is always an independent window (top-level widget). For these widgets,一个没有父构件的构件通常是top-level 属性的窗口。对于这类构件,setWindowTitle() 和 setWindowIcon() 都是有效的。
三、QWidget事件简介
基本事件
paintEvent() – 只要窗口部件需要被重绘就被调用。每个要显示输出的窗口部件必须实现它并且不在paintEvent()之外在屏幕上绘制是明智的。
resizeEvent() – 当窗口部件被重新定义大小时被调用。
mousePressEvent() – 当鼠标键被按下时被调用。有六个鼠标相关事件,但是鼠标按下和鼠标释放事件是到目前为止最重要的。当鼠标在窗口部件内或者当它使用grabMouse()来捕获鼠标时,它接收鼠标按下事件。
mouseReleaseEvent() – 当鼠标键被释放时被调用。当窗口部件已经接收相应的鼠标按下事件时,它接收鼠标释放事件。这也就是说如果用户在你的窗口部件内按下鼠标,然后拖着鼠标到其它某个地方,然后释放,你的窗口部件接收这个释放事件。这里有一个例外:如果出现在弹出菜单中,当鼠标键被按下时,这个弹出菜单立即会偷掉这个鼠标事件。
mouseDoubleClickEvent() – 和它看起来也许不太一样。如果用户双击,窗口部件接收一个鼠标按下事件(如果他们没有拿牢鼠标,也许会出现一个或两个鼠标移动事件)、一个鼠标释放事件并且最终是这个事件。直到你看到第二次点击是否到来之前,不能从一个双击中辨别一个点击。(这是为什么绝大多数图形用户界面图书建议双击是单击的一个扩展,而不是一个不同行为的触发的一个原因。)
如果你的窗口部件仅仅包含子窗口部件,你也许不需要实现任何一个事件处理器。如果你想检测在子窗口部件中的鼠标点击,请在父窗口部件的mousePressEvent()中调用子窗口部件的hasMouse()函数。
接收键盘的窗口部件需要重新实现一些更多的事件处理器:
keyPressEvent() – 只要键被按下和当键已经被按下足够长的时间可以自动重复了就被调用。注意如果Tab和Shift+Tab键被用在焦点变换机制中,它们仅仅被传递给窗口部件。为了强迫那些键被你的窗口部件处理,你必须重新实现QWidget::event()。
focusInEvent() – 当窗口部件获得键盘焦点(假设你已经调用setFocusPolicy())时被调用。写得好的窗口部件意味着它们能按照一种清晰但谨慎的方式来获得键盘焦点。
focusOutEvent() – 当窗口部件失去键盘焦点时被调用。
一些窗口部件也许需要实现一些不太普通的事件处理器:
mouseMoveEvent() – 只要当鼠标键被按下时鼠标移动就会被调用。举例来说,对于拖动,这个很有用。如果你调用setMouseTracking(TRUE),尽管没有鼠标键被按下,你也会获得鼠标移动事件。(注意这个使用鼠标跟踪的应用程序在低下的X连接下不是很有用。)(也可以参考拖放信息。)
keyReleaseEvent() – 只要键被释放和当如果这个键是自动重复的并且被按下一段时间时就被调用。在这种情况下窗口部件接收一个键释放事件并且对于每一个重复立即有一个键按下事件。注意如果Tab和Shift+Tab键被用在焦点变换机制中,它们仅仅被传递给窗口部件。为了强迫那些键被你的窗口部件处理,你必须重新实现QWidget::event()。
wheelEvent() — 当窗口部件拥有焦点时,只要用户转动鼠标滚轮就被调用。
enterEvent() – 当鼠标进入这个窗口部件屏幕空间时被调用。(这不包括被这个窗口部件的子窗口部件所拥有的屏幕空间。)
leaveEvent() – 当鼠标离开这个窗口部件的屏幕空间时被调用。
moveEvent() – 当窗口部件相对于它的父窗口部件已经被移动时被调用。
closeEvent() – 当用户关闭窗口部件时(或这当close()被调用时)被调用。
这里还有一些不太明显的事件。它们在qevent.h中被列出并且你需要重新实现event()来处理它们。event()的默认实现处理Tab和Shift+Tab(移动键盘焦点)并且其它绝大多数事件给上面提到的一个或更多的特定处理器。
#p#
四、Window flag标识
关于QWidget 的flag 的介绍: enum Qt::WindowType flags Qt::WindowFlags 这两个参数参阅官方文档。
Qt 的 WindowFlags 有很多,实际使用时,若不关心窗口层次的话,大可不比太关心这个。比如说在Window 上做应用开发,大可只关注:Qt::Dialog,Qt::Tool,Qt::Window 即可。但若是做嵌入式开发就得好好看看这个属性,整理好这部分属性,有利于窗口管理。
五、着重介绍几个重要成员函数
- bool QWidget::close () [slot]
关闭这个窗口部件。如果窗口部件被关闭,返回真,否则返回假。首先它发送给这个窗口部件一个QCloseEvent。如果它接收这个关闭事件,它就被隐藏了。QWidget::closeEvent()的默认实现是接收这个关闭事件。当***一个可视的***窗口部件被关闭,QApplication::lastWindowClosed()信号被发射。
注意窗口的enum Qt::WidgetAttribute 属性,窗口默认属性是Qt::WA_MacOpaqueSizeGrip,设置上这个属性意味着窗口调用close()只销毁了UI想关,QWidget内还有很多内存空间没有释放,需调用delete 销毁QWidget 。若需要在调用close时一并销毁窗口可以给窗口设置Qt::WA_DeleteOnClose属性。
- bool QWidget::event ( QEvent * e ) [虚 保护]
这是主事件处理器,它处理事件e。你可以在子类中被重新实现整个函数,但是我们建议你使用一个特定的事件处理器来替代它。
主事件首先把事件传递给所有已经被安装的事件过滤器。如果没有过滤器中途截取这个事件,它调用一个特定的事件处理器。
键按下和释放事件被处理得和其它事件不同。event()检查Tab和Shift+Tab并且试图适当地移动焦点。如果没有窗口部件被焦点移入(或者键按下不是Tab或Shift+Tab),event()调用keyPressEvent()。
如果它能够把一个事件传递给没个东西,这个函数就返回真,否则如果没有任何东西想要这个事件,返回假。
也可以参考closeEvent()、focusInEvent()、focusOutEvent()、enterEvent()、keyPressEvent()、keyReleaseEvent()、leaveEvent()、mouseDoubleClickEvent()、mouseMoveEvent()、mousePressEvent()、 mouseReleaseEvent()、moveEvent()、paintEvent()、resizeEvent()、QObject::event()和QObject::timerEvent()。
- void QWidget::paintEvent ( QPaintEvent * ) [虚 保护]
这个事件处理器可以在子类中被重新实现来接收绘制事件。
绘制事件就是重新绘制这个窗口部件的所有部分的一个请求。它可以是repaint()或update()的结果,或者因为这个窗口部件原来被变暗并且现在已经不再被覆盖了,或者其它很多原因。
很多窗口部件在当它们被请求时,它们很简单地重新绘制整个界面,但是一些比较慢的窗口部件需要通过仅仅绘制被请求的区域QPaintEvent::region()进行优化。这个速度优化不会改变结果,在事件处理过程中,绘制仅仅发生在被改变的区域中。例如,QListView和QCanvas就是这样做的。
Qt也试图通过把多个绘制事件合并为一个来加快绘制速度。当update()被调用几次或者窗口系统发送几次绘制事件,Qt把它们合并为一个比较大区域(请参考QRegion::unite())的一个事件中。repaint()不允许这样优化,所以只要可能我们建议使用update()。
当绘制事件发生,更新区域通常被擦除,所以你正在这个窗口部件的背景上绘制。这里有一些例外并且QPaintEvent::erased()告诉你这个窗口部件是否被擦除。
背景可以通过使用setBackgroundMode()、setPaletteBackgroundColor()或setBackgroundPixmap()来设置。setBackgroundMode()的文档详细描述了背景,我们建议你去读一下。
- void QWidget::raise () [槽]
把这个窗口部件升高到它的父窗口部件的栈的顶部。
如果在屏幕上有与这个窗口部件重叠的兄弟,这个窗口部件将在它后来的这些兄弟之前变的可视了。
也可以参考lower()和stackUnder()。
- void QWidget::repaint ( int x, int y, int w, int h, bool erase = TRUE ) [槽]
通过立即调用paintEvent()来直接重新绘制窗口部件,除非更新是失效的或者窗口部件被隐藏。
如果erase为真,Qt在paintEvent()调用之前擦除区域(x,y,w,h)。
如果w是负数,它被width()-x替换,并且如果h是负数,它被height()-y替换。
如果你需要立即重新绘制,我们建议使用repaint(),比如在动画期间。在绝大多数情况下,update()更好,因为它允许Qt来优化速度并且防止闪烁。
警告:如果你在一个函数中调用repaint(),而它自己又被paintEvent()调用,你也许会看到无线循环。update()函数从来不会产生循环。
也可以参考update()、paintEvent()、updatesEnabled和erase()。
小结:QWidget有很多成员函数,但是它们中的一些有少量的直接功能:例如,QWidget有一个字体属性,但是它自己从来不用。有很多继承它的子类提供了实际的功能,比如QPushButton、QListBox和QTabDialog等等。