基于Qt实现轻量化本地音乐播放器

在桌面端轻量化影音工具开发场景中,Qt凭借其跨平台、高性能、界面灵活定制的优势,成为开发者首选框架之一。相比于臃肿的第三方音乐软件,自主开发的本地音乐播放器无广告、无后台推送、启动速度快,仅专注本地音频播放核心功能,非常适合日常学习使用和Qt项目实战练习。

本文将基于Qt5 + QMediaPlayer + QMediaPlaylist实现一款功能完整的轻量化本地音乐播放器,涵盖音频播放控制、播放列表管理、多模式循环播放、系统托盘、配置文件持久化、窗口拦截关闭等核心功能。同时结合完整源码,逐模块讲解实现思路、核心逻辑、踩坑问题和优化方案,适合Qt初学者入门实战,也可作为课程设计、小型桌面项目参考案例。

一、项目整体功能概述

本音乐播放器为纯本地音频播放工具,不依赖网络资源,支持主流mp3、wav、wma等音频格式,整合了市面上主流播放器的基础核心功能,整体功能完备且轻量化,具体功能清单如下:

  • 基础播放控制:支持播放、暂停、停止、上一曲、下一曲无缝切换,播放状态实时同步UI按钮状态;

  • 进度与音量调节:可拖拽进度条跳转播放位置,实时显示当前播放时间/总时长,支持音量滑动调节、一键静音切换;

  • 播放列表管理:批量添加本地音频、删除单曲、清空全部列表,双击列表曲目快速切换播放;

  • 多播放模式切换:实现顺序播放、随机播放、单曲循环三种模式自由切换,模式状态持久化保存;

  • 数据持久化存储:通过INI配置文件保存播放列表、当前播放索引、播放模式,重启软件自动恢复上次播放状态;

  • 系统托盘功能:最小化后台运行,托盘右键支持显示主窗口、一键退出程序;

  • 人性化交互优化:关闭窗口二次确认、音频异常报错提示、空列表播放弹窗提示、中文乱码修复等细节优化。

二、核心技术栈介绍

本项目基于Qt5框架开发,核心依赖Qt多媒体模块,无需引入第三方库,原生API即可实现全部功能,兼容性极强:

  • QMediaPlayer:Qt原生多媒体播放核心类,负责音频解码、播放、暂停、停止、音量控制、播放状态监听,是播放器的核心载体;

  • QMediaPlaylist:播放列表管理类,专门用于管理多音频文件队列,支持索引切换、播放模式设置、媒体资源批量添加与删除;

  • QSettings:配置文件读写类,负责INI文件的解析与写入,实现项目数据持久化;

  • QSystemTrayIcon:系统托盘类,实现窗口后台驻留、托盘菜单自定义,提升软件使用体验;

  • QFileDialog/QFileInfo:文件选择与信息解析类,用于批量读取本地音频文件、解析文件名与文件路径。

三、核心代码模块详细解析

本项目代码结构清晰,分为主函数托盘初始化、主窗口构造初始化、播放核心逻辑、列表管理、配置读写、事件拦截六大模块,下面逐一拆解核心实现逻辑。

3.1 主函数:系统托盘与程序初始化

main函数主要负责程序启动初始化、主窗口创建、系统托盘功能搭建,是程序的入口核心。区别于普通Qt窗口项目,本项目新增了系统托盘常驻功能,实现软件最小化后台运行。

代码中通过QSystemTrayIcon创建托盘图标,搭配QMenu自定义右键菜单,实现"显示主窗口"和"退出程序"两大核心操作。通过信号槽绑定菜单点击事件,窗口最小化后可通过托盘快速唤起,关闭程序时自动保存配置,保证数据不丢失。同时移除了冗余的调试窗口标题,保证软件界面整洁。

托盘功能的核心优化点在于窗口状态判断,通过检测窗口是否为最小化状态,精准恢复窗口大小并置顶激活,解决了传统托盘唤起窗口层级过低、无法前置的问题。

