Qt 窗口类型与窗口标志

本文最后更新于 2022年8月28日 晚上

简介

Qt 为窗口提供了许多种类型与标志,用于自定义开发特殊的窗口(如无边框窗口等)。这些类型与标志具体能否实现、效果如何,取决于操作系统的窗口系统是否支持与效果。绝大多数情况下,使用默认窗口类型即可。

每个窗口只能有一种窗口类型,但可以有多个窗口标志修饰。

枚举值列表

翻译自官方文档

此枚举值用于为控件指定各种窗口系统(window-system)属性。它们一般比较少见,但在少数情况下是必要的。其中一些标志取决于底层窗口管理器是否支持。

窗口类型

主要类型包括:

常量 描述
Qt.Widget 0x00000000 QWidget 的默认类型。这种类型的控件如果有父控件则作为子控件,若没有父控件则为独立窗口。参见 Qt.Window 和 Qt.SubWindow。
Qt.Window 0x00000001 表示该控件是一个窗口,不管该控件是否有父控件,一般带有一个窗口系统框架和一个标题栏。注意如果控件没有父对象,则无法取消设置此标志。
Qt.Dialog 0x00000002 | Window 表示该控件是一个应装饰为对话框的窗口(即,一般在标题栏中没有最大化最小化按钮)。这是 QDialog 的默认类型。如果想用它作为模态对话框,它应该从另一个窗口启动,或者有父窗口并与 QWidget.windowModality 属性一起使用。如果将其设置为模态,对话框将阻止应用程序中的其他顶级窗口获得任何输入。我们将具有父控件的顶级窗口称为次要窗口(secondary window)。
Qt.Sheet 0x00000004 | Window 表示窗口是 macOS 上的 sheet。由于使用 sheet 意味着窗口模式,因此推荐的方法是使用 QWidget.setWindowModality() 或 QDialog::open() 替代。
Qt.Popup 0x00000008 | Window 表示该控件是一个弹出式顶级窗口,即它是模态的,但具有适合弹出式菜单的窗口系统框架。
Qt.Tool Popup | Dialog 表示该控件是一个工具窗口。工具窗口通常是一个小窗口,具有比一般窗口更小的标题栏和装饰,一般用于工具按钮的集合。如果有父控件,则工具窗口将始终保留在其顶部。如果没有父级,也可以考虑使用 Qt::WindowStaysOnTopHint。如果窗口系统支持,工具窗口可以用更轻量的框架来装饰。它也可以与 Qt::FramelessWindowHint 结合使用。在 macOS 上,工具窗口对应于窗口的 NSPanel 类。这意味着窗口位于普通窗口之上,因此无法在其上层放置普通窗口。默认情况下,当应用程序处于非活动状态时,工具窗口将消失。这可以通过 Qt.WA_MacAlwaysShowToolWindow 属性来控制。
Qt.ToolTip Popup | Sheet 表明该控件是工具提示。这在内部用于实现工具提示。
Qt.SplashScreen ToolTip | Dialog 表明该窗口是闪屏(splash screen)。这是 QSplashScreen 的默认类型。
Qt.SubWindow 0x00000012 表明此控件是子窗口,例如 QMdiSubWindow 控件。
Qt.ForeignWindow 0x00000020 | Window 表明此窗口对象是一个句柄,表示由另一个进程或手动使用本地代码创建的本地平台窗口。
Qt.CoverWindow 0x00000040 | Window 表示该窗口代表一个覆盖窗口,在某些平台上最小化应用程序时显示。

窗口标志

还有许多标志可用于自定义顶级窗口的外观。这对其他窗口没有影响:

