☰
NodeGui 中的 QSpinBox 整数微调框:从属性配置到信号处理的完整实战指南
2026/9/25 13:09:28 网站建设 项目流程
  • 桌面应用
  • 跨平台

【免费下载链接】nodegui

A library for building cross-platform native desktop applications with Node.js and CSS 🚀. React NodeGui : https://react.nodegui.org and Vue NodeGui: https://vue.nodegui.org

项目地址:https://gitcode.com/gh_mirrors/no/nodegui
点击查看免费下载

QSpinBox是 NodeGui 中对 QtQSpinBox原生组件的 JavaScript/TypeScript 封装,用于在跨平台桌面应用中创建、控制整数微调框(spin box)。本文以 QSpinBox API 文档 为主体,结合仓库中的 TypeScript 包装层与 C++ 原生实现,系统讲解其构造方式、数值范围配置、前后缀与进制显示、继承自QAbstractSpinBox的全部能力、valueChanged信号处理以及内存生命周期管理,读完即可在 NodeGui 应用中熟练落地带上下调节按钮的整数输入控件。

类概览与继承层级

QSpinBox用于提供一个"整数输入框 + 上下调节按钮"的原生控件:用户既可以直接在文本框中键入整数,也可以通过按钮步进增减。它默认支持int类型(int32 位)的取值范围,适合数量、序号、比例等整数场景。

从 API 文档的 Hierarchy 一节可以看到其继承链:

↳ QAbstractSpinBox‹QSpinBoxSignals› ↳ QSpinBox

即QSpinBox继承自抽象基类QAbstractSpinBox(再往上依次是QWidget、NodeWidget、EventWidget、Component、QObject)。由于QAbstractSpinBox是抽象类,实际可实例化的是QSpinBox(同族还有QDateTimeEdit),因此在 NodeGui 中所有微调框相关的通用行为都被收拢在抽象层,而QSpinBox提供整数特化的能力。

创建控件的最小示例(出自 API 文档与 QSpinBox.ts 源码注释):

const { QSpinBox } = require("@nodegui/nodegui"); const spinBox = new QSpinBox();

从 TypeScript 源码 可以看到构造函数的重载逻辑:

constructor(arg?: QWidget<QWidgetSignals> | NativeElement) { let native: NativeElement; if (checkIfNativeElement(arg)) { native = arg as NativeElement; } else if (arg != null) { const parent = arg as QWidget; native = new addon.QSpinBox(parent.native); } else { native = new addon.QSpinBox(); } super(native); }
  • 无参构造:new QSpinBox(),创建一个独立的原生微调框;
  • 传入父控件:new QSpinBox(parentWidget),将控件挂到指定父QWidget下,由父控件负责布局与生命周期;
  • 传入 NativeElement:用于包装已存在的原生实例(多用于跨层传递)。

对应的 C++ 构造逻辑在 qspinbox_wrap.cpp:无参时new NSpinBox(),带参且参数为外部对象时包装既有实例,否则从NodeWidgetWrap解包父控件后new NSpinBox(parentWidget->getInternalInstance()),最后通过configureQWidget完成原生控件与 Flex 布局节点的对接。

核心数值属性:value、minimum、maximum 与 setRange

QSpinBox最核心的数据模型是"整数 + 取值范围 + 步进"。API 文档与本类直接定义的属性方法包括setValue/value、setMinimum/minimum、setMaximum/maximum、setRange、setSingleStep/singleStep、setStepType/stepType,以及显示相关的setPrefix/prefix、setSuffix/suffix、setDisplayIntegerBase/displayIntegerBase、cleanText。

当前值:setValue / value

spinBox.setValue(42); const current = spinBox.value(); // 42

源码实现 中setValue/value都通过 Qt 属性系统读写:

setValue(val: number): void { this.setProperty('value', val); } value(): number { return this.property('value').toInt(); }

注意:setValue传入的值会自动被钳制(clamp)到[minimum, maximum]区间内,因此传入超出范围的值不会抛出异常,而是收敛到边界值。

取值范围:setMinimum / setMaximum / setRange

