Opening a Python Thread in a New Console Window

Opening a Python thread in a new console window

Rather than use a console or terminal window, re-examine your problem. What you are trying to do is create a GUI. There are a number of cross-platform toolkits including Wx and Tkinter that have widgets to do exactly what you want. A text box for output and an entry widget for reading keyboard input. Plus you can wrap them in a nice frame with titles, help, open/save/close, etc.

How to run a function/thread in a different terminal window in python?

What you are trying to accomplish isn't possible with just threads, when you create a new Thread it shares all the variables with other threads in your program, including sys.stdout / sys.stdin.

Normally you don't have to worry about PIPES in python programs because it takes care of it for you. print sends the text to sys.stdout and input grabs text from sys.stdin (and error messages are sent to sys.stderr)

So running one program in two terminal windows would mean you would have to have more then one input/output streams, to which there are two solutions:

  1. run a completely separate program with subprocess.Popen like the other fellow described and figure out how to bridge information across the two which is a real pain.

or 2. create your own terminal window with something like tkinter, which is difficult from scratch but luckily IDLE has the majority of the code available in the standard library.

Here is an adapted version of PyShell from idlelib.PyShell to run a Thread instead of the interactive interpretive:

from idlelib import PyShell,EditorWindow
import threading,sys
try:
import tkinter as tk #python 3.X
except ImportError:
import Tkinter as tk #python 2
import tkMessageBox as messagebox
tk.messagebox = messagebox

class ThreadShell(PyShell.PyShell):
"""mostly copied from idlelib.PyShell module but adapted to work with threads"""
#__adapted_by__ = "Tadhg McDonald-Jensen"
def __init__(self, tk_root,target=None):
#not sure exactly what the FileList object is for but it is required by the shell
flist = PyShell.PyShellFileList(tk_root)
super(ThreadShell,self).__init__(flist)

#internal event flag for input, allows thread waiting for input to wait until a tk event handles it
self.__input_flag = threading.Event()
#target is stored and called in .run_command() which also deals with finishing the shell
self.target = target
self.thread = threading.Thread(target=self.run_command)
#tk_root.after makes the .start method call when the program starts (after 0 miliseconds)
tk_root.after(0,self.start)

def start(self):
"""starts executing the Thread"""
super(ThreadShell,self).beginexecuting()
try:
self.thread.start()
except RuntimeError:
self.executing = 0
self.canceled = 0
#self.top.quit() #this causes double deletion warnings with better Implementation of mainloop

beginexecuting = start

def run_command(self):
"""calls target from constructor with self as argument then cleans up shell"""
if self.target:
self.target(self)
self.prompt_exit()
self.executing = 0
self.canceled = 0
try:
self.text.after(1,self.close)
except RuntimeError:
pass #tkinter has issues with changing threads so often after closing one shell others will throw this error

def printf(self,*stuff,**kw):
"""works just like python 3.x print function but writes to shell's .stdout file"""
if self.executing:
## if USING_OLD_METHOD: #Pretty sure this would do exact same thing
## kw.setdefault("file",self.stdout)
## print(*stuff,**kw), self.resetoutput()
## return
sep = kw.get("sep"," ")
end = kw.get("end","\n")
text = sep.join(stuff) + end
self.stdout.write(text)
self.resetoutput()

def input(self,prompt="",timeout=None):
"""python 2 equivelent to raw_input or py 3+ input
Prompts user for input and freezes thread until input is given
Will return "" if .executing is False or it timed out from optional timeout argument"""
if self.executing or self.closing:
if prompt:
self.stdout.write(prompt)
self.__in_buffer = ""
self.__input_flag.clear()
self.reading=True
self.__input_flag.wait(timeout)
#input is inserted into .__in_buffer by other events
#then set __input_flag so that it can be delivered to thread
self.reading = False
return self.__in_buffer.strip("\n")
else:
raise RuntimeError("cannot take input after finished")

def prompt_exit(self):
"""writes press enter to quit" to the console colour then waits for input"""
self.executing = False
self.closing = True
self.console.write("\n press enter to quit")
self.input()

def join_thread(self,timeout=None):
"""sets .executing label to False then waits to join thead,
returns True if thread finished or False if timeout activated"""
self.executing = False
self.closing = True
if self.thread:
self.thread.join(timeout)
return not self.thread.is_alive()

def _close(self):
"Extend EditorWindow._close(), joins thread to close it"

