Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 964
K DawG Avatar asked Oct 20 '13 15:10

K DawG


5 Answers

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()
like image 81
jfs Avatar answered Oct 17 '22 02:10

jfs


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

like image 43
Kevin Avatar answered Oct 17 '22 02:10

Kevin


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

like image 25
marxin Avatar answered Oct 17 '22 02:10

marxin


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


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

like image 32
Goldfish Doc Avatar answered Oct 17 '22 01:10

Goldfish Doc