spinBox.setMinimum(0); spinBox.setMaximum(100); // 等价于一次性设置 spinBox.setRange(0, 100);

setRange(minimum, maximum)是唯一一个在 C++ 侧原生实现的调用(而非通过属性系统),qspinbox_wrap.cpp 中将其映射到QSpinBox::setRange:

Napi::Value QSpinBoxWrap::setRange(const Napi::CallbackInfo& info) { Napi::Number minimum = info[0].As<Napi::Number>(); Napi::Number maximum = info[1].As<Napi::Number>(); this->instance->setRange(minimum.Int32Value(), maximum.Int32Value()); return env.Null(); }

从 C++ 头文件声明 可见setRange是本类自定义暴露给 JS 的唯一方法,其余方法全部复用QABSTRACTSPINBOX_WRAPPED_METHODS_DECLARATION宏生成的通用接口。Qt 语义上,setRange会保证minimum <= maximum:若传入的最小值大于最大值,会先交换两者,再调整当前值。

步进:setSingleStep / setStepType

spinBox.setSingleStep(5); // 点击一次按钮增减 5 const step = spinBox.singleStep(); // 5

singleStep默认值为 1,决定每次点击上下按钮(或按上下方向键)时数值的增减幅度。

stepType控制步进行为模式,取值来自QAbstractSpinBox.ts中定义的枚举(见 src/lib/QtWidgets/QAbstractSpinBox.ts):

枚举值含义
StepType.DefaultStepType默认模式,每次固定步进singleStep的数值
StepType.AdaptiveDecimalStepType自适应十进制步进,根据当前数值大小动态调整步进幅度(数值越大单次步进越大)
spinBox.setStepType(StepType.AdaptiveDecimalStepType);

显示格式:prefix、suffix、displayIntegerBase 与 cleanText

QSpinBox允许在不影响内部数值的前提下定制显示文本:

spinBox.setPrefix("$ "); // 数值前显示货币符号 spinBox.setSuffix(" px"); // 数值后显示单位 spinBox.setDisplayIntegerBase(16); // 以十六进制显示
  • setPrefix/prefix:在数值前拼接前缀文本,例如"$ "、"#";
  • setSuffix/suffix:在数值后拼接后缀文本,例如"px"、"%";
  • setDisplayIntegerBase/displayIntegerBase:设置整数显示的进制基数,默认为 10(十进制)。设为 16 时数值以十六进制显示,设为 2 即以二进制显示,适合做进制转换类工具。注意:显示进制不影响value()返回的十进制数值,也不影响步进计算。

这三个方法在 QSpinBox.ts 中同样走 Qt 属性系统(displayIntegerBase读取时toInt()转换)。

spinBox.setPrefix("#"); spinBox.setDisplayIntegerBase(16); spinBox.setValue(255); spinBox.text(); // "#ff" spinBox.cleanText(); // "ff" —— 不含前缀、后缀及空格
  • text():继承自QAbstractSpinBox,返回带前缀/后缀的完整显示文本;
  • cleanText():QSpinBox自有方法,返回去除前缀、后缀和空格后的"纯数值文本",适合在提交时做字符串解析。源码实现:
cleanText(): string { return this.property('cleanText').toString(); }

继承自 QAbstractSpinBox 的通用配置

QSpinBox从抽象基类继承了整套通用能力,对应 API 文档中标有Inherited from QAbstractSpinBox的方法。它们定义在 QAbstractSpinBox.ts,全部通过 Qt 属性系统读写:

外观与对齐

spinBox.setAlignment(AlignmentFlag.AlignCenter); // 文本对齐方式 spinBox.setButtonSymbols(ButtonSymbols.UpDownArrows); // 按钮符号样式 spinBox.setFrame(true); // 是否绘制边框 spinBox.setGroupSeparatorShown(true); // 千位分隔符(1,000)

