PyObjc/AppKit - launch Mac app without stealing focus - python

I'm not sure if what I want to do is possible. I'm using PyObjc and the iTerm2 API to create a split and send some text. Works great, but I'd like it to not steal focus if the user is doing something else; i.e. don't put iTerm in the foreground if it's not there, already.
What I have, now (also my first ever Python program - be kind):
#!/usr/bin/env python3
'''Upgrade Homebew Casks in a new iTerm2 vertical split'''
import os
import iterm2
import osascript
import AppKit
def get_term_cookie():
'''
Launch iTerm and get a security token.
Get a token from iTerm and store in $ITERM_COOKIE env variable.
This is required to run iterm API's without a "Do you really trust this program?"
dialog box from being presented.
See https://gitlab.com/gnachman/iterm2/-/wikis/iTerm2-Version-3.3-Security-Updates
'''
iterm = AppKit.NSWorkspace.sharedWorkspace().launchApplication_("iTerm")
if not iterm:
print("Failed to launch iTerm2")
return False
ret_code,os.environ["ITERM2_COOKIE"],err = (
osascript.run('tell application "iTerm2" to request cookie'))
if os.environ["ITERM2_COOKIE"]:
return True
else:
print("Failed to get a cookie from iTerm")
print("ret_code: " + ret_code)
print ("err: " + err)
return False
async def upgrade_brew_casks(connection):
'''Connect to iTerm2 and upgrade brew casks.'''
# get a connection to iTerm app
app = await iterm2.async_get_app(connection)
# get the current session and make a new vertical split
active_session = app.current_terminal_window.current_tab.current_session
await active_session.async_split_pane(vertical=True)
# run the cask upgrade in the new split
new_session = app.current_terminal_window.current_tab.current_session
await new_session.async_send_text('brew cu -ay --no-brew-update\n')
# return focus to the original split
await active_session.async_activate()
return True
if __name__ == '__main__':
if get_term_cookie():
# Passing True for the second parameter means keep trying to
# connect until the app launches.
iterm2.run_until_complete(upgrade_brew_casks, True)

Related

How to fix ALDialog Python Script NAOqi Error

I am trying to use ALDialog module to have a virtual conversation with the Choregraphe simulated NAO6 robot. I have the below script:
import qi
import argparse
import sys
def main(session):
"""
This example uses ALDialog methods.
It's a short dialog session with two topics.
"""
# Getting the service ALDialog
ALDialog = session.service("ALDialog")
ALDialog.setLanguage("English")
# writing topics' qichat code as text strings (end-of-line characters are important!)
topic_content_1 = ('topic: ~example_topic_content()\n'
'language: enu\n'
'concept:(food) [fruits chicken beef eggs]\n'
'u: (I [want "would like"] {some} _~food) Sure! You must really like $1 .\n'
'u: (how are you today) Hello human, I am fine thank you and you?\n'
'u: (Good morning Nao did you sleep well) No damn! You forgot to switch me off!\n'
'u: ([e:FrontTactilTouched e:MiddleTactilTouched e:RearTactilTouched]) You touched my head!\n')
topic_content_2 = ('topic: ~dummy_topic()\n'
'language: enu\n'
'u:(test) [a b "c d" "e f g"]\n')
# Loading the topics directly as text strings
topic_name_1 = ALDialog.loadTopicContent(topic_content_1)
topic_name_2 = ALDialog.loadTopicContent(topic_content_2)
# Activating the loaded topics
ALDialog.activateTopic(topic_name_1)
ALDialog.activateTopic(topic_name_2)
# Starting the dialog engine - we need to type an arbitrary string as the identifier
# We subscribe only ONCE, regardless of the number of topics we have activated
ALDialog.subscribe('my_dialog_example')
try:
raw_input("\nSpeak to the robot using rules from both the activated topics. Press Enter when finished:")
finally:
# stopping the dialog engine
ALDialog.unsubscribe('my_dialog_example')
# Deactivating all topics
ALDialog.deactivateTopic(topic_name_1)
ALDialog.deactivateTopic(topic_name_2)
# now that the dialog engine is stopped and there are no more activated topics,
# we can unload all topics and free the associated memory
ALDialog.unloadTopic(topic_name_1)
ALDialog.unloadTopic(topic_name_2)
if __name__ == "__main__":
session = qi.Session()
try:
session.connect("tcp://desktop-6d4cqe5.local:9559")
except RuntimeError:
print ("\nCan't connect to Naoqi at IP desktop-6d4cqe5.local(port 9559).\nPlease check your script's arguments."
" Run with -h option for help.\n")
sys.exit(1)
main(session, "desktop-6d4cqe5.local")
My simulated robot has desktop-6d4cqe5.local as IP address and its NAOqi port is running on 63361. I want to run the dialogs outside of the Choregraphe in a python script and only be able to use the dialog box within the Choregraphe to test it. When I ran the above python file I got:
Traceback (most recent call last):
File "C:\Users\...\Documents\...\choregraphe_codes\Welcome\speak.py", line 6, in <module>
import qi
File "C:\Python27\Lib\site-packages\pynaoqi\lib\qi\__init__.py", line 93
async, PeriodicTask)
^
SyntaxError: invalid syntax
I couldn't figure out the problem as there was not much resources online and the robot's documentations are a bit hard to understand.
Please help, thank you.
You are running the script using a Python version greater than 3.5, that sees async as a keyword, now.
NAOqi only supports Python 2.
Try running your script with python2 explicitly.

