PySide6从0开始学习的笔记(六) 控件(Widget)之按钮类

前面已经掌握了pyside6的基本框架和应用。从这一节开始详细展开学习pyside6的更多技术细节。


PySide6 中的交互类控件是用户与应用程序进行双向沟通的核心组件,涵盖按钮、输入框、选择器、滑块等,这类控件支持用户操作(点击、输入、选择等)并能触发响应逻辑。

这一节,学习按钮类。

按钮是最基础的交互控件,用于触发即时操作,PySide6 提供了多种功能化按钮变体。

1. QPushButton(普通按钮)

核心作用:响应点击事件,执行自定义逻辑(如提交表单、关闭窗口)。

关键特性

  • 支持文本 / 图标显示,可设置快捷键(如 &OK 对应 Alt+O);
  • 可设置禁用 / 启用状态、默认按钮(回车触发)、复选模式(toggle)。

示例代码

python 复制代码
import sys
from PySide6.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout

class ButtonDemo(QWidget):
    def __init__(self):
        super().__init__()
        self.init_ui()

    def init_ui(self):
        self.setWindowTitle("QPushButton 示例")
        layout = QVBoxLayout()

        # 基础按钮
        btn1 = QPushButton("普通按钮")
        btn1.clicked.connect(self.on_btn1_clicked)  # 绑定点击信号

        # 带快捷键的按钮(Alt+S)
        btn2 = QPushButton("&Save") 
        btn2.clicked.connect(lambda: print("保存操作"))

        # 切换模式按钮(复选效果)
        btn3 = QPushButton("切换按钮")
        btn3.setCheckable(True)
        btn3.toggled.connect(self.on_btn3_toggled)

        # 禁用按钮
        btn4 = QPushButton("禁用按钮")
        btn4.setEnabled(False)

        layout.addWidget(btn1)
        layout.addWidget(btn2)
        layout.addWidget(btn3)
        layout.addWidget(btn4)
        self.setLayout(layout)

    def on_btn1_clicked(self):
        print("普通按钮被点击")

    def on_btn3_toggled(self, checked):
        print(f"切换按钮状态: {'选中' if checked else '未选中'}")

if __name__ == "__main__":
    app = QApplication(sys.argv)
    demo = ButtonDemo()
    demo.show()
    sys.exit(app.exec())
2. QToolButton(工具按钮)

核心作用:常用于工具栏,支持图标优先显示、下拉菜单、箭头样式。

关键特性

  • 可设置 setToolButtonStyle() 控制图标 / 文本显示方式(如仅图标、文本在下);
  • 支持关联下拉菜单(setMenu()),设置触发方式(点击 / 悬停)。
python 复制代码
import sys

from PySide6.QtGui import QIcon
from PySide6.QtWidgets import (
    QApplication, QMainWindow, QToolBar, QToolButton,
    QWidget
)


class ToolButtonDemo(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("QToolButton 详解")
        self.resize(400, 200)

        # 1. 初始化工具栏
        self.init_toolbar()

        # 2. 初始化中心部件
        self.init_centralWidget()

    def init_toolbar(self):
        # 创建工具栏
        toolbar = QToolBar("主工具栏")
        self.addToolBar(toolbar)

        # 基础工具按钮(仅图标)
        btn1 = QToolButton()
        btn1.setIcon(QIcon("new.ico"))  # 图标
        btn1.setToolTip("新建文件")  # 悬浮提示
        btn1.clicked.connect(lambda: print("新建文件"))
        toolbar.addWidget(btn1)

    def init_centralWidget(self):
        # 中心窗口布局
        central = QWidget()
        self.setCentralWidget(central)



if __name__ == "__main__":
    app = QApplication(sys.argv)
    demo = ToolButtonDemo()
    demo.show()
    sys.exit(app.exec())
3. QCheckBox(复选框)

核心作用:多选项选择(可多选),支持三态(选中 / 未选中 / 半选中,如子项部分选中)。

关键信号:stateChanged(int)(状态变化时触发,0 = 未选,1 = 半选,2 = 选中)。

python 复制代码
import sys

from PySide6.QtWidgets import QApplication, QMainWindow, QHBoxLayout, QCheckBox


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.cb = QCheckBox("三态复选框", self)
        self.cb.setTristate(True)  # 启用半选状态
        # 状态变化(支持半选)
        self.cb.stateChanged[int].connect(lambda state: print(f"状态:{state}"))
        # 仅点击选中/未选
        self.cb.clicked[bool].connect(lambda checked: print(f"选中:{checked}"))
        self.cb.clicked.connect(lambda : print("被点击了"))
        layout = QHBoxLayout()
        layout.addWidget(self.cb)
        self.setLayout(layout)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())