常量 描述
Qt.MSWindowsFixedSizeDialogHint 0x00000100 在微软 Windows 上为窗口提供一个细对话框边框。这种风格传统上用于固定大小的对话框。注意:不建议在多显示器环境中使用此标志,因为系统将强制窗口在跨屏幕移动时保持其原始大小,这在使用具有不同分辨率的显示器时尤其不受欢迎。
Qt.MSWindowsOwnDC 0x00000200 在微软 Windows 上为窗口提供自己的显示上下文。
Qt.BypassWindowManagerHint 0x00000400 此标志可用于向平台插件指示应禁用“所有”窗口管理器协议。根据应用程序运行的操作系统和窗口管理器运行的情况,该标志的行为会有所不同。该标志可用于获取未设置配置的本机窗口。
Qt.X11BypassWindowManagerHint BypassWindowManagerHint 完全绕过窗口管理器。这会导致一个完全不受管理的无边框窗口(即,除非手动调用 QWidget.activateWindow(),否则没有键盘输入)。
Qt.FramelessWindowHint 0x00000800 生成无边框窗口。用户不能通过窗口系统移动或调整无边框窗口的大小。在 X11 上,标志的结果取决于窗口管理器及其理解 Motif 和/或 NETWM 的能力。大多数现有的现代窗口管理器都可以处理这个问题。
Qt.NoDropShadowWindowHint 0x40000000 禁用在支持的平台上的窗口投影。
Qt.CustomizeWindowHint 0x02000000 关闭默认窗口标题 hints。
Qt.WindowTitleHint 0x00001000 为窗口添加标题栏。
Qt.WindowSystemMenuHint 0x00002000 为窗口添加系统菜单,很可能是一个关闭按钮。如果想要隐藏/显示关闭按钮,更好的做法是使用 WindowCloseButtonHint。
Qt.WindowMinimizeButtonHint 0x00004000 为窗口添加最小化按钮。在某些平台上,这意味着 WindowSystemMenuHint 也已生效。
Qt.WindowMaximizeButtonHint 0x00008000 为窗口添加最大化按钮。在某些平台上,这意味着 WindowSystemMenuHint 也已生效。
Qt.WindowMinMaxButtonsHint WindowMinimizeButtonHint | WindowMaximizeButtonHint 为窗口添加最大化、最小化按钮。在某些平台上,这意味着 WindowSystemMenuHint 也已生效。
Qt.WindowCloseButtonHint 0x08000000 为窗口添加关闭按钮。在某些平台上,这意味着 WindowSystemMenuHint 也已生效。
Qt.WindowContextHelpButtonHint 0x00010000 为对话框添加上下文帮助按钮。在某些平台上,这意味着 WindowSystemMenuHint 也已生效。
Qt.MacWindowToolBarButtonHint 0x10000000 在 macOS 上添加一个工具栏按钮(即,在有工具栏的窗口的右上方的椭圆形按钮)
Qt.WindowFullscreenButtonHint 0x80000000 在 macOS 上添加一个全屏按钮
Qt.BypassGraphicsProxyWidget 0x20000000 如果父控件已经嵌入,则阻止窗口及其子窗口自动将自己嵌入到 QGraphicsProxyWidget 中。如果希望控件始终是桌面上的顶级控件,则可以设置此标志,无论父控件是否已嵌入场景中。
Qt.WindowShadeButtonHint 0x00020000 如果底层窗口管理器支持,则添加一个阴影按钮替代最小化按钮。
Qt.WindowStaysOnTopHint 0x00040000 通知窗口系统该窗口应位于所有其他窗口之上。注意,在某些基于 X11 的窗口管理器上,还必须传递 Qt.X11BypassWindowManagerHint 才能使此标志正常工作。
Qt.WindowStaysOnBottomHint 0x04000000 通知窗口系统该窗口应位于所有其他窗口之下。
Qt.WindowTransparentForInput 0x00080000 通知窗口系统该窗口仅用于输出(显示某些内容)而不接受输入。因此输入事件应该像不存在一样略过。
Qt.WindowOverridesSystemGestures 0x00100000 通知窗口系统该窗口实现了自己的一组手势,系统级的手势(例如三指切换屏幕)应当被禁用。
Qt.WindowDoesNotAcceptFocus 0x00200000 通知窗口系统该窗口不接受输入焦点。
Qt.MaximizeUsingFullscreenGeometryHint 0x00400000 通知窗口系统在最大化窗口时应尽可能多地使用可用的屏幕几何空间,包括可能被UI覆盖的区域(例如状态栏或应用程序启动器)。这可能会导致窗口被置于这些系统UI之下,具体情况取决于平台是否支持。启用该标志后,用户负责将 QScreen.availableGeometry() 也考虑在内,以便应用程序中需要用户交互的任何UI元素都不会被系统UI覆盖。
Qt.WindowType_Mask 0x000000ff 用于从窗口标志中提取窗口类型的掩码。

代码实例

Qt 官方提供了一个展示各种窗口类型、窗口标志实际效果的例子 Window Flags Example,此处将原 C++ 代码 “翻译” 至 PySide6 版本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
# window_flags_example.py
# @muzing <muzi2001@foxmail.com>
import sys

from PySide6 import QtWidgets
from PySide6.QtCore import Qt, QCoreApplication

"""
本案例为 Qt 官方案例 Window Flags Example 的 PySide6 移植版
https://doc.qt.io/qt-6/qtwidgets-widgets-windowflags-example.html

用于展示在不同的 Window Flags 标记下,顶层窗口的样式状态
https://doc.qt.io/qt-6/qt.html#WindowType-enum
"""


