10

The way I currently add an executable for my Python-based GUI is this:

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

On Windows, this will create "frontend.exe" and "frontend-script.pyw" in Python's scripts folder (using Python 2.6). When I execute the EXE file, a console window is shown but the PYW file works correctly without showing one.

So my question is: How can I make the EXE file execute the program without the console window? The solution should work on Linux, too (don't suggest py2exe ;).

2 Answers 2

12

Alright, I investigated a bit in the setuptools source code and it all boils down to a bug in setuptools (easy_install.py):

# 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

The last if statement decides whether pythonw.exe's or python.exe's path is written into the shebang of "frontend-script.pyw". As this shebang is evaluated by the created EXE file, it is necessary that the else statement is not executed. The problem is that new_header[2:-1] in my case was "C:\Program Files (x86)\Python26\pythonw.exe" (with the quotes!), so os.path.exists said it does not exist because of the quotes.

I will try to get this corrected by the setuptools developers. The remaining problem will be the absolute pythonw.exe path. If I create a Windows setup/MSI installer, the shebang will contain my pythonw.exe path ("C:\Program Files (x86)\Python26\pythonw.exe") but the user might have installed Python in "C:\Python26". I'll report the final solution after I've reported this problem.


I posted this over two years back, sorry that I didn't yet offer my solution. Not sure if there is any more modern solution (probably distribute offers something), but here's what I used back then (copy-pasted):

File 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"])

File dogsync-frontend.exe

Automatically copied from <python installation>\lib\site-packages\setuptools\gui.exe (see below). This file will automatically execute the script <name of EXE>-script.py[w] if I remember correctly.

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

With this setup, the exe/pyw files are copied to <python installation>\Scripts (on Windows) and starting dogsync-frontend.exe will run the pyw script without a console. Since setuptools did not get any updates for years, this solution is still working.

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks! I've put a fix in SVN for you (that does a .strip('"') on the path used in the exists() call), and it'll be available in tomorrow's snapshot version. Use "easy_install -U setuptools==dev06" to upgrade to the SVN version now and give it a try!
@pjeby: Cool, that was even before I could use my newly created bug tracker account. Thanks, I'll try it as soon as possible.
@pjeby: I filed the bug with the absolute paths at bugs.python.org/setuptools/issue112
I see the bug you raised hasn't been closed. Didn't you have problems with setuptools using absolute path?
@PiotrDobrogost: Please have a look at how I solved it, just added my solution. setuptools was not updated because it was superseded by "distribute". I didn't check if they fixed it somehow.
0

Why don't you use the .pyw file for Linux and py2exe for Windows?

1 Comment

Because on packages.python.org/distribute/setuptools.html the changelog says "0.6a3 - Added gui_scripts entry point group to allow installing GUI scripts on Windows and other platforms. (The special handling is only for Windows; other platforms are treated the same as for console_scripts.)". So I thought they made some changes to adapt it for Windows. And I have version 0.6c11 which is newer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.