Minimizing Application to Tray



  • Hi,

    I have got a problem. I want my application to minimize to tray when I click on minimize button or close button.

    I have got success with close button but when I click on minimize button, all the content on the window i.e. buttons, images, text etc disappears but a white window remains on screen with no content at all and when I click on tray and click show all the content becomes visible again.

    I am using following code to minimize application to tray.

    @void MainWindow::changeEvent(QEvent *event)
    {
    if(event->type() == QEvent::WindowStateChange) {
    if(isMinimized()) {
    this->hide();
    event->ignore();
    }
    }
    }@

    Thanks for any kind of help..



  • Hi, happy to find this post, I'm trying to solve the same problem too :)

    What I think at the actual state of my tests (not got solution yet) is that the event should be managed with some other things. It seems a problem of repaint without the window in place of the white rectangle.

    If I find more I update immediately!



  • You should at least call the base class' implementation of changeEvent() for the cases where you do not handle it yourself.

    So add at the end of you method (supposing you have a QMainWindow based UI):

    @
    QMainWindow::changeEvent(event);
    @

    (a guideline that holds for almost every virtual method that you re-implement)



  • Volker I have tried this too but can't get through.

    Will you please explain about QMainWindow based UI?



  • Your class MainWindow inherits from some base class. I suppose this is QMainWindow (others could be QDialog or QWidget for example).

    And for the code. Did you try already:

    @
    void MainWindow::changeEvent(QEvent *event)
    {
    QMainWindow::changeEvent(event);
        if(event->type() == QEvent::WindowStateChange)
            if(isMinimized())
                this->hide();
    }
    @

    This let's Qt do the regular work and after the window is minimized you hide it.



  • I tried this but the same result again..
    Just getting the blank screen while minimizing the application..
    I can't understand what's the problem..



  • Hi Khalidmushta,

    what exactly do you want to achieve?

    Minimize to system try, so to have a tray icon? Are you using "QSystemTryIcon":http://doc.qt.nokia.com/latest/qsystemtrayicon.html ?
    Can you show a bit more of your code which results in the problem? The best would be a very simple, small example showing the problem.



  • I want to minimize my application to tray when I click on Minimize button and same for the close button..

    I am using QSystemTryIcon, works fine with close button, but not working with minimize button.
    When I click on minimize button all the contents get hide and a screen with white background continues to show..

    This is for the minimize button that shows white background of screen but hides the content..

    @void MainWindow::changeEvent(QEvent *event)
    {
    if(event->type() == QEvent::WindowStateChange) {
    if(isMinimized())
    this->hide();
    event->ignore();
    }
    }@

    This is for the close button that works fine.

    @void MainWindow::closeEvent(QCloseEvent *event)
    {
    if (trayIcon->isVisible()) {
    this->hide();
    event->ignore();
    }
    }@



  • [quote author="khalidmushtaq65" date="1300015096"]

    This is for the minimize button that shows white background of screen but hides the content..

    @void MainWindow::changeEvent(QEvent *event)
    {
    if(event->type() == QEvent::WindowStateChange) {
    if(isMinimized())
    this->hide();
    event->ignore();
    }
    }@

    [/quote]

    Not sure if this is the problem, but shouldn't that be:

    @void MainWindow::changeEvent(QEvent *event)
    {
    if(event->type() == QEvent::WindowStateChange) {
    if(isMinimized()) {
    this->hide();
    event->ignore();
    }
    }
    QMainWindow::changeEvent(event);
    }@



  • I have tried that too but the problem still exists..



  • @MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent)
    {
    ui.setupUi(this);

    icon = new QSystemTrayIcon(this);
    icon->show();
    
    menu = new QMenu(this);
    
    quitAction = new QAction("Quit",this);
    connect(quitAction,SIGNAL(triggered()),this,SLOT(close()));
    menu->addAction(quitAction);
    icon->setContextMenu(menu);
    
    restore = new QAction("Restore",this);
    restore->setEnabled(false);
    connect(restore,SIGNAL(triggered()),this,SLOT(showNormal()));
    menu->addAction(restore);
    

    }

    void MainWindow::changeEvent(QEvent *e)
    {
    QMainWindow::changeEvent(e);
    switch (e->type()) {
    case QEvent::LanguageChange:
    ui.retranslateUi(this);
    break;
    case QEvent::WindowStateChange:
    {
    if(isMinimized())
    {
    hide();
    restore->setEnabled(true);
    }
    else
    {
    restore->setEnabled(false);
    }
    }
    break;
    default:
    break;
    }
    }
    @
    Just put this code together to test Your issue it works ass expected on my Linux box. Why are You ignoring the change event? It's not like the close event witch You need to ignore to prevent your window from actually closing.



  • I am sorry to say that it's not working too. Same problem exists..



  • Well, if kkrzewniak tested the code he posted, and you just claim "it doesn't work", perhaps you should start by giving more information. For starters: what platform are we talking about anyway? Can you post a minimal, yet complete, complileable example that shows the problem?



  • I am using Microsoft Windows 7 Ultimate.

    This is mainwindow.h
    @#ifndef MAINWINDOW_H
    #define MAINWINDOW_H

    #include <QWidget>
    #include <QSystemTrayIcon>

    #include <QMainWindow>

    class QMenu;

    namespace Ui {
    class MainWindow;
    }

    class MainWindow : public QMainWindow
    {
    Q_OBJECT

    public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
    bool createConnection();

    private:
    Ui::MainWindow *ui;

    // Tray Icon Functions
    void createTrayActions();
    void createTrayIcon();
    void setTrayIcon();
    
    void closeEvent(QCloseEvent *);
    void changeEvent(QEvent *);
    
    QSystemTrayIcon *trayIcon;
    QMenu *trayIconMenu;
    QAction *showHideTray;
    QAction *closeTray;
    

    private slots:
    void trayIconClicked(QSystemTrayIcon::ActivationReason);
    void showHideWindow();

    };

    #endif // MAINWINDOW_H@

    This is mainwindow.cpp

    @#include "mainwindow.h"
    #include "ui_mainwindow.h"
    #include <QMessageBox>
    #include <QtSql/QSqlError>
    #include <QMenu>
    #include <QCloseEvent>
    #include <QDir>

    MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
    {
    ui->setupUi(this);

    createTrayActions();
    createTrayIcon();
    setTrayIcon();
    trayIcon->show();
    

    }
    void MainWindow::changeEvent(QEvent *event)
    {
    QMainWindow::changeEvent(event);
    if(event->type() == QEvent::WindowStateChange) {
    if(isMinimized())
    this->hide();
    }
    }

    void MainWindow::closeEvent(QCloseEvent *event)
    {
    if (trayIcon->isVisible()) {
    showHideWindow();
    event->ignore();
    }
    }

    void MainWindow::showHideWindow()
    {
    if(this->isVisible())
    {
    this->hide();
    showHideTray->setIcon(QIcon(":/images/arrow_up.ico"));
    showHideTray->setText("Show Main Window");
    }
    else
    {
    this->show();
    showHideTray->setIcon(QIcon(":/images/arrow_down.ico"));
    showHideTray->setText("Hide Main Window");
    }
    }

    void MainWindow::trayIconClicked(QSystemTrayIcon::ActivationReason reason)
    {
    if(reason == QSystemTrayIcon::DoubleClick)
    showHideWindow();
    }

    void MainWindow::setTrayIcon()
    {
    trayIcon->setIcon(QIcon(":/images/Download.ico"));
    }
    void MainWindow::createTrayIcon()
    {
    trayIconMenu = new QMenu(this);
    trayIconMenu->addAction(showHideTray);
    trayIconMenu->addSeparator();
    trayIconMenu->addAction(closeTray);

    trayIcon = new QSystemTrayIcon(this);
    trayIcon->setContextMenu(trayIconMenu);
    
    connect(
            trayIcon,
          SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
            this,
            SLOT(trayIconClicked(QSystemTrayIcon::ActivationReason))
           );
    

    }
    void MainWindow::createTrayActions()
    {
    showHideTray = new QAction(tr("&Hide Main Window"), this);
    connect(showHideTray, SIGNAL(triggered()), this, SLOT(showHideWindow()));
    showHideTray->setIcon(QIcon(":/images/arrow_down.ico"));
    closeTray = new QAction(tr("&Exit"), this);
    connect(closeTray, SIGNAL(triggered()), qApp, SLOT(quit()));
    closeTray->setIcon(QIcon(":/images/Delete.png"));
    }
    MainWindow::~MainWindow()
    {
    delete ui;
    delete trayIcon;
    delete trayIconMenu;
    delete showHideTray;
    delete closeTray;
    }@



  • If it can be useful for those is trying to solve this problem:

    Actually I develop under linux ubuntu then rebuild under windows 7 ultimate and under xp. I use virtual machines but it doesn't matter, the machine is as a real computer (little slow).

    Using the beta Qt-Quick and Qt-Creator + Qt-sdk (all Qt wrote correct, eh Andre ? :) :) ) I experience problems under Windows 7. Strange problems that probably are not so depending from the application itself.



  • Hi khalidmushta..,

    I created an app using sys tray icon which works on windows 7.
    You can find the sources on "gitorious":http://gitorious.org/giesbert-s-software/giesbert-s-world-clock

    The code, where I minimize / restore to / from system tray looks like this:

    @
    void GiWorldClock::hideEvent(QHideEvent* pEvent)
    {
    if(isMinimized())
    {
    ::ShowWindow(this->winId(), SW_HIDE);
    m_pRestoreAction->setEnabled(true);
    m_pMinAction->setDisabled(true);
    }
    QWidget::hideEvent(pEvent);
    }

    void GiWorldClock::showEvent(QShowEvent* pEvent)
    {
    QWidget::showEvent(pEvent);
    ::ShowWindow(this->winId(), SW_SHOW);
    m_pRestoreAction->setDisabled(true);
    m_pMinAction->setEnabled(true);
    }
    @



  • Ok, as I use win32 functions, it's n9ot portable :-) but it works on windows....



  • I started the debugger on and when I minimize application it displays following error..

    (Internal error: pc 0x111 in read in psymtab, but not in symtab.)

    When I close application, it shows no error and hides the application to tray..



  • Just a small advice, what I will do in your case. Why don't remove from the windows the minimize button, leaving only the close, that works fine? Two buttons that do the same thing can confuse the user, not ?



  • ya.. nice advice:-)



  • I had the same problem with a windowless device... Decided for this solution :)



  • To remove the button is no solution...
    Here is a dirty hack, but it works.

    @void MainWindow::changeEvent(QEvent *e)
    {
    if(isMinimized())
    {
    show(); // The hack
    slotToggleVisibility(); // Your tray icon code
    }
    }@



  • If anyone still has this issue, the solution is to call qApp->processEvents() before calling hide(), ie.

    @void ApplicationWindow::changeEvent(QEvent *event)
    {
    QWidget::changeEvent(event);
    if (event->type() == QEvent::WindowStateChange)
    {
    QWindowStateChangeEvent e = (QWindowStateChangeEvent)event;
    // make sure we only do this for minimize events
    if ((e->oldState() != Qt::WindowMinimized) && isMinimized())
    {
    qApp->processEvents();
    hide();
    event->ignore();
    }
    }
    }
    @

    The test for the old state is probably unnecessary, but can't hurt.

    If this doesn't help in your particular situation, try calling hide from a single-shot timer, so that the call is outside the minimize event handler:

    @void ApplicationWindow::changeEvent(QEvent *event)
    {
    QWidget::changeEvent(event);
    if (event->type() == QEvent::WindowStateChange)
    {
    QWindowStateChangeEvent e = (QWindowStateChangeEvent)event;
    // make sure we only do this for minimize events
    if ((e->oldState() != Qt::WindowMinimized) && isMinimized())
    {
    QTimer::singleShot(0, this, SLOT(hide()));
    event->ignore();
    }
    }
    }
    @



  • Hi Ryan,

    take care if you call qApp->processEvents(), as you reopen the event queue and it might happen that an event, that should be worked on after you finished is processed now. I don't say never do it, but do it with care and always remember, synchronous workflow is broken up here.

    Even a deleteLater might be processed so an object can be deleted, where you expect it not to be deleted.



  • Yep, that's why I gave the second block of code (which I've just realised had the processEvents() call still in it - it wasn't meant to be there)


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.