3.2 主窗口构造:全局初始化配置

主窗口构造函数是整个播放器的初始化核心,涵盖控件初始化、播放器对象创建、信号槽绑定、配置读取、列表恢复等全流程操作。

首先初始化全局变量,定义当前播放索引、默认播放模式,固定窗口初始尺寸。随后创建QMediaPlayer和QMediaPlaylist对象,并将播放列表绑定到播放器,实现播放器对列表资源的统一调度管理。

信号槽绑定是本模块的核心,通过绑定播放器状态、播放进度、音频时长、播放错误、列表索引变更等信号,实时监听播放器状态变化,同步更新UI界面和播放逻辑。相比轮询检测,Qt信号槽机制属于事件驱动,资源占用更低、响应更及时。

最后读取本地INI配置文件,恢复上次保存的播放列表、播放索引和播放模式,实现软件重启无感知续播,极大提升了用户体验。

3.3 播放核心逻辑:状态与进度同步

播放状态同步是保证UI与功能一致性的关键。通过onStateChanged槽函数监听播放器播放状态,动态切换播放、暂停、停止按钮的可用状态:播放时禁用播放按钮、启用暂停和停止按钮,暂停/停止时恢复播放按钮可用状态,完全贴合主流播放器交互逻辑。

进度与时间更新模块实现了播放进度实时同步。onDurationChanged获取音频总时长,初始化进度条最大值和总时间文本;onPositionChanged实时监听播放位置,更新进度条和已播放时间。同时增加滑块拖拽判定逻辑,当用户手动拖动进度条时,暂停自动刷新,避免进度条抖动、跳转失灵的问题,这是播放器交互优化的核心细节。

针对时间显示格式,代码通过整除、取余运算换算分秒,后续可结合格式化补零,实现标准的00:00时间展示,解决单数字时间显示不规整的问题。

3.4 播放列表管理模块

播放列表是本地播放器的核心功能载体,本项目实现了批量添加、删除单曲、清空列表、双击切歌全功能逻辑。

批量添加音频功能通过QFileDialog弹出文件选择框,筛选mp3、wav、wma主流音频格式,支持多选文件批量导入。通过QFileInfo解析文件名称,去除路径和后缀,仅展示纯净歌名,同时将完整文件路径存入全局容器和播放列表,兼顾界面美观和播放功能。

删除功能做了边界逻辑优化:精准获取当前选中列表行,删除对应数据,同时判断删除曲目是否为正在播放的曲目。如果删除当前播放曲目,自动切换到上一曲,无曲目时停止播放并重置界面提示,避免程序闪退、播放逻辑错乱的问题。

清空列表功能会清空容器数据、播放列表和UI列表,停止播放并重置状态,保证数据完全同步,无残留冗余数据。同时支持列表双击快速切歌,无需点击按钮,操作更便捷。

3.5 播放模式切换逻辑

项目实现了顺序播放、随机播放、单曲循环 三种主流播放模式,通过全局变量m_iLoopMode记录当前模式,点击模式按钮循环切换。

updateLoopUi函数为模式更新核心,根据模式变量匹配QMediaPlaylist原生播放模式,同步修改按钮图标和文字,实现视觉与功能的统一。模式切换后自动保存到配置文件,重启软件无需重新设置。

三种模式完美适配Qt原生播放逻辑:顺序播放按列表索引依次播放、随机播放打乱播放顺序、单曲循环重复播放当前曲目,覆盖日常播放全部场景。

3.6 配置文件持久化

数据持久化是桌面软件的必备功能,本项目通过QSettings读写INI配置文件,实现播放数据永久保存。核心逻辑分为读取配置和写入配置两部分。

读取配置时,打开程序同级目录下的config.ini文件,读取曲目数量、当前播放索引、播放模式,循环读取所有音频路径并恢复播放列表,实现重启续播。同时增加边界保护,避免配置数据异常导致程序崩溃。

