QWidget
最近在学习Qt,每次阅读官方英文API都只读取自己需要的一部分,但是面对庞大的英文,老是觉得获取信息像大海捞针一样困难。于是我决定从头到尾好好读一读英文介绍,然后根据自己的理解,把它翻译为中文,一方面可以增加自己的理解能力,同时也方便英文和我一样不好的同学,我会尽量按照原文来翻译,如果其中某些语句让你觉得有些晦涩甚至难以理解时,可以直接转到原版进行阅读,或者您觉得有更好的表述方法,都可以告诉我,我会改正,提高翻译质量。如果有一些错误,还请大家批评指正。
注:这里的widget我们可以理解为子窗口,window可以理解独立的窗口。
QWidget类是所有的用户接口对象的基类
- widget是用户界面最基本的组成部分:它接收来自于windows系统的鼠标、键盘以及其他事件,然后将它自己绘制在屏幕上。每一个widget都是矩形形状,以Z-order顺序排列。一个widget会被它的父窗口或者在它前面的widgets所截断。
- 如果一个widget没有被嵌入到另外一个widget中,那么这个widget就叫做window,即一个独立的窗口。尽管我们可以通过设置window flags创建一个没有任何修饰的window,但是通常情况下,一个window都有一个边框和一些菜单栏等。在Qt中,QMainWindow以及QDialog的子类都是最为常见的window类型。
一个widget的构造函数可以接受一个或者两个标准参数:
- QWidget *parent = 0 ,为新创建的widget指定其父widget。如果parent为0(默认值),那么这个新widget就会变成一个独立的window。如果parent不为0,那么新创建的widget是parent的一个子窗口,但是此时我们新创建的widget的形状会受其父窗口形状的约束。(除非你指定这个新创建的widget的window flag为Qt::Window)
- Qt::WindowFlags f = 0,这个参数用来设置新创建的widget的window flags(例如是否有最大化按钮等)。默认的参数几乎对所有的widget都是适用的。但是如果你需要一个没有边框的widget,你必须使用特定的flag。
Tips: QWidget有很多成员函数,但是有一些成员函数并不会被使用。例如,QWidget有font(字体)属性,但是基本上不会调用这个函数。
顶级widget和子widget
- 一个没有父widget的widget是一个独立的window,即是一个顶级widget。对于这些widget,setWindowTitle()函数和setWindowIcon()分别设置窗口的标题和图标。
- 非window类型的widget是子widget,在父widget中显示。Qt中的大多数widget主要是作为子widget。例如,我们可以把一个按钮作为一个顶级窗口,但是大多数人倾向于把他们的按钮放在widget里面,例如把按钮放在QDialog(对话框类)中。
- 上图中展示了把一个QGroupBox作为widget来容纳QGridLayout中的各种各样的子widget。QLabel被设置为自适应大小
- 如果你想用一个QWidget来容纳子widget,通常情况下你应该给父QWidget添加一个layout(布局)。更多信息参见Layout Management
复合型Widget
- 如果要把一个widget作为一个容器来存放一组子widget,这个widget就称之为复合型widget。可以通过构造一个带有可视属性的widget-一个QFrame来实现。例如,通常我们通过该widget的layout(布局)来添加一个子widget。上图中的
widget便是通过Qt Designer创建的。
- 复合型的widget也可以是标准widget的一个子类,例如QWidget或者QFrame,然后在这个子类中添加必要的layout(布局)和子widget。Qt中的许多例子以及教程中的例子中的复合型widget都是使用该方法创建的。
自定义widget和绘制方式
- 因为QWidget是QPaintDevice的一个子类,因此QWidget可以通过实例化一个QPainter的对象,然后使用一系列的绘制方式来显示自定义的内容。该方法与Graphics View Framework(我猜指的是通过拖拽控件来制作一个特定窗口的方法)的canvas-style approach(画布风格方法)完全不同。在Graphics View Framework中,每一个被应用程序添加的控件都由framework自身来渲染。
- 每一个widget的绘制操作都由自己的paintEvent()来执行。无论何时,只要有一些变化发生或者是应程序的要求而导致这个widget需要被重新绘制时,就会调用这个函数。
- 这个模拟时钟的例子展示了一个简单的窗口是如何处理绘制事件的。
Size Hints 和Size Policies
- 当实例化了一个新的widget的时候,重载SizeHint()函数使得窗口有一个合理的默认大小,重载setSizePolicy()使得该窗口有正确的大小策略,这两个方法都是非常有用的。
- size policy使得layout management system(布局管理系统)拥有良好的默认大小变化管理依据。这使得其他的widget更加容易管理和容纳你的widget。默认的size policy表示widget的大小可以自由变化,一般倾向于采用sizeHint()返回的大小,这对大多数的widget来说已经足够好了。
- 顶级widget的大小一般约束为桌面大小长度和宽度的的2/3,但我们也可以通过resize()函数来手动改变大小。
Events(事件)
- 用户的行为引起了widget对事件的响应。Qt通过实例化QEvent的子类的一个对象,该对象包括每个事件的相关信息,然后通过调用对应的事件处理函数将事件传送给widget。
- 如果你的widget中只有些许widget,你很有可能不需要实现事件处理函数。如果你想在一个子widget中捕捉鼠标点击事件,只需要在这个子widget中的mousePressEvent()中调用underMouse()函数即可。
- Scribble实现了更多的有关于鼠标移动、按钮点击和窗口重设大小的事件处理过程。
- 你需要给自己的widget添加一些行为和内容,如下是一些简单的和QWidget有关的事件概览,我们从最常见的说起:
paintEvent(),只要widget需要重新绘制就会被调用。每一个自定义的widget都需要实现这个函数。使用QPainter进行绘制只能发生在一个PaintEvent()中,或者是以paintEvent()的形式调用它。
resizeEvent(),当widget大小被重新设置的时候就会被调用
mouseDoubleClickEvent() ,当用户在widget中双击鼠标就会被调用。如果用户双击,widget会收到一个mouse press 事件,一个 mouse release 事件,(一个mouse click 事件)和另外一个mouse press 事件,一个 mouseDoubleclick 事件,和一个mouse release 事件。如果在双击过程中发生了鼠标抖动,就会收到一个mouse move 事件。在没有收到第二次点击鼠标的事件前是不可能区分鼠标单击事件和鼠标双击事件的。(这就是为什么大多数GUI书推荐把双击事件作为单击事件的一个扩展而不是定义为另外一个不同的操作)
- 如果widget接受键盘输入,则需要实现一些其他的事件处理函数:
keyPressEvent(),用户按下一个键时就会被调用,如果长按一个键,这个函数会被重复调用。如果焦点变化机制没有使用Tab和Shift+Tab键,那么这两个键仅仅会传递给Widget。如果要强行使你的widget处理这两个键的信息,则需要实现QWidget::event()。
focusInEvent(),如果widget获得键盘焦点,该函数就会被调用。(假定你提前调用了setFocusPolicy()函数),功能良好的widget意味着它们会以明确的方式获得焦点。
focusOutEvent(),widget失去焦点时该函数就会被调用。
- 也许你还需要实现一些不太常见的事件处理函数:
mouseMoveEvent(),当你按下鼠标,并且移动的时候该函数会被调用。在拖拽操作中这个函数非常有用。如果你调用了setMouseTracking(true),那么即使鼠标没有被按下,但是只要鼠标在移动,这个函数就会被调用。(见 Drag and Drop板块)
keyReleaseEvent(),一个按键被松开时,或者持续按住一个键(这个键必须是自动重复的)时该函数就会被调用。如果是第二种情况,那么widget会在按键每次重复时收到一对key release和key press事件。如果焦点变化机制没有使用Tab和Shift+Tab键,那么这两个键仅仅会传递给Widget。如果要强行使你的widget处理这两个键的信息,则需要实现QWidget::event()。
wheelEvent(),鼠标滑轮滚动时该函数被调用
enterEvent(),鼠标进入该widget所在屏幕区域时被调用(该widget的屏幕区域不包括其子widget的屏幕区域)
leaveEvent(),鼠标离开widget所在屏幕区域时被调用,但是如果鼠标进入了子widget屏幕区域时该函数不会被调用
moveEvent(),widget相对于其父widget被移动时调用该函数
closeEvent(),用户关闭widget时或者调用close()函数时该函数被调用
- 还有一些鲜为人知的事件,详见QEvent::Type,如果要处理这些事件,你还需要亲自实现event
- 默认的event会处理Tab和Shift+Tab键(用来改变键盘焦点),同时也会传递上述的一个或者多个事件。但不包括所说的那些鲜为人知的事件,这就是为什么你需要自己亲自实现event的原因了。
to be continue……