class ControllerWindow(QtWidgets.QWidget):
"""控制窗口类"""

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

self.preview_window = PreviewWindow(self) # 创建预览窗口

self.create_type_groupbox()
self.create_hints_groupbox()

quit_button = QtWidgets.QPushButton("Quit")
quit_button.clicked.connect(QCoreApplication.quit) # type: ignore

bottom_layout = QtWidgets.QHBoxLayout()
bottom_layout.addStretch()
bottom_layout.addWidget(quit_button)

main_layout = QtWidgets.QVBoxLayout()
main_layout.addWidget(self.type_groupbox)
main_layout.addWidget(self.hints_group_box)
main_layout.addLayout(bottom_layout)
self.setLayout(main_layout)

self.setWindowTitle("Window Flags")
self.update_preview()

def create_type_groupbox(self) -> None:
"""
创建type单选按钮,并布局到组框中 \n
:return: None
"""

self.type_groupbox = QtWidgets.QGroupBox("Type")

self.window_radiobutton = self.crate_radiobutton("Window")
self.dialog_radiobutton = self.crate_radiobutton("Dialog")
self.sheet_radiobutton = self.crate_radiobutton("Sheet")
self.popup_radiobutton = self.crate_radiobutton("Popup")
self.tool_radiobutton = self.crate_radiobutton("Tool")
self.tool_tip_radiobutton = self.crate_radiobutton("Tooltip")
self.splash_screen_radiobutton = self.crate_radiobutton("Splash screen")
self.sub_window_radiobutton = self.crate_radiobutton("Sub window")
self.foreign_window_radiobutton = self.crate_radiobutton("Foreign window")
self.cover_window_radiobutton = self.crate_radiobutton("Cover window")
self.window_radiobutton.setChecked(True)

layout = QtWidgets.QGridLayout()
layout.addWidget(self.window_radiobutton, 0, 0)
layout.addWidget(self.dialog_radiobutton, 1, 0)
layout.addWidget(self.sheet_radiobutton, 2, 0)
layout.addWidget(self.popup_radiobutton, 3, 0)
layout.addWidget(self.tool_radiobutton, 4, 0)
layout.addWidget(self.tool_tip_radiobutton, 0, 1)
layout.addWidget(self.splash_screen_radiobutton, 1, 1)
layout.addWidget(self.sub_window_radiobutton, 2, 1)
layout.addWidget(self.foreign_window_radiobutton, 3, 1)
layout.addWidget(self.cover_window_radiobutton, 4, 1)
self.type_groupbox.setLayout(layout)

def create_hints_groupbox(self) -> None:
"""
创建hints复选框,并布局到组框中 \n
:return: None
"""

self.hints_group_box = QtWidgets.QGroupBox("Hints")

self.ms_windows_fixed_size_dialog_checkbox = self.create_checkbox(
"MS Windows fixed size dialog"
)
self.bypass_window_manager_checkbox = self.create_checkbox("Bypass window manager")
self.x11_bypass_window_manager_checkbox = self.create_checkbox("X11 bypass window manager")
self.frameless_window_check_box = self.create_checkbox("Frameless window")
self.window_no_shadow_check_box = self.create_checkbox("No drop shadow")
self.window_title_check_box = self.create_checkbox("Window title")
self.window_system_menu_check_box = self.create_checkbox("Window system menu")
self.window_minimize_button_check_box = self.create_checkbox("Window minimize button")
self.window_maximize_button_check_box = self.create_checkbox("Window maximize button")
self.window_close_button_check_box = self.create_checkbox("Window close button")
self.window_context_help_button_check_box = self.create_checkbox(
"Window context help button"
)
self.window_shade_button_check_box = self.create_checkbox("Window shade button")
self.window_stays_on_top_check_box = self.create_checkbox("Window stays on top")
self.window_stays_on_bottom_check_box = self.create_checkbox("Window stays on bottom")
self.customize_window_hint_check_box = self.create_checkbox("Customize window")
self.window_transparent_for_input_check_box = self.create_checkbox(
"Window transparent for input"
)
self.ms_windows_own_dc_check_box = self.create_checkbox("MS Windows own DC")
self.max_using_full_screen_hint_check_box = self.create_checkbox(
"Maximize using full screen"
)

