对于Qt:QDial常见属性以及信号的解析

开篇介绍:

hello 大家,本篇博客我们来学习QDial常见属性以及信号。

在 Qt 桌面应用开发中,我们除了会遇到 "文本输入""数值微调""日期时间选择" 的需求,还会经常遇到一个非常经典、非常有特色的需求:让用户通过拖动一个旋钮来调整数值------ 比如调节音量大小、调节屏幕亮度、调节播放器的进度、调节游戏中的视角角度、调节工业控制面板上的参数。在这些场景下,你总不能让用户一直点上下按钮吧?你需要一个 "旋钮控件"------ 一个圆形的、可以用鼠标拖动旋转的控件,用户旋转旋钮的角度对应数值的大小,直观又有交互感。

为了满足这个需求,Qt 专门为我们提供了一个开箱即用、功能强大的特色核心控件:QDial(旋钮控件) 。QDial 就像一个 "圆形的滑块"------ 它的核心逻辑和 QSlider(滑块控件)几乎完全一样,都有 value、minimum、maximum、singleStep、pageStep 这些属性,都有 valueChanged、rangeChanged 这些信号;但它的交互方式是 "旋转" 而不是 "拖动滑块",视觉上是一个圆形的旋钮,而不是一个长条的滑块,更适合某些复古风格、工业风格或者需要节省空间的界面。

很多新手刚接触 QDial 时,会觉得它很简单 ------ 不就是一个圆形的滑块吗?拖上去,设置范围,连接信号,就完事了。但用起来才发现,它还有很多实用的属性和细节:比如你要区分 "内部数值" 和 "视觉位置",就要搞懂 value 和 sliderPosition 的区别,以及 tracking 属性的作用;比如你要让旋钮显示刻度线,就要会用 notchesVisible 和 notchTarget 属性;比如你要让旋钮可以 "套圈"(到了最大值后继续转回到最小值),就要会用 wrapping 属性;比如你要区分 "方向键步长" 和 "PageUp/PageDown 步长",就要搞懂 singleStep 和 pageStep 的区别;更重要的是,这里面也有一些新手容易忽略的细节 ------ 比如 tracking 默认是 true,一般不需要修改;比如 notchTarget 是 "目标像素距离",不是 "刻度数量";比如 wrapping 仅对用户操作生效,代码调用 setValue() 不会循环。

别担心!这篇文章我会先讲 QDial 的核心数值与步长属性 (value、singleStep、pageStep),再讲刻度线显示与密度属性 (notchesVisible、notchTarget),接着讲视觉位置与交互规则属性 (sliderPosition、tracking、wrapping),最后讲核心交互信号 (valueChanged(int)、rangeChanged(int, int))。

话不多说,我们正式开始!


第一章 先搞懂基础:QDial 是什么?

在讲具体属性之前,我们必须先搞懂两个最基础、最重要的问题:QDial 是用来做什么的?以及,它和 QSlider 的关系是什么?

1.1 QDial 的核心定位

QDial 是 Qt Widgets 模块中用于通过旋转交互来输入和调整整数数值的特色核心控件------ 它的核心逻辑和 QSlider(滑块控件)几乎完全相同,但交互方式是 "旋转旋钮" 而非 "拖动滑块",视觉上是圆形的,而非长条的。

通俗理解:QDial 就像 "一个圆形的滑块"------ 或者说,像我们家里老式收音机上的音量旋钮、老式电视机上的调台旋钮、汽车仪表盘上的里程旋钮。你用鼠标按住旋钮,顺时针旋转,数值就增大;逆时针旋转,数值就减小;你也可以用键盘的方向键、PageUp/PageDown 来调整数值;它和 QSlider 一样,有最小值、最大值、步长,数值超出范围会自动修正,数值变化会触发信号。

QDial 内置了非常多的功能,不需要你手动实现:

  • 它支持鼠标拖动旋转调整数值,也支持键盘方向键、PageUp/PageDown 调整数值;
  • 它支持设置数值范围(最小值 minimum 和最大值 maximum),用户无法输入超出范围的数字,超出会自动修正;
  • 它支持设置不同键盘操作的步长:方向键步长(singleStep)和 PageUp/PageDown 步长(pageStep);
  • 它支持设置是否显示刻度线(notchesVisible),以及刻度线的疏密程度(notchTarget);
  • 它支持设置是否允许循环调整(wrapping)------ 也就是到了最大值后继续旋转,会跳回到最小值("套圈");
  • 它支持区分 "内部数值" 和 "视觉位置"(sliderPosition),以及是否让视觉位置跟踪数值变化(tracking);
  • 它的样式可以通过 QSS(Qt 样式表)完全自定义 ------ 比如修改旋钮的颜色、形状、刻度线的颜色和宽度等。

总之,QDial 是桌面端 "通过旋转交互调整整数数值" 场景的特色首选组件------ 只要是想让用户通过旋转来调整数值,或者界面风格适合用圆形旋钮,用 QDial 就对了。

1.2 QDial 和 QSlider 的关系(必知!)

QDial 和 QSlider 的核心逻辑几乎完全相同 ------ 它们都继承自 QAbstractSlider(抽象滑块类),所以它们的属性、方法、信号有大量的重叠:

  • 都有 value、minimum、maximum、singleStep、pageStep、sliderPosition、tracking 属性;
  • 都有 valueChanged(int)、rangeChanged(int, int) 信号;
  • 都有 setValue()、setRange()、setSingleStep() 等方法。

唯一的区别就是:

  • 交互方式:QDial 是 "旋转旋钮",QSlider 是 "拖动滑块";
  • 视觉样式:QDial 是圆形的,QSlider 是长条的(水平或垂直);
  • 特色属性 :QDial 有 notchesVisible、notchTarget、wrapping 这些和旋钮相关的特色属性,QSlider 没有;QSlider 有 orientation(水平 / 垂直)属性,QDial 没有(因为它是圆形的)。

因此,如果你已经学会了 QSlider,那么 QDial 对你来说非常简单,只需要关注它的几个特色属性即可;如果你还没学过 QSlider,也没关系,这篇文章会从零开始,把所有属性都讲得明明白白。

1.3 QDial 的典型应用场景

为了让你更清楚地理解 QDial 的用途,我举几个最典型的应用场景:

  1. 多媒体播放器:调节音量大小、调节播放进度、调节亮度 / 对比度;
  2. 游戏界面:调节视角角度、调节角色移动速度、调节游戏音量;
  3. 工业控制面板:调节温度、调节压力、调节转速;
  4. 复古风格界面:模拟老式收音机、老式电视机的调台 / 音量旋钮;
  5. 节省空间的场景:圆形旋钮比长条滑块更节省空间,适合小界面。

这些场景的共同特点是:需要调整一个数值,且旋转交互更直观、更符合界面风格、或者更节省空间------ 这正是 QDial 最擅长的。


第二章 QDial 核心数值与步长属性

好,我们正式开始讲属性!我们首先讲 QDial 最核心、最常用、最高频的部分:核心数值与步长属性 ------value(当前数值)、singleStep(方向键步长)、pageStep(PageUp/PageDown 步长)。这部分是 QDial 的基础,所有旋钮的使用都离不开这些内容,而且和 QSlider 几乎完全一样。


2.1 属性一:value(当前数值)------ 存的是什么数?

2.1.1 属性的核心定义

value 属性是 QDial 用于存储当前整数值的属性 ------ 它是旋钮的核心数据值,所有数值调整都是围绕这个属性进行的。

通俗理解:这个属性就像 "旋钮肚子里存的那个数"------ 不管你是拖动旋钮旋转的,还是按键盘调整的,还是代码设置的,这个属性里存的就是当前的实际数值;而且这个数值始终受 minimum 和 maximum 的限制,你想设一个超出范围的数,它会自动修正为最近的边界值。

这里有几个超级重要的规则,必须一开始就说清楚,刻在脑子里:

  1. 默认值:默认值为 0------ 也就是刚创建 QDial 时,里面存的数是 0;
  2. 始终受 minimum 和 maximum 限制!(超级重要!说三遍!) :
    • 无论你是用户操作(拖动旋钮、按键盘),还是代码调用 setValue(),数值始终在 [minimum, maximum] 这个闭区间内;
    • 如果你尝试设置一个小于 minimum 的数,会自动修正为 minimum;
    • 如果你尝试设置一个大于 maximum 的数,会自动修正为 maximum;
    • 不用担心数值越界,Qt 会自动处理!
  3. 修改该属性会强制触发信号!(超级重要!) :
    • 代码调用 setValue() 修改数值时,会强制触发 valueChanged() 信号;
    • 这点和用户操作一样,只要数值变了,就会触发信号;
  4. 与 tracking 的关系(超级重要!) :
    • 当 tracking=true(默认)时,setValue() 会同步更新旋钮的视觉显示位置 ------ 也就是数值变了,旋钮的角度也会跟着变;
    • 当 tracking=false 时,setValue() 仅更新内部数值,旋钮的视觉显示位置不变 ------ 需要手动调用 setSliderPosition() 才会更新视觉;
    • 不过 tracking 默认是 true,一般不需要修改,我们后面讲 tracking 属性时会详细说;
  5. 与 sliderPosition 的关系(超级重要!) :
    • 当 tracking=true(默认)时,sliderPosition(视觉位置)和 value(内部数值)始终实时同步 ------ 不用单独管 sliderPosition;
    • 当 tracking=false 时,二者可以分离 ------ 我们后面讲 sliderPosition 属性时会详细说。
2.1.2 关联 API 详解(逐词逐句,零模糊)

value 属性有两个关联的 API,我们逐个来讲:

1. 获取当前存储的纯整数值:int value () const

函数原型:

复制代码
int QDial::value() const;

逐词逐句底层解析:

  • int:返回值类型,返回当前存储的纯整数值;
  • QDial:::说明这个函数是 QDial 类的成员函数,只能用 QDial 对象调用;
  • value():函数名,很直观,就是 "获取当前数值" 的意思;
  • const:说明这个函数是常成员函数 ------ 它只会读取当前的数值,不会修改 QDial 的任何状态,所以你可以在 const 的成员函数里调用它。

返回值说明:

  • 返回 int 类型的整数;
  • 始终在 [minimum, maximum] 范围内;
  • 是纯数值,用于数值计算。

调用示例:

复制代码
// 获取当前数值并用于计算
int volume = ui->dial->value();
qDebug() << "当前音量:" << volume;
2. 设置当前数值:void setValue (int val)

函数原型:

复制代码
void QDial::setValue(int val);

逐词逐句底层解析:

  • void:函数没有返回值,只是执行 "设置当前数值" 的操作;
  • QDial:::成员函数;
  • setValue():函数名,"设置当前数值" 的意思;
  • int val:入参,val 是你要设置的目标整数值。

参数说明:

  • val:目标整数值,int 类型;
  • 会自动修正到 [minimum, maximum] 范围内:
    • val < minimum → 自动修正为 minimum;
    • val > maximum → 自动修正为 maximum;
    • minimum ≤ val ≤ maximum → 直接设置为 val。

生效规则:

  • 调用后立即生效;
  • 数值会自动修正到范围内;
  • 会强制触发 valueChanged() 信号;
  • 当 tracking=true(默认)时,会同步更新旋钮的视觉显示位置;
  • 当 tracking=false 时,仅更新内部数值,视觉位置不变。

使用示例:

复制代码
#include <QDebug>

// 先设置范围:0 到 100
ui->dial->setRange(0, 100);

// 示例1:设置正常范围内的数值
ui->dial->setValue(50);
qDebug() << "当前数值:" << ui->dial->value(); // 输出 50

// 示例2:尝试设置超出范围的数值(自动修正!)
ui->dial->setValue(150); // 大于 maximum=100
qDebug() << "修正后数值:" << ui->dial->value(); // 输出 100(自动修正为最大值)

ui->dial->setValue(-50); // 小于 minimum=0
qDebug() << "修正后数值:" << ui->dial->value(); // 输出 0(自动修正为最小值)
2.1.3 关键注意事项(必知!)

