Related
I would like to get the active window on the screen using python.
For example, the management interface of the router where you enter the username and password as admin
That admin interface is what I want to capture using python to automate the entry of username and password.
What imports would I require in order to do this?
On windows, you can use the python for windows extensions (http://sourceforge.net/projects/pywin32/):
from win32gui import GetWindowText, GetForegroundWindow
print GetWindowText(GetForegroundWindow())
Below code is for python 3:
from win32gui import GetWindowText, GetForegroundWindow
print(GetWindowText(GetForegroundWindow()))
(Found this on http://scott.sherrillmix.com/blog/programmer/active-window-logger/)
Thanks goes to the answer by Nuno André, who showed how to use ctypes to interact with Windows APIs. I have written an example implementation using his hints.
The ctypes library is included with Python since v2.5, which means that almost every user has it. And it's a way cleaner interface than old and dead libraries like win32gui (last updated in 2017 as of this writing). ((Update in late 2020: The dead win32gui library has come back to life with a rename to pywin32, so if you want a maintained library, it's now a valid option again. But that library is 6% slower than my code.))
Documentation is here: https://docs.python.org/3/library/ctypes.html (You must read its usage help if you wanna write your own code, otherwise you can cause segmentation fault crashes, hehe.)
Basically, ctypes includes bindings for the most common Windows DLLs. Here is how you can retrieve the title of the foreground window in pure Python, with no external libraries needed! Just the built-in ctypes! :-)
The coolest thing about ctypes is that you can Google any Windows API for anything you need, and if you want to use it, you can do it via ctypes!
Python 3 Code:
from typing import Optional
from ctypes import wintypes, windll, create_unicode_buffer
def getForegroundWindowTitle() -> Optional[str]:
hWnd = windll.user32.GetForegroundWindow()
length = windll.user32.GetWindowTextLengthW(hWnd)
buf = create_unicode_buffer(length + 1)
windll.user32.GetWindowTextW(hWnd, buf, length + 1)
# 1-liner alternative: return buf.value if buf.value else None
if buf.value:
return buf.value
else:
return None
Performance is extremely good: 0.01 MILLISECONDS on my computer (0.00001 seconds).
Will also work on Python 2 with very minor changes. If you're on Python 2, I think you only have to remove the type annotations (from typing import Optional and -> Optional[str]). :-)
Enjoy!
Win32 Technical Explanations:
The length variable is the length of the actual text in UTF-16 (Windows Wide "Unicode") CHARACTERS. (It is NOT the number of BYTES.) We have to add + 1 to add room for the null terminator at the end of C-style strings. If we don't do that, we would not have enough space in the buffer to fit the final real character of the actual text, and Windows would truncate the returned string (it does that to ensure that it fits the super important final string Null-terminator).
The create_unicode_buffer function allocates room for that many UTF-16 CHARACTERS.
Most (or all? always read Microsoft's MSDN docs!) Windows APIs related to Unicode text take the buffer length as CHARACTERS, NOT as bytes.
Also look closely at the function calls. Some end in W (such as GetWindowTextLengthW). This stands for "Wide string", which is the Windows name for Unicode strings. It's very important that you do those W calls to get proper Unicode strings (with international character support).
PS: Windows has been using Unicode for a long time. I know for a fact that Windows 10 is fully Unicode and only wants the W function calls. I don't know the exact cutoff date when older versions of Windows used other multi-byte string formats, but I think it was before Windows Vista, and who cares? Old Windows versions (even 7 and 8.1) are dead and unsupported by Microsoft.
Again... enjoy! :-)
UPDATE in Late 2020, Benchmark vs the pywin32 library:
import time
import win32ui
from typing import Optional
from ctypes import wintypes, windll, create_unicode_buffer
def getForegroundWindowTitle() -> Optional[str]:
hWnd = windll.user32.GetForegroundWindow()
length = windll.user32.GetWindowTextLengthW(hWnd)
buf = create_unicode_buffer(length + 1)
windll.user32.GetWindowTextW(hWnd, buf, length + 1)
return buf.value if buf.value else None
def getForegroundWindowTitle_Win32UI() -> Optional[str]:
# WARNING: This code sometimes throws an exception saying
# "win32ui.error: No window is is in the foreground."
# which is total nonsense. My function doesn't fail that way.
return win32ui.GetForegroundWindow().GetWindowText()
iterations = 1_000_000
start_time = time.time()
for x in range(iterations):
foo = getForegroundWindowTitle()
elapsed1 = time.time() - start_time
print("Elapsed 1:", elapsed1, "seconds")
start_time = time.time()
for x in range(iterations):
foo = getForegroundWindowTitle_Win32UI()
elapsed2 = time.time() - start_time
print("Elapsed 2:", elapsed2, "seconds")
win32ui_pct_slower = ((elapsed2 / elapsed1) - 1) * 100
print("Win32UI library is", win32ui_pct_slower, "percent slower.")
Typical result after doing multiple runs on an AMD Ryzen 3900x:
My function: 4.5769994258880615 seconds
Win32UI library: 4.8619983196258545 seconds
Win32UI library is 6.226762715455125 percent slower.
However, the difference is small, so you may want to use the library now that it has come back to life (it had previously been dead since 2017). But you're going to have to deal with that library's weird "no window is in the foreground" exception, which my code doesn't suffer from (see the code comments in the benchmark code).
Either way... enjoy!
The following script should work on Linux, Windows and Mac. It is currently only tested on Linux (Ubuntu Mate Ubuntu 15.10).
Prerequisites
For Linux:
Install wnck (sudo apt-get install python-wnck on Ubuntu, see libwnck.)
For Windows:
Make sure win32gui is available
For Mac:
Make sure AppKit is available
The script
#!/usr/bin/env python
"""Find the currently active window."""
import logging
import sys
logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s',
level=logging.DEBUG,
stream=sys.stdout)
def get_active_window():
"""
Get the currently active window.
Returns
-------
string :
Name of the currently active window.
"""
import sys
active_window_name = None
if sys.platform in ['linux', 'linux2']:
# Alternatives: https://unix.stackexchange.com/q/38867/4784
try:
import wnck
except ImportError:
logging.info("wnck not installed")
wnck = None
if wnck is not None:
screen = wnck.screen_get_default()
screen.force_update()
window = screen.get_active_window()
if window is not None:
pid = window.get_pid()
with open("/proc/{pid}/cmdline".format(pid=pid)) as f:
active_window_name = f.read()
else:
try:
from gi.repository import Gtk, Wnck
gi = "Installed"
except ImportError:
logging.info("gi.repository not installed")
gi = None
if gi is not None:
Gtk.init([]) # necessary if not using a Gtk.main() loop
screen = Wnck.Screen.get_default()
screen.force_update() # recommended per Wnck documentation
active_window = screen.get_active_window()
pid = active_window.get_pid()
with open("/proc/{pid}/cmdline".format(pid=pid)) as f:
active_window_name = f.read()
elif sys.platform in ['Windows', 'win32', 'cygwin']:
# https://stackoverflow.com/a/608814/562769
import win32gui
window = win32gui.GetForegroundWindow()
active_window_name = win32gui.GetWindowText(window)
elif sys.platform in ['Mac', 'darwin', 'os2', 'os2emx']:
# https://stackoverflow.com/a/373310/562769
from AppKit import NSWorkspace
active_window_name = (NSWorkspace.sharedWorkspace()
.activeApplication()['NSApplicationName'])
else:
print("sys.platform={platform} is unknown. Please report."
.format(platform=sys.platform))
print(sys.version)
return active_window_name
print("Active window: %s" % str(get_active_window()))
For Linux users:
All the answers provided required additional modules like "wx" that had numerous errors installing ("pip" failed on build), but I was able to modify this solution quite easily -> original source. There were bugs in the original (Python TypeError on regex)
import sys
import os
import subprocess
import re
def get_active_window_title():
root = subprocess.Popen(['xprop', '-root', '_NET_ACTIVE_WINDOW'], stdout=subprocess.PIPE)
stdout, stderr = root.communicate()
m = re.search(b'^_NET_ACTIVE_WINDOW.* ([\w]+)$', stdout)
if m != None:
window_id = m.group(1)
window = subprocess.Popen(['xprop', '-id', window_id, 'WM_NAME'], stdout=subprocess.PIPE)
stdout, stderr = window.communicate()
else:
return None
match = re.match(b"WM_NAME\(\w+\) = (?P<name>.+)$", stdout)
if match != None:
return match.group("name").strip(b'"')
return None
if __name__ == "__main__":
print(get_active_window_title())
The advantage is it works without additional modules. If you want it to work across multiple platforms, it's just a matter of changing the command and regex strings to get the data you want based on the platform (with the standard if/else platform detection shown above sys.platform).
On a side note: import wnck only works with python2.x when installed with "sudo apt-get install python-wnck", since I was using python3.x the only option was pypie which I have not tested. Hope this helps someone else.
There's really no need to import any external dependency for tasks like this. Python comes with a pretty neat foreign function interface - ctypes, which allows for calling C shared libraries natively. It even includes specific bindings for the most common Win32 DLLs.
E.g. to get the PID of the foregorund window:
import ctypes
from ctypes import wintypes
user32 = ctypes.windll.user32
h_wnd = user32.GetForegroundWindow()
pid = wintypes.DWORD()
user32.GetWindowThreadProcessId(h_wnd, ctypes.byref(pid))
print(pid.value)
In Linux under X11:
xdo_window_id = os.popen('xdotool getactivewindow').read()
print('xdo_window_id:', xdo_window_id)
will print the active window ID in decimal format:
xdo_window_id: 67113707
Note xdotool must be installed first:
sudo apt install xdotool
Note wmctrl uses hexadecimal format for window ID.
This only works on windows
import win32gui
import win32process
def get_active_executable_name():
try:
process_id = win32process.GetWindowThreadProcessId(
win32gui.GetForegroundWindow()
)
return ".".join(psutil.Process(process_id[-1]).name().split(".")[:-1])
except Exception as exception:
return None
I'll recommend checking out this answer for making it work on linux, mac and windows.
I'd been facing same problem with linux interface (Lubuntu 20).
What I do is using wmctrl and execute it with shell command from python.
First, Install wmctrl
sudo apt install wmctrl
Then, Add this code :
import os
os.system('wmctrl -a "Mozilla Firefox"')
ref wmctrl :
https://askubuntu.com/questions/21262/shell-command-to-bring-a-program-window-in-front-of-another
In Linux:
If you already have installed xdotool, you can just use:
from subprocess import run
def get__focused_window():
return run(['xdotool', 'getwindowfocus', 'getwindowpid', 'getwindowname'], capture_output=True).stdout.decode('utf-8').split()
While I was writing this answer I've realised that there were also:
A reference about "xdotool" on comments
& another slightly similar "xdotool" answer
So, I've decided to mention them here, too.
Just wanted to add in case it helps, I have a function for my program (It's a software for my PC's lighting I have this simple few line function:
def isRunning(process_name):
foregroundWindow = GetWindowText(GetForegroundWindow())
return process_name in foregroundWindow
Try using wxPython:
import wx
wx.GetActiveWindow()
Does anyone know a way to get the amount of space available on a Windows (Samba) share via Python 2.6 with its standard library? (also running on Windows)
e.g.
>>> os.free_space("\\myshare\folder") # return free disk space, in bytes
1234567890
If PyWin32 is available:
free, total, totalfree = win32file.GetDiskFreeSpaceEx(r'\\server\share')
Where free is a amount of free space available to the current user, and totalfree is amount of free space total. Relevant documentation: PyWin32 docs, MSDN.
If PyWin32 is not guaranteed to be available, then for Python 2.5 and higher there is ctypes module in stdlib. Same function, using ctypes:
import sys
from ctypes import *
c_ulonglong_p = POINTER(c_ulonglong)
_GetDiskFreeSpace = windll.kernel32.GetDiskFreeSpaceExW
_GetDiskFreeSpace.argtypes = [c_wchar_p, c_ulonglong_p, c_ulonglong_p, c_ulonglong_p]
def GetDiskFreeSpace(path):
if not isinstance(path, unicode):
path = path.decode('mbcs') # this is windows only code
free, total, totalfree = c_ulonglong(0), c_ulonglong(0), c_ulonglong(0)
if not _GetDiskFreeSpace(path, pointer(free), pointer(total), pointer(totalfree)):
raise WindowsError
return free.value, total.value, totalfree.value
Could probably be done better but I'm not really familiar with ctypes.
The standard library has the os.statvfs() function, but unfortunately it's only available on Unix-like platforms.
In case there is some cygwin-python maybe it would work there?
Does anyone know how I would go about detected what bit version Windows is under Python. I need to know this as a way of using the right folder for Program Files.
Many thanks
I think the best solution to the problem has been posted by Mark Ribau.
The best answer to the question for Python 2.7 and newer is:
def is_os_64bit():
return platform.machine().endswith('64')
On windows the cross-platform-function platform.machine() internally uses the environmental variables used in Matthew Scoutens answer.
I found the following values:
WinXP-32: x86
Vista-32: x86
Win7-64: AMD64
Debian-32: i686
Debian-64: x86_64
For Python 2.6 and older:
def is_windows_64bit():
if 'PROCESSOR_ARCHITEW6432' in os.environ:
return True
return os.environ['PROCESSOR_ARCHITECTURE'].endswith('64')
To find the Python interpreter bit version I use:
def is_python_64bit():
return (struct.calcsize("P") == 8)
I guess you should look in os.environ['PROGRAMFILES'] for the program files folder.
platform module -- Access to underlying platform’s identifying data
>>> import platform
>>> platform.architecture()
('32bit', 'WindowsPE')
On 64-bit Windows, 32-bit Python returns:
('32bit', 'WindowsPE')
And that means that this answer, even though it has been accepted, is incorrect. Please see some of the answers below for options that may work for different situations.
Came here searching for properly detecting if running on 64bit windows, compiling all the above into something more concise.
Below you will find a function to test if running on 64bit windows, a function to get the 32bit Program Files folder, and a function to get the 64bit Program Files folder; all regardless of running 32bit or 64bit python. When running 32bit python, most things report as if 32bit when running on 64bit, even os.environ['PROGRAMFILES'].
import os
def Is64Windows():
return 'PROGRAMFILES(X86)' in os.environ
def GetProgramFiles32():
if Is64Windows():
return os.environ['PROGRAMFILES(X86)']
else:
return os.environ['PROGRAMFILES']
def GetProgramFiles64():
if Is64Windows():
return os.environ['PROGRAMW6432']
else:
return None
Note: Yes, this is a bit hackish. All other methods that "should just work", do not work when running 32bit Python on 64bit Windows (at least for the various 2.x and 3.x versions I have tried).
Edits:
2011-09-07 - Added a note about why only this hackish method works properly.
def os_platform():
true_platform = os.environ['PROCESSOR_ARCHITECTURE']
try:
true_platform = os.environ["PROCESSOR_ARCHITEW6432"]
except KeyError:
pass
#true_platform not assigned to if this does not exist
return true_platform
http://blogs.msdn.com/b/david.wang/archive/2006/03/26/howto-detect-process-bitness.aspx
Many of these proposed solutions, such as platform.architecture(), fail because their results depend on whether you are running 32-bit or 64-bit Python.
The only reliable method I have found is to check for the existence of os.environ['PROGRAMFILES(X86)'], which is unfortunately hackish.
You should be using environment variables to access this. The program files directory is stored in the environment variable PROGRAMFILES on x86 Windows, the 32-bit program files is directory is stored in the PROGRAMFILES(X86) environment variable, these can be accessed by using os.environ('PROGRAMFILES').
Use sys.getwindowsversion() or the existence of PROGRAMFILES(X86) (if 'PROGRAMFILES(X86)' in os.environ) to determine what version of Windows you are using.
Following this documentation, try this code:
is_64bits = sys.maxsize > 2**32
Im aware that in comments of the question this method was already used.
This is the method the .net framework uses:
import ctypes
def is64_bit_os():
""" Returns wethever system is a 64bit operating system"""
is64bit = ctypes.c_bool()
handle = ctypes.windll.kernel32.GetCurrentProcess() # should be -1, because the current process is currently defined as (HANDLE) -1
success = ctypes.windll.kernel32.IsWow64Process(handle, ctypes.byref(is64bit)) #should return 1
return (success and is64bit).value
print(is64_bit_os())
I just found another way to do this, which may be useful in some situations.
import subprocess
import os
def os_arch():
os_arch = '32-bit'
if os.name == 'nt':
output = subprocess.check_output(['wmic', 'os', 'get', 'OSArchitecture'])
os_arch = output.split()[1]
else:
output = subprocess.check_output(['uname', '-m'])
if 'x86_64' in output:
os_arch = '64-bit'
else:
os_arch = '32-bit'
return os_arch
print 'os_arch=%s' % os_arch()
I tested this code in the following environments:
Ubuntu 16.04 + Python 2.7.12
Mac OS Sierra + Python 2.7.11
Windows 7 Pro 32-bit + Python 2.7.5 (32-bit)
Windows 10 Home 64-bit + Python 2.7.13 (32-bit)
The subject lines asks about detecting 64 or 32bit OS, while the body talks about determining the location of ProgramFiles. The latter has a couple of workable answers here. I'd like to add another solution generalized to handle StartMenu, Desktop, etc. as well as ProgramFiles: How to get path of Start Menu's Programs directory?
When you need to find out things about windows system, it is usually somewhere in the registry, according to MS documentation, you should look at (http://support.microsoft.com/kb/556009) this key value:
HKLM\HARDWARE\DESCRIPTION\System\CentralProcessor\0
and if it is:
0x00000020 (32 in decimal)
It is a 32 bit machine.
64-bit versions of Windows use something called registry redirection and reflection keys. There is a compatibility layer called WoW64 which enables compatibility of 32-bit applications. Starting from Windows 7 and Windows Server 2008 R2 WoW64 registry keys are not longer reflected but shared. You can read about it here:
registry-reflection: msdn.microsoft.com/en-us/library/aa384235(v=vs.85).aspx
affected-keys: msdn.microsoft.com/en-us/library/aa384253(v=vs.85).aspx
wikipedia: en.wikipedia.org/wiki/WoW64
All you need to do is detect existence of those keys. You can use _winreg for that. Use try: and try opening key, example:
try:
aReg = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE,"SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run")
import _winreg
def get_registry_value(key, subkey, value):
key = getattr(_winreg, key)
handle = _winreg.OpenKey(key, subkey )
(value, type) = _winreg.QueryValueEx(handle, value)
return value
windowsbit=cputype = get_registry_value(
"HKEY_LOCAL_MACHINE",
"SYSTEM\\CurrentControlSet\Control\\Session Manager\\Environment",
"PROCESSOR_ARCHITECTURE")
print windowsbit
just run this code
if you are working on 64 bit windows machine this will print AMD64
or if you are working on 32 bit it will print AMD32
i hope this code can help to solve this problem fully
This works for me in the Python versions I use: 2.7 and 2.5.4
import win32com.client
import _winreg
shell = win32com.client.Dispatch('WScript.Shell')
proc_arch = shell.ExpandEnvironmentStrings(r'%PROCESSOR_ARCHITECTURE%').lower()
if proc_arch == 'x86':
print "32 bit"
elif proc_arch == 'amd64':
print "64 bit"
else:
raise Exception("Unhandled arch: %s" % proc_arch)
Just to update this old thread - it looks like the platform module reports the correct architecture now (at least, in Python 2.7.8):
c:\python27\python.exe -c "import platform; print platform.architecture(), platform.python_version()"
('32bit', 'WindowsPE') 2.7.6
c:\home\python278-x64\python.exe -c "import platform; print platform.architecture(), platform.python_version()"
('64bit', 'WindowsPE') 2.7.8
(sorry I don't have the rep to comment on the first answer which still claims to be wrong :)
import platform
platform.architecture()[0]
It will return '32bit' or '64bit' depending on system architecture.
The solution posted by Alexander Brüsch is the correct solution, but it has a bug that only reveals itself on python3.x. He neglected to cast the returned value from GetCurrentProcess() to a HANDLE type. Passing a simple integer as the first parameter of IsWow64Process() returns 0 (which is an error flag from win32api). Also, Alexander incorrectly handles the return statement (success has no .value attribute).
For those who stumble on this thread, here is the corrected code:
import ctypes
def is64_bit_os():
"""Returns True if running 32-bit code on 64-bit operating system"""
is64bit = ctypes.c_bool()
handle = ctypes.wintypes.HANDLE(ctypes.windll.kernel32.GetCurrentProcess())
success = ctypes.windll.kernel32.IsWow64Process(handle, ctypes.byref(is64bit))
return success and is64bit.value
print(is64_bit_os())
There is a function named machine in platform module. I installed both Python3.8 32-bit and 64-bit versions on the same 64-bit machine with 64-bit Windows 10 and here is what I found:
And it looks like platform.machine returns machine architecture without bothering what type of python is installed. so here is my
final compilation
import platform
def is_64bit():
return platform.machine().endswith('64')
Most of the answers here are incorrect :/
Here is a simple translation of the well known method used in CMD and this is how microsoft do it too.
import os
_os_bit=64
if os.environ.get('PROCESSOR_ARCHITECTURE').lower() == 'x86' and os.environ.get('PROCESSOR_ARCHITEW6432') is None: _os_bit=32
print(_os_bit)
but remember: Windows 10 on ARM includes an x86-on-ARM64 emulation, so the possible values for PROCESSOR_ARCHITECTURE are: AMD64 or IA64 or ARM64 or x86
A solution, putting together the options from the links below and using os module:
import os
#next save the response from the command prompt saved to a file
window = os.system('PowerShell.exe "gwmi win32_operatingsystem | select osarchitecture" > prompt.txt')
#next read the file
f = open('prompt.txt','r')
windowsos = f.readlines()
f.close()
print(windowsos[3][:-1])
https://datatofish.com/command-prompt-python/
https://www.radishlogic.com/windows/how-to-check-if-your-windows-10-is-64-bit-or-32-bit/
https://www.tutorialspoint.com/how-to-run-a-powershell-script-from-the-command-prompt
import struct
def is64Windows():
return struct.calcsize('P') * 8 == 64
There should be a directory under Windows 64bit, a Folder called \Windows\WinSxS64 for 64 bit, under Windows 32bit, it's WinSxS.
How can I use win32 API in Python?
What is the best and easiest way to do it?
Can you please provide some examples?
PyWin32 is the way to go - but how to use it? One approach is to begin with a concrete problem you're having and attempting to solve it. PyWin32 provides bindings for the Win32 API functions for which there are many, and you really have to pick a specific goal first.
In my Python 2.5 installation (ActiveState on Windows) the win32 package has a Demos folder packed with sample code of various parts of the library.
For example, here's CopyFileEx.py:
import win32file, win32api
import os
def ProgressRoutine(TotalFileSize, TotalBytesTransferred, StreamSize, StreamBytesTransferred,
StreamNumber, CallbackReason, SourceFile, DestinationFile, Data):
print Data
print TotalFileSize, TotalBytesTransferred, StreamSize, StreamBytesTransferred, StreamNumber, CallbackReason, SourceFile, DestinationFile
##if TotalBytesTransferred > 100000:
## return win32file.PROGRESS_STOP
return win32file.PROGRESS_CONTINUE
temp_dir=win32api.GetTempPath()
fsrc=win32api.GetTempFileName(temp_dir,'cfe')[0]
fdst=win32api.GetTempFileName(temp_dir,'cfe')[0]
print fsrc, fdst
f=open(fsrc,'w')
f.write('xxxxxxxxxxxxxxxx\n'*32768)
f.close()
## add a couple of extra data streams
f=open(fsrc+':stream_y','w')
f.write('yyyyyyyyyyyyyyyy\n'*32768)
f.close()
f=open(fsrc+':stream_z','w')
f.write('zzzzzzzzzzzzzzzz\n'*32768)
f.close()
operation_desc='Copying '+fsrc+' to '+fdst
win32file.CopyFileEx(fsrc, fdst, ProgressRoutine, operation_desc, False, win32file.COPY_FILE_RESTARTABLE)
It shows how to use the CopyFileEx function with a few others (such as GetTempPath and GetTempFileName). From this example you can get a "general feel" of how to work with this library.
PyWin32, as mentioned by #chaos, is probably the most popular choice; the alternative is ctypes which is part of Python's standard library. For example, print ctypes.windll.kernel32.GetModuleHandleA(None) will show the module-handle of the current module (EXE or DLL). A more extensive example of using ctypes to get at win32 APIs is here.
The important functions that you can to use in win32 Python are the message boxes, this is classical example of OK or Cancel.
result = win32api.MessageBox(None,"Do you want to open a file?", "title",1)
if result == 1:
print 'Ok'
elif result == 2:
print 'cancel'
The collection:
win32api.MessageBox(0,"msgbox", "title")
win32api.MessageBox(0,"ok cancel?", "title",1)
win32api.MessageBox(0,"abort retry ignore?", "title",2)
win32api.MessageBox(0,"yes no cancel?", "title",3)
Is there a way to dynamically call an Objective C function from Python?
For example, On the mac I would like to call this Objective C function
[NSSpeechSynthesizer availableVoices]
without having to precompile any special Python wrapper module.
As others have mentioned, PyObjC is the way to go. But, for completeness' sake, here's how you can do it with ctypes, in case you need it to work on versions of OS X prior to 10.5 that do not have PyObjC installed:
import ctypes
import ctypes.util
# Need to do this to load the NSSpeechSynthesizer class, which is in AppKit.framework
appkit = ctypes.cdll.LoadLibrary(ctypes.util.find_library('AppKit'))
objc = ctypes.cdll.LoadLibrary(ctypes.util.find_library('objc'))
objc.objc_getClass.restype = ctypes.c_void_p
objc.sel_registerName.restype = ctypes.c_void_p
objc.objc_msgSend.restype = ctypes.c_void_p
objc.objc_msgSend.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
# Without this, it will still work, but it'll leak memory
NSAutoreleasePool = objc.objc_getClass('NSAutoreleasePool')
pool = objc.objc_msgSend(NSAutoreleasePool, objc.sel_registerName('alloc'))
pool = objc.objc_msgSend(pool, objc.sel_registerName('init'))
NSSpeechSynthesizer = objc.objc_getClass('NSSpeechSynthesizer')
availableVoices = objc.objc_msgSend(NSSpeechSynthesizer, objc.sel_registerName('availableVoices'))
count = objc.objc_msgSend(availableVoices, objc.sel_registerName('count'))
voiceNames = [
ctypes.string_at(
objc.objc_msgSend(
objc.objc_msgSend(availableVoices, objc.sel_registerName('objectAtIndex:'), i),
objc.sel_registerName('UTF8String')))
for i in range(count)]
print voiceNames
objc.objc_msgSend(pool, objc.sel_registerName('release'))
It ain't pretty, but it gets the job done. The final list of available names is stored in the voiceNames variable above.
2012-4-28 Update: Fixed to work in 64-bit Python builds by making sure all parameters and return types are passed as pointers instead of 32-bit integers.
Since OS X 10.5, OS X has shipped with the PyObjC bridge, a Python-Objective-C bridge. It uses the BridgeSupport framework to map Objective-C frameworks to Python. Unlike, MacRuby, PyObjC is a classical bridge--there is a proxy object on the python side for each ObjC object and visa versa. The bridge is pretty seamless, however, and its possible to write entire apps in PyObjC (Xcode has some basic PyObjC support, and you can download the app and file templates for Xcode from the PyObjC SVN at the above link). Many folks use it for utilities or for app-scripting/plugins. Apple's developer site also has an introduction to developing Cocoa applications with Python via PyObjC which is slightly out of date, but may be a good overview for you.
In your case, the following code will call [NSSpeechSynthesizer availableVoices]:
from AppKit import NSSpeechSynthesizer
NSSpeechSynthesizer.availableVoices()
which returns
(
"com.apple.speech.synthesis.voice.Agnes",
"com.apple.speech.synthesis.voice.Albert",
"com.apple.speech.synthesis.voice.Alex",
"com.apple.speech.synthesis.voice.BadNews",
"com.apple.speech.synthesis.voice.Bahh",
"com.apple.speech.synthesis.voice.Bells",
"com.apple.speech.synthesis.voice.Boing",
"com.apple.speech.synthesis.voice.Bruce",
"com.apple.speech.synthesis.voice.Bubbles",
"com.apple.speech.synthesis.voice.Cellos",
"com.apple.speech.synthesis.voice.Deranged",
"com.apple.speech.synthesis.voice.Fred",
"com.apple.speech.synthesis.voice.GoodNews",
"com.apple.speech.synthesis.voice.Hysterical",
"com.apple.speech.synthesis.voice.Junior",
"com.apple.speech.synthesis.voice.Kathy",
"com.apple.speech.synthesis.voice.Organ",
"com.apple.speech.synthesis.voice.Princess",
"com.apple.speech.synthesis.voice.Ralph",
"com.apple.speech.synthesis.voice.Trinoids",
"com.apple.speech.synthesis.voice.Vicki",
"com.apple.speech.synthesis.voice.Victoria",
"com.apple.speech.synthesis.voice.Whisper",
"com.apple.speech.synthesis.voice.Zarvox"
)
(a bridged NSCFArray) on my SL machine.
Mac OS X from 10.5 onward has shipped with Python and the objc module that will let you do what you want.
An example:
from Foundation import *
thing = NSKeyedUnarchiver.unarchiveObjectWithFile_(some_plist_file)
You can find more documentation here.
You probably want PyObjC. That said, I've never actually used it myself (I've only ever seen demos), so I'm not certain that it will do what you need.