Qt按钮类03 QToolButton——大丙+Gemini

Qt按钮类03 QToolButton——大丙+Gemini

https://www.bilibili.com/video/BV1ff4y1C7CK

QToolButton 是 Qt 框架中专为工具栏(QToolBar)和快捷操作面板设计的高阶命令按钮控件。虽然它与 QPushButton 同属于基类 QAbstractButton 的子类,但在外观渲染机制、图文拓扑排版、内存模型以及级联菜单的托管控制上存在本质差异。

级联菜单其实就是“子菜单”(Submenu)。当点击或把鼠标悬停在某个菜单项上时,该菜单项旁边会弹出一个新的菜单,这就是级联。

1. QPushButton 与 QToolButton 对比

				 ┌───────────────┐
                 │    QWidget    │
                 └───────┬───────┘
                         │
                 ┌───────┴───────┐
                 │QAbstractButton│
                 └───────┬───────┘
                         │
         ┌───────────────┴───────────────┐
         │                               │
 ┌───────┴───────┐               ┌───────┴───────┐
 │ QPushButton   │               │  QToolButton  │
 └───────────────┘               └───────────────┘
 (标准重量级命令按钮)             (增强型高阶工具按钮)
核心技术维度 QPushButton (标准命令按钮) QToolButton (高阶工具按钮)
空间占用与外观表现 重量级控件。具备固定的边框边距(Margin/Padding),默认占用较大的视觉空间。 轻量级控件。天然支持自动悬浮属性(Auto-Raise),未触发时背景透明,极易紧凑地嵌入工具栏。
图文拓扑排版能力 仅支持基础的水平排版(文字在图标右侧)。若要实现“文字在图标下方”,必须编写 QSS 样式表或重写 paintEvent 原生内置 Qt::ToolButtonStyle 状态机。无需任何附加代码,即可自由实现文字在图标下方、文字在图标右侧、纯文字、纯图标等全方位排版。
对象树生命周期模型 调用 setMenu() 后,按钮底层会自动托管该菜单的所有权(改变其 parent 并挂载到对象树上)。 调用 setMenu() 后,按钮完全不改变该菜单的所有权(不挂载对象树),必须由开发者显式指定父对象。
级联菜单控制流 菜单弹出模式固定唯一。点击按钮即以模态形式弹窗,且强行使常规的 clicked() 信号失效 内置三大 ToolButtonPopupMode 策略。支持延时长按弹出、主体与箭头区分裂弹出、即时弹出,信号控制自由度极高。

2. 核心 API 与属性控制

QToolButton 操作的大部分基础函数(如 setText(), setIcon())均从父类 QAbstractButton 继承,自身则通过以下高阶 API 扩展了图文排版与视觉展现控制。

2.1 构造函数

// 工具按钮通常直接嵌入工具栏或通过布局管理器托管,因此构造函数仅提供父对象接口
explicit QToolButton(QWidget *parent = nullptr);

2.2 图文外观布局控制(ToolButtonStyle)

图标/文本布局

// [槽函数] 控制按钮内部图标与文本的相对排版拓扑关系
[slot] void setToolButtonStyle(Qt::ToolButtonStyle style);

// 返回当前工具按钮的图标显示模式
Qt::ToolButtonStyle toolButtonStyle() const;

布局样式枚举值 Qt::ToolButtonStyle 说明:

  • Qt::ToolButtonIconOnly:单图标模式。仅展示图标,彻底隐藏文本(默认状态)。
  • Qt::ToolButtonTextOnly:单文本模式。仅展示文本,彻底隐藏图标。
  • Qt::ToolButtonTextBesideIcon:水平并排模式。文本展示在图标的正右侧。
  • Qt::ToolButtonTextUnderIcon:垂直并列模式。文本展示在图标的正下方(常用于功能区大图标按钮)。
  • Qt::ToolButtonFollowStyle:跟随全局或宿主工具栏的默认样式。

2.3 自动悬浮属性(Auto-Raise)

// 配置工具按钮是否启用自动浮起属性
void setAutoRaise(bool enable);

