基于Qt C++的仓库管理系统:从环境搭建到项目实战

📅 2026/8/19 1:27:32
基于Qt C++的仓库管理系统:从环境搭建到项目实战
很多同学在完成C课程设计或毕业设计时常常面临一个难题如何找到一个功能完整、界面美观、代码规范且易于二次开发的项目源码网上资料要么过于简单要么耦合严重难以理解。本文将分享一个基于Qt C开发的仓库管理系统它不仅是完整的课设/大作业解决方案更是一个学习Qt桌面应用开发、数据库操作和软件工程思想的优秀范例。从环境搭建到源码解析再到功能扩展本文将手把手带你走完全流程无论你是C初学者还是有一定基础的开发者都能从中获得实用的项目经验。1. 项目概述与核心技术栈1.1 什么是仓库管理系统仓库管理系统Warehouse Management System, WMS是一个用于跟踪和管理仓库内货物入库、出库、存储、盘点等核心业务流程的软件系统。对于课程设计而言它完美融合了增删改查CRUD、图形用户界面GUI、数据库操作和业务逻辑处理等多个核心知识点是检验和提升综合编程能力的绝佳课题。本系统采用经典的客户端-服务器C/S架构但为了简化部署和演示我们将数据库直接内嵌在客户端应用中使用SQLite数据库这非常适合单机版课设项目。1.2 核心技术栈Qt与CQt框架一个跨平台的C图形用户界面应用程序框架。它提供了丰富的控件库、强大的信号与槽机制、便捷的数据库访问接口以及优秀的国际化支持。使用Qt可以快速构建出专业、美观的桌面应用且代码可轻松移植到Windows、Linux、macOS等系统。C语言作为项目的核心编程语言负责实现所有业务逻辑、数据结构和算法。本项目将展示面向对象编程OOP思想在实战中的应用如类的封装、继承和多态。SQLite数据库一个轻量级、无服务器、零配置、事务性的SQL数据库引擎。它将整个数据库存储在一个单一的磁盘文件中非常适合嵌入式设备和桌面应用。Qt通过QSql模块提供了对SQLite的完美支持。开发工具Qt Creator官方IDE或Visual Studio with Qt插件。本文将使用Qt Creator进行演示。2. 开发环境搭建与项目准备2.1 安装Qt开发环境下载Qt安装包访问Qt官网下载Qt Online Installer。建议选择开源版本Open Source。运行安装程序运行下载的安装程序登录或注册Qt账户开源版本免费。选择组件在组件选择页面务必勾选以下内容Qt版本建议选择一个长期支持LTS版本如Qt 5.15.x或Qt 6.2.x。对于新手Qt 5.15更为稳定社区资源丰富。编译器在Windows下勾选MSVC 2019 64-bit如果你安装了Visual Studio或MinGW 64-bit。在Linux/macOS下选择对应的GCC/Clang。开发工具确保Qt Creator被选中。附加库勾选Qt Charts用于可能的统计图表功能和SourcesQt源码便于调试。完成安装。2.2 验证安装与创建测试项目打开Qt Creator。点击文件-新建文件或项目。选择Application-Qt Widgets Application点击Choose...。输入项目名称如TestProject和路径点击下一步。在Kit Selection页面选择你安装的编译套件如Desktop Qt 5.15.2 MSVC2019 64bit点击下一步。在类信息页面保持默认会生成一个主窗口类点击下一步然后完成。点击左下角的绿色运行按钮或按CtrlR。如果成功弹出一个空白窗口说明环境配置成功。2.3 获取仓库管理系统源码你可以从提供的链接下载本项目的完整源码。解压后用Qt Creator打开其中的.pro文件项目文件。3. 项目结构与核心模块解析打开项目后我们先从整体结构上理解它。一个典型的Qt Widgets项目结构如下WarehouseManagementSystem/ ├── WarehouseManagementSystem.pro # Qt项目配置文件 ├── main.cpp # 程序入口 ├── mainwindow.h # 主窗口类头文件 ├── mainwindow.cpp # 主窗口类实现文件 ├── mainwindow.ui # 主窗口界面设计文件XML格式 ├── database/ │ ├── database.h # 数据库操作类头文件 │ └── database.cpp # 数据库操作类实现文件 ├── entity/ │ ├── goods.h # 货物实体类头文件 │ └── goods.cpp # 货物实体类实现文件 ├── dao/ │ ├── goodsdao.h # 货物数据访问对象头文件 │ └── goodsdao.cpp # 货物数据访问对象实现文件 └── resources/ # 资源文件目录如图标各模块职责mainwindow程序的视图层和控制层核心负责界面展示和用户交互事件的响应。database数据库连接层封装了数据库的打开、关闭以及一些通用的SQL执行方法。entity实体层每个文件对应数据库中的一张表定义了货物的属性如ID、名称、类别、数量、价格等及其getter/setter方法。dao数据访问对象层负责执行针对特定实体如货物的所有数据库操作增、删、改、查是业务逻辑与数据库操作之间的桥梁。这种分层架构界面层-业务逻辑层-数据访问层-实体层极大地提高了代码的可读性、可维护性和可测试性是软件工程中的经典实践。4. 数据库设计与实现4.1 数据库表设计我们设计一张goods表来存储货物信息。-- 文件在database.cpp的初始化函数中执行此SQL CREATE TABLE IF NOT EXISTS goods ( id INTEGER PRIMARY KEY AUTOINCREMENT, -- 主键自增长 name TEXT NOT NULL, -- 货物名称 category TEXT, -- 货物类别 quantity INTEGER DEFAULT 0, -- 库存数量 price REAL, -- 单价 supplier TEXT, -- 供应商 storage_date TEXT, -- 入库日期 description TEXT -- 描述信息 );4.2 数据库连接类实现这是整个系统的数据基石我们来看database.cpp的核心部分。// 文件database/database.cpp #include “database.h” #include QSqlDatabase #include QSqlError #include QSqlQuery #include QDebug #include QDir #include QStandardPaths Database::Database() { // 构造函数初始化数据库连接 } bool Database::open() { // 使用SQLite数据库驱动 m_db QSqlDatabase::addDatabase(“QSQLITE”); // 确定数据库文件路径。优先尝试在可执行文件同级目录创建便于移植。 QString dbPath QDir::currentPath() “/warehouse.db”; // 如果上述路径不可写如在某些系统保护目录则使用用户数据目录 QFileInfo fi(dbPath); QDir dir fi.dir(); if (!dir.exists()) { dir.mkpath(“.”); } m_db.setDatabaseName(dbPath); if (!m_db.open()) { qDebug() “Error: Failed to open database.” m_db.lastError().text(); return false; } qDebug() “Database opened successfully at:” dbPath; return true; } void Database::close() { if (m_db.isOpen()) { m_db.close(); qDebug() “Database closed.”; } } bool Database::init() { if (!m_db.isOpen()) { if (!open()) return false; } QSqlQuery query; // 创建goods表 QString sql “CREATE TABLE IF NOT EXISTS goods (” “id INTEGER PRIMARY KEY AUTOINCREMENT,” “name TEXT NOT NULL,” “category TEXT,” “quantity INTEGER DEFAULT 0,” “price REAL,” “supplier TEXT,” “storage_date TEXT,” “description TEXT);”; if (!query.exec(sql)) { qDebug() “Failed to create table:” query.lastError().text(); return false; } // 可以在这里插入一些初始测试数据 // insertSampleData(); qDebug() “Database initialized successfully.”; return true; } QSqlDatabase Database::getDatabase() { return m_db; }5. 实体类与数据访问对象DAO实现5.1 货物实体类Goods实体类是数据的载体是面向对象思维的直接体现。// 文件entity/goods.h #ifndef GOODS_H #define GOODS_H #include QString class Goods { public: Goods(); Goods(int id, const QString name, const QString category, int quantity, double price, const QString supplier, const QString storageDate, const QString description); // Getter 和 Setter 方法 int getId() const; void setId(int id); QString getName() const; void setName(const QString name); // ... 其他属性的Getter/Setter private: int m_id; QString m_name; QString m_category; int m_quantity; double m_price; QString m_supplier; QString m_storageDate; QString m_description; }; #endif // GOODS_H5.2 货物数据访问对象GoodsDaoDAO层封装了所有与goods表交互的SQL语句是业务逻辑与数据库之间的隔离层。// 文件dao/goodsdao.cpp (部分核心函数) #include “goodsdao.h” #include “../entity/goods.h” #include “../database/database.h” #include QSqlQuery #include QSqlError #include QDebug #include QVariant GoodsDao::GoodsDao() { m_db Database::getInstance().getDatabase(); // 获取数据库单例连接 } bool GoodsDao::addGoods(const Goods goods) { QSqlQuery query(*m_db); query.prepare(“INSERT INTO goods (name, category, quantity, price, supplier, storage_date, description) “ “VALUES (:name, :category, :quantity, :price, :supplier, :storage_date, :description)”); // 使用绑定参数防止SQL注入攻击 query.bindValue(“:name”, goods.getName()); query.bindValue(“:category”, goods.getCategory()); query.bindValue(“:quantity”, goods.getQuantity()); query.bindValue(“:price”, goods.getPrice()); query.bindValue(“:supplier”, goods.getSupplier()); query.bindValue(“:storage_date”, goods.getStorageDate()); query.bindValue(“:description”, goods.getDescription()); if (!query.exec()) { qDebug() “Add goods failed:” query.lastError().text(); return false; } return true; } QVectorGoods GoodsDao::getAllGoods() { QVectorGoods goodsList; QSqlQuery query(“SELECT * FROM goods ORDER BY id DESC”, *m_db); // 按ID倒序新的在前 while (query.next()) { Goods goods; goods.setId(query.value(“id”).toInt()); goods.setName(query.value(“name”).toString()); goods.setCategory(query.value(“category”).toString()); goods.setQuantity(query.value(“quantity”).toInt()); goods.setPrice(query.value(“price”).toDouble()); goods.setSupplier(query.value(“supplier”).toString()); goods.setStorageDate(query.value(“storage_date”).toString()); goods.setDescription(query.value(“description”).toString()); goodsList.append(goods); } return goodsList; } bool GoodsDao::updateGoods(const Goods goods) { QSqlQuery query(*m_db); query.prepare(“UPDATE goods SET name:name, category:category, quantity:quantity, “ “price:price, supplier:supplier, storage_date:storage_date, “ “description:description WHERE id:id”); query.bindValue(“:id”, goods.getId()); // ... 绑定其他参数同上 // ... return query.exec(); } bool GoodsDao::deleteGoods(int id) { QSqlQuery query(*m_db); query.prepare(“DELETE FROM goods WHERE id :id”); query.bindValue(“:id”, id); return query.exec(); }6. 主界面设计与功能实现6.1 界面布局使用Qt DesignerQt Creator内置的Qt Designer是一个强大的可视化界面设计工具。我们设计的主界面通常包含菜单栏和工具栏提供文件、编辑、帮助等操作。中央区域一个QTableWidget表格用于展示所有货物列表。侧边或底部区域用于添加、修改、查询货物的表单多个QLineEdit、QComboBox、QSpinBox等。按钮区域添加、修改、删除、查询、刷新等操作按钮。你可以在mainwindow.ui文件中通过拖拽控件完成布局。设计好的界面会通过Qt的元对象系统自动生成界面代码并在mainwindow.cpp的setupUi函数中完成控件初始化。6.2 核心功能串联信号与槽Qt的核心机制是信号与槽用于对象间的通信。例如当用户点击“添加”按钮时这个按钮会发出一个clicked()信号我们需要编写一个槽函数来处理这个信号。// 文件mainwindow.cpp (部分代码) #include “mainwindow.h” #include “ui_mainwindow.h” #include “dao/goodsdao.h” #include “entity/goods.h” #include QMessageBox #include QDebug MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui-setupUi(this); // 初始化UI // 初始化数据库 if (!Database::getInstance().init()) { QMessageBox::critical(this, “错误”, “数据库初始化失败”); return; } // 初始化表格 initTable(); // 加载数据 refreshTable(); // 连接信号与槽 connect(ui-btnAdd, QPushButton::clicked, this, MainWindow::onAddButtonClicked); connect(ui-btnDelete, QPushButton::clicked, this, MainWindow::onDeleteButtonClicked); connect(ui-btnUpdate, QPushButton::clicked, this, MainWindow::onUpdateButtonClicked); connect(ui-btnSearch, QPushButton::clicked, this, MainWindow::onSearchButtonClicked); connect(ui-tableWidget, QTableWidget::cellDoubleClicked, this, MainWindow::onTableItemDoubleClicked); } void MainWindow::refreshTable() { GoodsDao dao; QVectorGoods goodsList dao.getAllGoods(); ui-tableWidget-setRowCount(0); // 清空表格 for (int i 0; i goodsList.size(); i) { const Goods goods goodsList.at(i); int row ui-tableWidget-rowCount(); ui-tableWidget-insertRow(row); ui-tableWidget-setItem(row, 0, new QTableWidgetItem(QString::number(goods.getId()))); ui-tableWidget-setItem(row, 1, new QTableWidgetItem(goods.getName())); ui-tableWidget-setItem(row, 2, new QTableWidgetItem(goods.getCategory())); ui-tableWidget-setItem(row, 3, new QTableWidgetItem(QString::number(goods.getQuantity()))); ui-tableWidget-setItem(row, 4, new QTableWidgetItem(QString::number(goods.getPrice(), ‘f’, 2))); // 保留两位小数 ui-tableWidget-setItem(row, 5, new QTableWidgetItem(goods.getSupplier())); ui-tableWidget-setItem(row, 6, new QTableWidgetItem(goods.getStorageDate())); ui-tableWidget-setItem(row, 7, new QTableWidgetItem(goods.getDescription())); } } void MainWindow::onAddButtonClicked() { // 1. 从界面输入框获取数据 QString name ui-lineEditName-text().trimmed(); if (name.isEmpty()) { QMessageBox::warning(this, “警告”, “货物名称不能为空”); return; } // ... 获取其他字段 // 2. 构造Goods对象 Goods goods(0, name, category, quantity, price, supplier, storageDate, description); // 3. 调用DAO层插入数据库 GoodsDao dao; if (dao.addGoods(goods)) { QMessageBox::information(this, “成功”, “货物添加成功”); clearInputFields(); refreshTable(); // 刷新表格显示 } else { QMessageBox::critical(this, “错误”, “货物添加失败”); } } void MainWindow::onTableItemDoubleClicked(int row, int column) { // 双击表格行将数据填充到编辑框方便修改 Q_UNUSED(column); int id ui-tableWidget-item(row, 0)-text().toInt(); ui-lineEditId-setText(QString::number(id)); // 隐藏的ID输入框用于更新时识别 ui-lineEditName-setText(ui-tableWidget-item(row, 1)-text()); // ... 填充其他字段 }7. 编译、运行与打包发布7.1 编译与运行在Qt Creator中确保选择了正确的Kit然后直接点击构建-运行或按CtrlR。程序会编译并启动。首次运行会在程序所在目录生成warehouse.db数据库文件。7.2 项目打包发布Windows示例Qt程序编译后其可执行文件.exe不能单独运行需要依赖一系列的Qt动态链接库DLL。在Qt Creator中将构建模式改为Release。重新构建项目。在构建输出目录如build-项目名-Release/release找到生成的.exe文件。使用Qt自带的命令行工具windeployqt来自动拷贝依赖。打开开始菜单-Qt 5.15.2 (MSVC 2019 64-bit)-Qt 5.15.2 (MSVC 2019 64-bit) Command Prompt。使用cd命令切换到你的.exe文件所在目录。执行命令windeployqt 你的程序名.exe命令执行后该目录下会包含所有必需的DLL和插件文件夹如platforms。此时这个目录下的所有文件一起就可以分发到其他没有安装Qt的Windows电脑上运行了。8. 常见问题与解决方案FAQ问题现象可能原因解决方案编译错误找不到头文件项目.pro文件中未正确包含头文件路径。在.pro文件中添加INCLUDEPATH $$PWD/entity $$PWD/dao $$PWD/database运行时报错数据库驱动未加载未将SQLite驱动插件打包或环境缺失。确保发布时sqldrivers文件夹内含qsqlite.dll在plugins目录下并被正确加载。在代码中可用QSqlDatabase::drivers()检查可用驱动。程序界面乱码源代码文件编码与编译器/系统编码不一致。在Qt Creator中将源代码文件编码设置为UTF-8工具-选项-文本编辑器-行为-默认编码。在main函数开头添加QTextCodec::setCodecForLocale(QTextCodec::codecForName(“UTF-8”));(Qt5) 或使用QString::fromUtf8。表格无法显示数据QTableWidget未设置列数或DAO层查询失败。在initTable()函数中调用setColumnCount()并设置列标题。检查数据库查询语句和错误信息lastError()。点击按钮无反应信号与槽未正确连接。检查connect语句参数是否正确特别是新式语法QPushButton::clicked要求编译器支持C11可在.pro中添加CONFIG c11。发布后在其他电脑无法运行缺少VC运行库或Qt依赖库不全。1. 确保目标电脑安装了对应版本的Visual C Redistributable。2. 使用windeployqt仔细检查或使用Dependency Walker工具查看缺失的DLL。9. 功能扩展与优化建议课设加分项一个基础的CRUD系统只能拿到及格分。要让你的课设脱颖而出可以考虑以下扩展方向用户登录与权限管理新增users表存储用户名和加密后的密码。设计登录对话框。根据用户角色如管理员、普通员工控制界面元素的可见性如删除按钮和数据访问权限。数据验证与业务逻辑强化入库时数量不能为负数。出库时出库数量不能大于当前库存。对单价、日期等输入进行格式校验。高级查询与报表实现多条件组合查询按名称模糊查询、按类别筛选、按日期范围查询。使用QChart模块实现库存数量、品类分布的统计图表。支持将查询结果导出为Excel可使用QAxObject操作COM组件或PDF文件。数据备份与恢复添加菜单项支持手动备份数据库文件warehouse.db到指定位置。实现从备份文件恢复数据库的功能。界面美化与体验优化使用Qt样式表QSS为控件添加自定义样式改变颜色、字体、边框等。为表格行设置交替背景色提高可读性。添加状态栏显示当前用户、数据统计等信息。网络化进阶将数据库部分拆分为独立的服务器程序可使用Qt Network模块编写TCP/UDP服务端。客户端程序通过网络协议与服务器通信实现多用户同时访问同一数据库。通过实现上述一个或多个扩展功能你不仅能完成课设要求更能深入理解软件开发的完整流程和复杂问题的解决方法。本源码提供了一个坚实、结构清晰的起点剩下的创造力就交给你了。