I am using the pywin32's demo win32gui_taskbar.py & extending it to take a screen shot of the window if a particular window is currently the active/foreground window.
The screen_shot
function is called whenever the user left click's on the system tray icon
import win32api, win32service
import win32gui, win32ui
import win32con, winerror
import sys, os
import time
from PIL import Image
class MainWindow:
def __init__(self):
msg_TaskbarRestart = win32gui.RegisterWindowMessage("TaskbarCreated");
message_map = {
msg_TaskbarRestart: self.OnRestart,
win32con.WM_DESTROY: self.OnDestroy,
win32con.WM_COMMAND: self.OnCommand,
win32con.WM_USER+20 : self.OnTaskbarNotify,
}
# Register the Window class.
wc = win32gui.WNDCLASS()
hinst = wc.hInstance = win32api.GetModuleHandle(None)
wc.lpszClassName = "PythonTaskbarDemo"
wc.style = win32con.CS_VREDRAW | win32con.CS_HREDRAW;
wc.hCursor = win32api.LoadCursor( 0, win32con.IDC_ARROW )
wc.hbrBackground = win32con.COLOR_WINDOW
wc.lpfnWndProc = message_map # could also specify a wndproc.
# Don't blow up if class already registered to make testing easier
try:
classAtom = win32gui.RegisterClass(wc)
except win32gui.error, err_info:
if err_info.winerror!=winerror.ERROR_CLASS_ALREADY_EXISTS:
raise
# Create the Window.
style = win32con.WS_OVERLAPPED | win32con.WS_SYSMENU
self.hwnd = win32gui.CreateWindow( wc.lpszClassName, "Taskbar Demo", style, \
0, 0, win32con.CW_USEDEFAULT, win32con.CW_USEDEFAULT, \
0, 0, hinst, None)
win32gui.UpdateWindow(self.hwnd)
self._DoCreateIcons()
def _DoCreateIcons(self):
# Try and find a custom icon
hinst = win32api.GetModuleHandle(None)
iconPathName = os.path.abspath(os.path.join( os.path.split(sys.executable)[0], "pyc.ico" ))
if not os.path.isfile(iconPathName):
# Look in DLLs dir, a-la py 2.5
iconPathName = os.path.abspath(os.path.join( os.path.split(sys.executable)[0], "DLLs",
"pyc.ico" ))
if not os.path.isfile(iconPathName):
# Look in the source tree.
iconPathName = os.path.abspath(os.path.join( os.path.split(sys.executable)[0], "..\\PC\
\pyc.ico" ))
if os.path.isfile(iconPathName):
icon_flags = win32con.LR_LOADFROMFILE | win32con.LR_DEFAULTSIZE
hicon = win32gui.LoadImage(hinst, iconPathName, win32con.IMAGE_ICON, 0, 0, icon_flags)
else:
print "Can't find a Python icon file - using default"
hicon = win32gui.LoadIcon(0, win32con.IDI_APPLICATION)
flags = win32gui.NIF_ICON | win32gui.NIF_MESSAGE | win32gui.NIF_TIP
nid = (self.hwnd, 0, flags, win32con.WM_USER+20, hicon, "Python Demo")
try:
win32gui.Shell_NotifyIcon(win32gui.NIM_ADD, nid)
except win32gui.error:
# This is common when windows is starting, and this code is hit
# before the taskbar has been created.
print "Failed to add the taskbar icon - is explorer running?"
# but keep running anyway - when explorer starts, we get the
# TaskbarCreated message.
def OnRestart(self, hwnd, msg, wparam, lparam):
print "In onrestart"
self._DoCreateIcons()
def OnDestroy(self, hwnd, msg, wparam, lparam):
nid = (self.hwnd, 0)
win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, nid)
win32gui.PostQuitMessage(0) # Terminate the app.
def OnTaskbarNotify(self, hwnd, msg, wparam, lparam):
if lparam==win32con.WM_LBUTTONUP:
print "You clicked me."
print win32gui.GetWindowText(hwnd)
print ".." + win32gui.GetWindowText(win32gui.GetForegroundWindow())
self.screen_shoot()
elif lparam==win32con.WM_LBUTTONDBLCLK:
print "You double-clicked me - goodbye"
win32gui.DestroyWindow(self.hwnd)
elif lparam==win32con.WM_RBUTTONUP:
print "You right clicked me."
return 1
def screen_shot(self):
wndh = win32gui.GetForegroundWindow()
if "Notepad" in win32gui.GetWindowText(wndh):
print "Inside if"
hwndDC = win32gui.GetWindowDC(wndh)
mfcDC = win32ui.CreateDCFromHandle(hwndDC)
saveDC = mfcDC.CreateCompatibleDC()
saveBitMap = win32ui.CreateBitmap()
left, top, right, bot = win32gui.GetWindowRect(wndh)
w = right - left
h = bot - top
saveBitMap.CreateCompatibleBitmap(mfcDC, w, h)
saveDC.SelectObject(saveBitMap)
saveDC.BitBlt((0, 0), (w, h), mfcDC, (0, 0), win32con.SRCCOPY)
bmpinfo = saveBitMap.GetInfo()
bmpstr = saveBitMap.GetBitmapBits(True)
im = Image.frombuffer('RGB', (bmpinfo['bmWidth'],
bmpinfo['bmHeight']),
bmpstr, 'raw', 'BGRX', 0, 1)
win32gui.DeleteObject(saveBitMap.GetHandle())
saveDC.DeleteDC()
mfcDC.DeleteDC()
win32gui.ReleaseDC(wndh, hwndDC)
im.save("c:\screenshot" + str(int(time.time())) + ".png")
def OnCommand(self, hwnd, msg, wparam, lparam):
id = win32api.LOWORD(wparam)
if id == 1023:
import win32gui_dialog
win32gui_dialog.DemoModal()
elif id == 1024:
print "Hello"
elif id == 1025:
print "Goodbye"
win32gui.DestroyWindow(self.hwnd)
else:
print "Unknown command -", id
def main():
w=MainWindow()
win32gui.PumpMessages()
if __name__=='__main__':
main()
In the above code the control never goes inside the if condition in the screen_shot function. How can get this program working? The same screen shot logic works fine when its run as a normal application.
Answering own question...
Just realised task bar is also another "window" & upon clicking the system tray icon the focus is shifted to the task window. Which is what is reported as the foreground window & get window text for this window is blank string. I verified this by slightly modifying my program.
In the MainWindow class init function after the win32gui.Shell_NotifyIcon(win32gui.NIM_ADD, nid)
icon init call added the following code to verify it
while True:
time.sleep(0.1)
wndh = win32gui.GetForegroundWindow()
print "windowndh" + str(wndh)
if "Notepad" in win32gui.GetWindowText(wndh):
# Take screen shot
print "Inside if"