写入配置时,先清空原有配置分组,清除冗余垃圾数据,再重新写入最新的列表数量、播放索引、播放模式和所有音频路径,保证配置文件整洁、数据精准,避免旧数据残留导致的功能异常。

3.7 事件拦截与异常处理

为优化软件容错率和使用体验,项目增加了多重异常拦截和提示机制。

重写closeEvent关闭事件,拦截窗口默认关闭行为,弹出确认弹窗,防止用户误触关闭软件。确认关闭后自动停止播放、保存最新配置,再关闭窗口;取消则忽略关闭事件,继续运行程序。

新增音频异常捕获槽函数,当音频文件损坏、路径失效、格式不支持时,主动捕获播放器错误信号,弹出异常提示弹窗,告知用户播放失败原因,避免程序闪退。同时对空列表播放、无选中删除等异常场景做了弹窗提示,交互更人性化。

四、项目优化亮点与问题解决

4.1 中文乱码解决方案

Windows平台MSVC编译器默认编码为GBK,Qt项目极易出现中文乱码问题。本项目通过添加编译指令#pragma execution_character_set("utf-8"),强制源码中文字符以UTF-8编码编译,彻底解决弹窗提示、按钮文字、配置文件中文乱码问题,兼容性极强。

4.2 进度条拖拽防抖优化

原生播放器进度条拖拽时会频繁刷新位置,导致界面抖动、跳转不准确。项目通过判断滑块拖拽状态,用户手动拖动时暂停自动进度刷新,松开后正常同步,完美解决进度条抖动问题。

4.3 托盘后台驻留优化

区别于简易托盘实现,本项目托盘唤起窗口时,不仅显示窗口,还会激活窗口、提升窗口层级,保证窗口置顶显示,解决了托盘唤起窗口隐藏在其他窗口下方的问题,交互更贴合商业软件逻辑。

4.4 数据冗余清理优化

配置文件写入时,先删除原有APPCFG分组,再重新写入数据,彻底避免多次增删曲目后,配置文件残留废弃key导致的读取异常、列表数据错乱问题。

五、项目可拓展方向

本项目已完成基础播放器全部核心功能,代码结构规范、拓展性极强,可根据需求进一步迭代优化,适合二次开发:

  • UI界面美化:添加QSS样式表,统一按钮、列表、进度条样式,增大列表行高、优化滑块尺寸、自定义选中高亮颜色,解决原生控件简陋、交互局促的问题;

  • 歌词显示功能:解析LRC歌词文件,实现歌词同步滚动、精准匹配播放进度;

  • 快捷键控制:绑定键盘快捷键,实现空格暂停/播放、上下键调节音量等快捷操作;

  • 皮肤主题切换:搭建明暗两套主题,实现一键切换界面配色;

  • 音频均衡器:基于Qt多媒体模块拓展音效调节功能,支持高低音调节。

六、项目总结

本文实现的Qt轻量化本地音乐播放器,代码结构清晰、功能完整、交互人性化,完美解决了基础音频播放、列表管理、数据持久化、后台驻留等核心需求。项目全程使用Qt原生API开发,无第三方依赖,编译运行简单、跨平台性强,非常适合Qt初学者练手、课程设计、小型桌面软件开发参考。

通过本项目开发,能够熟练掌握Qt信号槽机制、多媒体模块使用、配置文件读写、窗口事件拦截、系统托盘开发、异常处理等核心知识点,同时积累桌面软件交互优化、代码容错处理的实战经验。项目源码规范、注释清晰,可直接运行使用,也可在此基础上持续迭代拓展更多个性化功能。

cpp 复制代码
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include    <QMainWindow>
#include    <QMediaPlayer>
#include    <QMediaPlaylist>
#include <QPushButton>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

