C++学习之路(十九)C++ 用Qt5实现一个工具箱(用SQLite数据库来管理粘贴板数据)- 示例代码拆分讲解

news2024/11/27 18:39:25

上篇文章,我们用 Qt5 实现了在小工具箱中添加了《点击按钮以新窗口打开功能面板》功能。今天我们把粘贴板功能用SQLite数据库来管理,用SQLite来实现增删改查。下面我们就来看看如何来规划开发这样的小功能并且添加到我们的工具箱中吧。

老规矩,先上图

在这里插入图片描述

需求功能概述(基于老代码做变更):

我们这次想要使用 SQLite 来存储和管理剪贴板的内容,那么就需要进行一些重构。

首先,需要创建一个 SQLite 数据库并设计一个表来存储剪贴板的内容。通常,表会有一个自增主键 ID,以及存储文本内容的字段。

ClipboardManager 类中,我们需要:

  1. 初始化 SQLite 数据库连接。
  2. 创建一个表来存储剪贴板内容。
  3. 修改 updateList 函数,将文本数据插入到 SQLite 数据库中,而不是直接在 QListWidget 中添加。
  4. 编写一个新的函数来从数据库中检索内容,并在 UI 中显示它们。
  5. 实现删除数据库中内容的功能,以及清空数据库的功能。

这里是一个大致的示例,说明如何使用 SQLite 进行存储和检索:

#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>

// 初始化数据库连接
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName("clipboard_data.db");
if (!db.open()) {
    qDebug() << "Error: Failed to open database:" << db.lastError().text();
    return;
}

// 创建表格
QSqlQuery query;
query.exec("CREATE TABLE IF NOT EXISTS clipboard_data (id INTEGER PRIMARY KEY AUTOINCREMENT, content TEXT)");

// 插入数据
void insertData(const QString& data) {
    QSqlQuery query;
    query.prepare("INSERT INTO clipboard_data (content) VALUES (:content)");
    query.bindValue(":content", data);
    query.exec();
}

// 从数据库中获取数据并更新 UI
void updateListFromDatabase(QListWidget* listWidget) {
    listWidget->clear();
    QSqlQuery query;
    query.exec("SELECT * FROM clipboard_data");
    while (query.next()) {
        QString data = query.value(1).toString(); // Assumes content is stored in the second column
        listWidget->addItem(data);
    }
}

// 清空数据库
void clearDatabase() {
    QSqlQuery query;
    query.exec("DELETE FROM clipboard_data");
}

需要将这些功能结合到 ClipboardManager 类中,并对原有的 updateListclearClipboard 函数进行修改,以便使用这些数据库操作。

此外,为了在用户添加或删除条目时更新数据库,我们还需要重写 QListWidget 上的事件函数。


核心实现代码:

// 新引入的库
#include <QSqlDatabase>
#include <QSqlError>
#include <QSqlQuery>

class ClipboardManager : public QWidget {
Q_OBJECT
public:
    explicit ClipboardManager(QWidget *parent = nullptr) : QWidget(parent) {

        // 初始化数据库连接
        QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
        db.setDatabaseName("clipboard_data.db");
        if (!db.open()) {
            qDebug() << "Error: Failed to open database:" << db.lastError().text();
            return;
        }

        // 创建表格
        QSqlQuery query;
        query.exec("CREATE TABLE IF NOT EXISTS clipboard_data (id INTEGER PRIMARY KEY AUTOINCREMENT, content TEXT)");

        auto *newWindow = new QDialog(this);
        newWindow->setWindowTitle("粘贴板记录");

        // 创建布局并将组件添加到布局中
        auto *layout = new QVBoxLayout(newWindow);
        listWidget = new QListWidget(newWindow);
        
        updateListFromDatabase(); // 从数据库更新列表
        
        auto *clearButton = new QPushButton("清空记录", newWindow);
        connect(clearButton, &QPushButton::clicked, this, &ClipboardManager::clearClipboard);
        layout->addWidget(listWidget);
        layout->addWidget(clearButton);
        
        connect(myApp->clipboard(), &QClipboard::dataChanged, this, &ClipboardManager::addClipboardData);

        newWindow->setLayout(layout);
        newWindow->exec();
    }

private slots:

    // 当剪贴板发生变化时,将新的数据添加到数据库
    void addClipboardData() {
        const QClipboard *clipboard = myApp->clipboard();
        const QMimeData *mimeData = clipboard->mimeData();

        if (mimeData->hasText()) {
            const QString clipboardText = mimeData->text();
            static QString lastClipboardData; // 添加静态变量用于记录最后一次剪贴板内容

            if (!clipboardText.isEmpty() && clipboardText != lastClipboardData) {
                insertData(clipboardText);
                lastClipboardData = clipboardText; // 更新最后一次剪贴板内容
            }
        }
        updateListFromDatabase(); // 更新列表显示最新数据
    }

    // 清空剪贴板和数据库
    void clearClipboard() {
        myApp->clipboard()->clear();
        clearDatabase();
        listWidget->clear();
    }

private:
    QListWidget *listWidget;
    static void insertData(const QString& data) {
        QSqlQuery query;
        query.prepare("INSERT INTO clipboard_data (content) VALUES (:content)");
        query.bindValue(":content", data);
        query.exec();
    }

    void updateListFromDatabase() {
        listWidget->clear();
        QSqlQuery query;
        query.exec("SELECT * FROM clipboard_data");
        while (query.next()) {
            QString data = query.value(1).toString(); // Assumes content is stored in the second column
            listWidget->addItem(data);
        }
    }

    static void clearDatabase() {
        QSqlQuery query;
        query.exec("DELETE FROM clipboard_data");
    }
};

核心代码逻辑讲解

在本次核心代码中我们使用了 SQLite 数据库来存储(粘贴板)剪贴板内容。这段代码中的 ClipboardManager 类继承自 QWidget,它创建了一个包含列表和清空按钮的对话框,并使用数据库存储剪贴板内容。

它的工作原理如下:

  1. 在构造函数中,初始化了 SQLite 数据库连接,并创建了名为 clipboard_data.db 的数据库文件用于存储剪贴板内容。
  2. 创建了对话框及其布局,并将列表和清空按钮添加到布局中。
  3. 使用 addClipboardData() 槽函数捕获剪贴板内容变化事件,并将新的数据添加到数据库。
  4. 使用 updateListFromDatabase() 函数从数据库中读取剪贴板内容,更新列表显示最新数据。
  5. clearClipboard() 函数清空了剪贴板和数据库,并清空了列表显示。

