0

I try to install and run package with gui app in tkinter. I am not able to display app icon after installation. What should I change to make it possible? Here is my package directory tree:

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

setup.py contains:

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'],},
)

To load my icon I have tried different approaches described here: pkgutil, importlib.resources. Unfortunately none of them worked for me. I may do something wrong. Here is code which I ended up with:

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

When I use $ pip install . and then $ gui_app it produces FileNotFoundError. How can I correct it to display icon after installation?

Ethr
  • 431
  • 1
  • 3
  • 17
  • 1
    Instead of `data_files` it should be [`package_data`](https://stackoverflow.com/search?q=%5Bsetuptools%5D+package_data). – phd Mar 22 '20 at 15:24

2 Answers2

0

First: package_data is data located in a Python package. So make sure the directory containing the file is an actual Python package (you might want to add an __init__.py file in there), and make sure this package is correctly added to your distributions (sdist and bdist).

So probably for your case, something like the following recommandations could help:

  • move the image to gui_app/img/icon.gif
  • add gui_app/img/__init__.py (you might see advice saying it's not necessary, if you truly understand that advice and its implications, then feel free to follow it)
  • add gui_app.img to the list used as argument for the packages parameter in the setuptools.setup function call
  • adjust the code accordingly
sinoroc
  • 18,409
  • 2
  • 39
  • 70
  • I have moved icon and added `__init__.py`. Then appended packages list with `gui_app.img`. Modified data to: `package_data={'gui_app.img': ['gui_app/img/*.gif'],},` and used `res.resource_path('gui_app.img', 'icon.gif')`. I still get `FileNotFoundError`. Where am I doing mistake? Updated code on [Github](https://github.com/ethru/gui_app/tree/adjusted_to_answer). – Ethr Mar 23 '20 at 12:35
  • 1
    Check the content of your _sdist_ and _bdist_ (most likely a _wheel_). Make sure to clean the `*.egg-info` directory between attempts. There might be an issue with the argument for the `package_data` parameter. Maybe use the following as a reference to help you debug: https://sinoroc.gitlab.io/kb/python/package_data.html – sinoroc Mar 23 '20 at 12:40
0

Thanks to @sinoroc answer and linked materials I finally found working solution for me. First I made changes in directory structure (moved icon.gif and added MANIFEST.in):

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

Then added one line include gui_app/icon.gif to MANIFEST.in. Next I replaced package_data in setup.py with just include_package_data=True. Here is modified file:

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'],},
)

Finally I got rid off ResourceReader.resource_path('gui_app', 'icon.gif'). In his place I used pkgutil.get_data('gui_app', 'icon.gif'). __main__.py contains now:

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()
Ethr
  • 431
  • 1
  • 3
  • 17