# Restore std streams
sys.stdout = self.save_stdout
sys.stderr = self.save_stderr
sys.stdin = self.save_stdin
# Break cycles
self.interp = None
self.console = None
self.flist.pyshell = None
self.history = None
EditorWindow.EditorWindow._close(self)
self.join_thread()

def stop_readline(self):
self.__in_buffer = ""
self.__input_flag.set()

def update_in(self):
"""updates input from user, I think some of the labels are probably unnecessary but it is easier to leave it alone"""
line = self.text.get("iomark", "end-1c")
if len(line) == 0: # may be EOF if we quit our mainloop with Ctrl-C
line = "\n"
self.resetoutput()
if self.canceled:
self.canceled = 0
if self.endoffile:
self.endoffile = 0
line = ""
self.__in_buffer = line
self.__input_flag.set()

def cancel_callback(self, event=None):
try:
if self.text.compare("sel.first", "!=", "sel.last"):
return # Active selection -- always use default binding
except:
pass
if not (self.executing or self.reading):
return "break"
self.endoffile = 0
self.canceled = 1
if self.reading:
self.update_in()
return "break"

def eof_callback(self, event):
if self.executing and not self.reading:
return # Let the default binding (delete next char) take over
if not (self.text.compare("iomark", "==", "insert") and
self.text.compare("insert", "==", "end-1c")):
return # Let the default binding (delete next char) take over
if not self.executing:
self.resetoutput()
self.close()
else:
self.canceled = 0
self.endoffile = 1
self.update_in()
return "break"

def enter_callback(self, event):
"""called when the enter/return key is pressed,
only the recursive self.top.mainloop() / self.top.quit() had to be changed for support"""
# it is very long to copy/paste for the one line change, so I override the method temporarily
save = self.top.quit
self.top.quit = self.update_in
super(ThreadShell,self).enter_callback(event)
self.top.quit = save

#stupid module depends on this being set from the main function, so it needs to be done manually
PyShell.use_subprocess = True

#this defines the root tkinter window and sets it up
root = tk.Tk()
EditorWindow.fixwordbreaks(root)
root.withdraw()
#I need this to work on my mac, not sure if there are other OS specific stuff that should be included
try:
from idlelib import macosxSupport
macosxSupport.setupApp(root, None)
except (ImportError,AttributeError):
pass

##!!!!!!!!!!!!!!!!!!!! And This Is The Part You Need To Worry About !!!!!!!!!!!!!!!!!!!!##

switch = threading.Event()
switch.clear()

def foo(shell):
global x
x = shell.input("enter a message: ")
switch.set()
shell.printf("message sent")

def foo2(shell):
shell.printf("waiting for message...")
while shell.executing and not switch.is_set():
switch.wait(2) # by using shell.executing in the loop it will occasionally check
# if the program should quit because the window was closed
if shell.executing:
shell.printf("message recieved: ",x)

shell1 = ThreadShell(root,foo)
shell2 = ThreadShell(root,foo2)
first_time = True
while shell1.executing or shell2.executing or first_time:
first_time = False
root.mainloop()
root.destroy()

How to open new console in Python and get data from user that typed in it?

I'm a little unsure why you would want to do this. Can I suggest an entirely pythonic approach using threading and queues:

import threading
from queue import Queue
import time

def worker(q):
"""thread worker function"""
running = True
while running:
message = q.get()
print('Worker received message: {}'.format(message))
if message == 'KILL':
running = False
print('Worker completed')

if __name__ == '__main__':
q = Queue()
worker = threading.Thread(target=worker, args=(q,))
worker.start()
running = True
while running:
user_input = input('Input some data: ')
q.put(user_input)
if user_input == 'KILL':
running = False
time.sleep(0.5)
print('Program terminated')

You now have the ability to send commands to another python script from a console. The worker can do whatever you wish now. For a more detailed example see this post


Alternatively if thats not acceptable, what you are trying to do is 'PIPE' data from a command line in both directions. Have a look at this post where they do:

proc = subprocess.Popen(['some_cmd', '--command_line_parameters'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = proc.communicate()

Python 2.7 - How to spawn separate Windows consoles for individual subprocess.call() tasks when using threading?

I now have working code thanks to J.F. Sebastian's comment. The key was to use 'cmd.exe /c start "NAME"' at the start of my command line, join everything up as a string rather than a list, and then pass the command line string to subprocess.call() with shell=True. Working code is thus:

from time import sleep
import threading
from subprocess import call

def runmodel(arg):
call(arg, shell=True) # not the addition of shell=True - this is now required as the command line is being passed as a string rather than a list - I could not get the syntax with quotation marks and everything else to work without using a string and shell=True.
sGlobal.release()

if __name__ == '__main__':

n = 6 # maximum number of simultaneous runs
s = 15 # delay between starts in seconds

simulations = ['cmd.exe /c start "NAME" "<full file path to model exe>" -some -flags -here "<full file path to model control file>"'] # big list of simulations to run in here - each item in list is a string that represents the full command line to run a simulation

threads = []
global sGlobal

sGlobal = threading.Semaphore(n)

for arg in simulations:
sGlobal.acquire()
t = threading.Thread(target=runmodel, args=(arg,))
threads.append(t)
t.start()
sleep(s)

for t in threads:
t.join()

How can I open two consoles from a single script

If you don't want to reconsider your problem and use a GUI such as in @Kevin's answer then you could use subprocess module to start two new consoles concurrently and display two given strings in the opened windows:

#!/usr/bin/env python3
import sys
import time
from subprocess import Popen, PIPE, CREATE_NEW_CONSOLE

messages = 'This is Console1', 'This is Console2'

# open new consoles
processes = [Popen([sys.executable, "-c", """import sys
for line in sys.stdin: # poor man's `cat`
sys.stdout.write(line)
sys.stdout.flush()
"""],
stdin=PIPE, bufsize=1, universal_newlines=True,
# assume the parent script is started from a console itself e.g.,
# this code is _not_ run as a *.pyw file
creationflags=CREATE_NEW_CONSOLE)
for _ in range(len(messages))]

# display messages
for proc, msg in zip(processes, messages):
proc.stdin.write(msg + "\n")
proc.stdin.flush()

time.sleep(10) # keep the windows open for a while

# close windows
for proc in processes:
proc.communicate("bye\n")

Here's a simplified version that doesn't rely on CREATE_NEW_CONSOLE:

#!/usr/bin/env python
"""Show messages in two new console windows simultaneously."""
import sys
import platform
from subprocess import Popen

messages = 'This is Console1', 'This is Console2'

# define a command that starts new terminal
if platform.system() == "Windows":
new_window_command = "cmd.exe /c start".split()
else: #XXX this can be made more portable
new_window_command = "x-terminal-emulator -e".split()

# open new consoles, display messages
echo = [sys.executable, "-c",
"import sys; print(sys.argv[1]); input('Press Enter..')"]
processes = [Popen(new_window_command + echo + [msg]) for msg in messages]

# wait for the windows to be closed
for proc in processes:
proc.wait()

console application main thread is not in main loop using pysimplegui

It looks like you cannot call PySimpleGUI/tkinter in another thread.
Here, try to set main program as one thread and called in PySimpleGUI/tkinter.
The same, remember that don't call PySimpleGUI directly in your main_program and use method window.write_event_value to generate an event, then do it in your event loop.

Example code,

from time import sleep
import threading
import PySimpleGUI as sg

def hello():
layout = [[sg.Text("Hello, my friend !")]]
window = sg.Window("Hello", layout, keep_on_top=True, modal=True)
window.read(timeout=1000, close=True)

def main_program():
count = 5
while count > 0:
window.write_event_value("Hello", None)
sleep(3)
count -=1
window.write_event_value(sg.WINDOW_CLOSED, None)

layout = [[]]

window = sg.Window("Title", layout, alpha_channel=0, finalize=True)
threading.Thread(target=main_program, daemon=True).start()
while True:

event, values = window.read()
if event == sg.WINDOW_CLOSED:
break
elif event == "Hello":
hello()

window.close()

Python: Create another python console window sharing same data

If you are on windows you can use win32console module to open a second console for your child thread(since you want them to share same data) output. This is the most simple and easiest way that works if you are on windows.

Here is a sample code:

import win32console
import threading
from time import sleep

def child_thread():
win32console.FreeConsole() #Frees child_thread from using main console
win32console.AllocConsole() #Creates new console and all input and output of the child_thread goes to this new console

while True:
print("This is in child_thread console and the count is:",count)
#prints in new console dedicated to this thread

if __name__ == "__main__":
count = 0
threading.Thread(target=child_thread, args=[]).start()
while True:
count+=1
print("Hello from the main console - this is count:", count)
#prints in main console
sleep(1)
#and whatever else you want to do in ur main process

There is also a better method of using queues from the queue module as it gives you a more control over race conditions.

Here is the win32console module documentation



Related Topics



Leave a reply



Submit