layout = QtWidgets.QGridLayout()
layout.addWidget(self.ms_windows_fixed_size_dialog_checkbox, 0, 0)
layout.addWidget(self.ms_windows_own_dc_check_box, 1, 0)
layout.addWidget(self.bypass_window_manager_checkbox, 2, 0)
layout.addWidget(self.x11_bypass_window_manager_checkbox, 3, 0)
layout.addWidget(self.frameless_window_check_box, 4, 0)
layout.addWidget(self.window_no_shadow_check_box, 5, 0)
layout.addWidget(self.customize_window_hint_check_box, 6, 0)
layout.addWidget(self.window_title_check_box, 7, 0)
layout.addWidget(self.window_system_menu_check_box, 8, 0)
layout.addWidget(self.window_minimize_button_check_box, 0, 1)
layout.addWidget(self.window_maximize_button_check_box, 1, 1)
layout.addWidget(self.window_close_button_check_box, 2, 1)
layout.addWidget(self.window_context_help_button_check_box, 3, 1)
layout.addWidget(self.window_stays_on_top_check_box, 4, 1)
layout.addWidget(self.window_shade_button_check_box, 5, 1)
layout.addWidget(self.window_stays_on_bottom_check_box, 6, 1)
layout.addWidget(self.window_transparent_for_input_check_box, 7, 1)
layout.addWidget(self.max_using_full_screen_hint_check_box, 8, 1)

self.hints_group_box.setLayout(layout)

def update_preview(self) -> None:
"""
为preview窗口设置新的flags并重新显示
:return: None
"""

flags = Qt.WindowFlags

if self.window_radiobutton.isChecked():
flags = Qt.Window
elif self.dialog_radiobutton.isChecked():
flags = Qt.Dialog
elif self.sheet_radiobutton.isChecked():
flags = Qt.Sheet
elif self.popup_radiobutton.isChecked():
flags = Qt.Popup
elif self.tool_radiobutton.isChecked():
flags = Qt.Tool
elif self.tool_tip_radiobutton.isChecked():
flags = Qt.ToolTip
elif self.splash_screen_radiobutton.isChecked():
flags = Qt.SplashScreen
elif self.sub_window_radiobutton.isChecked():
flags = Qt.SubWindow
elif self.foreign_window_radiobutton.isChecked():
flags = Qt.ForeignWindow
elif self.cover_window_radiobutton.isChecked():
flags = Qt.CoverWindow

if self.ms_windows_fixed_size_dialog_checkbox.isChecked():
flags |= Qt.MSWindowsFixedSizeDialogHint
if self.bypass_window_manager_checkbox.isChecked():
flags |= Qt.BypassWindowManagerHint
if self.x11_bypass_window_manager_checkbox.isChecked():
flags |= Qt.X11BypassWindowManagerHint
if self.frameless_window_check_box.isChecked():
flags |= Qt.FramelessWindowHint
if self.window_no_shadow_check_box.isChecked():
flags |= Qt.NoDropShadowWindowHint
if self.window_title_check_box.isChecked():
flags |= Qt.WindowTitleHint
if self.window_system_menu_check_box.isChecked():
flags |= Qt.WindowSystemMenuHint
if self.window_minimize_button_check_box.isChecked():
flags |= Qt.WindowMinimizeButtonHint
if self.window_maximize_button_check_box.isChecked():
flags |= Qt.WindowMaximizeButtonHint
if self.window_close_button_check_box.isChecked():
flags |= Qt.WindowCloseButtonHint
if self.window_context_help_button_check_box.isChecked():
flags |= Qt.WindowContextHelpButtonHint
if self.window_shade_button_check_box.isChecked():
flags |= Qt.WindowShadeButtonHint
if self.window_stays_on_top_check_box.isChecked():
flags |= Qt.WindowStaysOnTopHint
if self.window_stays_on_bottom_check_box.isChecked():
flags |= Qt.WindowStaysOnBottomHint
if self.customize_window_hint_check_box.isChecked():
flags |= Qt.CustomizeWindowHint
if self.window_transparent_for_input_check_box.isChecked():
flags |= Qt.WindowTransparentForInput
if self.ms_windows_own_dc_check_box.isChecked():
flags |= Qt.MSWindowsOwnDC
if self.max_using_full_screen_hint_check_box.isChecked():
flags |= Qt.MaximizeUsingFullscreenGeometryHint

self.preview_window.set_window_flags(flags)

# 防止某些平台下窗口出现在不可见的位置
pos = self.preview_window.pos()
if pos.x() < 0:
pos.setX(0)
if pos.y() < 0:
pos.setY(0)
self.preview_window.move(pos)
self.preview_window.show() # 重新显示preview窗口

def crate_radiobutton(self, text: str) -> QtWidgets.QRadioButton:
"""
快速创建单选按钮并连接clicked信号至self.update_preview槽函数 \n
:param text: 单选按钮的文字
:return: 创建的单选按钮
"""
button = QtWidgets.QRadioButton(text)
button.clicked.connect(self.update_preview) # type: ignore
return button

