Qt 收藏夹书签管理

发布于:2025-06-29 ⋅ 阅读:(20) ⋅ 点赞:(0)

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("&amp;", "&");
                }
                else if (lst.size() == 3)
                {
                    level = lst[0].replace("&amp;", "&") + "/" + lst[1].replace("&amp;", "&");
                }
                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("&amp;", "&"));
                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("&amp;", "&"))
                    {
                        QTreeWidgetItem *item = new QTreeWidgetItem(top);
                        item->setText(0, lst[1].replace("&amp;", "&"));
                        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);
    }
}