这个类提供了基本的数据库交互功能,可以用于管理剪贴板内容,并在数据库中存储和显示。

有关模块引入需要提前做的工作

首先我们的 CMakeLists.txt 文件已经包含了 Widgets 模块,但并没有包含 Sql 模块。要使用数据库相关的类和功能,就需要添加 Sql 模块到我们的 CMakeLists.txt 文件中。

尝试添加 Sql 模块到 REQUIRED_LIBS 列表中,然后再次构建我们的项目,这样可以确保数据库相关的类能够被正确链接和使用。我们可以修改 REQUIRED_LIBSREQUIRED_LIBS_QUALIFIED 的设置,如下所示:

set(REQUIRED_LIBS Core Gui Widgets Sql)
set(REQUIRED_LIBS_QUALIFIED Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Sql)

这样修改之后,就可以正常的使用Sql 模块了。


完整 CMakeLists.txt 文件内容
cmake_minimum_required(VERSION 3.17)
project(qt_tools)

set(CMAKE_CXX_STANDARD 14)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)

set(QT_VERSION 5)
# 本次变更 ---start
set(REQUIRED_LIBS Core Gui Widgets Sql)
set(REQUIRED_LIBS_QUALIFIED Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Sql)
# 本次变更 ---end
# 将 resources.qrc 添加到项目中
set(QT_RESOURCES resources.qrc)

add_executable(${PROJECT_NAME} ${QT_RESOURCES} main.cpp)

if(NOT CMAKE_PREFIX_PATH)
    message(WARNING "CMAKE_PREFIX_PATH is not defined, you may need to set it "
                    "(-DCMAKE_PREFIX_PATH=\"path/to/Qt/lib/cmake\" or -DCMAKE_PREFIX_PATH=/usr/include/{host}/qt{version}/ on Ubuntu)")
endif()

find_package(Qt${QT_VERSION} COMPONENTS ${REQUIRED_LIBS} REQUIRED)
target_link_libraries(${PROJECT_NAME} ${REQUIRED_LIBS_QUALIFIED})
SQLite的数据文件在哪?

SQLite 数据库文件通常保存在我们指定的位置或者程序运行的当前目录。在上面的代码中,数据库文件名被设置为 clipboard_data.db

db.setDatabaseName("clipboard_data.db");

如果没有指定路径,数据库文件会默认存储在程序运行的当前工作目录中。通常情况下,这个目录是我们执行可执行文件的位置,比如在项目的构建目录中。

我们可以在代码中指定具体的路径,将数据库文件保存在你想要的位置,例如:

QString path = QDir::homePath() + "/MyAppData"; // 将数据库保存在用户的主目录下的 MyAppData 文件夹中
QDir().mkpath(path); // 创建目录(如果不存在的话)
db.setDatabaseName(path + "/clipboard_data.db");

这将在用户的主目录下创建一个名为 MyAppData 的文件夹,并将数据库文件保存在其中。我们也可以根据需要修改文件路径,将数据库保存在你希望的位置。

本次我们就不做指定路径存放了,就保持默认放在运行的目录中就行。
在这里插入图片描述


核心代码讲解完毕,下面是完整版代码,复制到本地跑一跑吧~


#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QVBoxLayout>
#include <QMessageBox>
#include <QListWidget>
#include <QClipboard>
#include <QMimeData>
#include <QTextEdit>
#include <QJsonDocument>
#include <QJsonObject>
#include <QDateTime>
#include <QLabel>
#include <QComboBox>
#include <QLineEdit>
#include <QXmlStreamReader>
#include <QFile>
#include <QFileDialog>
#include <QScreen>
#include <QCursor>
#include <QPoint>
#include <QGuiApplication>
#include <QPixmap>
#include <QThread>
#include <QMouseEvent>
#include <QTimer>
#include <QSystemTrayIcon>
#include <QMenu>
#include <QDebug>
#include <QSqlDatabase>
#include <QSqlError>
#include <QSqlQuery>

#define myApp (dynamic_cast<QApplication *>(QCoreApplication::instance()))

class ClickableLabel : public QLabel {
Q_OBJECT

public:
    explicit ClickableLabel(QWidget *parent = nullptr) : QLabel(parent) {}

signals:
    void clicked();

protected:
    void mousePressEvent(QMouseEvent *event) override {
        if (event->button() == Qt::LeftButton) {
            emit clicked();
        }
    }
};

class PlaceholderTextEdit : public QWidget {
Q_OBJECT
public:
    explicit PlaceholderTextEdit(const QString &placeholderText, QWidget *parent = nullptr) : QWidget(parent) {
        auto *layout = new QVBoxLayout(this);

        placeholderLabel = new QLabel(placeholderText, this);
        layout->addWidget(placeholderLabel);

        textEdit = new QTextEdit(this);
        layout->addWidget(textEdit);

        connect(textEdit, &QTextEdit::textChanged, this, &PlaceholderTextEdit::checkPlaceholder);
        checkPlaceholder(); // 初始检查

        setLayout(layout);
    }

    QString getText() const {
        return textEdit->toPlainText();
    }

private slots:

    void checkPlaceholder() {
        placeholderLabel->setVisible(textEdit->toPlainText().isEmpty());
    }

private:
    QLabel *placeholderLabel;
    QTextEdit *textEdit;
};

