我有一个通知区域图标的应用程序,所以主窗口可能会忽略closures事件。 我正在尝试保存应用程序退出时的偏好和历史logging。 我也想在应用程序运行时处理注销。 虽然应用程序是跨平台的,但它在GNU / Linux中是最方便/有用的,所以Windows注销行为不太受关注。 这是用于testing不同桌面环境/窗口pipe理器行为的最小可编译示例:
// main.cpp # include <QApplication> # include <QMainWindow> # include <QCloseEvent> # include <QTimer> # include <iostream> class M : public QMainWindow { Q_OBJECT public: ~M(); public slots: void onAboutToQuit(); private: void closeEvent(QCloseEvent *); }; M::~M() { std::cout << "M::~M()" << std::endl; } void M::onAboutToQuit() { std::cout << "onAboutToQuit()" << std::endl; } void M::closeEvent(QCloseEvent * e) { std::cout << "closeEvent()" << std::endl; hide(); QTimer::singleShot(5000, this, SLOT(show())); e->ignore(); } int main(int argc, char * argv[]) { QApplication app(argc, argv); M m; m.setWindowModality(Qt::NonModal); m.connect(& app, SIGNAL(aboutToQuit()), SLOT(onAboutToQuit())); m.show(); return app.exec(); } # include "main.moc" // CMakeLists.txt cmake_minimum_required(VERSION 2.6) project(closeeventbug) option(QT5 "Use Qt5" OFF) if(QT5) find_package(Qt5Core REQUIRED) find_package(Qt5Widgets REQUIRED) else() find_package(Qt4 REQUIRED) include_directories(${QT_INCLUDES}) endif() include_directories(${CMAKE_CURRENT_BINARY_DIR}) set(CMAKE_AUTOMOC ON) add_executable(closeeventbug main.cpp) if(QT5) qt5_use_modules(closeeventbug Core Widgets) else() target_link_libraries(closeeventbug ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY}) endif()
注销时大多数成熟的桌面环境都尝试closures可见的窗口。 但是由于testing应用程序在closures时不会退出,所以注销将被中断/取消。 如果注销是在窗口不可见的情况下执行的,则优雅地退出(就像我想要的那样)。 较lessfunction的桌面环境/窗口pipe理器忽略仍在运行的应用程序并退出。 他们中的大多数甚至不提醒注销应用程序,所以程序输出的文件中既没有“closeEvent()”,也没有“onAboutToQuit()”,也没有“M ::〜M()”被redirect。
详细的结果(所有非Windows结果都来自Manjaro GNU / Linux):
如果可见窗口拒绝退出,则取消注销的成熟桌面环境,优雅地完成不可见的应用程序:
closeEvent() onAboutToQuit() M::~M() { KDE, XFCE, Mate, GNOME, Cinnamon }
所有其他人不会取消戒烟,但其中一些人试图警告申请。
我不知道为什么onAboutToQuit()出现在日志中,但M ::〜M()不在这种情况下…
closeEvent() onAboutToQuit() { Windows7 }
3。
closeEvent() { icewm, E17 }
4。
<nothing in the log> { RazorQt, LxDE, LxQt, i3, budgie, fluxbox, awesome, openbox, wmii, E16, pekWM, uwm, fvwm, xmonad, spectrwm, windowmaker, herbstluftwm, WindowsXP }
对于(GCC 4.9.1或Clang 3.4.2)和(Qt 4.8.6或Qt 5.3.1)的组合,其行为完全相同。 但是,当我在Xubuntu上试用Qt 4.8和Qt 5.2时,结果有些不同:XFCE中没有使用Qt 5.2进行阻塞 – 无论主窗口可见性如何,应用程序都能正常完成。 但是Qt4.8(和Manjaro一样)是阻塞的。
我知道有可能正确处理注销(没有阻塞),因为有几个应用程序可以做到这一点。 他们都有通知区域图标,closures通知区域,但不阻止注销。
我已经检查了基于Qt的源代码,发现没有什么特别的处理closeEvent:
https://github.com/goldendict/goldendict/blob/master/mainwindow.cc void MainWindow::closeEvent( QCloseEvent * ev ) { if ( cfg.preferences.enableTrayIcon && cfg.preferences.closeToTray ) { ev->ignore(); hide(); } else { ev->accept(); qApp->quit(); } } https://github.com/bfleischer/transmission/blob/master/qt/mainwin.cc void TrMainWindow :: closeEvent( QCloseEvent * event ) { // if they're using a tray icon, close to the tray // instead of exiting if( !myPrefs.getBool( Prefs :: SHOW_TRAY_ICON ) ) event->accept( ); else { toggleWindows( false ); event->ignore( ); } } void TrMainWindow :: toggleWindows( bool doShow ) { if( !doShow ) { hide( ); } else { if ( !isVisible( ) ) show( ); if ( isMinimized( ) ) showNormal( ); //activateWindow( ); raise( ); QApplication::setActiveWindow( this ); } } git clone git://anongit.kde.org/kopete void KopeteWindow::closeEvent ( QCloseEvent *e ) { // if we are not ok to exit on close and we are not shutting down then just do what needs to be done if a // window is closed. KopeteApplication *app = static_cast<KopeteApplication *> ( kapp ); if ( !shouldExitOnClose() && !app->isShuttingDown() && !app->sessionSaving() ) { // BEGIN of code borrowed from KMainWindow::closeEvent // Save settings if auto-save is enabled, and settings have changed if ( settingsDirty() && autoSaveSettings() ) saveAutoSaveSettings(); if ( queryClose() ) { e->accept(); } // END of code borrowed from KMainWindow::closeEvent kDebug ( 14000 ) << "just closing because we have a system tray icon"; } else { kDebug ( 14000 ) << "delegating to KXmlGuiWindow::closeEvent()"; KXmlGuiWindow::closeEvent ( e ); } }
所以问题:
如何确保即使主窗口可见,我的应用程序也不会阻止注销?
如何确保在尽可能多的桌面环境/窗口pipe理器上注销onAboutToQuit()和〜M()?
我怀疑应该听取一些系统信号,但我不知道究竟是…
QApplication具有与OS会话操作相关的信号 – 您可以轻松处理它。 有关更多详细信息,请参阅Qt文档会话管理页面