How can I listen to Windows 10 notifications in Python?

My Python test script causes our product to raise Windows notifications ("Toasts"). How can my python script verify that the notifications are indeed raised?
I see it's possible to make a notification listener in C# using Windows.UI.Notifications.Management.UserNotificationListener (ref), And I see I can make my own notifications in Python using win10toast - but how do I listen to othe apps' notifications?
You can use pywinrt to access the bindings in python.
A basic example would look something like this:
from winrt.windows.ui.notifications.management import UserNotificationListener, UserNotificationListenerAccessStatus
from winrt.windows.ui.notifications import NotificationKinds, KnownNotificationBindings
if not ApiInformation.is_type_present("Windows.UI.Notifications.Management.UserNotificationListener"):
print("UserNotificationListener is not supported on this device.")
exit()
listener = UserNotificationListener.get_current()
accessStatus = await listener.request_access_async()
if accessStatus != UserNotificationListenerAccessStatus.ALLOWED:
print("Access to UserNotificationListener is not allowed.")
exit()
def handler(listener, event):
notification = listener.get_notification(event.user_notification_id)
# get some app info if available
if hasattr(notification, "app_info"):
print("App Name: ", notification.app_info.display_info.display_name)
listener.add_notification_changed(handler)
Searching python windows notification listener on google brings up only this ok-ish result but it is not complete.
Since i couldn't find any self contained example on how to do it, here is a fully working code:
from winrt.windows.ui.notifications.management import UserNotificationListener
from winrt.windows.ui.notifications import KnownNotificationBindings
def handler(asd, aasd):
unotification = asd.get_notification(aasd.user_notification_id)
# print(dir(unotification))
if hasattr(unotification, "app_info"):
print("App Name: ", unotification.app_info.display_info.display_name)
text_sequence = unotification.notification.visual.get_binding(KnownNotificationBindings.get_toast_generic()).get_text_elements()
it = iter(text_sequence)
print("Notification title: ", it.current.text)
while True:
next(it, None)
if it.has_current:
print(it.current.text)
else:
break
else:
pass
listener = UserNotificationListener.get_current()
listener.add_notification_changed(handler)
while True: pass
tested on windows 10 and winrt v1.0.21033.1

python3 running old version of script