// 获取当前按钮是否处于自动浮起状态
bool autoRaise() const;
  • 运行机理:当设置为 true 时,该按钮在初始状态下将处于“无边框且背景完全透明”的扁平状态。只有当鼠标光标悬停(Hover)在按钮区域上方时,系统才会动态渲染出激活的边框与背景,用以大幅降低密集工具栏的视觉疲劳度。

3. 按钮设置图标与文本的三种方式

3.1 方式一:基础多态配置(来自 QAbstractButton 继承)

normalBtn->setText("博丽灵梦");
normalBtn->setIcon(QIcon(":/imgs/reimu.png"));
normalBtn->setIconSize(QSize(50, 50));
normalBtn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);

3.2 方式二:高内聚动作关联(DefaultAction 机制)

在大型工程中,同一个操作(如“保存”)常共存于菜单、右键、快捷栏。通过引入 QAction 进行一体化联动:

[slot] void QToolButton::setDefaultAction(QAction *action);
  • 技术优势:工具按钮会自动拉取(Pull)该 Action 的文本、图标、自锁状态机(Checkable),并且将触发信号自动转接。
  • 信号重定向:绑定 DefaultAction 后,按钮的点击拦截应当从传统的 clicked() 信号转移到系统的 triggered(QAction*) 信号或直接在 QAction 内部拦截。
QAction* act = new QAction(normalBtn);
act->setIcon(QIcon(":/imgs/remilia.png"));
act->setText("蕾米莉亚·斯卡雷特");

normalBtn->setDefaultAction(act);
normalBtn->setIconSize(QSize(300, 300));
normalBtn->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);

3.3 方式三:内建矢量箭头直接渲染(ArrowType 机制)

无需任何外部图标素材,直接调用系统图形库在按钮表面绘制抗锯齿的矢量箭头(一旦启用,原设定的 QIcon 图标将被强行覆盖):

void setArrowType(Qt::ArrowType type);
箭头枚举值 Qt::ArrowType
  • Qt::NoArrow:无箭头(默认状态)。
  • Qt::UpArrow / Qt::DownArrow:自适应绘制向上 / 向下的矢量箭头。
  • Qt::LeftArrow / Qt::RightArrow:自适应绘制向左 / 向右的矢量箭头。

4. 级联菜单管理与三种弹出方式判定

QToolButton 允许通过 setPopupMode 函数精细化控制级联菜单的触发时机。这是它与 QPushButton 在控制流上的核心差异。

4.1 菜单配置函数

// 将给定的菜单与此工具按钮相关联。
void setMenu(QMenu *menu);

4.2 弹出模式控制

// 设置弹出菜单的触发行为模式
void setPopupMode(QToolButton::ToolButtonPopupMode mode);

QToolButton::ToolButtonPopupMode 枚举定义了以下三种互斥的控制流向:

  1. QToolButton::DelayedPopup(延时弹出模式)
    • 执行流表现:常规短促点击按钮,菜单不会弹出,系统正常发射 clicked() 信号。只有当用户按下鼠标并长按一段时间后,内置的定时器超时,关联的菜单才会呼出。
    • 核心细节:长按触发菜单弹出的那一刻,按钮本身的 clicked() 信号将被拦截屏蔽,不会发射
  2. QToolButton::MenuButtonPopup(分裂箭头模式)
    • 执行流表现:按钮的物理表面会被划分成两个完全独立的交互区域。左侧为主体点击区,右侧会额外渲染出一个内建的下拉小箭头
    • 核心细节:两条执行流互不干扰。点击左侧主体区域,只发射 clicked() 信号,绝对不弹出菜单;点击右侧小箭头,只弹出菜单,绝对不发射 clicked() 信号
  3. QToolButton::InstantPopup(即时弹出模式)
    • 执行流表现:按钮的常规点击动作被底层完全重定向。无论是轻点还是长按按钮的任意几何表面,菜单都会立刻、无延时地模态弹出
    • 核心细节:在这种模式下,按钮自身的常规点击响应被无条件剥夺,clicked() 信号彻底失效,永远不会被发射

5. 示例代码

