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

[Qt学习笔记]Qt实现程序单例运行,再次点击后弹出界面 在实际工程项目中,软件在打开情况下,再次打开软件会出现打开多个软件界面的情况,进而找出与下位机交互的错误,对于工程项目是不允许此类情况出现,所以在软件运行时必须保证该软件是单例运行,在运行状况下再次打开时进行提示或弹出之前的软件界面。 阅读详情

上篇文章,我们用 Qt5 实现了在小工具箱中添加了《增加托盘图标并且增加显示和退出菜单》功能。今天我们把按钮打开功能的方式改一改,让点击按钮以新窗口打开功能面板。下面我们就来看看如何来规划开发这样的小功能并且添加到我们的工具箱中吧。

老规矩,先上图

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

需求功能概述:

当用户点击主窗口的按钮时,需要打开一个新的窗口来显示特定功能的面板。这个新窗口应该包含用户所需功能的相关控件和布局。主要需求包括:

  1. 按钮触发:点击主窗口上的按钮触发事件,以在新窗口中显示对应的功能面板。
  2. 新窗口显示:打开新窗口时,应该显示相关的功能面板及其布局,可能包括输入框、按钮等控件。
  3. 功能面板:新窗口需要根据点击的按钮来显示相应的功能面板,这些面板可能涉及不同的逻辑和交互元素。
  4. 事件捕获:在新窗口中可能需要捕获和处理键盘事件、鼠标事件或其他用户交互事件,以响应用户的操作。

在实现过程中,需要创建一个新的窗口(例如使用 QDialog 或 QMainWindow)并设置相应的布局和控件,以便在新窗口中显示用户所需的功能面板。此外,需要确保在新窗口中的事件处理函数中捕获并处理相应的事件,以提供所需的功能和交互体验。


核心实现代码:

class ClipboardManager : public QWidget {
Q_OBJECT
public:
    explicit ClipboardManager(QWidget *parent = nullptr) : QWidget(parent) {
        auto *newWindow = new QDialog(this);
        newWindow->setWindowTitle("粘贴板记录");

        // ...

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

private slots:
	// ...

private:
    // ...
};

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);

        setLayout(layout);
    }

protected:
    // ...
public slots:

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

    // ...
};

核心代码逻辑讲解

这次变更主要包括两个类的修改:MyMainWindow 和 ClipboardManager。

MyMainWindow 类:
  • 构造函数变更:主窗口中修改了按钮名字(举例 “粘贴板记录” 按钮)。
  • 槽函数变更:更改了以前的 toggle 函数名为 showXxxx 槽函数,当点击“粘贴板记录”按钮时,showClipboardManager 槽函数会创建一个新的 ClipboardManager 对象,从而打开一个新的对话框来显示粘贴板记录。
ClipboardManager 类:
  • 构造函数变更:在构造函数中创建了一个新的对话框(QDialog),用于显示粘贴板记录。
  • 对话框显示:调用 exec() 或者 show() 函数显示对话框,并在其中展示粘贴板记录(在 // ... 部分的代码)。

这次变更主要实现了点击按钮时打开一个新的对话框来展示功能。按钮点击触发了新窗口的生成和显示。


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

#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>

#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) {
        auto *newWindow = new QDialog(this);
        newWindow->setWindowTitle("粘贴板记录");

        // 创建布局并将组件添加到布局中
        auto *layout = new QVBoxLayout(newWindow);
        listWidget = new QListWidget(newWindow);
        updateList(); // 初始更新列表
        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::updateList);

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

private slots:

    void updateList() {
        const QClipboard *clipboard = myApp->clipboard();
        const QMimeData *mimeData = clipboard->mimeData();

        if (mimeData->hasText()) {
            const QString clipboardText = mimeData->text();

            if (!clipboardText.isEmpty()) {
                listWidget->addItem(clipboardText);
            }
        }
    }

    void clearClipboard() {
        myApp->clipboard()->clear();
        listWidget->clear();
    }

private:
    QListWidget *listWidget;
};

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实现一个工具箱(增加托盘图标并且增加显示和退出菜单)- 示例代码拆分讲解


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

Qt 主窗口点击按钮 弹出另一个自定义窗口 1.问题重述这个其实是对C++类的理解程度有一定要求。 窗口1定义为类A,窗口2定义为类B,问题:如何点击窗口1中的按钮,打开窗口2.2.问题思考 把类理解成数据类型,把窗口理解成类的实例。 只要在类A的定义中添加类B的定义和对象,编写适当的槽函数处理这个对象即可.3.程序代码 (1)头文件#ifndef GLOBAL_H #define GLOBAL_H#include <QMainWin 阅读详情

相关推荐

Qt入门—— 使用按钮打开新的窗口

Qt入门—— 使用按钮打开新的窗口 - 优快云博客 https://blog.youkuaiyun.com/qq_35451572/article/details/80363908

QT使用按钮打开新窗口

QT点击按钮弹出新窗口(新建窗口类的形式)

shenlaoli的博客 2万+

