2

Here is what I coded...

import tkinter as tk
import subprocess
import sys
import time
import os
import tkinter.font as font
from tkinter.ttk import *

app = tk.Tk()
app.geometry("400x400")
app.configure(bg='gray')

photo = tk.PhotoImage(file=r"C:\Users\ex\ex_button_active.png")
myFont = font.Font(family='Helvetica', size=20, weight='normal')

tk.Label(app, text='EX', bg='gray', font=(
    'Verdana', 15)).pack(side=tk.TOP, pady=10)
app.iconbitmap(r'C:\Users\ex\ex_icon.ico')

start = time.time()
cmd = sys.executable + " -c 'import time; time.sleep(2)' &"
subprocess.check_call(cmd, shell=True)
assert (time.time() - start) < 1

p = subprocess.Popen(cmd, shell=True)


def ex_activation():
    #Python Code
    #Python Code...

def ex_stop():
    sys.exit(ex_activation) #This area is basically where I have a button to terminate the other script running. 
            #I have tried sys.exit() and had the same result

ex_activation_button = tk.Button(app,
                                    bg='black',
                                    image=photo,
                                    width=120,
                                    height=120,
                                    command=ex_activation)
ex_stop_button = tk.Button(app,
                              bg='Gray',
                              text='ex',
                              width=12,
                              command=ex_stop
                              height=3)
ex_stop_button['font'] = myFont

app.title("Example")
ex_activation_button.pack(side=tk.TOP)
ex_stop_button.pack(side=tk.LEFT)

app.mainloop()

I am looking for a way to get my program to stop the program the other button runs. I realized that this maybe be a "self destruct button" but I don't know how to do this with the script the other button runs. Any help greatly appreciated! I tried killing the code by putting the def ex_activation in the p.kill This did not work...

Redgar Pro
  • 51
  • 7
  • wait wait, what does the other python script contain? is it also tkinter? its best to `import` it rather than executing it. Try `root.destroy()` to get out of your current script – Delrius Euphoria Aug 13 '20 at 06:39
  • The other python script isnt really the problem. Its terminating it. Also, importing the python script wouldnt allow my button to run it when pressed right? And if I could, how would I do that? – Redgar Pro Aug 13 '20 at 07:21
  • put all of your code in the new script inside of a function and import that function and run it with the button? – Delrius Euphoria Aug 13 '20 at 07:59
  • I mean it sounds right but 1470 lines of code in the other script with it's own seperate functions doesn't feel right. – Redgar Pro Aug 13 '20 at 08:19
  • give it a try? all you have to do is define a new function and indent the rest of the code inside of it? if you think its not safe, then create a backup too – Delrius Euphoria Aug 13 '20 at 08:24

1 Answers1

2

If the other python script is made to run forever (has some kind of while True:), you can't run it on the command line as you did, because it will freeze your window while that script is running.

In order to run a python script on background you will need to do it with the subprocess library. (Find out here)

I also found an answer of another question that uses check_ouput() in order to know when the python program has finished. This can also be useful if you want to send a status to the tkinter app: you can print("33% Complete"), for example. You could add this in tkinter's main loop, so you always know if your program is running or not.

And last but not least, to kill that process (using the stop button), you should do it using os, and looking for the subprocess' ID. Here you can also find a good example.

I would try something like this:

cmd = "exec python file.py"
p = subprocess.Popen(cmd, shell=True)
# Continue running tkinter tasks.
tk.update()
tk.update_idletasks() # These both lines should be inside a while True
# Stop secondary program
p.kill()

EDIT

Example code using your question's code. WARNING: I have changed the png file location for testing, commented the app icon, and tested ONLY on Windows.

It's important to remove the mainloop() on the main file and put update...() in order to catch the keyboardInterrupt that (I don't know why) is killing both parent and child process.

I invite you to try it and be as happy as I have been when it was working after half an hour of testing!!