在讲完 value 属性的 API 后,我必须把几个最容易踩坑的注意事项单独列出来:

  1. 始终受 minimum 和 maximum 限制!(超级重要!说三遍!):再次强调!无论用户操作还是代码设置,数值始终在范围内,超出会自动修正,不用担心越界;
  2. setValue () 强制触发信号!(超级重要!) :再次强调!代码调用 setValue() 会强制触发 valueChanged();
  3. 与 tracking 的关系(超级重要!) :tracking=true(默认)时,setValue() 会同步更新视觉位置;tracking=false 时仅更新内部数值;
  4. 默认值为 0:刚创建 QDial 时,数值是 0;
  5. 修正后触发信号 :自动修正数值后,会触发 valueChanged() 信号。

2.2 属性二:singleStep(方向键步长)------ 按方向键每次变多少?

2.2.1 属性的核心定义

singleStep 属性是 QDial 用于定义按下键盘方向键(↑/↓/←/→)时,数值的单次变化量的属性 ------ 它是控制方向键调整精度的关键配置。

通俗理解:这个属性就像 "旋钮的'方向键每次跳多少'"------ 你设置步长为 1,按一下 ↑ 或 → 键,数值就加 1;按一下 ↓ 或 ← 键,数值就减 1;你设置步长为 10,按一下方向键,数值就变化 10。

这里有几个超级重要的规则,必须牢记:

  1. 默认值:默认值为 1------ 也就是按一下方向键,数值变化 1;
  2. 操作范围(超级重要!) :
    • 这个属性仅对键盘方向键(↑/↓/←/→)生效;
    • 鼠标拖动旋钮 、按下 PageUp/PageDown 键 ,不受这个属性影响;
    • 这点非常重要!一定要区分 singleStep 和 pageStep 的作用范围!
  3. 调整方向(超级重要!) :
    • 按 ↑ 键 / → 键 / 顺时针拖动旋钮 → 数值 + singleStep;
    • 按 ↓ 键 / ← 键 / 逆时针拖动旋钮 → 数值 - singleStep;
  4. 步长应为正整数 :
    • 步长建议设置为 ≥ 1 的正整数;
    • 设为 0 或负数时,调整行为无实际意义(Qt 通常会自动修正为 1);
  5. 边界处理(超级重要!) :
    • 调整后数值超出范围时,会自动修正为边界值;
    • 比如当前数值是 95,步长是 10,范围是 0-100,按一下 ↑ 键 → 95+10=105,超出最大值 100,自动修正为 100;
  6. 与 wrapping 配合(超级重要!) :
    • 当 wrapping=true(允许循环)时,方向键调整到边界会自动 "套圈"(比如最大值 + 步长 → 最小值);
    • 我们后面讲 wrapping 属性时会详细说。
2.2.2 关联 API 详解(逐词逐句,零模糊)

singleStep 属性有两个关联的 API,我们逐个来讲:

1. 获取当前方向键步长:int singleStep () const

函数原型:

复制代码
int QDial::singleStep() const;

逐词逐句底层解析:

  • int:返回值类型,返回当前的方向键步长;
  • QDial:::成员函数;
  • singleStep():函数名,"获取当前方向键步长" 的意思;
  • const:常成员函数。

返回值说明:

  • 返回 int 类型的整数;
  • 默认返回 1。
2. 设置方向键步长:void setSingleStep (int step)

函数原型:

复制代码
void QDial::setSingleStep(int step);

逐词逐句底层解析:

  • void:无返回值;
  • QDial:::成员函数;
  • setSingleStep():函数名,"设置方向键步长" 的意思;
  • int step:入参,step 是目标步长(建议 ≥ 1)。

参数说明:

  • step:目标步长,int 类型;
  • 建议设置为 ≥ 1 的正整数;
  • 设为 0 或负数时,调整行为无实际意义。

使用示例:

复制代码
#include <QDebug>

// 先设置范围:0 到 100
ui->dial->setRange(0, 100);
// 设置初始值:50
ui->dial->setValue(50);

// 示例:设置方向键步长为 10(每次按方向键变化 10)
ui->dial->setSingleStep(10);
// 此时:
// 按 ↑ 键 / → 键 → 50 → 60 → 70 → ... → 100
// 按 ↓ 键 / ← 键 → 50 → 40 → 30 → ... → 0

// 边界处理演示
ui->dial->setValue(95); // 当前值 95,步长 10,最大值 100
// 按 ↑ 键 → 95 + 10 = 105 → 自动修正为 100
qDebug() << "修正后数值:" << ui->dial->value(); // 输出 100
2.2.3 关键注意事项(必知!)
  1. 操作范围(超级重要!):仅对键盘方向键生效!鼠标拖动、PageUp/PageDown 不受影响!
  2. 调整方向(超级重要!):↑/→/ 顺时针 + 步长,↓/←/ 逆时针 - 步长;
  3. 边界处理(超级重要!):调整后超出范围时,自动修正为边界值;
  4. 步长应为正整数:建议 ≥ 1,0 或负数无实际意义;
  5. 与 wrapping 配合(超级重要!) :wrapping=true 时,方向键调整到边界会自动 "套圈";
  6. 默认值为 1:刚创建 QDial 时,方向键步长是 1。

2.3 属性三:pageStep(PageUp/PageDown 步长)------ 按 PageUp/PageDown 每次变多少?

2.3.1 属性的核心定义

pageStep 属性是 QDial 用于定义按下 PageUp/PageDown 键时,数值的单次变化量的属性 ------ 它是控制翻页键调整精度的关键配置。

通俗理解:这个属性就像 "旋钮的'翻页键每次跳多少'"------ 你设置步长为 20,按一下 PageUp 键,数值就加 20;按一下 PageDown 键,数值就减 20。通常 pageStep 会设置得比 singleStep 大一些,方便用户快速调整到目标数值(比如 singleStep=1 微调,pageStep=10 快调)。

这里有几个和 singleStep 对应的规则,快速过一遍,重点讲和 singleStep 不一样的地方:

  1. 默认值:默认值为 10------ 也就是按一下 PageUp/PageDown 键,数值变化 10;
  2. 操作范围(超级重要!) :
    • 这个属性仅对 PageUp/PageDown 键生效;
    • 鼠标拖动旋钮 、按下方向键 ,不受这个属性影响;
    • 再次强调!一定要区分 singleStep(方向键)和 pageStep(PageUp/PageDown)的作用范围!
  3. 调整方向(超级重要!) :
    • 按 PageUp 键 → 数值 + pageStep;
    • 按 PageDown 键 → 数值 - pageStep;
  4. 步长应为正整数 :
    • 步长建议设置为 ≥ 1 的正整数;
    • 设为 0 或负数时,调整行为无实际意义(Qt 通常会自动修正为 10);
  5. 边界处理(超级重要!) :
    • 调整后数值超出范围时,会自动修正为边界值;
  6. 与 wrapping 配合(超级重要!) :
    • 当 wrapping=true(允许循环)时,PageUp/PageDown 调整到边界会自动 "套圈";
  7. 步长建议(超级重要!) :
    • pageStep 通常设置为 singleStep 的整数倍(比如 10 倍),符合用户操作习惯 ------ 比如 singleStep=1,pageStep=10;singleStep=5,pageStep=20。
2.3.2 关联 API 详解(逐词逐句,零模糊)

pageStep 属性有两个关联的 API,和 singleStep 几乎完全一样:

1. 获取当前 PageUp/PageDown 步长:int pageStep () const

函数原型:

复制代码
int QDial::pageStep() const;

逐词逐句底层解析:

  • int:返回值类型,返回当前的 PageUp/PageDown 步长;
  • QDial:::成员函数;
  • pageStep():函数名,"获取当前翻页键步长" 的意思;
  • const:常成员函数。

返回值说明:

  • 返回 int 类型的整数;
  • 默认返回 10。
2. 设置 PageUp/PageDown 步长:void setPageStep (int step)

函数原型:

复制代码
void QDial::setPageStep(int step);

逐词逐句底层解析:

  • void:无返回值;
  • QDial:::成员函数;
  • setPageStep():函数名,"设置翻页键步长" 的意思;
  • int step:入参,step 是目标步长(建议 ≥ 1)。

参数说明:

  • step:目标步长,int 类型;
  • 建议设置为 ≥ 1 的正整数,通常为 singleStep 的整数倍;
  • 设为 0 或负数时,调整行为无实际意义。

使用示例:

复制代码
#include <QDebug>

// 先设置范围:0 到 100
ui->dial->setRange(0, 100);
// 设置初始值:50
ui->dial->setValue(50);

// 示例:设置方向键步长为 1(微调),PageUp/PageDown 步长为 20(快调)
ui->dial->setSingleStep(1);   // 方向键每次变 1
ui->dial->setPageStep(20);     // PageUp/PageDown 每次变 20
// 此时:
// 按 PageUp → 50 → 70 → 90 → 100
// 按 PageDown → 50 → 30 → 10 → 0
2.3.3 关键注意事项(必知!)
  1. 操作范围(超级重要!):仅对 PageUp/PageDown 键生效!鼠标拖动、方向键不受影响!
  2. 调整方向(超级重要!):PageUp + 步长,PageDown - 步长;
  3. 边界处理(超级重要!):调整后超出范围时,自动修正为边界值;
  4. 步长应为正整数 :建议 ≥ 1,通常为 singleStep 的整数倍;
  5. 与 wrapping 配合(超级重要!) :wrapping=true 时,PageUp/PageDown 调整到边界会自动 "套圈";
  6. 默认值为 10:刚创建 QDial 时,PageUp/PageDown 步长是 10。

示例代码:

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include <QDial>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //OK,本文件我们来学习一下QDial的比较关键的属性
    //value、singalStep、pageStep属性~~~
    //那么value属性是很简单的,就是获取或者设置QDail的数值罢了
    //而sinalStep属性则是控制按下方向键的时候改变的步长(数值).
    //pageStep属性则是按下 pageUp / pageDown 的时候改变的步长.
    //一般还是singalStep属性用的多哦~~~,毕竟方向键我们还是经常使用的~~~

    /**
     * @class QDial 核心数值与步长属性
     * @brief QDial旋钮控件的数值存储、方向键步长、PageUp/PageDown步长核心属性,是控制数值调整规则的基础配置
     * @details 以下属性聚焦QDial的数值存储与键盘调整步长,value表征当前数值,singleStep/pageStep分别定义不同键盘操作的单次调整幅度,适用于所有旋钮交互场景。
     *
     * ===================================== 属性1:value =====================================
     * @property QDial::value
     * @brief QDial旋钮持有的核心整数值属性
     * @details 该属性是旋钮的核心数据值,默认值为0;数值范围严格受minimum和maximum限制,超出范围的设置会自动修正为最近的边界值;
     *          修改该属性会触发valueChanged()信号,若tracking=true(默认),旋钮视觉位置会同步更新。
     *
     * 【关联API详解】
     * 1. int value() const → 获取当前旋钮持有的数值;
     * 2. void setValue(int val) → 设置旋钮数值(超出[minimum, maximum]时自动修正为边界值)。
     *
     * @使用示例:
     * // 初始化旋钮范围0-100,设置初始值为50
     * ui->dial->setRange(0, 100);
     * ui->dial->setValue(50);
     * qDebug() << "旋钮当前数值:" << ui->dial->value(); // 输出50
     *
     * // 尝试设置超出范围的数值(自动修正)
     * ui->dial->setValue(150);
     * qDebug() << "修正后数值:" << ui->dial->value(); // 输出100
     *
     * 【关键注意事项】
     * 1. 范围强制约束:无论用户操作还是代码设置,数值始终在[minimum, maximum]区间内;
     * 2. 视觉联动:tracking=true时,setValue()会同步更新旋钮的显示位置;tracking=false时仅更新内部数值;
     * 3. 信号触发:任何方式修改value(包括自动修正)都会触发valueChanged(int)信号。
     *
     * ===================================== 属性2:singleStep =====================================
     * @property QDial::singleStep
     * @brief QDial按下键盘方向键(↑/↓/←/→)时的单次调整步长属性
     * @details 该属性定义方向键操作时数值的变化幅度,默认值为1;步长需为正整数,设为0/负数时会自动修正为1,仅对方向键操作生效。
     *
     * 【关联API详解】
     * 1. int singleStep() const → 获取当前方向键步长;
     * 2. void setSingleStep(int step) → 设置方向键步长(建议step≥1)。
     *
     * @使用示例:
     * // 设置方向键步长为10,范围0-100
     * ui->dial->setRange(0, 100);
     * ui->dial->setSingleStep(10);
     * ui->dial->setValue(50);
     * // 按↑/→键 → 数值变为60;按↓/←键 → 数值变为40
     *
     * 【关键注意事项】
     * 1. 操作范围:仅对键盘方向键生效,鼠标拖动旋钮、PageUp/PageDown不受此属性影响;
     * 2. 边界处理:方向键调整后数值超出范围时,自动修正为minimum/maximum;
     * 3. 循环适配:wrapping=true时,方向键调整到边界会自动"套圈"(如max+step→min)。
     *
     * ===================================== 属性3:pageStep =====================================
     * @property QDial::pageStep
     * @brief QDial按下PageUp/PageDown键时的单次调整步长属性
     * @details 该属性定义PageUp/PageDown键操作时数值的变化幅度,默认值为10;步长需为正整数,设为0/负数时会自动修正为10,仅对Page键操作生效。
     *
     * 【关联API详解】
     * 1. int pageStep() const → 获取当前PageUp/PageDown步长;
     * 2. void setPageStep(int step) → 设置PageUp/PageDown步长(建议step≥1)。
     *
     * @使用示例:
     * // 设置PageUp/PageDown步长为20,范围0-100
     * ui->dial->setRange(0, 100);
     * ui->dial->setPageStep(20);
     * ui->dial->setValue(50);
     * // 按PageUp → 数值变为70;按PageDown → 数值变为30
     *
     * 【关键注意事项】
     * 1. 操作范围:仅对PageUp/PageDown键生效,鼠标拖动、方向键不受此属性影响;
     * 2. 边界处理:Page键调整后数值超出范围时,自动修正为minimum/maximum;
     * 3. 循环适配:wrapping=true时,Page键调整到边界会自动"套圈"(如max+pageStep→min);
     * 4. 步长建议:pageStep通常设置为singleStep的整数倍(如10倍),符合用户操作习惯。
     */


    QDial* dial=new QDial(this);

    dial->setSingleStep(5);

    dial->setPageStep(2);
}

