news 2026/9/6 11:19:57

Qt QStringListModel与QListView实战:高效列表数据管理与显示

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Qt QStringListModel与QListView实战:高效列表数据管理与显示

在 Qt C++ 开发中,列表数据的展示与交互是常见需求。QStringListModel 与 QListView 的组合提供了一套轻量高效的解决方案,特别适合处理字符串列表的显示与编辑。本文将完整拆解这对组件的使用方法,从基础概念到实战应用,帮助你在项目中快速实现列表功能。

1. 核心组件概述

1.1 QStringListModel 的作用

QStringListModel 是 Qt 框架中专门用于管理字符串列表数据的模型类。它继承自 QAbstractItemModel,为 QStringList 数据提供了标准的模型接口。主要特点包括:

  • 数据存储:内部维护一个 QStringList,用于存储字符串数据
  • 模型接口:实现了标准的模型方法,如 data()、setData()、rowCount() 等
  • 编辑支持:默认支持数据的编辑操作
  • 信号机制:当数据发生变化时,会发出相应的信号通知视图更新

1.2 QListView 的功能特性

QListView 是用于显示列表数据的视图组件,主要特性包括:

  • 多种显示模式:支持 IconMode、ListMode 等不同的显示方式
  • 选择行为:提供单选、多选、扩展选择等多种选择模式
  • 编辑触发:支持双击编辑、手动触发编辑等交互方式
  • 自定义装饰:可以设置项图标、背景色等视觉元素

1.3 MVC 架构中的协作关系

在 Qt 的 Model-View 架构中,QStringListModel 充当模型(Model)角色,负责数据的管理;QListView 充当视图(View)角色,负责数据的显示。这种分离的设计使得数据与显示逻辑解耦,提高了代码的可维护性。

2. 环境准备与版本说明

2.1 开发环境要求

  • 操作系统:Windows 10/11、macOS 10.14+ 或 Linux Ubuntu 18.04+
  • Qt 版本:Qt 5.15 或 Qt 6.2 及以上版本
  • 编译器:MSVC 2019、GCC 9.0 或 Clang 10.0
  • 开发工具:Qt Creator 或 Visual Studio with Qt插件

2.2 项目配置要点

在 .pro 文件中确保包含必要的模块:

QT += core gui widgets

对于 Qt6 项目,需要注意模块名称的变化:

QT += core gui widgets

2.3 头文件包含

在源代码文件中包含必要的头文件:

#include <QApplication> #include <QListView> #include <QStringListModel> #include <QVBoxLayout> #include <QWidget>

3. 基础用法详解

3.1 创建基本列表视图

下面是一个最简单的 QStringListModel 与 QListView 使用示例:

// 创建应用程序实例 QApplication app(argc, argv); // 创建主窗口 QWidget window; QVBoxLayout *layout = new QVBoxLayout(&window); // 创建数据模型 QStringListModel *model = new QStringListModel(); QStringList dataList; dataList << "苹果" << "香蕉" << "橙子" << "葡萄"; model->setStringList(dataList); // 创建列表视图 QListView *listView = new QListView(); listView->setModel(model); // 设置视图属性 listView->setEditTriggers(QAbstractItemView::DoubleClicked); listView->setSelectionMode(QAbstractItemView::SingleSelection); layout->addWidget(listView); window.show(); return app.exec();

3.2 模型数据操作详解

3.2.1 数据初始化与设置
// 方式1:直接设置字符串列表 QStringList initialData; initialData << "项目1" << "项目2" << "项目3"; model->setStringList(initialData); // 方式2:逐项添加数据 QStringListModel *model = new QStringListModel(); for(int i = 1; i <= 5; ++i) { model->insertRow(model->rowCount()); QModelIndex index = model->index(model->rowCount() - 1); model->setData(index, QString("动态添加项 %1").arg(i)); }
3.2.2 数据访问与修改
// 获取整个字符串列表 QStringList currentData = model->stringList(); // 修改特定项的数据 QModelIndex firstIndex = model->index(0); model->setData(firstIndex, "修改后的第一项"); // 获取特定项的数据 QVariant itemData = model->data(firstIndex, Qt::DisplayRole); qDebug() << "第一项数据:" << itemData.toString();

