Qt 收藏夹书签管理,对浏览器导出的标准书签html文件进行正则解析数据提取,对书签时间戳进行易读的格式转换,收藏夹层级树形显示,表格显示书签层级、名称、链接、日期的详细数据,右键菜单提供复制链接和浏览器打开的功能,支持按名称、链接、日期范围进行筛选,支持两个层级解析,层级内目录名不能重名。
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QFileDialog>
#include <QDir>
#include <QFileInfo>
#include <QFile>
#include <QTextStream>
#include <QDateTime>
#include <QRegularExpression>
#include <QRegularExpressionMatch>
#include <QTreeWidgetItem>
#include <QTableWidget>
#include <QMenu>
#include <QClipboard>
#include <QDesktopServices>
#include <QTimer>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
QFont font;
font.setPixelSize(16);
setFont(font);
setWindowTitle(QStringLiteral("收藏夹管理"));
ui->treeWidget->setHeaderHidden(true);
ui->treeWidget->setFixedWidth(200);
connect(ui->treeWidget, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, SLOT(showData(QTreeWidgetItem*,int)));
QStringList headers;
headers << QStringLiteral("层级") << QStringLiteral("名称") << QStringLiteral("链接") << QStringLiteral("日期");
initTableWidget(ui->tableWidget, headers);
ui->tableWidget->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->tableWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(menuDisplayed(QPoint)));
ui->dateEdit_start->setCalendarPopup(true);
ui->dateEdit_end->setCalendarPopup(true);
ui->dateEdit_start->setDate(QDate::currentDate().addDays(-7));
ui->dateEdit_end->setDate(QDate::currentDate());
ui->dateEdit_start->setMinimumDate(QDate(2000, 1, 1));
ui->dateEdit_end->setMinimumDate(QDate(2000, 1, 1));
ui->dateEdit_start->setMaximumDate(QDate::currentDate());
ui->dateEdit_end->setMaximumDate(QDate::currentDate());
ui->lineEdit_keyword->setFixedWidth(200);
connect(ui->lineEdit_keyword, SIGNAL(returnPressed()), this, SLOT(on_pushButton_keyword_clicked()));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::initTableWidget(QTableWidget *tableWidget, QStringList headers)
{
tableWidget->setColumnCount(headers.size());
tableWidget->setHorizontalHeaderLabels(headers);
tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
tableWidget->setSelectionMode(QAbstractItemView::SingleSelection);
tableWidget->verticalHeader()->setVisible(false);
tableWidget->horizontalHeader()->setHighlightSections(false);
tableWidget->setFrameShape(QFrame::NoFrame);
tableWidget->setShowGrid(true);
tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
tableWidget->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
tableWidget->horizontalHeader()->setSectionResizeMode(3, QHeaderView::ResizeToContents);
for (int i = 0; i < headers.size(); ++i)
{
tableWidget->horizontalHeaderItem(i)->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
}
tableWidget->setItemDelegate(new NoFocusDelegate());
}
void MainWindow::dataTableWidget(QTableWidget *tableWidget, QList<HREFData> dataLst)
{
if (dataLst.isEmpty())
{
return;
}
int rowCount = tableWidget->rowCount();
int newCount = dataLst.size();
if (newCount > rowCount)
{
for (int row = rowCount; row < newCount; ++row)
{
tableWidget->insertRow(row);
for (int i = 0; i < 4; ++i)
{
QTableWidgetItem *item = new QTableWidgetItem();
tableWidget->setItem(row, i, item);
}
}
}
else if (newCount < rowCount)
{
for (int row = rowCount; row > newCount; --row)
{
tableWidget->removeRow(row - 1);
}
}
for (int i = 0; i < newCount; ++i)
{
HREFData data = dataLst.at(i);
tableWidget->item(i, 0)->setText(data.Tag);
tableWidget->item(i, 1)->setText(data.Name);
tableWidget->item(i, 2)->setText(data.Href);
tableWidget->item(i, 3)->setText(data.Date);
tableWidget->item(i, 1)->setToolTip(data.Name);
tableWidget->item(i, 2)->setToolTip(data.Href);
}
}
void MainWindow::menuDisplayed(const QPoint &pos)
{
QTableWidgetItem *item = ui->tableWidget->itemAt(pos);
if (item != nullptr)
{
QMenu *menu = new QMenu(ui->tableWidget);
QAction *copyHref = new QAction(QStringLiteral("复制链接"), ui->tableWidget);
connect(copyHref, SIGNAL(triggered(bool)), this, SLOT(copyHref()));
QAction *openHref = new QAction(QStringLiteral("浏览器打开"), ui->tableWidget);
connect(openHref, SIGNAL(triggered(bool)), this, SLOT(openHref()));
menu->addAction(copyHref);
menu->addAction(openHref);
menu->exec(QCursor::pos());
delete copyHref;
delete openHref;
delete menu;
}
}
void MainWindow::copyHref()
{
QApplication::clipboard()->setText(ui->tableWidget->item(ui->tableWidget->currentRow(), 2)->text());
}
void MainWindow::openHref()
{
QDesktopServices::openUrl(QUrl(ui->tableWidget->item(ui->tableWidget->currentRow(), 2)->text()));
}
void MainWindow::on_pushButton_clicked()
{
QString filestr = QFileDialog::getOpenFileName(this, QStringLiteral("打开收藏夹文件"), ".", "*.html");
if (!filestr.isEmpty())
{
QFile file(filestr);
file.open(QIODevice::ReadOnly);
QString tt = file.readAll();
file.close();
QStringList lst = tt.split("\r\n");
static QRegularExpression reTag("\">(.+)</H3>");
static QRegularExpression reTagModified("LAST_MODIFIED=\"(.+)\"");
QString tagName;
QString tagModified;
QString level_1 = " <";
QString level_2 = " <";
mTagLst.clear();
foreach (QString str, lst)
{
if (str.contains("<H3"))
{
if (str.startsWith(level_1))
{
tagName = reTag.match(str).captured(1);
tagModified = QDateTime::fromTime_t(reTagModified.match(str).captured(1).toInt()).toString("yyyy-MM-dd hh:mm:ss");
mTagLst.append(QStringLiteral("%1/%2").arg(tagName).arg(tagModified));
}
else if (str.startsWith(level_2))
{
QString tmp = tagName;
mTagLst.append(QStringLiteral("%1/%2").arg(tmp + "/" + reTag.match(str).captured(1)).arg(QDateTime::fromTime_t(reTagModified.match(str).captured(1).toInt()).toString("yyyy-MM-dd hh:mm:ss")));
}
}
}
static QRegularExpression re(" ICON=\"(.+)\"");
static QRegularExpression reBookName("\">(.+)</A>");
static QRegularExpression reBookHref("HREF=\"(.+?)\"");
static QRegularExpression reBookDate("ADD_DATE=\"(.+?)\"");
QRegularExpressionMatch rematch;
mTagHrefMap.clear();
QString curTag;
int curLevel = 0;
QString level_0_href = " <";
QString level_1_href = " <";
QString level_2_href = " <";
foreach (QString tag, mTagLst)
{
QList<HREFData> tmp;
mTagHrefMap.insert(tag, tmp);
}
foreach (QString str, lst)
{
if (str.contains("HREF"))
{
rematch = re.match(str);
QString line = str;
if (rematch.hasMatch())
{
line = str.replace(rematch.captured(0), "");
}
HREFData data;
data.Name = reBookName.match(line).captured(1);
data.Href = reBookHref.match(line).captured(1);
data.Date = QDateTime::fromTime_t
(reBookDate.match(line).captured(1).toInt())
.toString("yyyy-MM-dd hh:mm:ss");
QString week;
switch (QDate::fromString(data.Date.split(" ")[0], "yyyy-MM-dd").dayOfWeek())
{
case Qt::Monday:
week = QStringLiteral("一");
break;
case Qt::Tuesday:
week = QStringLiteral("二");
break;
case Qt::Wednesday:
week = QStringLiteral("三");
break;
case Qt::Thursday:
week = QStringLiteral("四");
break;
case Qt::Friday:
week = QStringLiteral("五");
break;
case Qt::Saturday:
week = QStringLiteral("六");
break;
case Qt::Sunday:
week = QStringLiteral("日");
break;
}
data.Date = data.Date + "/" + week;
QString tag;
if (curLevel == 1)
{
if (str.startsWith(level_1_href))
{
tag = curTag;
}
}
else if (curLevel == 2)
{
if (str.startsWith(level_2_href))
{
tag = curTag;
}
else if (str.startsWith(level_1_href))
{
QString key = curTag.split("/")[0];
foreach (QString tmp, mTagLst)
{
lst = tmp.split("/");
if ((lst.size() == 2) && (lst[0] == key))
{
curTag = tmp;
curLevel = 1;
tag = curTag;
break;
}
}
}
}
if (str.startsWith(level_0_href))
{
tag = mTagLst[0];
}
QStringList lst = tag.split("/");
QString level;
if (lst.size() == 2)
{
level = lst[0].replace("&", "&");
}
else if (lst.size() == 3)
{
level = lst[0].replace("&", "&") + "/" + lst[1].replace("&", "&");
}
data.Tag = level;
QList<HREFData> hrefLst = mTagHrefMap.value(tag);
hrefLst.append(data);
mTagHrefMap.insert(tag, hrefLst);
}
else if (str.contains("<H3"))
{
tagName = reTag.match(str).captured(1);
foreach (QString tag, mTagLst)
{
QStringList lst = tag.split("/");
if (lst.size() == 2)
{
if (lst[0] == tagName)
{
curTag = tag;
curLevel = 1;
break;
}
}
else if (lst.size() == 3)
{
if (lst[1] == tagName)
{
curTag = tag;
curLevel = 2;
break;
}
}
}
}
}
for (int i = ui->treeWidget->topLevelItemCount() - 1; i >= 0; --i)
{
delete ui->treeWidget->takeTopLevelItem(i);
}
QList<QTreeWidgetItem *> topLst;
foreach (QString tag, mTagLst)
{
QStringList lst = tag.split("/");
if (lst.size() == 2)
{
QTreeWidgetItem *item = new QTreeWidgetItem;
item->setText(0, lst[0].replace("&", "&"));
item->setData(0, Qt::UserRole, tag);
item->setToolTip(0, lst[1]);
ui->treeWidget->insertTopLevelItem(ui->treeWidget->topLevelItemCount(), item);
topLst.append(item);
}
else if (lst.size() == 3)
{
foreach (QTreeWidgetItem *top, topLst)
{
if (top->text(0) == lst[0].replace("&", "&"))
{
QTreeWidgetItem *item = new QTreeWidgetItem(top);
item->setText(0, lst[1].replace("&", "&"));
item->setData(0, Qt::UserRole, tag);
item->setToolTip(0, lst[2]);
break;
}
}
}
}
ui->treeWidget->expandAll();
}
}
void MainWindow::showData(QTreeWidgetItem *item, int column)
{
Q_UNUSED(column);
QString tmp = item->data(0, Qt::UserRole).toString();
if (tmp != mCurTagData)
{
mCurTagData = tmp;
dataTableWidget(ui->tableWidget, mTagHrefMap.value(mCurTagData));
}
}
void MainWindow::on_pushButton_date_clicked()
{
if (ui->dateEdit_start->date() > ui->dateEdit_end->date() || mTagLst.isEmpty())
{
return;
}
QDate startDate = ui->dateEdit_start->date();
QDate endDate = ui->dateEdit_end->date();
QString tmp = QStringLiteral("%1;%2").arg(startDate.toString("yyyy-MM-dd")).arg(endDate.toString("yyyy-MM-dd"));
if (mCurTagData == tmp)
{
return;
}
QList<HREFData> retLst;
foreach (QString tag, mTagLst)
{
QList<HREFData> dataLst = mTagHrefMap.value(tag);
foreach (HREFData data, dataLst)
{
QDate date = QDate::fromString(data.Date.split(" ")[0], "yyyy-MM-dd");
if ((date >= startDate) && (date <= endDate))
{
retLst.append(data);
}
}
}
if (!retLst.isEmpty())
{
mCurTagData = tmp;
dataTableWidget(ui->tableWidget, retLst);
}
}
void MainWindow::on_pushButton_keyword_clicked()
{
if (ui->lineEdit_keyword->text().isEmpty() || mTagLst.isEmpty())
{
return;
}
QString keyword = ui->lineEdit_keyword->text();
if (mCurTagData == keyword)
{
return;
}
QList<HREFData> retLst;
foreach (QString tag, mTagLst)
{
QList<HREFData> dataLst = mTagHrefMap.value(tag);
foreach (HREFData data, dataLst)
{
if (data.Name.contains(keyword) || data.Href.contains(keyword))
{
retLst.append(data);
}
}
}
if (!retLst.isEmpty())
{
mCurTagData = keyword;
dataTableWidget(ui->tableWidget, retLst);
}
}