def create_checkbox(self, text: str) -> QtWidgets.QCheckBox:
"""
快速创建复选框并连接clicked信号至self.update_preview槽函数 \n
:param text: 复选框的文字
:return: 创建的复选框
"""
checkbox = QtWidgets.QCheckBox(text)
checkbox.clicked.connect(self.update_preview) # type: ignore
return checkbox


class PreviewWindow(QtWidgets.QWidget):
"""预览窗口类"""

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

# 创建一个只读的文本编辑器,用于显示当前窗口状态
self.text_edit = QtWidgets.QTextEdit()
self.text_edit.setReadOnly(True)
self.text_edit.setLineWrapMode(QtWidgets.QTextEdit.NoWrap)

self.close_button = QtWidgets.QPushButton("&Close")
self.close_button.clicked.connect(self.close) # type: ignore

layout = QtWidgets.QVBoxLayout()
layout.addWidget(self.text_edit)
layout.addWidget(self.close_button)
self.setLayout(layout)

self.setWindowTitle("Preview")

def set_window_flags(self, flags: Qt.WindowFlags) -> None:
"""
设置窗口标志并显示到文本编辑器中 \n
:param flags: 窗口标记
:return: None
"""

self.setWindowFlags(flags)

text = "" # 用于在窗口显示的提示文本
window_type = flags & Qt.WindowType_Mask

# 设置窗口类型的提示文本
if window_type == Qt.Window:
text = "Qt.Window"
elif window_type == Qt.Dialog:
text = "Qt.Dialog"
elif window_type == Qt.Sheet:
text = "Qt.Sheet"
elif window_type == Qt.Drawer:
text = "Qt.Drawer"
elif window_type == Qt.Popup:
text = "Qt.Popup"
elif window_type == Qt.Tool:
text = "Qt.Tool"
elif window_type == Qt.ToolTip:
text = "Qt.ToolTip"
elif window_type == Qt.SplashScreen:
text = "Qt.SplashScreen"
elif window_type == Qt.SubWindow:
text = "Qt.SubWindow"
elif window_type == Qt.ForeignWindow:
text = "Qt.ForeignWindow"
elif window_type == Qt.CoverWindow:
text = "Qt.CoverWindow"

# 设置窗口标志的提示文本
if flags & Qt.MSWindowsFixedSizeDialogHint:
text += "\n| Qt.MSWindowsFixedSizeDialogHint"
if flags & Qt.BypassWindowManagerHint:
text += "\n| Qt.BypassWindowManagerHint"
if flags & Qt.X11BypassWindowManagerHint:
text += "\n| Qt.X11BypassWindowManagerHint"
if flags & Qt.FramelessWindowHint:
text += "\n| Qt.FramelessWindowHint"
if flags & Qt.NoDropShadowWindowHint:
text += "\n| Qt.NoDropShadowWindowHint"
if flags & Qt.WindowTitleHint:
text += "\n| Qt.WindowTitleHint"
if flags & Qt.WindowSystemMenuHint:
text += "\n| Qt.WindowSystemMenuHint"
if flags & Qt.WindowMinimizeButtonHint:
text += "\n| Qt.WindowMinimizeButtonHint"
if flags & Qt.WindowMaximizeButtonHint:
text += "\n| Qt.WindowMaximizeButtonHint"
if flags & Qt.WindowCloseButtonHint:
text += "\n| Qt.WindowCloseButtonHint"
if flags & Qt.WindowContextHelpButtonHint:
text += "\n| Qt.WindowContextHelpButtonHint"
if flags & Qt.WindowShadeButtonHint:
text += "\n| Qt.WindowShadeButtonHint"
if flags & Qt.WindowStaysOnTopHint:
text += "\n| Qt.WindowStaysOnTopHint"
if flags & Qt.WindowStaysOnBottomHint:
text += "\n| Qt.WindowStaysOnBottomHint"
if flags & Qt.CustomizeWindowHint:
text += "\n| Qt.CustomizeWindowHint"
if flags & Qt.WindowTransparentForInput:
text += "\n| Qt.WindowTransparentForInput"
if flags & Qt.MSWindowsOwnDC:
text += "\n| Qt.MSWindowsOwnDC"
if flags & Qt.MaximizeUsingFullscreenGeometryHint:
text += "\n| Qt.MaximizeUsingFullscreenGeometryHint"

self.text_edit.setPlainText(text)


if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = ControllerWindow()
window.show()
sys.exit(app.exec())


Qt 窗口类型与窗口标志
https://muzing.top/posts/7fe03091/
作者
Muzing
发布于
2022年7月22日
更新于
2022年8月28日
许可协议