pywinauto possible to keep application alive if I exit python script? - python-3.x

app = Application(backend="uia").start("program.exe")
I am using pywinauto to do some tasks indefinitely. However, occasionally, I need to restart the script for some external reasons. When this happens, I would like to keep the created applications open. How can I do this? I noticed if the python script errors out, the applications will stay open. But if I exit the script manually, the windows will close. So there must be some way to accomplish this.

I think pywinauto's Application.start cannot do the work. You can try:
pid = os.spawnl(os.P_NOWAIT, "program.exe")
app = Application().connect(process=pid)

os.spawnl is considered deprecated. Use subprocess module.
Combining the answer in Run a program from python, and have it continue to run after the script is killed and pywinauto official doc, you can do this:
subprocess.Popen(
['your_program', 'with args'],
# These will make sure the desktop program will alive even when shell session terminates.
creationflags=subprocess.DETACHED_PROCESS | subprocess.CREATE_NEW_PROCESS_GROUP, shell=True
)
desktop = Desktop(backend="uia")
main_win = desktop.window(title="program's window title", control_type="Window")

Why not use system command:
#Use multiple thread to avoid block of system function
import _thread as qd
import os
qd.start_new_thread(os.system,('notepad',))
from pywinauto import Application
#connect pywinauto with application via title regular expression
win=Application(backend='uia').connect(title_re='.*Notepad.*')
Then you use pywinauto to connect application via title?

Related

Python multiprocessing manager showing error when used in flask API

I am pretty confused about the best way to do what I am trying to do.
What do I want?
API call to the flask application
Flask route starts 4-5 multiprocess using Process module and combine results(on a sliced pandas dataframe) using a shared Managers().list()
Return computed results back to the client.
My implementation:
pos_iter_list = get_chunking_iter_list(len(position_records), 10000)
manager = Manager()
data_dict = manager.list()
processes = []
for i in range(len(pos_iter_list) - 1):
temp_list = data_dict[pos_iter_list[i]:pos_iter_list[i + 1]]
p = Process(
target=transpose_dataset,
args=(temp_list, name_space, align_namespace, measure_master_id, df_searchable, products,
channels, all_cols, potential_col, adoption_col, final_segment, col_map, product_segments,
data_dict)
)
p.start()
processes.append(p)
for p in processes:
p.join()
My directory structure:
- main.py(flask entry point)
- helper.py(contains function where above code is executed & calls transpose_dataset function)
Error that i am getting while running the same?
RuntimeError: No root path can be found for the provided module "mp_main". This can happen because the module came from an import hook that does not provide file name information or because it's a namespace package. In this case the root path needs to be explicitly provided.
Not sure what went wong here, manager list works fine when called from a sample.py file using if __name__ == '__main__':
Update: The same piece of code is working fine on my MacBook and not on windows os.
A sample flask API call:
#app.route(PREFIX + "ping", methods=['GET'])
def ping():
man = mp.Manager()
data = man.list()
processes = []
for i in range(0,5):
pr = mp.Process(target=test_func, args=(data, i))
pr.start()
processes.append(pr)
for pr in processes:
pr.join()
return json.dumps(list(data))
Stack has an ongoing bug preventing me from commenting, so I'll just write up an answer..
Python has 2 (main) ways to start a new process: "spawn", and "fork". Fork is a system command only available in *nix (read: linux or macos), and therefore spawn is the only option in windows. After 3.8 spawn will be the default on MacOS, but fork is still available. The big difference is that fork basically makes a copy of the existing process while spawn starts a whole new process (like just opening a new cmd window). There's a lot of nuance to why and how, but in order to be able to run the function you want the child process to run using spawn, the child has to import the main file. Importing a file is tantamount to just executing that file and then typically binding its namespace to a variable: import flask will run the flask/__ini__.py file, and bind its global namespace to the variable flask. There's often code however that is only used by the main process, and doesn't need to be imported / executed in the child process. In some cases running that code again actually breaks things, so instead you need to prevent it from running outside of the main process. This is taken into account in that the "magic" variable __name__ is only equal to "__main__" in the main file (and not in child processes or when importing modules).
In your specific case, you're creating a new app = Flask(__name__), which does some amount of validation and checks before you ever run the server. It's one of these setup/validation steps that it's tripping over when run from the child process. Fixing it by not letting it run at all is imao the cleaner solution, but you can also fix it by giving it a value that it won't trip over, then just never start that secondary server (again by protecting it with if __name__ == "__main__":)

PYTHON rumps program stops responding after subprocess calls another script

