1

I have some Python code to call external executable program with sub-process, and read back the output to GUI in real-time, I hope to interrupt the external binary with Ctrl-C at anytime, but it seems not working.

I'm working on Windows. I am hoping to stop the sub-process when hitting Ctrl-C.

Here is my code:

class binary_run():
    def __init__ (self, tcl_file_name, cmd_str, output_ctrl, exe_cwd):

        self.some_exe = "E:\\some.exe"
        self.cmd = cmd = self.some_exe + cmd_str
        self.output_ctrl = output_ctrl


    def PrintOutputInRealTime(self):
        #The following two lines are to make sure the console window is hidden
        startupinfo = subprocess.STARTUPINFO()
        startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW

        #Start the subprocess
        process = subprocess.Popen(self.cmd, startupinfo=startupinfo, stdin=subprocess.PIPE, stdout=subprocess.PIPE)

        while True:
            try:
                output = process.stdout.readline()
                if output == '' and process.poll() is not None:
                    break
                if output:
                    self.output_ctrl.write(output)
            except KeyboardInterrupt: #Never comes here
                process.terminate()
        process.terminate()

    def run_binary(self):
        worker = Thread(target=self.PrintOutputInRealTime)
        worker.start()
Chris Martin
  • 30,334
  • 10
  • 78
  • 137
Y. Yuan
  • 21
  • 5

2 Answers2

1

Thanks to @J.F.Sebastian, instead of using KeyboardInterrupt, I bind the keys (Ctrl+Delete) to my GUI, if the keys are down, the sub-process is terminated, and it works as following:

class binary_run():
    def __init__ (self, tcl_file_name, cmd_str, output_ctrl, exe_cwd):

        self.some_exe = "E:\\some.exe"
        self.cmd = self.some_exe + cmd_str
        self.output_ctrl = output_ctrl

        self.output_ctrl.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)

    def PrintOutputInRealTime(self):
        #The following two lines are to make sure the console window is hidden
        startupinfo = subprocess.STARTUPINFO()
        startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW

        #Start the subprocess
        self.process = subprocess.Popen(self.cmd, startupinfo=startupinfo, stdin=subprocess.PIPE, stdout=subprocess.PIPE)

        while True:
            try:
                #output = process.stdout.readline()
                output = self.process.stdout.readline()
                if output == '' and self.process.poll() is not None:
                    break
                if output:
                    self.output_ctrl.write(output)
            except KeyboardInterrupt:
                self.process.terminate()

        self.process.terminate()

    def OnKeyDown(self, event):
        controlDown = event.ControlDown()
        keycode = event.GetKeyCode()

        if (controlDown and keycode == wx.WXK_DELETE):
            wx.MessageBox('Binary got interrupted!', 'INFO', wx.OK | wx.ICON_INFORMATION)
            self.process.terminate()

    def run_binary(self):
        worker = Thread(target=self.PrintOutputInRealTime)
        worker.start()
Y. Yuan
  • 21
  • 5
0

I myself got into a similar situation sometime back where I was using a thread to run an executable and reading its data. I used the below approach to solve this problem

import threading
import subprocess
import time

class binary_run():
    def __init__ (self):
        self.some_exe = "notepad.exe"

    def PrintOutputInRealTime(self):
        self.process = subprocess.Popen(self.some_exe,stdin=subprocess.PIPE, stdout=subprocess.PIPE)
        while True:
            try:
                output = self.process.stdout.readline()
                if output == '' and self.process.poll() is not None:
                    break
                if output:
                    self.output_ctrl.write(output)
            except:
                pass

    def KillProcess(self):
        self.process.terminate()

if __name__ == "__main__":
    x = binary_run()
    worker = threading.Thread(target=x.PrintOutputInRealTime)
    worker.start()
    try:
        while worker.isAlive():
            time.sleep(0.5)
    except KeyboardInterrupt:
            print "Got Interrupt"
            x.KillProcess()
sarbjit
  • 3,786
  • 9
  • 38
  • 60
  • 1
    another way is to set a custom handler for Ctrl+C: `signal.signal(signal.SIGINT, lambda *a: x.KillProcess())` (in the main thread). – jfs Mar 09 '16 at 21:18
  • @sarbjit Thanks a lot for your help. Somehow, it's not working for my case. I called the external executable program, but the console is hidden, and I print the output of the console to my GUI through pipe. With time.sleep(0.5), the main thread was blocked, I couldn't see the output anymore. I need the GUI still working while printing the output of executable program. – Y. Yuan Mar 10 '16 at 00:56
  • @J.F. Sebastian Thanks! If the console window of my external executable program is shown, the Ctrl+C works, but I need to hide the console window, that's why it cannot receive SIGINT. – Y. Yuan Mar 10 '16 at 00:58
  • @Y.Yuan : if there is no console (to generate SIGINT on Ctrl+C); you should bind the keys explicitly using whatever means your GUI toolkit provides, [example](http://stackoverflow.com/q/16082243/4279) – jfs Mar 10 '16 at 01:03