一、控件(Widget)基本概念

        控件(Widget)本质是 图形界面(GUI)里的 “基础功能组件” —— 就像搭界面的 “乐高积木”,是用户能看见、能操作(或仅展示)的最小单元,不用懂 Qt 底层,记住 3 个核心点即可:

1. 控件的核心作用:“承上启下”

  • 对用户:提供交互入口(比如点按钮、输文字)或信息展示(比如看文本、图片);
  • 对程序:接收用户操作(比如点击、输入),或呈现程序处理结果(比如显示计算结果)。

2. 常见控件类型

  • 容器类:窗口(Window,最外层 “大容器”)、面板(Widget,把多个小控件归为一组等)。
  • 交互类:按钮(Button,点一下触发功能)、输入框(Line Edit,输文字 / 数字)、复选框(CheckBox,选 / 不选)、下拉菜单(ComboBox,选列表里的选项)等;
  • 展示类:标签(Label,显示文字 / 图片,不能改)、文本框(Text Edit,显示多行文字)、进度条(Progress Bar,展示加载 / 处理进度);

3. 控件的关键特性

  • 有 “属性”:比如按钮的文字、大小、颜色,输入框的默认值、是否可编辑;
  • 能 “响应事件”:用户操作会触发反应(比如点按钮→程序执行某个功能,输文字→程序接收输入内容);
  • 可 “组合嵌套”:比如把按钮、输入框放进一个面板,再把面板放进窗口,搭出复杂界面。

    二、容器类控件详解

            PySide6 中的容器类控件是用于组织、管理其他子控件的核心组件,它们不仅能承载多个控件,还提供布局管理、分组显示、页面切换等功能。

    • 容器类控件核心分类

            PySide6 的容器控件主要分为两类:

            基础布局容器:以布局管理为核心(如 QWidget、QFrame);

            功能型容器:提供分组、分页、滚动等附加功能(如 QGroupBox、QTabWidget、QScrollArea 等)。


    • QWidget(基础万能容器)

            QWidget 是所有 PySide6 控件的基类,本身也是最基础的容器 —— 任何 QWidget 实例都可以作为父控件,承载其他子控件,并通过布局管理器组织子控件的位置。                

    • 核心特性:
    1. 纯空白,可自定义背景、边框等;
    2. 支持布局嵌套;
    3. 可作为独立窗口或子容器使用。
    import sys
    
    from PySide6.QtWidgets import QApplication, QWidget
    
    
    # 定义一个父容器类,作为主窗口
    class MainWindow(QWidget):
      def __init__(self):
        super().__init__()
        self.setWindowTitle("QWidget 基础容器示例")
        self.setStyleSheet("background-color: green;")  # 设置容器的显示样式,方便观察
        self.resize(300, 200)
    
        # 创建 QWidget 作为子容器
        son_container = QWidget(self)
        son_container.setGeometry(10, 10, 280, 180)  # 设置子容器的几何特征,位置为 (10, 10),大小为 (280, 180)
        son_container.setStyleSheet("background-color: red")  # 设置容器的显示样式,方便观察
    
    
    if __name__ == "__main__":
      app = QApplication(sys.argv)  # 创建 QApplication 对象
      main_window = MainWindow()  # 创建 MainWindow 实例对象
      main_window.show()   # 显示窗口
      sys.exit(app.exec())   # 退出应用程序

            在上面这个实例中,有两个QWidget容器,背景色为绿色的是主窗口main_window,背景色为红色的是主窗口的子容器son_container。

    继续,在子容器son_container中放置控件:

    import sys
    
    from PySide6.QtWidgets import QApplication, QWidget, QPushButton
    
    
    # 定义一个父容器类,作为主窗口
    class MainWindow(QWidget):
      def __init__(self):
        super().__init__()
        self.setWindowTitle("QWidget 基础容器示例")
        self.setStyleSheet("background-color: green;")  # 设置容器的显示样式,方便观察
        self.resize(300, 200)
    
        # 创建 QWidget 作为子容器
        son_container = QWidget(self)
        son_container.setGeometry(10, 10, 280, 180)  # 设置子容器的几何特征,位置为 (10, 10),大小为 (280, 180)
        son_container.setStyleSheet("background-color: red")  # 设置容器的显示样式,方便观察
        button = QPushButton("子容器的按钮", son_container)  # 创建按钮,作为子容器的子控件
        button.setStyleSheet("background-color: gray")  # 设置按钮的显示样式
        button.setGeometry(40, 50, 200, 40)  # 设置按钮的几何特征
    
    
    if __name__ == "__main__":
      app = QApplication(sys.argv)  # 创建 QApplication 对象
      main_window = MainWindow()  # 创建 MainWindow 实例对象
      main_window.show()   # 显示窗口
      sys.exit(app.exec())   # 退出应用程序

    放置了一个按钮在子容器中。


    • QFrame(带边框的容器)

            QFrame 继承自 QWidget,核心扩展是内置边框样式,适合需要视觉分隔的容器场景(如分组、分隔区域)。

    • 核心特性:
    1. 支持多种边框样式(如矩形、阴影、线条);
    2. 可设置边框宽度、颜色、阴影效果;
    3. 完全兼容 QWidget 的容器能力。

    常用边框样式(QFrame.Shape)

    样式常量 说明
    QFrame.NoFrame 无边框(默认)
    QFrame.Box 矩形边框(带背景)
    QFrame.HLine/VLine 水平 / 垂直线(分隔线)
    QFrame.Panel 面板样式(凸起 / 凹陷)
    import sys
    from PySide6.QtWidgets import (QApplication, QWidget, QFrame, QPushButton)
    
    class MainWindow(QWidget):
      def __init__(self):
        super().__init__()
        self.setWindowTitle("QFrame 带边框容器示例")
        self.resize(350, 150)
    
        # 创建 QFrame 容器
        frame = QFrame(self)
        frame.setFrameShape(QFrame.Box) # 矩形边框
        frame.setFrameShadow(QFrame.Raised) # 凸起阴影
        frame.setLineWidth(2) # 边框宽度
        frame.setStyleSheet("background-color: green;")  # 背景色
        frame.setGeometry(60, 30, 230, 90)  # 设置容器位置和大小
    
        # 容器内的控件
        button = QPushButton("登录",frame)  # 添加按钮
        button.setGeometry(80, 30, 70, 30)  # 设置按钮位置和大小
        button.setStyleSheet("background-color: lightgray;")   # 按钮背景色
    
    
    
    if __name__ == "__main__":
      app = QApplication(sys.argv)
      window = MainWindow()
      window.show()
      sys.exit(app.exec())


    • QGroupBox(分组容器)

            QGroupBox 是专为逻辑分组设计的容器,自带可自定义的标题,支持折叠 / 展开(可选),适合将相关控件归类(如表单分组、功能模块分组)。

    • 核心特性:
    1. 显示分组标题(可设置对齐方式);
    2. 支持勾选框(setCheckable(True)),勾选时才启用组内控件;
    3. 视觉上有明显的分组边框。
    import sys
    from PySide6.QtWidgets import (QApplication, QWidget, QGroupBox,
                                   QVBoxLayout, QRadioButton, QCheckBox, QPushButton, QFrame)
    
    class MainWindow(QWidget):
      def __init__(self):
        super().__init__()
        self.setWindowTitle("QGroupBox 分组容器示例")
        self.resize(400, 400)
    
        # 创建一个QFrame对象作为分组容器的父控件
        frame = QFrame(self)
        frame.setGeometry(50, 50, 300, 300)
        frame.setStyleSheet("QFrame {border: 3px solid green;background-color: blue}")
    
        # 1. 创建分组容器
        group = QGroupBox("group群组容器", frame)  # group群组容器
        group.setGeometry(50, 50, 200, 100)   # 设置组容器位置和大小
        group.setStyleSheet("QGroupBox {border: 3px solid green;background-color: red};")  # 设置组容器背景色
    
        # 2. 群组内的按钮控件
        button_1= QPushButton("按钮1", group)
        button_2= QPushButton("按钮2", group)
        button_1.setGeometry(50, 20, 100, 30)
        button_2.setGeometry(50, 60, 100, 30)
    
        # 3. 另一个不属于群组的按钮
        button_3 = QPushButton("按钮3", frame)
        button_3.setGeometry(100, 200, 100, 30)
    
    
    if __name__ == "__main__":
      app = QApplication(sys.argv)
      window = MainWindow()
      window.show()
      sys.exit(app.exec())

    • QTabWidget(分页容器)

            QTabWidget 是多页面切换容器,通过标签页(Tab)分隔不同功能模块,适合界面功能较多、需要分页面展示的场景(如软件设置界面、多模块工具)。

    • 核心特性:
    1. 可添加多个标签页,每个标签页对应一个 QWidget 容器;
    2. 支持自定义标签文本、图标、关闭按钮;
    3. 可设置标签位置(上 / 下 / 左 / 右);
    4. 支持标签页切换信号(currentChanged)。
    • import sys
      
      from PySide6.QtWidgets import (QApplication, QWidget, QTabWidget,
                                     QLabel)
      
      
      class MainWindow(QWidget):
        def __init__(self):
          super().__init__()
          self.setWindowTitle("QTabWidget 分页容器示例")
          self.resize(400, 300)
      
          # 1. 创建分页容器
          tab_widget = QTabWidget(self)
          tab_widget.setTabPosition(QTabWidget.North) # 标签在顶部(默认)
          tab_widget.setTabsClosable(False) # 不显示关闭按钮
          tab_widget.setGeometry(10, 10, 380, 280)
      
          # 2. 页面1
          page1 = QWidget()
          label1 = QLabel("这是页面 1 的内容", page1)
          label1.setGeometry(50, 50, 100, 50)
          tab_widget.addTab(page1, "基础设置") # 添加页面,设置标签文本
      
          # 3. 页面2
          page2 = QWidget()
          label2 = QLabel("这是页面 2 ", page2)
          label2.setGeometry(50, 50, 100, 50)
          tab_widget.addTab(page2, "高级设置")  # 添加页面,设置标签文本
      
      
      
      if __name__ == "__main__":
        app = QApplication(sys.argv)
        window = MainWindow()
        window.show()
        sys.exit(app.exec())

    
    
    • QScrollArea(滚动容器)

            当子控件内容超出可视区域时,QScrollArea 会自动显示滚动条,适合展示长列表、大表单、大图片等场景。

    • 核心特性
    1. 可以自动生成水平 / 垂直滚动条(按需显示);
    2. 可设置滚动条策略(始终显示 / 始终隐藏 / 按需);
    3. 子控件需通过 setWidget() 绑定。
    import sys
    from PySide6.QtWidgets import (QApplication, QWidget, QScrollArea, QPushButton)
    from PySide6.QtCore import Qt
    
    class MainWindow(QWidget):
      def __init__(self):
        super().__init__()
        self.setWindowTitle("QScrollArea 滚动容器示例")
        self.resize(300, 200)
    
        # 1. 创建滚动容器
        scroll_area = QScrollArea(self)
        # 设置滚动条策略
        scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)  # 水平滚动条(按需显示)
        scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)    # 垂直滚动条(按需显示)
        scroll_area.setGeometry(0, 0, 150, 150)   # 滚动区域大小
    
        # 2. 创建子容器(承载大量控件)
        content_widget = QWidget(self)
        content_widget.setGeometry(0, 0, 300, 300)
    
        # 3. 添加大量按钮(超出可视区域)
        for i in range(20):
          btn = QPushButton(f"按钮 {i+1}", content_widget)
          btn.setGeometry(0, i * 40, 200, 30)
    
        # 4. 将子容器绑定到滚动区域
        scroll_area.setWidget(content_widget)
    
    
    
    if __name__ == "__main__":
      app = QApplication(sys.argv)
      window = MainWindow()
      window.show()
      sys.exit(app.exec())
    
    
    • QStackedWidget(堆叠容器)

            QStackedWidget 是不可见的分页容器(无标签页),同一时间只显示一个子控件(页面),适合通过按钮 / 下拉框手动切换页面的场景(如向导界面、步骤式操作)。

    • 核心特性
    1. 子控件以 “堆叠” 方式存放,仅显示当前索引的页面;
    2. 轻量级(无标签栏,节省界面空间);
    3. 支持通过 setCurrentIndex() 或 setCurrentWidget() 切换页面;
    4. 触发 currentChanged 信号。
    import sys
    from PySide6.QtWidgets import (QApplication, QWidget, QStackedWidget,
                   QVBoxLayout, QHBoxLayout, QPushButton, QLabel)
    
    class MainWindow(QWidget):
      def __init__(self):
        super().__init__()
        self.setWindowTitle("QStackedWidget 堆叠容器示例")
        self.resize(400, 400)
    
        # 1. 创建堆叠容器
        stacked_widget = QStackedWidget(self)
    
        # 2. 创建多个页面
        page1 = QLabel("堆叠页面 1  ", self)
        page1.setGeometry(50, 0, 300, 200)
    
        page2 = QLabel("堆叠页面 2  ", self)
        page2.setGeometry(50, 0, 300, 200)
    
        page3 = QLabel("堆叠页面 3  ", self)
        page3.setGeometry(50, 0, 300, 200)
    
    
        # 3. 添加页面到堆叠容器
        stacked_widget.addWidget(page1)
        stacked_widget.addWidget(page2)
        stacked_widget.addWidget(page3)
    
        # 4. 切换按钮
        btn_prev = QPushButton("上一步", self)
        btn_prev.setGeometry(100, 200, 100, 50)
        btn_next = QPushButton("下一步", self)
        btn_next.setGeometry(200, 200, 100, 50)
        btn_prev.clicked.connect(lambda: self.switch_page(stacked_widget, -1))   # 连接上一步按钮的点击事件
        btn_next.clicked.connect(lambda: self.switch_page(stacked_widget, 1))   # 连接下一步按钮的点击事件
    
    
    
      def switch_page(self, stacked, step):
        """切换页面"""
        current_idx = stacked.currentIndex()
        new_idx = current_idx + step
        # 边界检查
        if 0 <= new_idx < stacked.count():
          stacked.setCurrentIndex(new_idx)
    
    if __name__ == "__main__":
      app = QApplication(sys.argv)
      window = MainWindow()
      window.show()
      sys.exit(app.exec())

    三、容器控件选型指南

           

    容器控件 核心场景 关键优势
    QWidget 通用容器、布局嵌套 轻量、万能、无额外样式
    QFrame 带边框的分隔区域 内置边框样式、阴影效果
    QGroupBox 逻辑分组、表单归类 自带标题、支持勾选启用
    QTabWidget 多标签页切换(可视化) 直观的标签切换、易操作
    QScrollArea 内容超出可视区域 自动滚动、适配长内容
    QStackedWidget 无标签页的页面切换 轻量、节省空间、手动控制切换


    四、核心注意事项

    布局绑定:容器的子控件最好通过布局管理器(如 QVBoxLayout)绑定,否则子控件可能无法正  常显示 / 自适应;下一节将会学习布局管理器,并重新编写本节代码。

    父子关系:子控件的父对象需设置为容器,或通过布局添加(布局会自动设置父对象),否则控件可能 “丢失”;

    样式定制:可通过 setStyleSheet() 自定义容器的背景、边框、间距等样式;

    性能优化:对于大量子控件的容器(如 QScrollArea),建议使用 QListView/QTableView 等视图控件(而非手动添加大量按钮 / 标签),提升渲染性能。

            通过合理选择容器控件,可大幅提升 GUI 界面的结构化和易用性,结合布局管理器能实现灵活、自适应的界面布局。

    五、补充知识:Qt 框架中父子继承的机制

    上面的代码中,有以下的语句

    class MainWindow(QWidget):
      def __init__(self):
        super().__init__() 
        # 创建 QWidget 作为子容器
        son_container = QWidget(self)

            son_container = QWidget(self),这行代码很重要,它的含义是本对象son_container是QWidget实例,它的父对象是括号中的对象self,在很多场合,括号中的父对象必不可少。

    下面是详细解释:

            PySide6 基于 Qt 框架,Qt 的控件(QWidget 及其子类)遵循对象树(Parent-Child)机制,给控件指定父对象会带来 4 个关键效果:

    1. 自动管理内存(最核心)

    Qt 的对象树会自动维护父子关系:

    • 父对象被销毁时,会自动递归销毁所有子对象;
    • 如果不指定父对象,子控件需要手动管理内存(容易导致内存泄漏)。

    比如上面的代码中:

    • son_container 是 MyWindow 的实例子控件;
    • 当关闭 MyWindow 窗口(实例被销毁)时,son_container 会被自动销毁,无需手动 del 或 deleteLater()
    2. 控件的显示归属
    • 子控件会显示在父控件的绘制区域内(而非独立窗口);
    • 如果不给 QWidget 指定父对象,它会默认成为一个独立的空白窗口(需要手动调用 show() 才会显示);
    • 指定父对象后,子控件的显示 / 隐藏会跟随父控件(父控件隐藏,子控件也隐藏)。
    3. 坐标系统的相对化

    子控件的坐标(x/y)是相对于父控件的左上角,而非屏幕全局坐标。比如:

    self.son_container.move(10, 10)  # 相对于 MyWindow 窗口的左上角偏移 10px,而非屏幕左上角
    
    4. 事件传递与焦点管理

            父控件会参与子控件的事件分发(比如鼠标、键盘事件),Qt 的事件系统会优先向子控件传递事件,再传递给父控件。

    如果写成 son_container = QWidget()(无父对象):

    1. son_container 会成为一个独立的空白窗口,需要手动 son_container.show() 才会显示;
    2. 内存需要手动管理(比如在窗口关闭时调用 son_container.deleteLater()),否则会造成内存泄漏;
    3. 它的坐标是屏幕全局坐标,而非相对某个窗口;
    4. 它的生命周期和父窗口无关,即使主窗口关闭,这个空白窗口可能还会残留。

    总结一下:

    QWidget(self) 中的父控件,核心作用是内存自动管理 + 显示归属 + 相对坐标 + 事件传递

            这是 PySide6 开发中最基础也最关键的机制,几乎所有控件创建时都会指定父对象(通常是 self),避免内存泄漏和控件显示异常。

    Logo

    魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

    更多推荐