QT学习笔记-任务栏图标程序(带菜单栏)C++实现

时间:2024-03-02 16:02:36

虽然是在Linux下编写的,但是参考了别人代码感觉是通用的,Windows也可以参考。

环境:Linux(Ubuntu/Debian)(方德)、QT5.6.2、GCC6.2.1

在编写程序之前首先需要准备一个任务栏图标。以下是QT官方文档对于系统任务栏图标大小的解释。

On Windows, the system tray icon size is 16x16; on X11, the preferred size is 22x22. The icon will be scaled to the appropriate size as necessary.[1]

翻译:在Windows上,系统任务栏图标的大小为16x16; 在X11上,首选尺寸为22x22。 图标将根据需要缩放为适当的大小。

我是在Linux上编写程序,而Linux基本都支持X11协议,所以将图片尺寸更改为22x22,这里可以在网上找到很多在线工具进行编辑。而如果在Windows上则应该将图标大小缩放至16x16,这里我并未在Windows上进行测试。

在官方文档中并未找到关于图片格式的说明,这里我就使用的图片格式为png,命名为icon.png。

我将项目的实现分为两步,第一步为实现一个任务栏图标,第二步为实现任务栏图标右键菜单栏。文章最后会给出完整代码。

0.创建一个项目

首先新建一个Qt Widgets Application项目,我把项目命名为taskbardemo。构建套件选择自己配置好的套件,基类我选的是QMainWindow,其他为默认。

image-20200813155620354

项目创建好之后会创建4个文件,分别是项目文件夹下的taskbardemo.pro、头文件的mainwindow.h、源文件的main.cpp和mainwindow.cpp、界面文件的mainwindow.ui。

image-20200813161130666

1.创建任务栏图标

1.1新建一个任务栏图标

在编写任务栏图标代码之前需要先将QSystemTrayIcon头文件加上,我加在mainwindow.h文件上。

#include <QSystemTrayIcon>

在mainwindow.h中还应该加一个类成员函数用来当托盘图标点击的槽函数:

private slots:
    void iconActived(QSystemTrayIcon::ActivationReason reason);

在mainwindow.cpp的MainWindow构造函数中加入以下代码:

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

    //新建一个托盘图标对象 //2020-09-22更新,在QSystemTrayIcon()中添加this指针指向mainwindow,以便在关闭窗口时销毁托盘图标
    QSystemTrayIcon *m_trayicon = new QSystemTrayIcon(this);

    //设置托盘图标提示:鼠标移动到上面会提示文字
    m_trayicon->setToolTip(QString("托盘图标程序Demo"));
    //设置图标文件,这里先使用路径来设置,后面介绍使用qt资源文件设置图标文件
    //我将图标放在了项目路径下,并使用了绝对路径
    m_trayicon->setIcon(QIcon("/home/fan/qtproject/taskbardemo/icon.png"));

    //连接信号与槽:点击托盘图标执行事件
    connect(m_trayicon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(iconActived(QSystemTrayIcon::ActivationReason)));

    //显示托盘图标
    m_trayicon->show();

}

接下来点击QT左下角的运行,会在构建完成后自动运行,这时候会发现已经可以有一个窗口在运行,并且显示托盘图标了。

image-20200825161216968

1.2 为图标增加双击显示主界面功能

在mainwindow.h中,添加一个类成员函数:

private:
    void changeEvent(QEvent *event);

我们重载事件改变函数,让点击最小化按钮改变一个功能,变成隐藏窗口,还有很多让窗口隐藏的方法,例如点击关闭按钮可以弹出消息框选择是否隐藏,大家可以自行尝试。

在mainwindow.cpp中,添加它的函数定义:

void MainWindow::changeEvent(QEvent *event)
{
    if(event->type()!=QEvent::WindowStateChange)
        return;
    if(this->windowState()==Qt::WindowMinimized)
    {
        this->hide();
    }
}

这里先判断事件的类型是不是窗体状态改变,如果是的话再判断是不是状态为最小化,如果是最小化,那么隐藏当前窗口。