4. QRadioButton(单选按钮)

核心作用:互斥选项选择(同一组内仅能选一个),需通过 QButtonGroup 管理互斥性。

示例

python 复制代码
from PySide6.QtWidgets import QApplication, QPushButton, QWidget, QVBoxLayout, QButtonGroup, QRadioButton
from PySide6.QtCore import Slot, QMetaObject  # 导入 slot 装饰器

class MyWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.init_ui()


    def init_ui(self):
        self.setLayout(QVBoxLayout())
        self.btn_group = QButtonGroup()
        radio1 = QRadioButton("选项1")
        radio2 = QRadioButton("选项2")
        radio3 = QRadioButton("选项3")
        radio1.setChecked(True)
        self.btn_group.addButton(radio1)
        self.btn_group.addButton(radio2)
        self.btn_group.addButton(radio3)
        radio1.toggled.connect(self.on_radio_toggled)
        radio2.toggled.connect(self.on_radio_toggled)
        radio3.toggled.connect(self.on_radio_toggled)
        self.layout().addWidget(radio1)
        self.layout().addWidget(radio2)
        self.layout().addWidget(radio3)

    @Slot(bool)
    def on_radio_toggled(self):
        print(f"本次翻转的按钮是{self.sender().text()}: {self.sender().isChecked()}")
        print(f"本次被选中的是:{self.btn_group.checkedButton().text()}")



if __name__ == "__main__":
    app = QApplication([])
    window = MyWindow()
    window.show()
    app.exec()

原生的单选按钮,选中标志图片和文字都很小,如果有需求,可以使用设置样式表,也就是setStyleSheet()的方法将其设置为自己的图片,同样的,它的字体也可以更改。

创建两个大小为40*40像素的图片文件,分别命名为Checked.png和unChecked.png:

然后:

python 复制代码
import sys
from PySide6.QtWidgets import (
    QApplication, QWidget, QRadioButton,
    QVBoxLayout, QButtonGroup, QLabel
)
from PySide6.QtCore import Qt