I am currently working on a project that contains many system tray utilities on mac osx, one of these utilities is a 'fake friend soundboard' where I am to enable the user to click on the module then select as example "Discord Ping x1" and the program playback a soundfile of the ping.
Im using RUMPS which enables me to add menus to the system tray however when trying to use libraries to play sounds directly through upon the #rumps.clicked, nothing seems to happen
Instead I've programmed it to run another script that can easily play the sound inside another folder containing all the sounds.
When running the script and clicking a sound, it plays the sound exactly as intended,
but then the program stops responding...
Is this something I'm doing wrong with my code or is there another way i could play the sound using this library?
Heres the code:
import rumps
import subprocess
class sound(rumps.App):
def __init__(self):
super(sound, self).__init__("🔊")
self.menu = ["Fake friend soundboard",
None,
"Discord Sounds:",
"Ping x1",
"Incoming Call",
"Enter Call",
"Leave Call",
"Mute",
"Deafen",
None,
"Skype Sounds:",
"Incoming Call",
None,
"Random:",
"okbuddyretard",
None]
#rumps.clicked("Ping x1")
def about(sender):
subprocess.run("python3 soundboard/discord_pingx1.py", shell=True)
if __name__ == "__main__":
sound().run()
In the soundboard folder there is the sounds folder containing the discord folder containing **
discord_ping.wav
The soundboard folder also hold the discordd_pingx1.py script which is below.
import pyglet
def sound():
sound = pyglet.resource.media('sounds/discord/discord_ping.wav', streaming=False)
sound.play()
pyglet.app.run()
sound()
after running my main.py (opens all the modules)
and clicking the sound tab and then clicking the button set to make the sound,
it plays, but then stops responding.
Any help at all would be much appreciated.
I highly recommend avoiding the insert of subprocess or sys-calls inside of a Python script that executes Objective-C code. This leads to Aborts and Traps. Instead, refer to Apple's APIs and utilize AppleScript (osascript).
Here is a better implementation that calls a script to call your script from osascript:
exec.py
import platform
import subprocess, sys, os
applescript = '''\
do shell script "bash /path/to/file/myShellScript.sh"\
'''
# parse and stdout
args = [item for x in [("-e",l.strip()) for l in applescript.split('\n') if l.strip() != ''] for item in x]
proc = subprocess.Popen(["osascript"] + args ,stdout=subprocess.PIPE )
progname = proc.stdout.read().strip()
sys.stdout.write(str(progname))
The parser takes the string with the osascript in it and parses it for a stdout write. Stdout writes are very safe, considering they are handling data from the same thread.
/path/to/file/myShellScript.sh (add the shebang on line 1 as well)
#!/bin/bash
python3 soundboard/discord_pingx1.py
This 100% handles your problem without causing subprocess errors. If you get hung with subprocess, your computer will continue running Python3. If the Application doesn't stop freezing, exit the app by cmd+Space and typing Activity Monitor. Then, find Python3 by cmd+f and typing python3. Click it and press Quit in the top left corner (the X or stop-light symbol).

Running console window in background for GUI using tkinter on Windows 10

So I have this GUI that I made with tkinter and everything works well. What it does is connects to servers and sends commands for both Linux or Windows. I went ahead and used pyinstaller to create a windowed GUI without console and when I try to uses a specific function for sending Windows commands it will fail. If I create the GUI with a console that pops up before the GUI, it works like a charm. What I'm trying to figure out is how to get my GUI to work with the console being invisible to the user.
The part of my code that has the issue revolves around subprocess. To spare you all from the 400+ lines of code I wrote, I'm providing the specific code that has issues. Here is the snippet:
def rcmd_in(server):
import subprocess as sp
for i in command_list:
result = sp.run(['C:/"Path to executable"/rcmd.exe', '\\\\' + server, i],
universal_newlines=True, stdout=sp.PIPE, stderr=sp.STDOUT)
print(result.stdout)
The argument 'server' is passed from another function that calls to 'rcmd_in' and 'command_list' is a mutable list created in the root of the code, accessible for all functions.
Now, I have done my due diligence. I scoured multiple searches and came up with an edit to my code that makes an attempt to run my code with that console invisible, found using info from this link: recipe-subprocess. Here is what the edit looks like:
def rcmd_in(server):
import subprocess as sp
import os, os.path
si = sp.STARTUPINFO()
si.dwFlags |= sp.STARTF_USESHOWWINDOW
for i in command_list:
result = sp.run(['C:/"Path to executable"/rcmd.exe', '\\\\' + server, i],
universal_newlines=True, stdin=sp.PIPE, stdout=sp.PIPE,
stderr=sp.STDOUT, startupinfo=si, env=os.environ)
print(result.stdout)
The the problem I have now is when it runs an error of "Error:8 - Internal error -109" pops up. Let me add I tried using functions 'call()', 'Popen()', and others but only 'run()' seems to work.
I've reached a point where my brain hurts and I can use some help. Any suggestions? As always I am forever great full for anyone's help. Thanks in advance!
I figured it out and it only took me 5 days! :D
Looks like the reason the function would fail falls on how Windows handles stdin. I found a post that helped me edit my code to work with pyinstaller -w (--noconsole). Here is the updated code:
def rcmd_in(server):
import subprocess as sp
si = sp.STARTUPINFO()
si.dwFlags |= sp.STARTF_USESHOWWINDOW
for i in command_list:
result = sp.Popen(['C:/"Path to executable"/rcmd.exe', '\\\\' + server, i],
universal_newlines=True, stdin=sp.PIPE, stdout=sp.PIPE,
stderr=sp.PIPE, startupinfo=si)
print(result.stdout.read())
Note the change of functions 'run()' to 'Popen()'. The 'run()' function will not work with the print statement at the end. Also, for those of you who are curious the 'si' variable I created is preventing 'subprocess' from opening a console when being ran while using a GUI. I hope this will become useful to someone struggling with this. Cheers

