如何在 tkinter 应用程序中打包和使用图标?

How can I package and use an icon within a tkinter app?

我尝试在 tkinter 中使用 gui 应用程序安装和 运行 软件包。安装后我无法显示应用程序图标。我应该改变什么才能让它成为可能?这是我的包目录树:

gui_app/
 | gui_app/
    | __init__.py
    | __main__.py
 | img/
    | icon.gif
 | setup.py

setup.py 包含:

from setuptools import setup
from gui_app import __version__

setup(
    name='gui_app',
    version=__version__,
    packages=['gui_app'],
    data_files=[('', ['img/icon.gif'])],
    entry_points={'gui_scripts': ['gui_app = gui_app.__main__:main'],},
)

为了加载我的图标,我尝试了此处描述的不同方法:pkgutilimportlib.resources。不幸的是 none 他们对我有用。我可能做错了什么。这是我最终得到的代码:

import tkinter as tk
from importlib.abc import ResourceReader as res

def get_path_before_install():
    return 'img/icon.gif'

def get_path_after_install():
    return res.resource_path('.', 'icon.gif')

def display_gui():
    root = tk.Tk()
    icon_path = get_path_after_install()
    icon = tk.PhotoImage(file=str(icon_path))
    root.iconphoto(False, icon)
    root.mainloop()

def main():
    display_gui()

if __name__ == '__main__': main()

当我使用 $ pip install . 然后 $ gui_app 时,它会生成 FileNotFoundError。安装后如何更正以显示图标?

首先:package_data 是位于 Python 包中的数据。因此,请确保包含该文件的目录是一个实际的 Python 包(您可能想在其中添加一个 __init__.py 文件),并确保该包已正确添加到您的发行版中(sdistbdist).

所以对于您的情况,类似以下的建议可能会有所帮助:

  • 将图片移动到gui_app/img/icon.gif
  • 添加gui_app/img/__init__.py(您可能会看到建议说没有必要,如果您真正理解该建议及其含义,请随时遵循)
  • gui_app.img 添加到用作 setuptools.setup 函数调用中 packages 参数的参数的列表中
  • 相应地调整代码

感谢@sinoroc 的回答和链接的材料,我终于找到了适合我的解决方案。首先,我更改了目录结构(移动 icon.gif 并添加 MANIFEST.in):

gui_app/
 | gui_app/
    | __init__.py
    | __main__.py
    | icon.gif
 | MANIFEST.in
 | setup.py

然后在 MANIFEST.in 中添加了一行 include gui_app/icon.gif。接下来,我将 setup.py 中的 package_data 替换为 include_package_data=True。这是修改后的文件:

from setuptools import setup
from gui_app import __version__

setup(
    name='gui_app',
    version=__version__,
    packages=['gui_app'],
    include_package_data=True,
    entry_points={'gui_scripts': ['gui_app = gui_app.__main__:main'],},
)

终于摆脱了ResourceReader.resource_path('gui_app', 'icon.gif')。我用 pkgutil.get_data('gui_app', 'icon.gif') 代替他。 __main__.py 现在包含:

import tkinter as tk
import pkgutil

def get_icon():
    return pkgutil.get_data('gui_app', 'icon.gif')

def display_gui():
    root = tk.Tk()
    icon = tk.PhotoImage(data=get_icon())
    root.iconphoto(False, icon)
    root.mainloop()

def main():
    display_gui()

if __name__ == '__main__': main()