如何在Windows 7中设置应用程序的任务栏图标

65

我该如何在PyQt4中设置应用程序的任务栏图标?

我尝试使用setWindowIcon,它成功地在主窗口左上角设置了图标,但它并没有影响显示在Windows 7任务栏中的图标 - 任务栏图标仍然是默认的Python pyw图标。这是我的代码:

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

[更新] 我已经尝试在 show() 之前使用 setWindowIcon()。 我已经尝试使用其他图像,包括ico和png格式,但都没有帮助。


在你调用.show()方法之前设置窗口图标有帮助吗? - Lukáš Lalinský
不,如果我在.show()之前设置图标也没有任何影响。 - DamonJW
你试过另一个图片文件吗?虽然我使用的是KDE4,但对于我来说,你的代码在随机png文件上运行良好。 - Gerald Senarclens de Grancy
顺便问一下,这个问题在Win XP或Vista上是否重现?如果没有,请咨询http://www.riverbankcomputing.co.uk/support/help-但请在此分享您的发现 :) - Gerald Senarclens de Grancy
我尝试了其他图像文件,但仍然不起作用。我没有WinXP或Vista可用来测试它。如果有人在这些系统上测试代码,请告诉我们答案。我已经在PyQt邮件列表中发布了一个查询。 - DamonJW
5个回答

164

经过一些深入挖掘,我找到了答案。

在Windows 7中,任务栏不是专门用于"应用程序窗口",而是用于"应用程序用户模型"。例如,如果您有多个不同的应用程序实例正在运行,并且每个实例都有自己的图标,则它们将全部分组在单个任务栏图标下。Windows使用各种启发式算法来决定是否应将不同的实例分组,而在这种情况下,它决定将由Pythonw.exe托管的所有内容都分组在Pythonw.exe图标下。

正确的解决方案是告诉Windows Pythonw.exe仅仅是托管其他应用程序而已。也许未来版本的Python会这样做。或者,您可以添加一个注册表键来告诉Windows Pythonw.exe只是一个主机,而不是一个独立的应用程序。请参阅MSDN文档AppUserModelIDs

另外,您还可以从Python中使用Windows调用,明确地告诉Windows此进程的正确AppUserModelID是什么:

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

编辑:请查看Ronan的答案:myappid字符串应该是Unicode。


如果您的问题得到解决,应该接受自己的答案。 :) - Macke
谢谢!我需要使用PySide做同样的事情。 - BaldDude
这也适用于Windows 10,对于任何想知道的人来说。但我不确定它是否适用于Windows 8或8.1。 - nonimportant
在Windows 10上运行得非常好。加上很棒的解释。可惜我只能点赞一次。 - Elad Weiss
听起来它对其他人都有用,但我在 Windows 10 上使用 wxPython 时却不行。你们把它放在代码的哪个位置?你们如何启动 Python 脚本? - Nicryc
显示剩余4条评论

19

@DamonJW的回答是可行的,但有一个小问题:myappid应该是unicode(参数类型为PCWSTR)。

import ctypes
myappid = u'mycompany.myproduct.subproduct.version' # arbitrary string
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid)
否则获取AppUserModelID将得到错误的Unicode字符(祭潣灭湡⹹祭牰摯捵⹴畳灢潲畤瑣瘮牥楳湯):
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)

话虽如此,这只是小事情,因为Windows仍然会将Unicode字符串识别为“另一个进程”,并相应地切换图标。


我无法让你的代码运行,但是@DamonJW的答案对我有用。 - Oliver Nybroe
我的代码的第一部分与@DamonJW的相同,只是在myappid字符串前添加了u。第二部分只是检查Windows实际看到的字符串。 - Ronan Paixão
不错的补充。可能在Python 3上这不是问题,因为所有字符串都是Unicode。 - Snorfalorpagus
1
我不确定这个程序在非Windows系统(如Linux)上是否会出现问题,如果有问题,你可以使用os.name进行检查。https://docs.python.org/2/library/os.html?highlight=os.name#os.name - Tom Myddeltyn

10

在应用程序显示任何GUI之前,您必须设置AppUserModelID。如果您需要访问其他Windows 7功能,您可以查看Q7Goodies,这是一个带有PyQt绑定的Windows 7 Qt附加组件。


1
这是一个重要的细节,应该在被接受的答案中。 - olenscki

1
使用PyQt6和Windows 11的工作示例。这段代码将把任务栏图标从Python解释器图标更改为您使用self.setWindowIcon(QtGui.QIcon('gui/icons/Logo.png'))设置的图标。
import ctypes
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID('company.app.1')

0
在我的Windows 10上,解决方案看起来稍有不同:
import ctypes
myappid = 'mycompany.myproduct.subproduct.version' 
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid)
time.sleep(1)

没有延迟的时候,一个图标只是闪烁了一下就消失了。 有延迟时,它正常工作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接