吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 607|回复: 2
收起左侧

[学习记录] 【PySide6】QtWidgets程序的三种主窗口控件

[复制链接]
PythonPan 发表于 2025-12-24 19:57
本帖最后由 PythonPan 于 2025-12-24 20:09 编辑

5 QtWidgets程序的三种主窗口控件

5.1 主要内容

前面介绍Qt程序的基本结构时,说过主窗口不是必须的但又不能没有,听起来有点自相矛盾。其实,想要理解也不难,那就要说说本章要介绍的主窗口控件。

前面介绍Qt程序的基本结构时,说主窗口算控件的一种,其实指的是用于产生主窗口的三种主窗口控件,分别是QWidget控件、QDialog控件、QMainWindow控件,它们与其他控件的区别是,对于一个窗口来说,只能创建一个。但它们又和其他控件的区别没那么大,因为从根上说,除了QWidget控件本身就是QWidget类,其他控件的基类都是QWidget类,所以QWidget控件具备的部分功能,所有控件都有。

因此,这么来看的话,主窗口的自相矛盾特性就很好解释了。

只要创建控件,都有主窗口控件之一——QWidget控件的功能,相当于无论如何都有主窗口(控件),所以主窗口(控件)是始终存在的。而其他控件从另一方面论证的话,又不算主窗口控件,所以主窗口(控件)又不是必须的。

当然,真要是较真的话,一个Qt程序不创建任何控件(真正意义上的没有主窗口)也可以运行,但是因为没有主窗口,所以不显示主窗口,没法正常点击结束,只能通过任务管理器(Windows系统,Linux系统通过命令)强制结束,这种状态的Qt程序是不能正常使用的。