Widget::~Widget()
{
    delete ui;
}

第三章 QDial 刻度线显示与密度属性

讲完了核心数值与步长属性,我们接下来讲两个和 "刻度线显示" 相关的特色属性:notchesVisible(是否显示刻度线)、notchTarget(刻度线密度)。这两个属性是 QDial 特有的(QSlider 没有),用于定制旋钮的视觉样式,让旋钮看起来更直观、更有复古或工业风格。


3.1 属性四:notchesVisible(是否显示刻度线)------ 要不要显示刻度?

3.1.1 属性的核心定义

notchesVisible 属性是 QDial 用于控制旋钮外围是否绘制刻度线的属性 ------ 它是定制旋钮视觉样式的简单开关。

通俗理解:这个属性就像 "旋钮的'刻度线开关'"------ 如果开关关闭(默认),旋钮就是一个光秃秃的圆形,没有刻度线;如果开关打开,旋钮周围会出现一圈均匀分布的刻度线,就像老式钟表的刻度、老式收音机音量旋钮的刻度一样,看起来更直观,用户可以通过刻度线大致判断当前数值的位置。

这里有几个超级重要的规则,必须牢记:

  1. 默认值 :默认值为 false(不显示刻度线)------ 也就是刚创建 QDial 时,旋钮是光秃秃的,没有刻度;
  2. 仅视觉影响(超级重要!) :
    • 刻度线的显示 / 隐藏不改变旋钮的任何交互功能 ------ 数值调整、步长、循环、信号触发等所有功能都正常;
    • 它只是一个视觉样式属性,不影响逻辑;
  3. 依赖 notchTarget(超级重要!) :
    • 仅当 notchesVisible=true 时,notchTarget(刻度密度)的设置才会生效;
    • 如果 notchesVisible=false,设置 notchTarget 没有任何视觉效果;
  4. 样式表覆盖(超级重要!) :
    • 若通过 QSS(Qt 样式表)自定义了 QDial 的刻度线样式(比如修改刻度颜色、宽度、长度),该属性的显隐逻辑可能被 QSS 覆盖;
    • 也就是说,如果你用了 QSS 自定义刻度,setNotchesVisible(false) 可能不起作用,刻度的显示由 QSS 决定;
  5. 范围适配(超级重要!) :
    • 刻度线的数量会自动适配旋钮的 [minimum, maximum] 范围,无需手动设置刻度数量;
    • 具体数量由 notchTarget(目标像素距离)、旋钮尺寸、数值范围共同决定,Qt 会自动计算。
3.1.2 关联 API 详解(逐词逐句,零模糊)

notchesVisible 属性有两个关联的 API,非常简单:

1. 获取当前是否显示刻度线:bool notchesVisible () const

函数原型:

复制代码
bool QDial::notchesVisible() const;

逐词逐句底层解析:

  • bool:返回值类型,返回 true 或 false;
  • QDial:::成员函数;
  • notchesVisible():函数名,"是不是显示了刻度线" 的意思;
  • const:常成员函数。

返回值说明:

  • true 表示显示刻度线;
  • false 表示不显示刻度线(默认)。
2. 设置是否显示刻度线:void setNotchesVisible (bool visible)

函数原型:

复制代码
void QDial::setNotchesVisible(bool visible);

逐词逐句底层解析:

  • void:无返回值;
  • QDial:::成员函数;
  • setNotchesVisible():函数名,"设置是否显示刻度线" 的意思;
  • bool visible:入参,visible 是 true 显示,false 隐藏。

参数说明:

  • visible:true 显示刻度线,false 隐藏。

使用示例:

复制代码
// 示例1:显示旋钮刻度线(基础配置)
ui->dial->setNotchesVisible(true);

// 示例2:完整示例:显示刻度线 + 调整刻度密度(配合 notchTarget)
ui->dial->setRange(0, 100);
ui->dial->setNotchesVisible(true);    // 先开启刻度显示
ui->dial->setNotchTarget(8.0);        // 再设置刻度稀疏度(我们接下来讲)
3.1.3 关键注意事项(必知!)
  1. 仅视觉影响(超级重要!):刻度线的显示 / 隐藏不改变任何交互功能,仅改变外观;
  2. 依赖 notchTarget(超级重要!) :必须先设置 notchesVisible=true,notchTarget 的设置才会生效;
  3. 样式表覆盖(超级重要!):若通过 QSS 自定义了刻度线样式,该属性可能被覆盖;
  4. 范围适配:刻度线数量会自动适配数值范围,无需手动设置;
  5. 默认值为 false:刚创建 QDial 时,不显示刻度线。

3.2 属性五:notchTarget(刻度线密度)------ 刻度线是密还是疏?

3.2.1 属性的核心定义

notchTarget 属性是 QDial 用于 ** 定义相邻两条刻度线之间的 "目标像素距离"** 的属性 ------ 它是控制刻度线疏密程度的核心配置。

通俗理解:这个属性就像 "旋钮刻度线的'疏密调节器'"------ 但它不是直接设置 "有多少条刻度线",而是设置 "相邻两条刻度线之间希望隔多少像素"。Qt 会根据这个目标像素距离、旋钮的实际尺寸、数值的范围,自动计算出应该显示多少条刻度线,尽可能让相邻刻度的像素距离接近你设定的目标值。数值越大,刻度线之间的像素距离越远,刻度线越稀疏;数值越小,刻度线之间的像素距离越近,刻度线越密集。

这里有几个超级重要的规则,必须牢记,刻在脑子里:

  1. 默认值:默认值为 3.766(约 4 像素)------ 也就是相邻刻度线的目标距离约为 4 像素,刻度线比较密集;
  2. 生效前提(超级重要!) :
    • 必须先设置 notchesVisible=true,否则该属性无任何视觉效果;
    • 如果 notchesVisible=false,设置 notchTarget 白搭,看不到任何变化;
  3. 是 "目标像素距离",不是 "刻度数量"(超级重要!说三遍!) :
    • 这点非常容易误解!notchTarget 不是 "你要显示多少条刻度线",而是 "相邻两条刻度线之间希望隔多少像素";
    • Qt 会根据这个目标距离、旋钮的实际尺寸(越大的旋钮,能显示的刻度线越多)、数值的范围,自动计算出实际的刻度线数量,尽可能让相邻刻度的像素距离接近你设定的 target;
    • 比如,同样的 notchTarget=8.0,大尺寸的旋钮可能显示 20 条刻度线,小尺寸的旋钮可能只显示 10 条;
  4. 数值与疏密的关系(超级重要!) :
    • notchTarget 数值越大 → 相邻刻度的目标像素距离越远 → 刻度线越稀疏;
    • notchTarget 数值越小 → 相邻刻度的目标像素距离越近 → 刻度线越密集;
  5. 数值建议(超级重要!) :
    • target 不宜过小(比如 < 2.0),否则刻度线会重叠在一起,影响视觉效果,看起来乱糟糟的;
    • target 也不宜过大(比如 > 20.0),否则刻度线过少,失去了参考意义,用户没法通过刻度判断位置;
    • 建议范围:2.0 ~ 20.0,根据旋钮尺寸和数值范围调整;
  6. 尺寸适配(超级重要!) :
    • 同一 target 值,旋钮尺寸越大,显示的刻度线数量越多;
    • 旋钮尺寸越小,显示的刻度线数量越少;
    • 因为大旋钮有更多空间放刻度线。
3.2.2 关联 API 详解(逐词逐句,零模糊)

notchTarget 属性有两个关联的 API,注意它的类型是 qreal(Qt 中的浮点数类型,相当于 double):

1. 获取当前刻度线的目标像素距离:qreal notchTarget () const

函数原型:

复制代码
qreal QDial::notchTarget() const;

逐词逐句底层解析:

  • qreal:返回值类型,Qt 中的浮点数类型(相当于 double),返回目标像素距离;
  • QDial:::成员函数;
  • notchTarget():函数名,"获取刻度目标距离" 的意思;
  • const:常成员函数。

返回值说明:

  • 返回 qreal 类型的浮点数;
  • 默认返回 3.766(约 4 像素)。
2. 设置刻度线的目标像素距离:void setNotchTarget (qreal target)

函数原型:

复制代码
void QDial::setNotchTarget(qreal target);

逐词逐句底层解析:

  • void:无返回值;
  • QDial:::成员函数;
  • setNotchTarget():函数名,"设置刻度目标距离" 的意思;
  • qreal target:入参,target 是目标像素距离(浮点数)。

参数说明:

  • target:目标像素距离,qreal 类型(浮点数);
  • 建议范围:2.0 ~ 20.0;
  • 数值越大,刻度越稀疏;数值越小,刻度越密集。

使用示例:

复制代码
// 先设置范围,再开启刻度显示
ui->dial->setRange(0, 100);
ui->dial->setNotchesVisible(true); // 必须先开启!

// 示例1:密集刻度(适合小范围数值,如 0-20)
ui->dial->setNotchTarget(3.0);  // 数值小,目标距离近,刻度密集

// 示例2:稀疏刻度(适合大范围数值,如 0-100)
ui->dial->setNotchTarget(10.0); // 数值大,目标距离远,刻度稀疏
3.2.3 关键注意事项(必知!)
  1. 生效前提(超级重要!) :必须先设置 notchesVisible=true,否则该属性无任何视觉效果!
  2. 是 "目标像素距离",不是 "刻度数量"(超级重要!说三遍!):Qt 会根据目标距离、旋钮尺寸、数值范围自动计算刻度数量!
  3. 数值与疏密的关系(超级重要!):数值越大越稀疏,越小越密集!
  4. 数值建议(超级重要!):建议 2.0 ~ 20.0,太小重叠,太大无参考意义!
  5. 尺寸适配(超级重要!):同一 target 值,旋钮越大,刻度线数量越多!
  6. 默认值为 3.766:刚创建 QDial 时,目标距离约为 4 像素,刻度较密集。

