18

How do I properly implement a "minimize to tray" function in Qt?

I tried the following code inside QMainWindow::changeEvent(QEvent *e), but the window simply minimizes to the taskbar and the client area appears blank white when restored.

if (Preferences::instance().minimizeToTray())
{
    e->ignore();
    this->setVisible(false);
}

Attempting to ignore the event doesn't seem to do anything, either.

Jake Petroules
  • 23,472
  • 35
  • 144
  • 225
  • 2
    From personal experience I've learned that you usually don't want this. The tray isn't the place for minimized applications. The tray _is_ the correct place for applets that watch for events (Bluetooth, network status, that kind of stuff). If your application contains that kind of functionality, it may be wise to split that part off in its own process. This helper process will then appear always in the tray, and nowhere else. – MSalters Jul 26 '10 at 08:37
  • 5
    Some users like this functionality, though. In my application, it's not the default behavior, but is presented as an option in the preferences dialog. I figure that kind of balances correct design and user preference. – Jake Petroules Jul 27 '10 at 07:22

4 Answers4

20

Apparently a small delay is needed to process other events (perhaps someone will post the exact details?). Here's what I ended up doing, which works perfectly:

void MainWindow::changeEvent(QEvent* e)
{
    switch (e->type())
    {
        case QEvent::LanguageChange:
            this->ui->retranslateUi(this);
            break;
        case QEvent::WindowStateChange:
            {
                if (this->windowState() & Qt::WindowMinimized)
                {
                    if (Preferences::instance().minimizeToTray())
                    {
                        QTimer::singleShot(250, this, SLOT(hide()));
                    }
                }

                break;
            }
        default:
            break;
    }

    QMainWindow::changeEvent(e);
}
Jake Petroules
  • 23,472
  • 35
  • 144
  • 225
  • Interesting fact that it requires a delay to work, but very useful :) Would like to know if there's a "cleaner" solution though.. But I will try your code, too :) – Exa Jul 26 '10 at 09:45
  • Hi im trying this and getting error "Preferences" has not been declared? – GeneCode Sep 15 '17 at 04:27
11

In addition to what Jake Petroules said, it appears that simply doing:

QTimer::singleShot(0, this, SLOT(hide()));

is enough. From http://qt-project.org/doc/qt-4.8/qtimer.html#details :

As a special case, a QTimer with a timeout of 0 will time out as soon as all the events in the window system's event queue have been processed.

This way you don't have the problem of selecting an appropriate delay value...

Bakuriu
  • 98,325
  • 22
  • 197
  • 231
Manjabes
  • 1,884
  • 3
  • 17
  • 34
8
 void main_window::create_tray_icon()
 {
    m_tray_icon = new QSystemTrayIcon(QIcon(":/icon.png"), this);

    connect( m_tray_icon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(on_show_hide(QSystemTrayIcon::ActivationReason)) );

    QAction *quit_action = new QAction( "Exit", m_tray_icon );
    connect( quit_action, SIGNAL(triggered()), this, SLOT(on_exit()) );

    QAction *hide_action = new QAction( "Show/Hide", m_tray_icon );
    connect( hide_action, SIGNAL(triggered()), this, SLOT(on_show_hide()) );

    QMenu *tray_icon_menu = new QMenu;
    tray_icon_menu->addAction( hide_action );
    tray_icon_menu->addAction( quit_action );

    m_tray_icon->setContextMenu( tray_icon_menu );

    m_tray_icon->show();
  }

void main_window::on_show_hide( QSystemTrayIcon::ActivationReason reason )
{
    if( reason )
    {
        if( reason != QSystemTrayIcon::DoubleClick )
        return;
    }

    if( isVisible() )
    {
        hide();
    }
    else
    {
        show();
        raise();
        setFocus();
    }
}

That's how I realize a "minimize to tray". You can now minimize either by double clicking on the icon, or by right-clicking and selecting "Show/Hide" in the menu.

Exa
  • 4,020
  • 7
  • 43
  • 60
  • Thanks, but I am specifically looking for a way to hide the window when the user minimizes it. ;) – Jake Petroules Jul 26 '10 at 07:07
  • Ah okay, sorry, got you wrong there :) So now it's just an example for those who need an working icon with show/hide-functions :) – Exa Jul 26 '10 at 07:30
  • 2
    +1 for a nice piece of code, but your "connect( hide_action, SIGNAL(triggered()), this, SLOT(on_show_hide()) );" is a bit wrong. If you run your Qt application in debug mode, you should see something like: "QObject::connect: No such slot on_show_hide()..." You can't use same "on_show_hide( QSystemTrayIcon::ActivationReason reason )" slot just like "on_show_hide()" – Gediminas Jan 08 '13 at 10:14
0

I have found that the showMinimized() slot works without a QTimer delay, so you can use code like:

mw->show();
if ( qApp->arguments().contains( "--startHidden" ) )
  mw->showMinimized();

in your main() to show a main window and immediately iconize it, when desired.

Draken
  • 3,134
  • 13
  • 34
  • 54
MangoCat
  • 89
  • 5