QT 5.12.10 自定义 QSqlTableModel:实现库存预警高亮(附完整代码)
QT 5.12.10 深度定制 QSqlTableModel实现智能库存预警与可视化交互1. 现代库存管理系统的核心需求与QT解决方案在当今快节奏的商业环境中一个高效的库存管理系统已经成为企业运营不可或缺的神经中枢。传统的手工记录或基础电子表格早已无法满足现代企业对库存准确性、实时响应和智能预警的需求。QT框架凭借其强大的跨平台能力、丰富的UI组件库和灵活的Model/View架构成为开发专业级库存管理系统的理想选择。库存管理系统的核心痛点通常集中在几个关键方面实时库存可视化、低库存预警、多维度数据分析和用户友好的交互界面。QT的Model/View框架完美解决了这些问题特别是通过自定义QSqlTableModel开发者可以轻松实现实时数据绑定数据库变更即时反映在界面智能预警系统库存低于阈值自动高亮提示高性能渲染即使处理万级数据也能保持流畅跨平台一致性Windows、Linux、macOS统一体验// 基础模型类继承关系 QObject └── QAbstractItemModel └── QAbstractTableModel └── QSqlQueryModel └── QSqlTableModel └── CustomSqlModel (我们的自定义类)2. 自定义QSqlTableModel的架构设计2.1 模型类的基本结构我们首先构建一个继承自QSqlTableModel的自定义模型类重写关键的data()和flags()方法来实现预警功能。以下是头文件的基本结构// customsqltablemodel.h #include QSqlTableModel #include QColor class CustomSqlTableModel : public QSqlTableModel { Q_OBJECT public: explicit CustomSqlTableModel(QObject *parent nullptr, QSqlDatabase db QSqlDatabase()); QVariant data(const QModelIndex index, int role Qt::DisplayRole) const override; Qt::ItemFlags flags(const QModelIndex index) const override; void setWarningThreshold(int threshold); void setWarningColumn(int column); private: int warningThreshold 0; int warningColumn -1; QColor warningColor QColor(255, 200, 200); // 浅红色背景 };2.2 关键方法实现原理data()方法是模型视图架构的核心它根据不同的角色(role)返回不同的数据表现形式。我们需要特别处理Qt::BackgroundRole来改变单元格背景色QVariant CustomSqlTableModel::data(const QModelIndex index, int role) const { if (!index.isValid()) return QVariant(); // 库存预警逻辑 if (role Qt::BackgroundRole warningColumn 0) { QModelIndex stockIndex this-index(index.row(), warningColumn); int stockValue QSqlTableModel::data(stockIndex, Qt::DisplayRole).toInt(); if (stockValue warningThreshold) { return warningColor; } } // 默认数据处理 return QSqlTableModel::data(index, role); }flags()方法控制每个单元格的交互属性我们可以通过它实现只读或可编辑区域Qt::ItemFlags CustomSqlTableModel::flags(const QModelIndex index) const { Qt::ItemFlags flags QSqlTableModel::flags(index); // 示例ID列设为只读 if (index.column() 0) { flags ~Qt::ItemIsEditable; } return flags; }3. 数据库设计与模型集成3.1 SQLite表结构设计合理的数据库设计是系统稳定性的基础。我们采用SQLite作为存储引擎设计库存表结构如下CREATE TABLE inventory ( id INTEGER PRIMARY KEY AUTOINCREMENT, product_code TEXT NOT NULL UNIQUE, product_name TEXT NOT NULL, category TEXT, current_stock INTEGER DEFAULT 0, warning_threshold INTEGER DEFAULT 10, last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP );3.2 模型与数据库的绑定将自定义模型与数据库表关联只需几行代码// 初始化数据库连接 QSqlDatabase db QSqlDatabase::addDatabase(QSQLITE); db.setDatabaseName(inventory.db); if (!db.open()) { qCritical() Database connection error: db.lastError().text(); return; } // 创建并配置模型 CustomSqlTableModel *model new CustomSqlTableModel(this, db); model-setTable(inventory); model-setEditStrategy(QSqlTableModel::OnManualSubmit); model-select(); // 设置预警参数 model-setWarningColumn(4); // current_stock列 model-setWarningThreshold(10); // 绑定到视图 QTableView *view new QTableView; view-setModel(model);4. 高级功能实现技巧4.1 动态阈值预警在实际业务中不同商品的库存预警阈值可能不同。我们可以扩展模型支持从数据库读取每行的独立阈值QVariant CustomSqlTableModel::data(const QModelIndex index, int role) const { // ... 其他逻辑 if (role Qt::BackgroundRole warningColumn 0) { QModelIndex stockIndex this-index(index.row(), warningColumn); QModelIndex thresholdIndex this-index(index.row(), warningColumn 1); int stockValue QSqlTableModel::data(stockIndex, Qt::DisplayRole).toInt(); int thresholdValue QSqlTableModel::data(thresholdIndex, Qt::DisplayRole).toInt(); if (stockValue thresholdValue) { return warningColor; } } // ... }4.2 多级预警颜色根据库存严重程度我们可以实现颜色梯度预警if (stockValue thresholdValue) { // 计算短缺比例 double ratio static_castdouble(stockValue) / thresholdValue; if (ratio 0.2) return QColor(255, 100, 100); // 严重短缺 if (ratio 0.5) return QColor(255, 150, 150); // 中度短缺 return QColor(255, 200, 200); // 轻微短缺 }4.3 性能优化策略处理大型库存表时这些技巧可以显著提升性能按需加载数据model-setFetchSize(100); // 每次只获取100行批量提交更改model-setEditStrategy(QSqlTableModel::OnManualSubmit); // ...多次修改后 model-submitAll();智能数据刷新// 只刷新可见区域 connect(view-verticalScrollBar(), QScrollBar::valueChanged, [](){ QModelIndex top view-indexAt(QPoint(0, 0)); QModelIndex bottom view-indexAt(QPoint(0, view-height())); model-fetchMore(bottom); });5. 完整示例代码实现5.1 自定义模型类实现// customsqltablemodel.cpp #include customsqltablemodel.h CustomSqlTableModel::CustomSqlTableModel(QObject *parent, QSqlDatabase db) : QSqlTableModel(parent, db) {} void CustomSqlTableModel::setWarningThreshold(int threshold) { warningThreshold threshold; } void CustomSqlTableModel::setWarningColumn(int column) { warningColumn column; } QVariant CustomSqlTableModel::data(const QModelIndex index, int role) const { if (!index.isValid()) return QVariant(); // 库存预警逻辑 if (role Qt::BackgroundRole warningColumn 0) { QModelIndex stockIndex this-index(index.row(), warningColumn); int stockValue QSqlTableModel::data(stockIndex, Qt::DisplayRole).toInt(); if (stockValue warningThreshold) { // 多级颜色预警 double ratio static_castdouble(stockValue) / warningThreshold; if (ratio 0.2) return QColor(255, 100, 100); if (ratio 0.5) return QColor(255, 150, 150); return QColor(255, 200, 200); } } // 文本对齐 if (role Qt::TextAlignmentRole index.column() 4) { return Qt::AlignRight Qt::AlignVCenter; } return QSqlTableModel::data(index, role); } Qt::ItemFlags CustomSqlTableModel::flags(const QModelIndex index) const { Qt::ItemFlags flags QSqlTableModel::flags(index); // ID列和预警阈值列设为只读 if (index.column() 0 || index.column() 5) { flags ~Qt::ItemIsEditable; } return flags; }5.2 主窗口集成// mainwindow.cpp #include mainwindow.h #include customsqltablemodel.h #include QTableView #include QHeaderView #include QSqlError #include QDebug MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { setupDatabase(); setupUI(); } void MainWindow::setupDatabase() { db QSqlDatabase::addDatabase(QSQLITE); db.setDatabaseName(inventory.db); if (!db.open()) { qCritical() Database error: db.lastError().text(); return; } // 创建示例表如果不存在 QSqlQuery query; query.exec(CREATE TABLE IF NOT EXISTS inventory ( id INTEGER PRIMARY KEY AUTOINCREMENT, product_code TEXT NOT NULL UNIQUE, product_name TEXT NOT NULL, category TEXT, current_stock INTEGER DEFAULT 0, warning_threshold INTEGER DEFAULT 10, last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP)); // 插入示例数据 query.exec(INSERT OR IGNORE INTO inventory (product_code, product_name, category, current_stock, warning_threshold) VALUES (P1001, 高端显卡, 电脑配件, 5, 8), (P1002, 机械键盘, 外设, 15, 10), (P1003, 无线鼠标, 外设, 3, 5)); } void MainWindow::setupUI() { // 创建模型 model new CustomSqlTableModel(this, db); model-setTable(inventory); model-setEditStrategy(QSqlTableModel::OnManualSubmit); model-select(); // 设置预警列和阈值 model-setWarningColumn(4); // current_stock列 model-setWarningThresholdColumn(5); // 从每行读取独立阈值 // 创建视图 tableView new QTableView; tableView-setModel(model); tableView-setSelectionMode(QAbstractItemView::SingleSelection); tableView-setSelectionBehavior(QAbstractItemView::SelectRows); tableView-resizeColumnsToContents(); tableView-horizontalHeader()-setSectionResizeMode(QHeaderView::Interactive); // 设置列宽策略 tableView-setColumnWidth(1, 120); // product_code tableView-setColumnWidth(2, 200); // product_name tableView-horizontalHeader()-setStretchLastSection(true); setCentralWidget(tableView); resize(800, 600); }6. 扩展功能与业务集成6.1 库存预警信号机制我们可以扩展模型当库存状态变化时发出信号// 在头文件中添加信号声明 signals: void stockWarning(int productId, const QString productName, int current, int threshold); // 修改data()方法 QVariant CustomSqlTableModel::data(const QModelIndex index, int role) const { // ... if (role Qt::BackgroundRole warningColumn 0) { // ...预警逻辑 if (stockValue thresholdValue) { emit stockWarning(id, productName, stockValue, thresholdValue); // ... } } // ... }6.2 与QT Charts集成实现可视化结合QT Charts模块我们可以创建直观的库存分析图表#include QtCharts void MainWindow::createStockChart() { QChart *chart new QChart; // 获取低库存商品 QSqlQuery query(SELECT product_name, current_stock, warning_threshold FROM inventory WHERE current_stock warning_threshold); QBarSeries *series new QBarSeries; while (query.next()) { QString name query.value(0).toString(); int stock query.value(1).toInt(); int threshold query.value(2).toInt(); QBarSet *set new QBarSet(name); *set stock threshold; series-append(set); } chart-addSeries(series); chart-setTitle(低库存商品预警); // 设置坐标轴 QBarCategoryAxis *axisX new QBarCategoryAxis; axisX-append({当前库存, 预警阈值}); chart-addAxis(axisX, Qt::AlignBottom); series-attachAxis(axisX); QValueAxis *axisY new QValueAxis; chart-addAxis(axisY, Qt::AlignLeft); series-attachAxis(axisY); // 显示图表 QChartView *chartView new QChartView(chart); chartView-setRenderHint(QPainter::Antialiasing); chartView-resize(600, 400); chartView-show(); }6.3 多线程数据加载对于大型库存数据库使用多线程加载数据可以避免界面冻结// 工作线程类 class DataLoader : public QThread { Q_OBJECT public: explicit DataLoader(QSqlDatabase db, QObject *parent nullptr) : QThread(parent), db(db) {} void run() override { QSqlQuery query(db); query.exec(SELECT * FROM inventory); QVectorInventoryItem items; while (query.next()) { InventoryItem item; item.id query.value(0).toInt(); // ...填充其他字段 items.append(item); } emit dataLoaded(items); } signals: void dataLoaded(const QVectorInventoryItem items); private: QSqlDatabase db; }; // 在主窗口中使用 void MainWindow::loadDataAsync() { DataLoader *loader new DataLoader(db, this); connect(loader, DataLoader::dataLoaded, this, MainWindow::updateModel); connect(loader, DataLoader::finished, loader, QObject::deleteLater); loader-start(); }