class CustomRadioButtonDemo(QWidget):
    def __init__(self):
        super().__init__()
        self.init_ui()

    def init_ui(self):
        self.setWindowTitle("自定义QRadioButton(字体+图片)")
        self.resize(400, 300)

        # 1. 创建布局
        layout = QVBoxLayout()
        layout.setSpacing(20)
        layout.setContentsMargins(40, 40, 40, 40)

        # 2. 创建单选按钮组(保证互斥)
        radio_group = QButtonGroup(self)

        # 3. 创建自定义单选按钮
        radio1 = QRadioButton("选项1(自定义样式)")
        radio2 = QRadioButton("选项2(自定义样式)")
        radio3 = QRadioButton("选项3(禁用状态)")
        radio3.setEnabled(False)  # 禁用按钮,测试禁用样式

        # 4. 绑定按钮到组
        radio_group.addButton(radio1)
        radio_group.addButton(radio2)
        radio_group.addButton(radio3)

        # 5. 自定义样式(核心:字体 + 图片替换)
        self.set_radio_style(radio1)
        self.set_radio_style(radio2)
        self.set_radio_style(radio3)

        # 6. 信号绑定(可选,测试选中状态)
        radio_group.buttonClicked.connect(
            lambda btn: print(f"选中: {btn.text()}")
        )

        # 7. 添加到布局
        layout.addWidget(radio1)
        layout.addWidget(radio2)
        layout.addWidget(radio3)
        self.setLayout(layout)

    def set_radio_style(self, radio_btn):
        """设置单选按钮的自定义样式(字体+图片)"""
        # 替换为你的图片路径(绝对路径/相对路径/QRC路径)
        unchecked_img = "unChecked.png"  # 未选中图片
        checked_img = "Checked.png"  # 选中图片

        # 样式表语法:CSS 风格,支持伪状态和子控件
        style_sheet = f"""
            /* 全局字体设置 */
            QRadioButton {{
                font-family: "Microsoft YaHei";  /* 字体 */
                font-size: 16px;                /* 字体大小 */
                font-weight: 500;               /* 字体粗细(400=常规,700=粗体) */
                color: #333333;                /* 字体颜色 */
                spacing: 10px;                 /* 单选框与文本的间距 */
            }}

            /* 禁用状态的字体 */
            QRadioButton:disabled {{
                color: #999999;  /* 禁用时字体变灰 */
            }}

            /* 单选框指示器(默认圆形)- 未选中状态 */
            QRadioButton::indicator {{
                width: 20px;                   /* 指示器宽度(匹配图片尺寸) */
                height: 20px;                  /* 指示器高度 */
                image: url({unchecked_img});   /* 未选中图片 */
            }}

            /* 选中状态的指示器 */
            QRadioButton::indicator:checked {{
                image: url({checked_img});     /* 选中图片 */
            }}

            /* 悬浮状态的指示器(可选) */
            QRadioButton::indicator:hover {{
                opacity: 0.9;  /* 悬浮时轻微透明 */
            }}

            /* 禁用状态的指示器 */
            QRadioButton::indicator:disabled {{
                opacity: 0.5;  /* 禁用时半透明 */
            }}
        """
        radio_btn.setStyleSheet(style_sheet)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    demo = CustomRadioButtonDemo()
    demo.show()
    sys.exit(app.exec())

运行结果:

除了使用设置样式表,还可以使用重写绘制事件的方法(def paintEvent(self, event)

),来实现动态图片的显示,比如我们可以利用复选框(QCheckBox)或按压按钮(QPushButton),实现一个电气scada界面中常见的"带指示灯按钮":

python 复制代码
import sys
from PySide6.QtWidgets import (QApplication, QWidget, QCheckBox,
                               QVBoxLayout, QLabel, QPushButton)
from PySide6.QtCore import Qt, QSize
from PySide6.QtGui import QPixmap, QPainter, QMouseEvent


