How to set application's taskbar icon in Windows 7

QtWindows 7Pyqt

Qt Problem Overview


How do I set an application's taskbar icon in PyQt4?

I have tried setWindowIcon, and it successfully sets the icon in the top-left of the main window, but it does not affect the icon shown in the Windows 7 taskbar -- the taskbar icon remains the default Python pyw icon. Here is my code:

from PyQt4 import QtGui

app = QtGui.QApplication([])
mainwindow = QtGui.QMainWindow()
mainwindow.show()

app.setWindowIcon(QtGui.QIcon('chalk.ico'))
mainwindow.setWindowIcon(QtGui.QIcon('chalk.ico'))
app.exec_()

[update] I've tried placing the setWindowIcon() before the show(). I've tried it with other images, ico and png. Nothing helps.

Qt Solutions


Solution 1 - Qt

I've found the answer, after some digging.

In Windows 7, the taskbar is not for "Application Windows" per se, it's for "Application User Models". For example, if you have several different instances of your application running, and each instance has its own icon, then they will all be grouped under a single taskbar icon. Windows uses various heuristics to decide whether different instances should be grouped or not, and in this case it decided that everything hosted by Pythonw.exe should be grouped under the icon for Pythonw.exe.

The correct solution is for Pythonw.exe to tell Windows that it is merely hosting other applications. Perhaps a future release of Python will do this. Alternatively, you can add a registry key to tell Windows that Pythonw.exe is just a host rather than an application in its own right. See MSDN documentation for AppUserModelIDs.

Alternatively, you can use a Windows call from Python, to explicitly tell Windows what the correct AppUserModelID is for this process:

import ctypes
myappid = 'mycompany.myproduct.subproduct.version' # arbitrary string
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid)

EDIT: Please see Ronan's answer: the myappid string should be unicode.

Solution 2 - Qt

@DamonJW's answer will work, but there is a minor catch: myappid should be unicode (argument type is PCWSTR).

import ctypes
myappid = u'mycompany.myproduct.subproduct.version' # arbitrary string
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid)

Otherwise getting the AppUserModelID will get wrong unicode characters (祭潣灭湡⹹祭牰摯捵⹴畳灢潲畤瑣瘮牥楳湯):

import ctypes
from ctypes import wintypes
lpBuffer = wintypes.LPWSTR()
AppUserModelID = ctypes.windll.shell32.GetCurrentProcessExplicitAppUserModelID
AppUserModelID(ctypes.cast(ctypes.byref(lpBuffer), wintypes.LPWSTR))
appid = lpBuffer.value
ctypes.windll.kernel32.LocalFree(lpBuffer)
if appid is not None:
    print(appid)

That said, it is a minor thing, since Windows will still recognize the unicode string as "another process" and switch the icon accordingly.

Solution 3 - Qt

You must set the AppUserModelID before your app shows any GUI. If you need to access other Windows 7 features you can have a look at Q7Goodies which is a Qt add-on for Windows 7 with a PyQt bindings.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDamonJWView Question on Stackoverflow
Solution 1 - QtDamonJWView Answer on Stackoverflow
Solution 2 - QtRonan PaixãoView Answer on Stackoverflow
Solution 3 - QttornView Answer on Stackoverflow