除了主窗口控件与其他控件有所区别,三种主窗口控件之间也有区别:

  • QWidget控件(完整用法可参考 https://doc.qt.io/qtforpython-6/PySide6/QtWidgets/QWidget.html#PySide6.QtWidgets.QWidget ),QWidget类是所有控件的基类,可以说其他控件都是基于QWidget控件实现的。因此,该控件主要用于创建简单的窗口或者通用控件。如果需要给窗口增加工具栏、菜单栏、状态栏,则需要手动添加(默认QWidget控件不包括)。此外,想要让窗口变为模态窗口(只允许当前窗口获得焦点,符合要求的其他窗口不能获得焦点,除非关闭当前窗口)的话,只能使用setWindowModality方法(仅支持应用级模态Qt.WindowModality.ApplicationModal)手动设置窗口的模态:

    from PySide6.QtWidgets import (
      QApplication,
      QWidget,
    )
    from PySide6.QtCore import Qt
    
    app = QApplication()
    
    # 窗口1正常显示
    window = QWidget()
    window.setWindowTitle('窗口1')
    window.resize(400,300)
    window.show()
    # 窗口2模态显示
    window2 = QWidget()
    window2.resize(300,200)
    window2.setWindowTitle('窗口2')
    window2.setWindowModality(Qt.WindowModality.ApplicationModal)
    window2.show()
    
    app.exec()
  • QDialog控件(完整用法可参考 https://doc.qt.io/qtforpython-6/PySide6/QtWidgets/QDialog.html#PySide6.QtWidgets.QDialog ),该控件的基类是QWidget类,生成的窗口只有关闭按钮,没有最大化、最小化按钮,一般用于创建简单的对话框,很多对话框控件也是通过继承QDialog类实现的。当然,对话框一般不需要工具栏、菜单栏、状态栏,自然也不包括。不同于QWidget控件只能手动设置窗口的模态,该控件还支持通过exec方法显示窗口(同时进入无限循环,阻止后续代码的运行),此时的窗口为模态窗口(其模态为窗口级模态Qt.WindowModality.WindowModal):

    from PySide6.QtWidgets import (
      QApplication,
      QDialog,
    )
    
    app = QApplication()
    
    # 窗口1正常显示
    window = QDialog()
    window.setWindowTitle('窗口1')
    window.resize(400,300)
    window.show()
    
    # 窗口2模态显示
    window2=QDialog(window)
    window2.setWindowTitle('窗口2')
    window2.resize(300,200)
    window2.exec()
    
    # 不关闭窗口2的话,窗口3不显示
    window3=QDialog(window)
    window3.setWindowTitle('窗口3')
    window3.resize(300,200)
    window3.show()
    
    app.exec()

    如上面的代码所示,QDialog控件与QWidget控件不同,可以在创建时设置父控件,组成父子关系,让父子窗口同时显示(QWidget控件不支持这样操作)。关于应用级模态与窗口级模态的区别,以及不同父子关系对模态影响,可以参考本节的扩展内容,这里受限于篇幅不做展开。

  • QMainWindow控件(完整用法可参考 https://doc.qt.io/qtforpython-6/PySide6/QtWidgets/QMainWindow.html#PySide6.QtWidgets.QMainWindow ),该控件的基类是QWidget类,生成的窗口功能丰富,包含工具栏、菜单栏、状态栏(需要手动添加内容),一般用作程序的主窗口(适用于不想额外创建工具栏、菜单栏、状态栏的情况)。虽然该控件也支持setWindowModality方法,但不建议设置为模态窗口。以下为在状态栏中添加控件的示例:

    from PySide6.QtWidgets import (
      QApplication,
      QMainWindow,
      QPushButton
    )
    
    app = QApplication()
    
    window = QMainWindow()
    window.resize(400,300)
    window.setWindowTitle('MainWindow')
    window.statusBar().addWidget(QPushButton('Hello'))
    window.show()
    app.exec()

    2025_5_1.png

三种主窗口控件的直观对比可以参考下面的表格:

QWidget QDialog QMainWindow
继承关系 所有控件的基类 继承自QWidget 继承自QWidget
用途 简单窗口、通用控件 对话框 功能丰富的主窗口
工具栏、菜单栏、状态栏 需要手动实现 一般不添加 内置
模态支持 需要手动实现(通过setWindowModality方法) 内置(通过exec方法) 不推荐使用模态
中心区域 有(通过setCentralWidget方法)
衍生控件 QPushButton等基础控件 QFileDialogQMessageBox等对话框控件

5.2 扩展内容

5.2.1 控件与主窗口

在运行主窗口的show方法之前创建控件,需要指定控件的父控件为主窗口,这样创建出来控件才会显示在主窗口中。但是,在show方法之后创建的控件,则需要额外调用控件的show方法才能显示。

没有指定父控件为主窗口的控件都不属于主窗口,这样的控件显示(调用控件的show方法)时会额外创建一个窗口,并显示在新窗口中。

示例如下:

from PySide6.QtWidgets import (
    QApplication,
    QWidget,
    QLabel
)

app = QApplication()

window = QWidget()
window.resize(400,300)
window.setWindowTitle('主窗口')
window.show()

# 在主窗口的show方法之后创建控件,需要调用控件的show方法才能显示

# 标签1的父控件为window,所以显示在主窗口中
label1 = QLabel('标签1',window)
label1.show()
# 标签2没有父控件,所以会自动创建新窗口
label2 = QLabel('标签2')
label2.resize(400,300)
label2.setWindowTitle('新窗口')
label2.show()

app.exec()

2025_5_2.png

5.2.2 应用级模态与窗口级模态的区别

既然应用级模态与窗口级模态都能做到只允许当前窗口获得焦点,那为什么还要设计为两种模态,而不是合并为一种?存在即合理,既然有两种模态,肯定在用法上有所不同。接下来,就通过使用setWindowModality方法设置窗口的模态,看一下二者的区别。

先说应用级模态。无论其余窗口是使用QDialog控件创建,还是使用QWidget控件创建(只能创建为主窗口的兄弟窗口),也无论其余窗口的父子关系有多复杂,只要不是模态窗口及其子窗口,在关闭(或者隐藏)模态窗口之前,都不能获得焦点。

需要注意的是,对于主窗口以及其他与主窗口同级的兄弟窗口,如果全部关闭的话,程序会直接结束,哪怕它们的子窗口还存在或者处于显示状态。

示例如下:

from PySide6.QtWidgets import (
    QApplication,
    QDialog,
    QWidget,
    QPushButton
)
from PySide6.QtCore import Qt

app = QApplication()

# 窗口1正常显示
window = QWidget()
window.setWindowTitle('窗口1')
window.resize(400,300)
window.move(100,100)
window.show()

# 窗口2(窗口1的子窗口)模态显示
window2=QDialog(window)
window2.setWindowTitle('窗口2')
window2.resize(400,300)
window2.move(200,200)
# 设置为应用级模态
window2.setWindowModality(Qt.WindowModality.ApplicationModal)
# 隐藏窗口2
QPushButton('hide me',window2).clicked.connect(lambda:window2.hide())
window2.show()

# 窗口3(窗口1的子窗口,窗口2的兄弟窗口)正常显示
window3=QDialog(window)
window3.setWindowTitle('窗口3')
window3.resize(400,300)
window3.move(300,300)
window3.show()

# 窗口4(窗口2的子窗口)正常显示
window4=QDialog(window2)
window4.setWindowTitle('窗口4')
window4.resize(400,300)
window4.move(400,400)
window4.show()

# 窗口5(窗口1的兄弟窗口)正常显示
window5=QWidget()
window5.setWindowTitle('窗口5')
window5.resize(400,300)
window5.move(500,500)
window5.show()

app.exec()

再说窗口级模态。无论其余窗口是使用QDialog控件创建,还是使用QWidget控件创建(只能创建为主窗口的兄弟窗口),也无论其余窗口的父子关系有多复杂,只要与模态窗口的任一父窗口有父子关系,并且不是模态窗口及其子窗口,在关闭(或者隐藏)模态窗口之前,都不能获得焦点。

示例如下:

from PySide6.QtWidgets import (
    QApplication,
    QDialog,
    QWidget,
    QPushButton
)
from PySide6.QtCore import Qt

app = QApplication()

# 窗口1正常显示
window = QWidget()
window.setWindowTitle('窗口1')
window.resize(400,300)
window.move(100,100)
window.show()

# 窗口2(窗口1的子窗口)模态显示
window2=QDialog(window)
window2.setWindowTitle('窗口2')
window2.resize(400,300)
window2.move(200,200)
# 设置为窗口级模态
window2.setWindowModality(Qt.WindowModality.WindowModal)
# 隐藏窗口2
QPushButton('hide me',window2).clicked.connect(lambda:window2.hide())
window2.show()

# 窗口3(窗口1的子窗口,窗口2的兄弟窗口)正常显示
window3=QDialog(window)
window3.setWindowTitle('窗口3')
window3.resize(400,300)
window3.move(300,300)
window3.show()

# 窗口4(窗口2的子窗口)正常显示
window4=QDialog(window2)
window4.setWindowTitle('窗口4')
window4.resize(400,300)
window4.move(400,400)
window4.show()

# 窗口5(窗口1的兄弟窗口)正常显示
window5=QWidget()
window5.setWindowTitle('窗口5')
window5.resize(400,300)
window5.move(500,500)
window5.show()

app.exec()

可能看完代码和描述还是有点不太清楚,没关系,两个示例使用了相同的窗口父子关系,只是模态不同,接下来看看窗口的父子关系图:

2025_5_3.png

当窗口2的模态为应用级模态时,除了窗口4是窗口2的子窗口,不受任何模态的影响,窗口1、窗口3、窗口5都与窗口2同属于一个程序类实例(应用程序),所以,在关闭(或者隐藏)窗口2之前,不能获得焦点。

2025_5_4.png

当窗口2的模态为窗口级模态时,除了窗口4是窗口2的子窗口,不受任何模态的影响之外,窗口5与窗口2没有相同的父窗口(无限向上追溯,与窗口本身或者父窗口存在父子关系就算),也不受影响。窗口1、窗口3都与窗口2有相同的父窗口(无限向上追溯,与窗口本身或者父窗口存在父子关系就算),所以,在关闭(或者隐藏)窗口2之前,不能获得焦点。

2025_5_5.png

5.2.3 高亮主窗口或者其兄弟窗口

上节提到主窗口也可以有兄弟窗口,这里顺便再说一个与之相关的功能,那就是QApplication类的alert方法(完整用法可参考 https://doc.qt.io/qtforpython-6/PySide6/QtWidgets/QApplication.html#PySide6.QtWidgets.QApplication.alert )。该方法可以高亮并闪烁当前没有获得焦点的主窗口或者其兄弟窗口,只有当地获得焦点时或者到一定时间后才会停止高亮和闪烁。

具体参数可以参考上面的文档链接,以下为示例:

from PySide6.QtWidgets import (
    QApplication,
    QDialog,
    QWidget,
    QPushButton
)

app = QApplication()

# 窗口1正常显示
window = QWidget()
window.setWindowTitle('窗口1')
window.resize(400,300)
window.move(100,100)
window.show()

# 窗口2(窗口1的兄弟窗口)正常显示
window2=QDialog()
window2.setWindowTitle('窗口2')
window2.resize(400,300)
window2.move(200,200)
QPushButton('高亮主窗口',window2).clicked.connect(lambda:app.alert(window,3000))
window2.show()

# 窗口3(窗口1的兄弟窗口)正常显示
window3=QDialog()
window3.setWindowTitle('窗口3')
window3.resize(400,300)
window3.move(300,300)
QPushButton('高亮主窗口的兄弟窗口',window3).clicked.connect(lambda:app.alert(window2,3000))
window3.show()

app.exec()

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

laotzudao0 发表于 2025-12-24 20:32
QT的官方文档是我用过最全的文档了
ygq170063 发表于 2025-12-25 08:35
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2025-12-25 16:27

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表