I have a Tk python program that creates a list of python files in the current directory and generates a button for each of them. When you click a button the corresponding python program is launched via subprocess in a new gnome-terminal. I'd like to switch the button's color to red after the subprocess has finished executing on the new terminal. Unfortunately, the button is changing color almost immediately.
from Tkinter import *
import os, subprocess
root = Tk()
buttonsD = {}
def launch(ourfile):
p=subprocess.Popen(["gnome-terminal","-e","python " + ourfile], shell=False)
buttonsD[ourfile].configure(bg='red')
dirlist=os.listdir(os.getcwd())
for fname in dirlist:
if fname.endswith('py') and fname != 'gui2.py':
buttonsD[fname] = Button(root,text=fname,command=lambda i=fname: launch(i))
buttonsD[fname].pack(side=TOP,expand=YES,fill=BOTH)
root.mainloop()
Almost immediately means that I can wait while p.poll == None, and see that it takes a moment for gnome-terminal to be created. But as soon as the terminal is created the button goes red, even though a process is still running in the new terminal. I can't create a new gnome-terminal and then communicate the process I'd like to run either. It seems gnome-terminal just creates a new instance of bash and then returns done, so there's a pipe error if I try to communicate to its stdin.
I believe gnome terminal is doing a double fork, in order to detach itself from the process group of its parent -- so what's actually your subprocess terminates almost immediately, as you observe, and everything is happening in a further descendant that you have no direct access to.
Unfortunately I don't believe gnome terminal offers any way to disable this double fork behavior; so, to find out when the "further descendant" is finished, you'll have to identify that process and monitor it periodically. Interacting directly with it is also a pretty tall order -- no easier than interacting with any "random" process you're not related to:-(.
There are two questions here: what command line to use to launch a Python program in gnome-terminal, and how to use subprocess in a Tkinter app. I only know about the latter.
subprocess.Popen returns immediately, which is why the button is turning red immediately. I think you probably need to make a list of which programs are running. Then write a function poll_processes which calls poll() on each running process, and when the result is not None, removes it from the list and turns the button red.
Then all you have to do is arrange for Tkinter to periodically call that function, which you can do by calling frame.after(msec, poll_processes) to schedule the first call to poll_processes and then having poll_processes do the same thing to schedule the next call.
Related
I have a problem with how to show a program window if it was opened
how I open it is using
import os
os.startfile('path/to/progarm.exe')
But if progarm.exe is opened and I forgot to close
when I run that script again, A program.exe doesn't show on the
screen when I was on another window.
So which script can show up the opened program?
The main reason is that your script lost the focus the opened programs windows.
You can control windows with parameters.
os.startfile(path[, operation][, arguments][, cwd][, show_cmd])
also, you can check detail information here.
https://docs.python.org/3/library/os.html#os.startfile
To do what you want, you need to provide the path to the program in your machine.
import os
ms_word = r"C:\Program Files\Microsoft Office\root\Office16\WINWORD.EXE"
os.startfile(ms_word)
I would not recommend doing what you are doing the way you are doing it.
Python has a subprocess module to do things like these, with functions specifically designed to do what you are doing. The easiest way to do what you are trying to do is to use the subprocess.runfunction.
import subprocess
subprocess.run(['path/to/app.exe', 'param1', 'param2'..], shell=True, check=True)
# params are optional.
However, subprocess.run is blocking, i.e., the script will not exit unless you close your launched application.
You can in that case use the subprocess.Popen class. This invokes the process and allows you to communicate with it asynchronously. However, if your objective is only to launch an app and shut down your script, then just call it as you made a call to run. The links I have provided has some examples. there are platform level considerations to make in the case of the parent-child process relationships, e.g. keep child running if the parent dies, kill the child with the parent, keep both of them running independently and allow them to die separately. probably this answer and this answer would provide you with some hints.
However, if you just want to launch an application and nothing else, just use the system shell, no?
OS = windows 7
I have a python program (works) that is listening to activity on the usb bus. I want to perform a lot of tests that require a particular user input at a particular time. I would like to pop up a window that says, "press button xxx". The key point is that the mainloop needs to continue running because it's looking for events. I don't care about the window or if it remains or not and I don't need to capture any information from the window. I just want a message to the user to press the correct button at the correct time. Any type of signaling would work; it doesn't have to be a gui window. It doesn't have to look pretty. Appreciate any suggestions or links to something like this. thx
It sounds like the operation of the Python script you're running does not depend upon the user input you request. To run another process without interrupting the Python script execution you can use:
import subprocess
subprocess.Popen([exe,arg1,arg2,arg3])
where
exe = executable/script to run from your OS command line
arg1= first argument to pass to exe
arg2= second argument to pass to exe
etc... (as many arguments as your OS supports in a list)
This separate exe process could request input from the user.
I'd like for my first program to automatically switch over to a second program when a button is pressed.
I tried break and sys.exit() but that put me back at the command line, which I don't want to I'm trying to eliminate having to use the mouse and keyboard to change to a different program.
The programs are for a microcomputer that will be doing the same tasks many times a day; I don't want to have a keyboard or mouse connected after the installation.
You can use the subprocess module for this. Create a Popen object for the script you want triggered, and then exit the parent process without waiting for the child to complete:
import subprocess
import sys
def on_button_pressed():
subprocess.Popen(["path/to/script", "arg1", "arg2"], shell=False)
sys.exit()
You may encapsulate the second program in a function, and call the function from your first program. Alternatively, if the second program resides in its own module, you may import that module. Importing the module will execute the code inside of it.
Currently, I'm running this on Windows:
args = ['start', windowname, 'python', '-i', myscript]
subprocess.Popen(args, shell=True)
As you can see, I launch a subprocess running myscript in python's interactive mode. In my case, this means that once the script exits, regardless if it errors out or successfully completes, the window/shell stays open. However, I'd like it so that when myscript errors out, the window will stay open, and when myscript runs successfully, the window will close.
The reason I'm doing this is because I want to see the errors and the output leading up to the errors -- I'd prefer to not use some form of logging because it's easier for me to visually see the windows and outputs.
I don't think I can check the returncode because the process I'm interacting with is start/cmd, rather than python. Please correct me if I'm wrong!
Thanks!
I think the easiest way would be to wrap the whole thing in a small batch script that checks for python's return code (presumably set by you by calling sys.exit() with an appropriate return code).
This stackoverflow question covers how to get the return code (apparently it's in %errorlevel%), and you can keep the cmd window open by executing pause in a batch script.
I made a simple PyGTK - Glade GUI for an application. I made the button, and the on_button_click calls a bash script.
I would like to show a popup window while the bash script is running, and hide it after it is done. I made the window called runningWindow in Glade and wrote the following code:
def on_button1_clicked(self,widget):
self.glade.get_object("runningWindow").show()
os.system('bash run.sh')
self.glade.get_object("runningWindow").hide()
This code shows nothing while run.sh is running. If I remove the hide() line, the window is displayed correctly, but only AFTER the run.sh process has finished.
The init function that starts the GUI:
def __init__(self):
self.gladefile = "MyFile.glade"
self.glade = gtk.Builder()
self.glade.add_from_file(self.gladefile)
self.glade.connect_signals(self)
self.glade.get_object("MainWindow").show_all()
How can I display the window before the os.system is called?
Thank you for your help!
You probably want to look into the subprocess module, and run your subprocess in a background task.
I couldn't solve the problem, but I could get around it. Maybe others find it useful:
First, one should use subprocess.Popen() instead of subprocess.call(). It puts the subprocess automatically in background, so the running window is displayed. The problem was that if I didn't remove the .hide() command, the window immediately disappeared after popping up. Otherwise, I couldn't hide the window when the run was finished.
To solve this, I created the runningWindow in a new (runningWindow.glade and runningWindow.py) file.
Then I created a wrapper bash script that says:
python runningWindow.py &
pid=$!
bash run.sh
kill pid
And called this bash script with subprocess.Popen() from the main python script.