Deelaka
Deelaka

Reputation: 13693

How can I open two consoles from a single script

Apart from the scripts own console (which does nothing) I want to open two consoles and print the variables con1 and con2 in different consoles, How can I achieve this.

con1 = 'This is Console1'
con2 = 'This is Console2'

I've no idea how to achieve this and spent several hours trying to do so with modules such as subprocess but with no luck. I'm on windows by the way.


Edit:

Would the threading module do the job? or is multiprocessing needed?

Eg:

enter image description here

Upvotes: 15

Views: 35189

Answers (7)

Goldfish Doc
Goldfish Doc

Reputation: 76

If you are on windows you can use win32console module to open a second console for your thread or subprocess output. This is the most simple and easiest way that works if you are on windows.

Here is a sample code:

import win32console
import multiprocessing

def subprocess(queue):
    win32console.FreeConsole() #Frees subprocess from using main console
    win32console.AllocConsole() #Creates new console and all input and output of subprocess goes to this new console
    while True:
        print(queue.get())
        #prints any output produced by main script passed to subprocess using queue

queue = multiprocessing.Queue()
multiprocessing.Process(Target=subprocess, args=[queue]).start()
while True:
    print("Hello World")
    #and whatever else you want to do in ur main process

You can also do this with threading. You have to use queue module if you want the queue functionality as threading module doesn't have queue

Here is the win32console module documentation

Upvotes: 1

pymux

pymux gets close to what you want: https://github.com/jonathanslenders/pymux

Unfortunately it is mostly a CLI tool replacement for tmux and does not have a decent programmatic API yet.

But hacking it up to expose that API is likely the most robust option if you are serious about this.

The README says:

Parts of pymux could become a library, so that any prompt_toolkit application can embed a vt100 terminal. (Imagine a terminal emulator embedded in pyvim.)

Upvotes: 1

JDOaktown
JDOaktown

Reputation: 4467

I used jfs' response. Here is my embellishment/theft of jfs response. This is tailored to run on Win10 and also handles Unicode:

# https://stackoverflow.com/questions/19479504/how-can-i-open-two-consoles-from-a-single-script
import sys, time, os, locale
from subprocess import Popen, PIPE, CREATE_NEW_CONSOLE

class console(Popen)  :
    NumConsoles = 0
    def __init__(self, color=None, title=None):
        console.NumConsoles += 1

        cmd = "import sys, os, locale"
        cmd += "\nos.system(\'color " + color + "\')" if color is not None else ""
        title = title if title is not None else "console #" + str(console.NumConsoles)
        cmd += "\nos.system(\"title " + title + "\")"
        # poor man's `cat`
        cmd += """
print(sys.stdout.encoding, locale.getpreferredencoding() )
endcoding = locale.getpreferredencoding()
for line in sys.stdin:
    sys.stdout.buffer.write(line.encode(endcoding))
    sys.stdout.flush()
"""

        cmd = sys.executable, "-c", cmd
        # print(cmd, end="", flush=True)
        super().__init__(cmd, stdin=PIPE, bufsize=1, universal_newlines=True, creationflags=CREATE_NEW_CONSOLE, encoding='utf-8')


    def write(self, msg):
        self.stdin.write(msg + "\n" )

if __name__ == "__main__":
    myConsole = console(color="c0", title="test error console")
    myConsole.write("Thank you jfs. Cool explanation")
    NoTitle= console()
    NoTitle.write("default color and title! This answer uses Windows 10")
    NoTitle.write(u"♥♥♥♥♥♥♥♥")
    NoTitle.write("♥")
    time.sleep(5)
    myConsole.terminate()
    NoTitle.write("some more text. Run this at the python console.")
    time.sleep(4)
    NoTitle.terminate()
    time.sleep(5)

Upvotes: 0

jfs
jfs

Reputation: 414245

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

Upvotes: 15

marxin
marxin

Reputation: 3922

I don't know if it suits you, but you can open two Python interpreters using Windows start command:

from subprocess import Popen
p1 = Popen('start c:\python27\python.exe', shell=True)
p2 = Popen('start c:\python27\python.exe', shell=True)

Of course there is problem that now Python runs in interactive mode which is not what u want (you can also pass file as parameter and that file will be executed).

On Linux I would try to make named pipe, pass the name of the file to python.exe and write python commands to that file. 'Maybe' it will work ;)

But I don't have an idea how to create named pipe on Windows. Windows API ... (fill urself).

Upvotes: 5

Kevin
Kevin

Reputation: 76194

You can get something like two consoles using two Tkinter Text widgets.

from Tkinter import *
import threading

class FakeConsole(Frame):
    def __init__(self, root, *args, **kargs):
        Frame.__init__(self, root, *args, **kargs)

        #white text on black background,
        #for extra versimilitude
        self.text = Text(self, bg="black", fg="white")
        self.text.pack()

        #list of things not yet printed
        self.printQueue = []

        #one thread will be adding to the print queue, 
        #and another will be iterating through it.
        #better make sure one doesn't interfere with the other.
        self.printQueueLock = threading.Lock()

        self.after(5, self.on_idle)

    #check for new messages every five milliseconds
    def on_idle(self):
        with self.printQueueLock:
            for msg in self.printQueue:
                self.text.insert(END, msg)            
                self.text.see(END)
            self.printQueue = []
        self.after(5, self.on_idle)

    #print msg to the console
    def show(self, msg, sep="\n"):
        with self.printQueueLock:
            self.printQueue.append(str(msg) + sep)

#warning! Calling this more than once per program is a bad idea.
#Tkinter throws a fit when two roots each have a mainloop in different threads.
def makeConsoles(amount):
    root = Tk()
    consoles = [FakeConsole(root) for n in range(amount)]
    for c in consoles:
        c.pack()
    threading.Thread(target=root.mainloop).start()
    return consoles

a,b = makeConsoles(2)

a.show("This is Console 1")
b.show("This is Console 2")

a.show("I've got a lovely bunch of cocounts")
a.show("Here they are standing in a row")

b.show("Lorem ipsum dolor sit amet")
b.show("consectetur adipisicing elit")

Result:

enter image description here

Upvotes: 6

Paulo Scardine
Paulo Scardine

Reputation: 77261

Do you know about screen/tmux?

How about tmuxp? For example, you can try to run cat in split panes and use "sendkeys" to send output (but dig the docs, may be there is even easier ways to achieve this).

As a side bonus this will work in the text console or GUI.

Upvotes: -1

Related Questions