• Qt SQL示例:Books(图书评级)


    这个示例展示将 Qt 的 SQL 类与模型/视图框架一起使用的方法。

    书籍的信息、作者的信息、图书类型的信息保存在数据库的不同表中。

    自定义委托实现鼠标按下为图书评级。

    构建在内存中的数据库表并插入数据:

    1. #ifndef INITDB_H
    2. #define INITDB_H
    3. #include
    4. void addBook(QSqlQuery &q, const QString & 书名, int 年份,
    5. const QVariant & 作者ID, const QVariant & 类型ID, int 星级)
    6. {
    7. q.addBindValue(书名);
    8. q.addBindValue(年份);
    9. q.addBindValue(作者ID);
    10. q.addBindValue(类型ID);
    11. q.addBindValue(星级);
    12. q.exec();
    13. }
    14. QVariant addGenre(QSqlQuery &q, const QString &作品类型名称)
    15. {
    16. q.addBindValue(作品类型名称);
    17. q.exec();
    18. return q.lastInsertId();
    19. }
    20. QVariant addAuthor(QSqlQuery &q, const QString &作者名称, QDate 作者生日)
    21. {
    22. q.addBindValue(作者名称);
    23. q.addBindValue(作者生日);
    24. q.exec();
    25. return q.lastInsertId();
    26. }
    27. const auto BOOKS_SQL = R"(create table 书籍(id integer primary key,
    28. 书名 varchar,
    29. 作者 integer,
    30. 类型 integer,
    31. 年份 integer,
    32. 星级 integer))";
    33. const auto AUTHORS_SQL = R"(create table 作家(id integer primary key,
    34. 姓名 varchar,
    35. 生日 date))";
    36. const auto GENRES_SQL = R"(create table 作品类型(id integer primary key,
    37. 类型名称 varchar))";
    38. const auto INSERT_AUTHOR_SQL = R"(insert into 作家(姓名, 生日)
    39. values(?, ?))";
    40. const auto INSERT_BOOK_SQL = R"(insert into 书籍(书名, 年份, 作者, 类型, 星级)
    41. values(?, ?, ?, ?, ?))";
    42. const auto INSERT_GENRE_SQL = R"(insert into 作品类型(类型名称)
    43. values(?))";
    44. QSqlError initDb()
    45. {
    46. QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
    47. db.setDatabaseName(":memory:");
    48. if (!db.open())
    49. return db.lastError();
    50. QStringList tables = db.tables();
    51. if (tables.contains("书籍", Qt::CaseInsensitive)
    52. && tables.contains("作家", Qt::CaseInsensitive))
    53. return QSqlError();
    54. QSqlQuery q;
    55. if (!q.exec(BOOKS_SQL))
    56. return q.lastError();
    57. if (!q.exec(AUTHORS_SQL))
    58. return q.lastError();
    59. if (!q.exec(GENRES_SQL))
    60. return q.lastError();
    61. if (!q.prepare(INSERT_AUTHOR_SQL))
    62. return q.lastError();
    63. QVariant asimovId = addAuthor(q, QLatin1String("艾萨克.阿西莫夫").data(), QDate(1920, 2, 1));
    64. QVariant greeneId = addAuthor(q, QLatin1String("格雷厄姆·格林").data(), QDate(1904, 10, 2));
    65. QVariant pratchettId = addAuthor(q, QLatin1String("特里·普拉切特").data(), QDate(1948, 4, 28));
    66. if (!q.prepare(INSERT_GENRE_SQL))
    67. return q.lastError();
    68. QVariant sfiction = addGenre(q, QLatin1String("科幻小说").data());
    69. QVariant fiction = addGenre(q, QLatin1String("幽默讽刺小说").data());
    70. QVariant fantasy = addGenre(q, QLatin1String("奇幻小说").data());
    71. if (!q.prepare(INSERT_BOOK_SQL))
    72. return q.lastError();
    73. addBook(q, QLatin1String("基地").data(), 1951, asimovId, sfiction, 3);
    74. addBook(q, QLatin1String("基地与帝国").data(), 1952, asimovId, sfiction, 4);
    75. addBook(q, QLatin1String("第二基地").data(), 1953, asimovId, sfiction, 3);
    76. addBook(q, QLatin1String("基金边缘").data(), 1982, asimovId, sfiction, 3);
    77. addBook(q, QLatin1String("基地与地球").data(), 1986, asimovId, sfiction, 4);
    78. addBook(q, QLatin1String("基地前奏").data(), 1988, asimovId, sfiction, 3);
    79. addBook(q, QLatin1String("迈向基地").data(), 1993, asimovId, sfiction, 3);
    80. addBook(q, QLatin1String("权力与荣耀").data(), 1940, greeneId, fiction, 4);
    81. addBook(q, QLatin1String("第三个人").data(), 1950, greeneId, fiction, 5);
    82. addBook(q, QLatin1String("我们在哈瓦那的人").data(), 1958, greeneId, fiction, 4);
    83. addBook(q, QLatin1String("卫兵!卫兵!").data(), 1989, pratchettId, fantasy, 3);
    84. addBook(q, QLatin1String("守夜人").data(), 2002, pratchettId, fantasy, 3);
    85. addBook(q, QLatin1String("邮局奇遇记").data(), 2004, pratchettId, fantasy, 3);
    86. return QSqlError();
    87. }
    88. #endif

    构建了书籍、作家、作品类型三个数据表并插入相应的数据。

    简单的UI界面

    使用模型显示数据库中的数据:

    1. BookWindow::BookWindow()
    2. {
    3. ui.setupUi(this);
    4. if (!QSqlDatabase::drivers().contains("QSQLITE"))
    5. QMessageBox::critical(this,"无法加载数据库","此示例需要 SQLITE 驱动程序");
    6. QSqlError err = initDb();
    7. if (err.type() != QSqlError::NoError)
    8. {
    9. QMessageBox::critical(this, "无法初始化数据库","错误信息:" + err.text());
    10. return;
    11. }
    12. model = new QSqlRelationalTableModel(ui.bookTable);
    13. model->setEditStrategy(QSqlTableModel::OnManualSubmit);
    14. model->setTable("书籍");
    15. //“书籍”表中相关索引
    16. authorIdx = model->fieldIndex("作者");
    17. genreIdx = model->fieldIndex("类型");
    18. //设置与其他数据库表的关系:
    19. model->setRelation(authorIdx, QSqlRelation("作家", "id", "姓名"));
    20. model->setRelation(genreIdx, QSqlRelation("作品类型", "id", "类型名称"));
    21. //设置本地化的标题
    22. model->setHeaderData(authorIdx, Qt::Horizontal, tr("_作者_"));
    23. model->setHeaderData(genreIdx, Qt::Horizontal, tr("_类型_"));
    24. model->setHeaderData(model->fieldIndex("书名"),Qt::Horizontal, tr("_书名_"));
    25. model->setHeaderData(model->fieldIndex("年份"), Qt::Horizontal, tr("_年份_"));
    26. model->setHeaderData(model->fieldIndex("星级"),Qt::Horizontal, tr("_星级_"));
    27. if (!model->select())
    28. {
    29. QMessageBox::critical(this, "无法初始化数据库","错误信息:" + model->lastError().text());
    30. return;
    31. }
    32. //设置模型并隐藏 ID 列:
    33. ui.bookTable->setModel(model);
    34. ui.bookTable->setItemDelegate(new BookDelegate(ui.bookTable));
    35. ui.bookTable->setColumnHidden(model->fieldIndex("id"), true);
    36. ui.bookTable->setSelectionMode(QAbstractItemView::SingleSelection);
    37. //使用作者姓名填充QComboBox
    38. ui.authorEdit->setModel(model->relationModel(authorIdx));
    39. ui.authorEdit->setModelColumn(model->relationModel(authorIdx)->fieldIndex("姓名"));
    40. //使用小说类型填充QComboBox
    41. ui.genreEdit->setModel(model->relationModel(genreIdx));
    42. ui.genreEdit->setModelColumn(model->relationModel(genreIdx)->fieldIndex("类型名称"));
    43. //此列拉伸
    44. ui.bookTable->horizontalHeader()->setSectionResizeMode(model->fieldIndex("星级"),QHeaderView::Stretch);
    45. auto mapper = new QDataWidgetMapper(this);
    46. mapper->setModel(model);
    47. mapper->setItemDelegate(new BookDelegate(this));
    48. mapper->addMapping(ui.titleEdit, model->fieldIndex("书名"));
    49. mapper->addMapping(ui.yearEdit, model->fieldIndex("年份"));
    50. mapper->addMapping(ui.authorEdit, authorIdx);
    51. mapper->addMapping(ui.genreEdit, genreIdx);
    52. mapper->addMapping(ui.ratingEdit, model->fieldIndex("星级"));
    53. ui.ratingEdit->setSuffix(" 星");
    54. connect(ui.bookTable->selectionModel(),&QItemSelectionModel::currentRowChanged,
    55. mapper,&QDataWidgetMapper::setCurrentModelIndex);
    56. ui.bookTable->setCurrentIndex(model->index(0, 0));
    57. }

    使用的是 QSqlRelationalTableModel 模型,它可为单个数据库表提供可编辑的数据模型,并允许将列设置为其他数据库表的外键。

    设置此模型显示 “书籍” 表中的数据,并设置:

    • “书籍” 表中的 “作者” 列(作家的id)对应 “作家” 表中的(id),显示为作家的姓名
    • “书籍” 表中的 “类型” 列(小说类型id)对应 “作品类型” 表中的(id),显示为作品的类型名称
    1. //“书籍”表中相关索引
    2. authorIdx = model->fieldIndex("作者");
    3. genreIdx = model->fieldIndex("类型");
    4. //设置与其他数据库表的关系:
    5. model->setRelation(authorIdx, QSqlRelation("作家", "id", "姓名"));
    6. model->setRelation(genreIdx, QSqlRelation("作品类型", "id", "类型名称"));

    最后使用 QDataWidgetMapper 使模型和小部件之间建立对应关系,它可以用于显示模型中的数据并将小部件中数据的更改反馈到模型。

    关于第五列的星形委托见:自定义委托(星形委托)

    涉及到的知识点和类:

  • 相关阅读:
    vue 项目中一些问题记录
    在Vue 3中使用v-model来构建复杂的表单
    goroutine 调度器
    [Linux]----文件系统
    Spark初学者出师未接身先死
    LeakCanary(4)面试题系列
    linux配置IP、子网掩码、网关
    【触想智能】工业显示器上市前的检测项目分享
    Matlab实现SUSAN角点检测
    docker root dir 迁移方案
  • 原文地址:https://blog.csdn.net/kenfan1647/article/details/126869114