I'm trying to make an exe by py2exe. The program is showing a popup-like window using Tkinter. The problem is, everything works fine when I run the setup like this:
setup(windows = [{'script': "msg.py"}], zipfile = None)
but it fails when I try to make an one-file exe:
setup(windows = [{'script': "msg.py"}], zipfile = None, options = {'py2exe': {'bundle_files': 1, 'compressed': True}})
Actually the final exe runs without problems, but it doesn't display any window. I've read there may be problems with bundle_files=1 at Windows 7, but I also tried bundle_files=2 with the same effect. Here is my msg.py script:
from win32gui import FindWindow, SetForegroundWindow
from Image import open as iopen
from ImageTk import PhotoImage
from Tkinter import Tk, Label
from threading import Timer
from subprocess import Popen
import os
def Thread(t, fun, arg=None):
if arg<>None: x = Timer(t, fun, arg)
else: x = Timer(t, fun)
x.daemon = True
x.start()
def NewMessage():
global root
if not os.path.exists('dane/MSG'):
open('dane/MSG', 'w').write('')
root = Tk()
img = PhotoImage(iopen("incl/nowa.png"))
label = Label(root, image=img)
label.image = img
label.bind("<Button-1>", Click)
label.pack()
root.geometry('-0-40')
root.wm_attributes("-topmost", 1)
root.overrideredirect(1)
root.mainloop()
def Click(event):
global root, exit
root.destroy()
os.remove('dane/MSG')
OpenApp()
exit = True
def OpenApp():
hwnd = FindWindow(None, 'My program name')
if hwnd: SetForegroundWindow(hwnd)
else: Popen('app.exe')
root, exit = None, False
NewMessage()
Any ideas? I've read there are some problems with Tkinter, but there were about compilation. My script is compiled and it doesn't throw any exceptions, but doesn't show the window...
I ended up encountering this same issue, my solution involved doing the following:
Add
"dll_excludes": ["tcl85.dll", "tk85.dll"],
in your options = {...}
and then manually copy those two DLLs from
PYTHON_PATH\DLLs\
(in my case C:\Python27\DLLs
)
to the location of your exe and try running it.
An alternative to dll_excludes and manual copying is to patch py2exe to know these files have to be placed directly in the dist directory.
Inside build_exe.py, there's a class called py2exe
, which contains a list dlls_in_exedir
for dll that have to go there. This list is set during a function named plat_prepare
, and you can add the tclXX.dll and tkXX.dll files to it to make sure they are copied correctly.
Of course, unless you're the only one who will ever build this, you don't necessarily know which Tcl and Tk version you need to bundle - someone might have built their Python themselves, or are using an older Python with older DLLs. Therefore, you'll need to check which versions the system is actually using. py2exe actually already does this in a different place: by importing the internal _tkinter
module (the actual Tk interface, usually a DLL) and accessing TK_VERSION
and TCL_VERSION
, which you can then use to generate and add the correct filenames.
If others are supposed to build your application, you probably don't want to make them modify their py2exe install, so here's how you can monkeypatch it from your setup.py:
import py2exe
py2exe.build_exe.py2exe.old_prepare = py2exe.build_exe.py2exe.plat_prepare
def new_prep(self):
self.old_prepare()
from _tkinter import TK_VERSION, TCL_VERSION
self.dlls_in_exedir.append('tcl{0}.dll'.format(TCL_VERSION.replace('.','')))
self.dlls_in_exedir.append('tk{0}.dll'.format(TK_VERSION.replace('.','')))
py2exe.build_exe.py2exe.plat_prepare = new_prep
This even works with bundle_files=1
on Windows 7.
If you have only one version the you can copy files with via data_file. Below a full example:
foo.py:
# -*- coding: iso-8859-1 -*-
import Tkinter
"""
sets TCL_LIBRARY, TIX_LIBRARY and TK_LIBRARY - see installation Lib\lib-tk\FixTk.py
"""
Tkinter._test()
Setup.py :
# -*- coding: iso-8859-1 -*-
from distutils.core import setup
import py2exe
import sys
import os
import os.path
sys.argv.append ('py2exe')
setup (
options =
{'py2exe':
{ "bundle_files" : 1 # 3 = don't bundle (default)
# 2 = bundle everything but the Python interpreter
# 1 = bundle everything, including the Python interpreter
, "compressed" : False # (boolean) create a compressed zipfile
, "unbuffered" : False # if true, use unbuffered binary stdout and stderr
, "includes" :
[ "Tkinter", "Tkconstants"
]
, "excludes" : ["tcl", ]
, "optimize" : 0 #-O
, "packages" :
[
]
, "dist_dir" : "foo"
, "dll_excludes": ["tcl85.dll", "tk85.dll"]
,
}
}
, windows =
["foo.py"
]
, zipfile = None
# the syntax for data files is a list of tuples with (dest_dir, [sourcefiles])
# if only [sourcefiles] then they are copied to dist_dir
, data_files = [ os.path.join (sys.prefix, "DLLs", f)
for f in os.listdir (os.path.join (sys.prefix, "DLLs"))
if ( f.lower ().startswith (("tcl", "tk"))
and f.lower ().endswith ((".dll", ))
)
]
,
)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With