I have a python script that I launch from a Windows 7 shortcut. The command works but while it is running the ‘cmd’ window is displayed. Since I use tkinter for all the user input/output, I don’t need nor want that window. How to I hide it? Do I need to do it in the python script or something in the shortcut command? TIA.
I’m on linux but if I’m remember well there is a pythonw alongside python that is supposed to not open a command window.
If you’re relying on the file association, try changing it to “.pyw” instead of “.py”. The “.pyw” file extension should be associated with “pyw.exe” or “pythonw.exe”. These are non-console executables, which means the API won’t automatically allocate a console. (This has nothing to do with “cmd.exe”, by the way, except that “cmd.exe” is a console app, just like “python.exe”.) The executables with a “w” suffix are usually called the GUI or windowed executables, but they’re also the ones to use for a background process that has no user interface.
As mentioned by Nacho Xxl, it’s even better (at least more dependable) to explicitly run the interpreter instead of relying on the file association, e.g. "path\to\py[thon]w.exe" "path\to\script.py"
.
Close but not quite there yet. The initial launch does not bring up the window (using .pyw). However, apparently because of selenium, this does:
options=Options()
options.set_headless()
options.binary=FirefoxBinary(r'C:\Program Files (x86)\Mozilla Firefox\firefox.exe')
browser=webdriver.Firefox(executable_path=r'C:\cygwin64\usr\local\bin\geckodriver.exe',options=options)
The window comes up for geckodriver.exe.
Is there a reason it’s built as a console application? If it never uses the console as an interactive command-line or text UI, then your script could allocate a console that either has no window or a hidden window, which should be inherited if webdriver.Firefox
spawns the process normally.
It’s simple to allocate a console (or a tab in Windows Terminal, if it’s set as the default terminal) via AllocConsole()
, or attach to an existing console via AttachConsole()
. One also has to set a console control handler via SetConsoleCtrlHandler()
, because a new console connection resets the registered handlers to the default handler that calls ExitProcess()
.
Allocating a console without a window, or without briefly flashing a window, is a bit more complicated. The API doesn’t provide a way to control AllocConsole()
. In the future, Python may provide convenient support for pseudoconsoles (i.e. headless console sessions, introduced in Windows 10). For now this requires spawning a process that allocates the console as desired, with no window or an invisible window, and then attaches to the console of the child.
Example:
import ctypes
import subprocess
import threading
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
ctrl_cancel_event = threading.Event()
ctrl_break_event = threading.Event()
ctrl_close_event = threading.Event()
exit_event = threading.Event()
@ctypes.WINFUNCTYPE(ctypes.c_int, ctypes.c_ulong)
def ctrl_handler(event_type):
if event_type == 0:
# CTRL_C_EVENT
ctrl_cancel_event.set()
elif event_type == 1:
# CTRL_BREAK_EVENT
ctrl_break_event.set()
else:
# CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT, CTRL_SHUTDOWN_EVENT
ctrl_close_event.set()
# Give cleanup handlers time to exit.
exit_event.wait()
return True
def allocate_console(create_window=True):
if create_window:
if not kernel32.AllocConsole():
raise ctypes.WinError(ctypes.get_last_error())
if not kernel32.SetConsoleCtrlHandler(ctrl_handler, True):
raise ctypes.WinError(ctypes.get_last_error())
return
with subprocess.Popen(
'echo ready & set /p=',
shell=True, creationflags=subprocess.CREATE_NO_WINDOW,
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
bufsize=0) as p_shell:
try:
p_shell.stdout.read(1)
if not kernel32.AttachConsole(p_shell.pid):
raise ctypes.WinError(ctypes.get_last_error())
if not kernel32.SetConsoleCtrlHandler(ctrl_handler, True):
raise ctypes.WinError(ctypes.get_last_error())
finally:
p_shell.terminate()
That is a bit complicated. Is there an easier way to just put that window at the bottom (as opposed to top) or minimize it so it doesn’t cover up my message windows?
Automatically looking for the console window to hide or minimize it is unreliable and not realistically possible if conhost.exe isn’t the default terminal. You could try raising your window to the top via SetForegroundWindow()
.
You can reduce the allocate_console()
example to simplify it. For example, if you only want a console that has no window, remove the create_window
parameter and the code that calls AllocConsole()
. Also, I used threading.Event()
as a general example for handling control events, but ctrl_handler()
at a minimum could just return True
. This prevents your process from getting killed if another process that’s attached to the console calls GenerateConsoleCtrlEvent()
to send a Ctrl+C or Ctrl+Break event to all processes in the console session.
Example:
import ctypes
import subprocess
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
@ctypes.WINFUNCTYPE(ctypes.c_int, ctypes.c_ulong)
def ctrl_handler(event_type):
return True
def allocate_console():
with subprocess.Popen(
'echo ready & set /p=',
shell=True, creationflags=subprocess.CREATE_NO_WINDOW,
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
bufsize=0) as p_shell:
try:
p_shell.stdout.read(1)
if not kernel32.AttachConsole(p_shell.pid):
raise ctypes.WinError(ctypes.get_last_error())
if not kernel32.SetConsoleCtrlHandler(ctrl_handler, True):
raise ctypes.WinError(ctypes.get_last_error())
finally:
p_shell.terminate()