示例代码:

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include <QDial>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //OK,本文件我们来学习一下QDial的显示刻度的两个属性
    //QDial_Property_value_singleStep_pageStep

    /**
     * @class QDial 刻度线显示与密度属性
     * @brief QDial旋钮控件的刻度线显隐、刻度密度控制属性,是定制旋钮视觉样式的核心配置
     * @details 以下属性聚焦QDial的刻度线展示效果,notchesVisible控制刻度线是否显示,notchTarget控制刻度线的疏密程度,二者配合可实现不同视觉风格的旋钮刻度。
     *
     * ===================================== 属性1:notchesVisible =====================================
     * @property QDial::notchesVisible
     * @brief QDial是否显示刻度线的属性
     * @details 该属性控制旋钮外围是否绘制刻度线,默认值为false(不显示);
     *          设为true时,旋钮周围会出现均匀分布的刻度线,刻度线的疏密由notchTarget属性决定,仅影响视觉效果,不改变任何交互逻辑。
     *
     * 【关联API详解】
     * 1. bool notchesVisible() const → 获取当前是否显示刻度线;
     * 2. void setNotchesVisible(bool visible) → 设置是否显示刻度线(true显示,false隐藏)。
     *
     * @使用示例:
     * // 显示旋钮刻度线(基础配置)
     * ui->dial->setNotchesVisible(true);
     *
     * // 完整示例:显示刻度线 + 调整刻度密度
     * ui->dial->setRange(0, 100);
     * ui->dial->setNotchesVisible(true);    // 开启刻度显示
     * ui->dial->setNotchTarget(8.0);        // 设置刻度稀疏度
     *
     * 【关键注意事项】
     * 1. 仅视觉影响:刻度线的显示/隐藏不改变旋钮的数值调整、步长、循环等任何交互功能;
     * 2. 依赖notchTarget:仅当notchesVisible=true时,notchTarget的设置才会生效;
     * 3. 样式表覆盖:若通过QSS自定义了QDial的刻度线样式(如修改刻度颜色、宽度),该属性的显隐逻辑可能被覆盖;
     * 4. 范围适配:刻度线数量会自动适配旋钮的[minimum, maximum]范围,无需手动设置刻度数量。
     *
     * ===================================== 属性2:notchTarget =====================================
     * @property QDial::notchTarget
     * @brief QDial刻度线之间的相对位置(密度)属性
     * @details 该属性定义相邻两条刻度线之间的"目标像素距离",默认值为3.766(约4像素);
     *          数值越大,刻度线之间的像素距离越远,刻度线越稀疏;数值越小,刻度线越密集;
     *          仅当notchesVisible=true时,该属性才会生效,且实际刻度数量会根据旋钮尺寸、数值范围自动调整,尽可能接近设定的目标距离。
     *
     * 【关联API详解】
     * 1. qreal notchTarget() const → 获取当前刻度线的目标像素距离;
     * 2. void setNotchTarget(qreal target) → 设置刻度线的目标像素距离(建议target≥2.0,避免刻度线重叠)。
     *
     * @使用示例:
     * // 场景1:密集刻度(适合小范围数值,如0-20)
     * ui->dial->setRange(0, 20);
     * ui->dial->setNotchesVisible(true);
     * ui->dial->setNotchTarget(3.0);  // 数值小,刻度密集
     *
     * // 场景2:稀疏刻度(适合大范围数值,如0-100)
     * ui->dial->setRange(0, 100);
     * ui->dial->setNotchesVisible(true);
     * ui->dial->setNotchTarget(10.0); // 数值大,刻度稀疏
     *
     * 【关键注意事项】
     * 1. 生效前提:必须先设置notchesVisible=true,否则该属性无任何视觉效果;
     * 2. 相对距离而非绝对数量:target是"目标像素距离",不是"刻度数量",Qt会根据旋钮大小、数值范围自动计算刻度数量,尽可能让相邻刻度的像素距离接近target;
     * 3. 数值建议:target不宜过小(如<2.0),否则刻度线会重叠,影响视觉;也不宜过大(如>20.0),否则刻度线过少,失去参考意义;
     * 4. 尺寸适配:同一target值,旋钮尺寸越大,显示的刻度线数量越多;尺寸越小,数量越少。
     */

    QDial* dial=new QDial(this);

    dial->setSingleStep(5);

    dial->setPageStep(2);

    dial->setNotchesVisible(true);

    dial->setNotchTarget(2);
}

Widget::~Widget()
{
    delete ui;
}

第四章 QDial 视觉位置与交互规则属性

讲完了刻度线显示与密度属性,我们接下来讲三个和 "视觉位置控制" 与 "交互规则" 相关的属性:sliderPosition(视觉位置)、tracking(视觉跟踪)、wrapping(循环调整)。这三个属性中,tracking 默认是 true 一般不需要修改,sliderPosition 通常也不用单独管(因为 tracking=true 时和 value 同步),但 wrapping 是 QDial 的特色属性,非常实用。


4.1 属性六:sliderPosition(视觉位置)------ 旋钮显示在什么位置?

4.1.1 属性的核心定义

sliderPosition 属性是 QDial 用于控制旋钮的视觉显示位置的属性 ------ 它仅控制旋钮的角度(视觉上看起来指在什么位置),默认值为 0。

通俗理解:这个属性就像 "旋钮的'临时视觉指针'"------ 通常情况下(tracking=true,默认),这个指针和 "内部数值"(value)是绑定在一起的,数值变了,指针位置也跟着变,不用你单独管;但在特殊情况下(tracking=false),你可以让指针位置和内部数值分离 ------ 比如内部数值是 50,但指针显示在 70 的位置(初始视觉位置),等用户操作旋钮后,二者又会同步。

这里有几个超级重要的规则,必须牢记:

  1. 默认值:默认值为 0;
  2. 与 tracking 的关系(超级重要!) :
    • 当 tracking=true(默认)时,sliderPosition 与 value 始终实时同步 ------ 不用单独设置 sliderPosition,直接设置 value 即可,视觉位置会自动更新;
    • 当 tracking=false 时,sliderPosition 与 value 可以分离 ------ 你可以通过 setSliderPosition() 设置旋钮的临时视觉位置,此时内部数值(value)不变,仅视觉位置变;
  3. 初始位置简化设置(超级重要!) :
    • 若仅需设置旋钮的初始显示位置,直接设置 value 即可 (因为 tracking=true 时 sliderPosition 自动同步);
    • 无需 单独调用 setSliderPosition(),多此一举;
  4. 范围约束(超级重要!) :
    • sliderPosition 同样受 minimum 和 maximum 限制,超出范围会自动修正为边界值;
    • 和 value 一样;
  5. 用户操作强制同步(超级重要!) :
    • 无论 tracking 是否开启,用户拖动旋钮、按键盘调整旋钮时 ,value 和 sliderPosition 都会立即同步;
    • 也就是说,用户操作一定会让二者保持一致,只有代码操作可以让二者分离;
  6. 无信号触发(超级重要!) :
    • 修改 sliderPosition(仅在 tracking=false 时生效)仅改变视觉位置,不会 触发 valueChanged() 信号;
    • 因为内部数值(value)没变。
4.1.2 关联 API 详解(逐词逐句,零模糊)

sliderPosition 属性有两个关联的 API,和 value 类似:

1. 获取当前旋钮的视觉显示位置:int sliderPosition () const

函数原型:

复制代码
int QDial::sliderPosition() const;

逐词逐句底层解析:

  • int:返回值类型,返回当前的视觉位置;
  • QDial:::成员函数;
  • sliderPosition():函数名,"获取滑块位置(视觉位置)" 的意思;
  • const:常成员函数。

返回值说明:

  • 返回 int 类型的整数;
  • tracking=true 时,和 value() 返回值相同;
  • tracking=false 时,可能和 value() 不同。
2. 设置旋钮的视觉显示位置:void setSliderPosition (int pos)

函数原型:

复制代码
void QDial::setSliderPosition(int pos);

逐词逐句底层解析:

  • void:无返回值;
  • QDial:::成员函数;
  • setSliderPosition():函数名,"设置滑块位置(视觉位置)" 的意思;
  • int pos:入参,pos 是目标视觉位置。

参数说明:

  • pos:目标视觉位置,int 类型;
  • 超出范围自动修正为边界值;
  • tracking=true 时,调用这个方法会同步设置 value,并触发 valueChanged();
  • tracking=false 时,仅改变视觉位置,不改变 value,不触发信号。

使用示例:

复制代码
#include <QDebug>

// 先设置范围
ui->dial->setRange(0, 100);

// 示例1:默认 tracking=true,sliderPosition 与 value 自动同步
ui->dial->setValue(50); // 设置数值为 50
qDebug() << "内部数值:" << ui->dial->value(); // 输出 50
qDebug() << "视觉位置:" << ui->dial->sliderPosition(); // 输出 50(自动同步)

// 示例2:tracking=false,设置临时视觉位置(初始位置)
ui->dial->setTracking(false); // 先关闭跟踪
ui->dial->setValue(50); // 内部数值保留 50
ui->dial->setSliderPosition(70); // 旋钮显示在 70 的位置(临时视觉位置)
qDebug() << "内部数值:" << ui->dial->value(); // 输出 50(没变)
qDebug() << "视觉位置:" << ui->dial->sliderPosition(); // 输出 70(变了)
// 此时用户拖动旋钮,二者会立即同步
4.1.3 关键注意事项(必知!)
  1. 与 tracking 的关系(超级重要!) :tracking=true 时与 value 实时同步,无需单独管;tracking=false 时可分离;
  2. 初始位置简化设置(超级重要!) :仅需设置初始位置时,直接设 value 即可,不用单独设 sliderPosition;
  3. 范围约束(超级重要!) :同样受 minimum 和 maximum 限制,超出自动修正;
  4. 用户操作强制同步(超级重要!) :无论 tracking 是否开启,用户操作都会让二者立即同步;
  5. 无信号触发(超级重要!) :tracking=false 时修改 sliderPosition 仅改变视觉,不触发 valueChanged();
  6. 默认值为 0:刚创建 QDial 时,视觉位置是 0。

4.2 属性七:tracking(视觉跟踪)------ 视觉位置要不要跟着数值变?

4.2.1 属性的核心定义

tracking 属性是 QDial 用于控制旋钮视觉位置与内部数值(value)的同步规则的属性 ------ 它决定了 "代码修改数值时,视觉位置要不要立即跟着变"。

通俗理解:这个属性就像 "旋钮的'视觉跟拍开关'"------ 如果开关打开(默认),你用代码修改数值,旋钮的视觉位置会立即跟着变(实时跟拍);如果开关关闭,你用代码修改数值,旋钮的视觉位置不变(不跟拍),需要你手动调用 setSliderPosition() 才会更新视觉位置。

这里有几个超级重要的规则,必须牢记:

  1. 默认值 :默认值为 true(开启视觉跟踪)------ 也就是代码修改数值时,视觉位置立即同步更新;
  2. 默认无需修改(超级重要!) :
    • true 是最符合用户直觉的配置 ------ 数值变了,旋钮位置当然要跟着变;
    • 一般不需要修改这个属性,保持默认即可;
    • 仅在 "避免频繁视觉刷新" 的特殊场景(比如高频数值更新,每次更新都刷新视觉会导致界面闪烁)才需关闭;
  3. 对代码操作和用户操作的不同影响(超级重要!) :
    • 仅影响代码操作 :tracking 仅对 "代码调用 setValue()" 的视觉表现生效;
    • 不影响用户操作 :无论 tracking 是否开启,用户拖动旋钮、按键盘调整旋钮时 ,视觉位置和数值都会立即同步;
  4. 不影响信号触发(超级重要!) :
    • 无论 tracking 是否开启,只要 value 变化,都会触发 valueChanged() 信号;
    • tracking 仅影响视觉位置,不影响信号触发;
  5. 特殊场景:关闭跟踪 :
    • 比如你要批量更新数值,中间的过渡数值不想让用户看到,避免界面闪烁:可以先关闭 tracking,多次调用 setValue()(仅更新内部数值,视觉不变),最后统一调用 setSliderPosition() 刷新视觉。
4.2.2 关联 API 详解(逐词逐句,零模糊)

tracking 属性有两个关联的 API,注意获取函数的名字是 hasTracking(),符合 Qt 的命名习惯(布尔值的获取函数通常以 is 或 has 开头):