3.3 视图属性配置

3.3.1 编辑行为控制
// 设置编辑触发方式 listView->setEditTriggers(QAbstractItemView::EditTriggers triggers); // 常用编辑触发组合 // 双击编辑 listView->setEditTriggers(QAbstractItemView::DoubleClicked); // 手动编辑(需要通过代码触发) listView->setEditTriggers(QAbstractItemView::NoEditTriggers); // 多种触发方式组合 listView->setEditTriggers(QAbstractItemView::DoubleClicked | QAbstractItemView::EditKeyPressed);
3.3.2 选择模式配置
// 单选模式 listView->setSelectionMode(QAbstractItemView::SingleSelection); // 多选模式 listView->setSelectionMode(QAbstractItemView::MultiSelection); // 扩展选择(Shift/Ctrl辅助) listView->setSelectionMode(QAbstractItemView::ExtendedSelection); // 选择行为配置 listView->setSelectionBehavior(QAbstractItemView::SelectItems);

4. 高级功能实现

4.1 自定义数据显示

4.1.1 使用委托进行自定义绘制
#include <QStyledItemDelegate> #include <QPainter> class CustomDelegate : public QStyledItemDelegate { public: explicit CustomDelegate(QObject *parent = nullptr) : QStyledItemDelegate(parent) {} protected: void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override { // 自定义绘制逻辑 if (option.state & QStyle::State_Selected) { painter->fillRect(option.rect, QColor("#e3f2fd")); } // 绘制文本 painter->drawText(option.rect.adjusted(5, 0, -5, 0), Qt::AlignLeft | Qt::AlignVCenter, index.data().toString()); } QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override { return QSize(100, 30); // 固定项大小 } }; // 应用自定义委托 CustomDelegate *delegate = new CustomDelegate(listView); listView->setItemDelegate(delegate);
4.1.2 添加图标和富文本支持
// 创建包含图标的数据模型 QStringListModel *model = new QStringListModel(); QStringList data; data << "文档文件" << "图片文件" << "视频文件"; model->setStringList(data); // 为特定项设置图标 QModelIndex docIndex = model->index(0); model->setData(docIndex, QIcon(":/icons/document.png"), Qt::DecorationRole); // 设置富文本显示 QModelIndex richIndex = model->index(1); model->setData(richIndex, "<b>粗体文本</b>", Qt::DisplayRole);

4.2 数据过滤与排序

4.2.1 使用 QSortFilterProxyModel 进行过滤
#include <QSortFilterProxyModel> // 创建代理模型进行过滤 QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel; proxyModel->setSourceModel(model); proxyModel->setFilterRegExp(QRegExp("苹果", Qt::CaseInsensitive)); listView->setModel(proxyModel); // 动态过滤示例 void filterList(const QString &pattern) { QSortFilterProxyModel *proxy = qobject_cast<QSortFilterProxyModel*>(listView->model()); if (proxy) { proxy->setFilterRegExp(QRegExp(pattern, Qt::CaseInsensitive)); } }
4.2.2 实现自定义排序
// 自定义排序逻辑 proxyModel->setSortRole(Qt::DisplayRole); proxyModel->sort(0, Qt::AscendingOrder); // 动态切换排序 void toggleSortOrder() { QSortFilterProxyModel *proxy = qobject_cast<QSortFilterProxyModel*>(listView->model()); if (proxy) { Qt::SortOrder currentOrder = proxy->sortOrder(); Qt::SortOrder newOrder = (currentOrder == Qt::AscendingOrder) ? Qt::DescendingOrder : Qt::AscendingOrder; proxy->sort(0, newOrder); } }

4.3 信号与槽机制