ButtonSymbols枚举(src/lib/QtWidgets/QAbstractSpinBox.ts#L92-L96)提供三种按钮形态:

枚举值按钮样式
ButtonSymbols.UpDownArrows默认的上下箭头
ButtonSymbols.PlusMinus加减号按钮
ButtonSymbols.NoButtons不显示按钮(纯输入框)

输入与校验

spinBox.setReadOnly(true); // 只读,禁止编辑 spinBox.setKeyboardTracking(false); // 关闭按键跟踪 spinBox.setAccelerated(true); // 按住按钮持续加速步进 spinBox.setCorrectionMode(CorrectionMode.CorrectToNearestValue); const ok = spinBox.hasAcceptableInput(); // 当前文本是否合法可接受
  • setKeyboardTracking(false):默认情况下用户每敲一个字符valueChanged都会触发;关闭后仅在回车或失焦(editingFinished)时才提交,适合大数据量重绘场景;
  • CorrectionMode枚举(src/lib/QtWidgets/QAbstractSpinBox.ts#L98-L101):CorrectToPreviousValue(输入非法时回退到上一个合法值)与CorrectToNearestValue(钳制到最接近的合法值)。

边界行为与快捷操作

spinBox.setWrapping(true); // 超过最大值后循环回最小值 spinBox.setSpecialValueText("Empty"); // 数值为最小值时显示替代文本 spinBox.selectAll(); // 选中全部文本 spinBox.stepUp(); // 程序化步进 +1 spinBox.stepDown(); // 程序化步进 -1 const t = spinBox.text(); // 当前显示文本

setSpecialValueText常用于"最小值即特殊状态"的场景:例如最小值 0 且文案为 "0" 时可显示成 "Free" 或 "Off",此时value()返回的仍是数值 0,text()返回特殊文本。

信号处理:valueChanged 与 editingFinished

QSpinBox支持两类事件监听,对应 API 文档 addEventListener 小节 的两种签名:

1. 信号监听(Signal)

QSpinBoxSignals接口定义了本类自有信号(QSpinBox.ts):

export interface QSpinBoxSignals extends QAbstractSpinBoxSignals { valueChanged: (value: number) => void; }

继承的QAbstractSpinBoxSignals还包含editingFinished: () => void。

spinBox.addEventListener('valueChanged', (value) => { console.log('当前值:', value); }); spinBox.addEventListener('editingFinished', () => { console.log('编辑结束,提交值:', spinBox.value()); });

信号在 C++ 原生侧通过事件发射器桥接到 Node.js。在 nspinbox.hpp 中可以看到valueChanged的底层连接逻辑——使用QOverload<int>明确匹配QSpinBox::valueChanged(int)重载(因为 Qt 中还存在valueChanged(const QString&)重载),将数值作为参数回传给 JS 回调:

QObject::connect( this, QOverload<int>::of(&QSpinBox::valueChanged), = { Napi::Env env = this->emitOnNode.Env(); Napi::HandleScope scope(env); this->emitOnNode.Call({Napi::String::New(env, "valueChanged"), Napi::Value::From(env, val)}); });

2. 事件监听(WidgetEventTypes)

const { WidgetEventTypes } = require("@nodegui/nodegui"); spinBox.addEventListener(WidgetEventTypes.KeyPress, (event) => { console.log("spinBox 内按键事件"); });

removeEventListener(signalType, callback)与addEventListener对称,用于移除已注册的回调。若在事件回调中需要阻止事件继续传播,可调用setEventProcessed(true),配合eventProcessed()查询状态——当置位后 NodeGui 的QObject::event()会直接返回 true 而不再调用父类event()。

样式、Flex 布局与组合示例

QSpinBox属于QWidget体系,天然支持 NodeGui 的 Flexbox 布局与样式系统:

const { QSpinBox, QWidget, FlexLayout } = require("@nodegui/nodegui"); const container = new QWidget(); container.setObjectName("root"); container.setLayout(new FlexLayout()); const spinBox = new QSpinBox(); spinBox.setRange(0, 100); spinBox.setValue(50); spinBox.setSingleStep(5); spinBox.setPrefix("$ "); spinBox.setSuffix(" 元"); spinBox.setStyleSheet("background-color: #f0f0f0; color: #333;"); container.layout.addWidget(spinBox);
  • setStyleSheet(styleSheet, postprocess=true):应用 QSS 样式(与setInlineStyle类似,第二个参数控制是否后处理),可从 QWidget.ts 的对应实现继承而来;
  • setFixedWidth(w)/setFixedHeight(h)/setMinimumSize(w,h)/setMaximumSize(w,h)等尺寸约束方法与所有QWidget一致;
  • 若希望尺寸由 Flex 布局完全接管,可用setFlexNodeSizeControlled(true)(API 文档说明:将控件尺寸控制权交给外部,例如窗口被拖拽时由窗口框架控制其大小)。

生命周期与内存管理

QSpinBox的实例管理继承自QObject/Component体系:

  • native属性(继承自Component):持有底层原生句柄NativeElement | null;
  • _id():返回标识底层 C++ 对象的哈希数字,配合setLogCreateQObject()/setLogDestroyQObject()可排查 C++ 对象泄漏;
  • delete()/deleteLater():销毁底层对象。C++ 侧 qspinbox_wrap.cpp 的析构函数调用extrautils::safeDelete(this->instance),保证 JS 侧释放时原生QSpinBox同步回收。

NodeGui 通过wrapperCache.registerWrapper('QSpinBoxWrap', QSpinBox)(见 QSpinBox.ts)注册包装类,使同一原生对象多次回传 JS 时复用同一包装实例,避免重复包装。setLogCreateQObject()与setLogDestroyQObject()是QObject提供的日志开关,可观察包装对象的创建/销毁时序。

一个包含信号监听的完整可运行示例:

const { QSpinBox, QMainWindow, QLabel, FlexLayout } = require("@nodegui/nodegui"); const win = new QMainWindow(); const root = new QWidget(); root.setLayout(new FlexLayout()); win.setCentralWidget(root); const label = new QLabel(); const spinBox = new QSpinBox(); spinBox.setRange(0, 255); spinBox.setSingleStep(10); spinBox.setPrefix("亮度: "); spinBox.setValue(128); label.setText(`当前亮度: ${spinBox.value()}`); spinBox.addEventListener("valueChanged", (value) => { label.setText(`当前亮度: ${value}`); }); root.layout.addWidget(spinBox); root.layout.addWidget(label); win.show();

小结:QSpinBox 使用速查

  • 构造:new QSpinBox()或new QSpinBox(parent);
  • 数值域:setRange(min, max)、setMinimum、setMaximum、setValue、value;
  • 步进:setSingleStep、setStepType(AdaptiveDecimalStepType);
  • 显示:setPrefix、setSuffix、setDisplayIntegerBase、cleanText、setSpecialValueText;
  • 通用配置:setButtonSymbols、setWrapping、setReadOnly、setKeyboardTracking、setCorrectionMode、setGroupSeparatorShown;
  • 信号:valueChanged、editingFinished、WidgetEventTypes.*;
  • 样式与布局:setStyleSheet/setInlineStyle、FlexLayout、尺寸约束方法;
  • 内存:delete()/deleteLater(),必要时用_id()结合创建/销毁日志排查泄漏。

如需进一步查阅完整方法签名(包括全部继承自QWidget/QMenu的窗口与几何方法),可继续阅读 QSpinBox API 文档、抽象基类 QAbstractSpinBox 文档,以及相关源码 src/lib/QtWidgets/QSpinBox.ts、src/lib/QtWidgets/QAbstractSpinBox.ts 与原生层 qspinbox_wrap.cpp、nspinbox.hpp。

  • 桌面应用
  • 跨平台

【免费下载链接】nodegui

A library for building cross-platform native desktop applications with Node.js and CSS 🚀. React NodeGui : https://react.nodegui.org and Vue NodeGui: https://vue.nodegui.org

项目地址:https://gitcode.com/gh_mirrors/no/nodegui
点击查看免费下载

相关推荐

上一篇:FakeLocation终极指南:三步实现应用级位置模拟,完美保护隐私与突破地理限制
下一篇:3种创新用法:重新定义你的数字足迹管理

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询