1
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... !!!

Taku
  • 31,927
  • 11
  • 74
  • 85
Tsubasa
  • 1,389
  • 11
  • 21
  • Probable solution: save your output to a file to read when needed and hide the console window like described here http://stackoverflow.com/questions/764631/how-to-hide-console-window-in-python/764654 . You will not be able to open the console window, but you'll not need it if you save the output to file. – Andrew Che Apr 09 '17 at 13:07
  • Sorry but I didn't get you. Can you please explain me with the above codes ? – Tsubasa Apr 09 '17 at 13:24
  • Insert `sys.stdout = open('output.txt', 'a')` line in hhh.py before the definition of `hello` function. This will redirect `hello`'s output to the file `output.txt` (it should appear in the same directory as hhh.py). Then you will be able to hide the console window (forever) with the method described by link I pasted above. You will never need the console window to show again because you will be able to check its output in output.txt file – Andrew Che Apr 09 '17 at 13:35
  • Thanks but I want to show the output in the terminal. Nevermind but I'm working on the game (especially terminal based) where the GUI window pops up as soon as the player start it with usual options like 'PLAY', 'LOAD', 'QUIT'....etc. Now when the player clicks on play then the GUI closes and the terminal opens up welcoming the player and the game starts. – Tsubasa Apr 09 '17 at 13:47
  • Possibly you should check Python win api tools. You will be able to start a Python program with console minimized and then show it when you need it (programmatically). See this question http://stackoverflow.com/questions/2319838/open-a-program-with-python-minimized-or-hidden – Andrew Che Apr 09 '17 at 13:53
  • It's such a low level job and honestly I don't know much about win32 api. Isn't there any other way ? – Tsubasa Apr 09 '17 at 15:19
  • Well, you could imitate the terminal in your GUI. It could be a separate window. I think you shouldn't be uncomfortable with learning new things, because there always are and will be. – Andrew Che Apr 09 '17 at 15:21
  • Did someone delete all comments beyond this point? – Andrew Che Apr 09 '17 at 19:58
  • Sorry but stackoverflow was telling to move those comments to chats and my reputation is too low to move it. – Tsubasa Apr 10 '17 at 06:41

1 Answers1

1

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.

Eryk Sun
  • 33,190
  • 5
  • 92
  • 111
  • Thanks! It worked. Can you tell how to make that console window appear in fullscreen while opening (it would be better if no window is present around) ? – Tsubasa Apr 10 '17 at 06:54
  • One thing i couldn't understand, ie, why you gave try statement in line 5. We can just simply import "Tkinter" or "tkinter" (both worked in my case) and what's the reason of setting up raw_input variable in line 9 ? I tested the script without that variable and it worked fine. – Tsubasa Apr 10 '17 at 08:05
  • I wrote it for Python 2 and 3. In Python 3's standard library, Tkinter was renamed tkinter. The `raw_input` was accidentally left behind from when I was initially testing to see how `raw_input` would work in Python 2. It didn't work when I allocated a new console, so I investigated why and determined that fixing it would be more work than it was worth when I could write a simple `input` function instead. – Eryk Sun Apr 10 '17 at 13:44
  • Well thanks a lot. Could you help me in opening the console in full screen mode (without window it would be better but not much important) ? – Tsubasa Apr 10 '17 at 13:55
  • Full-screen mode doesn't exist for the console in Windows Vista to Windows 8. In this case, all you can do is maximize the window, and it won't be useful unless you also resize the screen buffer. IMO, it's more work than it's worth, but I can write code for that if you really want it. In Windows 10, Microsoft restored full-screen mode in the new console implementation, but the old `SetConsoleDisplayMode` function still doesn't work. You can send Alt+Enter to the window to toggle full-screen mode. I'll add code for this case when the example is run in Windows 10.0.14393 and above. – Eryk Sun Apr 10 '17 at 15:46
  • Answers are [licensed](http://stackoverflow.com/help/licensing) under [CC BY-SA 3.0](https://creativecommons.org/licenses/by-sa/3.0). – Eryk Sun Apr 10 '17 at 17:35