private:
    QMediaPlayer    *player;        // 媒体播放器对象
    QMediaPlaylist  *playlist;      // 播放列表管理

    QString  durationTime;         // 音频总时长文本
    QString  positionTime;         // 当前播放位置时间文本

    void updateLoopUi();            // 更新播放模式UI(图标+文字)

    void ReadConfig();              // 读取ini配置文件
    void WriteConfig();             // 保存配置到ini

    int m_iCurrentIndex;            // 记录上次播放索引
    int m_iLoopMode;                // 播放模式:0顺序 1随机 2单曲循环

    QStringList m_list;             // 保存全部音频文件路径

    virtual void closeEvent(QCloseEvent *); // 重写窗口关闭事件

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
    // 播放器状态变更槽
    void onStateChanged(QMediaPlayer::State state);

    // 播放列表索引切换
    void onPlaylistChanged(int position);

    // 获取音频总时长
    void onDurationChanged(qint64 duration);
    // 播放位置实时更新
    void onPositionChanged(qint64 position);

    // 添加音频文件
    void on_btnAdd_clicked();

    // 播放
    void on_btnPlay_clicked();
    // 暂停
    void on_btnPause_clicked();
    // 停止
    void on_btnStop_clicked();

    // 双击列表项播放
    void on_listWidget_doubleClicked(const QModelIndex &index);

    // 清空播放列表
    void on_btnClear_clicked();

    // 调节音量滑块
    void on_sliderVolumn_valueChanged(int value);
    // 静音按钮
    void on_btnSound_clicked();

    // 播放进度拖动跳转
    void on_sliderPosition_valueChanged(int value);

    // 删除选中音频
    void on_btnRemove_clicked();
    // 上一曲
    void on_btnPrevious_clicked();
    // 下一曲
    void on_btnNext_clicked();

    // 切换播放模式
    void on_btnLoop_clicked();

    // 播放器出错回调
    void onMediaError();

private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H
cpp 复制代码
#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QFileDialog>
#include <QSettings>
#include <QMessageBox>
#include <QCloseEvent>

// MSVC编译器:源码中文字符串编译为UTF‑8,解决Windows中文乱码
#if _MSC_VER >= 1600
#pragma execution_character_set("utf-8")
#endif

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    m_iCurrentIndex = 0;
    m_iLoopMode = 0;

    resize(359, 512); // 设置窗口初始大小

    player = new QMediaPlayer(this);
    playlist = new QMediaPlaylist(this);
    player->setPlaylist(playlist);

    // 信号‑槽绑定
    connect(player, SIGNAL(stateChanged(QMediaPlayer::State)),
            this, SLOT(onStateChanged(QMediaPlayer::State)));
    connect(player, SIGNAL(positionChanged(qint64)),
            this, SLOT(onPositionChanged(qint64)));
    connect(player, SIGNAL(durationChanged(qint64)),
            this, SLOT(onDurationChanged(qint64)));
    connect(player, SIGNAL(error(QMediaPlayer::Error)), this, SLOT(onMediaError()));
    connect(playlist, SIGNAL(currentIndexChanged(int)),
            this, SLOT(onPlaylistChanged(int)));

    ReadConfig();        // 读取ini配置
    updateLoopUi();      // 初始化播放模式UI

    // 加载配置里保存的音频列表
    for(int i = 0; i < m_list.size(); i++)
    {
        QString filePath = m_list.at(i);
        QFileInfo fileInfo(filePath);
        QString fileName = fileInfo.completeBaseName();
        ui->listWidget->addItem(fileName);
        playlist->addMedia(QUrl::fromLocalFile(filePath));
    }

    // 恢复上次播放的曲目并自动播放
    if(m_list.size() > 0)
    {
        if(m_iCurrentIndex < 0 || m_iCurrentIndex >= m_list.size())
            m_iCurrentIndex = 0;
        playlist->setCurrentIndex(m_iCurrentIndex);
        player->play();
    }
}

MainWindow::~MainWindow()
{
    delete ui;
}

