23

Is there a way to minimize to tray in PyQt4? I've already worked with the QSystemTrayIcon class, but now I would like to minimize or "hide" my app window, and show only the tray icon.

Has anybody done this? Any direction would be appreciated.

Using Python 2.5.4 and PyQt4 on Window XP Pro

Gavin Vickery
  • 303
  • 1
  • 3
  • 9

7 Answers7

36

It's pretty straightforward once you remember that there's no way to actually minimize to the system tray.

Instead, you fake it by doing this:

  1. Catch the minimize event on your window
  2. In the minimize event handler, create and show a QSystemTrayIcon
  3. Also in the minimize event handler, call hide() or setVisible(false) on your window
  4. Catch a click/double-click/menu item on your system tray icon
  5. In your system tray icon event handler, call show() or setVisible(true) on your window, and optionally hide your tray icon.
Hat
  • 540
  • 8
  • 25
Kevin
  • 8,353
  • 3
  • 37
  • 33
12

Code helps, so here's something I wrote for an application, except for the closeEvent instead of the minimize event.

Notes:

"closeEvent(event)" is an overridden Qt event, so it must be put in the class that implements the window you want to hide.

"okayToClose()" is a function you might consider implementing (or a boolean flag you might want to store) since sometimes you actually want to exit the application instead of minimizing to systray.

There is also an example of how to show() your window again.

def __init__(self):
  traySignal = "activated(QSystemTrayIcon::ActivationReason)"
  QtCore.QObject.connect(self.trayIcon, QtCore.SIGNAL(traySignal), self.__icon_activated)

def closeEvent(self, event):
  if self.okayToClose(): 
    #user asked for exit
    self.trayIcon.hide()
    event.accept()
  else:
    #"minimize"
    self.hide()
    self.trayIcon.show() #thanks @mojo
    event.ignore()

def __icon_activated(self, reason):
  if reason == QtGui.QSystemTrayIcon.DoubleClick:
    self.show()
  • #"minimize" self.hide() self.trayIcon.show() event.ignore() – Dmitry Mukhin May 19 '09 at 17:00
  • I added @mojo's suggestion for flexibility in how the system tray icon is handled. The original code came from an application where the tray icon was always visible, the self.trayIcon.hide() was so that the icon didn't stick around on the system tray after exit (typical windows behaviour, sadly). Note that now the user must implement the showEvent(event) method and call self.trayIcon.hide() to complete this example. I should have just posted a minimize/restore-only example in the first place :) –  May 19 '09 at 18:21
7

Just to add to the example by Chris:

It is crucial that you use the Qt notation when declaring the signal, i.e.

correct:

self.connect(self.icon, SIGNAL("activated(QSystemTrayIcon::ActivationReason)"), self.iconClicked)

and not the PyQt one

incorrect and won't work:

self.connect(self.icon, SIGNAL("activated(QSystemTrayIcon.ActivationReason)"), self.iconClicked)

Note the :: in the signal string. This took me about three hours to figure out.

  • Please embelish the above with >> "CRUCIAL that your use Qt NOTATION"<<. Have been here with a long time ..think hours of thinking.. doing other things byt until I read this post.. == QtCore.SISNAL("activated(QSystemTrayIcon::ActivationReason)").. ie no reason ;-) – PedroMorgan Jan 13 '11 at 04:53
  • def create_sys_tray(self): self.sysTray = QtGui.QSystemTrayIcon(self) self.sysTray.setIcon( QtGui.QIcon('../images/corp/blip_32.png') ) self.sysTray.setVisible(True) self.connect(self.sysTray, QtCore.SIGNAL("activated(QSystemTrayIcon::ActivationReason)"), self.on_sys_tray_activated) self.sysTrayMenu = QtGui.QMenu(self) act = self.sysTrayMenu.addAction("FOO") def on_sys_tray_activated(self, reason): print "reason-=" , reason – PedroMorgan Jan 13 '11 at 04:58
4

This was an edit of vzades response, but it was rejected on a number of grounds. It does the exact same thing as their code but will also obey the minimize event (and run without syntax errors/missing icons).

import sys
from PyQt4 import QtGui, QtCore


