I try to display the current windows for each click on the system.
I do this code :
from AppKit import NSWorkspace
def getwindows():
activeAppName = NSWorkspace.sharedWorkspace().activeApplication()['NSApplicationName']
print activeAppName
return
def main():
getwindows()
main()
But only the current windows when i setup the script is displayed.
How can i bind this script in a loop with a click event ?
I already try to use Turtle but some errors appended.
Note that the activeApplication method of NSWorkSpace is deprecated. The following can be used to actively probe the running applications for their active status:
import AppKit
import time
rl = AppKit.NSRunLoop.currentRunLoop()
ws = AppKit.NSWorkspace.sharedWorkspace()
for i in xrange(10):
for app in ws.runningApplications():
if app.isActive():
print "active app:", app.localizedName()
date = AppKit.NSDate.date()
time.sleep(1)
rl.acceptInputForMode_beforeDate_( AppKit.NSDefaultRunLoopMode, date )
Active means it is the receiving keyboard input. Clicking on an application will cause it to become active. Note that the acceptInputForMode method must be called so that property changes are reflected in the current app. Run this program then click on various other applications -- you should see the active app change.
A kind of binding can be done through observers:
import AppKit
ws = AppKit.NSWorkspace.sharedWorkspace()
appL = ws.runningApplications()
class MyClass( AppKit.NSObject ):
def observeValueForKeyPath_ofObject_change_context_(self,
kpath, objid, change, context ):
print "path change", kpath, change['new'], appL[context].localizedName()
obj = MyClass.new()
for i in xrange(len(appL)):
appL[i].addObserver_forKeyPath_options_context_( obj,
"isActive", AppKit.NSKeyValueObservingOptionNew, i )
date = AppKit.NSDate.date().dateByAddingTimeInterval_( 10 )
rl = AppKit.NSRunLoop.currentRunLoop()
rl.acceptInputForMode_beforeDate_( AppKit.NSDefaultRunLoopMode, date )
for app in appL:
app.removeObserver_forKeyPath_( obj, "isActive" )
Run this program same as the last.
There are a few other properties of NSRunningApplication that you could probe/observe (such as hidden) but the list is quite short.
Related
I am trying to simulate an environment with vms and trying to run an object method in background thread. My code looks like the following.
hyper_v.py file :
import random
from threading import Thread
from virtual_machine import VirtualMachine
class HyperV(object):
def __init__(self, hyperv_name):
self.hyperv_name = hyperv_name
self.vms_created = {}
def create_vm(self, vm_name):
if vm_name not in self.vms_created:
vm1 = VirtualMachine({'vm_name': vm_name})
self.vms_created[vm_name] = vm1
vm1.boot()
else:
print('VM:', vm_name, 'already exists')
def get_vm_stats(self, vm_name):
print('vm stats of ', vm_name)
print(self.vms_created[vm_name].get_values())
if __name__ == '__main__':
hv = HyperV('temp')
vm_name = 'test-vm'
hv.create_vm(vm_name)
print('getting vm stats')
th2 = Thread(name='vm1_stats', target=hv.get_vm_stats(vm_name) )
th2.start()
virtual_machine.py file in the same directory:
import random, time, uuid, json
from threading import Thread
class VirtualMachine(object):
def __init__(self, interval = 2, *args, **kwargs):
self.vm_id = str(uuid.uuid4())
#self.vm_name = kwargs['vm_name']
self.cpu_percentage = 0
self.ram_percentage = 0
self.disk_percentage = 0
self.interval = interval
def boot(self):
print('Bootingup', self.vm_id)
th = Thread(name='vm1', target=self.update() )
th.daemon = True #Setting the thread as daemon thread to run in background
print(th.isDaemon()) #This prints true
th.start()
def update(self):
# This method needs to run in the background simulating an actual vm with changing values.
i = 0
while(i < 5 ): #Added counter for debugging, ideally this would be while(True)
i+=1
time.sleep(self.interval)
print('updating', self.vm_id)
self.cpu_percentage = round(random.uniform(0,100),2)
self.ram_percentage = round(random.uniform(0,100),2)
self.disk_percentage = round(random.uniform(0,100),2)
def get_values(self):
return_json = {'cpu_percentage': self.cpu_percentage,
'ram_percentage': self.ram_percentage,
'disk_percentage': self.disk_percentage}
return json.dumps(return_json)
The idea is to create a thread that keeps on updating the values and on request, we read the values of the vm object by calling the vm_obj.get_values() we would be creating multiple vm_objects to simulate multiple vms running in parallel and we need to get the information from a particular vm on request.
The problem, that I am facing, is that the update() function of the vm doesnot run in the background (even though the thread is set as daemon thread).
The method call hv.get_vm_stats(vm_name) waits until the completion of vm_object.update() (which is called by vm_object.boot()) and then prints the stats. I would like to get the stats of the vm on request by keeping the vm_object.update() running in the background forever.
Please share your thoughts if I am overlooking anything related to the basics. I tried looking into the issues related to the python threading library but I could not come to any conclusion. Any help is greatly appreciated. The next steps would be to have a REST api to call these functions to get the data of any vm but I am struck with this problem.
Thanks in advance,
As pointed out by #Klaus D in the comments, my mistake was using the braces when specifying the target function in the thread definition, which resulted in the function being called right away.
target=self.update() will call the method right away. Remove the () to
hand the method over to the thread without calling it.
I am writing a script intended to be used by members of a project team. As part of the script, I am launching a 3rd party proprietary application run through Citrix. I am going to use the script mostly to send keys to this application, but the first step once it launches is for the user to log in.
Because I would like the user to log in while the script is running, rather than asking for user/pass from some kind of GUI input earlier, and because the time it takes Citrix to launch varies, I would like to include some kind of logic that detects when the user has logged in and then resume the script from there, rather than including an obnoxiously long implicit wait or risking the script timing out.
Is there a way to detect user keystrokes using win32com.client (or to detect a change in state of the application itself)? See below for the relevant code to launch the app:
import win32com.client
shell = win32com.client.Dispatch("WScript.Shell")
shell.Run('C:\Citrix\[rest of path])
EDIT:
Per Vasily's suggestion in the comments below, I attempted to adapt the "hook and listen" code to my scenario, but was unsuccessful. When I launch my file, I don't even get an exception message in my terminal, I get a Windows pop-up that says Python encountered a problem and needs to quit.
This is how I adapted it:
#[omitting import lines for brevity]
def on_timer():
"""Callback by timer out"""
win32api.PostThreadMessage(main_thread_id, win32con.WM_QUIT, 0, 0);
def on_event(args):
"""Callback for keyboard and mouse events"""
if isinstance(args, KeyboardEvent):
for i in range(1,100):
time.sleep(1)
if args.pressed_key == 'Lcontrol':
break
def init():
hk = Hook()
hk.handler = on_event
main_thread_id = win32api.GetCurrentThreadId()
t = Timer(55.0, on_timer) # Quit after 55 seconds
t.start()
hk.hook(keyboard=True, mouse=True)
At the point when the 3rd party Citrix app begins to launch in my main script, I call hookandlisten.init().
As a reminder, my goal is to wait until the user sends a certain keystroke (here I chose Control) before proceeding with the rest of the main script.
Solved this by eliminating the timer and unhooking the keyboard upon the correct keystroke:
import win32api
import win32con
from pywinauto.win32_hooks import Hook
from pywinauto.win32_hooks import KeyboardEvent
from pywinauto.win32_hooks import MouseEvent
def on_event(args):
"""Callback for keyboard and mouse events"""
if isinstance(args, KeyboardEvent):
if args.current_key == 'Lcontrol' and args.event_type == 'key down':
print("Success")
hk.unhook_keyboard()
return
def init():
hk.handler = on_event
hk.hook(keyboard=True, mouse=False)
hk = Hook()
I'm developing my first app on python for OS X (and also generally on python) and i faced the problem… My current script parses sounds from iTunes and prints it in to the window. It looks like this
from Cocoa import *
from Foundation import *
from ScriptingBridge import *
class SocialTunesController(NSWindowController):
testLabel = objc.IBOutlet()
def windowDidLoad(self):
NSWindowController.windowDidLoad(self)
self.updateTrack()
def updateTrack(self):
iTunes = SBApplication.applicationWithBundleIdentifier_("com.apple.iTunes")
current_track_info = "Name: " + iTunes.currentTrack().name() + "\nArtist: " + iTunes.currentTrack().artist() + "\nAlbum: " + iTunes.currentTrack().album()
self.testLabel.setStringValue_(current_track_info)
if __name__ == "__main__":
app = NSApplication.sharedApplication()
viewController = SocialTunesController.alloc().initWithWindowNibName_("SocialTunes")
viewController.showWindow_(viewController)
from PyObjCTools import AppHelper
AppHelper.runEventLoop()
The main problem is how to fire event when track is changes that it automatically would update the track info in current window…
iTunes posts a distributed notification when a track change occurs. You need to register a controller to listen for those notifications:
noteCenter = NSDistributedNotificationCenter.defaultCenter()
noteCenter.addObserver_selector_name_object_(theController,
objc.selector(theController.updateTrack_,
signature="v#:#"),
"com.apple.iTunes.playerInfo",
None)
And your updateTrack_() method needs to take one argument (aside from self), which is the posted notification.
You can use events with PyObjC, whether or not you can receive iTunes events depends on whether of not iTunes sends events. For all I know all iTunes status widgets just regularly poll if the iTunes track has changed.
I need to detect when the user pressed "quit" in the dock menu.
My application is actually just a launcher for the backend server of a web interface. I keep it in the dock menu by manually waiting for the launched process to end (with poll and sleep). The actvity monitor showed it as not responding so I added a native function to process events like "touches". Not responding flag is now gone, but the user cannot quit this application (because the native functions processes the event, I guess).
I used ctypes to access that native function.
TVB = subprocess.popen(args)
coreFoundation = cdll.LoadLibrary('/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation')
CFRunLoopRunInMode = coreFoundation.CFRunLoopRunInMode # the native function
CFRunLoopRunInMode.restype = c_int32 # its return type
CFRunLoopRunInMode.argtypes = [ c_void_p, c_double, c_bool ] # its arguments types
defaultMode = c_void_p.in_dll(coreFoundation, u'kCFRunLoopDefaultMode') # the default mode to process events
sleepTime = c_double(5) # the duration to process the events
retAfterSourceHandled = c_bool(0) # do NOT return after processing
while not TVB.poll(): # keep alive as long as TVB is alive
CFRunLoopRunInMode(defaultMode, sleepTime, retAfterSourceHandled)
sleep(5)
#detect 'quit' here and stop TVB, then quit
I will also consider other solutions to CFRunLoopRunInMode... Something like processNextEvent() would be ideal.
A possible solution to this problem is to use PyObjC and a custom UIApplicationDelegate implementation.
import AppKit
import Foundation
from PyObjCTools import AppHelper
class ApplicationDelegate(Foundation.NSObject):
""" MAC OS UI specific Delegate class """
def applicationDidFinishLaunching_(self, notification):
""" Here you could register a timer to pull your sub-processes, for example. """
pass
def applicationWillTerminate_(self, notification):
""" This is the hook you get for when the user clicks QUIT in dock """
pass
app = AppKit.NSApplication.sharedApplication()
delegate = ApplicationDelegate.alloc().init()
app.setDelegate_(delegate)
AppHelper.runEventLoop()
In the end PyObjC is not much different than the ctypes loads you tried, but it has some helper methods (like AppKit.NSRunLoop.currentRunLoop(), or AppHelper.stopEventLoop()) which could make the python code clearer.
For this solution I assume to have a Python project further packed for deployment with py2app. I used pyObjc version 2.3 (installed with easy_install in Python 2.7 on Mac OS x 10.7.5).
Good evening!
I'm writing a simple system tray app for Linux that checks for system updates using Python and GTK. So far, I have the basic functionality, the app runs, displays an icon, and by right-clicking there is a menu with a few more options. The problem comes about when I try to change the gtk.StatusIcon to a different image, rather than "swapping" the original icon for the "alert" icon, it creates a second icon in the tray (so now the standard icon and the alert icon are side by side) and when quitting the alert icon instance of the app, closes both.
This particular app is in two parts (code below), the back-end script that runs within an hourly cron job, and the GUI (system tray icon) which can be run as auto-started and/or via the applications menu.
I have read through the PyGTK documentation, but nothing that I saw explains how to switch (swap) the icons "in-place". I'm sure that I am missing something and would appreciate any constructive input from a second pair of eyes.
Here's the "back-end" code:
import time
import subprocess
import logging
import lupdater
# Create the log, set level to DEBUG so that all messages are available
logging.basicConfig(filename='/tmp/lupdater.log', level=logging.DEBUG)
# Global package list
paclist = []
class Pacman():
'''Provides functions to call pacman and update the repos, as well as
return a list with number of updates. '''
def pac_update(self):
'''Updates the repositories, notifies the user.'''
subprocess.call(['/usr/bin/notify-send', 'Updating repositories for update check...'], shell=False)
upd = subprocess.Popen('sudo pacman -Syy', shell=True, stdout=subprocess.PIPE)
stdout, stderr = upd.communicate()
def pac_list(self):
'''Creates a list of packages needing to be updated and counts them,
displays the count in a notification for user action.'''
subprocess.call(['/usr/bin/notify-send', 'Checking for updates...'], shell=False)
# Clean up the list from previous checks so that we keep an accurate count.
if len(paclist) > 0:
for i in paclist:
paclist.remove(i)
lst = subprocess.Popen('pacman -Qu', shell=True, stdout=subprocess.PIPE)
for line in lst.stdout:
line.rstrip('\r\n')
paclist.append(line)
numupdates = len(paclist)
if numupdates >= 1:
subprocess.call(['/usr/bin/notify-send', '%s %s %s' % ('You have', numupdates, 'updates available!')], shell=False)
# Here we set the status icon to change and start blinking
lupblinker = lupdater.SystrayApp()
lupblinker.blinker()
logging.info(time.ctime() + ': lupdater had %s updates available.\n' % (numupdates))
else:
subprocess.call(['/usr/bin/notify-send', 'Your system is already up to date! :)'], shell=False)
logging.info(time.ctime() + ': No updates available, system is up to date.')
# "Future-proofing"
return numupdates, paclist
def pac_check_list(self, paclist):
# For now only checks for kernel updates, packages named "linux".
# TODO: Check for kernel modules such as video drivers that require
# a system restart or manual initialization.
critical = []
if len(paclist) > 0:
for i in paclist:
if i.startswith('linux'):
critical.append(i)
if len(critical) >= 1:
for i in critical:
subprocess.call(['/usr/bin/notify-send',
'%s %s' % (i, 'is a critical update, it requires a system restart to take effect.')], shell=False)
logging.info(time.ctime() + ': Critical update detected, user notified via notify-send.')
return critical, paclist
def run_me(x):
logging.info(time.ctime() + ': lupdater now running with sleep enabled process.')
# Meat and Potatoes
p = Pacman()
p.pac_update()
p.pac_list()
p.pac_check_list(paclist)
if __name__ == '__main__':
x = 0
run_me(x)enter code here
And here is the "GUI" code for the systray app:
import gtk
import subprocess
import lupdaterapi
class SystrayApp():
def __init__(self):
self.tray = gtk.StatusIcon()
self.tray.set_from_file('/usr/share/icons/lupdater.png')
self.tray.connect('popup-menu', self.on_right_click)
self.tray.set_tooltip('Lemur Updater')
self.lapi = lupdaterapi
def blinker(self):
self.tray.set_from_file('/usr/share/icons/lupdater-alert.png')
self.tray.set_blinking(True)
def on_right_click(self, icon, event_button, event_time):
self.make_menu(event_button, event_time)
if self.tray.set_blinking() == True:
self.tray.set_blinking(False)
self.tray.set_from_file('/usr/share/icons/lupdater.png')
def make_menu(self, event_button, event_time):
menu = gtk.Menu()
# show about dialog
about = gtk.MenuItem('About')
about.show()
menu.append(about)
about.connect('activate', self.show_about_dialog)
# add run item for manual updates
run = gtk.MenuItem('Check Updates')
run.show()
menu.append(run)
run.connect('activate', self.lapi.run_me)
#add log checker, open in leafpad for now
chklog = gtk.MenuItem('View Log')
chklog.show()
menu.append(chklog)
chklog.connect('activate', self.show_log)
# add quit item
quit = gtk.MenuItem('Quit')
quit.show()
menu.append(quit)
quit.connect('activate', gtk.main_quit)
menu.popup(None, None, gtk.status_icon_position_menu,
event_button, event_time, self.tray)
def show_about_dialog(self, widget):
about_dialog = gtk.AboutDialog()
about_dialog.set_destroy_with_parent (True)
about_dialog.set_icon_name ('Lemur Updater')
about_dialog.set_name('Lemur Updater')
about_dialog.set_version('1.3b')
about_dialog.set_comments((u'System Tray interface to the Lemur Updater'))
about_dialog.set_authors([u'Brian Tomlinson <darthlukan#gmail.com>'])
about_dialog.run()
about_dialog.destroy()
def show_log(self, widget):
subprocess.Popen('/usr/bin/leafpad /tmp/lupdater.log', shell=True)
# Let's roll!
if __name__ == '__main__':
SystrayApp()
gtk.main()
Thanks again in advance!
Have you tried setting a name and a title for the icon?