// 根据播放器状态更新按钮可用状态
void MainWindow::onStateChanged(QMediaPlayer::State state)
{
    ui->btnPlay->setEnabled(!(state == QMediaPlayer::PlayingState));
    ui->btnPause->setEnabled(state == QMediaPlayer::PlayingState);
    ui->btnStop->setEnabled(state == QMediaPlayer::PlayingState);
}

// 播放索引切换,同步更新列表选中行与标题显示
void MainWindow::onPlaylistChanged(int position)
{
    ui->listWidget->setCurrentRow(position);
    QListWidgetItem *item = ui->listWidget->currentItem();
    if (item)
        ui->LabCurMedia->setText(item->text());
}

// 获取音频总时长,更新进度条最大值与总时间文本
void MainWindow::onDurationChanged(qint64 duration)
{
    ui->sliderPosition->setMaximum(duration);

    int secs = duration / 1000;
    int mins = secs / 60;
    secs = secs % 60;
    durationTime = QString::asprintf("%d:%d", mins, secs);
    ui->LabRatio->setText(positionTime + "/" + durationTime);
}

// 当前播放位置变化,更新进度条与已播放时间文本
void MainWindow::onPositionChanged(qint64 position)
{
    if (ui->sliderPosition->isSliderDown()) // 正在手动拖动滑块,不回写
        return;

    ui->sliderPosition->setSliderPosition(position);

    int secs = position / 1000;
    int mins = secs / 60;
    secs = secs % 60;
    positionTime = QString::asprintf("%d:%d", mins, secs);
    ui->LabRatio->setText(positionTime + "/" + durationTime);
}

// 添加音频文件
void MainWindow::on_btnAdd_clicked()
{
    QString curPath = QDir::homePath();
    QString dlgTitle = "选择音频文件";
    QString filter = "音频文件(*.mp3 *.wav *.wma);;mp3文件(*.mp3);;wav文件(*.wav);;wma文件(*.wma);;所有文件(*.*)";
    QStringList fileList = QFileDialog::getOpenFileNames(this, dlgTitle, curPath, filter);

    if (fileList.count() < 1)
        return;

    for (int i = 0; i < fileList.count(); i++)
    {
        QString filePath = fileList.at(i);
        playlist->addMedia(QUrl::fromLocalFile(filePath));
        QFileInfo fileInfo(filePath);
        ui->listWidget->addItem(fileInfo.completeBaseName());
        m_list.append(filePath);
    }
    WriteConfig(); // 更新配置文件
}

// 播放按钮
void MainWindow::on_btnPlay_clicked()
{
    if(playlist->mediaCount() <= 0)
    {
        QMessageBox::information(this, "提示", "请先添加音频文件");
        return;
    }
    if (playlist->currentIndex() < 0)
        playlist->setCurrentIndex(0);
    player->play();
}

// 暂停
void MainWindow::on_btnPause_clicked()
{
    player->pause();
}

// 停止播放
void MainWindow::on_btnStop_clicked()
{
    player->stop();
}

// 双击列表项切换播放
void MainWindow::on_listWidget_doubleClicked(const QModelIndex &index)
{
    int rowNo = index.row();
    playlist->setCurrentIndex(rowNo);
    player->play();
}

// 清空全部播放列表
void MainWindow::on_btnClear_clicked()
{
    m_list.clear();
    playlist->clear();
    ui->listWidget->clear();
    player->stop();
    WriteConfig();
}

// 音量滑块调节
void MainWindow::on_sliderVolumn_valueChanged(int value)
{
    player->setVolume(value);
}

// 静音切换
void MainWindow::on_btnSound_clicked()
{
    bool mute = player->isMuted();
    player->setMuted(!mute);
    if (mute)
        ui->btnSound->setIcon(QIcon(":/images/images/volumn.bmp"));
    else
        ui->btnSound->setIcon(QIcon(":/images/images/mute.bmp"));
}