class ColorConverter : public QWidget {
Q_OBJECT
public:
    explicit ColorConverter(QWidget *parent = nullptr) : QWidget(parent) {
        // 在构造函数中初始化定时器
        colorTimer = new QTimer(this);
        colorTimer->setInterval(100); // 设置定时器间隔为100毫秒
        connect(colorTimer, &QTimer::timeout, this, &ColorConverter::extractScreenColor);

        auto *layout = new QVBoxLayout(this);

        // 创建颜色值转换部分的输入框和按钮
        colorInput = new QLineEdit(this);
        layout->addWidget(colorInput);

        convertButton = new QPushButton("16进制转RGB", this);
        connect(convertButton, &QPushButton::clicked, this, &ColorConverter::convertToRgbColor);
        layout->addWidget(convertButton);

        convertButton = new QPushButton("RGB转16进制", this);
        connect(convertButton, &QPushButton::clicked, this, &ColorConverter::convertToHexColor);
        layout->addWidget(convertButton);

        convertedColorOutput = new QTextEdit(this);
        convertedColorOutput->setReadOnly(true);
        layout->addWidget(convertedColorOutput);

        // 创建屏幕颜色提取器部分的按钮和显示框
        extractColorButton = new QPushButton("开始提取屏幕颜色", this);
        connect(extractColorButton, &QPushButton::clicked, this, &ColorConverter::startColorCapture);
        layout->addWidget(extractColorButton);

        // 创建用于存放颜色块和颜色输出的网格布局
        auto *colorBlockLayout = new QGridLayout();

        // 创建颜色输出
        screenColorOutput = new ClickableLabel(this);
        screenColorOutput->setFixedSize(200, 90);
        screenColorOutput->setAlignment(Qt::AlignCenter);
        screenColorOutput->setStyleSheet("border: 1px solid #ddd");
        // 这里是触发点击事件后的复制到剪切板的函数调用
        connect(screenColorOutput, &ClickableLabel::clicked, this, &ColorConverter::copyColorToClipboard);


        colorBlockLayout->addWidget(screenColorOutput, 0, 0); // 将颜色输出放在网格布局的左上角

        // 创建颜色块
        screenColorBlock = new QLabel(this);
        screenColorBlock->setFixedSize(30, 30); // 设置颜色块大小
        screenColorBlock->setStyleSheet("border: 1px solid #ddd");
        colorBlockLayout->addWidget(screenColorBlock, 0, 0, Qt::AlignTop | Qt::AlignRight); // 将颜色块放在颜色输出的右上角

        layout->addLayout(colorBlockLayout);

        setLayout(layout);
    }

protected slots:

    void convertToHexColor() {
        // 从输入框获取颜色值
        QString colorText = colorInput->text();
        // 按逗号分隔RGB文本
        QStringList rgbValues = colorText.split(',');
        if (rgbValues.size() == 3) {
            // 获取R、G和B的值
            int red = rgbValues[0].toInt();
            int green = rgbValues[1].toInt();
            int blue = rgbValues[2].toInt();

            // 将RGB值转换为16进制字符串
            QString hexRed = QString("%1").arg(red, 2, 16, QChar('0')).toUpper();
            QString hexGreen = QString("%1").arg(green, 2, 16, QChar('0')).toUpper();
            QString hexBlue = QString("%1").arg(blue, 2, 16, QChar('0')).toUpper();

            // 构建16进制颜色代码文本
            QString hexText = QString("#%1%2%3").arg(hexRed).arg(hexGreen).arg(hexBlue);
            convertedColorOutput->setText(hexText);
        } else {
            // 处理无效颜色输入的情况
            convertedColorOutput->setText("Invalid color input");
        }
    }

    void convertToRgbColor() {
        // 从输入框获取颜色值
        QString colorText = colorInput->text();
        QColor color(colorText);
        if (color.isValid()) {
            // 获取16进制颜色码的红、绿、蓝通道值
            int red = color.red();
            int green = color.green();
            int blue = color.blue();
            // 构建RGB颜色代码文本
            QString outputText = QString("RGB(%1, %2, %3)").arg(red).arg(green).arg(blue);
            convertedColorOutput->setText(outputText);
        } else {
            // 处理无效颜色输入的情况
            convertedColorOutput->setText("Invalid color input");
        }
    }

    // 开始捕获颜色的槽函数
    void startColorCapture() {
        extractColorButton->setText("请开始移动鼠标 按Tab键终止取色");
        colorTimer->start();
    }

    void extractScreenColor() {
        QPoint cursorPos = QCursor::pos();
        QScreen *screen = QGuiApplication::screenAt(cursorPos);

        if (screen) {
            QPixmap screenshot = screen->grabWindow(0, cursorPos.x(), cursorPos.y(), 1, 1);
            if (!screenshot.isNull()) {
                QColor color = screenshot.toImage().pixel(0, 0);
                QString colorText = QString("#%1").arg(color.name().mid(1));
                screenColorOutput->setText(colorText);

                QString styleSheet = QString("background-color: %1; border: 1px solid #ddd;").arg(color.name());
                screenColorBlock->setStyleSheet(styleSheet);
            } else {
                screenColorOutput->setText("Failed to grab screen color");
            }
        } else {
            screenColorOutput->setText("No screen found at the cursor position");
        }
    }

    void copyColorToClipboard() {
        // 从screenColorOutput获取颜色信息
        QString colorText = screenColorOutput->text();

        // 复制颜色值到剪切板
        QApplication::clipboard()->setText(colorText);

        // 弹出提示框显示复制的颜色值
        QMessageBox::information(this, "颜色已复制", QString("已复制颜色:") + colorText);
    }

public:
    // 停止捕获颜色的槽函数
    void stopColorCapture() {
        colorTimer->stop();
        extractColorButton->setText("开始提取屏幕颜色");
    }
private:
    QLineEdit *colorInput;
    QPushButton *convertButton;
    QTextEdit *convertedColorOutput;
    QPushButton *extractColorButton;
    ClickableLabel *screenColorOutput;
    QLabel *screenColorBlock;
    QTimer *colorTimer;
};

class Base64ImageConverter : public QWidget {
Q_OBJECT
public:
    explicit Base64ImageConverter(QWidget *parent = nullptr) : QWidget(parent) {
        auto *newWindow = new QDialog(this);
        newWindow->setWindowTitle("Base64图片预览");

        auto *layout = new QVBoxLayout(this);

        selectImageButton = new QPushButton("选择图片", this);
        connect(selectImageButton, &QPushButton::clicked, this, &Base64ImageConverter::selectImage);
        layout->addWidget(selectImageButton);

        imagePathTextEdit = new QTextEdit(this);
        imagePathTextEdit->setReadOnly(true);
        layout->addWidget(imagePathTextEdit);

        convertToBase64Button = new QPushButton("转换为Base64编码", this);
        connect(convertToBase64Button, &QPushButton::clicked, this, &Base64ImageConverter::convertToBase64);
        layout->addWidget(convertToBase64Button);

        base64OutputTextEdit = new QTextEdit(this);
        base64OutputTextEdit->setAcceptRichText(false);
        layout->addWidget(base64OutputTextEdit);

        convertToImagePreviewButton = new QPushButton("Base64编码转图片预览", this);
        connect(convertToImagePreviewButton, &QPushButton::clicked, this, &Base64ImageConverter::convertToImagePreview);
        layout->addWidget(convertToImagePreviewButton);

        imagePreviewLabel = new QLabel(this);
        imagePreviewLabel->setFixedHeight(200); // 设置图片高度为 200
        layout->addWidget(imagePreviewLabel);

        newWindow->setLayout(layout);
        newWindow->exec();
    }

private slots:

