【Qt实战教程第2篇 数据库 QSqlTableModel分页查询、增删改查 含源码+注释】

本博主只关注实战系列,Qt的基本语法课参考其他博主的Qt教程
1:本章实战教程会实现一个图书信息管理的功能,其中包含分页查询、增删改查等功能。基于上一节创建的MysqlTest项目进行开发,图书信息管理项目基于visual studio 2022、Qt 5.14.2、,mysql 8.0.29进行开发,由于Qt本身不自带mysql驱动,需自行编译,具体操作请参考上一节【Qt实战教程第1篇 编译mysql数据库驱动–MSVC2017版本】
2:本章内容涉及知识点:
QTableView、QSqlTableModel、自定义委托类(delegate)、mysql基本语法等
3:本章内容只实现功能,函数析构时某些指针内存未被释放,可自行添加指针释放操作
4:项目源码下载

一、图书信息管理项目运行结果

在这里插入图片描述
在这里插入图片描述

二、数据准备

1:数据库:在test数据库中创建图书信息表book_info。

CREATE TABLE `book_info` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT 'id',
  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '图书名称',
  `author` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '作者',
  `type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '类型',
  `publish` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '出版社',
  `photo` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '封面图片',
  `intime` datetime DEFAULT NULL COMMENT '创建时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

2:创建项目目录结构

创建BookInfo的相关文件,先创建空文件,下一步我们依次填充文件内容。最终的目录结构如下所示:
在这里插入图片描述

三、源码+注释

1:图书信息类

BookInfo.h文件

#pragma once
#include <QtWidgets/qwidget.h>
#include "ui_BookInfo.h"
#include "qsqlerror.h"
#include "qmessagebox.h"
#include "qsqlrecord.h"
#include "qdebug.h"
#include "qmath.h"
#include "BookQSqlTableModel.h"
#include "qdatetime.h"
#include "OperateColumnDelegate.h"
#include "BookInfoDialog.h"
#include "CommonDefine.h"
#include "qsqlrecord.h"

class BookInfo : public QWidget
{
	Q_OBJECT
public:
	BookInfo(QWidget* parent = Q_NULLPTR);
	~BookInfo();

	void initPagnition(int totalRowCount); // 初始化分页
	void createSignalPageBtn(int page); // 创建单个的分页按钮
public slots:
	void slotHandleSearch(); // 搜索
	void slotHandleSave(); // 保存
	void slotHandleBatchDelete(); // 批量删除
	void slotPagnition(QPushButton* button, int currPage); // 分页跳转
	void slotPageSizeChanged(int pageSize); // 每页显示数量
	void slotSearchChanged(const QString& text); // 搜索框
	void slotOperateEvent(QString text, QModelIndex index); // 操作列按钮点击事件
	void slotSaveEvent(); // 新增按钮
	void slotDialogOperate(BookInfoEntity* bookInfo, QModelIndex index); // 弹窗操作


private:
	Ui::BookInfoClass ui;
	BookQSqlTableModel* theModel; // 自定义QSqlTableModel
	int m_nCurrPage; // 当前页
	int m_nPagesToShow; // 显示的页码数量
	QMap<int, QPushButton*> m_pPageMap; // 保存的页码列表指针
	OperateColumnDelegate* m_ColumnDelegate; // 操作列的委托类
};


BookInfo.cpp

#include "BookInfo.h"

BookInfo::BookInfo(QWidget* parent)
	: QWidget(parent)
{
	ui.setupUi(this);
	QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");
	db.setHostName("localhost");
	db.setDatabaseName("test");
	db.setUserName("root");
	db.setPassword("root");
	if (!db.open())
	{
		QString sqlError = db.lastError().text();
		QString text = QString("%1\n%2").arg(QString::fromLocal8Bit("数据库连接失败!")).arg(sqlError);
		QMessageBox::critical(this, QStringLiteral("提示"), text, QMessageBox::Yes);
		return;
	}
	m_nCurrPage = 1; // 默认是第1页
	m_nPagesToShow = 5; // 默认显示的页码数量
	ui.comboBox->insertItem(0, QStringLiteral("20条/页"), 20);
	ui.comboBox->insertItem(1, QStringLiteral("30条/页"), 30);
	ui.comboBox->insertItem(2, QStringLiteral("50条/页"), 50);
	ui.comboBox->insertItem(3, QStringLiteral("100条/页"), 100);
	ui.comboBox->setCurrentIndex(2);
	connect(ui.comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotPageSizeChanged(int))); // 每页数量选择

	connect(ui.lineEdit_search, &QLineEdit::textEdited, this, &BookInfo::slotSearchChanged); // 搜索框--文本更改

	theModel = new BookQSqlTableModel(this, db);
	theModel->setTable("book_info"); // 设置需要查询的数据库表名
	theModel->setSort(theModel->fieldIndex("id"), Qt::DescendingOrder); // 设置排序字段及排序方式,此处为降序
	theModel->selectPination(m_nCurrPage, ui.comboBox->currentData().toInt()); // 使用自定义的分页查询方法

	// 设置QTableView表格的表头
	theModel->setHeaderData(theModel->fieldIndex("name"), Qt::Horizontal, QStringLiteral("图书名称"));
	theModel->setHeaderData(theModel->fieldIndex("author"), Qt::Horizontal, QStringLiteral("作者"));
	theModel->setHeaderData(theModel->fieldIndex("type"), Qt::Horizontal, QStringLiteral("类型"));
	theModel->setHeaderData(theModel->fieldIndex("publish"), Qt::Horizontal, QStringLiteral("出版社"));
	theModel->setHeaderData(theModel->fieldIndex("photo"), Qt::Horizontal, QStringLiteral("封面"));
	theModel->setHeaderData(theModel->fieldIndex("intime"), Qt::Horizontal, QStringLiteral("创建时间"));
	ui.tableView->setModel(theModel); // 把数据模型添加到QTableView中

	// 自定义委托类(代理),实现操作列按钮添加
	m_ColumnDelegate = new OperateColumnDelegate(QStringList() << QStringLiteral("编辑") << QStringLiteral("删除"));
	connect(m_ColumnDelegate, &OperateColumnDelegate::clicked, this, &BookInfo::slotOperateEvent); // 按钮点击信号槽
	ui.tableView->setItemDelegateForColumn(theModel->getColumnCount(), m_ColumnDelegate); // 给最后一列添加代理


	ui.tableView->setAlternatingRowColors(true); // 隔行颜色显示
	ui.tableView->setEditTriggers(QAbstractItemView::NoEditTriggers); //  不允许编辑
	ui.tableView->setColumnHidden(theModel->fieldIndex("id"), true); // 隐藏id列
	ui.tableView->setColumnWidth(theModel->fieldIndex("name"), 260);
	ui.tableView->setColumnWidth(theModel->fieldIndex("author"), 200);
	ui.tableView->setColumnWidth(theModel->fieldIndex("type"), 200);
	ui.tableView->setColumnWidth(theModel->fieldIndex("publish"), 260);
	ui.tableView->setColumnWidth(theModel->fieldIndex("intime"), 200);
	ui.tableView->setColumnWidth(theModel->getColumnCount(), 100); // 操作列
	ui.tableView->horizontalHeader()->setSectionResizeMode(theModel->fieldIndex("photo"), QHeaderView::Stretch);
	ui.tableView->setSelectionBehavior(QAbstractItemView::SelectRows); // 设置选中时为整行选中
	ui.tableView->setSelectionMode(QAbstractItemView::MultiSelection);

	connect(ui.btnSearch, SIGNAL(clicked()), this, SLOT(slotHandleSearch())); // 搜索操作
	connect(ui.btnSave, SIGNAL(clicked()), this, SLOT(slotHandleSave())); // 新增操作
	connect(ui.btnBatchDelete, SIGNAL(clicked()), this, SLOT(slotHandleBatchDelete())); //批量删除操作


	int totalRowCount = theModel->totalRowCount(); // 获取数据总行数
	ui.label_totalNum->setText(QString::fromLocal8Bit("共") + QString::number(totalRowCount) + QString::fromLocal8Bit("条"));

	connect(ui.btnPrevPage, &QPushButton::clicked, this, [=]() {slotPagnition(ui.btnPrevPage, m_nCurrPage - 1); });
	connect(ui.btnNextPage, &QPushButton::clicked, this, [=]() {slotPagnition(ui.btnNextPage, m_nCurrPage + 1); });
	initPagnition(totalRowCount); // 初始化分页
}

BookInfo::~BookInfo()
{
	QMap<int, QPushButton*>::iterator iteorPage = m_pPageMap.begin();
	while (iteorPage != m_pPageMap.end())
	{
		QPushButton* button = iteorPage.value();
		ui.PageBtnHorizontalLayout->removeWidget(button);
		delete button; // 释放按钮指针
		button = Q_NULLPTR;
		iteorPage = m_pPageMap.erase(iteorPage);
	}
}

void BookInfo::initPagnition(int totalRowCount)
{
	// 清除之前的页码列表
	QMap<int, QPushButton*>::iterator iteorPage = m_pPageMap.begin();
	while (iteorPage != m_pPageMap.end())
	{
		QPushButton* button = iteorPage.value();
		ui.PageBtnHorizontalLayout->removeWidget(button);
		delete button; // 释放按钮指针
		button = Q_NULLPTR;
		iteorPage = m_pPageMap.erase(iteorPage);
	}
	// 计算页码,最多显示5页,不足5页显示全部
	int lastPage = qCeil(totalRowCount * 1.0 / ui.comboBox->currentData().toInt());
	if (m_nCurrPage == 1)
	{
		ui.btnPrevPage->setEnabled(false); // 默认上一页按钮不可点击
	}
	if (lastPage == 1)
	{
		ui.btnNextPage->setEnabled(false); // 默认下一页按钮不可点击
	}
	int startRangePage = qMax(1, m_nCurrPage - m_nPagesToShow / 2);
	int endRangePage = qMin(lastPage, startRangePage + m_nPagesToShow - 1);
	int firstPage = 1;
	// 加载首页
	if (firstPage == lastPage) // 只有一页的时候只显示一个按钮
	{
		createSignalPageBtn(firstPage);
		return;
	}
	createSignalPageBtn(firstPage);
	if (m_nCurrPage - (m_nPagesToShow / 2) - 1 > firstPage)
	{
		// 展示前面跳转的页码
		createSignalPageBtn(INT_MIN);
	}

	for (int i = startRangePage; i <= endRangePage; i++) {
		if (m_nCurrPage <= m_nPagesToShow && i == 1) {
			continue;
		}
		if (m_nCurrPage >= (lastPage - m_nPagesToShow) && i == lastPage) {
			continue;
		}
		createSignalPageBtn(i);
	}

	if (m_nCurrPage + (m_nPagesToShow / 2) + 1 < lastPage)
	{
		// 展示前面跳转的页码
		createSignalPageBtn(INT_MAX);
	}
	// 加载尾页
	createSignalPageBtn(lastPage);
}

void BookInfo::createSignalPageBtn(int page)
{
	QString btnText = QString::number(page);
	if (page == INT_MIN)
	{
		btnText = "<<";
	}
	if (page == INT_MAX)
	{
		btnText = ">>";
	}
	QPushButton* button = new QPushButton(this);
	if (page == m_nCurrPage)
	{
		button->setStyleSheet("QPushButton{background-color: #5eaef1; color: #000;}");
	}
	button->setFixedSize(20, 20);
	button->setText(btnText);
	ui.PageBtnHorizontalLayout->addWidget(button);
	connect(button, &QPushButton::clicked, this, [=]() {slotPagnition(button, page); }); // 点击分页按钮触发slotPagnition时间
	m_pPageMap.insert(page, button);
}

void BookInfo::slotHandleSave()
{
	BookInfoDialog dlg(FORM::SAVE);
	connect(&dlg, &BookInfoDialog::update, this, &BookInfo::slotDialogOperate);
	dlg.exec();
}

void BookInfo::slotHandleBatchDelete()
{
	// 获取选中的索引列表
	QItemSelectionModel* selectionModel = ui.tableView->selectionModel();
	QModelIndexList selectedIndexes = selectionModel->selectedIndexes(); // 这里获得的是所有格子的总index数量
	QMessageBox confirm;
	confirm.setWindowTitle(QStringLiteral("提示"));
	confirm.setText(QString("%1%2%3").arg(QStringLiteral("是否确认删除当前这")).arg(selectedIndexes.count() / 7).arg(QStringLiteral("条数据?")));
	confirm.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
	confirm.button(QMessageBox::Yes)->setText(QStringLiteral("确定"));
	confirm.button(QMessageBox::No)->setText(QStringLiteral("取消"));
	int ret = confirm.exec();
	if (ret == QMessageBox::No) return;
	// 遍历选中的索引,并处理每一行
	for each (const QModelIndex & index in selectedIndexes) {
		if (index.isValid()) {
			theModel->removeRow(index.row());
		}
	}
	if (theModel->submitAll())
	{
		m_nCurrPage = 1;
		ui.comboBox->setCurrentIndex(2);
		int pageSize = ui.comboBox->itemData(2).toInt();
		theModel->selectPination(m_nCurrPage, pageSize);
		int totalRowCount = theModel->totalRowCount();
		ui.label_totalNum->setText(QString::fromLocal8Bit("共") + QString::number(totalRowCount) + QString::fromLocal8Bit("条"));
		initPagnition(totalRowCount);
		QMessageBox::information(this, QStringLiteral("提示"),
			QString("%1%2%3").arg(QStringLiteral("共删除")).arg(selectedIndexes.count() / 7).arg(QStringLiteral("条数据"))); // 共7列
	}
}

void BookInfo::slotPagnition(QPushButton* button, int currPage)
{
	if (currPage == m_nCurrPage) return;

	int lastPage = qCeil(theModel->totalRowCount() * 1.0 / ui.comboBox->currentData().toInt());
	if (currPage == INT_MIN)
	{
		currPage = m_nCurrPage;
		currPage = qMax(1, currPage - m_nPagesToShow);
	}
	if (currPage == INT_MAX)
	{
		currPage = m_nCurrPage;
		currPage = qMin(lastPage, currPage + m_nPagesToShow);
	}
	m_nCurrPage = currPage;
	theModel->selectPination(currPage, ui.comboBox->currentData().toInt());

	// 刷新按钮状态
	if (m_nCurrPage == 1)
	{
		ui.btnPrevPage->setEnabled(false);
	}
	else
	{
		ui.btnPrevPage->setEnabled(true);
	}
	if (m_nCurrPage == lastPage)
	{
		ui.btnNextPage->setEnabled(false);
	}
	else
	{
		ui.btnNextPage->setEnabled(true);
	}

	// 刷新列表
	initPagnition(theModel->totalRowCount());
}

void BookInfo::slotPageSizeChanged(int index)
{
	ui.comboBox->setCurrentIndex(index);
	int pageSize = ui.comboBox->itemData(index).toInt();
	m_nCurrPage = 1;
	theModel->selectPination(m_nCurrPage, pageSize);
	initPagnition(theModel->totalRowCount()); // 初始化分页
}

void BookInfo::slotSearchChanged(const QString& text)
{
	theModel->setFilter(""); // 会自动执行sql查询,从而刷新模型
	if (!text.isEmpty())
	{
		theModel->setFilter(" name like '%" + text + "%' or author like '%" + text + "%' or publish like '%" + text + "%'");
	}
	m_nCurrPage = 1;
	ui.comboBox->setCurrentIndex(2);
	int pageSize = ui.comboBox->itemData(2).toInt();
	int totalRowCount = theModel->totalRowCount();
	ui.label_totalNum->setText(QString::fromLocal8Bit("共") + QString::number(totalRowCount) + QString::fromLocal8Bit("条"));
	initPagnition(totalRowCount);
}

void BookInfo::slotOperateEvent(QString text, QModelIndex index)
{
	ui.tableView->selectRow(index.row());
	if (text == QString::fromLocal8Bit("编辑"))
	{
		QSqlRecord record = theModel->record(index.row());
		BookInfoEntity* bookInfo = new BookInfoEntity();
		bookInfo->setId(record.value("id").toInt());
		bookInfo->setName(record.value("name").toString());
		bookInfo->setAuthor(record.value("author").toString());
		bookInfo->setType(record.value("type").toString());
		bookInfo->setPublish(record.value("publish").toString());
		bookInfo->setPhoto(record.value("photo").toString());
		BookInfoDialog dlg(FORM::EDIT, bookInfo, index);
		connect(&dlg, &BookInfoDialog::update, this, &BookInfo::slotDialogOperate);
		dlg.exec();
	}
	else if (text == QString::fromLocal8Bit("删除"))
	{
		QMessageBox confirm;
		confirm.setWindowTitle(QStringLiteral("提示"));
		confirm.setText(QStringLiteral("是否确认删除当前数据?"));
		confirm.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
		confirm.button(QMessageBox::Yes)->setText(QStringLiteral("确定"));
		confirm.button(QMessageBox::No)->setText(QStringLiteral("取消"));
		int ret = confirm.exec();
		if (ret == QMessageBox::Yes)
		{
			theModel->removeRow(index.row());
			if (theModel->submitAll())
			{
				m_nCurrPage = 1;
				ui.comboBox->setCurrentIndex(2);
				int pageSize = ui.comboBox->itemData(2).toInt();
				theModel->selectPination(m_nCurrPage, pageSize);
				int totalRowCount = theModel->totalRowCount();
				ui.label_totalNum->setText(QString::fromLocal8Bit("共") + QString::number(totalRowCount) + QString::fromLocal8Bit("条"));
				initPagnition(totalRowCount);
				QMessageBox::information(this, QStringLiteral("提示"), QStringLiteral("数据删除成功"));
			}
		}
	}
}

void BookInfo::slotDialogOperate(BookInfoEntity* bookInfo, QModelIndex index)
{
	int rowCount = index.row();
	if (!index.isValid())
	{
		rowCount = theModel->rowCount();
	}

	if (!bookInfo->id().isEmpty())
	{
		theModel->setData(theModel->index(rowCount, theModel->fieldIndex("id")), bookInfo->id().toInt());
	}
	else
	{
		theModel->insertRow(rowCount);
	}
	theModel->setData(theModel->index(rowCount, theModel->fieldIndex("name")), bookInfo->name());
	theModel->setData(theModel->index(rowCount, theModel->fieldIndex("author")), bookInfo->author());
	theModel->setData(theModel->index(rowCount, theModel->fieldIndex("type")), bookInfo->type());
	theModel->setData(theModel->index(rowCount, theModel->fieldIndex("publish")), bookInfo->publish());
	theModel->setData(theModel->index(rowCount, theModel->fieldIndex("photo")), bookInfo->photo());
	theModel->setData(theModel->index(rowCount, theModel->fieldIndex("intime")), QDateTime::currentDateTime());

	theModel->submitAll();
	qDebug() << theModel->lastError();
	m_nCurrPage = 1;
	ui.comboBox->setCurrentIndex(2);
	int pageSize = ui.comboBox->itemData(2).toInt();
	theModel->selectPination(m_nCurrPage, pageSize);
	int totalRowCount = theModel->totalRowCount();
	ui.label_totalNum->setText(QString::fromLocal8Bit("共") + QString::number(totalRowCount) + QString::fromLocal8Bit("条"));
	initPagnition(totalRowCount);
}

void BookInfo::slotHandleSearch()
{
	// TODO 由于文字更改会触发搜索(数据量大会影响性能),此处未实现
	// 如要实现则拷贝slotSearchChanged方法中的内容到这里,并在前面添加QString text = ui.lineEdit_search->text();
}

BookInfo.ui 使用xml方式打开

<?xml version="1.0" encoding="utf-8"?>
<ui version="4.0">
	<class>BookInfoClass</class>
	<widget class="QWidget" name="BookInfoClass">
		<property name="geometry">
			<rect>
				<x>0</x>
				<y>0</y>
				<width>814</width>
				<height>402</height>
			</rect>
		</property>
		<property name="windowTitle">
			<string>图书信息</string>
		</property>
		<layout class="QGridLayout" name="gridLayout">
			<item row="0" column="0">
				<layout class="QVBoxLayout" name="verticalLayout">
					<item>
						<layout class="QHBoxLayout" name="horizontalLayout_3">
							<item>
								<layout class="QHBoxLayout" name="horizontalLayout">
									<item>
										<widget class="QLineEdit" name="lineEdit_search"/>
									</item>
									<item>
										<widget class="QPushButton" name="btnSearch">
											<property name="text">
												<string>搜索</string>
											</property>
										</widget>
									</item>
								</layout>
							</item>
							<item>
								<layout class="QHBoxLayout" name="horizontalLayout_2">
									<item>
										<spacer name="horizontalSpacer">
											<property name="orientation">
												<enum>Qt::Horizontal</enum>
											</property>
											<property name="sizeHint" stdset="0">
												<size>
													<width>40</width>
													<height>20</height>
												</size>
											</property>
										</spacer>
									</item>
									<item>
										<widget class="QPushButton" name="btnSave">
											<property name="text">
												<string>新增</string>
											</property>
										</widget>
									</item>
									<item>
										<widget class="QPushButton" name="btnBatchDelete">
											<property name="text">
												<string>批量删除</string>
											</property>
										</widget>
									</item>
								</layout>
							</item>
						</layout>
					</item>
					<item>
						<widget class="QTableView" name="tableView"/>
					</item>
					<item>
						<layout class="QHBoxLayout" name="horizontalLayout_4">
							<item>
								<spacer name="horizontalSpacer_2">
									<property name="orientation">
										<enum>Qt::Horizontal</enum>
									</property>
									<property name="sizeHint" stdset="0">
										<size>
											<width>40</width>
											<height>20</height>
										</size>
									</property>
								</spacer>
							</item>
							<item>
								<widget class="QLabel" name="label_totalNum">
									<property name="text">
										<string>共0条</string>
									</property>
								</widget>
							</item>
							<item>
								<widget class="QComboBox" name="comboBox"/>
							</item>
							<item>
								<widget class="QPushButton" name="btnPrevPage">
									<property name="text">
										<string>上一页</string>
									</property>
								</widget>
							</item>
							<item>
								<layout class="QHBoxLayout" name="PageBtnHorizontalLayout"/>
							</item>
							<item>
								<widget class="QPushButton" name="btnNextPage">
									<property name="text">
										<string>下一页</string>
									</property>
								</widget>
							</item>
						</layout>
					</item>
				</layout>
			</item>
		</layout>
	</widget>
	<resources/>
	<connections/>
</ui>

2:图书信息弹窗类(新增、编辑)

BookInfoDialog.h

#pragma once
#include "qdialog.h"
#include "qlineedit.h"
#include "qcombobox.h"
#include "qpushbutton.h"
#include "qimage.h"
#include <QHBoxLayout>
#include "CommonDefine.h"
#include "qlabel.h"
#include "ui_BookInfoDialog.h"
#include "BookInfoEntity.h"
#include "qfiledialog.h"
/*
自定义弹窗类
	1:实现表单布局
	2:实现表单功能
*/
class BookInfoDialog : public QDialog
{
	Q_OBJECT
public:
	BookInfoDialog(FORM formType, BookInfoEntity* bookInfo = Q_NULLPTR, QModelIndex index = QModelIndex(), QWidget* parent = Q_NULLPTR);
	~BookInfoDialog();
signals:
	void update(BookInfoEntity* bookInfo, QModelIndex index);

private slots:
	void slotBtnSaveClick();
private:
	QLineEdit* name;
	QLineEdit* author;
	QComboBox* type;
	QComboBox* publish;
	QLabel* photo;
	QPushButton* m_BtnSave;
	QPushButton* m_BtnPhoto;
	QString sPhotoPath;

	Ui::BookInfoDialogClass ui;
	FORM m_FormType;
	BookInfoEntity* m_pBookInfoEntity;
	QModelIndex m_pIndex;
};


BookInfoDialog.cpp

#include "BookInfoDialog.h"

BookInfoDialog::BookInfoDialog(FORM formType, BookInfoEntity* bookInfo, QModelIndex index, QWidget* parent)
	: QDialog(parent)
{
	ui.setupUi(this);
	setWindowTitle(QStringLiteral("图书信息"));
	m_pBookInfoEntity = bookInfo;
	m_FormType = formType;
	m_pIndex = index;
	name = new QLineEdit(this);
	name->setClearButtonEnabled(true);
	author = new QLineEdit(this);
	type = new QComboBox(this);
	type->addItem(QStringLiteral("文学"));
	type->addItem(QStringLiteral("历史"));
	type->addItem(QStringLiteral("地理"));
	type->addItem(QStringLiteral("化学"));
	type->addItem(QStringLiteral("计算机"));
	type->setCurrentIndex(0);
	publish = new QComboBox(this);
	publish->addItem(QStringLiteral("中信出版社"));
	publish->addItem(QStringLiteral("机械工业出版社"));
	publish->addItem(QStringLiteral("人民邮电出版社"));
	publish->addItem(QStringLiteral("电子工业出版社"));
	publish->addItem(QStringLiteral("北京大学出版社"));


	photo = new QLabel(this);
	if (bookInfo != Q_NULLPTR)
	{
		name->setText(bookInfo->name());
		author->setText(bookInfo->author());
		type->setCurrentText(bookInfo->type());
		publish->setCurrentText(bookInfo->publish());
		sPhotoPath = bookInfo->photo();
	}

	// 这里使用网格布局
	QGridLayout* gridLayout1 = new QGridLayout();
	// 第一行
	gridLayout1->addWidget(new QLabel(QStringLiteral("名称:")), 0, 0, Qt::AlignLeft); // 第一行第1列
	gridLayout1->addWidget(name, 0, 1); // 第一行第2列
	gridLayout1->addWidget(new QLabel(QStringLiteral("作者:")), 0, 2, Qt::AlignLeft); // 第一行第3列
	gridLayout1->addWidget(author, 0, 3); // 第一行第4列
	// 第二行
	gridLayout1->addWidget(new QLabel(QStringLiteral("类型:")), 1, 0, Qt::AlignLeft); // 第二行第1列
	gridLayout1->addWidget(type, 1, 1); // 第二行第2列
	gridLayout1->addWidget(new QLabel(QStringLiteral("出版社:")), 1, 2, Qt::AlignLeft); // 第二行第3列
	gridLayout1->addWidget(publish, 1, 3); // 第二行第4列
	// 第三行,这里使用了跨行和跨列
	gridLayout1->addWidget(new QLabel(QStringLiteral("封面图片:")), 2, 0, 2, 1, Qt::AlignLeft); // 第三行第1列
	photo->setAlignment(Qt::AlignCenter);
	photo->setFixedSize(this->width(), 200);
	photo->setStyleSheet("QLabel{background-color: #fff;}");
	gridLayout1->addWidget(photo, 2, 1, 1, 3); // 第三行第2,3,4列
	if (!sPhotoPath.isEmpty())
	{
		QPixmap pixmap(sPhotoPath);
		pixmap = pixmap.scaled(this->width(), 200, Qt::KeepAspectRatio); // 保持图像长宽比
		photo->setPixmap(pixmap);
	}
	m_BtnPhoto = new QPushButton(QStringLiteral("选择封面"));
	gridLayout1->addWidget(m_BtnPhoto, 3, 1, 1, 3); // 第四行第2,3,4列,第四行的第一列被上面的跨行占用了
	connect(m_BtnPhoto, &QPushButton::clicked, this, [=] { // 选择图片
		QString sPhotoName = QFileDialog::getOpenFileName(this, QStringLiteral("选择图片封面"), "/", "*.jpg *.png;; All Files(*.*)");
		sPhotoPath = sPhotoName;
		QPixmap pixmap(sPhotoName);
		pixmap = pixmap.scaled(this->width(), 200, Qt::KeepAspectRatio); // 保持图像长宽比
		photo->setPixmap(pixmap);
		});

	// 第五行
	m_BtnSave = new QPushButton(QStringLiteral("保存"));
	gridLayout1->addWidget(m_BtnSave, 4, 0, 1, 4, Qt::AlignBottom); // 第五行第1,2,3,4列


	ui.formLayout->setLayout(0, QFormLayout::SpanningRole, gridLayout1);
	ui.formLayout->setSpacing(10);
	ui.formLayout->setMargin(5);
	ui.formLayout->setRowWrapPolicy(QFormLayout::DontWrapRows);
	ui.formLayout->setFormAlignment(Qt::AlignLeft | Qt::AlignVCenter);


	connect(m_BtnSave, &QPushButton::clicked, this, &BookInfoDialog::slotBtnSaveClick);
}

BookInfoDialog::~BookInfoDialog()
{
	// TODO 执行close方法后会执行析构,这里需不需要删除字段的指针
}

void BookInfoDialog::slotBtnSaveClick()
{
	if (m_pBookInfoEntity == Q_NULLPTR)
	{
		m_pBookInfoEntity = new BookInfoEntity(); // 这个指针放到BookInfo中去释放
	}
	if (m_FormType == FORM::SAVE)
	{
		//bookInfo->setId(1);
	}
	m_pBookInfoEntity->setName(name->text());
	m_pBookInfoEntity->setAuthor(author->text());
	m_pBookInfoEntity->setType(type->currentText());
	m_pBookInfoEntity->setPublish(publish->currentText());
	m_pBookInfoEntity->setPhoto(sPhotoPath);

	// TODO 这里表单需做校验
	emit update(m_pBookInfoEntity, m_pIndex);
	this->close();
}

BookInfoDialog.ui

<?xml version="1.0" encoding="utf-8"?>
<ui version="4.0">
	<class>BookInfoDialogClass</class>
	<widget class="QWidget" name="BookInfoDialogClass">
		<property name="geometry">
			<rect>
				<x>0</x>
				<y>0</y>
				<width>400</width>
				<height>300</height>
			</rect>
		</property>
		<property name="windowTitle">
			<string>Form</string>
		</property>
		<layout class="QGridLayout" name="gridLayout">
			<item row="0" column="0">
				<layout class="QFormLayout" name="formLayout"/>
			</item>
		</layout>
	</widget>
	<resources/>
	<connections/>
</ui>

3:图书信息实体类

BookInfoEntity.h

#pragma once
#include "qstring.h"
#include "qdatetime.h"
/*
图书信息实体类
	1:用于在弹窗和图书信息类之间进行交互
*/
class BookInfoEntity
{
public:
	BookInfoEntity(int id, QString name, QString author, QString type, QString publish, QString photo, QDateTime intime);
	BookInfoEntity();
	~BookInfoEntity();

public:
	QString id();
	void setId(int id);
	QString name();
	void setName(QString name);
	QString author();
	void setAuthor(QString author);
	QString type();
	void setType(QString type);
	QString publish();
	void setPublish(QString publish);
	QString photo();
	void setPhoto(QString photo);
	QDateTime intime();
	void setIntime(QDateTime intime);
private:
	int m_id; // id
	QString m_name; // 图书名称
	QString m_author; // 作者
	QString m_type; // 类型
	QString m_publish; // 出版社
	QString m_photo; // 封面图片
	QDateTime m_intime; // 创建时间
};


BookInfoEntity.cpp

#include "BookInfoEntity.h"

BookInfoEntity::BookInfoEntity(int id, QString name, QString author, QString type, QString publish, QString photo, QDateTime intime)
{
	m_id = id;
	m_name = name;
	m_author = author;
	m_type = type;
	m_publish = publish;
	m_photo = photo;
	m_photo = photo;
	m_intime = intime;
}

BookInfoEntity::BookInfoEntity()
{
	m_id = -1;
}

BookInfoEntity::~BookInfoEntity()
{
}

QString BookInfoEntity::id()
{
	if (this->m_id == -1) return "";
	return QString::number(this->m_id);
}

void BookInfoEntity::setId(int id)
{
	this->m_id = id;
}

QString BookInfoEntity::name()
{
	return this->m_name;
}

void BookInfoEntity::setName(QString name)
{
	this->m_name = name;
}

QString BookInfoEntity::author()
{
	return this->m_author;
}

void BookInfoEntity::setAuthor(QString author)
{
	this->m_author = author;
}

QString BookInfoEntity::type()
{
	return this->m_type;
}

void BookInfoEntity::setType(QString type)
{
	this->m_type = type;
}

QString BookInfoEntity::publish()
{
	return this->m_publish;
}

void BookInfoEntity::setPublish(QString publish)
{
	this->m_publish = publish;
}

QString BookInfoEntity::photo()
{
	return this->m_photo;
}

void BookInfoEntity::setPhoto(QString photo)
{
	this->m_photo = photo;
}

QDateTime BookInfoEntity::intime()
{
	return this->m_intime;
}

void BookInfoEntity::setIntime(QDateTime intime)
{
	this->m_intime = intime;
}

4:自定义QSqlTableModel类

BookQSqlTableModel.h

#pragma once
#include "qsqltablemodel.h"
#include "qsqldatabase.h"
#include "qsqlquery.h"
#include "qdebug.h"
/*
自定义QSqlTableModel
	1:用于实现添加操作列、分页
	2:统一保存方法
*/
class BookQSqlTableModel : public QSqlTableModel
{
	Q_OBJECT
public:
	BookQSqlTableModel(QObject* parent = nullptr, QSqlDatabase db = QSqlDatabase());
	~BookQSqlTableModel();

public:
	int getColumnCount(); // 获取列数量
	bool selectPination(int page, int pageSize); // 分页查询
	int totalRowCount(); // 总条数
	bool submitAll(); // 统一操作,添加事务

private:
	// 自定义生成列
	QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; // 头数据
	int columnCount(const QModelIndex& parent = QModelIndex()) const override; // 列数量
	virtual QString selectStatement() const; // select方法执行前执行此方法,重写此方法为了加分页


	int nPage; // 当前页
	int nPageSize; // 每页显示条数
};


BookQSqlTableModel.cpp

#include "BookQSqlTableModel.h"

BookQSqlTableModel::BookQSqlTableModel(QObject* parent, QSqlDatabase db)
	: QSqlTableModel(parent, db)
{
	nPage = 0;
	nPageSize = 20;
}

BookQSqlTableModel::~BookQSqlTableModel()
{
}

int BookQSqlTableModel::getColumnCount()
{
	return QSqlTableModel::columnCount();
}

bool BookQSqlTableModel::selectPination(int page, int pageSize) // TODO这里得加状态,用于清空filter
{

	nPage = page;
	nPageSize = pageSize;
	return QSqlTableModel::select();
}

int BookQSqlTableModel::totalRowCount()
{
	QString filter = QSqlTableModel::filter();
	QString sql = QString("select count(1) from %1").arg(QSqlTableModel::tableName());
	if (!filter.isEmpty())
	{
		sql += " where " + filter;
	}
	QSqlQuery query;
	query.exec(sql);

	if (query.next()) {
		int totalCount = query.value(0).toInt();
		return totalCount;
	}
	return QSqlTableModel::rowCount();
}

QVariant BookQSqlTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
	if (section == QSqlTableModel::columnCount() && orientation == Qt::Horizontal && role == Qt::DisplayRole)
	{
		return QStringLiteral("操作");
	}
	return QSqlTableModel::headerData(section, orientation, role);
}

int BookQSqlTableModel::columnCount(const QModelIndex& parent) const
{
	return QSqlTableModel::columnCount() + 1; // 加一列
}

QString BookQSqlTableModel::selectStatement() const
{
	int startCount = (nPage - 1) * nPageSize;
	QString statement = QSqlTableModel::selectStatement() + " limit " + QString::number(startCount) + ", " + QString::number(nPageSize) + ";";
	return statement;
}

bool BookQSqlTableModel::submitAll()
{
	if (!QSqlTableModel::database().transaction()) return false; // 事务开启失败
	if (!QSqlTableModel::submitAll())
	{
		QSqlTableModel::database().rollback(); // 事务回滚
		return false;
	}
	return QSqlTableModel::database().commit(); // 提交事务
}

5:自定义操作列委托类(代理)

OperateColumnDelegate.h

#pragma once
#include "qstyleditemdelegate.h"
#include <QApplication>
#include <QMouseEvent>
#include "CommonDefine.h"
#include <QDebug>
/*
自定义委托类
	1:实现操作列添加按钮
	2:点击按钮发送信号
*/
class OperateColumnDelegate : public QStyledItemDelegate
{
	Q_OBJECT
public:
	OperateColumnDelegate(QStringList btnNameList, QWidget* parent = Q_NULLPTR);
	~OperateColumnDelegate();

signals:
	void clicked(QString text, QModelIndex index); // 操作列按钮点击事件

private:
	void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
	bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index) override;

private:
	QStringList m_BtnNameList; // 按钮名称列表
	int m_BtnState; // 按钮状态:点击、按下、释放
	int m_BtnNum; //按钮数量 
	int m_BtnSpace; // 按钮间隔
	QPoint m_MousePoint; // 鼠标位置
};


OperateColumnDelegate.cpp

#include "OperateColumnDelegate.h"

OperateColumnDelegate::OperateColumnDelegate(QStringList btnNameList, QWidget* parent)
    : QStyledItemDelegate(parent)
{
    m_BtnNameList = btnNameList;
    m_BtnNum = m_BtnNameList.count();
    m_BtnSpace = 4;
}

OperateColumnDelegate::~OperateColumnDelegate()
{
}

void OperateColumnDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
    Q_UNUSED(index)
        QStyleOptionViewItem optionItem(option);
    //根据按钮数量,确定按键长宽及坐标
    int buttonWidth = (optionItem.rect.width() - (m_BtnNum + 1) * m_BtnSpace) / m_BtnNum;
    int buttonHeight = optionItem.rect.height() - 2 * m_BtnSpace;
    int buttonLeft = optionItem.rect.left() + m_BtnSpace;
    int buttonTop = optionItem.rect.top() + m_BtnSpace;
    for (int i = 0; i < m_BtnNum; i++) {
        QStyleOptionButton styleButton; //绘制按键
        styleButton.rect = QRect(buttonLeft, buttonTop, buttonWidth, buttonHeight);
        buttonLeft += buttonWidth + m_BtnSpace;
        styleButton.text = m_BtnNameList.at(i);
        styleButton.state = QStyle::State_Enabled;  //设置按键状态
        if ((m_BtnState == 0) && (styleButton.rect.contains(m_MousePoint))) {
            styleButton.state |= QStyle::State_MouseOver;
        }
        else if ((m_BtnState == 1) && (styleButton.rect.contains(m_MousePoint))) {
            styleButton.state |= QStyle::State_Raised;

        }
        QApplication::style()->drawControl(QStyle::CE_PushButton, &styleButton, painter);
    }
}

bool OperateColumnDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index)
{
    Q_UNUSED(model)
    bool ret = false;
    m_BtnState = -1;
    QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
    m_MousePoint = mouseEvent->pos();

    int buttonWidth = (option.rect.width() - (m_BtnNum + 1) * m_BtnSpace) / m_BtnNum;
    int buttonHeight = option.rect.height() - 2 * m_BtnSpace;
    int buttonLeft = option.rect.left() + m_BtnSpace;
    int buttonTop = option.rect.top() + m_BtnSpace;
    for (int i = 0; i < m_BtnNum; i++) {
        QStyleOptionButton styleButton;
        styleButton.rect = QRect(buttonLeft, buttonTop, buttonWidth, buttonHeight);
        buttonLeft += buttonWidth + m_BtnSpace;
        if (!styleButton.rect.contains(mouseEvent->pos())) {
            continue;
        }
        else {
            switch (mouseEvent->type()) {
            case QEvent::MouseMove:
                ret = false;
                break;
            case QEvent::MouseButtonPress:
                m_BtnState = 0;
                ret = true;
                break;
            case QEvent::MouseButtonRelease:
                m_BtnState = 1;
                ret = true;
                emit clicked(m_BtnNameList.at(i), index);
                break;
            case QEvent::MouseButtonDblClick:
                ret = true;
                break;
            default:
                ret = false;
                break;
            }
        }

    }
    return ret;
}

6:常量类

CommonDefine.h

#pragma once
/*
常量类
*/
enum OPTCOLUMN // 操作列按钮
{
	OPT_EDIT, // 编辑
	OPT_SAVE,// 保存
	OPT_VIEW // 查看
};

enum FORM
{
	SAVE, // 新增
	EDIT // 编辑
};

四、结语

下一节【Qt实战教程第3篇 Qt+ffmpeg实现视频播放器,含ffmpeg视频编解码源码(本地文件、http、rtsp)、项目源码+注释】

  • 21
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现QTableWidget的分页功能,可以在QTableWidget下方添加一个QComboBox选择页码,以及上一页和下一页按钮进行翻页。具体步骤如下: 1. 创建QTableWidget和分页控制控件(QComboBox和QPushButton) ```cpp ui->tableWidget->setColumnCount(2); ui->tableWidget->setHorizontalHeaderLabels(QStringList() << "ID" << "Name"); // 分页控件 ui->comboBox->addItems(QStringList() << "10" << "20" << "50" << "100"); ui->pushButton_prev->setEnabled(false); ui->pushButton_next->setEnabled(false); ``` 2. 绑定分页控制事件,例如选择每页显示条数,上一页和下一页按钮事件 ```cpp connect(ui->comboBox, QOverload<int>::of(&QComboBox::activated), [=](int index) { pageSize = ui->comboBox->currentText().toInt(); currentPage = 1; refreshTable(); }); connect(ui->pushButton_prev, &QPushButton::clicked, [=]() { if (currentPage > 1) { currentPage--; refreshTable(); } }); connect(ui->pushButton_next, &QPushButton::clicked, [=]() { if (currentPage < totalPages) { currentPage++; refreshTable(); } }); ``` 3. 实现刷新表格的函数refreshTable() ```cpp void MainWindow::refreshTable() { int rowCount = ui->tableWidget->rowCount(); for (int i = rowCount - 1; i >= 0; i--) { ui->tableWidget->removeRow(i); } QSqlDatabase db = QSqlDatabase::database(); QSqlQuery query(db); query.exec(QString("SELECT COUNT(*) FROM mytable")); query.next(); int totalRows = query.value(0).toInt(); totalPages = (totalRows + pageSize - 1) / pageSize; ui->label_totalPages->setText(QString("of %1").arg(totalPages)); ui->pushButton_prev->setEnabled(currentPage > 1); ui->pushButton_next->setEnabled(currentPage < totalPages); int offset = (currentPage - 1) * pageSize; QString sql = QString("SELECT * FROM mytable LIMIT %1, %2").arg(offset).arg(pageSize); query.exec(sql); int row = 0; while (query.next()) { ui->tableWidget->insertRow(row); for (int col = 0; col < ui->tableWidget->columnCount(); col++) { QTableWidgetItem *item = new QTableWidgetItem(query.value(col).toString()); ui->tableWidget->setItem(row, col, item); } row++; } } ``` 4. 在需要刷新表格的地方调用refreshTable()函数即可 至于Qt数据库QTableWidget结合的增删改查操作,可以使用QSqlTableModel或者手动执行SQL查询语句,具体实现可以参考前面的回答。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值