0

I am running a simple server on windows and outputting the process to a simple GUI.

I want to keep this process running, but execute other simple processes in the same terminal.

When I try to write any further processes to the terminal the UI freezes, although the server continues to run, the command itself is not executed on the server.

I have a simple batch process to simulate the server that executes every 5 seconds

#runserver.bat
:loop
@echo OFF
@echo %time%
ping localhost -n 6 > nul
goto loop

The output will run indefinitely once called until the script is terminated even when the UI locks up.

from threading import Thread
from subprocess import Popen, PIPE, STDOUT, call
from Tkinter import *
from ttk import Frame, Button, Label, Style
from Queue import Queue, Empty
from collections import deque
from itertools import islice


def iter_except(function, exception):
    try:
        while True:
            yield function()
    except exception:
        return

class buildGUI(Frame):

    def __init__(self, parent):
        Frame.__init__(self, parent)         
        self.parent = parent        
        self.initUI() 

    def initUI(self):

        self.proc = Popen("runserver.bat", shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT)

        q = Queue()
        t = Thread(target=self.reader_thread, args=[q]).start()

        #setup UI      
        self.parent.title("Server Manager")
        self.style = Style()
        self.style.theme_use("default")
        self.pack(fill=BOTH, expand=1)

        self.columnconfigure(1, weight=1)
        self.columnconfigure(3, pad=7)
        self.rowconfigure(7, weight=1)
        self.rowconfigure(5, pad=2)

        self.loglbl = Label(self, text="Log")
        self.loglbl.grid(sticky=W, column=1, pady=4, padx=5)

        self.var = StringVar()
        self.playerlbl = Label(self, text=0, textvariable=self.var)
        self.playerlbl.grid(sticky=W, column=0, row=5, pady=4, padx=5)

        self.area = Text(self)
        self.area.grid(row=1, column=1, columnspan=2, rowspan=10, 
            padx=5, sticky=E+W+S+N)

        self.dirbtn = Button(self, text="dir")
        self.dirbtn.grid(row=3, column=0,pady=4, padx=5)
        self.dirbtn.bind("<Button-1>", self.runProcess)

        self.updateUI(q)

    def runProcess(self, event):
            print "pressed"
            self.proc.communicate("dir") 

    def reader_thread(self, q):
        #add output to thread
        for line in iter(self.proc.stdout.readline, b''):
            q.put(line)

    def updateUI(self, q):
        #use deque to discard lines as they are printed to the UI, 
        for line in deque(islice(iter_except(q.get_nowait, Empty), None), maxlen=5):
            if line is None:
                return # stop updating
            else:
                self.area.insert(END,line) # update GUI

        self.parent.after(1000, self.updateUI, q) # schedule next update

def main():

    root = Tk()
    root.geometry("850x450+100+100")
    app = buildGUI(root)
    root.mainloop()

if __name__ == '__main__':
    main()

I have tried replacing communicate() with stdin.write but both lock up the UI

def runProcess(self, event):
        print "pressed"
        self.proc.communicate("dir") 

I haven't been able to find any examples where someone needed to execute a process during an existing process, most examples assume that a process can either be terminated or the process can be executed when the other is finished, so a poll or terminate can be used.

The only example that reads with an open process appears to terminate the process. [answer/(https://stackoverflow.com/a/16770371)

I have tried calling additional Popen commands, but this just appears to be ignored by the server

Community
  • 1
  • 1
  • your code (based on [my example](http://stackoverflow.com/questions/15362372/display-realtime-output-of-a-subprocess-in-a-tkinter-widget#comment24334740_15362372)) already shows how to get output in "real-time" from a subprocess without blocking a GUI. Read the code and if you don't understand what *any* of the lines does; ask a question – jfs Jan 16 '15 at 13:11
  • If you don't want to discard the output from the child process then remove `deque`, `islice` – jfs Jan 16 '15 at 13:22

1 Answers1

0

To run the second process without blocking the GUI, start the second thread in exactly the same way as it is done for the first process.

If you don't need to capture the output in "real-time" then the code could be simpler: just call subprocess.call() or proc.communicate() in another thread.

If you don't need to capture the output at all then you could use Popen() that does not block for long, to start the child processes.

jfs
  • 399,953
  • 195
  • 994
  • 1,670
  • As I am trying to call commands on the server itself would the command not need to be run on the same thread? 'subprocess.call()' did successfully call the dir command and outputted to the terminal, but I have not been able to call server specific commands. Using 'proc.communicate()' I do not see the call on the server, and I'm not sure that a separate thread would help this would it? –  Jan 16 '15 at 18:09
  • @ShaunFallis: you have one GUI thread that started `"runserver.bat"` child process, one reader thread that reads output from the server. What do you mean by *"call commands on the server"*? Unrelated: if you don't want to see the output in the terminal then just redirect it to `os.devnull` file (set `call(..., stdout=DEVNULL, stderr=subprocess.STDOUT)`) – jfs Jan 16 '15 at 18:18
  • The server is a simple game server, if I type 'player' into command prompt while the server is running it will show a list of the active players. This is what I am trying to replicate while still diverting the full output to the GUI –  Jan 16 '15 at 19:38
  • @ShaunFallis: To get output from `player` command, call `players_list = subprocess.check_output('player')` in a new thread. How does `player` command communicate with the server (sockets, named pipes, ...)? If there are Python binding (or e.g., a simple http-based protocol) then you could send commands from Python directly without running a child process. – jfs Jan 16 '15 at 20:00