    void selectImage() {
        // 创建文件对话框用于选择图片文件
        QString imagePath = QFileDialog::getOpenFileName(this, tr("选择图片"), "", tr("Images (*.png *.jpg *.bmp)"));

        // 如果用户选择了图片文件,则显示文件路径
        if (!imagePath.isEmpty()) {
            imagePathTextEdit->setText(imagePath);
            selectedImagePath = imagePath;
        }
    }

    void convertToBase64() {
        if (selectedImagePath.isEmpty()) {
            QMessageBox::warning(this, "警告", "请先选择图片");
            return;
        }

        // 读取选定图片文件的内容
        QFile file(selectedImagePath);
        if (file.open(QIODevice::ReadOnly)) {
            QByteArray imageData = file.readAll();
            QString base64Data = imageData.toBase64();

            base64OutputTextEdit->setText(base64Data);
        } else {
            QMessageBox::critical(this, "错误", "无法读取图片文件");
        }
    }

    void convertToImagePreview() {
        QString base64Data = base64OutputTextEdit->toPlainText().toUtf8();

        if (base64Data.isEmpty()) {
            QMessageBox::warning(this, "警告", "请先转换为Base64编码");
            return;
        }

        // 从Base64数据创建QImage并显示在标签中
        QByteArray byteArray = QByteArray::fromBase64(base64Data.toUtf8());
        QImage image;
        image.loadFromData(byteArray);

        if (!image.isNull()) {
            // 水平居中对齐
            imagePreviewLabel->setAlignment(Qt::AlignHCenter);
            // 缩放图片到 200x200
            QPixmap pixmap = QPixmap::fromImage(image.scaled(200, 200, Qt::KeepAspectRatio));
            imagePreviewLabel->setPixmap(pixmap);
        } else {
            QMessageBox::critical(this, "错误", "无法加载图片");
        }
    }

private:
    QPushButton *selectImageButton;
    QTextEdit *imagePathTextEdit;
    QPushButton *convertToBase64Button;
    QTextEdit *base64OutputTextEdit;
    QPushButton *convertToImagePreviewButton;
    QLabel *imagePreviewLabel;
    QString selectedImagePath;
};

class Base64Converter : public QWidget {
Q_OBJECT
public:
    explicit Base64Converter(QWidget *parent = nullptr) : QWidget(parent) {
        auto *newWindow = new QDialog(this);
        newWindow->setWindowTitle("Base64加解密");

        auto *layout = new QVBoxLayout(this);

        inputTextEdit = new QTextEdit(this);
        layout->addWidget(inputTextEdit);

        encryptButton = new QPushButton("加密", this);
        connect(encryptButton, &QPushButton::clicked, this, &Base64Converter::encryptText);
        layout->addWidget(encryptButton);

        decryptButton = new QPushButton("解密", this);
        connect(decryptButton, &QPushButton::clicked, this, &Base64Converter::decryptText);
        layout->addWidget(decryptButton);

        outputTextEdit = new QTextEdit(this);
        outputTextEdit->setReadOnly(true);
        layout->addWidget(outputTextEdit);

        newWindow->setLayout(layout);
        newWindow->exec();
    }

private slots:

    void encryptText() {
        QString inputText = inputTextEdit->toPlainText().toUtf8();
        QByteArray byteArray = inputText.toUtf8().toBase64();
        outputTextEdit->setText(byteArray);
    }

    void decryptText() {
        QString inputText = inputTextEdit->toPlainText();
        QByteArray byteArray = QByteArray::fromBase64(inputText.toUtf8());
        outputTextEdit->setText(byteArray);
    }

private:
    QTextEdit *inputTextEdit;
    QTextEdit *outputTextEdit;
    QPushButton *encryptButton;
    QPushButton *decryptButton;
};

class XmlFormatter : public QWidget {
Q_OBJECT
public:
    explicit XmlFormatter(QWidget *parent = nullptr) : QWidget(parent) {
        auto *newWindow = new QDialog(this);
        newWindow->setWindowTitle("XML格式化");

        auto *layout = new QVBoxLayout(this);

        inputTextEdit = new QTextEdit(this);
        layout->addWidget(inputTextEdit);

        formatButton = new QPushButton("格式化 XML", this);
        connect(formatButton, &QPushButton::clicked, this, &XmlFormatter::formatXml);
        layout->addWidget(formatButton);

        outputTextEdit = new QTextEdit(this);
        outputTextEdit->setReadOnly(true);
        layout->addWidget(outputTextEdit);

        newWindow->setLayout(layout);
        newWindow->exec();
    }

private slots:

    void formatXml() {
        // 获取输入的XML文本
        QString inputText = inputTextEdit->toPlainText();

        if (!inputText.isEmpty()) {
            // 使用 QXmlStreamReader 读取输入的 XML 文本
            QXmlStreamReader reader(inputText);
            QString formattedXml;

            int indentLevel = 0;
            while (!reader.atEnd() && !reader.hasError()) {
                if (reader.isStartElement()) {
                    formattedXml += getIndent(indentLevel) + "<" + reader.name().toString() + ">\n";
                    ++indentLevel;
                } else if (reader.isEndElement()) {
                    --indentLevel;
                    formattedXml += getIndent(indentLevel) + "</" + reader.name().toString() + ">\n";
                } else if (reader.isCharacters() && !reader.isWhitespace()) {
                    formattedXml += getIndent(indentLevel) + reader.text().toString() + "\n";
                }
                reader.readNext();
            }

            if (reader.hasError()) {
                outputTextEdit->setText("XML 解析错误:" + reader.errorString());
            } else {
                outputTextEdit->setText(formattedXml);
            }
        } else {
            // 如果输入为空,则清空输出区域
            outputTextEdit->clear();
        }
    }

    static QString getIndent(int level) {
        return QString(level * 4, ' '); // 4空格作为缩进
    }

private:
    QTextEdit *inputTextEdit;
    QPushButton *formatButton;
    QTextEdit *outputTextEdit;
};

