I am trying to write the Python script which prints the title of the active window using python in Mac OS.
Here is my code:
from AppKit import NSWorkspace
active_app_name = NSWorkspace.sharedWorkspace().frontmostApplication().localizedName()
print active_app_name
This code just prints name of the app like Google chrome or firefox, but not title. How to get title of the window?
Here is what I used to find both the active application name and window title on Mac OS X using Python by using Quartz API.
First of all, we need to add imports as required:
if sys.platform == "darwin":
import applescript
from AppKit import NSWorkspace
from Quartz import (
CGWindowListCopyWindowInfo,
kCGWindowListOptionOnScreenOnly,
kCGNullWindowID
)
And then we can get the active app name and window title via the code below:
def getActiveInfo(event_window_num):
try:
if sys.platform == "darwin":
app = NSWorkspace.sharedWorkspace().frontmostApplication()
active_app_name = app.localizedName()
options = kCGWindowListOptionOnScreenOnly
windowList = CGWindowListCopyWindowInfo(options, kCGNullWindowID)
windowTitle = 'Unknown'
for window in windowList:
windowNumber = window['kCGWindowNumber']
ownerName = window['kCGWindowOwnerName']
# geometry = window['kCGWindowBounds']
windowTitle = window.get('kCGWindowName', u'Unknown')
if windowTitle and (
event_window_num == windowNumber
or ownerName == active_app_name
):
# log.debug(
# 'ownerName=%s, windowName=%s, x=%s, y=%s, '
# 'width=%s, height=%s'
# % (window['kCGWindowOwnerName'],
# window.get('kCGWindowName', u'Unknown'),
# geometry['X'],
# geometry['Y'],
# geometry['Width'],
# geometry['Height']))
break
return _review_active_info(active_app_name, windowTitle)
if sys.platform == "win32":
(active_app_name, windowTitle) = _getActiveInfo_Win32()
return _review_active_info(active_app_name, windowTitle)
except:
log.error('Unexpected error: %s' % sys.exc_info()[0])
log.error('error line number: %s' % sys.exc_traceback.tb_lineno)
return 'Unknown', 'Unknown'
There is no access to app title from NSWorkspace.sharedWorkspace().activeApplication().
But you can find the current window title by its PID:
For example:
from AppKit import NSWorkspace
pid = NSWorkspace.sharedWorkspace().activeApplication()['NSApplicationProcessIdentifier']
Then find the right window using below code (it's stored in kCGWindowOwnerPID) as shown in below code:
Here is a complete shell example based on #JakeW's script:
#!/usr/bin/python
# Prints list of windows in the current workspace.
import sys
if sys.platform == "darwin":
from AppKit import NSWorkspace
from Quartz import (
CGWindowListCopyWindowInfo,
kCGWindowListOptionOnScreenOnly,
kCGNullWindowID
)
if sys.platform == "darwin":
curr_app = NSWorkspace.sharedWorkspace().frontmostApplication()
curr_pid = NSWorkspace.sharedWorkspace().activeApplication()['NSApplicationProcessIdentifier']
curr_app_name = curr_app.localizedName()
options = kCGWindowListOptionOnScreenOnly
windowList = CGWindowListCopyWindowInfo(options, kCGNullWindowID)
for window in windowList:
pid = window['kCGWindowOwnerPID']
windowNumber = window['kCGWindowNumber']
ownerName = window['kCGWindowOwnerName']
geometry = window['kCGWindowBounds']
windowTitle = window.get('kCGWindowName', u'Unknown')
if curr_pid == pid:
print("%s - %s (PID: %d, WID: %d): %s" % (ownerName, windowTitle.encode('ascii','ignore'), pid, windowNumber, geometry))
elif sys.platform == "win32":
(active_app_name, windowTitle) = _getActiveInfo_Win32()
It will list details of the current active window including its title.
As of macOS 10.6 and later it is better to use: frontmostApplication and if you want to get all of the applications listed you can call runningApplications method.
You can see more details at https://developer.apple.com/documentation/appkit/nsworkspace#overview
For example:
from AppKit import NSWorkspace
NSWorkspace.sharedWorkspace().runningApplications() // for getting all applications
NSWorkspace.sharedWorkspace().frontmostApplication() // for active window
I tried #kenorb's code but unlucky it can only get the app name, but no title content.
I finally found a way to do this with AppleScript:
You can find the answer here:
MacOSX: get foremost window title
First create a appleScript GetNameAndTitleOfActiveWindow.scpt
global frontApp, frontAppName, windowTitle
set windowTitle to ""
tell application "System Events"
set frontApp to first application process whose frontmost is true
set frontAppName to name of frontApp
tell process frontAppName
tell (1st window whose value of attribute "AXMain" is true)
set windowTitle to value of attribute "AXTitle"
end tell
end tell
end tell
return {frontAppName, windowTitle}
You can test it first in your mac terminal:
osascript GetNameAndTitleOfActiveWindow.scpt
And then write this in python:
title = subprocess.check_output(['osascript', 'GetNameAndTitleOfActiveWindow.scpt'])
Related
I would like that QFileDialog in PyQt5 opens in default browser dowload location. I have this code, which opens last used location, because of '' empty third parameter. How can I read this information in Windows and Linux?
def selectfile_Dialog(self, event=None):
fname, _ = QtWidgets.QFileDialog.getOpenFileName(
self, 'Open File', '', 'Binary executable (*.exe)', None)
# sender is object that sends the signal
sender = self.sender()
# write the selected file name into that QLineEdit widget 'list1_lineEdit'
sender.setText(fname)
Here's a possible solution:
import sys
import webbrowser
import os
import winreg
from PyQt5.Qt import * # noqa
def get_download_path():
if os.name == 'nt':
sub_key = r'SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders'
downloads_guid = '{374DE290-123F-4565-9164-39C4925E467B}'
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, sub_key) as key:
location = winreg.QueryValueEx(key, downloads_guid)[0]
return location
else:
return os.path.join(os.path.expanduser('~'), 'downloads')
def main():
app = QApplication(sys.argv)
fname, _ = QFileDialog.getOpenFileName(
None, 'Open File', get_download_path(), 'Binary executable (*.exe)', None
)
sys.exit(app.exec_())
if __name__ == "__main__":
main()
I followed the http://www.toadstorm.com/blog/?p=136 to open a maya scene without the UI.
This is the main script:
import maya.cmds as cmds
import subprocess
# replace mayaPath with the path on your system to mayapy.exe
mayaPath = 'C:/Program Files/Autodesk/Maya2018/bin/mayapy.exe'
# replace scriptPath with the path to the script you just saved
scriptPath = 'C:/Users/Rik/Desktop/CreateNewLayerWithMeshesForBatchRenderPurpose.py'
def massAddRenderLayer(filenames, layername):
for filename in filenames:
maya = subprocess.Popen(mayaPath+' '+scriptPath+' '+filename+' '+layername,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
out,err = maya.communicate()
exitcode = maya.returncode
if str(exitcode) != '0':
print(err)
print 'error opening file: %s' % (filename)
else:
print 'added new layer %s to %s' % (out,filename)
filenames = ['C:\Users\Rik\Desktop\Test_maya_file_FILE1.ma']
renderLayerToAdd = 'newRenderLayer'
massAddRenderLayer(filenames, renderLayerToAdd)
and this is the script that is called for:
import sys
import maya.standalone as std
std.initialize(name='python')
import maya.cmds as cmds
from mtoa.cmds.arnoldRender import arnoldRender
filename = sys.argv[1]
layername = sys.argv[2]
def addRenderLayer(filename,layername):
try:
cmds.file(filename,o=True,f=True)
newLyr = cmds.createRenderLayer(n=layername,empty=True,makeCurrent=True)
meshes = cmds.ls(type='mesh')
xforms = []
for i in meshes:
xf = cmds.listRelatives(i,p=True)[0]
xforms.append(xf)
cmds.editRenderLayerMembers(layername,xforms)
cmds.loadPlugin( 'mtoa.mll' )
arnoldRender(1, 1, True, True,'camera1', ' -layer' + newLyr)
cmds.setAttr("defaultArnoldDriver.ai_translator", "png", type="string")
cmds.setAttr("defaultArnoldDriver.pre", "file_name", type="string")
arnoldRender(1920, 1080, True, True,'camera1', ' -layer' + newLyr)
sys.stdout.write(newLyr)
return newLyr
except Exception, e:
sys.stderr.write(str(e))
sys.exit(-1)
addRenderLayer(filename,layername)
When I run the main script he opens the mayapy.exe program, but when I want to render a frame then I get an error saying Failed to register renderer 'arnold' even when I have imported arnold and loaded the 'mtoa.mll' plugin.
How can I 'register 'arnold''?
I have the same problem mentioned here : Not being able to edit NSTextField on NSPopover even though Editable behavior is set. The solution seems to be to override the canBecomeKeyWindow of NSWindow. I am trying to do the same thing in PyObjC, but I am getting an error Python signature doesn't match implied objective-C signature.
In the following code, if I comment out canBecomeKeyWindow_(), then the app runs as expected, but I am not able to click and edit the textfields.
# from Cocoa import *
from AppKit import NSWindowController, NSApplication, NSApp, NSMaxYEdge, NSImage, NSStatusBar, NSMenu, NSMenuItem, NSVariableStatusItemLength, NSRect
from Cocoa import objc
from Foundation import NSUserNotification, NSUserNotificationCenter, NSObject
from PyObjCTools import AppHelper
import webbrowser
import subprocess
import os
global popover
class TestApp(NSApplication):
def finishLaunching(self):
# Make statusbar item
statusbar = NSStatusBar.systemStatusBar()
self.statusitem = statusbar.statusItemWithLength_(NSVariableStatusItemLength)
self.icon = NSImage.alloc().initByReferencingFile_('app-icon.png')
self.icon.setScalesWhenResized_(True)
self.icon.setSize_((20, 20))
self.statusitem.setImage_(self.icon)
self.statusitem.setHighlightMode_(1)
# make the menu
self.menubarMenu = NSMenu.alloc().init()
self.menuItem = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_('Login', 'loginCallback:', '')
self.menubarMenu.addItem_(self.menuItem)
self.quit = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_('Quit', 'terminate:', '')
self.menubarMenu.addItem_(self.quit)
# add menu to statusitem
self.statusitem.setMenu_(self.menubarMenu)
def loginCallback_(self, notification):
# Initiate the contrller with a XIB
viewController = SimpleXibDemoController.alloc().initWithWindowNibName_("Login")
# Show the window
viewController.showWindow_(viewController)
rect = self.statusitem.valueForKey_('button').frame()
viewController.popover.showRelativeToRect_ofView_preferredEdge_(rect, self.statusitem.valueForKey_('button'), NSMaxYEdge)
class SimpleXibDemoController(NSWindowController):
popover = objc.IBOutlet()
counterTextField = objc.IBOutlet()
username_field = objc.IBOutlet()
password_field = objc.IBOutlet()
submit_button = objc.IBOutlet()
def canBecomeKeyWindow_(self):
return 1
def windowDidLoad(self):
NSWindowController.windowDidLoad(self)
#objc.IBAction
def submit_(self, sender):
username = self.username_field.stringValue()
password = self.password_field.stringValue()
self.updateDisplay(username + ' ' + password)
def updateDisplay(self, value):
self.counterTextField.setStringValue_(value)
if __name__ == "__main__":
app = TestApp.sharedApplication()
icon = NSImage.alloc().initByReferencingFile_('app-icon.png')
app.setApplicationIconImage_(icon)
AppHelper.runEventLoop()
It looks like you're adding an underscore where you shouldn't. The PyObjC bridge will translate it into a colon. Besides that, the corresponding Python boolean value should be True. Thus, the correct function would look like this:
def canBecomeKeyWindow(self):
return True
I want to detect applications window name when changing focus event occurs with python xlib, so in the first step I use this code:
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import Xlib.display
import time
display = Xlib.display.Display()
while True:
window = display.get_input_focus().focus
wmname = window.get_wm_name()
wmclass = window.get_wm_class()
if wmclass is None and wmname is None:
window = window.query_tree().parent
wmname = window.get_wm_name()
print "WM Name: %s" % ( wmname, )
time.sleep(3)
But I want a correct way, then I research about xlib events and find Input Focus Events and write this code:
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import Xlib.display
from Xlib import X
def main():
display = Xlib.display.Display(':0')
root = display.screen().root
root.change_attributes(event_mask=Xlib.X.FocusChangeMask)
while True:
event = root.display.next_event()
#if event.type == X.FocusIn or event.type == X.FocusOut:
if event.type == X.FocusOut :
window = display.get_input_focus().focus
wmname = window.get_wm_name()
wmclass = window.get_wm_class()
if wmclass is None and wmname is None:
window = window.query_tree().parent
wmname = window.get_wm_name()
print "WM Name: %s" % ( wmname, )
if __name__ == "__main__":
main()
Sadly it's not work correctly especially in tabbed browsing on google chrome and firefox, so Is there a correct way for this situation?
Your code is almost right, but it misses two things:
rather than listening only to focus changes, it should also listen to window property events which include changes of WM_NAME property, that also happen when you cycle tabs in your browser.
rather than listening only in root window, it should listen to every window (that gets focused). You can attach the event handler the same way as you do with the root window.
That being said, here is a working sample:
#!/usr/bin/python3
import Xlib
import Xlib.display
disp = Xlib.display.Display()
root = disp.screen().root
NET_WM_NAME = disp.intern_atom('_NET_WM_NAME')
NET_ACTIVE_WINDOW = disp.intern_atom('_NET_ACTIVE_WINDOW')
root.change_attributes(event_mask=Xlib.X.FocusChangeMask)
while True:
try:
window_id = root.get_full_property(NET_ACTIVE_WINDOW, Xlib.X.AnyPropertyType).value[0]
window = disp.create_resource_object('window', window_id)
window.change_attributes(event_mask=Xlib.X.PropertyChangeMask)
window_name = window.get_full_property(NET_WM_NAME, 0).value
except Xlib.error.XError: #simplify dealing with BadWindow
window_name = None
print(window_name)
event = disp.next_event()
#rr- As I just corrected elsewhere, you'll want to query both the current _NET_WM_NAME (UTF-8) and the legacy WM_NAME (non-UTF8) properties or the default xterm configuration will return no title.
I just posted a complete working example over on your Unix & Linux StackExchange question.
To avoid sending people on a cross-reference hunt, here's a copy of the code I posted there:
#!/usr/bin/python
from contextlib import contextmanager
import Xlib
import Xlib.display
disp = Xlib.display.Display()
root = disp.screen().root
NET_ACTIVE_WINDOW = disp.intern_atom('_NET_ACTIVE_WINDOW')
NET_WM_NAME = disp.intern_atom('_NET_WM_NAME') # UTF-8
WM_NAME = disp.intern_atom('WM_NAME') # Legacy encoding
last_seen = { 'xid': None, 'title': None }
#contextmanager
def window_obj(win_id):
"""Simplify dealing with BadWindow (make it either valid or None)"""
window_obj = None
if win_id:
try:
window_obj = disp.create_resource_object('window', win_id)
except Xlib.error.XError:
pass
yield window_obj
def get_active_window():
win_id = root.get_full_property(NET_ACTIVE_WINDOW,
Xlib.X.AnyPropertyType).value[0]
focus_changed = (win_id != last_seen['xid'])
if focus_changed:
with window_obj(last_seen['xid']) as old_win:
if old_win:
old_win.change_attributes(event_mask=Xlib.X.NoEventMask)
last_seen['xid'] = win_id
with window_obj(win_id) as new_win:
if new_win:
new_win.change_attributes(event_mask=Xlib.X.PropertyChangeMask)
return win_id, focus_changed
def _get_window_name_inner(win_obj):
"""Simplify dealing with _NET_WM_NAME (UTF-8) vs. WM_NAME (legacy)"""
for atom in (NET_WM_NAME, WM_NAME):
try:
window_name = win_obj.get_full_property(atom, 0)
except UnicodeDecodeError: # Apparently a Debian distro package bug
title = "<could not decode characters>"
else:
if window_name:
win_name = window_name.value
if isinstance(win_name, bytes):
# Apparently COMPOUND_TEXT is so arcane that this is how
# tools like xprop deal with receiving it these days
win_name = win_name.decode('latin1', 'replace')
return win_name
else:
title = "<unnamed window>"
return "{} (XID: {})".format(title, win_obj.id)
def get_window_name(win_id):
if not win_id:
last_seen['title'] = "<no window id>"
return last_seen['title']
title_changed = False
with window_obj(win_id) as wobj:
if wobj:
win_title = _get_window_name_inner(wobj)
title_changed = (win_title != last_seen['title'])
last_seen['title'] = win_title
return last_seen['title'], title_changed
def handle_xevent(event):
if event.type != Xlib.X.PropertyNotify:
return
changed = False
if event.atom == NET_ACTIVE_WINDOW:
if get_active_window()[1]:
changed = changed or get_window_name(last_seen['xid'])[1]
elif event.atom in (NET_WM_NAME, WM_NAME):
changed = changed or get_window_name(last_seen['xid'])[1]
if changed:
handle_change(last_seen)
def handle_change(new_state):
"""Replace this with whatever you want to actually do"""
print(new_state)
if __name__ == '__main__':
root.change_attributes(event_mask=Xlib.X.PropertyChangeMask)
get_window_name(get_active_window()[0])
handle_change(last_seen)
while True: # next_event() sleeps until we get an event
handle_xevent(disp.next_event())
There's also a more heavily commented version in this GitHub Gist.
I would like to be able to save my session state within the PythonWin editor (e.g. these three files are opened and positioned in these particular locations within the PythonWin window). I can get handles to each of the child windows within PythonWin using win32gui, as well as the titles of each of the files and the positions/sizes of the windows. I'm unclear though in how to get the full path for the file listed as the child window name (i.e. if child window name is test.py and test.py lives at c:\python\test.py, I don't know how to get c:\python). I was thinking I would write out which files were opened plus their window positions to a small file that I would then call at PythonWin start time for loading.
Any ideas on how to get the full paths to the child window names?
Alternatively if someone already has a more elegant solution for saving session state in PythonWin please pass it along.
Below is the code I'm using right now (thanks to Michal Niklas for the starter code for using win32gui).
import win32gui
import re
MAIN_HWND = 0
def is_win_ok(hwnd, starttext):
s = win32gui.GetWindowText(hwnd)
if s.startswith(starttext):
global MAIN_HWND
MAIN_HWND = hwnd
return None
return 1
def find_main_window(starttxt):
global MAIN_HWND
win32gui.EnumChildWindows(0, is_win_ok, starttxt)
return MAIN_HWND
def winPos(hwnd):
if type(hwnd) == type(1): ( left, top, right, bottom ) = win32gui.GetWindowRect(hwnd)
return "%i, %i, %i, %i" % (left, right, top, bottom)
def winName(hwnd, children):
s = win32gui.GetWindowText(hwnd)
rePy = re.compile(r'[a-zA-Z1-9_ ]*.py')
rePySearch = rePy.search(s)
if rePySearch is not None:
if rePySearch.group()[0:7] != "Running":
s = s + ',' + winPos(hwnd) + '\n'
children.append(s)
return 1
def main():
children = []
main_app = 'PythonWin'
hwnd = win32gui.FindWindow(None, main_app)
if hwnd < 1:
hwnd = find_main_window(main_app)
if hwnd:
win32gui.EnumChildWindows(hwnd, winName, children)
filename = "sessionInfo.txt"
sessionFile = os.path.join(sys.path[0],filename)
fp=open(sessionFile, 'wb')
for i in range(len(children)):
fp.write(children[i])
fp.close()
main()
I could be wrong, but isn't PythonWin written in Python?
Have you tried reading the source to the "Save" command to figure out where it stores its full paths?
(I'd take a look myself, but I haven't used Windows in half a decade)