1. 获取当前是否开启视觉跟踪:bool hasTracking () const

函数原型:

复制代码
bool QDial::hasTracking() const;

逐词逐句底层解析:

  • bool:返回值类型,返回 true 或 false;
  • QDial:::成员函数;
  • hasTracking():函数名,"有没有开启跟踪" 的意思;
  • const:常成员函数。

返回值说明:

  • true 表示开启视觉跟踪(默认);
  • false 表示关闭视觉跟踪。
2. 设置是否开启视觉跟踪:void setTracking (bool enable)

函数原型:

复制代码
void QDial::setTracking(bool enable);

逐词逐句底层解析:

  • void:无返回值;
  • QDial:::成员函数;
  • setTracking():函数名,"设置是否开启跟踪" 的意思;
  • bool enable:入参,enable 是 true 开启,false 关闭。

参数说明:

  • enable:true 开启视觉跟踪,false 关闭;
  • 一般保持默认 true 即可。

使用示例:

复制代码
#include <QDebug>

// 特殊场景:关闭跟踪(批量更新数值后统一刷新视觉)
ui->dial->setTracking(false); // 先关闭跟踪
// 多次修改数值,视觉位置不刷新(避免闪烁)
ui->dial->setValue(20);
ui->dial->setValue(40);
ui->dial->setValue(60);
// 统一同步视觉位置
ui->dial->setSliderPosition(60);
// 此时旋钮才会显示在 60 的位置
4.2.3 关键注意事项(必知!)
  1. 默认无需修改(超级重要!) :true 是最符合直觉的配置,一般不需要改;
  2. 仅影响代码操作(超级重要!) :仅对 "代码调用 setValue()" 的视觉表现生效;
  3. 不影响用户操作(超级重要!):用户操作始终同步数值和视觉;
  4. 不影响信号触发(超级重要!) :无论 tracking 是否开启,value 变化都会触发 valueChanged();
  5. 特殊场景使用:仅在高频更新避免闪烁时才需关闭;
  6. 默认值为 true:刚创建 QDial 时,开启视觉跟踪。

4.3 属性八:wrapping(循环调整)------ 到了边界能不能 "套圈"?

4.3.1 属性的核心定义

wrapping 属性是 QDial 用于控制旋钮调整至边界时的交互行为的属性 ------ 也就是是否允许数值循环调整("套圈")。

通俗理解:这个属性就像 "旋钮的'套圈开关'"------ 如果开关关闭(默认),数值到了最大值后,继续顺时针旋转、按 ↑ 键、按 PageUp,就没反应了(停在最大值);数值到了最小值后,继续逆时针旋转、按 ↓ 键、按 PageDown,也没反应了(停在最小值);如果开关打开,数值到了最大值后继续增大,会跳至最小值;数值到了最小值后继续减小,会跳至最大值 ------ 就像一个循环的圆圈,"套圈" 了。

这里有几个超级重要的规则,必须牢记,刻在脑子里:

  1. 默认值 :默认值为 false(不循环)------ 也就是到了边界后就停住,不 "套圈";
  2. 设为 true 时的行为(超级重要!) :
    • 数值达到 maximum 后,继续增大(顺时针拖动、按 ↑ 键、按 PageUp),会跳至 minimum;
    • 数值达到 minimum 后,继续减小(逆时针拖动、按 ↓ 键、按 PageDown),会跳至 maximum;
  3. 仅用户操作生效(超级重要!) :
    • 循环功能仅对用户操作生效(拖动旋钮、按方向键、按 PageUp/PageDown);
    • 代码直接调用 setValue() 设置超出范围的数值时,仍会修正为边界值,不会触发循环;
    • 这点非常重要!一定要注意!
  4. 场景适配(超级重要!) :
    • 循环功能适合 "连续无边界" 的调整场景 ------ 比如角度 0-359(到了 359 后继续转应该回到 0)、数字 0-9(模拟拨号盘)、颜色色相 0-359;
    • 大范围数值(如 0-100)慎用循环 ------ 用户可能不小心 "套圈",从 100 跳到 0,体验不好;
  5. 步长兼容(超级重要!) :
    • singleStep(方向键)和 pageStep(PageUp/PageDown)调整至边界时,同样触发循环;
    • 比如 max=9、step=2、value=8,按 ↑ 键 → 8+2=10 → 超出 max=9 → 触发循环 → 跳至 min=0;
  6. 视觉表现(超级重要!) :
    • 循环时旋钮从边界位置直接跳转至另一边界,无过渡旋转,符合 "套圈" 的交互直觉;
    • 比如从 max 跳到 min,旋钮会直接从最大角度跳到最小角度,不会慢慢转一圈。
4.3.2 关联 API 详解(逐词逐句,零模糊)

wrapping 属性有两个关联的 API,非常简单:

1. 获取当前是否允许循环调整:bool wrapping () const

函数原型:

复制代码
bool QDial::wrapping() const;

逐词逐句底层解析:

  • bool:返回值类型,返回 true 或 false;
  • QDial:::成员函数;
  • wrapping():函数名,"是不是允许循环" 的意思;
  • const:常成员函数。

返回值说明:

  • true 表示允许循环调整("套圈");
  • false 表示不循环(默认)。
2. 设置是否允许循环调整:void setWrapping (bool wrap)

函数原型:

复制代码
void QDial::setWrapping(bool wrap);

逐词逐句底层解析:

  • void:无返回值;
  • QDial:::成员函数;
  • setWrapping():函数名,"设置是否允许循环" 的意思;
  • bool wrap:入参,wrap 是 true 允许循环,false 不循环。

参数说明:

  • wrap:true 允许循环调整,false 不循环。

使用示例:

复制代码
#include <QDebug>

// 示例:允许循环调整(模拟 0-9 的数字选择旋钮)
ui->dial->setRange(0, 9);
ui->dial->setWrapping(true); // 开启循环!
ui->dial->setValue(9);
// 按 ↑ 键 / PageUp / 顺时针拖动 → 数值跳至 0
ui->dial->setValue(0);
// 按 ↓ 键 / PageDown / 逆时针拖动 → 数值跳至 9

// 代码调用 setValue 不循环(仅用户操作循环!)
ui->dial->setValue(9);
ui->dial->setValue(10); // 代码设置超出范围
// 不会循环到 0,而是修正为 9(最大值)
qDebug() << "修正后数值:" << ui->dial->value(); // 输出 9
4.3.3 关键注意事项(必知!)
  1. 设为 true 时的边界跳转(超级重要!):最大值后增大跳至最小值,最小值后减小跳至最大值;
  2. 仅用户操作生效(超级重要!) :代码调用 setValue() 超出范围时,仍修正为边界值,不会循环!
  3. 场景适配(超级重要!):适合连续无边界场景(角度 0-359、数字 0-9),大范围数值慎用!
  4. 步长兼容(超级重要!) :singleStep 和 pageStep 调整至边界时同样触发循环!
  5. 视觉表现(超级重要!):循环时旋钮直接跳转,无过渡旋转!
  6. 默认值为 false:刚创建 QDial 时,不循环。

示例代码:

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //OK,本文件我们来学习QDial的剩下几个不重要的属性

    /**
     * @class QDial 视觉位置与交互规则属性
     * @brief QDial旋钮控件的显示位置、视觉跟踪、循环调整属性,是定制旋钮视觉同步逻辑与边界交互规则的核心配置
     * @details 以下属性聚焦QDial的视觉位置控制(sliderPosition)、视觉与数值的同步规则(tracking)、边界循环交互(wrapping),决定了旋钮的视觉表现和调整行为逻辑。
     *
     * ===================================== 属性1:sliderPosition =====================================
     * @property QDial::sliderPosition
     * @brief QDial界面上旋钮显示的初始/临时视觉位置属性
     * @details 该属性仅控制旋钮的视觉显示位置,默认值为0;
     *          当tracking=true(默认)时,sliderPosition与value始终实时同步,无需单独设置;
     *          当tracking=false时,可通过该属性设置旋钮的临时视觉位置,此时内部数值(value)与视觉位置可分离,用户操作旋钮后会同步更新value和sliderPosition。
     *
     * 【关联API详解】
     * 1. int sliderPosition() const → 获取当前旋钮的视觉显示位置;
     * 2. void setSliderPosition(int pos) → 设置旋钮的视觉显示位置(仅tracking=false时生效)。
     *
     * @使用示例:
     * // 场景1:默认tracking=true,sliderPosition与value自动同步
     * ui->dial->setRange(0, 100);
     * ui->dial->setValue(50); // 设置数值为50
     * qDebug() << "内部数值:" << ui->dial->value(); // 输出50
     * qDebug() << "视觉位置:" << ui->dial->sliderPosition(); // 输出50
     *
     * // 场景2:tracking=false,设置临时视觉位置(初始位置)
     * ui->dial->setTracking(false);
     * ui->dial->setValue(50); // 内部数值保留50
     * ui->dial->setSliderPosition(70); // 旋钮显示在70的位置(初始视觉位置)
     * qDebug() << "内部数值:" << ui->dial->value(); // 输出50
     * qDebug() << "视觉位置:" << ui->dial->sliderPosition(); // 输出70
     *
     * 【关键注意事项】
     * 1. 初始位置简化设置:若仅需设置旋钮初始显示位置,直接设置value即可(tracking=true时sliderPosition自动同步),无需单独调用setSliderPosition;
     * 2. 范围约束:sliderPosition同样受minimum和maximum限制,超出范围会自动修正为边界值;
     * 3. 用户操作强制同步:无论tracking是否开启,用户拖动/按键调整旋钮时,value和sliderPosition都会立即同步;
     * 4. 无信号触发:修改sliderPosition(tracking=false时)仅改变视觉,不会触发valueChanged()信号。
     *
     * ===================================== 属性2:tracking =====================================
     * @property QDial::tracking
     * @brief QDial旋钮外观是否跟踪数值变化的属性
     * @details 该属性控制旋钮视觉位置与内部数值(value)的同步规则,默认值为true(跟踪);
     *          - tracking=true:任何修改value的操作(代码setValue/用户操作)都会立即同步更新旋钮视觉位置;
     *          - tracking=false:代码调用setValue仅更新内部数值,视觉位置不变(需手动setSliderPosition),用户操作仍会同步数值和视觉位置。
     *
     * 【关联API详解】
     * 1. bool hasTracking() const → 获取当前是否开启视觉跟踪;
     * 2. void setTracking(bool enable) → 设置是否开启视觉跟踪(默认true,一般无需修改)。
     *
     * @使用示例:
     * // 特殊场景:关闭跟踪(批量更新数值后统一刷新视觉)
     * ui->dial->setTracking(false);
     * // 多次修改数值,视觉位置不刷新
     * ui->dial->setValue(20);
     * ui->dial->setValue(40);
     * ui->dial->setValue(60);
     * // 统一同步视觉位置
     * ui->dial->setSliderPosition(60);
     *
     * 【关键注意事项】
     * 1. 默认无需修改:true是最符合用户直觉的配置,视觉位置与数值实时同步,仅在"避免频繁视觉刷新"的特殊场景(如高频数值更新)才需关闭;
     * 2. 不影响信号触发:无论tracking是否开启,value变化时都会触发valueChanged()信号;
     * 3. 仅影响代码操作:用户操作始终同步数值和视觉,tracking仅对"代码调用setValue"的视觉表现生效。
     *
     * ===================================== 属性3:wrapping =====================================
     * @property QDial::wrapping
     * @brief QDial是否允许数值循环调整("套圈")的属性
     * @details 该属性控制旋钮调整至边界时的交互行为,默认值为false(不循环);
     *          - wrapping=true:数值达到maximum后继续增大(拖动/方向键/PageUp),会跳至minimum;数值达到minimum后继续减小,会跳至maximum;
     *          - wrapping=false:数值达到边界后无法继续向该方向调整,停在minimum/maximum。
     *
     * 【关联API详解】
     * 1. bool wrapping() const → 获取当前是否允许循环调整;
     * 2. void setWrapping(bool wrap) → 设置是否允许循环调整。
     *
     * @使用示例:
     * // 允许循环调整(模拟0-9的数字选择旋钮)
     * ui->dial->setRange(0, 9);
     * ui->dial->setWrapping(true);
     * ui->dial->setValue(9);
     * // 按↑键/PageUp/顺时针拖动 → 数值跳至0
     * ui->dial->setValue(0);
     * // 按↓键/PageDown/逆时针拖动 → 数值跳至9
     *
     * 【关键注意事项】
     * 1. 仅用户操作生效:代码直接调用setValue设置超出范围的数值时,仍会修正为边界值,不会触发循环;
     * 2. 场景适配:循环功能适合"连续无边界"的调整场景(如角度0-359、数字0-9),大范围数值(如0-100)慎用;
     * 3. 步长兼容:singleStep/pageStep调整至边界时,同样触发循环(如max=9、step=2,value=8按↑→0);
     * 4. 视觉表现:循环时旋钮从边界位置直接跳转至另一边界,无过渡旋转,符合"套圈"的交互直觉。
     */
}