I've changed a script by commenting out a section and adding some print statements above it for testing purposes, the problem is when I run the script from any other directory than the one it's in, python is running the old version of the script.
Clearing __pycache__ had no effect.
Here's the python script in question:
import discord
import watson
import configparser
import os
from discord.ext.commands import Bot
from getpass import getuser
print("WHY ISN'T THIS BEING CALLED!!")
#print(os.getcwd())
#os.chdir("/home/{}/discordBotStaging".format(getuser()))
#print(os.getcwd())
#if(not os.path.isfile("./config.ini")):
# print("No config file found, make sure you have one in the same directory as this python script\nexiting")
# quit()
config = configparser.ConfigParser()
config.read("./config.ini")
TOKEN = config["DEFAULT"]["DISCORD_KEY"]
BOT_PREFIX = ("!", "$")
client = Bot(command_prefix=BOT_PREFIX)
#client.command(name="memealyze",
description="When uploading image include command \"!memealyze\" | \"!Memealyze\" | \"!MemeAlyze\" | \"!ma\" as a comment",
brief="Neural network put to good use",
aliases=["Memealyze", "MemeAlyze", "ma"],
pass_context=True)
async def GetMemeContents(context):
await client.say("Sending image to the mothership, hold tight.")
if(not context.message.attachments):
await client.say(
"Couldn't find image attachement. Make sure you include \"!memealyze\" or any of it's variants as a comment when submitting an image")
return
imageUrl = str(context.message.attachments[0]["url"])
messageContent = ""
resultDict = watson.ReturnWatsonResults(imageUrl)
for key,val in resultDict.items():
messageContent += "{} : {}%\n".format(key, val)
await client.say("Done, the boys at IBM said they found this:\n" + messageContent)
client.run(TOKEN)
And here's the issue:
yugnut#RyzenBuild:~$ python3 discordBotStaging/main.py
No config file found, make sure you have one in the same directory as this python script
exiting
yugnut#RyzenBuild:~$
yugnut#RyzenBuild:~/discordBotStaging$ python3 main.py
WHY ISN'T THIS BEING CALLED!!
^Cyugnut#RyzenBuild:~/discordBotStaging$
EDITS:
#ShadowRanger suggestions:
Try moving the print above all the imports in your script.
This yields promising results, I do get output by trying this but right after that I still run into the same issue
You can't use relative paths like that if the config file is expected to be in the same directory as the script; you have to do config.read(os.path.join(os.path.dirname(__file__), 'config.ini'))
I think my ignorance is showing :^), I changed this in my script as well
After making these edits along with trying to run the script after commenting out my import configparser line I still get the same error.

ROS Error. "Error processing request: signal only works in main thread"

