Reputation: 1429
import Tkinter as tk
import os
from hhh import hello
def runshell():
root.destroy()
hello()
root=tk.Tk()
nvar=tk.StringVar(root)
en=tk.Entry(textvariable=nvar)
en.pack()
btn=tk.Button(text="Shell", command=runshell)
btn.pack()
root.mainloop()
Here is the above code of Tkinter GUI.
import time
import sys
import ctypes
ctypes.windll.kernel32.SetConsoleTitleA("HELLO WORLD")
def hello():
def printf(s):
for c in s:
sys.stdout.write('%s' % c)
sys.stdout.flush()
time.sleep(0.15)
printf('Hello, World!')
The above code is named as "hhh.py" which I've imported as module in the first code and is needed to be run in a CUI. I am on windows platform. Now how can I hide the console window that pops up while starting Tkinter apps and at the same time could reopen it by pressing the button to see the output of the "hhh.py" ? Please help... !!!
Upvotes: 1
Views: 4332
Reputation: 34290
Hiding an existing console window isn't a good idea in general. It's a shared resource, and if your application dies with the window hidden, it's basically rendering useless every other application that's attached to the console.
You can run your script via pythonw.exe, which doesn't automatically allocate or attach to a console. Then allocate your own console on demand, switch to full-screen mode (if supported), set the window title, and rebind sys.std*
to the console device files "CONIN$" and "CONOUT$". You have sole ownership of this window, so you're entitled to hide it.
For example:
import os
import sys
import time
import ctypes
import platform
try:
import Tkinter as tk
except ImportError:
import tkinter as tk
from ctypes import wintypes
user32 = ctypes.WinDLL('user32', use_last_error=True)
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
_windows_version = tuple(map(int, platform.version().split('.')))
kernel32.GetConsoleWindow.restype = wintypes.HWND
user32.SendMessageW.argtypes = (wintypes.HWND, wintypes.UINT,
wintypes.WPARAM, wintypes.LPARAM)
user32.ShowWindow.argtypes = (wintypes.HWND, ctypes.c_int)
SW_HIDE = 0
SW_MAXIMIZE = 3
SW_SHOW = 5
WM_SYSKEYDOWN = 0x0104
VK_RETURN = 0x0D
def toggle_fullscreen(hwnd=None):
if _windows_version < (10, 0, 14393):
return
if hwnd is None:
hwnd = kernel32.GetConsoleWindow()
lparm = (user32.MapVirtualKeyW(VK_RETURN, 0) << 16) | 0x20000001
user32.SendMessageW(hwnd, WM_SYSKEYDOWN, VK_RETURN, lparm)
def printf(s):
for c in s:
sys.stdout.write('%s' % c)
sys.stdout.flush()
time.sleep(0.15)
def input(s):
sys.stdout.write(s)
sys.stdout.flush()
return sys.stdin.readline().rstrip('\n')
def hello():
kernel32.SetConsoleTitleW(u"Hello, World!")
printf('Hello, World!')
input('\nPress enter to continue...')
class App(object):
allocated_console = None
def __init__(self):
if self.allocated_console is None:
# one-time set up for all instances
allocated = bool(kernel32.AllocConsole())
App.allocated_console = allocated
if allocated:
hwnd = kernel32.GetConsoleWindow()
user32.ShowWindow(hwnd, SW_HIDE)
toggle_fullscreen(hwnd)
self.root = root = tk.Tk()
nvar = tk.StringVar(root)
en = tk.Entry(textvariable=nvar)
en.pack()
btn = tk.Button(text="Shell", command=self.runshell)
btn.pack()
def mainloop(self):
self.root.mainloop()
def runshell(self):
hwnd = kernel32.GetConsoleWindow()
user32.ShowWindow(hwnd, SW_SHOW)
try:
old_title = ctypes.create_unicode_buffer(512)
n = kernel32.GetConsoleTitleW(old_title, 512)
if n > 512:
old_title = ctypes.create_unicode_buffer(n)
kernel32.GetConsoleTitleW(old_title, n)
old_stdin = sys.stdin
old_stderr = sys.stderr
old_stdout = sys.stdout
try:
with open('CONIN$', 'r') as sys.stdin,\
open('CONOUT$', 'w') as sys.stdout,\
open('CONOUT$', 'w', buffering=1) as sys.stderr:
self.root.destroy()
hello()
finally:
kernel32.SetConsoleTitleW(old_title)
sys.stderr = old_stderr
sys.stdout = old_stdout
sys.stdin = old_stdin
finally:
if self.allocated_console:
user32.ShowWindow(hwnd, SW_HIDE)
if __name__ == '__main__':
for i in range(3):
app = App()
app.mainloop()
pythonw.exe is typically associated with the .pyw file extension. You can also configure tools such as py2exe to create a non-console executable.
I had to write an input
function since raw_input
writes its prompt to the stderr
FILE
stream. I'd rather avoid rebinding C standard I/O from Python.
It toggles full-screen mode for an allocated console in Windows 10 by sending the key combination Alt+Enter to the console window using a WM_SYSKEYDOW
message. Full-screen mode isn't supported in Windows Vista up to Windows 8. In that case you could maximize the window and resize the screen buffer.
Note that I'm only hiding the allocated console window. Avoid calling FreeConsole
. The C runtime's conio API (e.g. kbhit
, getch
) caches a handle to "CONIN$", but it provides no dynamically exported and supported way to reset this cached handle. These CRT functions weren't designed to support cycling over multiple consoles. The assumption is that a process is attached to at most one console for its lifetime. At least in Windows 10 this cached handle also prevents the unused console host process from destroying its window and exiting until your process exits.
If the user closes the console while the application is attached, the console will kill the application. This cannot be prevented. At best you can set a control handler to be notified that process is about to be killed.
Another approach to check whether you can hide the console window would be to call GetConsoleProcessList
to get the list of attached processes. You're entitled to hide the window if your process is the only one. If there are two processes attached, it seems reasonable to hide the window if the other one is the py[w].exe launcher that Python 3 installs. Checking the latter requires opening a handle to the process via OpenProcess
to get the image name via GetModuleBaseName
.
Upvotes: 1