简介
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 版本。
# 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())