问题
最近在做托盘程序,有这么个要求:启动程序时不打开任何界面界面,在系统托盘显示一个程序图标;右击图标时显示菜单,可选择显示弹窗和退出程序,并且点击“退出”程序才会结束程序。
我的托盘程序大致代码如下:
#include <QApplication>
#include <QSystemTrayIcon>
#include <QMenu>
#include <QAction>
#include <QDialog>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QSystemTrayIcon *sti = new QSystemTrayIcon(&app);
sti->setIcon(QIcon("logo.jpg"));
sti->setToolTip("托盘程序");
QMenu *menu = new QMenu();
QAction *show_dialog = menu->addAction("弹窗");
QAction *quit = menu->addAction("退出");
app.connect(show_dialog, &QAction::triggered, [&](bool){
QDialog d;
d.exec();
});
app.connect(quit, &QAction::triggered, [&](bool){
QApplication::instance()->quit();
});
sti->setContextMenu(menu);
sti->show();
return app.exec();
}
如果按照上面这么写的话,点击“弹窗”后显示一个窗口,这时将这个窗口关掉,整个托盘程序就直接就结束了,也就是退出了QApplication的事件循环(代码中的app.exec()),和我的想法背道而驰,我想要的是点击“退出”才会退出QApplication事件循环。
在网上查找解决方法基本上都是继承QDialog,然后重写closeEvent函数,将event忽略掉,这样虽然说不会退出QApplication事件循环,但是弹窗还一直存在,占用内存;而且调用hide()的话,程序也会直接结束。
我的解决方法 :
我试着写App继承QApplication,查看它退出时曾触发过什么事件:
class App : public QApplication{
public:
App(int &argc, char **argv) : QApplication(argc, argv){
installEventFilter(this);
}
bool eventFilter(QObject *watched, QEvent *event){
qDebug() << watched->metaObject()->className() << event->type();
return false;
}
};
程序输出如下:
然后我在它退出前发现有触发过QEvent::Quit事件,于是我把它拦截了下来:
class App : public QApplication{
public:
App(int &argc, char **argv) : QApplication(argc, argv){
installEventFilter(this);
}
bool eventFilter(QObject *watched, QEvent *event){
return (watched == this && event->type() == QEvent::Quit);
}
};
经测试,这样做确实可以做到关闭唯一的窗口,QApplication不退出。但是还不够方便,于是我对着QApplication按下了F1,看到了这个:
果然还是要多看文档啊😭。
最后全部代码:
#include <QApplication>
#include <QSystemTrayIcon>
#include <QMenu>
#include <QAction>
#include <QDialog>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
app.setQuitOnLastWindowClosed(false);
QSystemTrayIcon *sti = new QSystemTrayIcon(&app);
sti->setIcon(QIcon("logo.jpg"));
sti->setToolTip("托盘程序");
QMenu *menu = new QMenu();
QAction *show_dialog = menu->addAction("弹窗");
QAction *quit = menu->addAction("退出");
app.connect(show_dialog, &QAction::triggered, [&](bool){
QDialog d;
d.exec();
});
app.connect(quit, &QAction::triggered, [&](bool){
aApp->quit();
});
sti->setContextMenu(menu);
sti->show();
return app.exec();
}