Widget::~Widget()
{
    delete ui;
}

第五章 QDial 核心交互信号详解

讲完了所有核心属性,我们最后来讲 QDial 的核心交互信号 。QDial 的信号非常少,只有两个,而且和 QSlider 完全一样:valueChanged(int)(数值改变时触发)、rangeChanged(int, int)(范围改变时触发)。这两个信号是 QDial 与业务逻辑联动的核心桥梁。


5.1 信号一:valueChanged (int value)------ 数值变了!(核心!最常用!)

5.1.1 信号的核心定义

valueChanged(int value) 是 QDial 最核心、最常用、最高频 的信号 ------ 它是旋钮内部数值(value)发生任何变更时触发的信号,无论变化来源是什么。

通俗理解:这个信号就像 "数值报警器"------ 只要旋钮的数值变了,不管是用户拖动旋钮变的、按键盘变的、代码设置变的、还是自动修正变的,它都会响,告诉你 "现在的新数值是这个"。它是实现 "旋钮调整 - 逻辑联动" 的核心入口 ------ 比如旋钮调整音量,音量跟着变;旋钮调整进度,进度条跟着变;旋钮调整透明度,窗口透明度跟着变。

这里有几个超级重要的规则,必须牢记:

  1. 触发时机(超级重要!) :只要旋钮内部数值(value)发生变化,就会触发,包括但不限于:
    1. 用户交互:拖动旋钮、按方向键、按 PageUp/PageDown 调整数值;
    2. 代码操作 :调用 setValue() 直接修改数值;
    3. 自动修正 :设置数值超出范围、修改 minimum/maximum 导致当前值被自动修正;
    4. 循环调整 :wrapping=true 时,数值 "套圈" 跳转(如 max→min);
  2. 参数说明(超级重要!) :
    • 参数 value 是 int 类型,是旋钮变更后的最新数值;
    • 这个数值始终在 [minimum, maximum] 范围内,是修正后的有效数值,不用担心越界;
  3. 与 tracking 无关(超级重要!) :
    • 无论 tracking 是否开启,只要 value 变化就会触发;
    • tracking 仅影响视觉位置,不影响信号触发;
  4. 高频触发(超级重要!) :
    • 用户拖动旋钮时会高频触发(每帧调整数值都会触发一次);
    • 如果信号槽内有耗时操作(比如网络请求、大量计算、复杂的 UI 更新),需做防抖处理(比如延迟 100ms 执行,期间再次触发则重置延迟);
    • 这点非常重要!否则拖动旋钮时界面会卡顿!
  5. 触发优先级(超级重要!) :
    • 所有导致 value 变化的操作都会先变更数值,再触发信号;
    • 也就是说,在槽函数里调用 ui->dial->value(),得到的就是新数值,和参数 value 一样。
5.1.2 使用示例

这个信号没有重载(只有一个 int 参数的版本),所以连接时不需要 static_cast 或 QOverload,直接连就行,非常简单:

复制代码
#include <QDebug>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    // 先初始化旋钮
    ui->dial->setRange(0, 100);
    ui->dial->setValue(100);
    ui->dial->setWrapping(true);
    ui->dial->setNotchesVisible(true);

    // 示例1:联动进度条显示旋钮数值(最常用!)
    ui->progressBar->setRange(0, 100);
    connect(ui->dial, &QDial::valueChanged, ui->progressBar, &QProgressBar::setValue);

    // 示例2:打印变更后的数值,同时调整窗口透明度(结合你提供的资料!)
    connect(ui->dial, &QDial::valueChanged, this, [=](int newVal){
        qDebug() << "旋钮当前数值:" << newVal;
        // 调整窗口透明度:0 完全透明,100 完全不透明
        this->setWindowOpacity((double)newVal / 100.0);
        // 更新标签显示
        ui->label->setText(QString("当前不透明度为: %1").arg(newVal));
    });
}
5.1.3 关键注意事项(必知!)
  1. 触发时机(超级重要!) :所有导致 value 变化的操作都会触发!
  2. 参数可靠性(超级重要!) :参数 value 始终是修正后的有效数值,不会越界!
  3. 与 tracking 无关(超级重要!) :无论 tracking 是否开启,只要 value 变化就触发!
  4. 高频触发(超级重要!):用户拖动时会高频触发,耗时操作需做防抖!
  5. 触发优先级:先变更数值,再触发信号!
  6. 无重载 :只有一个 int 参数的版本,连接时直接连即可!

5.2 信号二:rangeChanged (int min, int max)------ 范围变了!

5.2.1 信号的核心定义

rangeChanged(int min, int max) 是 QDial 仅在数值范围(minimum/maximum)发生变更时触发的信号 ------ 它的使用频率比 valueChanged 低很多,但在 "范围变更后同步更新 UI / 逻辑" 的场景下非常有用。

通俗理解:这个信号就像 "范围报警器"------ 只有当你用代码修改了最小值或最大值时,它才会响,告诉你 "现在的新范围是从 min 到 max";用户旋转旋钮不会触发这个信号,因为用户操作不会改变范围。

这里有几个超级重要的规则,必须牢记:

  1. 触发时机(超级重要!) :仅在旋钮的数值上下限发生变化时触发,包括:
    1. 代码调用 setMinimum() / setMaximum() 单独修改最小值 / 最大值;
    2. 代码调用 setRange() 同时修改最小值和最大值;
    3. 自动调整:设置 min > max 时,Qt 自动交换二者导致范围变更;
  2. 仅范围变更触发(超级重要!) :
    • 数值变化 (setValue / 用户操作)不会触发该信号;
    • 只有范围变化才会触发;
  3. 参数说明(超级重要!) :
    • 参数 min 是 int 类型,是旋钮变更后的最新最小值;
    • 参数 max 是 int 类型,是旋钮变更后的最新最大值;
    • min 始终 ≤ max,Qt 会自动修正 min > max 的情况,信号参数为修正后的合法范围;
  4. 联动 valueChanged(超级重要!) :
    • 若范围变更导致当前 value 被自动修正(比如原 value=150,新 max=100,value 被修正为 100),会先触发 rangeChanged,再触发 valueChanged;
    • 顺序是:先改范围,触发范围信号;再修正数值,触发数值信号;
  5. 低频触发(超级重要!) :
    • 仅代码修改范围时触发,用户操作不会触发;
    • 无需考虑高频触发问题,信号槽内可以放心写逻辑。
5.2.2 使用示例

同样,这个信号也没有重载,直接连即可:

复制代码
#include <QDebug>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    // 绑定信号,更新标签显示当前数值范围
    connect(ui->dial, &QDial::rangeChanged, this, [=](int newMin, int newMax){
        qDebug() << "[Dial Range Changed] 新范围:" << newMin << "~" << newMax;
        // 更新界面上的范围标签
        ui->labRange->setText(QString("旋钮范围:%1 ~ %2").arg(newMin).arg(newMax));
    });

    // 修改范围触发信号
    ui->dial->setRange(0, 200); // 触发 rangeChanged(0, 200)
}
5.2.3 关键注意事项(必知!)
  1. 仅范围变更触发(超级重要!):数值变化不会触发!
  2. 触发时机(超级重要!):仅代码修改范围时触发!
  3. 参数合法性(超级重要!) :min 始终 ≤ max,Qt 会自动修正!
  4. 联动 valueChanged(超级重要!) :范围变更导致 value 修正时,先触发 rangeChanged,再触发 valueChanged!
  5. 低频触发:无需考虑高频问题!

示例代码:

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //OK,本文件我们就来学习一下QDial的信号,其实也是很简单的,就是获取数值罢了
    //属性                                说明
    //valueChanged(int) 数值改变时触发
    //rangeChanged(int, int) 范围变化时触发

    /**
     * @class QDial 核心交互信号
     * @brief QDial旋钮控件的核心反馈信号,覆盖数值变更与范围变更两类关键交互反馈
     * @details 这两个信号是QDial与业务逻辑联动的核心桥梁,valueChanged反馈数值调整结果,rangeChanged反馈数值范围变更,可根据场景绑定不同信号实现逻辑联动。
     *
     * ===================================== 核心信号详解 =====================================
     *
     * ------------------------------------- 信号1:valueChanged(int value) -------------------------------------
     * @signal void QDial::valueChanged(int value)
     * @brief QDial旋钮数值发生任何变更时触发的核心信号
     * @details 该信号是QDial最常用的反馈信号,只要旋钮内部数值(value)发生变化,无论变化来源(用户操作、代码设置、自动修正),都会触发;
     *          信号参数为变更后的最新数值,是实现"旋钮调整-逻辑联动"的核心入口(如音量调整、进度同步)。
     *
     * @参数说明:
     * @param value - int类型,旋钮变更后的最新数值(始终在[minimum, maximum]范围内)。
     *
     * @触发场景:
     * 1. 用户交互:拖动旋钮、按方向键/PageUp/PageDown调整数值;
     * 2. 代码操作:调用setValue()直接修改数值;
     * 3. 自动修正:设置数值超出范围、修改minimum/maximum导致当前值被修正;
     * 4. 循环调整:wrapping=true时,数值"套圈"跳转(如max→min)。
     *
     * @使用示例:
     * // 场景1:联动进度条显示旋钮数值
     * ui->dial->setRange(0, 100);
     * ui->progressBar->setRange(0, 100);
     * connect(ui->dial, &QDial::valueChanged, ui->progressBar, &QProgressBar::setValue);
     *
     * // 场景2:打印变更后的数值
     * connect(ui->dial, &QDial::valueChanged, this, [=](int newVal){
     *     qDebug() << "旋钮当前数值:" << newVal;
     * });
     *
     * 【关键注意事项】
     * 1. 触发优先级:所有导致value变化的操作都会触发,且先变更数值再触发信号;
     * 2. 与tracking无关:无论tracking是否开启,只要value变化就会触发,tracking仅影响视觉位置,不影响信号;
     * 3. 参数可靠性:参数value始终是修正后的有效数值,不会出现超出[minimum, maximum]的情况;
     * 4. 高频触发:用户拖动旋钮时会高频触发(每帧调整数值),若信号槽内有耗时操作,需做防抖处理。
     *
     * ------------------------------------- 信号2:rangeChanged(int min, int max) -------------------------------------
     * @signal void QDial::rangeChanged(int min, int max)
     * @brief QDial旋钮数值范围(minimum/maximum)发生变更时触发的信号
     * @details 该信号仅在旋钮的数值上下限发生变化时触发,参数为变更后的最新最小值和最大值,适用于"范围变更后同步更新UI/逻辑"的场景(如更新范围提示标签)。
     *
     * @参数说明:
     * @param min - int类型,旋钮变更后的最新最小值;
     * @param max - int类型,旋钮变更后的最新最大值(始终满足min ≤ max)。
     *
     * @触发场景:
     * 1. 代码调用setMinimum()/setMaximum()单独修改最小值/最大值;
     * 2. 代码调用setRange()同时修改最小值和最大值;
     * 3. 自动调整:设置min > max时,Qt自动交换二者导致范围变更。
     *
     * @使用示例:
     * // 绑定信号,更新标签显示当前数值范围
     * connect(ui->dial, &QDial::rangeChanged, this, [=](int newMin, int newMax){
     *     ui->labRange->setText(QString("旋钮范围:%1 ~ %2").arg(newMin).arg(newMax));
     * });
     *
     * // 修改范围触发信号
     * ui->dial->setRange(0, 200); // 触发rangeChanged(0, 200)
     *
     * 【关键注意事项】
     * 1. 仅范围变更触发:数值变化(setValue/用户操作)不会触发该信号;
     * 2. 参数合法性:min始终≤max,Qt会自动修正min>max的情况,信号参数为修正后的合法范围;
     * 3. 联动valueChanged:若范围变更导致当前value被自动修正(如原value=150,新max=100),会先触发rangeChanged,再触发valueChanged;
     * 4. 低频触发:仅代码修改范围时触发,用户操作不会触发,无需考虑高频问题。
     */

    //我们依旧使用ui界面进行设置

}