I am working with Robot Operating System (ROS) and am attempting to make a server/client where the server will boot up ROS nodes that are specified by the client. To perform the "boot up" I am using roslaunch based on the recommendations found here: http://wiki.ros.org/roslaunch/API%20Usage
I can run the roscore in a window and then I can run the server which boots up fine. However, as soon as I try to send the node names I want to boot up via the client, I get the following error:
"Error processing request: signal only works in main thread"
It then points to a bunch of files in various areas that I have not yet tracked down.
I have tried using a simple roslaunch call on each of the launch files I made individually for the nodes I want to launch (in this case turtlesim_node and turtle_teleop_key) and they boot up fine and work by just running roslaunch [package] turtlesim_launch.launch, etc.
Below is the code for my server:
#!/usr/bin/env python
#Filename: primary_server.py
import rospy
import roslaunch
from robot_man.srv import *
class StartUpServer(object):
def __init__(self):
self._nodes = []
def handle_startup(self, names):
self._temp = names.nodes.split(",") #This reades in the
# information from the srv file sent by the client and
# separates each node/package pair
#This loop separates the input from pairs of 2 corresponding
# to the package and node names the user inputs
for i in range(len(self._temp)):
self._nodes.append(self._temp[i].split())
#This loop will launch each node that the user has specified
for package, executable in self._nodes:
print('package is: {0}, executable is: {1}'.format(package, executable))
node = roslaunch.core.Node(package, executable)
launch = roslaunch.scriptapi.ROSLaunch()
launch.start()
process = launch.launch(node)
print('running node: %s'%executable)
return StartUpResponse(self._nodes) #I will change this later
if __name__ == '__main__':
rospy.init_node('startup_node')
server = StartUpServer()
rospy.Service('startup', StartUp, server.handle_startup)
print('The servers are up and running')
rospy.spin()
Here is the code for my client:
#!/usr/bin/env python
#Filename: primary_client_startup.py
import rospy
from robot_man.srv import *
def main(nodes):
rospy.wait_for_service('startup')
proxy = rospy.ServiceProxy('startup', StartUp)
response = proxy(nodes)
return response
if __name__ == '__main__':
nodes = input('Please enter the node packages followed by \
node names separated by spaces. \n To activate multiple nodes \
separate entries with a comma > ')
main(nodes)
Here is my srv file:
#This should be a string of space/comma separated values
string nodes
---
#This should return "success" if node/s start up successfully, else "fail"
string status
And finally, here are the two launch files I have made to launch the turtle simulator:
turtlesim_launch.launch
<launch>
<node name="turtlesim_node" pkg="turtlesim" type="turtlesim_node" />
</launch>
turtle_teleop_launch.launch
<launch>
<node name="turtle_teleop_key" pkg="turtlesim" type="turtle_teleop_key" />
</launch>
I have done a bit of google searching and found no similar problems for ROS (though there are some similar errors for Django and the like but they don't relate).
I appreciate the help!
P.S. It is worth noting that I make it to line 34 when the error occurs (process = launch.launch(node)).
actually that is mentioned in the documentation
You're not calling init_node() from the Python Main thread. Python only allows signals to be registered from the Main thread.
Look here rospyOverviewInitialization and Shutdown
A solution that works (but is quite dirty) is to remove the signal handler registration function:
def dummy_function(): pass
roslaunch.pmon._init_signal_handlers = dummy_function
This way you'll lose the ability to kill the node with CTRL+C, but at least you can start it.
I believe the problem is that you are trying to launch a node in a callback, which as user3732793 says isn't allowed.
If you use append to Queue (or just a list) in the callback, then instead of just rospy.spin() check for items in the queue and then launch if they are there. I believe it will work.
Here is an example

Python Cmd Tab Completion Problems

I've got an application I'm currently working on for our company. Its currently built around Python's Cmd module, and features tab-completion for a number of tasks.
For some reason however, the Tab completion only currently works on one machine in the building - running the scripts from other machines doesn't allow the tab completion.
Here's the offending code parts:
def populate_jobs_list():
global avail_jobs
avail_jobs = os.walk(rootDir()).next()[1]
print avail_jobs
...
def complete_job(self, text, line, start_index, end_index):
global avail_jobs
populate_jobs_list()
if text:
return [
jobs for jobs in avail_jobs
if jobs.startswith(text)
]
else:
return avail_jobs
def do_job(self, args):
pass
split_args = args.rsplit()
os.environ['JOB'] = args
job_dir = os.path.join( rootDir(), os.getenv('JOB'))
os.environ['JOB_PROPS'] = (job_dir + '\\job_format.opm')
if not os.path.isdir(job_dir):
print 'Job does not exist. Try again.'
return
else:
print('Jobbed into: ' + os.getenv('JOB'))
return
populate_jobs_list()
prompt = outPrompt()
prompt.prompt = '\> '
prompt.cmdloop('Loading...')
Am I missing something obvious here? Just to clarify, on machine A, the tab completion works as intended. When its run on any other machine in the building, it fails to complete.
Check if the environment variable PYTHONSTARTUP is set properly. It should point to a script which in turn needs to do sth like this:
try:
import readline
except ImportError:
sys.stdout.write("No readline module found, no tab completion available.\n")
else:
import rlcompleter
readline.parse_and_bind('tab: complete')
Maybe (some part of) this is only done properly on the one working machine?
Maybe the readline module is available only on the one working machine?

Categories

Resources