从零开始搭建GD32F103CBT6开发环境:VSCode配置与实战技巧

本文详细介绍了如何从零开始为国产MCU GD32F103CBT6搭建高效的VSCode开发环境。内容涵盖工具链安装、VSCode核心插件配置、工程目录结构规划、关键配置文件编写以及Makefile实战,旨在帮助开发者摆脱传统IDE束缚,利用现代化工具链提升嵌入式开发效率。

weixin_29158413的博客 448

Qt学习——点击按钮打开一个新窗口

基础目标:创建两个按钮,open和close,点击open打开一个新窗口,点击close,关闭窗口。进阶目标:创建一个按钮,点击一下打开新窗口,且按钮文本变成close,再点击一下关闭按钮。知识点:1.如何创建窗口和按钮2. 信号与槽的连接3. lambda函数的使用4.如何调出按钮的文本

LS_20220905的博客 7055

[Vue][面试]谈谈你对MVC、MVP和MVVM的理解

谈谈你对MVC、MVP和MVVM的理解 答题思路:此题涉及知识点很多,很难说清楚,说透彻,因为MVC,MVP这些我们前端程序员自己甚至都没用过。但是恰恰反映了前端这些年从无到有, 从有到优的变迁过程,因此沿此思路回答将十分清除。 Web1.0时代 在web1.0时代,并没有前端的概念。开发一个web应用多数采用ASP.NET/Java/PHP编写,项目通常由多个aspx/jsp/php文件构成,每个文件中同时包含了HTML、CSS、JavaScript、C#/Java/PHP代码, 系统整体架构可能如下图所

你好,欢迎光临! 723

Arnold Denoise流程

参考:https://support.solidangle.com/display/ARP/Removing+Noise+Workflow Arnold生来不是一个以单帧渲染速度见长的渲染器,但这并不能作为一个简单场景渲染时间长达几个小时的借口。 个人认为高品质生产级渲染结果中,Unbiased renderer在渲染时最耗计算时间的其实就是降噪,以至于降噪专门成了一门灯光师必备的技术

机器猫的CG空间 5084

C++实战:中药方剂管理平台开发全解析

在软件工程实践中,数据管理平台是连接业务逻辑与信息技术的核心枢纽,其设计原理关乎系统的稳定性、可扩展性与数据处理效率。通过关系型数据库建模、业务逻辑层封装与高性能算法实现,此类平台能够将复杂的领域知识转化为结构化的数字资产,从而创造显著的技术价值。在医疗信息化、传统行业数字化转型等应用场景中,平台需要处理诸如多维度查询、关联分析与规则校验等核心需求。本文聚焦于一个具体案例——基于C++与Qt框架开发的中药方剂综合管理平台,详细阐述了如何利用**MySQL数据库设计**与**智能查询算法**,解决药材标准化、

weixin_30876945的博客 334

51单片机也能玩转信号发生器?手把手教你用DAC0832和LM358输出四种标准波形

本文详细介绍了如何使用51单片机和DAC0832芯片搭配LM358运放,自制低成本信号发生器,实现正弦波、矩形波、三角波和锯齿波四种标准波形的输出。从硬件选型、电路设计到编程实现,手把手教你完成这一实用电子项目,总成本不到50元,适合电子爱好者和学生实践。

weixin_30530939的博客 552

3.Linux基础开发⼯具【由浅入深-Linux】

在 Windows 环境中,我们习惯了双击 或 文件并一路点击“下一步”来安装软件。但在 Linux 的世界里,软件的分发和安装理念有着本质的不同。Linux 强调权限控制、模块化和依赖管理。为了帮您构建完整的 Linux 系统管理知识体系,我们可以将 Linux 下安装软件的方式归纳为五大主流阵营。这是 Linux 下最正统、也是日常使用频率最高的方式。它又细分为“在线仓库安装”和“本地包安装”两种场景。1. 在线仓库安装(如同手机自带的 App Store) 操作系统维护着一个庞大的软件云端仓库。通

插花弄玉的博客 759

逆向分析第一步:如何从HEX文件‘还原’出单片机的原始程序与数据?

本文详细解析了如何从HEX文件逆向分析出单片机的原始程序与数据,涵盖了固件逆向的底层逻辑与实践。通过介绍HEX文件的结构、内存映像重建技术以及专业工具链的使用,帮助读者掌握从十六进制数据到可执行代码的完整流程,适用于嵌入式系统开发和逆向工程领域。

weixin_29745609的博客 317

STM32F4串口接收ATGM336H GPS数据,手把手教你解析NMEA协议并转换经纬度格式

本文详细介绍了如何使用STM32F4开发板通过串口接收ATGM336H GPS模块的NMEA协议数据,并解析GNRMC语句获取经纬度信息。内容涵盖硬件连接、NMEA协议深度解析、数据校验实现以及经纬度格式转换的数学原理与代码实现,为嵌入式开发者提供完整的GPS数据处理解决方案。

weixin_42522575的博客 497

