En bref: je veux deux consoles pour mon programme. Un pour l'entrée utilisateur active. Et l'autre pour la sortie du journal pur. (Le code de travail incluant la réponse acceptée est dans le texte de la question ci-dessous, dans la section "Edit-3". Et dans la section "Edit- 1 "et la section" Edit-2 "sont des solutions de contournement qui fonctionnent.)
Pour cela, j'ai une ligne de commande principale Python, qui est censé ouvrir une console supplémentaire pour la sortie du journal uniquement. Pour cela, j'ai l'intention de rediriger la sortie du journal, qui serait imprimée sur le principal la console du script, vers le stdin de la deuxième console, que je démarre en tant que sous-processus (j'utilise un sous-processus, car je n'ai trouvé aucun autre moyen d'ouvrir une deuxième console).
Le problème est qu'il semble que je peux envoyer au stdin de cette deuxième console - cependant, rien n'est imprimé sur cette deuxième console.
Voici le code que j'ai utilisé pour expérimenter (avec Python 3.4 sur PyDev sous Windows 10). La fonction writing(input, pipe, process)
contient la partie, où la chaîne générée est copiée dans le as pipe
passé stdin, de la console ouverte via le sous-processus. La fonction d'écriture (...) est exécutée via la classe writetest(Thread)
. (J'ai laissé du code, que j'ai commenté.)
import os
import sys
import io
import time
import threading
from cmd import Cmd
from queue import Queue
from subprocess import Popen, PIPE, CREATE_NEW_CONSOLE
REPETITIONS = 3
# Position of "The class" (Edit-2)
# Position of "The class" (Edit-1)
class generatetest(threading.Thread):
def __init__(self, queue):
self.output = queue
threading.Thread.__init__(self)
def run(self):
print('run generatetest')
generating(REPETITIONS, self.output)
print('generatetest done')
def getout(self):
return self.output
class writetest(threading.Thread):
def __init__(self, input=None, pipe=None, process=None):
if (input == None): # just in case
self.input = Queue()
else:
self.input = input
if (pipe == None): # just in case
self.pipe = PIPE
else:
self.pipe = pipe
if (process == None): # just in case
self.process = subprocess.Popen('C:\Windows\System32\cmd.exe', universal_newlines=True, creationflags=CREATE_NEW_CONSOLE)
else:
self.process = proc
threading.Thread.__init__(self)
def run(self):
print('run writetest')
writing(self.input, self.pipe, self.process)
print('writetest done')
# Position of "The function" (Edit-2)
# Position of "The function" (Edit-1)
def generating(maxint, outline):
print('def generating')
for i in range(maxint):
time.sleep(1)
outline.put_nowait(i)
def writing(input, pipe, process):
print('def writing')
while(True):
try:
print('try')
string = str(input.get(True, REPETITIONS)) + "\n"
pipe = io.StringIO(string)
pipe.flush()
time.sleep(1)
# print(pipe.readline())
except:
print('except')
break
finally:
print('finally')
pass
data_queue = Queue()
data_pipe = sys.stdin
# printer = sys.stdout
# data_pipe = os.pipe()[1]
# The code of 'C:\\Users\\Public\\Documents\\test\\test-cmd.py'
# can be found in the question's text further below under "More code"
exe = 'C:\Python34\python.exe'
# exe = 'C:\Windows\System32\cmd.exe'
arg = 'C:\\Users\\Public\\Documents\\test\\test-cmd.py'
arguments = [exe, arg]
# proc = Popen(arguments, universal_newlines=True, creationflags=CREATE_NEW_CONSOLE)
proc = Popen(arguments, stdin=data_pipe, stdout=PIPE, stderr=PIPE,
universal_newlines=True, creationflags=CREATE_NEW_CONSOLE)
# Position of "The call" (Edit-2 & Edit-1) - file init (proxyfile)
# Position of "The call" (Edit-2) - thread = sockettest()
# Position of "The call" (Edit-1) - thread0 = logtest()
thread1 = generatetest(data_queue)
thread2 = writetest(data_queue, data_pipe, proc)
# time.sleep(5)
# Position of "The call" (Edit-2) - thread.start()
# Position of "The call" (Edit-1) - thread0.start()
thread1.start()
thread2.start()
# Position of "The call" (Edit-2) - thread.join()
# Position of "The call" (Edit-1) - thread.join()
thread1.join(REPETITIONS * REPETITIONS)
thread2.join(REPETITIONS * REPETITIONS)
# data_queue.join()
# receiver = proc.communicate(stdin, 5)
# print('OUT:' + receiver[0])
# print('ERR:' + receiver[1])
print("1st part finished")
L'extrait de code supplémentaire suivant fonctionne en ce qui concerne l'extraction de la sortie standard du sous-processus. Cependant, le stdin précédemment envoyé n'est toujours pas imprimé sur la deuxième console. De plus, la deuxième console est fermée immédiatement.
proc2 = Popen(['C:\Python34\python.exe', '-i'],
stdin=PIPE,
stdout=PIPE,
stderr=PIPE,
creationflags=CREATE_NEW_CONSOLE)
proc2.stdin.write(b'2+2\n')
proc2.stdin.flush()
print(proc2.stdout.readline())
proc2.stdin.write(b'len("foobar")\n')
proc2.stdin.flush()
print(proc2.stdout.readline())
time.sleep(1)
proc2.stdin.close()
proc2.terminate()
proc2.wait(timeout=0.2)
print("Exiting Main Thread")
Dès que j'utilise l'un des paramètres stdin=data_pipe, stdout=PIPE, stderr=PIPE
Pour démarrer le sous-processus, la deuxième console résultante n'est pas active et n'accepte pas la saisie au clavier (ce qui n'est pas souhaité, mais peut-être des informations utiles ici).
La méthode de sous-processus communicate()
ne peut pas être utilisée pour cela car elle attend la fin du processus.
Enfin le code du fichier, qui correspond à la deuxième console.
C:\Users\Public\Documents\test\test-cmd.py
from cmd import Cmd
from time import sleep
from datetime import datetime
INTRO = 'command line'
Prompt = '> '
class CommandLine(Cmd):
"""Custom console"""
def __init__(self, intro=INTRO, Prompt=PROMPT):
Cmd.__init__(self)
self.intro = intro
self.Prompt = Prompt
self.doc_header = intro
self.running = False
def do_dummy(self, args):
"""Runs a dummy method."""
print("Do the dummy.")
self.running = True
while(self.running == True):
print(datetime.now())
sleep(5)
def do_stop(self, args):
"""Stops the dummy method."""
print("Stop the dummy, if you can.")
self.running = False
def do_exit(self, args):
"""Exits this console."""
print("Do console exit.")
exit()
if __name__ == '__main__':
cl = CommandLine()
cl.Prompt = Prompt
cl.cmdloop(INTRO)
Jusqu'à présent, je ne suis même pas certain que l'interface de ligne de commande Windows offre la possibilité d'accepter une autre entrée que celle du clavier (au lieu du canal stdin souhaité ou similaire). Cependant, avec un mode passif, je m'y attendais.
Pourquoi ça ne marche pas?
L'utilisation d'un fichier comme solution de contournement pour afficher son nouveau contenu, comme suggéré dans la réponse de Travailler sur plusieurs consoles en python , fonctionne en général. Cependant, comme le fichier journal augmentera jusqu'à plusieurs Go, ce n'est pas une solution pratique dans ce cas. Cela nécessiterait au moins le fractionnement de fichiers et sa gestion appropriée.
La classe:
class logtest(threading.Thread):
def __init__(self, file):
self.file = file
threading.Thread.__init__(self)
def run(self):
print('run logtest')
logging(self.file)
print('logtest done')
La fonction:
def logging(file):
pexe = 'C:\Python34\python.exe '
script = 'C:\\Users\\Public\\Documents\\test\\test-004.py'
filek = '--file'
filev = file
file = open(file, 'a')
file.close()
time.sleep(1)
print('LOG START (outer): ' + script + ' ' + filek + ' ' + filev)
proc = Popen([pexe, script, filek, filev], universal_newlines=True, creationflags=CREATE_NEW_CONSOLE)
print('LOG FINISH (outer): ' + script + ' ' + filek + ' ' + filev)
time.sleep(2)
L'appel:
# The file tempdata is filled with several strings of "0\n1\n2\n"
# Looking like this:
# 0
# 1
# 2
# 0
# 1
# 2
proxyfile = 'C:\\Users\\Public\\Documents\\test\\tempdata'
f = open(proxyfile, 'a')
f.close()
time.sleep(1)
thread0 = logtest(proxyfile)
thread0.start()
thread0.join(REPETITIONS * REPETITIONS)
Le script de queue ("test-004.py"):
Comme Windows n'offre pas la commande tail, j'ai plutôt utilisé le script suivant (basé sur la réponse pour Comment implémenter un équivalent Pythonic de tail -F? ), qui a fonctionné pour cela. La fonction supplémentaire [ mais non nécessaire class CommandLine(Cmd)
était initialement une tentative de garder la deuxième console ouverte (car l'argument du fichier de script était manquant). Cependant, il s'est également révélé utile pour garder la console imprimant couramment le nouveau contenu du fichier journal. Sinon, la sortie n'était pas déterministe/prévisible.
import time
import sys
import os
import threading
from cmd import Cmd
from argparse import ArgumentParser
def main(args):
parser = ArgumentParser(description="Parse arguments.")
parser.add_argument("-f", "--file", type=str, default='', required=False)
arguments = parser.parse_args(args)
if not arguments.file:
print('LOG PRE-START (inner): file argument not found. Creating new default entry.')
arguments.file = 'C:\\Users\\Public\\Documents\\test\\tempdata'
print('LOG START (inner): ' + os.path.abspath(os.path.dirname(__file__)) + ' ' + arguments.file)
f = open(arguments.file, 'a')
f.close()
time.sleep(1)
words = ['Word']
console = CommandLine(arguments.file, words)
console.Prompt = ''
thread = threading.Thread(target=console.cmdloop, args=('', ))
thread.start()
print("\n")
for hit_Word, hit_sentence in console.watch():
print("Found %r in line: %r" % (hit_Word, hit_sentence))
print('LOG FINISH (inner): ' + os.path.abspath(os.path.dirname(__file__)) + ' ' + arguments.file)
class CommandLine(Cmd):
"""Custom console"""
def __init__(self, fn, words):
Cmd.__init__(self)
self.fn = fn
self.words = words
def watch(self):
fp = open(self.fn, 'r')
while True:
time.sleep(0.05)
new = fp.readline()
print(new)
# Once all lines are read this just returns ''
# until the file changes and a new line appears
if new:
for Word in self.words:
if Word in new:
yield (Word, new)
else:
time.sleep(0.5)
if __name__ == '__main__':
print('LOG START (inner - as main).')
main(sys.argv[1:])
Trois solutions de contournement, que je n'ai pas encore essayées et qui pourraient fonctionner sont des sockets (également suggérées dans cette réponse Travailler plusieurs consoles en python ), obtenir un objet de processus via l'ID de processus pour plus de contrôle et utiliser le Bibliothèque ctypes pour accéder directement à l'API de la console Windows, permettant de définir le tampon d'écran, car la console peut avoir plusieurs tampons, mais un seul tampon actif (indiqué dans les remarques de la documentation pour le fonction CreateConsoleScreenBuffer ) .
Cependant, l'utilisation de sockets peut être la plus simple. Et au moins, la taille du journal n'a pas d'importance de cette façon. Cependant, les problèmes de connexion peuvent être un problème ici.
L'utilisation de sockets comme solution de contournement pour afficher de nouvelles entrées de journal, comme cela a également été suggéré dans la réponse de Travailler sur plusieurs consoles en python , fonctionne également en général. Cependant, cela semble être trop d'effort pour quelque chose, qui devrait simplement être envoyé au processus de la console de réception.
La classe:
class sockettest(threading.Thread):
def __init__(self, Host, port, file):
self.Host = Host
self.port = port
self.file = file
threading.Thread.__init__(self)
def run(self):
print('run sockettest')
socketing(self.Host, self.port, self.file)
print('sockettest done')
La fonction:
def socketing(Host, port, file):
pexe = 'C:\Python34\python.exe '
script = 'C:\\Users\\Public\\Documents\\test\test-005.py'
hostk = '--address'
hostv = str(Host)
portk = '--port'
portv = str(port)
filek = '--file'
filev = file
file = open(file, 'a')
file.close()
time.sleep(1)
print('Host START (outer): ' + pexe + script + ' ' + hostk + ' ' + hostv + ' ' + portk + ' ' + portv + ' ' + filek + ' ' + filev)
proc = Popen([pexe, script, hostk, hostv, portk, portv, filek, filev], universal_newlines=True, creationflags=CREATE_NEW_CONSOLE)
print('Host FINISH (outer): ' + pexe + script + ' ' + hostk + ' ' + hostv + ' ' + portk + ' ' + portv + ' ' + filek + ' ' + filev)
time.sleep(2)
L'appel:
# The file tempdata is filled with several strings of "0\n1\n2\n"
# Looking like this:
# 0
# 1
# 2
# 0
# 1
# 2
proxyfile = 'C:\\Users\\Public\\Documents\\test\\tempdata'
f = open(proxyfile, 'a')
f.close()
time.sleep(1)
thread = sockettest('127.0.0.1', 8888, proxyfile)
thread.start()
thread.join(REPETITIONS * REPETITIONS)
Le script de socket ("test-005.py"):
Le script suivant est basé sur Python: application serveur-client de programmation de socket utilisant des threads . Ici, je viens de conserver la class CommandLine(Cmd)
comme générateur d'entrée de journal. À ce stade, cela ne devrait pas être un problème, de mettre le client dans le script principal, qui appelle la deuxième console, puis d'alimenter la file d'attente avec de véritables entrées de journal au lieu de (nouvelles) lignes de fichier. (Le serveur est l'imprimante.)
import socket
import sys
import threading
import time
from cmd import Cmd
from argparse import ArgumentParser
from queue import Queue
BUFFER_SIZE = 5120
class CommandLine(Cmd):
"""Custom console"""
def __init__(self, fn, words, queue):
Cmd.__init__(self)
self.fn = fn
self.words = words
self.queue = queue
def watch(self):
fp = open(self.fn, 'r')
while True:
time.sleep(0.05)
new = fp.readline()
# Once all lines are read this just returns ''
# until the file changes and a new line appears
self.queue.put_nowait(new)
def main(args):
parser = ArgumentParser(description="Parse arguments.")
parser.add_argument("-a", "--address", type=str, default='127.0.0.1', required=False)
parser.add_argument("-p", "--port", type=str, default='8888', required=False)
parser.add_argument("-f", "--file", type=str, default='', required=False)
arguments = parser.parse_args(args)
if not arguments.address:
print('Host PRE-START (inner): Host argument not found. Creating new default entry.')
arguments.Host = '127.0.0.1'
if not arguments.port:
print('Host PRE-START (inner): port argument not found. Creating new default entry.')
arguments.port = '8888'
if not arguments.file:
print('Host PRE-START (inner): file argument not found. Creating new default entry.')
arguments.file = 'C:\\Users\\Public\\Documents\\test\\tempdata'
file_queue = Queue()
print('Host START (inner): ' + ' ' + arguments.address + ':' + arguments.port + ' --file ' + arguments.file)
# Start server
thread = threading.Thread(target=start_server, args=(arguments.address, arguments.port, ))
thread.start()
time.sleep(1)
# Start client
thread = threading.Thread(target=start_client, args=(arguments.address, arguments.port, file_queue, ))
thread.start()
# Start file reader
f = open(arguments.file, 'a')
f.close()
time.sleep(1)
words = ['Word']
console = CommandLine(arguments.file, words, file_queue)
console.Prompt = ''
thread = threading.Thread(target=console.cmdloop, args=('', ))
thread.start()
print("\n")
for hit_Word, hit_sentence in console.watch():
print("Found %r in line: %r" % (hit_Word, hit_sentence))
print('Host FINISH (inner): ' + ' ' + arguments.address + ':' + arguments.port)
def start_client(Host, port, queue):
Host = Host
port = int(port) # arbitrary non-privileged port
queue = queue
soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
soc.connect((Host, port))
except:
print("Client connection error" + str(sys.exc_info()))
sys.exit()
print("Enter 'quit' to exit")
message = ""
while message != 'quit':
time.sleep(0.05)
if(message != ""):
soc.sendall(message.encode("utf8"))
if soc.recv(BUFFER_SIZE).decode("utf8") == "-":
pass # null operation
string = ""
if (not queue.empty()):
string = str(queue.get_nowait()) + "\n"
if(string == None or string == ""):
message = ""
else:
message = string
soc.send(b'--quit--')
def start_server(Host, port):
Host = Host
port = int(port) # arbitrary non-privileged port
soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# SO_REUSEADDR flag tells the kernel to reuse a local socket in TIME_WAIT state, without waiting for its natural timeout to expire
soc.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
print("Socket created")
try:
soc.bind((Host, port))
except:
print("Bind failed. Error : " + str(sys.exc_info()))
sys.exit()
soc.listen(5) # queue up to 5 requests
print("Socket now listening")
# infinite loop- do not reset for every requests
while True:
connection, address = soc.accept()
ip, port = str(address[0]), str(address[1])
print("Connected with " + ip + ":" + port)
try:
threading.Thread(target=client_thread, args=(connection, ip, port)).start()
except:
print("Thread did not start.")
traceback.print_exc()
soc.close()
def client_thread(connection, ip, port, max_buffer_size=BUFFER_SIZE):
is_active = True
while is_active:
client_input = receive_input(connection, max_buffer_size)
if "--QUIT--" in client_input:
print("Client is requesting to quit")
connection.close()
print("Connection " + ip + ":" + port + " closed")
is_active = False
Elif not client_input == "":
print("{}".format(client_input))
connection.sendall("-".encode("utf8"))
else:
connection.sendall("-".encode("utf8"))
def receive_input(connection, max_buffer_size):
client_input = connection.recv(max_buffer_size)
client_input_size = sys.getsizeof(client_input)
if client_input_size > max_buffer_size:
print("The input size is greater than expected {}".format(client_input_size))
decoded_input = client_input.decode("utf8").rstrip() # decode and strip end of line
result = process_input(decoded_input)
return result
def process_input(input_str):
return str(input_str).upper()
if __name__ == '__main__':
print('Host START (inner - as main).')
main(sys.argv[1:])
Le contrôle direct du canal/tampon d'entrée de la console du sous-processus serait la solution préférable à ce problème. Car c'est la prime de 500 Réputation.
Malheureusement, je manque de temps. Par conséquent, je pourrais utiliser une de ces solutions de contournement pour l'instant et les remplacer par la solution appropriée plus tard. Ou peut-être que je dois utiliser l'option nucléaire, une seule console, où la sortie du journal en cours est interrompue pendant toute entrée au clavier de l'utilisateur et imprimée par la suite. Bien sûr, cela peut entraîner des problèmes de tampon, lorsque l'utilisateur décide de taper quelque chose à mi-chemin.
Avec la réponse de James Kent, j'obtiens le comportement souhaité, lorsque je démarre un script avec le code via la ligne de commande Windows (cmd) ou PowerShell. Cependant, lorsque je démarre ce même script via Eclipse/PyDev avec "Python run", la sortie est toujours imprimée sur la console principale Eclipse/PyDev, tandis que la deuxième console du sous-processus reste vide et reste inactive. Cependant, je suppose que c'est une autre spécialité système/environnement et un problème différent.
from sys import argv, stdin, stdout
from threading import Thread
from cmd import Cmd
from time import sleep
from datetime import datetime
from subprocess import Popen, PIPE, CREATE_NEW_CONSOLE
INTRO = 'command line'
Prompt = '> '
class CommandLine(Cmd):
"""Custom console"""
def __init__(self, subprocess, intro=INTRO, Prompt=PROMPT):
Cmd.__init__(self)
self.subprocess = subprocess
self.intro = intro
self.Prompt = Prompt
self.doc_header = intro
self.running = False
def do_date(self, args):
"""Prints the current date and time."""
print(datetime.now())
sleep(1)
def do_exit(self, args):
"""Exits this command line application."""
print("Exit by user command.")
if self.subprocess is not None:
try:
self.subprocess.terminate()
except:
self.subprocess.kill()
exit()
class Console():
def __init__(self):
if '-r' not in argv:
self.p = Popen(
['python.exe', __file__, '-r'],
stdin=PIPE,
creationflags=CREATE_NEW_CONSOLE
)
else:
while True:
data = stdin.read(1)
if not data:
# break
sleep(1)
continue
stdout.write(data)
def write(self, data):
self.p.stdin.write(data.encode('utf8'))
self.p.stdin.flush()
def getSubprocess(self):
if self.p:
return self.p
else:
return None
class Feeder (Thread):
def __init__(self, console):
self.console = console
Thread.__init__(self)
def run(self):
feeding(self.console)
def feeding(console):
for i in range(0, 100):
console.write('test %i\n' % i)
sleep(1)
if __name__ == '__main__':
p = Console()
if '-r' not in argv:
thread = Feeder(p)
thread.setDaemon(True)
thread.start()
cl = CommandLine(subprocess=p.getSubprocess())
cl.use_rawinput = False
cl.Prompt = Prompt
cl.cmdloop('\nCommand line is waiting for user input (e.g. help).')
Dans le texte des questions ci-dessus, j'ai mentionné l'utilisation de la bibliothèque ctypes pour accéder directement à l'API de la console Windows comme autre solution (sous "Édition-1: Autres réflexions"). Ou en utilisant une seule console d'une manière, l'invite d'entrée reste toujours en bas comme option nucléaire pour tout ce problème. (sous "Edit-2: En outre des pensées")
Pour utiliser la bibliothèque ctypes, je me serais orienté sur la réponse suivante à Changer la police de la console sous Windows . Et pour utiliser une seule console, j'aurais essayé la réponse suivante à Conserver la ligne d'entrée de la console sous la sortie . Je pense que ces deux réponses peuvent offrir des erreurs potentielles en ce qui concerne ce problème et peut-être qu'elles sont utiles aux autres sur la manière de traverser ce message. Aussi, si je trouve le temps, j'essaierai s'ils fonctionnent d'une manière ou d'une autre.
Le problème auquel vous êtes confronté est l'architecture du sous-système de console sous Windows, la fenêtre de console que vous voyez normalement n'est pas hébergée par cmd.exe mais par conhost.exe, un processus enfant d'une fenêtre conhost ne peut se connecter qu'à un instance de conhost unique, ce qui signifie que vous êtes limité à une seule fenêtre par processus.
Cela conduit ensuite à avoir un processus supplémentaire pour chaque fenêtre de console que vous souhaitez avoir, puis afin de regarder afficher quoi que ce soit dans cette fenêtre, vous devez regarder comment stdin et stdout sont normalement traités, en ce sens qu'ils sont écrits et lus à partir de par l'instance conhost, sauf si vous transformez stdin en pipe (afin que vous puissiez écrire dans le processus) il ne provient plus de conhost mais plutôt de votre processus parent et en tant que tel, conhost n'a aucune visibilité. Cela signifie que tout ce qui est écrit dans stdin n'est lu que par le processus enfant et n'est donc pas affiché par conhost.
Pour autant que je sache, il n'y a pas de moyen de partager la pipe comme ça.
En tant qu'effet secondaire, si vous faites de stdin un canal, toutes les entrées de clavier envoyées à la nouvelle fenêtre de console ne vont nulle part, car stdin n'est pas connecté à cette fenêtre.
Pour une fonction de sortie uniquement, cela signifie que vous pouvez générer un nouveau processus qui communique avec le parent via un canal vers stdin et renvoie tout à stdout.
Voici une tentative:
#!python3
import sys, subprocess, time
class Console():
def __init__(self):
if '-r' not in sys.argv:
self.p = subprocess.Popen(
['python.exe', __file__, '-r'],
stdin=subprocess.PIPE,
creationflags=subprocess.CREATE_NEW_CONSOLE
)
else:
while True:
data = sys.stdin.read(1)
if not data:
break
sys.stdout.write(data)
def write(self, data):
self.p.stdin.write(data.encode('utf8'))
self.p.stdin.flush()
if (__name__ == '__main__'):
p = Console()
if '-r' not in sys.argv:
for i in range(0, 100):
p.write('test %i\n' % i)
time.sleep(1)
Donc, un simple tuyau sympa entre deux processus et faisant écho de l'entrée à la sortie si c'est le sous-processus, j'ai utilisé un -r pour signifier si l'instance est un processus mais il existe d'autres façons selon la façon dont vous l'implémentez.
Plusieurs choses à noter:
__file__
__file__
cette approche peut nécessiter une modification si elle est gelée à l'aide de cx_Freeze ou similaire.pour une version qui peut être gelée avec cx_Freeze:
import sys, subprocess
class Console():
def __init__(self, ischild=True):
if not ischild:
if hasattr(sys, 'frozen'):
args = ['Console.exe']
else:
args = [sys.executable, __file__]
self.p = subprocess.Popen(
args,
stdin=subprocess.PIPE,
creationflags=subprocess.CREATE_NEW_CONSOLE
)
else:
while True:
data = sys.stdin.read(1)
if not data:
break
sys.stdout.write(data)
def write(self, data):
self.p.stdin.write(data.encode('utf8'))
self.p.stdin.flush()
if (__name__ == '__main__'):
p = Console()
from Console import Console
import sys, time
if (__name__ == '__main__'):
p = Console(False)
for i in range(0, 100):
p.write('test %i\n' % i)
time.sleep(1)
from cx_Freeze import setup, Executable
setup(
name = 'Console-test',
executables = [
Executable(
'Console.py',
base=None,
),
Executable(
'test.py',
base=None,
)
]
)
Nouvelle version qui devrait fonctionner avec des outils de développement comme IDLE
#!python3
import ctypes, sys, subprocess
Kernel32 = ctypes.windll.Kernel32
class Console():
def __init__(self, ischild=True):
if ischild:
# try allocate new console
result = Kernel32.AllocConsole()
if result > 0:
# if we succeed open handle to the console output
sys.stdout = open('CONOUT$', mode='w')
else:
# if frozen we assume its names Console.exe
# note that when frozen 'Win32GUI' must be used as a base
if hasattr(sys, 'frozen'):
args = ['Console.exe']
else:
# otherwise we use the console free version of python
args = ['pythonw.exe', __file__]
self.p = subprocess.Popen(
args,
stdin=subprocess.PIPE
)
return
while True:
data = sys.stdin.read(1)
if not data:
break
sys.stdout.write(data)
def write(self, data):
self.p.stdin.write(data.encode('utf8'))
self.p.stdin.flush()
if (__name__ == '__main__'):
p = Console()
from Console import Console
import sys, time
if (__name__ == '__main__'):
p = Console(False)
for i in range(0, 100):
p.write('test %i\n' % i)
time.sleep(1)
from cx_Freeze import setup, Executable
setup(
name = 'Console-test',
executables = [
Executable(
'Console.py',
base='Win32GUI',
),
Executable(
'test.py',
base=None,
)
]
)
Cela pourrait être rendu plus robuste, c'est-à-dire toujours vérifier une console existante et la détacher si elle est trouvée avant de créer une nouvelle console, et éventuellement une meilleure gestion des erreurs.