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 枚举定义了以下三种互斥的控制流向:
QToolButton::DelayedPopup(延时弹出模式)- 执行流表现:常规短促点击按钮,菜单不会弹出,系统正常发射
clicked()信号。只有当用户按下鼠标并长按一段时间后,内置的定时器超时,关联的菜单才会呼出。 - 核心细节:长按触发菜单弹出的那一刻,按钮本身的
clicked()信号将被拦截屏蔽,不会发射。
- 执行流表现:常规短促点击按钮,菜单不会弹出,系统正常发射
QToolButton::MenuButtonPopup(分裂箭头模式)- 执行流表现:按钮的物理表面会被划分成两个完全独立的交互区域。左侧为主体点击区,右侧会额外渲染出一个内建的下拉小箭头。
- 核心细节:两条执行流互不干扰。点击左侧主体区域,只发射
clicked()信号,绝对不弹出菜单;点击右侧小箭头,只弹出菜单,绝对不发射clicked()信号。
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(); // 尾部垫入弹簧,确保工具组件向左靠拢紧凑排列
}

浙公网安备 33010602011771号