5

我正在尝试通过py2exe制作一个exe。该程序正在使用 Tkinter 显示一个类似弹出的窗口。问题是,当我像这样运行设置时一切正常:

setup(windows = [{'script': "msg.py"}], zipfile = None)

但是当我尝试制作一个单一文件的exe时它失败了:

setup(windows = [{'script': "msg.py"}], zipfile = None, options = {'py2exe': {'bundle_files': 1, 'compressed': True}})

实际上最终的 exe 运行没有问题,但它不显示任何窗口。我已经读过在 Windows 7 上 bundle_files=1 可能存在问题,但我也尝试过 bundle_files=2 具有相同的效果。这是我的 msg.py 脚本:

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

有任何想法吗?我读过 Tkinter 存在一些问题,但有一些关于编译的问题。我的脚本已编译,它不会抛出任何异常,但不显示窗口......

4

3 回答 3

9

我最终遇到了同样的问题,我的解决方案涉及执行以下操作:

添加 "dll_excludes": ["tcl85.dll", "tk85.dll"],

在你的options = {...}

然后手动复制这两个 DLL

PYTHON_PATH\DLLs\(在我的情况下C:\Python27\DLLs

到您的exe的位置并尝试运行它。

于 2013-01-28T16:34:26.797 回答
5

dll_excludes 和手动复制的替代方法是修补 py2exe 以知道这些文件必须直接放在 dist 目录中。

在 build_exe.py 中,有一个名为 的类py2exe,其中包含dlls_in_exedir必须去那里的 dll 列表。该列表是在名为 的函数期间设置的plat_prepare,您可以将 tclXX.dll 和 tkXX.dll 文件添加到其中以确保它们被正确复制。

当然,除非你是唯一会构建它的人,否则你不一定知道需要捆绑哪个 Tcl 和 Tk 版本——有人可能自己构建了他们的 Python,或者正在使用带有较旧 DLL 的较旧 Python。因此,您需要检查系统实际​​使用的版本。py2exe 实际上已经在不同的地方做到了这一点:通过导入内部_tkinter模块(实际的 Tk 接口,通常是 DLL)并访问TK_VERSIONand TCL_VERSION,然后您可以使用它来生成和添加正确的文件名。

如果其他人应该构建您的应用程序,您可能不想让他们修改他们的 py2exe 安装,因此您可以从 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

这甚至适用bundle_files=1于 Windows 7。

于 2013-08-01T16:20:33.483 回答
1

如果您只有一个版本,您可以通过 data_file 复制文件。下面是一个完整的例子:

  • 操作系统
  • Python2.7.6
  • TK8.5
  • tcl8.5
  • tix8.4.3
  • py2exe 0.6.9

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

安装程序.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", ))
                       )
                    ] 

    , 
)
于 2014-05-27T20:10:24.733 回答