mainwindow.cpp构造函数:

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    // 配置顶级视窗物理尺寸
    this->resize(1600, 1000);

    // 纯代码架构:手动构建中心承载视窗
    QWidget *centralWidget = new QWidget(this);
    this->setCentralWidget(centralWidget);
    
    // 手动构造水平布局管理器,并将其挂载到中心视窗上
    QHBoxLayout *mainLayout = new QHBoxLayout(centralWidget);

    // =========================================================================
    // 验证点一:纯代码基础多态配置与自锁状态机(对应 P7/P8 属性演示)
    // =========================================================================
    QToolButton *normalBtn = new QToolButton(this);
    normalBtn->setText("雾雨魔理沙");
    normalBtn->setIcon(QIcon(":/imgs/marisa.png"));
    normalBtn->setIconSize(QSize(50, 50));
    normalBtn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); // 文字在图标右侧
    normalBtn->setCheckable(true); // 开启双稳态自锁机制

    connect(normalBtn, &QToolButton::toggled, this, [=](bool isChecked) 
    {
        qDebug() << "魔理沙按钮自锁状态变更: " << isChecked;
    });

    // =========================================================================
    // 验证点二:纯代码 Action 联动驱动机制(对应 P8 图标显示方式)
    // =========================================================================
    QToolButton *actionBtn = new QToolButton(this);
    
    // 纯代码实例化全局动作流,显式挂载到当前顶级窗口(this)
    QAction *actRun = new QAction(QIcon(":/imgs/run.png"), "启动核心线程", this);
    actionBtn->setDefaultAction(actRun); // 文本、图标及自锁状态实现全权托管联锁
    actionBtn->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); // 文字在图标下方

    // 绑定 Action 触发槽流
    connect(actionBtn, &QToolButton::triggered, this, [=](QAction *act) 
    {
        qDebug() << "Action 捕获成功,原动作文本:" << act->text();
        act->setText("线程正在运行..."); // 动态改写后,按钮外观将自适应保持同步
    });

    // =========================================================================
    // 验证点三:系统内建抗锯齿矢量箭头渲染(对应 P8 样式演示)
    // =========================================================================
    QToolButton *arrowBtn = new QToolButton(this);
    arrowBtn->setArrowType(Qt::UpArrow); // 开启自适应向上箭头,强制压制并屏蔽常规 QIcon
    arrowBtn->setText("面板向上滚动");
    arrowBtn->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);

    // =========================================================================
    // 验证点四:分裂箭头模式(MenuButtonPopup)信号解耦独立测试(对应 P9 弹出方式)
    // =========================================================================
    QToolButton *popMenuBtn = new QToolButton(this);
    popMenuBtn->setText("战术模式切换");
    popMenuBtn->setIcon(QIcon(":/imgs/strategy.png"));
    popMenuBtn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);

    // 工业级内存补正:实例化 QMenu 时,必须传入 this 指针挂载到对象树上,防止所有权真空泄漏
    QMenu *strategyMenu = new QMenu(this);
    QAction *actSakuya  = strategyMenu->addAction("十六夜咲夜");
    QAction *actRemilia = strategyMenu->addAction("蕾米莉亚");

    popMenuBtn->setMenu(strategyMenu);
    popMenuBtn->setPopupMode(QToolButton::MenuButtonPopup); // 核心设定:分裂小箭头模式

    // 黑盒执行流独立性测试
    connect(popMenuBtn, &QToolButton::clicked, this, [=]() 
    {
        qDebug() << "【信号触发提示】: 用户点击了左侧主体部分,发射 clicked 信号,菜单未弹出.";
    });

    connect(actSakuya, &QAction::triggered, this, [=]() 
    {
        qDebug() << "【菜单动作拦截】: 砸瓦鲁多!时停控制流激活.";
    });

    // 纯代码手动将动态创建的所有工具按钮依序注入水平布局管理器
    mainLayout->addWidget(normalBtn);
    mainLayout->addWidget(actionBtn);
    mainLayout->addWidget(arrowBtn);
    mainLayout->addWidget(popMenuBtn);
    mainLayout->addStretch(); // 尾部垫入弹簧,确保工具组件向左靠拢紧凑排列
}
posted @ 2026-06-13 14:49  学习笔记草稿存放账号  阅读(0)  评论(0)    收藏  举报