Widget::~Widget()
{
    delete ui;
}


void Widget::on_dial_valueChanged(int value)
{
    // 1. valueChanged 信号
    // 触发时机:当用户旋转旋钮,导致数值发生变化时触发。
    // 注意:如果开启了 tracking (默认 true),每转动一点都会触发;
    //       如果关闭了 tracking,只有在释放鼠标时才触发。

    qDebug() << "[Dial Value Changed] 当前数值:" << value;

    // 典型用法:联动标签显示、控制音量、调节亮度等
    // ui->labelValue->setText(QString::number(value));
}

void Widget::on_dial_rangeChanged(int min, int max)
{
    // 2. rangeChanged 信号
    // 触发时机:当调用 setMinimum()、setMaximum() 或 setRange()
    //          导致最小值或最大值发生改变时触发。
    // 注意:用户旋转旋钮不会触发此信号,只有代码动态修改范围时才会触发。

    qDebug() << "[Dial Range Changed] 新范围:" << min << "~" << max;

    // 典型用法:
    // 1. 日志记录:记录范围何时被动态调整。
    // 2. 联动更新:如果界面上有显示范围的标签(如 "0 - 100"),在这里更新它。
    // 3. 逻辑校验:检查新范围是否合法,或者根据新范围重置当前值。

    // 示例:更新范围标签
    // ui->labelRange->setText(QString("%1 - %2").arg(min).arg(max));
}

第六章 综合示例:调整窗口透明度

讲完了所有属性和信号,我们最后来做一个综合示例,实现 "通过 QDial 调整窗口透明度" 的功能 ------ 这是 QDial 最经典的应用场景之一。

6.1 场景描述

界面上有:

  1. 一个 QDial 控件(objectName 为 dial);
  2. 一个 QLabel 控件(objectName 为 label),用于显示当前不透明度;
  3. 窗口本身。

功能:

  • 拖动 QDial 或按键盘调整数值,窗口透明度随之变化;
  • QDial 范围 0-100,0 表示完全透明,100 表示完全不透明;
  • QDial 显示刻度线,允许循环调整;
  • Label 实时显示当前不透明度数值。

6.2 代码实现

复制代码
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    // 1. 初始化 QDial
    ui->dial->setWrapping(true);          // 设置可以循环旋转
    ui->dial->setNotchesVisible(true);     // 设置刻度线可见
    ui->dial->setRange(0, 100);            // 设置范围 0-100
    ui->dial->setValue(100);                // 设置初始值为 100(完全不透明)
    ui->dial->setSingleStep(1);             // 方向键步长 1
    ui->dial->setPageStep(10);              // PageUp/PageDown 步长 10

    // 2. 初始化窗口透明度为 100%(完全不透明)
    this->setWindowOpacity(1.0);

    // 3. 初始化 Label 显示
    ui->label->setText("当前不透明度为: 100");
}

Widget::~Widget()
{
    delete ui;
}

// 4. QDial 的 valueChanged 槽函数(自动生成或手动连接)
void Widget::on_dial_valueChanged(int value)
{
    // 更新 Label 显示
    ui->label->setText(QString("当前不透明度为: %1").arg(value));
    // 调整窗口透明度:value 是 0-100,windowOpacity 需要 0.0-1.0,所以除以 100.0
    this->setWindowOpacity((double)value / 100.0);
}

完整示例代码:

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //OK大家,接下来我们来学习一下QDial控件,这个控件也是比较简单
    //这个空间和微调框的作用类似,只不过它是使用一个类似旋钮的,就是类似车的把手的样子去进行调整数值的~~
    //我们来看它的属性:

    /**
     * @class QDial
     * @brief Qt中提供圆形旋钮交互的数值输入控件,与QSlider/QSpinBox功能类似但视觉为旋转式
     * @details QDial通过鼠标拖动旋钮旋转、键盘方向键/PageUpDown调整数值,支持范围限制、步长设置、刻度显示、循环调整等功能;
     *          视觉上为圆形,天然适配音量、亮度、角度、百分比这类有"旋转感"的场景,也可替代QSlider用于界面空间有限的位置。
     *
     * 【核心特征】
     * 1. 多方式调整:鼠标拖动旋钮、键盘方向键(↑/↓/←/→)、PageUp/PageDown;
     * 2. 范围与步长:支持最小值/最大值、单步(方向键)/页步(PageUpDown)设置;
     * 3. 视觉定制:可显示/隐藏刻度线、调整刻度密度、设置旋钮初始位置;
     * 4. 循环调整:支持数值"套圈"(超过最大值→最小值,低于最小值→最大值);
     * 5. 跟踪同步:默认外观(旋钮位置)与数值同步,可关闭。
     *
     * ===================================== 核心属性详解 =====================================
     *
     * ------------------------------------- 属性1:value -------------------------------------
     * @property QDial::value
     * @brief QDial存储的当前整数值属性
     * @details 该属性表征旋钮的核心数值,默认值为0;
     *          数值范围受minimum和maximum限制,超出范围的设置会自动修正为最近的边界值;
     *          修改该属性会触发valueChanged()信号,若tracking=true,旋钮位置会同步更新。
     *
     * 【关联API详解】
     * 1. int value() const → 获取当前数值;
     * 2. void setValue(int val) → 设置当前数值(超出范围自动修正)。
     *
     * @使用示例:
     * // 设置初始值为50(范围0-100)
     * ui->dial->setRange(0, 100);
     * ui->dial->setValue(50);
     * qDebug() << "当前旋钮数值:" << ui->dial->value(); // 输出50
     *
     * 【关键注意事项】
     * 1. 范围强制限制:无论用户操作还是代码设置,数值始终在[minimum, maximum]之间;
     * 2. 与tracking联动:tracking=true时,setValue()会同步更新旋钮位置;tracking=false时,仅更新内部数值,旋钮位置不变;
     * 3. 信号触发:setValue()会强制触发valueChanged(),不受tracking影响。
     *
     * ------------------------------------- 属性2:minimum -------------------------------------
     * @property QDial::minimum
     * @brief QDial允许的最小数值属性(数值下限)
     * @details 该属性定义旋钮可调整的数值下限,默认值为0;
     *          数值范围为[minimum, maximum],若设置的minimum > maximum,会自动交换二者确保范围有效;
     *          当前值小于新minimum时,会自动修正为新minimum。
     *
     * 【关联API详解】
     * 1. int minimum() const → 获取当前最小值;
     * 2. void setMinimum(int min) → 设置最小值(自动调整范围有效性);
     * 3. void setRange(int min, int max) → 同时设置最小值和最大值(更便捷,推荐使用)。
     *
     * @使用示例:
     * // 单独设置最小值为-100
     * ui->dial->setMinimum(-100);
     * ui->dial->setMaximum(100);
     *
     * // 更便捷:同时设置范围
     * ui->dial->setRange(-100, 100);
     *
     * 【关键注意事项】
     * 1. 范围有效性:若min > max,setMinimum()会自动将max设为min,setMaximum()会自动将min设为max;
     * 2. 自动修正:当前值小于新min时,立即修正为min,并触发valueChanged()和rangeChanged();
     * 3. 与wrapping配合:wrapping=true时,最小值+1步长会跳到最大值。
     *
     * ------------------------------------- 属性3:maximum -------------------------------------
     * @property QDial::maximum
     * @brief QDial允许的最大数值属性(数值上限)
     * @details 该属性定义旋钮可调整的数值上限,默认值为99;
     *          数值范围为[minimum, maximum],若设置的maximum < minimum,会自动交换二者确保范围有效;
     *          当前值大于新maximum时,会自动修正为新maximum。
     *
     * 【关联API详解】
     * 1. int maximum() const → 获取当前最大值;
     * 2. void setMaximum(int max) → 设置最大值(自动调整范围有效性);
     * 3. void setRange(int min, int max) → 同时设置最小值和最大值(推荐)。
     *
     * @使用示例:
     * // 单独设置最大值为200
     * ui->dial->setMaximum(200);
     *
     * 【关键注意事项】
     * 1. 范围有效性:同minimum,若max < min,自动交换;
     * 2. 自动修正:当前值大于新max时,立即修正为max,并触发valueChanged()和rangeChanged();
     * 3. 与wrapping配合:wrapping=true时,最大值-1步长会跳到最小值。
     *
     * ------------------------------------- 属性4:singleStep -------------------------------------
     * @property QDial::singleStep
     * @brief QDial按下键盘方向键时的单步调整步长属性
     * @details 该属性定义按下↑/↓/←/→方向键时,数值的单次变化量,默认值为1;
     *          步长应为正整数,设为0或负数时,调整行为无实际意义(通常自动修正为1)。
     *
     * 【关联API详解】
     * 1. int singleStep() const → 获取当前单步步长;
     * 2. void setSingleStep(int step) → 设置单步步长(建议step≥1)。
     *
     * @使用示例:
     * // 设置方向键步长为10(每次按方向键变化10)
     * ui->dial->setRange(0, 100);
     * ui->dial->setSingleStep(10);
     * ui->dial->setValue(50);
     * // 按↑键 → 数值变为60;按↓键 → 数值变为40
     *
     * 【关键注意事项】
     * 1. 仅方向键生效:鼠标拖动旋钮、PageUp/PageDown不受该属性影响;
     * 2. 边界处理:调整后超出范围时,自动修正为边界值;
     * 3. 与wrapping配合:wrapping=true时,方向键调整到边界会自动套圈。
     *
     * ------------------------------------- 属性5:pageStep -------------------------------------
     * @property QDial::pageStep
     * @brief QDial按下PageUp/PageDown时的页步调整步长属性
     * @details 该属性定义按下PageUp/PageDown键时,数值的单次变化量,默认值为10;
     *          步长应为正整数,设为0或负数时,调整行为无实际意义(通常自动修正为10)。
     *
     * 【关联API详解】
     * 1. int pageStep() const → 获取当前页步步长;
     * 2. void setPageStep(int step) → 设置页步步长(建议step≥1)。
     *
     * @使用示例:
     * // 设置PageUp/PageDown步长为20(每次按变化20)
     * ui->dial->setRange(0, 100);
     * ui->dial->setPageStep(20);
     * ui->dial->setValue(50);
     * // 按PageUp → 数值变为70;按PageDown → 数值变为30
     *
     * 【关键注意事项】
     * 1. 仅PageUp/PageDown生效:鼠标拖动旋钮、方向键不受该属性影响;
     * 2. 边界处理:调整后超出范围时,自动修正为边界值;
     * 3. 与wrapping配合:wrapping=true时,PageUp/PageDown调整到边界会自动套圈。
     *
     * ------------------------------------- 属性6:sliderPosition -------------------------------------
     * @property QDial::sliderPosition
     * @brief QDial界面上旋钮的初始/临时显示位置属性
     * @details 该属性仅控制旋钮的视觉位置,默认值为0;
     *          当tracking=true时,sliderPosition与value始终同步,无需单独设置;
     *          当tracking=false时,可通过该属性设置旋钮的临时位置,用户操作旋钮后会同步更新value和sliderPosition。
     *
     * 【关联API详解】
     * 1. int sliderPosition() const → 获取当前旋钮的视觉位置;
     * 2. void setSliderPosition(int pos) → 设置旋钮的视觉位置(仅tracking=false时生效)。
     *
     * @使用示例:
     * // 关闭跟踪,设置旋钮临时位置为70(内部value仍为50)
     * ui->dial->setTracking(false);
     * ui->dial->setValue(50);
     * ui->dial->setSliderPosition(70);
     * qDebug() << "内部数值:" << ui->dial->value(); // 输出50
     * qDebug() << "旋钮位置:" << ui->dial->sliderPosition(); // 输出70
     *
     * 【关键注意事项】
     * 1. 与tracking强关联:tracking=true时,setSliderPosition()无效果,sliderPosition始终等于value;
     * 2. 用户操作同步:无论tracking是否开启,用户拖动旋钮后,value和sliderPosition都会同步更新;
     * 3. 范围限制:sliderPosition也受minimum和maximum限制,超出范围自动修正。
     *
     * ------------------------------------- 属性7:tracking -------------------------------------
     * @property QDial::tracking
     * @brief QDial旋钮外观是否跟踪数值变化的属性
     * @details 该属性控制旋钮视觉位置与内部数值的同步关系,默认值为true(跟踪);
     *          设为true时,setValue()、用户操作旋钮都会立即同步更新旋钮位置;
     *          设为false时,setValue()仅更新内部数值,旋钮位置不变,需通过setSliderPosition()手动设置,用户操作旋钮后会同步更新。
     *
     * 【关联API详解】
     * 1. bool hasTracking() const → 获取是否开启跟踪;
     * 2. void setTracking(bool enable) → 设置是否开启跟踪。
     *
     * @使用示例:
     * // 关闭跟踪(仅用户操作同步,代码设置不同步)
     * ui->dial->setTracking(false);
     *
     * 【关键注意事项】
     * 1. 默认无需修改:true是最常用的配置,旋钮位置与数值实时同步;
     * 2. 仅影响视觉:不影响valueChanged()的触发,setValue()仍会触发信号;
     * 3. 与sliderPosition配合:仅tracking=false时,sliderPosition才有独立设置的意义。
     *
     * ------------------------------------- 属性8:wrapping -------------------------------------
     * @property QDial::wrapping
     * @brief QDial是否允许数值循环调整(套圈)的属性
     * @details 该属性控制数值达到边界时的调整行为,默认值为false(不循环);
     *          设为true时:
     *          - 数值达到maximum后,再按↑/PageUp/拖动旋钮顺时针到底,会跳到minimum;
     *          - 数值达到minimum后,再按↓/PageDown/拖动旋钮逆时针到底,会跳到maximum。
     *
     * 【关联API详解】
     * 1. bool wrapping() const → 获取是否允许循环;
     * 2. void setWrapping(bool wrap) → 设置是否允许循环。
     *
     * @使用示例:
     * // 允许循环,范围0-9(模拟1位数字选择)
     * ui->dial->setRange(0, 9);
     * ui->dial->setWrapping(true);
     * ui->dial->setValue(9);
     * // 按↑键 → 数值变为0;拖动旋钮顺时针到底 → 数值变为0
     * ui->dial->setValue(0);
     * // 按↓键 → 数值变为9;拖动旋钮逆时针到底 → 数值变为9
     *
     * 【关键注意事项】
     * 1. 仅用户操作生效:直接输入/设置数值超出范围时,仍会修正为边界值,不会循环;
     * 2. 范围要求:需设置合理的[minimum, maximum]范围,循环才有实际意义;
     * 3. 视觉适配:循环时旋钮会从最大值位置直接跳到最小值位置(无过渡旋转),需结合场景判断是否合适。
     *
     * ------------------------------------- 属性9:notchesVisible -------------------------------------
     * @property QDial::notchesVisible
     * @brief QDial是否显示刻度线的属性
     * @details 该属性控制旋钮周围是否显示刻度线,默认值为false(不显示);
     *          设为true时,旋钮周围会显示均匀分布的刻度线,刻度密度由notchTarget控制。
     *
     * 【关联API详解】
     * 1. bool notchesVisible() const → 获取是否显示刻度线;
     * 2. void setNotchesVisible(bool visible) → 设置是否显示刻度线。
     *
     * @使用示例:
     * // 显示刻度线
     * ui->dial->setNotchesVisible(true);
     *
     * 【关键注意事项】
     * 1. 仅影响视觉:不影响任何交互功能;
     * 2. 与notchTarget配合:刻度密度由notchTarget控制,需同时设置以达到预期效果;
     * 3. 样式兼容:若通过QSS自定义了刻度线样式,notchesVisible属性会被覆盖。
     *
     * ------------------------------------- 属性10:notchTarget -------------------------------------
     * @property QDial::notchTarget
     * @brief QDial刻度线之间的相对位置(密度)属性
     * @details 该属性定义相邻两条刻度线之间的"目标像素距离",默认值为3.766(约4像素);
     *          数值越大,刻度线越稀疏;数值越小,刻度线越密集;
     *          仅当notchesVisible=true时,该属性才生效。
     *
     * 【关联API详解】
     * 1. qreal notchTarget() const → 获取当前刻度目标距离;
     * 2. void setNotchTarget(qreal target) → 设置刻度目标距离(建议target≥2.0)。
     *
     * @使用示例:
     * // 显示刻度线,并设置为较稀疏的密度(目标距离10像素)
     * ui->dial->setNotchesVisible(true);
     * ui->dial->setNotchTarget(10.0);
     *
     * 【关键注意事项】
     * 1. 仅notchesVisible=true时生效:不显示刻度线时,设置该属性无效果;
     * 2. 相对距离:target是"目标"距离,实际刻度线数量会根据旋钮大小和范围自动调整,以尽可能接近目标距离;
     * 3. 样式兼容:若通过QSS自定义了刻度线数量,notchTarget属性会被覆盖。
     *
     * ===================================== 核心信号详解 =====================================
     *
     * ------------------------------------- 信号1:valueChanged(int value) -------------------------------------
     * @signal void QDial::valueChanged(int value)
     * @brief QDial数值发生变化时触发的信号
     * @details 该信号反馈旋钮数值的变更,触发场景覆盖:
     *          1. 用户拖动旋钮、按方向键/PageUp/PageDown;
     *          2. 代码调用setValue();
     *          3. 超出范围自动修正;
     *          4. 修改minimum/maximum导致当前值自动修正。
     *
     * @参数说明:
     * @param value - int类型,变更后的新数值。
     *
     * @使用示例:
     * // 绑定valueChanged信号,联动进度条
     * ui->dial->setRange(0, 100);
     * ui->progressBar->setRange(0, 100);
     * connect(ui->dial, &QDial::valueChanged, ui->progressBar, &QProgressBar::setValue);
     *
     * 【关键注意事项】
     * 1. 通用触发:所有导致数值变化的操作都会触发;
     * 2. 与tracking无关:无论tracking是否开启,setValue()都会触发该信号;
     * 3. 修正触发:超出范围自动修正、修改范围导致修正,都会触发该信号。
     *
     * ------------------------------------- 信号2:rangeChanged(int min, int max) -------------------------------------
     * @signal void QDial::rangeChanged(int min, int max)
     * @brief QDial数值范围发生变化时触发的信号
     * @details 该信号反馈最小值/最大值的变更,触发场景覆盖:
     *          1. 代码调用setMinimum()/setMaximum()/setRange();
     *          2. 设置min > max导致自动交换。
     *
     * @参数说明:
     * @param min - int类型,变更后的新最小值;
     * @param max - int类型,变更后的新最大值。
     *
     * @使用示例:
     * // 绑定rangeChanged信号,更新标签显示范围
     * connect(ui->dial, &QDial::rangeChanged, this, [=](int newMin, int newMax){
     *     ui->labRange->setText(QString("范围:%1 ~ %2").arg(newMin).arg(newMax));
     * });
     *
     * 【关键注意事项】
     * 1. 仅范围变化触发:数值变化不会触发该信号;
     * 2. 自动交换触发:设置min > max导致自动交换时,也会触发该信号;
     * 3. 与valueChanged联动:修改范围导致当前值自动修正时,会先触发rangeChanged(),再触发valueChanged()。
     */
}