class Example(QtGui.QWidget):
    def __init__(self):
        super(Example, self).__init__()
        self.initUI()

    def initUI(self):
        style = self.style()

        # Set the window and tray icon to something
        icon = style.standardIcon(QtGui.QStyle.SP_MediaSeekForward)
        self.tray_icon = QtGui.QSystemTrayIcon()
        self.tray_icon.setIcon(QtGui.QIcon(icon))
        self.setWindowIcon(QtGui.QIcon(icon))

        # Restore the window when the tray icon is double clicked.
        self.tray_icon.activated.connect(self.restore_window)

    def event(self, event):
        if (event.type() == QtCore.QEvent.WindowStateChange and 
                self.isMinimized()):
            # The window is already minimized at this point.  AFAIK,
            # there is no hook stop a minimize event. Instead,
            # removing the Qt.Tool flag should remove the window
            # from the taskbar.
            self.setWindowFlags(self.windowFlags() & ~QtCore.Qt.Tool)
            self.tray_icon.show()
            return True
        else:
            return super(Example, self).event(event)

    def closeEvent(self, event):
        reply = QtGui.QMessageBox.question(
            self,
            'Message',"Are you sure to quit?",
            QtGui.QMessageBox.Yes | QtGui.QMessageBox.No,
            QtGui.QMessageBox.No)

        if reply == QtGui.QMessageBox.Yes:
            event.accept()
        else:
            self.tray_icon.show()
            self.hide()
            event.ignore()

    def restore_window(self, reason):
        if reason == QtGui.QSystemTrayIcon.DoubleClick:
            self.tray_icon.hide()
            # self.showNormal will restore the window even if it was
            # minimized.
            self.showNormal()

def main():
    app = QtGui.QApplication(sys.argv)
    ex = Example()
    ex.show()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()
Jordan
  • 476
  • 7
  • 16
  • How do I make it show only the tray icon after launching? If I move setWindowFlags line to initUI(), it has no effect, the normal window still shows up – Shuman Feb 29 '16 at 18:56
4

Here's working code..Thanks Matze for Crucial, the SIGNAL took me more hours of curiosity.. but doing other things. so ta for a #! moment :-)

def create_sys_tray(self):
    self.sysTray = QtGui.QSystemTrayIcon(self)
    self.sysTray.setIcon( QtGui.QIcon('../images/corp/blip_32.png') )
    self.sysTray.setVisible(True)
    self.connect(self.sysTray, QtCore.SIGNAL("activated(QSystemTrayIcon::ActivationReason)"), self.on_sys_tray_activated)

    self.sysTrayMenu = QtGui.QMenu(self)
    act = self.sysTrayMenu.addAction("FOO")

def on_sys_tray_activated(self, reason):
    print "reason-=" , reason
PedroMorgan
  • 926
  • 12
  • 15
3

This is the correct way to handle double click on a tray icon for PyQt5.

def _create_tray(self):
    self.tray_icon = QSystemTrayIcon(self)
    self.tray_icon.activated.connect(self.__icon_activated)

def __icon_activated(self, reason):
    if reason in (QSystemTrayIcon.Trigger, QSystemTrayIcon.DoubleClick):
        pass
Jorjon
  • 5,316
  • 1
  • 41
  • 58
  • 1
    I understand this doesn't answer the question directly, but this question has a lot of views and people from Google is looking for a more up-to-date answer. – Jorjon Sep 28 '17 at 11:51
1

This is the code and it does help i believe in show me the code

import sys
from PyQt4 import QtGui, QtCore
from PyQt4.QtGui import QDialog, QApplication, QPushButton, QLineEdit, QFormLayout, QSystemTrayIcon


class Example(QtGui.QWidget):

    def __init__(self):
        super(Example, self).__init__()
        self.initUI()

    def initUI(self):
        self.icon = QSystemTrayIcon()
        r = self.icon.isSystemTrayAvailable()
        print r
        self.icon.setIcon(QtGui.QIcon('/home/vzades/Desktop/web.png'))
        self.icon.show()
        # self.icon.setVisible(True)
        self.setGeometry(300, 300, 250, 150)
        self.setWindowIcon(QtGui.QIcon('/home/vzades/Desktop/web.png'))
        self.setWindowTitle('Message box')
        self.show()
        self.icon.activated.connect(self.activate)
        self.show()

    def closeEvent(self, event):

        reply = QtGui.QMessageBox.question(self, 'Message', "Are you sure to quit?", QtGui.QMessageBox.Yes |
                                           QtGui.QMessageBox.No, QtGui.QMessageBox.No)

        if reply == QtGui.QMessageBox.Yes:
            event.accept()
        else:
            self.icon.show()

            self.hide()

            event.ignore()

    def activate(self, reason):
        print reason
        if reason == 2:
            self.show()

    def __icon_activated(self, reason):
        if reason == QtGui.QSystemTrayIcon.DoubleClick:
            self.show()


def main():

    app = QtGui.QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()
Shuman
  • 3,914
  • 8
  • 42
  • 65
vzades
  • 112
  • 2
  • 4
  • 2
    This code looks horrible. How it managed to get up votes is beyond understanding. – qed Jun 07 '14 at 15:59
  • I don't see why its so bad. Maybe a little more than required for the question and short on explanation but looks like like regular code otherwise. – Paul Rooney Sep 24 '20 at 01:08