接下来为任务栏图标增加双击显示主窗口的功能,之前我们添加了一个槽函数iconActived,我们在mainwindow.cpp中添加以下定义:

void MainWindow::iconActived(QSystemTrayIcon::ActivationReason reason)
{
    switch(reason)
    {
    //双击托盘显示窗口
    case QSystemTrayIcon::DoubleClick:
    {
        this->show();
        break;
    }
    default:
        break;
    }
}

这里QSystemTrayIcon::ActivationReason枚举描述了系统托盘被激活的原因,其中QSystemTrayIcon::DoubleClick是双击系统托盘条目,QSystemTrayIcon::Trigger是单击系统托盘条目,QSystemTrayIcon::Context是请求了托盘条目的上下文菜单,更多资料另请参见参考资料[1]中enum QSystemTrayIcon::ActivationReason条目。

this->show();为显示主窗口界面。这样我们在构建项目后运行程序,点击最小化按钮就可以隐藏窗口界面了,并且双击托盘图标,就可以恢复窗口界面了。

2.创建任务栏图标右键菜单

我们见过非常多的程序都可以右键单击托盘图标之后弹出菜单,可以选择一些功能,例如显示主界面、退出程序等功能,这里我们实现这两个功能,和其他一些演示。

在mainwindow.cpp的MainWindow构造函数中继续添加以下代码:

    //创建菜单项
    QMenu *m_traymenu = new QMenu();

    //创建菜单项内容
    QAction *action_show = new QAction(tr("Show MainWindow"),this);
    QAction *action_quit = new QAction(tr("Quit"),this);
    QAction *action_test1 = new QAction(tr("test1"),this);
    QAction *action_test2 = new QAction(tr("test2"),this);
    QAction *action_test3 = new QAction(tr("test3"),this);

    //设置一个菜单项的图标作为演示
    action_show->setIcon(QIcon("/home/fan/qtproject/taskbardemo/icon.png"));
    //将他们关联起来
    m_traymenu->addAction(action_show);
    m_traymenu->addAction(action_quit);
    m_traymenu->addSeparator();//设置一个分割条作为演示
    m_traymenu->addAction(action_test1);
    m_traymenu->addAction(action_test2);
    m_traymenu->addAction(action_test3);
    m_trayicon->setContextMenu(m_traymenu);//设置上下文菜单

    //连接信号与槽:单击菜单项内容执行相应的函数
    connect(action_show,SIGNAL(triggered()),this,SLOT(show()));
    connect(action_quit,SIGNAL(triggered()),this,SLOT(close()));

这里我直接创建了QAction对象,如果需要在其它函数操作菜单项的内容,例如改变菜单项显示的文本,可以将其添加成类成员变量,在mainwindow.h的类声明中添加一个QAction *action_show即可,这里就不需要再声明了。

这里注意addAction的顺序和最终形成的菜单项顺序是一致的,具体可以联系我的代码看下面的图。

进阶:加入二级菜单,在mainwindow.cpp的MainWindow构造函数中继续添加以下代码:

    m_traymenu->addSeparator();//设置一个分割条作为演示
    //增加一个二级菜单,以此类推可以实现多级菜单
    QMenu *m_traymenu1 = m_traymenu->addMenu("1_0");
    //一个更快捷添加菜单项的方法
    m_traymenu1->addAction("1_1",this,SLOT(close));

image-20200826100858236

至此,一个完整的任务栏图标程序就完成了,接下来我们进阶,使用QT的资源文件存放图片资源。

3.将图片资源使用qrc存放

在QT中新建文件(Ctrl+N),在模版中选择QT,文件类型选择QT Resource File(.qrc)文件。

image-20200826101135160

接下来填写名称,我起的名字是myqrc,路径是默认路径,即项目下。点击下一步后直接点击完成。在项目管理器中就会出现一个资源文件夹,其中新增了一个myqrc.qrc文件。

QT也会打开这个文件,这时候我们在下方点击添加->添加前缀,在前缀上填写"/img"。

image-20200826101639183

然后就添加了一个/img的虚拟文件夹,再点击添加文件,选择你的图标文件添加进去。