How to run PyQt5 GUIs in non-blocking threads?

I have a PyQt5 GUI class that I want to be able to create multiple instances of either from an interactive console or normal run. I need these GUIs to be non-blocking so that they can be used while subsequent code runs.
I've tried calling app.exec__() in separate threads for each GUI like this answer, but the program sometimes crashes as the comment on the answer warned it would:
Run pyQT GUI main app in seperate Thread
And now I'm trying to get the code below working which I made based on this answer:
Run Pyqt GUI main app as a separate, non-blocking process
But when I run it the windows pop and and immediately disappear
import sys
from PyQt5 import QtWidgets, QtGui, QtCore
import time
class MainWindow(QtWidgets.QWidget):
def __init__(self):
# call super class constructor
super(MainWindow, self).__init__()
# build the objects one by one
layout = QtWidgets.QVBoxLayout(self)
self.pb_load = QtWidgets.QPushButton('Load')
self.pb_clear= QtWidgets.QPushButton('Clear')
self.edit = QtWidgets.QTextEdit()
layout.addWidget(self.edit)
layout.addWidget(self.pb_load)
layout.addWidget(self.pb_clear)
# connect the callbacks to the push-buttons
self.pb_load.clicked.connect(self.callback_pb_load)
self.pb_clear.clicked.connect(self.callback_pb_clear)
def callback_pb_load(self):
self.edit.append('hello world')
def callback_pb_clear(self):
self.edit.clear()
def show():
app = QtWidgets.QApplication.instance()
if not app:
app = QtWidgets.QApplication(sys.argv)
win = MainWindow()
win.show()
if __name__ == '__main__':
show()
show()
EDIT - I don't see how this question is a duplicate. The 'duplicate' questions are only slightly related and don't provide solutions to my problem at all.
I want to be able to create multiple instances of a GUI (MainWindow in my example) by calling the show() function from either an interactive session or script, and I want those windows to stay on my screen while subsequent code is running.
EDIT2 - When I run the code as a script I can do what I want by using multiprocessing, see this demo:
https://www.screencast.com/t/5WvJNVSLm9OR
However I still need help because I want it to also work in interactive Python console sessions, and multiprocessing does not work in that case.
It isn't necessary to use separate threads or processes for this. You just need a way to maintain a reference to each new window when importing the script in a python interactive session. A simple list can be used for this. It is only necessary to explictly start an event-loop when running the script from the command-line; in an interactive session, it will be handled automatically by PyQt.
Here is an implementation of this approach:
...
_cache = []
def show(title=''):
if QtWidgets.QApplication.instance() is None:
_cache.append(QtWidgets.QApplication(sys.argv))
win = MainWindow()
win.setWindowTitle(title)
win.setAttribute(QtCore.Qt.WA_DeleteOnClose)
win.destroyed.connect(lambda: _cache.remove(win))
_cache.append(win)
win.show()
if __name__ == '__main__':
show('Foo')
show('Bar')
sys.exit(QtWidgets.QApplication.instance().exec_())
This is a minor addendum to #ekhumoro's answer. I don't have enough reputation to only add a comment so I had to write this as an answer.
#ekhumoro's answer almost fully answers #Esostack's question, but doesn't work in the Ipython console. After many hours of searching for the answer to this question myself, I came across a comment from #titusjan in a three year old thread (here) also responding to a good answer from #ekhumoro.
The missing part to #ekhumoro's answer which results in the gui windows freezing for Ipython specifically is that Ipython should be set to use the qt gui at launch or once running.
To make this work with Ipython:
Launch Ipython with ipython --gui=qt5
In a running Ipython console run the magic command %gui qt5
To fix it from a Python script you can run this function
def fix_ipython():
from IPython import get_ipython
ipython = get_ipython()
if ipython is not None:
ipython.magic("gui qt5")

Python Selenium : How to hide geckodriver?

I am writing an program for a web automation in python. Is here a ways to hide the geckodriver? So that the console (see picture) won't show up when I start the program.
console of geckodriver
here is a fraction of my code:
from selenium import webdriver
from selenium import *
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC`
driver=webdriver.Firefox()
wait=WebDriverWait(driver,120)
url = r"http://google.com"
driver.get(url) #This line starts the console (see picture)
To prevent geckodriver from displaying any windows, you need to pass -headless argument:
from selenium import webdriver
options = webdriver.FirefoxOptions()
options.add_argument('-headless')
driver = webdriver.Firefox(options=options)
driver.get('https://www.example.com')
...
driver.quit()
This worked for me in C#. It blocks both geckodriver and firefox window
FirefoxOptions f = new FirefoxOptions();
f.AddArgument("-headless");
var ffds = FirefoxDriverService.CreateDefaultService();
ffds.HideCommandPromptWindow = true;
driver = new FirefoxDriver(ffds,f);
I was able to do that after implementing PyVirtualDisplay
sudo pip install pyvirtualdisplay # Install it into your Virtual Environment
Then just import Display as follows:
from pyvirtualdisplay import Display
Then, before fetching, start the virtual display as follows:
# initiate virtual display with 'visible=0' activated
# this way you will hide the browser
display = Display(visible=0, size=(800, 600))
# Start Display
display.start()
...
# Do your fetching/scrapping
...
# Stop Display
display.stop()
I hope it helps
Here the approach which has solved it in my case. I used #thekingofravens suggestion but realized that it is just enough to create the Run.bat file which he mentions in his post. Previously I ran my programms in IDLE with F5. Because of this the Geckodriver popped up every few seconds.
My Solution: I just made the Run.bat file with the same code:
cd C:\PathToYourFileWhichIsCausingTheGeckodriverToPopUp
python FileWhichIsCausingTheGeckodriverToPopUp.py
And thats it. Just start this file when you want to run your code and the Geckodriver won't pop up. (That it works without the whole path to you program, Python has to be in PATH.)
Also, of course you can run your program from the command line with the same commands like above and without creating an extra bat file.
You have to take care of a couple of stuffs here:
Keep the useful imports. Unusual imports like from selenium import * (in your code) must be avoided.
Keep your code minimal. wait=WebDriverWait(driver,120) have no usage in your code block.
When you use the raw r switch remember to use single quotes '...'
If you initialize the webdriver instance remember to call the quit() method.
Be careful about indentation while using Python.
Here is the minimal code which uses geckodriver to open the url http://www.google.com, print the Page Title and quits the driver instance.
from selenium import webdriver
driver=webdriver.Firefox(executable_path=r'C:\Utility\BrowserDrivers\geckodriver.exe')
url = r'http://www.google.com'
driver.get(url)
print("Page Title is : %s" %driver.title)
driver.quit()
Console Output:
Page Title is : Google
So I found a very generic workaround to solve this on Windows (in Linux it doesn't seem to be an issue in the first place, can't speak for OSX).
So you need to make three files and its very awkward.
First, you make a file. call it start.bat (or anything) and put the following code in it:
wscript.exe "C:\Wherever\invisible.vbs" "C:\Some Other Place\Run.bat"
This will be the top level batch script. It will be visible for a split second while it launches a visual basic script and passes a batch script as an argument. The purpose of this is to make the next console invisible. Next we make the VBscript, invisible.vbs:
CreateObject("Wscript.Shell").Run """" & WScript.Arguments(0) & """", 0, False
Finally, we make the script that invisible.vbs is supposed to hide, we could call it Run.bat
cd C:\Wherever
python script_using_geckodriver.py
What happens is, as follows:
The first .bat file launches invisible.vbs
invisible.vbs launches the second .bat file without showing it on screen.
The second .bat file then launches the python program. Python (and geckodriver) output to the invisible cmd therefore hiding the geckodriver console window.
P.S. all of this works with PyInstaller to produce a single redistributable package the user can just click on.
Credit harrymc # superuser.com for this solution, which I found when trying to solve an unrelated problem. I tested and realized it was cross applicable to this.
https://superuser.com/questions/62525/run-a-batch-file-in-a-completely-hidden-way

Resources