1

I'm currently trying to write a Python program that starts a Tkinter GUI. One of the features of the GUI is, that it runs a shell command after clicking a button. For this I use the subprocess module.

Since the shell command takes a while to complete, I use a thread so the GUI does not freeze.

So far, this all works, but now I would like to add another button, that can kill the shell command. If I run the command without my Python GUI, I just use ctrl-c. How can I do that within my GUI, at the click of an button? I'd Imagine that this is a fairly common and easy thing to do, but I just didn't find anything that would work for me ...

Here is a minimal version of my code:

import subprocess
import Tkinter as tk
from threading import Thread
import signal

class GUI:
    def __init__(self,master):
        self.master = master
        master.title('Program')

        #Button to Run command
        self.button_idarun = tk.Button(master,text="Run",command=self.Runthread)
        self.button_idarun.grid(row = 0, column = 0)

        #Button to terminate command
        self.button_terminate = tk.Button(master, text = "Stop", command = self.terminate)
        self.button_terminate.grid(row = 1, column =0)


    #Function that runs command.
    def Run(self):        
        self.p = subprocess.Popen("cd IDA/trunk;./job_sge", stdout=subprocess.PIPE, stderr=subprocess.STDOUT,shell=True)
        for line in iter(self.p.stdout.readline,''):
            print line

    #Function that calls Run in a thread, so other buttons can still be pressed ...
    #Probably not the correct way to do this(?)
    def Runthread(self):
        self.thread = Thread(target=self.Run)
        self.thread.start()

    #Function that terminates the process started in RUN
    #This is the part that does not work.
    def terminate(self):
        self.p.send_signal(signal.SIGINT)


root = tk.Tk()
gui = GUI(root)
root.mainloop()

Thanks in advance for any help you guys can offer, and sorry for my bad coding. I'm trying my best here :-/

Edit:

So I now tried an even simpler version of my code and replaced the command that I actually want to run with a simple ping. So the function Run looks now like this:

def Run(self):        
    self.p = subprocess.Popen("ping 8.8.8.8", stdout=subprocess.PIPE, stderr=subprocess.STDOUT,shell=True)
    for line in iter(self.p.stdout.readline,''):
        print line

With the Ping, my Program works without problems. When I click Run, it starts continuously pinging, until I click Stop, when it stops.

So the Problem seems to be the command ./job_sge that I am running. Though I do not understand why this would make a difference. If I execute it from the shell directly, I can kill it with ctrl-c, isn't that the same thing signal.SIGINT does?

Edit 2:

Ok, so I think I found the problem. Appearently, it doesn't work, if you send two commands at once. For example, with the ping command above it works, but when I click the Run button twice, it doesn't anymore.

Same if I cange the Run command for example to

def Run(self):        
    self.p = subprocess.Popen("cd ..;ping 8.8.8.8", stdout=subprocess.PIPE, stderr=subprocess.STDOUT,shell=True)
    for line in iter(self.p.stdout.readline,''):
        print line

then the stop button does not work anymore either.

The problem now is that my command ./job_sge is a bash script that itself runs several other commands. So even if I change cd IDA/trunk;./job_sge to <Full Path>/job_sge, I still can not stop the command ...

1 Answers1

0

Ok, so after figuring out what the actual problem was, I found an answer here on StackOverflow:

How to terminate a python subprocess launched with shell=True

What I had to do was to add preexec_fn=os.setsid to my subprocess.Popen command, and change the terminate function to

def terminate(self):
    os.killpg(os.getpgid(self.p.pid),signal.SIGKILL)

So basically, I combine all the commands in one command group and kill them at once. If I do it like this, it works for me.