介绍
在 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() 定位子控件,会导致窗口缩放时子控件位置错乱。因此,必须结合布局管理实现自适应排版,核心步骤:
创建 QWidget 作为父容器;
创建布局(如 QVBoxLayout、QHBoxLayout);
将子控件加入布局;
通过
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\_())
