2

I'm trying to package my project into an executable file using pyinstaller. main.py, my program, should run when I run the exe file. Exe because program has to run somehow without the user, who is not a developer in any way, needing to install modules and python itself. If there's a better/easier way please do tell me.

i installed tkinterdnd2 by using pip install tkinterdnd2.

Not sure if this is necessary, but here is the directory of the program: (the pngs are only used for my program, I don't think that is the culprit):

Folder
- cardiologist.png
- img.png
    .
    . # a couple more pngs
    .
- main.py
- patients' data csv.csv
- patients' data xlsx test.xlsx
- sample.xlsx
- sun-valley.tcl #this file and the 'theme' folder below are for my program's theme, also don't think these are the culprit
- theme
    - dark
        - a lot of pngs...
    - dark.tcl
    - light
        - a lot of pngs...
    - light.tcl

Pyinstaller created 2 files, build and dist as expected.

build
    - main
        - base_library.zip
        - certifi
        - IPython
        - jedi
        - (some other files)
                .
                . a lot of .dylib files
                .
        - main    # exe file to execute to run program
        - matplotlib (this and next 4 are folders)
        - numpy
        - pandas
        - parso
        - PIL
        - Python  # exe file, don't know what this was created for
        - (some more folders)
        - tcl (this and next 2 are folders)
        - tcl8
            - 8.4
                - platform
                    - .tm file
                - .tm file
            - 8.5
                - 2 .tm files
            - 8.6
                - .tm file
                - tdbc
                    - .tm file
                - tkdnd2.8 (tried renaming to just tkdnd but same error)
        - tk
        - ttkwidgets
        - 2 other folders

The command I used: python -m PyInstaller main.py. # without python -m, 'command not found error' would happen

Error when running the produced exe:

Traceback (most recent call last):
  File "tkinterdnd2/TkinterDnD.py", line 53, in _require
_tkinter.TclError: can't find package tkdnd

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "main.py", line 16, in <module>
  File "tkinterdnd2/TkinterDnD.py", line 285, in __init__
  File "tkinterdnd2/TkinterDnD.py", line 55, in _require
RuntimeError: Unable to load tkdnd library.
[7015] Failed to execute script 'main' due to unhandled exception!
logout
Saving session...
...copying shared history...
...saving history...truncating history files...
...completed.

I've went on the internet and tried looking for answers, but they either did not work for me, or wasn't clear enough like this: *.py script with tkinterdnd2 module doesn`t compile to an executable file (this solution still gave me the same error).

I've also tried using other modules to package such as cx_freeze and py2app, but both produced a different error, so I went back to pyinstaller for now. I'm on macOS.

Any way to fix this error?

EDIT

also tried python -m PyInstaller --clean -y -n "output_name" --add-data="tkdnd:tkdnd" main.py but same error produced when running.

Timo
  • 46
  • 7
  • Try to use the library straight outta github https://github.com/petasis/tkdnd/tree/master/library – Petr L. Aug 27 '21 at 15:13
  • Since you installed `tkinterdnd2`, did you try `- tkinterdnd2` in your config file? – Bryan Oakley Aug 27 '21 at 15:19
  • @PetrL. just tried downloading the whole zip from there, putting the whole folder into tcl 8.6 and even renaming it to `tkdnd`, same error still pops up. Putting it in the same directory as main produced the same error too. – Timo Aug 28 '21 at 03:20
  • @BryanOakley may I know what config file you are talking about? – Timo Aug 28 '21 at 03:21

4 Answers4

3

There is a section on PyInstaller in tkinterdnd2 website.

Basically the required steps are:

  • copy hook-tkinterdnd2.py from the website to your project folder or create that file with below content:
from PyInstaller.utils.hooks import collect_data_files

datas = collect_data_files('tkinterdnd2')
  • run PyInstaller as below:
pyinstaller -F main.py --additional-hooks-dir=.

Or based on the command in your question:

python -m PyInstaller -F main.py --additional-hooks-dir=.
acw1668
  • 40,144
  • 5
  • 22
  • 34
1

I finally made it work without manually moving folders.

pip install tkinterdnd2

I used auto py to exe and it didn't work with the "--add-binary" so I used "add-folder" in the gui, which is basically just "--add-data".

pyinstaller --noconfirm --onefile --windowed --add-data "<python_path>/Lib/site-packages/tkinterdnd2;tkinterdnd2/" "<your_script>"
Hamet
  • 11
  • 2
0

The issue here is that Drag n Drop requires two components: the TCL libraries and the Tkinter interface to them. I install both manually to config my environment. (See my answer to How to Install and Use TkDnD with Python Tkinter on OSX?). I know someone packaged something on PyPI for the TkinterDnD2, but I haven't looked into it.

I have a project which uses TkinterDnD2. I build it with PyInstaller and see the same error you see (more or less). Running PyInstaller with the --onedir option (rather than the --onefile option), I saw that tkdnd2.8 was missing from my dist directory.

To rectify this, on Windows, I added

--add-binary "C:/Python/Python38-32/tcl/tkdnd2.8;tkdnd2.8"

to the PyInstaller command line, and that did the trick. I can now build a --onefile executable, and it runs without error.

You tried something similar, but I used the --add-binary rather than --add-data, and I give the full path to the libraries.

GaryMBloom
  • 5,350
  • 1
  • 24
  • 32
0

I'm not sure about the first hand experience of the other answers, but I personally distribute tkdnd in my applications. Granted, I first did this before tkinterdnd2 was available in PyPi. I used both the tkdnd distribution along with the python wrapper. In my build script, I have

copy_tree('build_files/tkdnd2.9.2', os.path.dirname(sys.executable) + '/tcl/tkdnd2.9.2')
copy_tree('build_files/TkinterDnD2', os.path.dirname(sys.executable) + '/Lib/site-packages/TkinterDnD2')

And in my spec file,

tkdnd = [(os.path.abspath(file), 'tkdnd2.9.2') for file in iglob('build_files/tkdnd2.9.2/*.*')]
data_files = [...] + tkdnd
a = Analysis9(..., datas=data_files,...)

You can further take a look at the my project, Music Caster

Elijah
  • 1,814
  • 21
  • 27