class NumberBaseConverter : public QWidget {
Q_OBJECT
public:
    explicit NumberBaseConverter(QWidget *parent = nullptr) : QWidget(parent) {
        auto *newWindow = new QDialog(this);
        newWindow->setWindowTitle("进制转换");

        auto *layout = new QVBoxLayout(this);
        // 横向排列的输入框和选择框
        auto *horizontalLayout = new QHBoxLayout();

        // 创建输入框并添加到水平布局
        inputLineEdit = new QLineEdit(this);
        horizontalLayout->addWidget(inputLineEdit);

        // 连接输入框的文本变化信号到槽函数
        connect(inputLineEdit, &QLineEdit::textChanged, this, &NumberBaseConverter::convertNumber);

        // 创建下拉选择框并添加到水平布局
        baseComboBox = new QComboBox(this);
        baseComboBox->addItem("二进制");
        baseComboBox->addItem("八进制");
        baseComboBox->addItem("十进制");
        baseComboBox->addItem("十六进制");
        horizontalLayout->addWidget(baseComboBox);

        // 将水平布局添加到垂直布局
        layout->addLayout(horizontalLayout);

        // 创建四个只读的输出框并添加到垂直布局
        binaryOutput = new QLineEdit(this);
        binaryOutput->setReadOnly(true);
        layout->addWidget(binaryOutput);

        octalOutput = new QLineEdit(this);
        octalOutput->setReadOnly(true);
        layout->addWidget(octalOutput);

        decimalOutput = new QLineEdit(this);
        decimalOutput->setReadOnly(true);
        layout->addWidget(decimalOutput);

        hexOutput = new QLineEdit(this);
        hexOutput->setReadOnly(true);
        layout->addWidget(hexOutput);

        // 连接下拉选择框的选择变化信号到槽函数,并进行初始转换
        connect(baseComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
                &NumberBaseConverter::convertNumber);
        convertNumber(); // 初始转换

        newWindow->setLayout(layout);
        newWindow->exec();
    }

private slots:

    void convertNumber() {
        QString inputText = inputLineEdit->text();
        bool ok;
        int base = baseComboBox->currentIndex();

        if (!inputText.isEmpty()) {
            // 根据所选进制进行转换
            if (base == 0) {
                int number = inputText.toInt(&ok, 2); // 二进制转十进制
                if (ok) {
                    binaryOutput->setText(inputText);
                    octalOutput->setText(QString::number(number, 8));
                    decimalOutput->setText(QString::number(number));
                    hexOutput->setText(QString::number(number, 16).toUpper());
                }
            } else if (base == 1) {
                // 八进制转换
                int number = inputText.toInt(&ok, 8);
                if (ok) {
                    binaryOutput->setText(QString::number(number, 2));
                    octalOutput->setText(inputText);
                    decimalOutput->setText(QString::number(number));
                    hexOutput->setText(QString::number(number, 16).toUpper());
                }
            } else if (base == 2) {
                // 十进制转换
                int number = inputText.toInt(&ok, 10);
                if (ok) {
                    binaryOutput->setText(QString::number(number, 2));
                    octalOutput->setText(QString::number(number, 8));
                    decimalOutput->setText(inputText);
                    hexOutput->setText(QString::number(number, 16).toUpper());
                }
            } else if (base == 3) {
                // 十六进制转换
                int number = inputText.toInt(&ok, 16);
                if (ok) {
                    binaryOutput->setText(QString::number(number, 2));
                    octalOutput->setText(QString::number(number, 8));
                    decimalOutput->setText(QString::number(number));
                    hexOutput->setText(inputText.toUpper());
                }
            }
        } else {
            // 如果输入为空,则清空输出
            binaryOutput->clear();
            octalOutput->clear();
            decimalOutput->clear();
            hexOutput->clear();
        }
    }

private:
    QLineEdit *inputLineEdit;
    QComboBox *baseComboBox;
    QLineEdit *binaryOutput;
    QLineEdit *octalOutput;
    QLineEdit *decimalOutput;
    QLineEdit *hexOutput;
};

class DateTimeTimestampConverter : public QWidget {
Q_OBJECT
public:
    explicit DateTimeTimestampConverter(QWidget *parent = nullptr) : QWidget(parent) {
        auto *newWindow = new QDialog(this);
        newWindow->setWindowTitle("时间戳转换");

        auto *layout = new QVBoxLayout(this);

        inputTextEdit = new PlaceholderTextEdit("在此输入日期时间或时间戳", this);
        layout->addWidget(inputTextEdit);

        convertToTimestampButton = new QPushButton("日期时间转时间戳", this);
        connect(convertToTimestampButton, &QPushButton::clicked, this, &DateTimeTimestampConverter::convertToTimestamp);
        layout->addWidget(convertToTimestampButton);

        convertToDateTimeButton = new QPushButton("时间戳转日期时间", this);
        connect(convertToDateTimeButton, &QPushButton::clicked, this, &DateTimeTimestampConverter::convertToDateTime);
        layout->addWidget(convertToDateTimeButton);

        outputTextEdit = new QTextEdit(this);
        outputTextEdit->setReadOnly(true);
        layout->addWidget(outputTextEdit);

        newWindow->setLayout(layout);
        newWindow->exec();
    }

private slots:

    void convertToTimestamp() {
        QString inputText = inputTextEdit->getText();
        QDateTime dateTime = QDateTime::fromString(inputText, "yyyy-MM-dd HH:mm:ss");

        if (dateTime.isValid()) {
            qint64 timestamp = dateTime.toSecsSinceEpoch();
            outputTextEdit->setText(QString::number(timestamp));
        } else {
            outputTextEdit->setText("无效的日期时间格式!");
        }
    }

    void convertToDateTime() {
        QString inputText = inputTextEdit->getText();
        bool ok;
        qint64 timestamp = inputText.toLongLong(&ok);

        if (ok) {
            QDateTime dateTime;
            dateTime.setSecsSinceEpoch(timestamp);

            outputTextEdit->setText("时间戳 " + inputText + " 对应的日期时间是:" + dateTime.toString("yyyy-MM-dd HH:mm:ss"));
        } else {
            outputTextEdit->setText("无效的时间戳格式!");
        }
    }

private:
    QPushButton *convertToTimestampButton;
    QPushButton *convertToDateTimeButton;
    QTextEdit *outputTextEdit;
    PlaceholderTextEdit *inputTextEdit;
};

class JsonFormatter : public QWidget {
Q_OBJECT
public:
    explicit JsonFormatter(QWidget *parent = nullptr) : QWidget(parent) {
        auto *newWindow = new QDialog(this);
        newWindow->setWindowTitle("JSON格式化");

        auto *layout = new QVBoxLayout(this);

        inputTextEdit = new QTextEdit(this);
        layout->addWidget(inputTextEdit);

        formatButton = new QPushButton("格式化", this);
        connect(formatButton, &QPushButton::clicked, this, &JsonFormatter::formatJson);
        layout->addWidget(formatButton);

        outputTextEdit = new QTextEdit(this);
        outputTextEdit->setReadOnly(true);
        layout->addWidget(outputTextEdit);

        newWindow->setLayout(layout);
        newWindow->exec();
    }

private slots:

