从Python setuptools创建可启动的GUI脚本(无控制台窗口!)

10 投票
2 回答
5723 浏览
提问于 2025-04-16 03:08

我现在为我的基于Python的图形界面添加可执行文件的方法是这样的:

setup(
      # ...
      entry_points = {"gui_scripts" : ['frontend = myfrontendmodule.launcher:main']},
      # ...
      )

在Windows上,这样做会在Python的脚本文件夹里创建“frontend.exe”和“frontend-script.pyw”(我使用的是Python 2.6)。当我运行这个EXE文件时,会弹出一个控制台窗口,但PYW文件运行时就不会出现这个窗口。

所以我想问的是:如何让这个EXE文件在运行程序时不显示控制台窗口?这个解决方案也应该适用于Linux(不要建议使用py2exe;)。

相关问题:

2 个回答

0

你为什么不在Linux上使用.pyw文件,在Windows上使用py2exe呢?

12

好的,我稍微研究了一下setuptools的源代码,发现问题其实是出在setuptools(easy_install.py)里的一个bug上:

# On Windows/wininst, add a .py extension and an .exe launcher
if group=='gui_scripts':
    ext, launcher = '-script.pyw', 'gui.exe'
    old = ['.pyw']
    new_header = re.sub('(?i)python.exe','pythonw.exe',header)
else:
    ext, launcher = '-script.py', 'cli.exe'
    old = ['.py','.pyc','.pyo']
    new_header = re.sub('(?i)pythonw.exe','python.exe',header)

if os.path.exists(new_header[2:-1]) or sys.platform!='win32':
    hdr = new_header
else:
    hdr = header

最后的if语句决定了是将pythonw.exe还是python.exe的路径写入到"frontend-script.pyw"的开头部分(shebang)。因为这个开头部分会被生成的EXE文件读取,所以必须确保else语句不会被执行。问题在于,new_header[2:-1]在我的情况下是"C:\Program Files (x86)\Python26\pythonw.exe"(带引号!),所以os.path.exists就说这个路径不存在,因为引号的问题。

我会尝试让setuptools的开发者来修复这个问题。剩下的问题是pythonw.exe的绝对路径。如果我创建一个Windows安装程序/MSI安装包,开头部分会包含我的pythonw.exe路径("C:\Program Files (x86)\Python26\pythonw.exe"),但用户可能把Python安装在"C:\Python26"。我会在报告这个问题后,告诉大家最终的解决方案。


我在两年前就发过这个帖子,抱歉到现在还没提供我的解决方案。不确定现在有没有更现代的解决方案(可能distribute提供了什么),不过这是我当时用的(复制粘贴的):

文件 dogsync-frontend-script.pyw

#!pythonw.exe

# This script will be executed by the primary Python version that is installed, which might as well be Python 3. But
# we want to execute it with the Python version that belongs to this script's path. So let's do a major hack:

import os
import sys
import subprocess

if sys.argv[-1] == "magic":
    from dogsync_frontend.launcher import main
    main()
else:
    # The CPython folder hierarchy is assumed here (<installation>\pythonw.exe, <installation>\Scripts\<thisscript>)
    subprocess.Popen([os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "pythonw.exe")),
                      __file__,
                      "magic"])

文件 dogsync-frontend.exe

这个文件是自动从<python安装路径>\lib\site-packages\setuptools\gui.exe复制过来的(见下文)。如果我没记错的话,这个文件会自动执行脚本<EXE的名称>-script.py[w]

文件 setup.py

from setuptools import __file__ as setupToolsFilename

if os.name == "nt":
    # Use a customized (major hack) start script instead of the one that gets automatically created by setuptools
    # when the "gui_scripts" parameter is used. This way, we don't need setuptools installed in order to run DogSync.
    shutil.copy2(os.path.join(os.path.dirname(setupToolsFilename), "gui.exe"),
                 "build-environment/windows-scripts/dogsync-frontend.exe")
    startScripts = dict(scripts = ["build-environment/windows-scripts/dogsync-frontend-script.pyw",
                                   "build-environment/windows-scripts/dogsync-frontend.exe"])
else:
    # For Linux, I don't have a solution to remove the runtime dependency on setuptools (yet)
    startScripts = dict(entry_points = {"gui_scripts" : ['dogsync-frontend = dogsync_frontend.launcher:main']})

setup(<other options>,
      **startScripts)

通过这个设置,exe/pyw文件会被复制到<python安装路径>\Scripts(在Windows上),启动dogsync-frontend.exe会运行pyw脚本而不会打开控制台。由于setuptools多年没有更新,这个解决方案依然有效。

撰写回答