python pyside2 Widget容器(QWidget ) 作者:马育民 • 2025-09-14 08:37 • 阅读:10002 # 介绍 [](https://www.malaoshi.top/upload/0/0/1GW1rHnYRIzk.png) 在 PySide2(Qt for Python)GUI 开发中,`QWidget` 是所有可视化控件的**基类**(如 QFrame、QPushButton、QLineEdit 均继承自 QWidget),同时也是最基础的 **容器控件**—— 用于承载子控件、组织界面结构。本文将从核心特性、基础操作、布局结合、自定义扩展四个维度,系统讲解 QWidget 的使用方法,为复杂界面开发打下基础。 **提示:** **一般很少使用** ### 作为 “控件基类” 的作用 所有 PySide2 可视化控件(除少数特殊组件外)均直接或间接继承自 QWidget,这意味着 QWidget 的核心属性与方法(如大小、位置、显示 / 隐藏)会被所有控件继承。例如: * QPushButton(按钮)继承自 QAbstractButton,而 QAbstractButton 继承自 QWidget; * QFrame(框架)直接继承自 QWidget,QLineEdit 则继承自 QFrame。 因此,掌握 QWidget 是理解其他控件的基础,其核心能力可概括为: | 核心能力 | 说明 | 继承后控件的表现 | | --------------------------- | ------------------------------------------ | ------------------------------------------------- | | 几何属性控制(位置 / 大小) | 控制控件在父容器中的坐标、宽度、高度 | 所有控件均可通过 `setGeometry()` 调整位置大小 | | 显示状态控制 | 控制控件的显示、隐藏、启用 / 禁用 | 所有控件均可调用 `show()`/`hide()`/`setEnabled()` | | 事件处理 | 响应鼠标点击、键盘输入、窗口 resize 等事件 | 所有控件均可重写 `mousePressEvent()` 等事件方法 | | 样式定制 | 通过 StyleSheet 调整外观 | 所有控件均可通过 `setStyleSheet()` 美化 | ### 作为 “容器控件” 的作用 当 QWidget 不承担具体交互功能(如按钮点击、文本输入)时,其主要作用是**承载子控件并组织界面结构**,常见应用场景: * **顶层窗口**:作为应用程序的主窗口(如 `QMainWindow` 的中心部件,或独立的弹窗窗口); * **子容器**:包裹一组相关控件(如 “登录表单” 的账号、密码输入框,用 QWidget 作为父容器统一管理); * **自定义控件基类**:基于 QWidget 扩展带特定功能的自定义控件(如自定义仪表盘、进度条)。 # 基础用法 ### 创建顶层窗口(独立窗口) QWidget 可直接作为顶层窗口使用,需设置窗口标题、大小等属性,调用 `show()` 显示: ``` from PySide2.QtWidgets import QApplication, QWidget import sys app = QApplication(sys.argv) # 初始化应用(所有 PySide2 程序必须有一个 QApplication) \# 1. 创建 QWidget 作为顶层窗口 window = QWidget() window.setWindowTitle("QWidget 顶层窗口示例") # 设置窗口标题 window.resize(400, 300) # 设置窗口初始大小(宽度 400px,高度 300px) window.move(100, 100) # 设置窗口在屏幕上的初始位置(x=100px,y=100px,左上角为原点) \# 2. 显示窗口 window.show() sys.exit(app.exec\_()) # 启动应用事件循环,等待用户操作 ``` ### 创建子控件(依附于父容器) QWidget 作为子控件时,需指定父容器(通过构造函数参数或 `setParent()` 方法),其位置会相对于父容器计算: ``` from PySide2.QtWidgets import QLabel \# 1. 创建顶层窗口(父容器) parent\_window = QWidget() parent\_window.setWindowTitle("QWidget 子控件示例") parent\_window.resize(400, 300) \# 2. 创建 QWidget 作为子容器(父容器为 parent\_window) child\_widget = QWidget(parent\_window) child\_widget.setGeometry(50, 50, 300, 200) # 相对于父容器的位置与大小(x=50, y=50, w=300, h=200) child\_widget.setStyleSheet("background-color: #F0F8FF;") # 设置背景色,便于区分 \# 3. 在子容器中添加标签(子控件的子控件) label = QLabel("这是子容器中的标签", child\_widget) label.move(20, 20) # 相对于子容器 child\_widget 的位置 parent\_window.show() sys.exit(app.exec\_()) ``` ### 关键方法说明: | 方法 | 功能描述 | 示例代码 | | ---------------------------------- | ------------------------------------------------------------ | ----------------------------------------------- | | `resize(width, height)` | 设置控件大小(宽度、高度) | `window.resize(400, 300)` | | `move(x, y)` | 设置控件位置(相对于父容器左上角的 x、y 坐标) | `child_widget.move(50, 50)` | | `setGeometry(x, y, width, height)` | 同时设置位置与大小(合并 move + resize) | `child_widget.setGeometry(50,50,300,200)` | | `size()` | 获取控件当前大小(返回 `QSize` 对象,含 `width()`/`height()` 方法) | `size = window.size()` → `width = size.width()` | | `pos()` | 获取控件当前位置(返回 `QPoint` 对象,含 `x()`/`y()` 方法) | `pos = child_widget.pos()` → `x = pos.x()` | ### 显示与状态控制 QWidget 提供多种方法控制显示状态与交互能力,满足不同场景需求: ``` \# 1. 显示与隐藏 window.show() # 显示控件(若为顶层窗口,显示窗口) window.hide() # 隐藏控件(若为顶层窗口,隐藏窗口) window.isVisible() # 判断控件是否可见(返回 True/False) \# 2. 启用与禁用(禁用后控件不可交互,外观变灰) button = QPushButton("点击按钮", window) button.setGeometry(20, 20, 100, 30) button.setEnabled(False) # 禁用按钮(无法点击) button.isEnabled() # 判断控件是否启用(返回 True/False) \# 3. 置顶窗口(仅顶层窗口生效) window.setWindowFlags(window.windowFlags() | Qt.WindowStaysOnTopHint) # 设置窗口置顶 window.setWindowFlags(window.windowFlags() & \~Qt.WindowStaysOnTopHint) # 取消置顶 \# 4. 全屏显示(仅顶层窗口生效) window.showFullScreen() # 全屏显示 window.showNormal() # 恢复正常大小 ``` # 与布局结合:自适应排版 QWidget 作为容器时,若手动通过 `setGeometry()` 定位子控件,会导致窗口缩放时子控件位置错乱。因此,**必须结合布局管理**实现自适应排版,核心步骤: 1. 创建 QWidget 作为父容器; 2. 创建布局(如 QVBoxLayout、QHBoxLayout); 3. 将子控件加入布局; 4. 通过 `setLayout()` 将布局绑定到 QWidget。 ### 实战示例:QWidget + QVBoxLayout 实现自适应表单 ``` from PySide2.QtWidgets import QVBoxLayout, QLineEdit, QPushButton, QLabel \# 1. 创建父容器 QWidget(顶层窗口) form\_window = QWidget() form\_window.setWindowTitle("QWidget + 布局 自适应表单") form\_window.resize(350, 250) \# 2. 创建垂直布局 v\_layout = QVBoxLayout() v\_layout.setSpacing(15) # 子控件间间距 15px v\_layout.setContentsMargins(20, 20, 20, 20) # 布局内边距(上下左右各 20px) \# 3. 向布局中添加子控件 title\_label = QLabel("用户登录") title\_label.setStyleSheet("font-size: 18px; font-weight: bold; text-align: center;") account\_edit = QLineEdit() account\_edit.setPlaceholderText("请输入账号") account\_edit.setStyleSheet("padding: 8px; border: 1px solid #DDDDDD; border-radius: 4px;") pwd\_edit = QLineEdit() pwd\_edit.setEchoMode(QLineEdit.Password) pwd\_edit.setPlaceholderText("请输入密码") pwd\_edit.setStyleSheet("padding: 8px; border: 1px solid #DDDDDD; border-radius: 4px;") login\_btn = QPushButton("登录") login\_btn.setStyleSheet(""" QPushButton { background-color: #2196F3; color: white; border: none; border-radius: 4px; padding: 10px; font-size: 14px; } QPushButton:hover { background-color: #1976D2; } """) \# 4. 将控件加入布局(顺序决定排列顺序:上→下) v\_layout.addWidget(title\_label) v\_layout.addWidget(account\_edit) v\_layout.addWidget(pwd\_edit) \# 加入弹性空白,将按钮推到下方(窗口缩放时按钮位置固定在底部) v\_layout.addStretch(1) v\_layout.addWidget(login\_btn) \# 5. 将布局绑定到父容器 QWidget form\_window.setLayout(v\_layout) form\_window.show() sys.exit(app.exec\_()) ``` #### 效果说明: * 窗口缩放时,账号 / 密码输入框宽度自动跟随窗口变化(因布局自适应); * `v_layout.addStretch(1)` 加入的弹性空白,确保登录按钮始终位于表单底部; * 无需手动调整子控件位置,布局自动维护控件间距与对齐方式。 # 事件处理 QWidget 作为基类,支持重写大量事件方法(如鼠标点击、键盘输入、窗口大小变化),实现自定义交互逻辑。事件处理的核心是**重写 QWidget 的内置事件方法**,常见事件如下: ### 鼠标事件(点击、移动、释放) ``` class CustomWidget(QWidget): def \_\_init\_\_(self, parent=None): super().\_\_init\_\_(parent) self.setWindowTitle("自定义鼠标事件") self.resize(400, 300) self.click\_pos = None # 记录鼠标点击位置 \# 重写鼠标按下事件(左键点击时触发) def mousePressEvent(self, event): if event.button() == Qt.LeftButton: # 判断是否为左键点击 self.click\_pos = event.pos() # 获取鼠标相对于当前控件的位置 print(f"鼠标左键点击:x={self.click\_pos.x()}, y={self.click\_pos.y()}") \# 重写鼠标移动事件(鼠标在控件上移动时触发) def mouseMoveEvent(self, event): if self.click\_pos is not None: # 仅当左键按下时才响应移动 current\_pos = event.pos() dx = current\_pos.x() - self.click\_pos.x() dy = current\_pos.y() - self.click\_pos.y() \# 移动窗口(若为顶层窗口) self.move(self.x() + dx, self.y() + dy) \# 重写鼠标释放事件(鼠标按键释放时触发) def mouseReleaseEvent(self, event): if event.button() == Qt.LeftButton: self.click\_pos = None # 重置点击位置 print("鼠标左键释放") \# 使用自定义控件 app = QApplication(sys.argv) custom\_window = CustomWidget() custom\_window.show() sys.exit(app.exec\_()) ``` ### 键盘事件(按键按下、释放) ``` class KeyEventWidget(QWidget): def \_\_init\_\_(self, parent=None): super().\_\_init\_\_(parent) self.setWindowTitle("自定义键盘事件") self.resize(400, 300) self.label = QLabel("按下键盘查看输出...", self) self.label.setGeometry(20, 20, 360, 30) \# 重写键盘按下事件 def keyPressEvent(self, event): \# 获取按下的键(如 Qt.Key\_A、Qt.Key\_Escape) key = event.key() \# 获取按键文本(如 "A"、"Escape") key\_text = event.text() if event.text() else event.keyToString() self.label.setText(f"按下键盘:{key\_text}(键值:{key})") \# 特殊键处理(如 Esc 键关闭窗口) if key == Qt.Key\_Escape: self.close() app = QApplication(sys.argv) key\_window = KeyEventWidget() key\_window.show() sys.exit(app.exec\_()) ``` ### 窗口大小变化事件 ``` class ResizeWidget(QWidget): def \_\_init\_\_(self, parent=None): super().\_\_init\_\_(parent) self.setWindowTitle("窗口大小变化事件") self.resize(400, 300) \# 重写窗口大小变化事件 def resizeEvent(self, event): \# 获取变化后的窗口大小 new\_size = event.size() print(f"窗口大小变化:宽={new\_size.width()}, 高={new\_size.height()}") \# 可在此处调整子控件大小(若未使用布局) \# 例如:让标签宽度跟随窗口宽度变化 if hasattr(self, "label"): self.label.setGeometry(20, 20, new\_size.width() - 40, 30) \# 初始化时添加标签 def showEvent(self, event): self.label = QLabel("窗口大小变化时我会自适应宽度", self) self.label.setGeometry(20, 20, self.width() - 40, 30) app = QApplication(sys.argv) resize\_window = ResizeWidget() resize\_window.show() sys.exit(app.exec\_()) ``` 原文出处:http://malaoshi.top/show_1GW1rHsEnUFz.html