如何在启动 Tkinter 应用程序时隐藏控制台 window,但在按下 GUI 按钮到 运行 python 脚本时重新打开它?

How to hide the console window while starting Tkinter applications but reopening it when the GUI button is pressed to run the python script?

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

以上是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!')

以上代码命名为"hhh.py",我在第一个代码中将其作为模块导入,需要在CUI 中为运行。我在 windows 平台上。 现在如何隐藏启动 Tkinter 应用程序时弹出的控制台 window,同时可以通过按下按钮重新打开它以查看 "hhh.py" 的输出? 请帮忙...!!!

隐藏现有控制台 window 通常不是一个好主意。它是一个共享资源,如果您的应用程序因 window 隐藏而死掉,它基本上会使附加到控制台的所有其他应用程序变得无用。

您可以通过 pythonw.exe 运行 您的脚本,它不会自动分配或附加到控制台。然后按需分配自己的控制台,切换到full-screen模式(如果支持),设置window标题,重新绑定sys.std*到控制台设备文件"CONIN$"和"CONOUT$"。您拥有此 window 的唯一所有权,因此您有权隐藏它。

例如:

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 通常与 .pyw 文件扩展名相关联。您还可以配置 py2exe 等工具来创建 non-console 可执行文件。


我必须编写一个 input 函数,因为 raw_input 将其提示写入 stderr FILE 流。我宁愿避免从 Python 重新绑定 C 标准 I/O。

通过向控制台 window 发送组合键 Alt+Enter,它为 Windows 10 中分配的控制台切换 full-screen 模式使用 WM_SYSKEYDOW 消息。 Full-screen 模式在 Windows Vista 最高 Windows 8 中不受支持。在这种情况下,您可以最大化 window 并调整屏幕缓冲区的大小。

请注意,我只是隐藏了分配的控制台 window。避免调用 FreeConsole。 C 运行time 的 conio API(例如 kbhitgetch)将句柄缓存到 "CONIN$",但它不提供动态导出和支持的方法来重置此句柄缓存句柄。这些 CRT 功能并非设计用于支持在多个控制台上循环。假设一个进程在其生命周期内最多连接到一个控制台。至少在 Windows 10 中,此缓存句柄还可以防止未使用的控制台主机进程破坏其 window 并退出,直到您的进程退出。

如果用户在附加应用程序时关闭控制台,控制台将终止该应用程序。这是无法避免的。充其量你可以设置一个控制处理程序来通知进程即将被杀死。

另一种检查是否可以隐藏控制台 window 的方法是调用 GetConsoleProcessList 以获取附加进程的列表。如果您的进程是唯一的,您有权隐藏 window。如果附加了两个进程,那么隐藏 window 似乎是合理的,如果另一个是 Python 3 安装的 py[w].exe 启动程序。检查后者需要通过 OpenProcess 打开进程句柄以通过 GetModuleBaseName.

获取图像名称