widget 是用户界面的最小单位:它从window系统接收鼠标、键盘和其他事件,并在屏幕上显示自己。每个组件都是矩形的,并且按z轴顺序排列(就是窗口在屏幕里面层次的位置)。一个 控件 会被它的父 控件 和它前面的 控件 剪切。(其实就是被遮挡的意思)。
QWidget 是Qt里面其他控件的公共基类。
没有嵌入到父窗口中的控件称为窗口。通常,窗口有一个边框和一个标题栏,不过也可以使用适当的窗口标志创建没有这两个装饰的窗口)。在Qt中,QMainWindow 和 QDialog 的各种子类是最常见的窗口类型。
每个 控件 的构造函数都接受一到两个标准参数:
QWidget *parent = nullptr
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() |
除了每个平台的标准窗口样式外,还可以根据样式表中指定的规则设置窗口小部件的样式。此功能使我们能够自定义特定部件的外观,以向用户提供有关其用途的视觉提示。例如,可以为一个按钮设置特定的样式,以表明它执行了破坏性操作。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属性来实现不同的效果:
要用简单的背景颜色快速更新自定义窗口组件,如实时绘图或绘图窗口组件,最好定义一个合适的背景颜色(使用带有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属性,并确保其背景在希望部分透明的区域中使用非不透明颜色绘制。
平台注意事项: