How to unhide and show a process created with subprocess.popen()? - python

I am trying to create a simple command-line process and show it to the user (I do NOT want the process to be hidden):
import subprocess
import win32con
kwargs = {}
info = subprocess.STARTUPINFO()
info.dwFlags |= subprocess.STARTF_USESHOWWINDOW
info.wShowWindow = win32con.SW_SHOWMAXIMIZED
ExecuteString = ('process.cmd')
kwargs['startupinfo'] = info
sp = subprocess.Popen(ExecuteString, **kwargs)
It works with e.g. notepad.exe but not with the simple process.cmd:
echo "This is a process run from python"
pause
I run out of ideas, how to achieve this. I find all kind of stuff, how to HIDE a process. But I want to achieve the opposite.
Any idea?
Thanks!

You seem to be confusing the notions of process and window. All windows are associated to a process, but a certain process may not be associated with any window.
Your simple batch script is interpreted from the cmd.exe process. If you're used to the behaviour of windows when you open batch scripts with a double-click, might believe cmd.exe is always associated with a window, but that is not true. You can check this yourself by simply running cmd.exe inside a existing command prompt - it doesn't open a new window (as running notepad.exe, for example, would).
In python, processes run "as if" they were run from a command prompt - which is why you don't get another window.
This doesn't actually answer the question, but it might be useful in understanding the problem.

For Windowed applications, you simply need to use the SW_HIDE constant instead of SW_SHOWMAXIMIZED.
If you also want to cover console applications that start up a terminal window, I'm guessing that you would want to run something like this:
start the process;
use EnumWindows();
in your EnumWindowsProc implementation, check for a top-level window (using GetParent()) that is owned by the process you just launched (use GetWindowThreadProcessId()).
This will allow you to find the top-level window(s) of the process you just launched. From there, you can call ShowWindow() to show/hide the Window.
Note that this may be subject to some timing issues (if your search runs before the child process can create its window, the search will yield no results) as well as flicker (because you'll hide the window after it displays).

Related

Python & subprocess - Open terminal session as user and execute one/two commands

As much as I hate regurgitating questions, it's a necessary evil to achieve a result to the next issue I'll present.
Using python3, tkinter and the subprocess package, my goal is to write a control panel to start and stop different terminal windows with a specific set of commands to run applications/sessions of the ROS application stack, including the core.
As such, the code would look like this per executable I wish to control:
class TestProc(object):
def __init__(self):
pass
def start(self):
self.process = subprocess.Popen(["gnome-terminal", "-c", "'cd /path/to/executable/script.sh; ./script.sh'"])
print("Process started.")
def stop(self):
self.process.terminate()
print("Process terminated.")
Currently, it is possible to start a terminal window and the assigned commands/processes, yet two issues persist:
gnome-terminal is set to launch a terminal window, then relieve control to the processes inside; as such, I have no further control once it has started. A possible solution for this is to use xterm yet that poses a slew of other issues. I am required to have variables from the user's .bashrc and/or export
Certain "global commands" eg. cd or roslaunch would be unavailable to the terminal sessions, perhaps due to the order of execution (eg. the commands are run before the bash profile is loaded) preventing any usable terminal at all
Thus, the question rings: How would I be able to start and stop a new terminal window that would run up to two commands/processes in the user environment?
There are a couple approaches you can take, the most flexible here is also the most complicated, so you'd want to consider whether you need to do it.
If you only need to show the output of the script, you can simply pipe the output to a file or to a named pipe. You can then capture that output by reading/tailing the file. This is simplest, as long as the script don't actually need to have any user interaction.
If you really only need to spawn a script that runs in the background, and you need to simulate user interaction but you don't actually need to accept actual user input, you can use expect approach (using the pexpect library).
If you need to actually allow the real user to interact with the program, then you have two approaches. First is that you can embed the VTE widget into your application, this is the most seamless integration as it'll make the terminal look seamless with your application, however it's also the most heavy.
Another approach is to start gnome-terminal as you've done here, this necessarily spawns a new window.
If you need to both script some interaction while also allowing some user input, you can do this by spawning your script in a tmux session. Using tmux send-keys command to automate the moon interactive part, and then spawn a terminal emulator for users to interact with tmux attach. If you need to go back and forth between automated part and interactive part, you can combine this approach with expect.

How to catch a process in python?

I'm currently making a lil' launcher for PortableMu while in an internship.
We (company and I) modeled a special mode for the Mu-Editor and we are shipping it with PortableMu so that users don't need to install Mu and/or Python to use it.
The problem of PortableMu for Windows is, that you start it with a .bat and this doesnt give you any feedback.
You click, you wait ~1-2min and maybe Mu-Editor will popup.
This is not very userfriendly.
So my duty is to create a launcher.
My launcher is a simple thing: Only lil "welcome" a picture and a button to start PortableMu. It works on my private windows10.
Now I want to add in randomly picked messages for simulating "loading" which shall stop when the Mu-Editor pops up. Simply to bridge the time
Is there a method to catch when this happens?
Alas:
Can Python catch the moment when Windows opens the task/process for Mu-Editor?
If, how?
use the tasklist
subprocess.Popen('tasklist').comunicate()[0] will return all the tasks currently happening in windows, simply do this every minute or so and check for your task. There are ways to make this pass without a command window popping up, here's one that i use often
command =subprocess.Popen(["ping","-n","1","-w","100", str(ip)], stdout=subprocess.PIPE, shell=False, creationflags = 0x08000000)
reply = str(command.communicate()[0])

Python restart script in another cmd's window

is there a way to restart another script in another shell?
i have script that sometimes stuck waiting to read email from gmail and imap. from another script i would like to restart the main one but without stopping the execution of the second
i have tried:
os.system("C:\Users\light\Documents\Python\BOTBOL\Gmail\V1\send.py")
process = subprocess.Popen(["python", "C:\Users\light\Documents\Python\BOTBOL\Gmail\V1\send.py"])
but both run the main in the second's shell
EDIT:
sorry, for shell i mean terminal window
After your last comment and as the syntax show that you are using Windows, I assume that you want to launch a Python script in another console. The magic word here is START if you want that the launching execute in parallel with the new one, or START /W if you want to wait for the end of the subprocess.
In your case, you could use:
subprocess.call(["cmd.exe", "/c", "START", "C:\Path\To\PYTHON.EXE",
"C:\Users\light\Documents\Python\BOTBOL\Gmail\V1\send.py"])
Subprocess has an option called shell which is what you want. Os calls are blocking which means that only after the command is completed will the interpreter move to the next line. On the other hand subprocess popens are non blocking, however both these commands will spawn off child process from the process running this code. If you want to run in shell and get access shell features to execute this , try the shell = True in subprocess.
I could try and explain everything you need but I think this video will do it better: Youtube Video about multithreading
This will allow you to run 2 things f.e.
Have 1 run on checkin email and the other one on inputs so it wont stop at those moments and making multiple 'shelves' possible, as they are parallel.
If you really want to have a different window for this, i am sorry and I can not help.
Hope this was were you were looking for.

Start another program and leave it running when the script ends

I'm using subprocess.Popen to launch an external program with arguments, but when I've opened it the script is hanging, waiting for the program to finish and if I close the script the program immediately quits.
I thought I was just using a similar process before without issue, so I'm unsure if I've actually done it wrong or I'm misremembering what Popen can do. This is how I'm calling my command:
subprocess.Popen(["rv", rvFile, '-nc'])
raw_input("= Opened file")
The raw_input part is only there so the user has a chance to see the message and know that the file should be opened now. But what I end up getting is all the information that the process itself is spitting back out as if it were called in the command line directly. My understanding was that Popen made it an independent child process that would allow me to close the script and leave the other process open.
The linked duplicate question does have a useful answer for my purposes, though it's still not working as I want it.
This is the answer. And this is how I changed my code:
DETACHED_PROCESS = 0x00000008
pid = subprocess.Popen(["rv", rvFile, '-nc'], creationflags=DETACHED_PROCESS).pid
raw_input("= Opened file")
It works from IDLE but not when I run the py file through the command prompt style interface. It's still tied to that window, printing the output and quitting the program as soon as I've run the script.
The stackoverflow question Calling an external command in python has a lot of useful answers which are related.
Take a look at os.spawnl, it can take a number of mode flags which include NOWAIT, WAIT.
import os
os.spawnl(os.P_NOWAIT, 'some command')
The NOWAIT option will return the process ID of the spawned task.
Sorry for such a short answer but I have not earned enough points to leave comments yet. Anyhow, put the raw_input("= Opened file") inside the file you are actually opening, rather than the program you are opening it from.
If the file you are opening is not a python file, then it will close upon finishing,regardless of what you declare from within python. If that is the case you could always try detaching it from it's parent using:
from subprocess import Popen, CREATE_NEW_PROCESS_GROUP
subprocess.Popen(["rv", rvFile, '-nc'], close_fds = True | CREATE_NEW_PROCESS_GROUP)
This is specifically for running the python script as a commandline process, but I eventually got this working by combining two answers that people suggested.
Using the combination of DETACHED_PROCESS suggested in this answer worked for running it through IDLE, but the commandline interface. But using shell=True (as ajsp suggested) and the DETACHED_PROCESS parameter it allows me to close the python script window and leave the other program still running.
DETACHED_PROCESS = 0x00000008
pid = subprocess.Popen(["rv", rvFile, '-nc'], creationflags=DETACHED_PROCESS, shell=True).pid
raw_input("= Opened file")

Tkinter embed Graphical subprocess

I am trying to execute a compiled Pygame Graphical application from a Tkinter interface. However, I want the pygame interface to launch into a 800x600 Graphical frame. In the same root window below the 800x600 frame that is for the pygame application, I am looking for a way to embed either:
a xterm that launches a python executable automatically located in a subdirectory called "lib/" OR
directly call the executable located in the named "lib/" folder.
The only helpful documentation i have found on the subject is here:
http://poultryandprogramming.wordpress.com/2012/10/28/embedded-terminal-in-pythontk-window/
The os.system route is the only method I see any form of documentation for.
Is it possible to do what I want with subprocess.Popen?
This seems to be two separate questions.
First, to directly run an executable in Python, all you need is the subprocess module. I don't know how you missed seeing this when you say you've seen the documentation for os.system, because that clearly says:
The subprocess module provides more powerful facilities for spawning new processes and retrieving their results; using that module is preferable to using this function. See the Replacing Older Functions with the subprocess Module section in the subprocess documentation for some helpful recipes.
In particular, to run some executable in the "lib/" folder, just do this:
p = subprocess.Popen(['lib/the_executable'])
However, I'm guessing you really want not lib from the current working directory, but lib from the directory the main script resides in, right? For that, you'll want to do something like this at script startup:
scriptdir = os.path.abspath(os.path.dirname(__file__))
… and then something like this when you launch the child:
path = os.path.join(scriptdir, 'lib/the_executable')
p = subprocess.Popen([path])
At any rate, once you have a Popen object, you can check whether p is still running, etc., by calling poll every so often, or by spawning a thread to block on wait; you can kill it by calling kill on it; etc. The docs on Popen objects show all the things you can do.
If you'd prefer to run an xterm executable that launches the Python executable, you can do that—just pass xterm as the first argument, and the relevant xterm arguments as the remaining arguments.
But I can't see what good that would do you. You're not trying to embed a terminal session in your window, but the game itself. When you launch a GUI app from an xterm session, it doesn't run inside the xterm window; it runs in a new window. The same thing will happen if the xterm window is embedded.
As for embedding the Pygame window in your own window, there are two ways to do that.
If you're writing the Pygame game yourself, as the display.init docs say:
On some platforms it is possible to embed the pygame display into an already existing window. To do this, the environment variable SDL_WINDOWID must be set to a string containing the window id or handle. The environment variable is checked when the pygame display is initialized. Be aware that there can be many strange side effects when running in an embedded display.
Notice that the Popen constructor takes an env argument:
If env is not None, it must be a mapping that defines the environment variables for the new process; these are used instead of inheriting the current process’ environment, which is the default behavior.
So, you can do this:
child_env = dict(os.environ)
child_env['SDL_WINDOWID'] = the_window_id
p = subprocess.Popen([path], env=child_env)
The problem is, what window ID do you give it? Well, the blog you posted to already has the answer. The same ID that you'd give xterm's -into.
The blog doesn't explain how to restrict it to part of your window, but the code it refers to must. The answer is to either (a) embed a child window in the main window, and give the child window's ID to the child process, or (b) give the entire window to the child process, then have the child immediately create a sub-surface and only draw to that instead of to the whole display.
However, for the particular case of Pygame (or other SDL) apps, as opposed to xterm, setting SDL_VIDEO_WINDOW_POS in the environment should also work. (As far as I can tell, this isn't a documented feature of Pygame, but it is a documented feature of SDL, so it ought to be reliable.)
Ultimately, you'll probably need a bit of cooperation between the two apps. Spmething like this:
Tkinter parent:
child_env = dict(os.environ)
child_env['SDL_WINDOWID'] = the_window_id
child_env['SDL_VIDEO_WINDOW_POS'] = '{},{}'.format(left, top)
p = subprocess.Popen([path, width, height], env=child_env)
Pygame child:
width, height = sys.argv[1:3]
pygame.display.init()
pygame.display.set_mode((width, height), pygame.NOFRAME)
If you can't modify the Pygame app, things will be trickier. You will have to create two separate windows embedded into a parent window, or two windows top-level docked together in some way. (The latter isn't as scary as it sounds in X11. Whenever one window moves, programmatically move the other one.) Either way, you then launch the Pygame app embedded in one child window, and cram the Tkinter stuff into the other. You may be able to do that all through Tkinter; you may have to make Xlib calls directly (either by ctypes-ing to Xlib, or by using something like python-xlib).

Categories