在视频监控系统中也需要对日志记录进行查询显示,有时候查询到的记录并不能一页显示完,最好的做成翻页显示,如果所有记录都在一页显示通过滚动条查看,不是很符合用户习惯,比如搜索引擎的记录也都是分页显示,一页显示十几条记录。数据库的分页算法,大部分人都会选择用select查询结果limit关键字来过滤结果,其实未必所有数据库都支持limit关键字,对应Qt应用程序最常用的sqlite+mysql数据库都是用limit关键字以外,比如sqlserver数据库采用offset 4 rows fetch next 5 rows only这种写法来分页,PostgreSQL和人大金仓数据库kingbase采用的是limit+offset两个关键字组合(为何国产的人大金仓数据库和开源的PostgreSQL机制一样?后面查阅了资料才发现这家伙就是PostgreSQL改的), 而oracle的分页就更加复杂了(暂时没有找到好办法,就这个数据库分页最不好弄)。
数据库分页查询还算是比较基础的东西,一般人都会,稍微难的是对应UI上面有个分页控件,可以单击页码直接跳转到对应页码的数据显示,也可以直接输入页码,还可以单击上一页、下一页、第一页、末一页几个按钮切换,这个切换算法就有点讲究了,本人研究了网上各种翻页展示控件,大致可以总结为以下几点:
根据以上归纳总结,特意将分页功能拆分成一个类,专门负责分页查询返回数据等,将分页按钮展示UI拆分成一个类,负责外观展示,这样更灵活,类似MVC模式,分页功能类查询后触发对应的信号通知UI改变页码,分页按钮切换后发出对应的信号通知分页功能类执行对应的操作,完美搭配干活不累。
void NavPage::initStyle() { if (!this->showStyle) { return; } QStringList list; list << QString("*{font:%1px;}").arg(fontSize); list << QString("QLabel{qproperty-alignment:AlignCenter;}"); list << QString("QPushButton{border-radius:%1px;border:%2px solid %3;}") .arg(borderRadius).arg(borderWidth).arg(borderColor.name()); list << QString("QPushButton{background-color:%1;color:%2;}") .arg(normalBgColor.name()).arg(normalTextColor.name()); list << QString("QPushButton:hover{background-color:%1;color:%2;}") .arg(hoverBgColor.name()).arg(hoverTextColor.name()); list << QString("QPushButton:pressed{background-color:%1;color:%2;}") .arg(pressedBgColor.name()).arg(pressedTextColor.name()); list << QString("QPushButton:checked{background-color:%1;color:%2;}") .arg(checkedBgColor.name()).arg(checkedTextColor.name()); //禁用状态将颜色调淡 list << QString("QPushButton:disabled{color:rgba(%1,%2,%3,100);}") .arg(normalTextColor.red()).arg(normalTextColor.green()).arg(normalTextColor.blue()); this->setStyleSheet(list.join("")); } void NavPage::addBtnMove() { qDeleteAll(btnMove); btnMove.clear(); //先生成 第一页 上一页 下一页 末一页 四个按钮 QList<QString> listName; listName << "pageFirst" << "pagePrevious" << "pageNext" << "pageLast"; QList<int> listText; listText << 0xf049 << 0xf04a << 0xf04e << 0xf050; int count = listName.count(); for (int i = 0; i < count; ++i) { QPushButton *btn = new QPushButton; btn->setFont(iconFont); btn->setMinimumWidth(minWidth); btn->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); btn->setObjectName(listName.at(i)); btn->setText((QChar)listText.at(i)); btnMove << btn; } } void NavPage::addBtnPage() { qDeleteAll(btnPage); btnPage.clear(); //放入按钮分组自动形成选中排斥效果 QButtonGroup *btnGroup = new QButtonGroup(this); //生成中间页码按钮 for (int i = 1; i <= pageButtonCount; ++i) { QPushButton *btn = new QPushButton; connect(btn, SIGNAL(clicked(bool)), this, SLOT(btnClicked())); btn->setCheckable(true); btnGroup->addButton(btn); btn->setMinimumWidth(minWidth); btn->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); btn->setObjectName(QString("page%1").arg(i)); btn->setText(QString("%1").arg(i)); btnPage << btn; } btnPage.first()->setChecked(true); } void NavPage::addBtnAll() { //新建左右布局 if (layout == 0) { layout = new QHBoxLayout; layout->setContentsMargins(6, 6, 6, 6); this->setLayout(layout); } //实例化左侧右侧弹簧 if (spacerLeft == 0) { spacerLeft = new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum); } if (spacerRight == 0) { spacerRight = new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum); } //居中对齐或者右对齐则需要在左侧插入弹簧 if (navPosition == NavPosition_Center || navPosition == NavPosition_Right) { layout->addItem(spacerLeft); } //添加翻页提示信息 if (labInfo == 0) { labInfo = new QLabel; labInfo->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); layout->addWidget(labInfo); labInfo->setVisible(showLabInfo); } //添加 第一页 上一页 layout->addWidget(btnMove.at(0)); layout->addWidget(btnMove.at(1)); //循环添加 中间页码 foreach (QPushButton *btn, btnPage) { layout->addWidget(btn); } //添加 下一页 末一页 layout->addWidget(btnMove.at(2)); layout->addWidget(btnMove.at(3)); //添加 页码微调框 if (spinbox == 0) { spinbox = new QSpinBox; spinbox->setRange(1, 1); spinbox->setMinimumWidth(minWidth + 20); spinbox->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); } //添加 跳转页码按钮 if (btnGoPage == 0) { btnGoPage = new QPushButton; connect(btnGoPage, SIGNAL(clicked(bool)), this, SLOT(btnClicked())); btnGoPage->setMinimumWidth(minWidth + 20); btnGoPage->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); btnGoPage->setObjectName("pagego"); btnGoPage->setText("跳转"); } layout->addWidget(spinbox); spinbox->setVisible(showGoPage); layout->addWidget(btnGoPage); btnGoPage->setVisible(showGoPage); //居中对齐或者左对齐则需要在右侧插入弹簧 if (navPosition == NavPosition_Center || navPosition == NavPosition_Left) { layout->addItem(spacerRight); } btnAll.clear(); btnAll << btnMove << btnPage << btnGoPage; } void NavPage::btnClicked() { //直接跳转到对应页码 QPushButton *btn = (QPushButton *)sender(); if (btn == btnGoPage) { emit selectPage(spinbox->value()); } else { emit selectPage(btn->text().toInt()); } } void NavPage::receivePage(quint32 pageCurrent, quint32 pageCount, quint32 resultCount, quint32 resultCurrent) { //设置页码微调框范围 spinbox->setRange(1, pageCount); //根据页码总数显示隐藏按钮,比如只有1页的时候隐藏其他几个按钮 for (int i = 0; i < pageButtonCount; ++i) { btnPage.at(i)->setVisible(i < pageCount); btnPage.at(i)->setText(QString::number(i + 1)); } //重新设置页码按钮的页码 //计算出中间页码差值,起始页码和结束页码在差值基础上做加减 int offset = pageButtonCount / 2; //偶数页个数需要-1更精确 if (pageButtonCount % 2 == 0) { offset -= 1; } //总页数大于页码按钮个数才需要处理 if (pageCount > pageButtonCount) { int pageMin = pageCurrent - offset; int pageMax = pageCurrent + offset; //最小页码不能小于1 pageMin = pageMin < 1 ? 1 : pageMin; //最大页码不能超过总页码数 pageMax = pageMax > pageCount ? pageCount : pageMax; //差值不等于按钮个数重新修改最小值 7-2=5 但是确是6个按钮所以需要-1 while (pageMax - pageMin != (pageButtonCount - 1)) { if (pageMin == 1) { break; } pageMin--; } //qDebug() << pageButtonCount << offset << pageCurrent << pageMin << pageMax; int pageTemp = pageMin; for (int i = 0; i < pageButtonCount; ++i) { btnPage.at(i)->setText(QString::number(pageTemp)); pageTemp++; } } //重新选中当前页码按钮 foreach (QPushButton *btn, btnPage) { if (btn->text().toInt() == pageCurrent) { btn->setChecked(true); break; } } }