    void formatJson() {
        QString inputText = inputTextEdit->toPlainText();
        QJsonParseError error{};
        QJsonDocument jsonDoc = QJsonDocument::fromJson(inputText.toUtf8(), &error);

        if (error.error != QJsonParseError::NoError) {
            outputTextEdit->setText("JSON 解析错误:" + error.errorString());
            return;
        }

        QJsonObject jsonObj = jsonDoc.object();
        QJsonDocument formattedJson(jsonObj);

        outputTextEdit->setText(formattedJson.toJson());
    }

private:
    QTextEdit *inputTextEdit;
    QPushButton *formatButton;
    QTextEdit *outputTextEdit;
};

class ClipboardManager : public QWidget {
Q_OBJECT
public:
    explicit ClipboardManager(QWidget *parent = nullptr) : QWidget(parent) {

        // 初始化数据库连接
        QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
        db.setDatabaseName("clipboard_data.db");
        if (!db.open()) {
            qDebug() << "Error: Failed to open database:" << db.lastError().text();
            return;
        }

        // 创建表格
        QSqlQuery query;
        query.exec("CREATE TABLE IF NOT EXISTS clipboard_data (id INTEGER PRIMARY KEY AUTOINCREMENT, content TEXT)");

        auto *newWindow = new QDialog(this);
        newWindow->setWindowTitle("粘贴板记录");

        // 创建布局并将组件添加到布局中
        auto *layout = new QVBoxLayout(newWindow);
        listWidget = new QListWidget(newWindow);
        updateListFromDatabase(); // 从数据库更新列表
        auto *clearButton = new QPushButton("清空记录", newWindow);
        connect(clearButton, &QPushButton::clicked, this, &ClipboardManager::clearClipboard);
        layout->addWidget(listWidget);
        layout->addWidget(clearButton);
        connect(myApp->clipboard(), &QClipboard::dataChanged, this, &ClipboardManager::addClipboardData);

        newWindow->setLayout(layout);
        newWindow->exec();
    }

private slots:

    // 当剪贴板发生变化时,将新的数据添加到数据库
    void addClipboardData() {
        const QClipboard *clipboard = myApp->clipboard();
        const QMimeData *mimeData = clipboard->mimeData();

        if (mimeData->hasText()) {
            const QString clipboardText = mimeData->text();
            static QString lastClipboardData; // 添加静态变量用于记录最后一次剪贴板内容

            if (!clipboardText.isEmpty() && clipboardText != lastClipboardData) {
                insertData(clipboardText);
                lastClipboardData = clipboardText; // 更新最后一次剪贴板内容
            }
        }
        updateListFromDatabase(); // 更新列表显示最新数据
    }

    // 清空剪贴板和数据库
    void clearClipboard() {
        myApp->clipboard()->clear();
        clearDatabase();
        listWidget->clear();
    }

private:
    QListWidget *listWidget;
    static void insertData(const QString& data) {
        QSqlQuery query;
        query.prepare("INSERT INTO clipboard_data (content) VALUES (:content)");
        query.bindValue(":content", data);
        query.exec();
    }

    void updateListFromDatabase() {
        listWidget->clear();
        QSqlQuery query;
        query.exec("SELECT * FROM clipboard_data");
        while (query.next()) {
            QString data = query.value(1).toString(); // Assumes content is stored in the second column
            listWidget->addItem(data);
        }
    }

    static void clearDatabase() {
        QSqlQuery query;
        query.exec("DELETE FROM clipboard_data");
    }
};

class MyMainWindow : public QWidget {
Q_OBJECT
public:
    explicit MyMainWindow(QWidget *parent = nullptr) : QWidget(parent) {
        setWindowTitle("天河工具箱");
        auto *layout = new QVBoxLayout(this);

        auto *clipboardButton = new QPushButton("粘贴板记录");
        clipboardButton->setObjectName("clipboardButton");
        connect(clipboardButton, &QPushButton::clicked, this, &MyMainWindow::showClipboardManager);
        layout->addWidget(clipboardButton);

        auto *jsonFormatButton = new QPushButton("JSON格式化");
        jsonFormatButton->setObjectName("jsonFormatButton");
        connect(jsonFormatButton, &QPushButton::clicked, this, &MyMainWindow::showJsonFormatter);
        layout->addWidget(jsonFormatButton);

        auto *timestampConverterButton = new QPushButton("时间戳转换");
        timestampConverterButton->setObjectName("timestampConverterButton");
        connect(timestampConverterButton, &QPushButton::clicked, this, &MyMainWindow::showDateTimeTimestampConverter);
        layout->addWidget(timestampConverterButton);

        auto *numberBaseConverterButton = new QPushButton("进制转换");
        numberBaseConverterButton->setObjectName("numberBaseConverterButton");
        connect(numberBaseConverterButton, &QPushButton::clicked, this, &MyMainWindow::showNumberBaseConverter);
        layout->addWidget(numberBaseConverterButton);

        auto *xmlFormatterButton = new QPushButton("XML格式化");
        xmlFormatterButton->setObjectName("xmlFormatterButton");
        connect(xmlFormatterButton, &QPushButton::clicked, this, &MyMainWindow::showXmlFormatter);
        layout->addWidget(xmlFormatterButton);

        auto *base64ConverterButton = new QPushButton("Base64加解密");
        base64ConverterButton->setObjectName("base64ConverterButton");
        connect(base64ConverterButton, &QPushButton::clicked, this, &MyMainWindow::showBase64Converter);
        layout->addWidget(base64ConverterButton);

        auto *base64ImageConverterButton = new QPushButton("Base64图片预览");
        base64ImageConverterButton->setObjectName("base64ImageConverterButton");
        connect(base64ImageConverterButton, &QPushButton::clicked, this, &MyMainWindow::showBase64ImageConverter);
        layout->addWidget(base64ImageConverterButton);

        auto *colorConverterButton = new QPushButton("色值提取转换");
        colorConverterButton->setObjectName("colorConverterButton");
        connect(colorConverterButton, &QPushButton::clicked, this, &MyMainWindow::showColorConverter);
        colorConverter = new ColorConverter(this);
        colorConverter->hide();
        layout->addWidget(colorConverter);
        layout->addWidget(colorConverterButton);

        setLayout(layout);
    }

protected:
    ColorConverter *colorConverter{};
    // 重写关闭事件处理函数
    void closeEvent(QCloseEvent *event) override {
        if (this->isVisible()) {
            // 隐藏主窗口,而不是退出程序
            this->hide();
            event->ignore(); // 忽略关闭事件,防止程序退出
        }
    }
    void keyPressEvent(QKeyEvent *event) override {
        qDebug() << event->modifiers();
        if (event->modifiers() == Qt::ControlModifier && event->key() == Qt::Key_C) {
            colorConverter->stopColorCapture();
            // 在这里执行按下 Control + C 键的操作
            qDebug() << "Control + C pressed";
        } else {
            // 如果有其他键盘事件,交给基类处理
            QWidget::keyPressEvent(event);
        }
    }
public slots:

