Ho un'applicazione con l'icona dell'area di notifica, quindi la finestra principale può ignorare l'evento di chiusura. Sto cercando di salvare le preferenze e la cronologia per la chiusura dell'applicazione. Voglio anche gestire il logout quando l'applicazione è in esecuzione. Mentre l'applicazione è multipiattaforma, è più comoda/utile in GNU/Linux, quindi il comportamento di logout di Windows è molto meno preoccupante. Questo è un esempio compilabile minimo che è stato utilizzato per il comportamento sperimentazione di diversi ambienti desktop/window manager:Come chiudere QApplication con garbo al logout?
// 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()
maggior parte degli ambienti in piena regola del desktop sul tentativo di logout per chiudere la finestra visibile. Ma poiché l'app di test non esce quando è chiusa, la disconnessione viene interrotta/annullata. Se la disconnessione viene eseguita mentre la finestra è invisibile, si chiude con garbo (proprio come voglio io). Meno ambienti desktop/gestori di finestre con funzionalità ignora l'applicazione ancora in esecuzione e l'uscita. Molti di loro non avvertono nemmeno l'applicazione relativa alla disconnessione, quindi non c'è né "closeEvent()", né "onAboutToQuit()", né "M :: ~ M()" nel file, a cui l'output del programma viene reindirizzato.
I risultati dettagliati (tutti i risultati non Windows sono da Manjaro GNU/Linux):
ambienti conclamata desktop che annulla il logout se la finestra visibile si rifiuta di uscire, terminare l'applicazione invisibile con grazia:
closeEvent() onAboutToQuit() M::~M() { KDE, XFCE, Mate, GNOME, Cinnamon }
Tutti gli altri non annullano la chiusura, ma alcuni di essi cercano di avvisare l'applicazione.
Non ho idea del motivo per cui è presente nel registro onAboutToQuit(), ma M :: ~ M() non è in questo caso ...
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 }
Il comportamento è esattamente le s per qualsiasi combinazione di (GCC 4.9.1 o Clang 3.4.2) E (Qt 4.8.6 O Qt 5.3.1). Tuttavia, quando ho provato Qt 4.8 e Qt 5.2 su Xubuntu, i risultati erano un po 'diversi: non c'era alcun blocco con Qt 5.2 in XFCE - l'applicazione finiva con garbo indipendentemente dalla visibilità della finestra principale. Ma il blocco era presente con Qt 4.8 (proprio come in Manjaro).
So che è possibile gestire correttamente il logout (senza blocco) perché ci sono diverse applicazioni che lo fanno bene. Tutti hanno l'icona dell'area di notifica, sono chiusi all'area di notifica, ma non bloccano la disconnessione.
- basato su Qt: GoldenDict, Transmission-Qt, Kopete;
- basato su GTK: Audacious, Pidgin.
Ho esaminato il codice sorgente di quelle basate su Qt e ha trovato niente di speciale in CloseEvent gestione:
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);
}
}
Quindi le domande:
Come garantire che la mia domanda fa non bloccare la disconnessione anche se la finestra principale è visibile?
Come assicurarsi che onAboutToQuit() e ~ M() vengano richiamati nel logout in quanti più ambienti desktop/gestori di finestre possibile?
ho il sospetto che alcuni segnali del sistema devono essere ascoltati, ma non ho idea di che esattamente ...
Mi hai indicato nella giusta direzione. Grazie mille. Comunque sia GoldenDict che Kopete usano [QApplication :: commitData] (http://qt-project.org/doc/qt-4.8/qapplication.html#commitData) - Credo che sia più appropriato nel mio caso. E (almeno per GoldenDict in KDE) sembra che commitData() sia sufficiente perché la finestra viene ripristinata correttamente quando viene ripristinata la sessione. Quindi userò [QApplication :: commitData] (http://qt-project.org/doc/qt-4.8/qapplication.html#commitData) o [QApplication :: commitDataRequest] (http: // qt- project.org/doc/qt-4.8/qapplication.html#commitDataRequest). – vedg
[Questo bug/regressione Qt5] (https://bugreports.qt-project.org/browse/QTBUG-28228) non mi lascia scelta: per supportare sia Qt4 che Qt5 (5.2 o successivo!) Avrò per utilizzare [QApplication :: commitDataRequest] (http://qt-project.org/doc/qt-5/qguiapplication.html#commitDataRequest). – vedg