// 进度条手动跳转播放位置
void MainWindow::on_sliderPosition_valueChanged(int value)
{
    player->setPosition(value);
}

// 删除选中的音频项
void MainWindow::on_btnRemove_clicked()
{
    int pos = ui->listWidget->currentRow();
    if(pos < 0) return;

    m_list.removeAt(pos);
    QListWidgetItem *item = ui->listWidget->takeItem(pos);
    delete item;

    // 如果删除的是正在播放的曲目
    if (playlist->currentIndex() == pos)
    {
        int nextPos = 0;
        if (pos >= 1)
            nextPos = pos - 1;

        playlist->removeMedia(pos);
        if (ui->listWidget->count() > 0)
        {
            playlist->setCurrentIndex(nextPos);
        }
        else
        {
            player->stop();
            ui->LabCurMedia->setText("无曲目");
        }
    }
    else
    {
        playlist->removeMedia(pos);
    }
    WriteConfig();
}

// 上一曲
void MainWindow::on_btnPrevious_clicked()
{
    playlist->previous();
}

// 下一曲
void MainWindow::on_btnNext_clicked()
{
    playlist->next();
}

// 读取config.ini配置
void MainWindow::ReadConfig()
{
    QString sPath = qApp->applicationDirPath();
    QSettings settings(sPath + QString("/config.ini"), QSettings::IniFormat, this);
    settings.setIniCodec("UTF-8");

    int iCount = settings.value("/APPCFG/ItemCount").toInt();
    m_iCurrentIndex = settings.value("/APPCFG/CurrentIndex").toInt();
    m_iLoopMode = settings.value("/APPCFG/LoopMode").toInt();

    // 边界保护
    if (m_iLoopMode < 0) m_iLoopMode = 0;
    if (m_iLoopMode > 2) m_iLoopMode = 2;

    // 读出保存的音频路径
    for(int i = 0; i < iCount; i++)
    {
        QString sItem = QString("/APPCFG/Item") + QString::number(i);
        QString sTemp = settings.value(sItem).toString();
        m_list.append(sTemp);
    }
}

// 保存配置到config.ini,先清除旧分组避免残留垃圾key
void MainWindow::WriteConfig()
{
    QString sPath = qApp->applicationDirPath();
    QString filePath = sPath + QString("/config.ini");

    QSettings settings(filePath, QSettings::IniFormat, this);
    settings.setIniCodec("UTF-8");

    settings.remove("/APPCFG"); // 删除整个分组,清理旧数据

    settings.setValue("/APPCFG/ItemCount", ui->listWidget->count());
    settings.setValue("/APPCFG/CurrentIndex", playlist->currentIndex());
    settings.setValue("/APPCFG/LoopMode", m_iLoopMode);

    for(int i = 0; i < m_list.size(); i++)
    {
        QString sItem = QString("/APPCFG/Item") + QString::number(i);
        settings.setValue(sItem, m_list.at(i));
    }
}

// 拦截窗口关闭事件,弹出退出确认框
void MainWindow::closeEvent(QCloseEvent *e)
{
    int ret = QMessageBox::question(this, "提示", "确认退出?", QMessageBox::Ok|QMessageBox::Cancel);
    if(ret == QMessageBox::Ok)
    {
        player->stop();
        WriteConfig();
        e->accept();  // 允许关闭窗口
    }
    else
    {
        e->ignore();  // 取消关闭
    }
}

// 切换播放模式:顺序/随机/单曲循环
void MainWindow::on_btnLoop_clicked()
{
    m_iLoopMode++;
    if(m_iLoopMode > 2)
        m_iLoopMode = 0;

    updateLoopUi();
    WriteConfig();
}