QT通过点击按钮弹出新的窗口(新建弹出式窗口)

目录第一步:新建QT设计师界面类第二步:为按钮事件设置信号与槽 需求说明:主窗口为mainWindow,主窗口上有一个按钮,通过点击按钮能弹出一个新的窗口。 第一步:新建QT设计师界面类 右击最顶层的工程文件夹,在弹出的选项里选择Add New(有的版本为添加新文件)。 选择Qt–>Qt设计师界面类,点击Choose。 默认选择Widget,点击下一步。 在这里设置类名,我设置为newWindow,路径默认即可,点击下一步。 这里是汇总信息,默认添加到当前项目,点击完成按钮即可。 完成后Q

jkjijijkv的博客 7万+

银河麒麟Arm64 v10 sp1系统QT5.12.8离线环境配置全攻略

本文详细介绍了在银河麒麟Arm64 v10 sp1系统上配置QT5.12.8离线开发环境的完整流程。从安装包获取、依赖检查到环境变量配置,逐步指导开发者解决飞腾处理器架构下的常见问题,包括OpenGL库冲突、中文乱码处理等,特别适合军工、政务等需要离线部署的场景。

weixin_26899659的博客 507

QT开发笔记之跳转并打开另一个页面

在我们开始写逻辑之前,首先应该新创建一个页面的文件a)右键单击项目名出来如下页面,选择Add New…b)选择 Qt 设计师界面类c)任意选择一个页面,本文选择Widget,一直下一步,点击确定,即可生成新的页面文件。d)项目下会新生成一个.ui/.cpp/.h文件。

m0_49507996的博客 8725

ROS2与Gazebo协同实现多机器人避障仿真

本文详细介绍了如何利用ROS2与Gazebo协同搭建多机器人避障仿真环境。通过创建SDF机器人模型、编写多机器人启动文件以及集成基础避障算法,构建了一个低成本、高效率的虚拟测试平台,为AGV编队、机器人集群等项目的算法验证与迭代提供了完整解决方案。

weixin_29065659的博客 295

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

QT使用同一按钮实现打开/关闭新窗口

【代码】QT使用同一按钮实现打开/关闭新窗口。

罗伯特祥的博客 1724

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

QT C++实现点击按键弹出窗口并显示图片/视频|多窗口应用程序的设计和开发

一般用vs2019开发,创建Qt Widgets Application之后,会自带一个和项目名同名的主窗口类。这个时候需要手动创建另外一个,即新窗口类。要在Qt C++中实现点击按钮后显示新窗口,并在新窗口中显示由主程序生成的图片,你需要创建两个窗口类:主窗口类和图片显示窗口类。Tips:为了突出多窗口的实现,省略了与此无关的代码。函数,检测子线程启动,像主线程发送图片结果信号,函数负责让子窗口对象将图片在其窗口内展示。添加两个窗口类,一个是主窗口类。,另一个是显示图片的窗口类。类的实现(假设图片使用。

Yaoyao2024的博客 7526

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

PyQt5入门笔记1--------按键弹出另一个窗口

学习PyQt的第一个程序,可以点击按钮,弹出一个新窗口。

fzh389603688的博客 6095

黑马 QT 小作业按下按键打开新窗口,转换为CLOSE,再按下变为关闭窗口实现代码

【代码】黑马 QT 小作业按下按键打开新窗口,转换为CLOSE,再按下变为关闭窗口实现代码。

chy2948331536的博客 522

QT点击按钮打开和关闭子窗口

在主窗口上点击一个按钮生成一个新的窗口,并同时生成另外一个按钮实现点击关闭新窗口的功能。

qq_43762434的博客 2974

Qt 如何新建一个弹出式窗口

Qt 如何新建一个弹出式窗口

m0_73443478的博客 4740

qt 打开一个新的页面

qt 打开一个新的页面

xie__jin__cheng的博客 1306

qt界面样式设置,qt打开新窗口及设置,opencv基础函数

qt,opencv

JamieLe的博客 1431

Qt菜单栏点击事件触发新窗口(新手自留记录,高手)

QTmenu下拉弹出子界面自留下次查看

xiaoqixiaoguai的博客 6250

Qt第四十四章:弹出窗口

为什么子窗口会一闪而逝呢?因为是在槽函数中创建的窗口对象,函数执行完毕后,窗口对象就 会被销毁。

Good Luck 4708

Qt creator创建按钮实现打开、关闭新窗口

首先创建一个新的widget项目本文使用的 Qt版本为 Qt 5.8.0 MinGW 32bit,取消创建界面,创建完成后,接下来都在widget.cpp文件中进行操作。

tufazhe的博客 2713
上一篇: C++学习之路(十七)C++ 用Qt5实现一个工具箱(增加托盘图标并且增加显示和退出菜单)- 示例代码拆分讲解
下一篇: C++学习之路(十九)C++ 用Qt5实现一个工具箱(用SQLite数据库来管理粘贴板数据)- 示例代码拆分讲解
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天河书阁 VicRestart

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值