- 桌面应用
- 跨平台
【免费下载链接】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
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(); // 5singleStep默认值为 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
相关推荐
终极ArduinoFFT信号处理实战:从入门到精通的完整指南
终极ArduinoFFT信号处理实战:从入门到精通的完整指南 ArduinoFFT是一款专为Arduino平台设计的快速傅里叶变换(FFT)库,能够帮助开发者轻
音频处理嵌入式HackRF信号解调实战:从IQ数据到音频信号的完整流程指南
HackRF信号解调实战:从IQ数据到音频信号的完整流程指南 想要掌握软件定义无线电的核心技能吗?HackRF One作为低成本软件无线电平台的代表,能够让你从
嵌入式硬件开发固件通信SerenityOS posix_spawnattr 完整指南:配置子进程属性与信号默认处理
SerenityOS posix_spawnattr 完整指南:配置子进程属性与信号默认处理 导读 posix_spawnattr_t 是 POSIX posi
操作系统内核驱动
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考