// 更新播放模式:设置实际模式+按钮文字图标
void MainWindow::updateLoopUi()
{
    if (m_iLoopMode == 0)
    {
        playlist->setPlaybackMode(QMediaPlaylist::Sequential);
        ui->btnLoop->setIcon(QIcon(":/images/images/Sequential.bmp"));
        ui->btnLoop->setText("顺序播放");
    }
    else if(m_iLoopMode == 1)
    {
        playlist->setPlaybackMode(QMediaPlaylist::Random);
        ui->btnLoop->setIcon(QIcon(":/images/images/Random.bmp"));
        ui->btnLoop->setText("随机播放");
    }
    else if(m_iLoopMode == 2)
    {
        playlist->setPlaybackMode(QMediaPlaylist::CurrentItemInLoop);
        ui->btnLoop->setIcon(QIcon(":/images/images/CurrentItemInLoop.bmp"));
        ui->btnLoop->setText("单曲循环");
    }
}

// 播放器错误弹窗
void MainWindow::onMediaError()
{
    QMessageBox::warning(this, "播放异常", "该音频无法播放,文件损坏或已被移除!");
}
复制代码
#include "mainwindow.h"
#include <QApplication>
#include <QSystemTrayIcon>
#include <QMenu>
#include <QMessageBox>

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

    //主窗口
    MainWindow w;

    w.setWindowTitle("音乐播放器260814");


    // -------------------- 系统托盘功能 --------------------
    // 创建托盘图标
    QSystemTrayIcon trayIcon(QIcon(":/images/images/music.ico"), &a);
    trayIcon.setToolTip("音乐播放器");

    // 创建右键菜单
    QMenu *trayMenu = new QMenu(&w);

    // 添加"显示主窗口"菜单项
    QAction *showAction = trayMenu->addAction("显示主窗口");
    // 添加"关闭程序"菜单项
    QAction *quitAction = trayMenu->addAction("退出程序");

    // -------------------- 信号与槽连接 --------------------
    // 点击"显示主窗口"时,恢复主窗口显示并激活
    QObject::connect(showAction, &QAction::triggered, &w, [&]()
    {
        // 检查窗口是否处于最小化状态,如果是则恢复正常大小
        if (w.windowState() & Qt::WindowMinimized)
            w.setWindowState(w.windowState() & ~Qt::WindowMinimized);
        w.show();           // 显示窗口(若已最小化/隐藏)
        w.activateWindow(); // 激活窗口并置于前端
        w.raise();          // 提升窗口层级
    });

    // 点击"退出程序"时,弹出确认
    QObject::connect(quitAction, &QAction::triggered, &a,  [&]()
    {
        w.close();
    });

    // 将菜单关联到托盘图标
    trayIcon.setContextMenu(trayMenu);


    // 显示托盘图标和主窗口
    trayIcon.show();
    w.show();


    return a.exec();
}
相关推荐
qq_4480111640 分钟前
C语言中的柔性数组
c语言·开发语言·柔性数组
小白学大数据42 分钟前
长周期爬虫的数据一致性:断点续爬 + 事务回滚保障采集质量
开发语言·爬虫·测试工具
zlinear数据采集卡1 小时前
D223上位机C#开发实战:从帧解析到波形显示的完整实现
开发语言·arm开发·嵌入式硬件·fpga开发·开源·c#
Poo_Chai2 小时前
QT emit信号后完整处理流程,包括槽函数响应流程
java·开发语言·数据库
瑞码空间2 小时前
Java 图形界面(GUI)完整知识点手册
java·开发语言·图形界面·swing
小努蛋2 小时前
linux编译openresty异常问题,lua_cjson.c:706:5: 错误: ‘for‘ 循环初始化声明只在 C99 或 C++ 模式下允许
开发语言·lua·openresty
qz_Serene2 小时前
C++:类和对象(上)
开发语言·c++
西安景驰电子3 小时前
《PTP精确时间协议系列》第一篇:从原理到应用,全面解读IEEE 1588
linux·运维·服务器·开发语言·网络·windows·php
ctlover3 小时前
Python文件操作
开发语言·python