    void showClipboardManager() {
        new ClipboardManager(this);
    }

    void showJsonFormatter() {
        new JsonFormatter(this);
    }

    void showDateTimeTimestampConverter() {
        new DateTimeTimestampConverter(this);
    }

    void showNumberBaseConverter() {
        new NumberBaseConverter(this);
    }

    void showXmlFormatter() {
        new XmlFormatter(this);
    }

    void showBase64Converter() {
        new Base64Converter(this);
    }

    void showBase64ImageConverter() {
        new Base64ImageConverter(this);
    }

    void showColorConverter() {
        auto *curButton = findChild<QPushButton *>("colorConverterButton");
        if (colorConverter->isHidden()) {
            if (curButton) {
                curButton->setText("隐藏色值提取转换功能");
            }
            colorConverter->show();
        } else {
            if (curButton) {
                curButton->setText("显示色值提取转换功能");
            }
            colorConverter->hide();
        }
    }

    void showPanel() {
        this->show(); // 显示主窗口
        this->raise(); // 将窗口置于其他窗口上方
    }
};

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);

    MyMainWindow mainWindow;
    // 创建托盘图标
    auto* trayIcon = new QSystemTrayIcon(QIcon(":/res/icon/logo.png"), &mainWindow);
    trayIcon->setToolTip("天河工具箱");

    // 创建托盘菜单
    auto* trayMenu = new QMenu(&mainWindow);

    auto* showPanelAction = new QAction("显示面板", &mainWindow);
    QObject::connect(showPanelAction, &QAction::triggered, &mainWindow, &MyMainWindow::showPanel);
    trayMenu->addAction(showPanelAction);

    auto* quitAction = new QAction("退出", &mainWindow);
    QObject::connect(quitAction, &QAction::triggered, &a, &QApplication::quit);
    trayMenu->addAction(quitAction);

    trayIcon->setContextMenu(trayMenu);
    trayIcon->show();

    mainWindow.show();

    return QApplication::exec();
}

#include "main.moc"


好了,这次的完整代码就这些了,抓紧动手开始吧~ 💪🏻

想要了解这个小工具是如何发展到现在这个地步的话,就看看往期文章吧~,记得要多跑一跑代码。如果大家有什么想要的功能或者想问的问题的话,就在评论区留言吧。Thanks♪(・ω・)ノ

往期文章一览

C++学习之路(一)什么是C++?如何循序渐进的学习C++?【纯干货】

C++学习之路(二)C++如何实现一个超简单的学生信息管理系统?C++示例和小项目实例

C++学习之路(三)解析讲解超简单学生信息管理系统代码知识点 - 《根据实例学知识》

C++学习之路(四)C++ 实现简单的待办事项列表命令行应用 - 示例代码拆分讲解

C++学习之路(五)C++ 实现简单的文件管理系统命令行应用 - 示例代码拆分讲解

C++学习之路(六)C++ 实现简单的工具箱系统命令行应用 - 示例代码拆分讲解

C++学习之路(七)C++ 实现简单的Qt界面(消息弹框、按钮点击事件监听)- 示例代码拆分讲解

C++学习之路(八)C++ 用Qt5实现一个工具箱(增加一个粘贴板记录管理功能)- 示例代码拆分讲解

C++学习之路(九)C++ 用Qt5实现一个工具箱(增加一个JSON数据格式化功能)- 示例代码拆分讲解

C++学习之路(十)C++ 用Qt5实现一个工具箱(增加一个时间戳转换功能)- 示例代码拆分讲解

C++学习之路(十一)C++ 用Qt5实现一个工具箱(增加一个进制转换器功能)- 示例代码拆分讲解

C++学习之路(十二)C++ 用Qt5实现一个工具箱(增加一个XML文本格式化功能)- 示例代码拆分讲解

C++学习之路(十三)C++ 用Qt5实现一个工具箱(增加一个Base64加解密功能)- 示例代码拆分讲解

C++学习之路(十四)C++ 用Qt5实现一个工具箱(增加一个Base64图片编码预览功能)- 示例代码拆分讲解

C++学习之路(十五)C++ 用Qt5实现一个工具箱(增加16进制颜色码转换和屏幕颜色提取功能)- 示例代码拆分讲解

C++学习之路(十六)C++ 用Qt5实现一个工具箱(为屏幕颜色提取功能增加一个点击复制的功能)- 示例代码拆分讲解

C++学习之路(十七)C++ 用Qt5实现一个工具箱(增加托盘图标并且增加显示和退出菜单)- 示例代码拆分讲解

C++学习之路(十八)C++ 用Qt5实现一个工具箱(点击按钮以新窗口打开功能面板)- 示例代码拆分讲解


好了~ 本文就到这里了,感谢您的阅读,每天还有更多的实例学习文章等着你 🎆。别忘了点赞、收藏~ Thanks♪(・ω・)ノ 🍇。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1286860.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

30个Python小游戏,小白练手,我都能玩一天【内附源码】

给大家带来30个 Python 小游戏&#xff0c;一定要收藏&#xff01; 文末获取完整代码 有手就行 1、吃金币 import os import cfg import sys import pygame import random from modules import *游戏初始化 def initGame():# 初始化pygame, 设置展示窗口pygame.init()screen…

全面解析修复msvcr120.dll缺失问题的方法,msvcr120.dll丢失的原因

在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中最常见的就是“msvcr120.dll丢失”。这个错误通常会导致某些程序无法正常运行&#xff0c;给用户带来很大的困扰。那么&#xff0c;当我们遇到这个问题时&#xff0c;应该如何修复呢&#xff1f;本文…

忽略python运行出现的大量警告

添加以下代码即可 import warnings warnings.filterwarnings(ignore)

使用rust slint开发桌面应用