image-20200826101838319

这时资源已经加载完成了。

利用资源文件最好在项目下新建一个同名文件夹,可以更高效的管理资源文件。

接下来将之前的代码更改一下,在mainwindow.cpp的构造函数中,两处设置图标的地方分别使用":/img/icon.png"来替换之前的路径,使用qrc资源只需添加一个冒号即可,非常方便快捷。

    m_trayicon->setIcon(QIcon(":/img/icon.png"));

    action_show->setIcon(QIcon(":/img/icon.png"));

4.完整代码

给出完整代码仅供参考。

//mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QSystemTrayIcon>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

private:
    Ui::MainWindow *ui;

    void changeEvent(QEvent *event);

private slots:
    void iconActived(QSystemTrayIcon::ActivationReason reason);

};

#endif // MAINWINDOW_H
//mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"

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

    //新建一个托盘图标对象 //2020-09-22 添加this
    QSystemTrayIcon *m_trayicon = new QSystemTrayIcon(this);

    //设置托盘图标提示:鼠标移动到上面会提示文字
    m_trayicon->setToolTip(QString("托盘图标程序Demo"));
    //设置图标文件,这里先使用路径来设置,后面介绍使用qt资源文件设置图标文件
    //我将图标放在了项目路径下
    m_trayicon->setIcon(QIcon(":/img/icon.png"));

    //连接信号与槽:点击托盘图标执行事件
    connect(m_trayicon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(iconActived(QSystemTrayIcon::ActivationReason)));

    //显示托盘图标
    m_trayicon->show();

    //创建菜单项
    QMenu *m_traymenu = new QMenu();

    //创建菜单项内容
    QAction *action_show = new QAction(tr("Show MainWindow"),this);
    QAction *action_quit = new QAction(tr("Quit"),this);
    QAction *action_test1 = new QAction(tr("test1"),this);
    QAction *action_test2 = new QAction(tr("test2"),this);
    QAction *action_test3 = new QAction(tr("test3"),this);

    //设置一个菜单项的图标作为演示
    action_show->setIcon(QIcon(":/img/icon.png"));
    //将他们关联起来
    m_traymenu->addAction(action_show);
    m_traymenu->addAction(action_quit);
    m_traymenu->addSeparator();//设置一个分割条作为演示
    m_traymenu->addAction(action_test1);
    m_traymenu->addAction(action_test2);
    m_traymenu->addAction(action_test3);
    m_trayicon->setContextMenu(m_traymenu);//设置上下文菜单

    //连接信号与槽:单击菜单项内容执行相应的函数
    connect(action_show,SIGNAL(triggered()),this,SLOT(show()));
    connect(action_quit,SIGNAL(triggered()),this,SLOT(close()));

    m_traymenu->addSeparator();//设置一个分割条作为演示
    //增加一个二级菜单,以此类推可以实现多级菜单
    QMenu *m_traymenu1 = m_traymenu->addMenu("1_0");
    //一个更快捷添加菜单项的方法
    m_traymenu1->addAction("1_1",this,SLOT(close()));

}

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

void MainWindow::iconActived(QSystemTrayIcon::ActivationReason reason)
{
    switch(reason)
    {
    //双击托盘显示窗口
    case QSystemTrayIcon::DoubleClick:
    {
        this->show();
        break;
    }
    default:
        break;
    }
}
void MainWindow::changeEvent(QEvent *event)
{
    if(event->type()!=QEvent::WindowStateChange)
        return;
    if(this->windowState()==Qt::WindowMinimized)
    {
        this->hide();
    }
}

参考资料:

[1]Qt Documentation 之 QSystemTrayIcon

https://doc.qt.io/qt-5/qsystemtrayicon.html

[2]QT多级菜单

https://www.jianshu.com/p/c79958fec4cb

[3]【Qt开发】实现系统托盘,托盘菜单,托盘消息

https://www.cnblogs.com/woniu201/p/10573431.html

[4]qt 实现托盘菜单

https://blog.csdn.net/sinat_33859977/article/details/89489046