QWidget理论使用总结
- 一、概述
- 二、顶层 控件 和子 控件
- 三、复合控件
- 四、自定义控件和绘制
- 五、大小提示和大小策略
- 六、事件
- 七、一组函数和属性
- 八、QWidget样式表
- 九、透明度和双缓冲
- 十、创建半透明窗口
一、概述
widget 是用户界面的最小单位:它从window系统接收鼠标、键盘和其他事件,并在屏幕上显示自己。每个组件都是矩形的,并且按z轴顺序排列(就是窗口在屏幕里面层次的位置)。一个 控件 会被它的父 控件 和它前面的 控件 剪切。(其实就是被遮挡的意思)。
QWidget 是Qt里面其他控件的公共基类。
没有嵌入到父窗口中的控件称为窗口。通常,窗口有一个边框和一个标题栏,不过也可以使用适当的窗口标志创建没有这两个装饰的窗口)。在Qt中,QMainWindow 和 QDialog 的各种子类是最常见的窗口类型。
每个 控件 的构造函数都接受一到两个标准参数:
QWidget *parent = nullptr
- QWidget *parent = nullptr 是新widget的父组件。如果它是nullptr(默认值),新 控件 将是一个窗口。如果不是,它将是parent的子元素,并受其父元素几何形状的限制(除非指定Qt::Window作为窗口标志)。
Qt::WindowFlags f = 0
- Qt::WindowFlags f = 0 (在可用的地方)设置窗口标志;默认值适用于几乎所有窗口组件,但如果要获得一个没有窗口系统框架的窗口,则必须使用特殊的标志。
QWidget有许多成员函数,但其中一些几乎没有直接功能;例如,QWidget有一个font属性,但它自己从不使用它,因为他也作为其他窗口的基类。其子类才提供了真正的功能,例如QLabel、QPushButton、QListWidget和QTabWidget等。
二、顶层 控件 和子 控件
没有父窗口的窗口始终是一个独立窗口(顶级窗口组件)。对于这些窗口组件,setWindowTitle() 和 setWindowIcon() 分别设置标题栏和图标。就像下图就能看出来是什么意思,设置窗口的图标和标题栏。
非窗口 控件 是子控件 ,显示在它们的父 控件 中。Qt中的大多数控件主要作为子控件使用。例如,可以将按钮显示为顶层窗口,但大多数人更喜欢将按钮放在其他 控件 中,如QDialog。
上图显示了一个QGroupBox小 控件 用于在QGridLayout提供的布局中保存各种子小 控件 。QLabel子 控件 已被概述以表明其完整尺寸。
三、复合控件
当一个小 控件 被用作一个 容器 来分组许多子小 控件 时,它被称为复合小 控件 。因为QWidget可以做容器来承载控件的。这些可以通过构建具有所需视觉属性的窗口组件(例如QFrame,QGroupBox)来创建,并向其中添加子窗口组件,通常通过布局来管理。上图使用的QGroupBox 了一个使用Qt Designer创建的复合组件。其实就是说用类似 QWidget 或者 QFrame 来作为其他控件的承载控件。其实可以理解为由父控件和子控件构成一起的控件可看成复合控件。
还可以通过继承标准窗口小 控件 (如QWidget或QFrame)并在子类的构造函数中添加必要的布局和子窗口小 控件 来创建复合窗口小 控件 。Qt提供的许多示例都使用这种方法,Qt教程中也介绍了这种方法。
四、自定义控件和绘制
由于QWidget是QPaintDevice的子类,可以使用子类来显示使用一系列绘制操作和QPainter类的实例组成的自定义内容。绘制就是自定义控件的一大利器,我们可以用绘制的方式绘制控件的外观,交互的话就可以用 鼠标事件或者在自定义控件里面添加对应的响应控件也可以响应,绘制主要是影响的外观。绘制的话也是绘制的背景,不会影响这个 QWidget内部的子控件。
这种方法与 Graphics View Framework 使用的canvas风格的方法不同,后者是由应用程序将项目添加到场景中,然后由框架自身渲染。
每个 控件 都在其 paintEvent() 函数中执行所有绘制操作。 每当控件需要重绘时,无论是由于某些外部更改还是应用程序请求重绘时,都会调用这个方法。我们需要做的就是重写这个函数就可以啦,就行下面的时钟例子,就用的绘制方式。
更为酷炫的,像这种的也是通过绘制的方式来制作的。
五、大小提示和大小策略
在实现新窗口组件时,几乎总是需要重新实现 sizeHint(),为窗口组件提供一个合理的默认大小,并使用 setSizePolicy() 设置正确的大小策略。
默认情况下,没有提供大小提示的复合 控件 将根据其子 控件 的空间需求进行调整。
size策略允许你为布局管理系统提供良好的默认行为,以便其他 控件 可以轻松地包含和管理你的 控件 。默认大小策略表明size提示表示窗口组件的首选大小,这对于许多窗口组件来说通常已经足够了。
注意:顶级 控件 的大小被限制为桌面高度和宽度的2/3。如果这些边界不够用,可以手动 resize() 控件 。
六、事件
QWidget 的 事件也是非常重要的,Qt的窗口相关事件系统基本上体现在 QWidget 的 基础函数里面,这些函数都可以让我们自己重写,然后扩展功能。
控件 响应通常由用户操作引起的事件。Qt通过调用特定的事件处理程序函数和包含每个事件信息的QEvent子类实例来向 控件 传递事件。
如果控件只包含子 控件 ,则可能不需要实现任何事件处理程序。如果要检测子 控件 中的鼠标点击,则在控件的mousePressEvent() 中调用子控件的 underMouse() 函数。
Scribble示例实现了一组更广泛的事件来处理鼠标移动、按钮按下和窗口大小调整。
您需要为自己的控件提供行为和内容,但下面是与QWidget相关的事件的简要概述,从最常见的开始:
事件名 | 功能 |
---|---|
paintEvent() | 每当控件需要重绘时,都会调用这个函数,每个显示自定义内容的小控件都必须实现它。使用QPainter进行绘制只能在paintEvent()或由paintEvent()调用的函数中进行。 |
resizeEvent() | resizeEvent() 在控件被调整大小后被调用。 |
mousePressEvent() | 当鼠标指针在控件内部按下鼠标按钮,或者控件使用grabMouse()抓取鼠标时,会调用mousePressEvent()。在不松开鼠标的情况下按下鼠标,实际上与调用grabMouse()是一样的。 |
mouseReleaseEvent() | mouseReleaseEvent() 在鼠标按键松开时调用。当widget接收到相应的鼠标按下事件时,它会接收鼠标释放事件。这意味着,如果用户在widget内按下鼠标,然后在释放鼠标按钮之前将鼠标拖动到其他地方,则widget将接收release事件。但有一个例外:如果鼠标按钮按下时出现一个弹出菜单,这个弹出菜单会立即窃取鼠标事件。 |
mouseDoubleClickEvent() | mouseDoubleClickEvent() 在用户双击widget时被调用。如果用户双击,widget会接收到一个鼠标按压事件、一个鼠标释放事件、(一个鼠标点击事件) 、第二个鼠标按下事件、这个事件以及最后一个鼠标释放事件。(如果在操作过程中鼠标没有保持稳定,也可能会接收到一些鼠标移动事件。) 在第二次点击到来之前,无法区分是点击还是双击。(这就是为什么大多数GUI书籍建议双击是单击的扩展,而不是触发另一个动作的原因之一。) |
接受键盘输入的控件需要重新实现以下几个事件处理程序。
事件名 | 功能 |
---|---|
keyPressEvent() | 每当按键被按下时,都会调用keyPressEvent()方法,当按键按下时间长到自动重复时,也会调用keyPressEvent()方法。只有当Tab和Shift+Tab键不被焦点改变机制使用时,它们才会被传递给widget。要强制小控件处理这些键,必须重新实现QWidget::event()。 |
focusInEvent() | focusInEvent()在小控件获得键盘焦点时被调用(假设您已经调用了setFocusPolicy())。表现良好的控件以一种清晰而谨慎的方式表明它们拥有键盘焦点。 |
focusOutEvent() | focusOutEvent()在小控件失去键盘焦点时被调用。 |
你可能还需要重新实现一些不太常见的事件处理程序:
事件名 | 功能 |
---|---|
mouseMoveEvent() | mouseMoveEvent()会在鼠标移动而鼠标按钮按住不放时调用。这在拖放操作中很有用。如果调用setMouseTracking(true),即使没有按下任何按钮,也会得到鼠标移动事件。(参见拖放参考线。) |
keyReleaseEvent() | keyReleaseEvent()方法会在按键被释放时以及按键被按下时(如果按键是自动重复的)被调用。在这种情况下,widget将在每次重复时收到一对按键释放和按键按下事件。只有当Tab和Shift+Tab键不被焦点改变机制使用时,它们才会被传递给widget。要强制小控件处理这些键,必须重新实现QWidget::event()。 |
wheelEvent() | 每当用户在widget获得焦点时转动鼠标滚轮时,都会调用wheelEvent()。 |
enterEvent() | enterEvent()在鼠标进入控件的屏幕空间时被调用。(这不包括组件的任何子组件拥有的屏幕空间。) |
leaveEvent() | 当鼠标离开控件的屏幕空间时,会调用leaveEvent()。如果鼠标进入一个子控件,它不会导致leaveEvent()。 |
moveEvent() | enterEvent()会在控件相对于其父控件被移动时调用。 |
closeEvent() | closeEvent()在用户关闭控件时(或调用close()时)被调用。 |
在QEvent::Type的文档中还描述了一些相当模糊的事件。要处理这些事件,需要直接重新实现event()。
event()的默认实现处理Tab和Shift+Tab(移动键盘焦点),并将大多数其他事件传递给上面更专门的处理程序之一。
七、一组函数和属性
功能相关 | 相关函数 |
---|---|
窗口功能 | show(), hide(), raise(), lower(), close(). |
顶层窗口 | windowModified, windowTitle, windowIcon, isActiveWindow, activateWindow(), minimized, showMinimized(), maximized, showMaximized(), fullScreen, showFullScreen(), showNormal(). |
窗口内容 | update(), repaint(), scroll(). |
窗口几何 | pos, x(), y(), rect, size, width(), height(), move(), resize(), sizePolicy, sizeHint(), minimumSizeHint(), updateGeometry(), layout(), frameGeometry, geometry, childrenRect, childrenRegion, adjustSize(), mapFromGlobal(), mapToGlobal(), mapFromParent(), mapToParent(), maximumSize, minimumSize, sizeIncrement, baseSize, setFixedSize() |
模式 | visible, isVisibleTo(), enabled, isEnabledTo(), modal, isWindow(), mouseTracking, updatesEnabled, visibleRegion(). |
观感 | style(), setStyle(), styleSheet, cursor, font, palette, backgroundRole(), setBackgroundRole(), fontInfo(), fontMetrics(). |
键盘焦点功能 | focus, focusPolicy, setFocus(), clearFocus(), setTabOrder(), setFocusProxy(), focusNextChild(), focusPreviousChild(). |
鼠标和键盘抓取 | grabMouse(), releaseMouse(), grabKeyboard(), releaseKeyboard(), mouseGrabber(), keyboardGrabber(). |
事件处理程序 | event(), mousePressEvent(), mouseReleaseEvent(), mouseDoubleClickEvent(), mouseMoveEvent(), keyPressEvent(), keyReleaseEvent(), focusInEvent(), focusOutEvent(), wheelEvent(), enterEvent(), leaveEvent(), paintEvent(), moveEvent(), resizeEvent(), closeEvent(), dragEnterEvent(), dragMoveEvent(), dragLeaveEvent(), dropEvent(), childEvent(), showEvent(), hideEvent(), customEvent(). changeEvent(), |
系统功能 | parentWidget(), window(), setParent(), winId(), find(), metric(). |
上下文菜单 | contextMenuPolicy, contextMenuEvent(), customContextMenuRequested(), actions() |
交互式帮助 | setToolTip(), setWhatsThis() |
- 窗口功能
其实就是 控制窗口的 显示隐藏,关闭,以及窗口在屏幕里面的 Z-index 上移、下沉。 - 顶层窗口
控制窗口最大,最小化,全屏,激活、设置窗口图标或者标题等 - 窗口内容
窗口内容的刷新,重新绘制,滚动等等。 - 窗口几何
窗口的长宽大小、坐标位置等信息 - 模式
窗口的显示、隐藏、使能等 - 观感
窗口的样式,外观,字体、颜色等等 - 键盘焦点功能
就是输入的 焦点管理 - 系统功能
获取窗口的句柄,与Windows交互要多些
八、QWidget样式表
除了每个平台的标准窗口样式外,还可以根据样式表中指定的规则设置窗口小部件的样式。此功能使我们能够自定义特定部件的外观,以向用户提供有关其用途的视觉提示。例如,可以为一个按钮设置特定的样式,以表明它执行了破坏性操作。Qt提供的就是 QSS 样式表。
QSS 其实是Qt样式表,就是一种美化界面的方式。Qt样式表是Qt界面的一种强大的机制,可以让我们自定义窗口组件的外观。Qt样式表的概念、术语和语法很大程度上受到HTML层叠样式表(CSS)的启发。功能和CSS的基本一样,大家应该也是有CSS 的基本基础,那写QSS就很容易的。下面就是我对QSS的相关笔记
✔️ Qt扫盲-QSS概述 🏤
✔️ Qt扫盲-QSS语法概述 ♻️
✔️ Qt Designer配置QSS交互使用 ✈️
✔️ Qt扫盲-QSS定制Qt Widget控件 🌟
✔️ Qt扫盲-QSS帮助手册使用 ✨
✔️ Qt扫盲-QSS示例代码 🌵
九、透明度和双缓冲
自Qt 4.0以来,QWidget自动对其绘制进行双缓冲,因此不需要在paintEvent()中编写双缓冲代码以避免闪烁。
从Qt 4.1开始,Qt::WA_ContentsPropagated部 件属性已经被弃用。相反,只要没有设置Qt::WA_PaintOnScreen,父部件的内容就会默认传播到每个子部件。
我们可以编写自定义部件来利用这一特性,方法是更新不规则区域(以创建非矩形的子部件),或者使用小于完整alpha的组件绘制颜色。下图显示了如何微调自定义小部件的属性和属性以实现不同的效果。
在上图中,构建了一个移除区域的半透明矩形子部件,并将其添加到父部件(显示pixmap的QLabel)。然后,设置不同的属性和widget属性来实现不同的效果:
- 左边的小部件没有其他属性或设置小部件属性。这个默认状态适合大多数使用透明度、形状不规则或者不使用不透明的画笔覆盖整个区域的自定义部件。
- 中心部件设置了autoFillBackground属性。此属性与依赖widget提供默认背景的自定义部件一起使用,这些部件不会用不透明的画笔绘制整个区域。
- 右边的部件设置了Qt::WA_OpaquePaintEvent部件属性。这表示部件将用不透明的颜色覆盖整个区域。小部件的区域最初是未初始化的,在图中以红色对角网格模式表示,该模式穿过覆盖的区域。WA_OpaquePaintArea属性对于需要快速绘制自己的专门化内容且不需要默认填充背景的部件非常有用。
要用简单的背景颜色快速更新自定义窗口组件,如实时绘图或绘图窗口组件,最好定义一个合适的背景颜色(使用带有QPalette::Window 的 setBackgroundRole()),设置 autoFillBackground 属性,并仅在窗口组件的 paintEvent() 中实现必要的绘图功能。
为了快速更新不断用不透明内容覆盖整个区域的自定义窗口组件(例如视频流窗口组件),最好设置窗口组件的Qt::WA_OpaquePaintEvent,避免与重绘窗口组件背景相关的任何不必要的开销。
如果窗口组件同时设置了Qt::WA_OpaquePaintEvent属性和autoFillBackground属性,则Qt::WA_OpaquePaintEvent属性优先。根据您的需求,您可以选择其中任何一个。
从Qt 4.1开始,父组件的内容也会传播到标准Qt组件。如果父部件以非标准的方式装饰,这可能会导致一些意想不到的结果,如下图所示。
在不借助于子类的情况下,定制标准Qt窗口组件的绘制行为的范围比定制窗口组件的范围要小一些。通常,标准窗口组件的期望外观可以通过设置其 autoFillBackground 属性来实现。
十、创建半透明窗口
从Qt 4.5开始,可以在支持合成的窗口系统上创建具有半透明区域的窗口。
要在顶级部件中启用此功能,请使用 setAttribute() 设置其Qt::WA_TranslucentBackground属性,并确保其背景在希望部分透明的区域中使用非不透明颜色绘制。
平台注意事项:
- X11:此功能依赖于支持ARGB视觉效果的X服务器和合成窗口管理器的使用。
- Window:窗口组件需要设置 Qt::FramelessWindowHint 窗 口标志,才能使半透明工作。