2014-08-03 17 views
5

Tôi có một ứng dụng có biểu tượng khu vực thông báo, vì vậy cửa sổ chính có thể bỏ qua sự kiện đóng. Tôi đang cố gắng lưu các sở thích và lịch sử về việc bỏ ứng dụng. Tôi cũng muốn xử lý đăng xuất khi ứng dụng đang chạy. Trong khi ứng dụng là đa nền tảng, nó thuận tiện nhất/hữu ích trong GNU/Linux, vì vậy hành vi đăng xuất Windows ít quan tâm hơn nhiều. Đây là một ví dụ compilable tối thiểu đã được sử dụng để thử nghiệm hành vi của các môi trường máy tính để bàn/quản lý cửa sổ khác nhau:Làm thế nào để thoát khỏi QApplication một cách duyên dáng khi đăng xuất?

// 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() 

Hầu hết các môi trường máy tính để bàn toàn diện về nỗ lực logout để đóng cửa sổ nhìn thấy được. Nhưng vì ứng dụng thử nghiệm không thoát khi đóng, việc đăng xuất bị gián đoạn/bị hủy. Nếu đăng xuất được thực hiện trong khi cửa sổ là vô hình, nó thoát một cách duyên dáng (giống như tôi muốn nó). Các trình quản lý cửa sổ/cửa sổ máy tính để bàn đầy đủ tính năng ít hơn bỏ qua ứng dụng vẫn đang chạy và thoát. Hầu hết trong số họ thậm chí không cảnh báo ứng dụng về đăng xuất, vì vậy không có "closeEvent()", cũng không phải "onAboutToQuit()", cũng không phải "M :: ~ M()" trong tệp, mà đầu ra chương trình được chuyển hướng.

Kết quả chi tiết (tất cả các kết quả không phải là Windows là từ Manjaro GNU/Linux):

  1. Full-thổi các môi trường máy tính để bàn mà hủy đăng xuất khỏi nếu cửa sổ có thể nhìn thấy từ chối để thoát, kết thúc ứng dụng vô hình một cách duyên dáng:

    closeEvent() 
    onAboutToQuit() 
    M::~M() 
        { KDE, XFCE, Mate, GNOME, Cinnamon } 
    

    Tất cả những người khác không hủy bỏ, nhưng một số người trong số họ cố gắng cảnh báo ứng dụng.

  2. Tôi không có ý tưởng tại sao onAboutToQuit() hiện diện trong nhật ký, nhưng M :: ~ M() không phải là trong trường hợp này ...

    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 } 

hành vi này là chính xác s ame cho bất kỳ sự kết hợp nào (GCC 4.9.1 HOẶC Clang 3.4.2) VÀ (Qt 4.8.6 HOẶC Qt 5.3.1). Tuy nhiên khi tôi thử Qt 4.8 và Qt 5.2 trên Xubuntu, kết quả có phần khác nhau: không có sự chặn nào với Qt 5.2 trong XFCE - ứng dụng đã hoàn thành một cách duyên dáng bất kể khả năng hiển thị cửa sổ chính. Nhưng việc chặn lại có mặt với Qt 4.8 (giống như ở Manjaro).

Tôi biết rằng có thể xử lý đăng xuất đúng cách (không chặn) vì có một số ứng dụng hoạt động tốt. Tất cả đều có biểu tượng khu vực thông báo, được đóng vào khu vực thông báo, nhưng không chặn đăng xuất.

  • Qt: GoldenDict, Transmission-Qt, Kopete;
  • dựa trên GTK: Audacious, Pidgin.

Tôi đã kiểm tra mã nguồn của những Qt-based và thấy không có gì đặc biệt trong việc xử lý 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); 
    } 
} 

Vì vậy, các câu hỏi sau:

  1. Làm thế nào để đảm bảo rằng ứng dụng của tôi không không chặn đăng xuất ngay cả khi cửa sổ chính hiển thị?

  2. Làm cách nào để đảm bảo rằng onAboutToQuit() và ~ M() được gọi là đăng xuất trong nhiều trình quản lý cửa sổ/môi trường máy tính để bàn nhất có thể?

Tôi nghi ngờ rằng một số tín hiệu hệ thống nên được lắng nghe, nhưng tôi không có ý tưởng mà chính xác ...

Trả lời

5

QApplication đã signal liên quan đến hành động phiên OS - bạn có thể dễ dàng xử lý nó. Để biết thêm chi tiết, hãy xem tài liệu Qt Session Management trang

+0

Bạn đã chỉ cho tôi đúng hướng. Cảm ơn nhiều. Tuy nhiên cả GoldenDict và Kopete đều sử dụng [QApplication :: commitData] (http://qt-project.org/doc/qt-4.8/qapplication.html#commitData) - Tôi tin rằng nó phù hợp hơn trong trường hợp của tôi. Và (ít nhất là cho GoldenDict trong KDE) có vẻ như là commitData() là đủ bởi vì cửa sổ được khôi phục tốt khi phiên được khôi phục. Vì vậy, tôi sẽ sử dụng [QApplication :: commitData] (http://qt-project.org/doc/qt-4.8/qapplication.html#commitData) hoặc [QApplication :: commitDataRequest] (http: // qt- project.org/doc/qt-4.8/qapplication.html#commitDataRequest). – vedg

+0

[Lỗi Qt5/hồi quy] (https://bugreports.qt-project.org/browse/QTBUG-28228) để tôi không có lựa chọn - để hỗ trợ cả Qt4 và Qt5 (5.2 hoặc mới hơn!), Tôi sẽ có để sử dụng [QApplication :: commitDataRequest] (http://qt-project.org/doc/qt-5/qguiapplication.html#commitDataRequest). – vedg

Các vấn đề liên quan