4.3.1 处理选择变化
// 连接选择变化的信号 connect(listView->selectionModel(), &QItemSelectionModel::selectionChanged, this, [](const QItemSelection &selected, const QItemSelection &deselected) { if (!selected.indexes().isEmpty()) { QModelIndex selectedIndex = selected.indexes().first(); QString selectedText = selectedIndex.data().toString(); qDebug() << "选中项:" << selectedText; } });
4.3.2 监听数据变化
// 监听模型数据变化 connect(model, &QStringListModel::dataChanged, this, [](const QModelIndex &topLeft, const QModelIndex &bottomRight) { for (int row = topLeft.row(); row <= bottomRight.row(); ++row) { QModelIndex index = topLeft.sibling(row, 0); qDebug() << "数据变化,行" << row << ":" << index.data().toString(); } }); // 监听行变化 connect(model, &QStringListModel::rowsInserted, this, [](const QModelIndex &parent, int first, int last) { qDebug() << "插入了行从" << first << "到" << last; }); connect(model, &QStringListModel::rowsRemoved, this, [](const QModelIndex &parent, int first, int last) { qDebug() << "删除了行从" << first << "到" << last; });

5. 完整实战案例:任务管理器

5.1 需求分析与设计

实现一个简单的任务管理器,包含以下功能:

  • 显示任务列表
  • 添加新任务
  • 删除选中任务
  • 标记任务完成状态
  • 过滤显示不同状态的任务

5.2 界面布局设计

#include <QMainWindow> #include <QListView> #include <QStringListModel> #include <QPushButton> #include <QLineEdit> #include <QComboBox> #include <QVBoxLayout> #include <QHBoxLayout> #include <QWidget> class TaskManager : public QMainWindow { Q_OBJECT public: TaskManager(QWidget *parent = nullptr) : QMainWindow(parent) { setupUI(); setupConnections(); } private: QListView *taskListView; QStringListModel *taskModel; QLineEdit *taskInput; QPushButton *addButton; QPushButton *deleteButton; QPushButton *completeButton; QComboBox *filterCombo; void setupUI() { QWidget *centralWidget = new QWidget(this); setCentralWidget(centralWidget); QVBoxLayout *mainLayout = new QVBoxLayout(centralWidget); // 创建过滤选择框 QHBoxLayout *filterLayout = new QHBoxLayout(); filterCombo = new QComboBox(); filterCombo->addItems({"所有任务", "未完成", "已完成"}); filterLayout->addWidget(new QLabel("过滤:")); filterLayout->addWidget(filterCombo); filterLayout->addStretch(); // 创建列表视图 taskListView = new QListView(); taskModel = new QStringListModel(this); taskListView->setModel(taskModel); taskListView->setSelectionMode(QAbstractItemView::SingleSelection); // 创建输入区域 QHBoxLayout *inputLayout = new QHBoxLayout(); taskInput = new QLineEdit(); taskInput->setPlaceholderText("输入新任务..."); addButton = new QPushButton("添加"); inputLayout->addWidget(taskInput); inputLayout->addWidget(addButton); // 创建操作按钮 QHBoxLayout *buttonLayout = new QHBoxLayout(); deleteButton = new QPushButton("删除选中"); completeButton = new QPushButton("标记完成"); buttonLayout->addWidget(deleteButton); buttonLayout->addWidget(completeButton); buttonLayout->addStretch(); mainLayout->addLayout(filterLayout); mainLayout->addWidget(taskListView); mainLayout->addLayout(inputLayout); mainLayout->addLayout(buttonLayout); } };

5.3 核心功能实现