Widget::~Widget()
{
    delete ui;
}

第七章 全文总结与学习建议

恭喜你!终于看完了这篇QDial 控件常见属性详细解析!

如果你跟着文章一步步敲了代码,做了练习,那么现在你已经完全掌握了 QDial 的核心内容:

  • 你知道了 QDial 是旋钮控件,核心逻辑和 QSlider 几乎一样,交互方式是旋转;
  • 你会用 value、minimum、maximum、setRange() 控制数值范围,知道了 value 始终受范围限制,超出会自动修正!;
  • 你会用 singleStep 和 pageStep 分别控制方向键和 PageUp/PageDown 的步长,知道了它们的作用范围不同!;
  • 你会用 notchesVisible 和 notchTarget 控制刻度线的显隐和疏密,知道了 notchTarget 是目标像素距离,不是刻度数量!;
  • 你会用 sliderPosition 和 tracking 控制视觉位置,知道了 tracking 默认是 true 一般不需要修改!;
  • 你会用 wrapping 让旋钮循环调整,知道了它仅对用户操作生效!;
  • 你会绑定 QDial 的核心信号,知道了 valueChanged 是核心信号,拖动时会高频触发,耗时操作需做防抖!;
  • 你能实现 "调整窗口透明度" 这样的综合应用。

毫不夸张地说,掌握了这些内容,你就掌握了 QDial 开发的 95% 以上的需求!

7.1 核心要点回顾

在结束之前,我们最后再回顾一下这篇文章的核心要点,特别是那些最容易踩坑的地方:

  1. value 始终受 minimum 和 maximum 限制!(超级重要!):超出范围自动修正!
  2. singleStep 仅对方向键生效,pageStep 仅对 PageUp/PageDown 生效!(超级重要!):不要搞混!
  3. notchesVisible 必须先设为 true,notchTarget 才生效!(超级重要!);
  4. notchTarget 是目标像素距离,不是刻度数量!(超级重要!说三遍!):数值越大越稀疏,越小越密集!
  5. tracking 默认是 true,一般不需要修改!(超级重要!);
  6. wrapping 仅对用户操作生效!(超级重要!):代码调用 setValue () 不会循环!
  7. valueChanged 是核心信号,拖动时会高频触发!(超级重要!):耗时操作需做防抖!
  8. rangeChanged 仅范围变更时触发!(超级重要!):数值变化不会触发!
  9. sliderPosition 在 tracking=true 时与 value 自动同步!(超级重要!):不用单独管!
  10. 默认值回顾:value=0,singleStep=1,pageStep=10,notchesVisible=false,notchTarget=3.766,tracking=true,wrapping=false。

7.2 最后的话

编程学习没有捷径,唯手熟尔。QDial 的属性、方法和信号虽然看起来不少,但只要你多敲代码、多做练习、多踩坑、多排查,很快就能熟练掌握。

这篇文章虽然很长,但我相信,如果你认真看完了,跟着敲了代码,你一定会对 QDial 有一个极其深刻的理解,再也不会为旋钮调整的实现发愁。

感谢你阅读这篇文章,希望它能帮到你。我们下一篇 Qt 教程再见!

相关推荐
Java后端的Ai之路2 小时前
RAG 还是 Agent 自主检索?Claude Code 删掉向量库之后,我把选型讲透了
开发语言·人工智能·prompt·agent·rag
starzy19903 小时前
深入理解Scala高阶函数:函数式编程核心
开发语言·scala
科技苑8 小时前
C++ CAS与ABA问题详解,无锁编程的经典陷阱怎么解决?
c++·人工智能
czq_26867194879 小时前
Python打卡第46天
开发语言·python
宛 禾11 小时前
Qt 事件系统完全指南:从事件循环到实战避坑
qt
Evand J11 小时前
【MATLAB例程介绍】(17)永磁同步电机(PMSM)基于自适应无迹卡尔曼滤波(AUKF)的电流噪声在线估计。测量噪声协方差自适应与转速估计对比仿真
开发语言·matlab·卡尔曼滤波·无迹卡尔曼滤波·pmsm·电机滤波
BaiPeter12 小时前
ZPL文件预览
开发语言·c++
悠仁さん15 小时前
【C++】哈希表
c++·算法·散列表
Severus_black17 小时前
【C++初阶】list使用及相关实现,思路顺滑流畅!
c语言·数据结构·c++·stl·list