- 桌面应用
- 跨平台
【免费下载链接】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
QTableView 是 NodeGui(基于 Node.js + Qt 的跨平台原生桌面应用框架)提供的表格视图控件,是 Qt 官方 QTableView 类的 JavaScript 封装,负责以"模型/视图(Model/View)"架构呈现二维表格数据。本指南聚焦于如何在实际 NodeGui 应用中创建 QTableView、绑定数据模型、定制行列与网格外观、控制选择与排序行为,并结合仓库源码解释其底层实现机制。
认识 QTableView:NodeGui 中的模型/视图表格
在 Qt 的模型/视图体系中,QTableView提供了一个"默认的模型/视图实现的表格视图"(官方文档原语:The QTableView class provides a default model/view implementation of a table view)。在 NodeGui 中,它被包装成 TypeScript 类 QTableView,作为 Qt QTableView 类的 JS 封装对外提供。
它的核心定位是:只负责"显示"与"交互",不负责"存储数据"。表格内容由独立的模型(Model)提供,视图(View)通过模型索引(QModelIndex)读取数据并渲染。这种解耦使得同一个数据模型可以被多个视图复用,也便于将数据与界面逻辑彻底分离。
继承层次
从文档的 Hierarchy 一节可以看到 QTableView 的完整继承链:
QAbstractItemView < QAbstractScrollArea < QFrame < QWidget(向上追溯) ↳ QTableView ↳ QTableWidgetQAbstractItemView:所有基于模型/视图架构的条目视图的抽象基类(QListView、QTreeView、QTableView 等均继承自它);QAbstractScrollArea:提供滚动条、视口(viewport)等滚动区域能力;QFrame:提供边框、矩形、阴影等外观能力。
在仓库 C++ 侧,这一层次同样成立:NTableView同时继承QTableView与NodeWidget(见 ntableview.hpp),并通过宏QABSTRACTITEMVIEW_WRAPPED_METHODS_DECLARATION复用父类的全部导出方法(见 qtableview_macro.h)。
类型参数与信号
QTableView 是一个泛型类,类型参数Signals默认取QTableViewSignals,而QTableViewSignals = QAbstractItemViewSignals(见 QTableView.ts)。C++ 侧通过QTABLEVIEW_SIGNALS宏同样继承自QABSTRACTITEMVIEW_SIGNALS,意味着 QTableView 自身不新增信号,而是完整复用父类信号集(如点击、选择变化、双击等条目视图信号)。
快速上手:最小可用示例
文档给出的最小示例如下,两行代码即可创建表格视图:
const { QTableView } = require("@nodegui/nodegui"); const tableview = new QTableView();构造函数:三种用法
从文档的 Constructors 一节以及 qtableview_wrap.cpp 的实现可以看出,构造函数接受一个可选参数:
| 调用方式 | 行为 |
|---|---|
new QTableView() | 创建一个无父级的表格视图 |
new QTableView(parentWidget) | 创建表格视图并指定父级 QWidget |
new QTableView(nativeElement) | 包装一个已存在的原生 C++ 实例(内部使用) |
C++ 构造逻辑对 0 个参数直接new NTableView();对 1 个参数则判断是外部原生实例(IsExternal)还是父级控件包装对象,分别执行包装或带父级构造。若参数个数非法,会抛出TypeError: NodeGui: QTableViewWrap: Wrong number of arguments to constructor。
与数据模型配合的完整示例
单独创建 QTableView 并不会显示任何内容——必须先通过setModel()绑定数据模型。仓库自带的官方示例 modelview_1_readonly.ts 演示了最典型的只读用法:
import { ItemDataRole, QAbstractTableModel, QModelIndex, QTableView, QVariant } from '..'; function main(): void { const tableView = new QTableView(); const model = new MyModel(); tableView.setModel(model); tableView.show(); (global as any).win = tableView; } class MyModel extends QAbstractTableModel { rowCount(parent = new QModelIndex()): number { return 2; } columnCount(parent = new QModelIndex()): number { return 2; } data(index: QModelIndex, role = ItemDataRole.DisplayRole): QVariant { if (role === ItemDataRole.DisplayRole) { return new QVariant(`Row${index.row() + 1}, Column${index.column() + 1}`); } return new QVariant(); } } main();要点说明:
- 自定义模型需继承
QAbstractTableModel并至少实现rowCount()、columnCount()与data(); data()根据ItemDataRole.DisplayRole返回QVariant作为单元格显示文本;tableView.show()之后视图才会出现在窗口中;- 将视图挂到
(global as any).win是为了防止被 Node.js 垃圾回收,这与 NodeGui 的 WrapperCache 机制相关(参见 WrapperCache.ts)。
核心 API 详解:QTableView 自身方法
文档中 QTableView 的方法数量庞大,但绝大多数是沿继承链继承而来的通用控件方法(如show()、resize()、setStyleSheet()、addEventListener()等)。真正属于 QTableView 自身的 API 集中在表格专属能力上,在 QTableView.ts 中可以看到完整实现。下面按功能分组讲解。
行列尺寸与坐标换算
| 方法 | 说明 |
|---|---|
rowAt(y: number): number | 返回给定 y 坐标所在的行号 |
columnAt(x: number): number | 返回给定 x 坐标所在的列号 |
rowViewportPosition(row): number | 返回某行在视口内的 y 坐标 |
columnViewportPosition(column): number | 返回某列在视口内的 x 坐标 |
rowHeight(row): number | 返回指定行的高度 |
columnWidth(column): number | 返回指定列的宽度 |
setRowHeight(row, height) | 设置指定行的高度 |
setColumnWidth(column, width) | 设置指定列的宽度 |
resizeRowToContents(row)/resizeRowsToContents() | 按内容自适应行高 |
resizeColumnToContents(column)/resizeColumnsToContents() | 按内容自适应列宽 |
这些"坐标换算"类方法在实现上直接透传原生 Qt 调用。例如 C++ 宏中columnAt的实现是this->instance->columnAt(x),参数以Int32Value()取出、结果以Napi::Number::New返回(见 qtableview_macro.h)。resizeColumnsToContents()/resizeRowsToContents()分别一次调整所有列/行,适合在数据加载完成后调用以获得紧凑布局。
行列的显示与隐藏
| 方法 | 说明 |
|---|---|
hideColumn(column)/showColumn(column) | 隐藏 / 显示指定列 |
hideRow(row)/showRow(row) | 隐藏 / 显示指定行 |
setColumnHidden(column, hide) | 以布尔值控制列是否隐藏 |
setRowHidden(row, hide) | 以布尔值控制行是否隐藏 |
isColumnHidden(column): boolean | 查询列隐藏状态 |
isRowHidden(row): boolean | 查询行隐藏状态 |
这几组方法互为对称:hideColumn/showColumn是便捷插槽(Public Slots),setColumnHidden(column, hide)则把"是否隐藏"作为显式参数,便于与状态变量联动。
单元格合并(Span)
| 方法 | 说明 |
|---|---|
setSpan(row, column, rowSpanCount, columnSpanCount) | 将 (row, column) 起始的单元格跨越 rowSpanCount 行、columnSpanCount 列 |
rowSpan(row, column): number | 查询指定单元格跨越的行数 |
columnSpan(row, column): number | 查询指定单元格跨越的列数 |
clearSpans() | 清除所有合并,恢复默认布局 |
// 将第 0 行第 0 列的单元格横向合并 2 列,作为表头说明单元格 tableView.setSpan(0, 0, 1, 2);表头(Header)访问
| 方法 | 说明 |
|---|---|
horizontalHeader(): QHeaderView | 获取水平表头(列标题栏) |
verticalHeader(): QHeaderView | 获取垂直表头(行号栏) |
返回类型均为 QHeaderView。在 C++ 侧,horizontalHeader()/verticalHeader()通过WrapperCache::instance.getWrapper(env, header)将原生QHeaderView指针包装为 JS 对象返回(见 qtableview_macro.h),这保证了同一原生对象多次访问得到同一个 JS 包装实例。获取表头后可以进一步定制标题文本、拉伸模式等。
选择与排序
| 方法 | 说明 |
|---|---|
selectRow(row)/selectColumn(column) | 选中整行 / 整列 |
sortByColumn(column, order) | 按指定列排序,order取 SortOrder(AscendingOrder/DescendingOrder) |
setSortingEnabled(enable)/isSortingEnabled() | 开关/查询点击表头排序功能 |
setCornerButtonEnabled(enable)/isCornerButtonEnabled() | 开关/查询左上角全选按钮 |
选择行为的高级配置(setSelectionMode、setSelectionBehavior、clearSelection、selectAll等)继承自QAbstractItemView,在 QAbstractItemView.ts 中有完整实现,可配合QItemSelectionModel使用。
网格与外观
| 方法 | 说明 |
|---|---|
setShowGrid(show)/showGrid(): boolean | 开关/查询网格线显示 |
setGridStyle(style)/gridStyle(): PenStyle | 设置/查询网格线画笔样式,取值来自 PenStyle 枚举(如SolidLine、DashLine、NoPen等) |
setWordWrap(on)/wordWrap(): boolean | 开关/查询单元格文本自动换行 |
值得注意的实现细节:这部分"属性型"方法并未全部走原生调用,而是利用了 Qt 的属性(Property)系统。例如:
setShowGrid(show)→this.setProperty('showGrid', show);setGridStyle(style)→this.setProperty('gridStyle', style);setSortingEnabled(enable)→this.setProperty('sortingEnabled', enable);setCornerButtonEnabled(enable)→this.setProperty('cornerButtonEnabled', enable);setWordWrap(on)→this.setProperty('wordWrap', on)。
对应的读取方法(如showGrid()、gridStyle()、isSortingEnabled())则通过this.property(...)读取并转换成boolean/int(见 QTableView.ts)。这与rowAt、setSpan等直接透传原生方法的实现方式形成了两种不同模式,是理解 NodeGui 包装风格的切入点。
继承自父类的重要能力
文档 Index 中列出的绝大多数方法来自父类链,这里整理与表格使用最相关的一组:
来自 QAbstractItemView(模型/视图核心)
setModel(model: QAbstractItemModel)/setRootIndex(index):绑定数据模型、设置根索引;currentIndex(): QModelIndex、setCurrentIndex(index):当前项;selectionModel(): QItemSelectionModel:选择模型;setSelectionMode(mode)/setSelectionBehavior(behavior):选择模式与行为(SelectionMode、QAbstractItemViewSelectionBehavior枚举);setAlternatingRowColors(enable)/alternatingRowColors():交替行背景色;setDragEnabled(enable)、setDragDropMode(mode)、setDropIndicatorShown(enable):拖放能力;setEditTriggers(triggers)/editTriggers():编辑触发方式;setItemDelegate(delegate)/setItemDelegateForColumn(column, delegate)/setItemDelegateForRow(row, delegate):统一/按列/按行定制绘制与编辑代理;scrollTo(index, hint)(默认ScrollHint.EnsureVisible)、scrollToTop()、scrollToBottom():滚动定位;setIndexWidget(index, widget)/indexWidget(index):为特定索引挂载普通 Widget;openPersistentEditor(index)/closePersistentEditor(index)/isPersistentEditorOpen(index):持久化编辑器;keyboardSearch(search):键盘增量搜索;setTabKeyNavigation(enable)/tabKeyNavigation():Tab 键导航。
来自 QAbstractScrollArea(滚动区域)
setHorizontalScrollBarPolicy(policy)/setVerticalScrollBarPolicy(policy)(ScrollBarPolicy);setViewport(widget)/viewport()、horizontalScrollBar()/verticalScrollBar();maximumViewportSize()。
来自 QWidget / QFrame(通用控件能力)
- 窗口生命周期:
show()、hide()、close()、setWindowTitle()、setWindowIcon()等; - 布局尺寸:
resize()、setFixedSize()、setMinimumSize()、setMaximumSize()、setSizePolicy()等; - 样式:
setStyleSheet(styleSheet, postprocess = true)、setInlineStyle(style, postprocess = true); - 边框(来自 QFrame):
setFrameShape(type)、setFrameShadow(type)、setFrameStyle(style)、setLineWidth()、setMidLineWidth()等; - 焦点与输入:
setFocusPolicy()、setFocus()、setMouseTracking()、setInputMethodHints()等。
此外,所有 NodeGui 控件都通过 EventWidget 支持事件监听:
// 信号(Signal)监听,例如继承自 QAbstractItemView 的信号 tableView.addEventListener('clicked', (index: QModelIndex) => { console.log(`clicked row=${index.row()} col=${index.column()}`); }); // 事件(Event)监听,例如悬停事件 tableView.addEventListener(WidgetEventTypes.HoverEnter, () => { console.log('hover enter'); });addEventListener/removeEventListener支持两种重载:以Signals接口中的信号名为键,或以WidgetEventTypes事件类型为键(详见 EventWidget.ts 与文档 EventWidget 条目)。
内存与生命周期注意点
文档中_id()方法(继承自 QObject)返回一个标识底层 C++ 对象的唯一数字——它是 C++ 对象内存地址的哈希,可用于配合setLogCreateQObject()/setLogDestroyQObject()调试内存问题。
QTableView 在 C++ 侧使用QPointer<QTableView>持有实例(见 qtableview_wrap.h),析构时通过extrautils::safeDelete(this->instance)安全删除(见 qtableview_wrap.cpp)。在 JS 侧,创建控件后若不再被引用会被垃圾回收,因此长期存活的窗口/视图建议挂载在全局对象上(如示例中的(global as any).win = tableView)。NodeGui 的 WrapperCache 会在创建包装时注册(wrapperCache.registerWrapper('QTableViewWrap', QTableView),见 QTableView.ts),用于维护原生对象与 JS 包装对象的对应关系。
在 NodeGui 应用中使用 QTableView 的完整流程
综合以上内容,一个可运行的实战骨架如下:
import { QTableView, QAbstractTableModel, QModelIndex, QVariant, ItemDataRole } from '@nodegui/nodegui'; class MyModel extends QAbstractTableModel { private dataRows = [ ['Alice', 'Engineer'], ['Bob', 'Designer'], ['Carol', 'Manager'], ]; rowCount(parent: QModelIndex = new QModelIndex()): number { return this.dataRows.length; } columnCount(parent: QModelIndex = new QModelIndex()): number { return 2; } data(index: QModelIndex, role: number = ItemDataRole.DisplayRole): QVariant { if (role === ItemDataRole.DisplayRole) { return new QVariant(this.dataRows[index.row()][index.column()]); } return new QVariant(); } } const table = new QTableView(); const model = new MyModel(); table.setModel(model); table.setColumnWidth(0, 120); table.resizeColumnsToContents(); table.setShowGrid(true); table.setSortingEnabled(true); table.setAlternatingRowColors(true); table.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows); table.show(); (global as any).win = table;应用时需注意:
- 若使用自定义排序,模型需要重写
sort()方法,否则开启setSortingEnabled(true)后按表头点击只会走默认排序逻辑; resizeColumnsToContents()在模型数据就绪后调用效果最佳;- 具体引入路径以项目实际安装为准(
@nodegui/nodegui或仓库内src/lib的相对导入),本仓库还提供了多份可直接对照的 modelview 系列示例:modelview_2_formatting.ts、modelview_3_changingmodel.ts、modelview_4_headers.ts、modelview_5_edit.ts、modelview_buddy.ts。
小结
QTableView 是 NodeGui 中实现"表格"场景的核心控件:它继承自QAbstractItemView,通过setModel()与自定义的QAbstractTableModel协作,把数据展示、行列操作、网格样式、排序选择、拖放编辑等能力完整地带到 JavaScript 世界。无论是做数据管理工具、报表界面还是配置面板,掌握 QTableView + 自定义模型这一组合,即可在 NodeGui 中原生渲染高性能表格。更多模型/视图细节可继续阅读仓库中的 QAbstractItemModel.ts、QAbstractTableModel.ts 与 QModelIndex.ts。
- 桌面应用
- 跨平台
【免费下载链接】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
相关推荐
NodeGui QListWidgetItem 完全指南:用 JavaScript/TypeScript 创建原生桌面列表项
NodeGui QListWidgetItem 完全指南:用 JavaScript/TypeScript 创建原生桌面列表项 导读 QListWidgetIte
桌面应用跨平台NodeGui 桌面应用开发:使用 QTableWidget 构建基于单元格的表格视图
NodeGui 桌面应用开发:使用 QTableWidget 构建基于单元格的表格视图 本篇指南以 NodeGui 官方 API 文档为骨架,深入讲解 QTab
桌面应用跨平台NodeGui QTableWidgetItem 完全指南:用 JavaScript 构建表格单元格项
NodeGui QTableWidgetItem 完全指南:用 JavaScript 构建表格单元格项 本指南基于 NodeGui 官方 API 文档与仓库源码
桌面应用跨平台
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考