private slots: void addTask() { QString taskText = taskInput->text().trimmed(); if (!taskText.isEmpty()) { // 添加新任务到模型 int row = taskModel->rowCount(); taskModel->insertRow(row); QModelIndex index = taskModel->index(row); taskModel->setData(index, taskText); taskInput->clear(); // 自动选中新添加的任务 taskListView->setCurrentIndex(index); } } void deleteSelectedTask() { QModelIndexList selectedIndexes = taskListView->selectionModel()->selectedIndexes(); if (!selectedIndexes.isEmpty()) { // 从后往前删除,避免索引变化问题 for (int i = selectedIndexes.size() - 1; i >= 0; --i) { taskModel->removeRow(selectedIndexes[i].row()); } } } void markTaskComplete() { QModelIndexList selectedIndexes = taskListView->selectionModel()->selectedIndexes(); if (!selectedIndexes.isEmpty()) { QModelIndex index = selectedIndexes.first(); QString currentText = index.data().toString(); if (!currentText.startsWith("✓ ")) { taskModel->setData(index, "✓ " + currentText); } } } void setupConnections() { connect(addButton, &QPushButton::clicked, this, &TaskManager::addTask); connect(deleteButton, &QPushButton::clicked, this, &TaskManager::deleteSelectedTask); connect(completeButton, &QPushButton::clicked, this, &TaskManager::markTaskComplete); connect(taskInput, &QLineEdit::returnPressed, this, &TaskManager::addTask); }

5.4 高级功能扩展

// 添加任务优先级支持 void setTaskPriority(int priority) { QModelIndexList selectedIndexes = taskListView->selectionModel()->selectedIndexes(); if (!selectedIndexes.isEmpty()) { QModelIndex index = selectedIndexes.first(); QString currentText = index.data().toString(); QString priorityPrefix; switch (priority) { case 1: priorityPrefix = "🔴 "; break; // 高优先级 case 2: priorityPrefix = "🟡 "; break; // 中优先级 case 3: priorityPrefix = "🟢 "; break; // 低优先级 } // 移除现有优先级前缀 if (currentText.startsWith("🔴 ") || currentText.startsWith("🟡 ") || currentText.startsWith("🟢 ")) { currentText = currentText.mid(2); } taskModel->setData(index, priorityPrefix + currentText); } } // 添加任务搜索功能 void searchTasks(const QString &keyword) { for (int i = 0; i < taskModel->rowCount(); ++i) { QModelIndex index = taskModel->index(i); QString taskText = index.data().toString(); bool shouldShow = taskText.contains(keyword, Qt::CaseInsensitive); taskListView->setRowHidden(i, !shouldShow); } }

6. 性能优化与最佳实践

6.1 大数据量处理策略

6.1.1 分批加载数据
// 对于大量数据,采用分批加载 void loadDataInBatches(const QStringList &largeData, int batchSize = 100) { taskModel->removeRows(0, taskModel->rowCount()); for (int i = 0; i < largeData.size(); i += batchSize) { int end = qMin(i + batchSize, largeData.size()); QStringList batch = largeData.mid(i, end - i); int startRow = taskModel->rowCount(); taskModel->insertRows(startRow, batch.size()); for (int j = 0; j < batch.size(); ++j) { QModelIndex index = taskModel->index(startRow + j); taskModel->setData(index, batch[j]); } // 处理UI事件,避免界面冻结 QApplication::processEvents(); } }
6.1.2 使用代理模型优化性能
// 创建高性能的代理模型 class OptimizedProxyModel : public QSortFilterProxyModel { public: explicit OptimizedProxyModel(QObject *parent = nullptr) : QSortFilterProxyModel(parent) { setDynamicSortFilter(false); // 禁用动态排序提升性能 } protected: bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override { if (filterRegExp().isEmpty()) return true; QModelIndex index = sourceModel()->index(source_row, 0, source_parent); QString data = sourceModel()->data(index).toString(); return data.contains(filterRegExp()); } };

6.2 内存管理最佳实践

6.2.1 正确的对象父子关系
// 推荐做法:设置正确的父子关系 QWidget *parentWidget = new QWidget; QStringListModel *model = new QStringListModel(parentWidget); // 自动管理内存 QListView *listView = new QListView(parentWidget); listView->setModel(model); // 避免内存泄漏:不要混合使用栈对象和堆对象 void createListView() { QWidget window; // 错误:堆对象没有父对象,会导致内存泄漏 // QStringListModel *model = new QStringListModel(); // 正确:设置父对象或使用智能指针 QStringListModel *model = new QStringListModel(&window); QListView *listView = new QListView(&window); listView->setModel(model); }
6.2.2 使用智能指针
#include <memory> // 使用智能指针管理内存 std::unique_ptr<QStringListModel> createTaskModel() { auto model = std::make_unique<QStringListModel>(); QStringList tasks = {"任务1", "任务2", "任务3"}; model->setStringList(tasks); return model; } // 在类中使用智能指针 class TaskManager { private: std::unique_ptr<QStringListModel> taskModel; std::unique_ptr<QListView> taskListView; };

6.3 用户体验优化

6.3.1 添加动画效果
#include <QPropertyAnimation> // 为列表项添加简单的动画效果 void animateItemAddition(int row) { QModelIndex index = taskModel->index(row); QWidget *itemWidget = taskListView->indexWidget(index); if (!itemWidget) { // 创建临时widget用于动画 itemWidget = new QWidget(taskListView); taskListView->setIndexWidget(index, itemWidget); } QPropertyAnimation *animation = new QPropertyAnimation(itemWidget, "geometry"); animation->setDuration(300); animation->setStartValue(QRect(0, 0, 0, taskListView->visualRect(index).height())); animation->setEndValue(taskListView->visualRect(index)); animation->start(QPropertyAnimation::DeleteWhenStopped); }
6.3.2 实现拖拽排序
// 启用拖拽功能 listView->setDragEnabled(true); listView->setAcceptDrops(true); listView->setDragDropMode(QAbstractItemView::InternalMove); listView->setDefaultDropAction(Qt::MoveAction); // 处理拖拽完成后的逻辑 connect(model, &QStringListModel::rowsMoved, this, [](const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row) { qDebug() << "项目从位置" << start << "移动到" << row; });

7. 常见问题与解决方案

7.1 数据同步问题

7.1.1 模型数据不同步

问题现象:修改模型数据后视图没有及时更新解决方案

// 确保使用正确的方法修改数据 void updateItemSafely(int row, const QString &newText) { QModelIndex index = model->index(row); if (index.isValid()) { model->setData(index, newText); // 强制刷新视图 listView->viewport()->update(); } } // 批量更新时使用布局变化通知 model->layoutAboutToBeChanged(); // 执行批量数据修改 model->layoutChanged();
7.1.2 选择状态丢失

问题现象:更新模型后选中状态丢失解决方案

// 保存和恢复选择状态 QModelIndexList saveSelection() { return listView->selectionModel()->selectedIndexes(); } void restoreSelection(const QModelIndexList &selectedIndexes) { QItemSelection selection; foreach (const QModelIndex &index, selectedIndexes) { if (index.isValid()) { selection.select(index, index); } } listView->selectionModel()->select(selection, QItemSelectionModel::SelectCurrent); }

7.2 性能问题排查

7.2.1 滚动卡顿处理

问题原因:自定义绘制过于复杂或数据量过大优化方案

// 简化自定义委托的绘制逻辑 void SimpleDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { // 使用缓存优化绘制性能 static QPixmap selectedBackground(option.rect.size()); // 简单的文本绘制,避免复杂操作 painter->setPen(option.palette.text().color()); painter->drawText(option.rect, Qt::AlignVCenter, index.data().toString()); }
7.2.2 内存占用过高

排查方法

// 监控内存使用 void monitorMemoryUsage() { static qint64 lastMemory = 0; qint64 currentMemory = QApplication::applicationPid(); if (currentMemory - lastMemory > 1024 * 1024) { // 1MB增长 qDebug() << "内存显著增长,当前使用:" << currentMemory << "字节"; lastMemory = currentMemory; } } // 定期清理不需要的数据 void cleanupModelData() { // 移除过期的数据项 for (int i = model->rowCount() - 1; i >= 0; --i) { QModelIndex index = model->index(i); if (shouldRemoveItem(index.data().toString())) { model->removeRow(i); } } }

7.3 界面显示问题

7.3.1 文本显示不完整

解决方案

// 设置合适的项高度 listView->setUniformItemSizes(true); listView->setWordWrap(true); // 允许文本换行 // 自定义项大小提示 QSize MyDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { QString text = index.data().toString(); QFontMetrics fm(option.font); QRect textRect = fm.boundingRect(0, 0, listView->width() - 10, 1000, Qt::TextWordWrap, text); return QSize(listView->width(), textRect.height() + 10); }
7.3.2 滚动条行为异常

调整方案

// 设置滚动条策略 listView->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); listView->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); // 优化滚动性能 listView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); listView->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);

8. 扩展应用场景

8.1 文件管理器实现

class FileManager : public QWidget { Q_OBJECT public: explicit FileManager(const QString &directory, QWidget *parent = nullptr) : QWidget(parent) { setupFileList(directory); } private: void setupFileList(const QString &directory) { QDir dir(directory); QStringList files = dir.entryList(QDir::Files | QDir::NoDotAndDotDot); QStringListModel *fileModel = new QStringListModel(this); fileModel->setStringList(files); QListView *fileListView = new QListView(this); fileListView->setModel(fileModel); // 连接双击打开文件信号 connect(fileListView, &QListView::doubleClicked, this, &FileManager::onFileDoubleClicked); } private slots: void onFileDoubleClicked(const QModelIndex &index) { QString fileName = index.data().toString(); qDebug() << "打开文件:" << fileName; // 实现文件打开逻辑 } };

8.2 日志查看器应用

class LogViewer : public QWidget { Q_OBJECT public: LogViewer(QWidget *parent = nullptr) : QWidget(parent) { logModel = new QStringListModel(this); logListView = new QListView(this); logListView->setModel(logModel); // 设置自动滚动到底部 connect(logModel, &QStringListModel::rowsInserted, this, &LogViewer::scrollToBottom); } public slots: void addLogMessage(const QString &message) { int row = logModel->rowCount(); logModel->insertRow(row); QModelIndex index = logModel->index(row); QString timestamp = QDateTime::currentDateTime().toString("hh:mm:ss"); logModel->setData(index, QString("[%1] %2").arg(timestamp, message)); // 限制日志数量,避免内存过度增长 if (logModel->rowCount() > 1000) { logModel->removeRow(0); } } private slots: void scrollToBottom() { QModelIndex lastIndex = logModel->index(logModel->rowCount() - 1); logListView->scrollTo(lastIndex); } };

通过本文的详细讲解,相信你已经掌握了 QStringListModel 和 QListView 的核心用法。在实际项目中,根据具体需求选择合适的配置和优化方案,可以构建出既美观又高效的列表界面。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/6 11:19:27

从电机控制到车规芯片:嵌入式工程师的进阶路线图

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/6 11:19:13

企业架构设计实战:从四层架构到落地治理的完整指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/6 11:18:39

指数移动平均与一阶低通滤波:一个递推公式的跨域统一

1. 从两个名字说起&#xff1a;它们是同一个东西我最早接触这两个词&#xff0c;是在完全不同的场景里。一次是做嵌入式传感器数据处理&#xff0c;同事张口就是“一阶低通滤波”&#xff0c;递推公式写出来就完事&#xff1b;另一次是看量化交易的研报&#xff0c;“指数移动平…

作者头像 李华
网站建设 2026/9/6 11:18:28

AI全栈开发工程实践:从Vibe Coding到稳定交付

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/6 11:18:27

指数移动平均EMA与一阶低通滤波:数学同构与量化应用解析

看到“指数移动平均”这个词&#xff0c;做量化的朋友第一反应是MACD、均线策略&#xff0c;做信号处理的工程师想的却是RC低通滤波器。有意思的是&#xff0c;这俩看似八竿子打不着的概念&#xff0c;底层数学结构是完全一致的。我在折腾一个小型趋势跟踪策略的时候&#xff0…

作者头像 李华
网站建设 2026/9/6 11:17:33

24bit磁编码器KTM5900深度解析:分辨率≠精度,如何挑战光编?

1. 这颗"24bit磁编码器"到底是怎么来的拿到KTM5900的规格书&#xff0c;第一眼看到“24bit绝对角度”的时候&#xff0c;我确实愣了一下。干这行的都知道&#xff0c;磁编码器主流市场还在16bit到18bit之间打转&#xff0c;AS5047P这种老将凭借14bit的分辨率就能通吃…

作者头像 李华