安装QT5&#xff0c;过程省略 安装rust&#xff0c;过程省略 创建工程 cargo new slint_demo 在cargo.toml添加依赖 [dependencies] slint "1.1.1" [build-dependencies] slint-build "1.1.1" 创建build.rs fn main() {slint_build::compile(&quo…

DFT(离散傅里叶变换)的通俗理解

本文包含了博主对离散傅里叶变换&#xff0c;负频率&#xff0c;实信号与复信号频谱的理解&#xff0c;如有不妥&#xff0c;欢迎各位批评指正与讨论。 文章目录 DFT的理解信号的频谱实信号的频谱复信号的频谱 DFT的理解 傅里叶变换是一种将信号从时域转换到频域的数学工具。…

使用消息队列遇到的问题—kafka

目录 1 分区2 消费者3 Kafka 如何保证消息的消费顺序&#xff1f;3.1 方案一3.2 方案二 在项目中使用kafka作为消息队列&#xff0c;核心工作是创建生产者—包装数据&#xff1b;创建消费者----包装数据。 欠缺一些思考&#xff0c;特此梳理项目中使用kafka遇到的一些问题和解决…

探索Selenium的规避检测策略

Selenium之规避检测 背景 ​ 目前很多大网站有对selenium采取了监测机制。在正常情况下我们用浏览器访问相关网站的window.navigator.webdriver的值为 undefined或者为false。而使用selenium访问则该值为true。我们如何伪装&#xff0c;防止被检测出来呢&#xff1f; ​ 这是…

【Maven】依赖管理

1. 依赖管理 1.1 依赖配置 依赖&#xff1a;指当前项目运行所需要的jar包。一个项目中可以引入多个依赖。 依赖引入步骤&#xff1a;在pom.xml中编写标签&#xff0c;在标签中使用引入坐标&#xff0c;定义坐标的 groupId、artifactId、version&#xff0c;最后点击刷新&…

FluxMQ—2.0.8版本更新内容

FluxMQ—2.0.8版本更新内容 前言 FLuxMQ是一款基于java开发&#xff0c;支持无限设备连接的云原生分布式物联网接入平台。FluxMQ基于Netty开发&#xff0c;底层采用Reactor3反应堆模型&#xff0c;具备低延迟&#xff0c;高吞吐量&#xff0c;千万、亿级别设备连接&#xff1…

WIN10下解决HIVE 初始化MYSQL表报错:Unknown version specified for initialization

今天本地WINDOWS装HIVE&#xff0c;走到最后一步初始化数据库死活不通过&#xff1a; D:\hive\hive-rel-release-3.1.3\bin\ext>hive --service schematool -dbType mysql -initSchema --verbose SLF4J: Class path contains multiple SLF4J bindings. SLF4J: Found bind…

基于JSDoc实现TypeScript类型安全的实践报告

在FEDay 2023中我讲了《从JS到TS无缝迁移的实践报告》【视频在这里在这里】&#xff0c;是将一个传统的JS项目&#xff08;mochajs/mocha&#xff09;迁移到TypeScript环境的全程。其中提到了一件事情&#xff0c;就是“可以通过JSDoc/TSDoc来生成.d.ts”&#xff0c;从而实现T…

【C语言:自定义类型(结构体、位段、共用体、枚举)】

文章目录 1.结构体1.1什么是结构体1.2结构体类型声明1.3结构体变量的定义和初始化1.4结构体的访问 2.结构体对齐2.1如何对齐2.2为什么存在内存对齐&#xff1f; 3.结构体实现位段3.1什么是位段3.2位段的内存分配3.3位段的跨平台问题3.4位段的应用3.5位段使用注意事项 4.联合体4…

全国规模最大!玻色量子加入算力并网行动!

8月19日&#xff0c;在2023中国算力大会主论坛上&#xff0c;中国移动携手多个超算中心、智算中心&#xff0c;以及云服务商&#xff0c;共同发起全国规模最大的“百川”算力并网行动&#xff0c;打造算力类型最全、规模最大、覆盖最广的“百川”算力并网平台&#xff0c;加快推…

提高Idea编码速度和插件自用推荐

非常推荐 Easy Javadoc 一款注释生成器&#xff0c;很好使~免费&#xff0c;配合使用腾讯、百度之类的翻译免费额度完全够用了&#xff0c;印象中是50万字符每月。下图是使用快捷键生成的注释&#xff08;我采用鼠标侧面按键&#xff0c;随手一按很方便&#xff09; Chinese …

HTTP会话技术---Cookie、Session和Token介绍及它们在JavaWeb中的使用

当涉及到Web应用程序的身份验证和状态管理时&#xff0c;我们通常会使用到Cookie、Session和Token这些会话技术。下面是对它们的介绍&#xff0c;并在JavaWeb中的示例 Cookie&#xff08;HTTP Cookie&#xff09; Cookie是一种存储在用户浏览器中的小型文本文件&#xff0c;由…

360公司-2019校招笔试-Windows开发工程师客观题合集解析

360公司-2019校招笔试-Windows开发工程师客观题合集 API无法实现进程间数据的相互传递是PostMessage2.以下代码执行后,it的数据为(异常) std::list<int> temp; std::list<int>::iterator it = temp.begin(); it = --it; 3.API在失败时的返回值跟其他不一样是 …

OSPF浅析

一、预习&#xff1a; 1、优点&#xff1a; 是一种典型的链路状态路由协议&#xff0c;协议号89&#xff0c;把大型网络分隔为多个较小、可管理的单元&#xff1a;Area a.减少LSA泛洪范围&#xff0c;有效地把拓朴变化 控制在区域内&#xff0c;达到网络优化的目的…

混音编曲软件tudio One 6.5.1 保姆级安装教程

根据软件大数据显示De-Esser驯服人声嘶嘶声和其他高频声音&#xff0c;和其他 Studio One 中新的去实体插件一样高效且直观易用&#xff0c;使用“收听”按钮查找有问题的频率&#xff0c;然后使用相关的旋钮和 S-Mon 功能拨入 S-Reduce 量即可。实际上我们可以这样讲工作流和协…

消费数据可视化大屏,助力金融机构智慧运维

在今天的数字化时代&#xff0c;消费数据的可视化已经成为了一种重要的趋势。通过将消费数据以图表、图像等形式展现出来&#xff0c;可以帮助我们更直观地了解消费者的行为和趋势。同时&#xff0c;这也为企业提供了更多的分析和决策依据。无论是针对市场营销策略的制定&#…

如何搭建eureka-server

在Spring Cloud项目的pom文件中添加eureka-server的starter依赖坐标 <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.0.0 http://ma…