class IndicatorPushButton(QPushButton):
    """带指示灯的按钮"""

    def __init__(self, parent=None):
        super().__init__(parent)
        self.setCheckable(True)

        # 初始化图片路径(请替换为你的实际图片路径)
        self.img_unchecked_not_pressed = "unChecked_notPressed.png"
        self.img_unchecked_pressed = "unChecked_pressed.png"
        self.img_checked_not_pressed = "checked_notPressed.png"
        self.img_checked_pressed = "checked_pressed.png"

        # 初始化状态变量
        self.is_pressed = False  # 记录是否被按压

        # 设置按钮大小(根据你的图片尺寸调整)
        self.setFixedSize(60, 60)

        # 取消默认的勾选框样式
        # self.setStyleSheet("QCheckBox { border: none; }")

    def paintEvent(self, event):
        """重写绘制事件,根据状态绘制对应图片"""
        painter = QPainter(self)
        painter.setRenderHint(QPainter.Antialiasing)

        # 根据状态选择对应的图片
        if self.isChecked():
            img_path = self.img_checked_pressed if self.is_pressed else self.img_checked_not_pressed
        else:
            img_path = self.img_unchecked_pressed if self.is_pressed else self.img_unchecked_not_pressed

        # 加载并绘制图片
        pixmap = QPixmap(img_path)
        if not pixmap.isNull():
            # 缩放图片适配按钮大小(保持比例)
            scaled_pixmap = pixmap.scaled(self.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation)
            # 居中绘制
            x = (self.width() - scaled_pixmap.width()) // 2
            y = (self.height() - scaled_pixmap.height()) // 2
            painter.drawPixmap(x, y, scaled_pixmap)
        else:
            # 图片加载失败时绘制默认提示
            painter.drawText(self.rect(), Qt.AlignCenter, "图片加载失败")

    def mousePressEvent(self, event):
        """鼠标按下事件:更新按压状态并重绘"""
        if event.button() == Qt.LeftButton:
            self.is_pressed = True
            self.update()  # 触发重绘
        super().mousePressEvent(event)

    def mouseReleaseEvent(self, event):
        """鼠标释放事件:更新按压状态并重绘"""
        if event.button() == Qt.LeftButton:
            self.is_pressed = False
            self.update()  # 触发重绘
        super().mouseReleaseEvent(event)

    def leaveEvent(self, event):
        """鼠标离开按钮时,重置按压状态(防止拖拽离开时状态异常)"""
        self.is_pressed = False
        self.update()
        super().leaveEvent(event)


class DemoWindow(QWidget):
    """演示窗口"""

    def __init__(self):
        super().__init__()
        self.init_ui()

    def init_ui(self):
        # 设置窗口属性
        self.setWindowTitle("带指示灯的按钮")
        self.resize(300, 200)

        # 创建布局
        layout = QVBoxLayout()

        # 创建自定义按钮
        self.push_btn = IndicatorPushButton()
        # 连接状态变化信号
        self.push_btn.clicked.connect(self.on_button_state_changed)

        # 创建状态提示标签
        self.status_label = QLabel("当前状态:off")

        # 添加控件到布局
        layout.addWidget(self.push_btn, alignment=Qt.AlignCenter)
        layout.addWidget(self.status_label, alignment=Qt.AlignCenter)

        # 设置布局
        self.setLayout(layout)

    def on_button_state_changed(self, state):
        """状态变化回调"""
        # print(state)
        if state:
            self.status_label.setText("当前状态:on")
        else:
            self.status_label.setText("当前状态:off")


if __name__ == "__main__":
    app = QApplication(sys.argv)

    # 注意:请将图片路径替换为你的实际图片路径
    # 建议将图片放在程序同目录下
    window = DemoWindow()
    window.show()

    sys.exit(app.exec())

相关推荐
bj_zhb7 小时前
Python 内置的 HTTP 服务
开发语言·python·http
qq_4783775157 小时前
python cut_merge video, convert video2gif, cut gif
java·前端·python
被制作时长两年半的个人练习生7 小时前
【大模型】happy-llm笔记
笔记·大模型·llm
CodeLongBear7 小时前
机器学习入门:逻辑回归超详细学习笔记(含案例+代码)
学习·机器学习·逻辑回归
Pyeako7 小时前
机器学习--逻辑回归
人工智能·python·机器学习·逻辑回归
aloha_7897 小时前
接口自动化框架学习
功能测试·学习·自动化·模块测试
sheeta19987 小时前
LeetCode 每日一题笔记 日期:2025.12.17 题目:3573.买卖股票的最佳时机Ⅴ
笔记·算法·leetcode
走在路上的菜鸟7 小时前
Android学Dart学习笔记第十九节 类-混入Mixins
android·笔记·学习·flutter
中年程序员一枚7 小时前
Python防止重复资源的链接mysql方法
开发语言·python·mysql