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

上篇文章,我们用 Qt5 实现了在小工具箱中添加了《为屏幕颜色提取功能增加一个点击复制的功能》功能。今天我们增加一个比较正式点的功能,就是增加托盘图标并且增加显示和退出菜单(越来越像回事了吧 😁 )。下面我们就来看看如何来规划开发这样的小功能并且添加到我们的工具箱中吧。

老规矩,先上图

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

需求功能概述:

  1. 系统托盘图标的创建

    • 使用 QSystemTrayIcon 类创建了一个系统托盘图标。
    • 图标加载了指定路径下的图标文件,并设置了工具提示信息。
  2. 托盘菜单的实现

    • 创建了一个托盘菜单 (QMenu) 用于托盘图标右键点击时弹出。
    • 托盘菜单包括了两个选项:显示面板和退出应用程序。
  3. 主窗口的显示与隐藏

    • 实现了 MyMainWindow 类,重写了关闭事件处理函数,在关闭窗口时隐藏窗口而不是退出应用程序。
    • 创建了一个 showPanel() 槽函数,在点击托盘菜单的 “显示面板” 选项时显示主窗口并将其置于其他窗口之上。

功能实现细节:

  • MyMainWindow 类中的 closeEvent 函数重写,用于控制关闭窗口的行为,实现隐藏窗口而非退出程序。
  • showPanel() 槽函数负责显示主窗口并将其置于其他窗口之上。
  • 创建了两个菜单选项:一个用于显示主窗口,另一个用于退出应用程序。
  • 使用 QAction 将触发事件与特定功能(如显示主窗口、退出程序)连接起来。

这些功能集合起来,使得应用程序在系统托盘中可以通过右键点击托盘图标,显示主窗口或退出应用程序,同时在关闭窗口时隐藏而非退出应用程序。


核心实现代码:

class MyMainWindow : public QWidget {
Q_OBJECT
public:explicit MyMainWindow(QWidget *parent = nullptr) : QWidget(parent) {// ...}protected:// 重写关闭事件处理函数void closeEvent(QCloseEvent *event) override {if (this->isVisible()) {// 隐藏主窗口,而不是退出程序this->hide();event->ignore(); // 忽略关闭事件,防止程序退出}}
public slots:// ...void showPanel() {this->show(); // 显示主窗口this->raise(); // 将窗口置于其他窗口上方}private:// ...
};int main(int argc, char *argv[]) {// ...// 创建托盘图标auto* trayIcon = new QSystemTrayIcon(QIcon(":/res/icon/logo.png"), &mainWindow);trayIcon->setToolTip("My App Tray Icon");// 创建托盘菜单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();// ...
}

核心代码逻辑讲解

让我们逐段分解并解释代码的作用以及各函数的使用:

创建 MyMainWindow
class MyMainWindow : public QWidget {Q_OBJECT
public:explicit MyMainWindow(QWidget *parent = nullptr) : QWidget(parent) {// ...}protected:void closeEvent(QCloseEvent *event) override {if (this->isVisible()) {this->hide();event->ignore();}}public slots:void showPanel() {this->show();this->raise();}private:// ...
};
  • MyMainWindow:自定义主窗口类,继承自 QWidget,重写了关闭事件处理函数 closeEvent(),在窗口关闭时隐藏而非退出应用程序。另外,实现了 showPanel() 槽函数,用于显示主窗口并置于其他窗口之上。
主函数 main
int main(int argc, char *argv[]) {QApplication a(argc, argv);// ...auto* trayIcon = new QSystemTrayIcon(QIcon(":/res/icon/logo.png"), &mainWindow);trayIcon->setToolTip("My App Tray Icon");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();// ...return a.exec();
}
  • main 函数:应用程序入口,创建了 QApplication 实例,并初始化了系统托盘图标、托盘菜单和相关动作。
  • QSystemTrayIcon:创建了一个系统托盘图标对象,并加载了指定图标文件设置了提示信息。
  • QMenu:创建了一个托盘菜单对象,用于托盘图标右键点击时弹出。
  • QAction:创建了两个动作选项并分别与槽函数连接。一个是显示面板 (showPanelAction),另一个是退出应用程序 (quitAction)。
  • setContextMenu:将托盘菜单设置为系统托盘图标的右键菜单。
  • show()raise():显示主窗口并置于其他窗口之上。

这些代码段集合起来,实现了系统托盘图标、菜单和主窗口的显示控制,允许用户通过托盘图标来控制应用程序的行为。

图标资源文件如何添加到项目中?

在Clion中创建的QT5项目,并没有找到所谓的 Qt Creator ,我们可以通过以下办法来使用相对路径引入图片资源。

在 CLion 中创建的 Qt 项目可以使用资源文件(.qrc)来包含图片资源。你可以手动创建 .qrc 文件并将其添加到你的项目中。

以下是一个示例:

  1. 在项目的根目录(和 main.cpp 同级)手动创建一个新的文件,命名为 resources.qrc 或其他你喜欢的名字。
    在这里插入图片描述

  2. 编辑 .qrc 文件,添加资源:

<!DOCTYPE RCC>
<RCC version="1.0"><qresource><file>res/icon/logo.png</file></qresource>
</RCC>

确保路径 res/icon/logo.png 是相对于 .qrc 文件的路径,以正确地引用图片资源。

  1. CMakeLists.txt 文件中添加 .qrc 文件到你的项目中:
# 将 resources.qrc 添加到项目中
set(QT_RESOURCES resources.qrc)# 编译项目时包含生成的资源文件
add_executable(YourProjectName${QT_RESOURCES}# ...其他文件...
)

确保将 YourProjectName 替换为你实际的项目名称(也有用 ${PROJECT_NAME}来表示的),并将 resources.qrc 文件包含到 set() 中。

  1. 在你的代码中使用资源文件的路径来加载图标:
QSystemTrayIcon* trayIcon = new QSystemTrayIcon(QIcon(":/res/icon/logo.png"), &window);

这样配置后,重新编译你的项目,图标应该能够正确地被加载和显示了。


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

#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QVBoxLayout>
#include <QMessageBox>
#include <QDebug>
#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 <QHBoxLayout>
#include <QThread>
#include <QMouseEvent>
#include <QTimer>
#include <QSystemTrayIcon>
#include <QMenu>#define myApp (dynamic_cast<QApplication *>(QCoreApplication::instance()))class ClickableLabel : public QLabel {
Q_OBJECTpublic:explicit ClickableLabel(QWidget *parent = nullptr) : QLabel(parent) {}signals:void clicked();protected:void mousePressEvent(QMouseEvent *event) override {if (event->button() == Qt::LeftButton) {emit clicked();}}
};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);installEventFilter(this); // 安装事件过滤器以捕获鼠标事件}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 stopColorCapture() {colorTimer->stop();extractColorButton->setText("开始提取屏幕颜色");}void copyColorToClipboard() {// 从screenColorOutput获取颜色信息QString colorText = screenColorOutput->text();// 复制颜色值到剪切板QApplication::clipboard()->setText(colorText);// 弹出提示框显示复制的颜色值QMessageBox::information(this, "颜色已复制", QString("已复制颜色:") + colorText);}
protected:// 捕获松开Tab按键事件bool eventFilter(QObject *watched, QEvent *event) override {if (event->type() == QEvent::WindowDeactivate || event->type() == QEvent::KeyRelease) {stopColorCapture();return true;}return QObject::eventFilter(watched, event);}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 *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); // 设置图片高度为 200layout->addWidget(imagePreviewLabel);setLayout(layout);}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);// 缩放图片到 200x200QPixmap 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 *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);setLayout(layout);}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 *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);setLayout(layout);}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 *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(); // 初始转换setLayout(layout); // 设置整体布局}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 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 DateTimeTimestampConverter : public QWidget {
Q_OBJECT
public:explicit DateTimeTimestampConverter(QWidget *parent = nullptr) : QWidget(parent) {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);setLayout(layout);}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 *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);setLayout(layout);}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 *layout = new QVBoxLayout(this);listWidget = new QListWidget(this);updateList(); // 初始更新列表auto *clearButton = new QPushButton("清空记录", this);connect(clearButton, &QPushButton::clicked, this, &ClipboardManager::clearClipboard);layout->addWidget(listWidget);layout->addWidget(clearButton);setLayout(layout);connect(myApp->clipboard(), &QClipboard::dataChanged, this, &ClipboardManager::updateList);}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::toggleClipboardManager);clipboardManager = new ClipboardManager(this);clipboardManager->hide();layout->addWidget(clipboardManager);layout->addWidget(clipboardButton);auto *jsonFormatButton = new QPushButton("显示格式化 JSON 功能");jsonFormatButton->setObjectName("jsonFormatButton");connect(jsonFormatButton, &QPushButton::clicked, this, &MyMainWindow::toggleJsonFormatter);jsonFormatter = new JsonFormatter(this);jsonFormatter->hide();layout->addWidget(jsonFormatter);layout->addWidget(jsonFormatButton);auto *timestampConverterButton = new QPushButton("显示时间戳转换功能");timestampConverterButton->setObjectName("timestampConverterButton");connect(timestampConverterButton, &QPushButton::clicked, this, &MyMainWindow::toggleDateTimeTimestampConverter);timestampConverter = new DateTimeTimestampConverter(this);timestampConverter->hide();layout->addWidget(timestampConverter);layout->addWidget(timestampConverterButton);auto *numberBaseConverterButton = new QPushButton("显示进制转换功能");numberBaseConverterButton->setObjectName("numberBaseConverterButton");connect(numberBaseConverterButton, &QPushButton::clicked, this, &MyMainWindow::toggleNumberBaseConverter);numberBaseConverter = new NumberBaseConverter(this);numberBaseConverter->hide();layout->addWidget(numberBaseConverter);layout->addWidget(numberBaseConverterButton);auto *xmlFormatterButton = new QPushButton("显示XML格式化功能");xmlFormatterButton->setObjectName("xmlFormatterButton");connect(xmlFormatterButton, &QPushButton::clicked, this, &MyMainWindow::toggleXmlFormatter);xmlFormatter = new XmlFormatter(this);xmlFormatter->hide();layout->addWidget(xmlFormatter);layout->addWidget(xmlFormatterButton);auto *base64ConverterButton = new QPushButton("显示Base64加解密功能");base64ConverterButton->setObjectName("base64ConverterButton");connect(base64ConverterButton, &QPushButton::clicked, this, &MyMainWindow::toggleBase64Converter);base64Converter = new Base64Converter(this);base64Converter->hide();layout->addWidget(base64Converter);layout->addWidget(base64ConverterButton);auto *base64ImageConverterButton = new QPushButton("显示Base64图片预览功能");base64ImageConverterButton->setObjectName("base64ImageConverterButton");connect(base64ImageConverterButton, &QPushButton::clicked, this, &MyMainWindow::toggleBase64ImageConverter);base64ImageConverter = new Base64ImageConverter(this);base64ImageConverter->hide();layout->addWidget(base64ImageConverter);layout->addWidget(base64ImageConverterButton);auto *colorConverterButton = new QPushButton("显示色值提取转换功能");colorConverterButton->setObjectName("colorConverterButton");connect(colorConverterButton, &QPushButton::clicked, this, &MyMainWindow::toggleColorConverter);colorConverter = new ColorConverter(this);colorConverter->hide();layout->addWidget(colorConverter);layout->addWidget(colorConverterButton);setLayout(layout);}protected:// 重写关闭事件处理函数void closeEvent(QCloseEvent *event) override {if (this->isVisible()) {// 隐藏主窗口,而不是退出程序this->hide();event->ignore(); // 忽略关闭事件,防止程序退出}}
public slots:void toggleClipboardManager() {auto *curButton = findChild<QPushButton *>("clipboardButton");if (clipboardManager->isHidden()) {if (curButton) {curButton->setText("隐藏管理粘贴板记录");}clipboardManager->show();} else {if (curButton) {curButton->setText("显示管理粘贴板记录");}clipboardManager->hide();}}void toggleJsonFormatter() {auto *curButton = findChild<QPushButton *>("jsonFormatButton");if (jsonFormatter->isHidden()) {if (curButton) {curButton->setText("隐藏格式化 JSON");}jsonFormatter->show();} else {if (curButton) {curButton->setText("显示格式化 JSON");}jsonFormatter->hide();}}void toggleDateTimeTimestampConverter() {auto *curButton = findChild<QPushButton *>("timestampConverterButton");if (timestampConverter->isHidden()) {if (curButton) {curButton->setText("隐藏时间戳转换");}timestampConverter->show();} else {if (curButton) {curButton->setText("显示时间戳转换");}timestampConverter->hide();}}void toggleNumberBaseConverter() {auto *curButton = findChild<QPushButton *>("numberBaseConverterButton");if (numberBaseConverter->isHidden()) {if (curButton) {curButton->setText("隐藏进制转换器");}numberBaseConverter->show();} else {if (curButton) {curButton->setText("显示进制转换器");}numberBaseConverter->hide();}}void toggleXmlFormatter() {auto *curButton = findChild<QPushButton *>("xmlFormatterButton");if (xmlFormatter->isHidden()) {if (curButton) {curButton->setText("隐藏XML格式化");}xmlFormatter->show();} else {if (curButton) {curButton->setText("显示XML格式化");}xmlFormatter->hide();}}void toggleBase64Converter() {auto *curButton = findChild<QPushButton *>("base64ConverterButton");if (base64Converter->isHidden()) {if (curButton) {curButton->setText("隐藏Base64加解密功能");}base64Converter->show();} else {if (curButton) {curButton->setText("显示Base64加解密功能");}base64Converter->hide();}}void toggleBase64ImageConverter() {auto *curButton = findChild<QPushButton *>("base64ImageConverterButton");if (base64ImageConverter->isHidden()) {if (curButton) {curButton->setText("隐藏Base64图片预览功能");}base64ImageConverter->show();} else {if (curButton) {curButton->setText("显示Base64图片预览功能");}base64ImageConverter->hide();}}void toggleColorConverter() {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(); // 将窗口置于其他窗口上方}private:ClipboardManager *clipboardManager;JsonFormatter *jsonFormatter;DateTimeTimestampConverter *timestampConverter;NumberBaseConverter *numberBaseConverter;XmlFormatter *xmlFormatter;Base64Converter *base64Converter;Base64ImageConverter *base64ImageConverter;ColorConverter *colorConverter;
};int main(int argc, char *argv[]) {QApplication a(argc, argv);MyMainWindow mainWindow;// 创建托盘图标auto* trayIcon = new QSystemTrayIcon(QIcon(":/res/icon/logo.png"), &mainWindow);trayIcon->setToolTip("My App Tray Icon");// 创建托盘菜单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"
资源文件 resources.qrc
<!DOCTYPE RCC>
<RCC version="1.0"><qresource><file>res/icon/logo.png</file></qresource>
</RCC>
CMakeLists.txt 文件变更
# 将 resources.qrc 添加到项目中
set(QT_RESOURCES resources.qrc)add_executable(${PROJECT_NAME} ${QT_RESOURCES} main.cpp)

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

想要了解这个小工具是如何发展到现在这个地步的话,就看看往期文章吧~,记得要多跑一跑代码。如果大家有什么想要的功能或者想问的问题的话,就在评论区留言吧。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实现一个工具箱(为屏幕颜色提取功能增加一个点击复制的功能)- 示例代码拆分讲解


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

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

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

相关文章

【CMD】关于在for中SET变量失效的问题

如题&#xff0c;在for中进行一些细化操作时发现SET变量一直不成功(尤其在读取文件时)   简单搜了下看到有个帖子(BAT求助与讨论-SET变量失效)是讨论这个问题的&#xff0c;然后有个老哥给出的解答&#xff0c;其中使用了使用CALL&#xff0c;这让我想起几天前看到的一个博客…

Linux4.7、环境变量

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 目录 基本概念 见见环境变量 指令原理 常见环境变量及其测试 环境变量相关指令 环境变量组织方式 通过代码获取环境变量 通过系统变量获取环境变量以及设置环境变量 环境变量的全局属性 基本概念 首先&#xff0c;…

什么?居然可以免费使用Jetbrains?!

JetBrains是一家捷克的软件开发公司&#xff0c;该公司位于捷克的布拉格&#xff0c;并在俄罗斯的圣彼得堡及美国麻州波士顿都设有办公室&#xff0c;该公司最为人所熟知的产品是Java编程语言开发撰写时所用的集成开发环境&#xff1a;IntelliJ IDEA。 如下是jetbrains旗下的产…

【笔记】Clion 中运行 C/C++11 之 CMakeLists.txt 的配置

该文章记录第一次使用 Clion 时&#xff0c;对 CMakeLists 的配置&#xff0c;使其能够运行 C/C11 的代码。 一. CMakeLists.txt 的配置 1、首先我们在需要新建一个项目 2、填写新建项目相关的信息 3、修改 CMakeLists.txt 文件内容 替换文本&#xff1a; # 使用此 CMakeLis…

帆软报表不能增加SAP连接方式 通过插件一致性检测 同步至本地解决

帆软报表开发人员需要增加一个SAP数据连接方式&#xff1a;SAP_ECC_600环境 在服务器端不能直接增加&#xff0c;而在帆软报表设计器切换到远程模式时&#xff0c;又不能显示SAP连接&#xff0c;导致不能增加。 解决&#xff1a;重新进入帆软报表报计器时报以下错误&#xff0c…

开源运维监控系统-Nightingale(夜莺)应用实践(未完)

一、前言 某业务系统因OS改造,原先的Zabbix监控系统推倒后未重建,本来计划用外部企业内其他监控系统接入,后又通知需要自建才能对接,考虑之前zabbix的一些不便,本次计划采用一个类Prometheus的监控系统,镜调研后发现Nightingale兼容Prometheus,又有一些其他功能增强,又…

从马帮到金蝶云星空通过接口配置打通数据

从马帮到金蝶云星空通过接口配置打通数据 接入系统&#xff1a;马帮 上海马帮科技有限公司&#xff0c;是一家专注于提供全流程跨境电商ERP管理软件解决方案的企业。聚焦服务于各阶段、各领域的跨境电商从业者&#xff0c;旗下包含专业版ERP、亚马逊专用版ERP、东南亚海外版ERP…

JavaScript空值合并运算符

The Nullish Coalescing Operator&#xff08;空值合并运算符&#xff09;是一种 JavaScript 的新运算符&#xff0c;用于解决默认值设定中存在的一些问题。它的语法为 ??&#xff08;两个问号&#xff09;&#xff0c;表示当左侧的操作数为 null 或 undefined 时&#xff0c…

【java+vue+微信小程序项目】从零开始搭建——健身房管理平台(1)spring boot项目搭建、vue项目搭建、微信小程序项目搭建

项目笔记为项目总结笔记,若有错误欢迎指出哟~ 【项目专栏】 【java+vue+微信小程序项目】从零开始搭建——健身房管理平台(1)项目搭建 持续更新中… java+vue+微信小程序项目】从零开始搭建——健身房管理平台 项目简介Java项目搭建(IDEA)1.新建项目2.项目类型3.项目设置4…

mysql基础语句

【select查询】 1、Select * from 表名; 查询 例&#xff1a; Where 限定的条件查询 AND 连接两个或者多个查询条件 OR 连接两个或者多个查询条件&#xff0c;满足其中一个就可以查询出 2、Select * from 表名 where 字段 某某条件&#xff1b; 例&#xff1a; 3、Selec…

EasyRecovery易恢复2024最新免费版电脑数据恢复软件功能介绍

EasyRecovery从&#xff08;易恢复2024&#xff09;支持恢复不同存储介质数据&#xff0c;在Windows中恢复受损和删除文件,以及能检索数据格式化或损坏卷&#xff0c;甚至还可以从初始化磁盘。同时&#xff0c;你只需要最简单的操作就可以恢复数据文件&#xff0c;如&#xff1…

kubeadm 安装k8s1.28.x 底层走containerd 容器

1. k8s1.28.x 的概述 1.1 k8s 1.28.x 更新 Kubernetes v1.28 是 2023 年的第二个大版本更新&#xff0c;包含了 46 项主要的更新。 而今年发布的第一个版本 v1.27 有近 60 项&#xff0c;所以可以看出来&#xff0c;在发布节奏调整后&#xff0c; 每个 Kubernetes 版本中都会包…

DeepSort知识整理:余弦距离和马氏距离 (一)

一、余弦距离 1.1 余弦相似度 余弦相似度是用来衡量两个非零向量之间的夹角的余弦值。对于两个向量 A A A 和 B B B&#xff0c;余弦相似度的计算公式为&#xff1a; C o s i n e S i m i l a r i t y ( A , B ) A ⋅ B ∥ A ∥ ∥ B ∥ {\rm{Cosine Similarity }}\left( …

maven篇---第二篇

系列文章目录 文章目录 系列文章目录前言一、什么是Maven的坐标?二、讲一下maven的生命周期三、说说你熟悉哪些maven命令?前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的…

离散型制造业生产管理mes系统介绍

典型的离散制造业主要包括机械、电子、航空、汽车等行业&#xff0c;这些企业既有按订单生产&#xff0c;也有按库存生产&#xff0c;既有批量生产&#xff0c;也有单件小批生产。那么&#xff0c;注重生产计划的制定&#xff0c;生产的快速响应是离散行业MES系统应用的关键。 …

两道面试题秒杀你的C++基础!

大家好&#xff0c;我是光城&#xff0c;今天发两个非常重要的面试题&#xff0c;可以留言区说出你的答案&#xff0c;这两个题目都比较重要&#xff0c;看你能答对不&#xff1f; 1.C中初始化变量有几种方式&#xff0c;各自有什么区别&#xff1f; 或者说Initialization分为哪…

【已验证】SqlBulkCopy 执行批量插入的时候报超时问题-解决办法

把datatable里面的数据插入到数据库&#xff0c;但是数据量大的情况下批量插入会提示超时&#xff0c;所以把datatable的数据分批写入数据库的 using (SqlConnection connection new SqlConnection(ConnectionString)){connection.Open();int pageSize 100000;//SqlBulkCopy大…

已解决:虚拟机集群xsehll连接不上

问题描述&#xff1a; hadoop102能连上&#xff0c;hadoop103、hadoop104无法连接&#xff0c;以前都能连上&#xff0c;今天突然就连不上了 解决方案&#xff1a; 使用ifconfig命令查看有没有ens33 如果没有的话那就证明你的问题和我一样 依次使用以下命令&#xff1a; sys…

MVCC是如何保证隔离性的

之前提到了MVCC可以一定程度上避免幻读&#xff0c;那具体MVCC是咋工作的呢&#xff1f; 需要介绍两个机制&#xff1a;read view和聚簇索引的两个隐藏列 read view 这个就是我们理解的快照&#xff0c;有四个字段&#xff0c;本事务id、活跃事务id列表&#xff08;包含自己&…

精神衰弱怎么办?如何改变精神衰弱?

精神衰弱也叫神经衰弱&#xff0c;跟个人体质有一定的关系&#xff0c;更多是因为环境因素的长期作用所致&#xff0c;比如长期处于紧张和压力之下&#xff0c;这容易产生精神衰弱。当下有个流行词叫“精神内耗”&#xff0c;这个严重的精神内耗其实同样会导致精神衰弱。 精神…