如何在Qt5+SQLite3中同时访问同一数据库的不同表
嘿,我来帮你搞定这个Qt5+SQLite3跨表展示日志的需求!要在列表视图里同时展示两张表的关联信息,核心是用SQL关联查询+Qt的数据库模型来实现,下面是具体的步骤和代码示例:
实现跨表日志展示方案
1. 先写关联两张表的SQL查询语句
log表只存了书籍的UID,要拿到对应的书名、作者,就得和allBooks表做关联查询。用INNER JOIN就能把两张表通过UID字段绑定起来,SQL语句如下:
SELECT log.STUDENT, allBooks.NAME AS BOOK_NAME, allBooks.AUTHOR, log.ISSUED, log.RETURNED FROM log INNER JOIN allBooks ON log.UID = allBooks.UID;
如果担心有些日志记录对应的书籍已经被删除(allBooks里没有对应UID),可以把INNER JOIN换成LEFT JOIN,这样即使书籍信息不存在,日志记录依然会被保留,只是书籍相关字段会显示NULL。
2. 在日志对话框构造函数里实现模型与视图绑定
假设你的对话框类叫LogDialog,可以用QSqlQueryModel来承载自定义查询的结果,再绑定到视图上。这里建议用QTableView(比QListView更适合展示多列的日志数据),如果一定要用ListView,后面也会给你补充优化方案:
#include <QSqlQueryModel> #include <QTableView> #include <QVBoxLayout> #include <QSqlDatabase> #include <QDebug> LogDialog::LogDialog(QWidget *parent) : QDialog(parent) { // 先确保数据库连接已经初始化并打开(这里假设你已经在全局建立了名为"bookDB"的连接) QSqlDatabase db = QSqlDatabase::database("bookDB"); if (!db.isOpen()) { qDebug() << "数据库连接失败:" << db.lastError().text(); return; } // 创建查询模型,执行关联查询 QSqlQueryModel *logModel = new QSqlQueryModel(this); logModel->setQuery(R"( SELECT log.STUDENT, allBooks.NAME AS BOOK_NAME, allBooks.AUTHOR, log.ISSUED, log.RETURNED FROM log INNER JOIN allBooks ON log.UID = allBooks.UID )", db); // 设置表头的友好名称 logModel->setHeaderData(0, Qt::Horizontal, tr("学生姓名")); logModel->setHeaderData(1, Qt::Horizontal, tr("书籍名称")); logModel->setHeaderData(2, Qt::Horizontal, tr("作者")); logModel->setHeaderData(3, Qt::Horizontal, tr("借出日期")); logModel->setHeaderData(4, Qt::Horizontal, tr("归还日期")); // 创建表格视图并绑定模型 QTableView *logTableView = new QTableView(this); logTableView->setModel(logModel); // 可以让列宽自适应内容 logTableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); // 把视图放到布局里 QVBoxLayout *mainLayout = new QVBoxLayout(this); mainLayout->addWidget(logTableView); setLayout(mainLayout); }
3. 如果你坚持用QListView展示
因为ListView默认只显示单列,需要自定义委托来把多列数据拼接成一行展示,代码示例如下:
首先定义委托类:
#include <QStyledItemDelegate> #include <QPainter> class LogItemDelegate : public QStyledItemDelegate { Q_OBJECT public: explicit LogItemDelegate(QWidget *parent = nullptr) : QStyledItemDelegate(parent) {} void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override { // 获取当前行的所有列数据 QString student = index.sibling(index.row(), 0).data().toString(); QString bookName = index.sibling(index.row(), 1).data().toString(); QString author = index.sibling(index.row(), 2).data().toString(); QString issued = index.sibling(index.row(), 3).data().toString(); QString returned = index.sibling(index.row(), 4).data().toString(); // 拼接成友好的显示文本 QString displayText = QString("学生: %1 | 书籍: %2(作者:%3) | 借出:%4 | 归还:%5") .arg(student).arg(bookName).arg(author).arg(issued).arg(returned); // 绘制文本 QStyleOptionViewItem opt = option; initStyleOption(&opt, index); opt.text = displayText; QStyledItemDelegate::paint(painter, opt, index); } };
然后在对话框构造函数里替换视图部分的代码:
// 替换原来的QTableView部分 QListView *logListView = new QListView(this); logListView->setModel(logModel); // 设置自定义委托 logListView->setItemDelegate(new LogItemDelegate(this)); QVBoxLayout *mainLayout = new QVBoxLayout(this); mainLayout->addWidget(logListView); setLayout(mainLayout);
4. 额外注意事项
- 确保在应用启动时已经正确初始化SQLite连接,比如:
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE"); db.setDatabaseName("book_system.db"); if (!db.open()) { qDebug() << "打开数据库失败:" << db.lastError().text(); }
- 如果需要实时刷新日志列表,可以在添加/修改日志记录后,调用
logModel->setQuery()重新执行查询来更新数据。
内容的提问来源于stack exchange,提问作者Hemil
相关产品推荐
相关产品推荐