File 1: daemon.py - this file will run forever.

from time import sleep
from sys import exit

while True:
    try:
        print("hello")
        sleep(1)
    except KeyboardInterrupt:
        print("bye")
        exit()

File 2: tkinterapp.py - The name is self-explainatory

import tkinter as tk
import subprocess
import sys
import time
import os
import tkinter.font as font
from tkinter.ttk import *

app = tk.Tk()
app.geometry("400x400")
app.configure(bg='gray')

photo = tk.PhotoImage(file=r"C:\Users\royal\github\RandomSketches\baixa.png")
myFont = font.Font(family='Helvetica', size=20, weight='normal')

tk.Label(app, text='EX', bg='gray', font=(
    'Verdana', 15)).pack(side=tk.TOP, pady=10)
# app.iconbitmap(r'C:\Users\ex\ex_icon.ico')


def ex_activation():
    global pro
    print("running!")
    pro = subprocess.Popen("python daemon.py", shell=True)

def ex_stop():
    global pro
    print("stopping!")
    os.kill(pro.pid, 0)

ex_activation_button = tk.Button(app,
                                    bg='black',
                                    image=photo,
                                    width=120,
                                    height=120,
                                    command=ex_activation)
ex_stop_button = tk.Button(app,
                              bg='Gray',
                              text='ex',
                              width=12,
                              command=ex_stop, # BE CAREFUL You were missing a "," here !!!
                              height=3)
ex_stop_button['font'] = myFont

app.title("Example")
ex_activation_button.pack(side=tk.TOP)
ex_stop_button.pack(side=tk.LEFT)

# app.mainloop()
while True:
    try:
        app.update()
        app.update_idletasks()
    except KeyboardInterrupt:
        pass
Eric Roy
  • 334
  • 5
  • 15
  • This looks great and I will try this. If it works, I will accept the answer. The other program does infinitely run until I stop the process in the IDE or the program encounters an error and automatically stops. But the program will run when the button is pressed. It runs in the background. The issue is in stopping the program with the stop button that the other button executed. – Redgar Pro Aug 13 '20 at 07:49
  • Okey, let me know if I can help you with anything else ;) – Eric Roy Aug 13 '20 at 08:31
  • I did the above comment from Cool Cloud. Now all of it is in one python file. The kill button doesn't work. I updated the question with the new code... – Redgar Pro Aug 13 '20 at 19:52
  • If you only have one python file, you will not be able to call the same python file in the terminal, to perform a different job. But i think you forgot the `exec` at the beginning of the command, so that's why a p.kill() wouldn't work. On the link above they stated that this "exec" was important in order to stop the program, so maybe consider trying it :) – Eric Roy Aug 13 '20 at 20:52
  • so I reverted back to `sys.exit(ex_activation` as the command for the stop button. When I press the stop button before pressing the start button, the program closes successfully. As soon as the other process is running and I attempt to stop it, the program freezes but the other program is still running normally without being interrupted. Any advice? – Redgar Pro Aug 13 '20 at 21:16
  • When the other file (now the same file) is running, you are not running the `app.mainloop()`. I mean: during the execution of the other program, you should add some `app.update()` and `app.update_idletasks()`, to keep track if a button has been pressed, as it will only check for that when the other file has finished. That's why we have proposed you to run it on a separate command, or using threads, to prevent the button (or the entire app) from not responding. – Eric Roy Aug 13 '20 at 21:19
  • I don't mean to be a bother but how could I do that with the given code above? By the way, I greatly appreciate you sticking around to assist me. Could you possibly update your answer? – Redgar Pro Aug 13 '20 at 21:51
  • Okey, let me edit my answer. I will test it myself too, so it may take some time ! – Eric Roy Aug 13 '20 at 22:10
  • 1
    WOW! It was incredible seeing it work! If I have an issue with any tkinter programs, I would totally come to you :) – Redgar Pro Aug 13 '20 at 23:43