On 2/7/06, Thomas Heller <theller@python.net> wrote:
Theodore KGB <theodore.kgb@gmail.com> writes:

> Is it possible to create a windows app that will start a GUI when exectued
> without arguments, but will behave like a console app when run from the
> command line with arguments?
>
> In the past I created both a console app and windows app.  They both
> performed the same calculation.  Generating two applications was never an
> issue before because my applications were dependent on many other files.
> Now that I have upgraded to py2exe v0.6.3 I'm using the bundle option.  The
> directory full of dependencies has been reduced to my two executables (and
> MSVCR71.dll if I use Python24).  Since both the GUI and console version
> perform exactly the same calculation, it would be nice if they could be
> combined.  I have added the console features to the GUI script.  However,
> when I package it as a windows app the console features are broken.

I don't think that's possible.  There's a bit in the executable that
marks whether this runs in the Windows console subsystem or in the GUI
subsystem.  The only thing that comes to mind is to call windows
FreeConsole() api to detach the app from the current console.

Can you live with two small exe-files (some 10 kBytes), plus a
library.zip and MSVCR71.dll?

I have succeeded in getting my Windows app to also work from the command line
using the windows console functions.  Originally I used the new --custom-boot-script
feature to reset stdout and stderr.  Since the custom boot script is run after
boot_common.py, I saw no reason why I couldn't just reset stdout and stderr from my
script.  I ended up adding the following to my script:

def main_is_frozen():
    return (hasattr(sys, "frozen"))

class Blackhole(object):
    import win32console
    try:
        win32console.AttachConsole(win32console.ATTACH_PARENT_PROCESS)
        std_hand = win32console.GetStdHandle(win32console.STD_OUTPUT_HANDLE)
        softspace = 0
        def write(self, text):
            self.std_hand.WriteConsole (text)
        def flush(self):
            ## I have no idea if the next line is what should be done here
            self.std_hand.FlushConsoleInputBuffer()
    except:
        softspace = 0
        def write(self, text):
        pass
    def flush(self):
        pass
    del win32console

I then added the following to the __main__ section (after checking for command line
arguments and before printing anything):

    if main_is_frozen():
        if sys.frozen == "windows_exe":
            sys.stdout = Blackhole()
            sys.stderr = Blackhole()
            del Blackhole
    # The following print line is needed because the command line is returned
    # long before the output is displayed.
    print ""

These additions all the app to run in gui form when double-clicked or run from the
command line without arguments.  When run from the command line with arguments
the output is printed to the console window.  The only remaining "problem" is that
the command line is returned before the output is displayed.  This isn't a problem for
me as my computer is reasonably fast, but on a slower computer, the command line
will be returned long before the calculation is complete and the output is displayed.
Eventually it would be nice to have the command line returned after the output is displayed
so that it is clear the output is associated with the program.  For now I'm happy that I can
distribute one file that does the job of two.


> My understanding (from reading the "black hole" posts and boot_common.py) is
> that the breaking of stdout is the intended behavior for a windows app.  Is
> there a way to conditionally undo the "blackholing" of stdout in my script?
> I'd rather not hack boot_common.py because most of the time I will probably
> want the default behavior.

Thomas