How would I programmatically activate a window in Windows using Python? I'm sending keystrokes to it and at the moment I'm just making sure it's the last application us开发者_运维知识库ed then sending the keystroke Alt+Tab to switch over to it from the DOS console. Is there a better way (since I've learned by experience that this way is by no means foolproof)?
You can use the win32gui module to do that. First you need to get a valid handle on your window. You can use the win32gui.FindWindow
if you know the window class name or the exact title. If not, you can enumerate the windows with the win32gui.EnumWindows
and try to find the right one.
Once you have the handle, you can call the win32gui.SetForegroundWindow
with the handle. It will activate the window and will be ready for getting your keystrokes.
See an example below. I hope it helps
import win32gui
import re
class WindowMgr:
"""Encapsulates some calls to the winapi for window management"""
def __init__ (self):
"""Constructor"""
self._handle = None
def find_window(self, class_name, window_name=None):
"""find a window by its class_name"""
self._handle = win32gui.FindWindow(class_name, window_name)
def _window_enum_callback(self, hwnd, wildcard):
"""Pass to win32gui.EnumWindows() to check all the opened windows"""
if re.match(wildcard, str(win32gui.GetWindowText(hwnd))) is not None:
self._handle = hwnd
def find_window_wildcard(self, wildcard):
"""find a window whose title matches the wildcard regex"""
self._handle = None
win32gui.EnumWindows(self._window_enum_callback, wildcard)
def set_foreground(self):
"""put the window in the foreground"""
win32gui.SetForegroundWindow(self._handle)
w = WindowMgr()
w.find_window_wildcard(".*Hello.*")
w.set_foreground()
Pywinauto and SWAPY will probably require the least effort to set the focus of a window.
Use SWAPY to auto-generate the python code necessary to retrieve the window object, e.g.:
import pywinauto
# SWAPY will record the title and class of the window you want activated
app = pywinauto.application.Application()
t, c = u'WINDOW SWAPY RECORDS', u'CLASS SWAPY RECORDS'
handle = pywinauto.findwindows.find_windows(title=t, class_name=c)[0]
# SWAPY will also get the window
window = app.window_(handle=handle)
# this here is the only line of code you actually write (SWAPY recorded the rest)
window.SetFocus()
If by chance other windows are in front of the window of interest, not a problem. This additional code or this will make sure it is shown before running the above code:
# minimize then maximize to bring this window in front of all others
window.Minimize()
window.Maximize()
# now you can set its focus
window.SetFocus()
import ctypes, platform
if platform.system() == 'Windows':
Active_W = ctypes.windll.user32.GetActiveWindow()
ctypes.windll.user32.SetWindowPos(Active_W,0,0,0,0,0,0x0002|0x0001)
Here we go. you just need to store the value of the active window.
Pip install keyboard. Before you set foreground window, simulate a keyboard to esc that is keyboard.send('esc') You may want to do it three times for either of the following:
- Sidebar
- Windows key overlay
- Task manager which is always on top
Using SetWindowPos
or SetForegroundWindow
might NOT be enough if the window was minified aka IsIconic
! We can use ShowWindow
with SW_RESTORE
(9):
import ctypes
def activate_window(hwnd):
user32 = ctypes.windll.user32
user32.SetForegroundWindow(hwnd)
if user32.IsIconic(hwnd):
user32.ShowWindow(hwnd, 9)
Depending on how you identify the desired window there are some ways to get the hwnd
aka window handle.
You could loop through all the windows to find the right handle according to the pid
via user32.GetWindowThreadProcessId
or by window name with user32.GetWindowTextW
To get ProcessIds you could use Windows built-in wmic
. There are loads of other nifty things you can do with it. (all attributes: wmic process get /?
) (get handle
is broken tho) For example:
def get_pids(proc_name):
out = subprocess.check_output('wmic process where Name="%s" get ProcessId' % proc_name)
pids = out.decode().strip().split()[1:]
if not pids:
raise WindowsError('Could not find pids for process')
return [int(pid) for pid in pids]
GUI Application to keep windows active
Python3
install library
pip install pywin32
save below code as alive.pyw
file
from ctypes import windll, wintypes, byref, c_uint, sizeof, Structure
import tkinter as tk
import ctypes
import sys
import threading
import time
import win32api
import win32con
stop_threads = True
SET_IDLE_TIME = 40 #in seconds
tm1 = time.time()
value = 0
class LASTINPUTINFO(Structure):
_fields_ = [
('cbSize', c_uint),
('dwTime', c_uint),
]
def get_idle_duration():
global value, tm1
lastInputInfo = LASTINPUTINFO()
lastInputInfo.cbSize = sizeof(lastInputInfo)
windll.user32.GetLastInputInfo(byref(lastInputInfo))
# millis = 4294967 - lastInputInfo.dwTime - windll.kernel32.GetTickCount()
# print(windll.kernel32.GetTickCount(), lastInputInfo.dwTime, sizeof(lastInputInfo), millis)
tm2 = time.time() - tm1
last_idel_time = lastInputInfo.dwTime
# print()
if value != last_idel_time:
value = last_idel_time
tm2 = time.time() - time.time()
tm1 = time.time()
# print("time:", tm1)
return tm2
def press_key_2():
global stop_threads, tm1
while True:
if not stop_threads:
break
idle_time = get_idle_duration() #seconds
# print(idle_time)
g = float("{:.2f}".format(idle_time))
st = str(g) + " / " + str(SET_IDLE_TIME)
var.set(st)
time.sleep(0.1)
if idle_time < SET_IDLE_TIME:
continue
print("in ideal state pressing cltr")
win32api.keybd_event(ord('x'), 0, win32con.KEYEVENTF_EXTENDEDKEY, 0)
tm1 = time.time()
#---------------- Monitor threads ------------------------------
t1 = threading.Thread(target=press_key_2, name='t1')
t1.daemon = True
#----------------- TK functions ----------------------
def display_on():
global tk, t1, stop_threads
stop_threads = True
print("Always On")
ctypes.windll.kernel32.SetThreadExecutionState(0x80000002)
root.iconify()
t1.start()
# t2.start()
def display_reset():
print("quit pressed")
global stop_threads
stop_threads = False
ctypes.windll.kernel32.SetThreadExecutionState(0x80000000)
sys.exit(0)
root = tk.Tk()
root.geometry("200x110")
root.title("Devil")
frame = tk.Frame(root)
frame.pack()
var = tk.StringVar()
var_idle = tk.StringVar()
label = tk.Label(frame, textvariable = var)#, bd = 5, justify = tk.RIGHT, padx = 10, pady = 10)
label_idle = tk.Label(frame,textvariable = var_idle)
var_idle.set("Idle Time")
var.set("-")
button = tk.Button(frame,
text="Quit",
fg="red",
command=display_reset)
slogan = tk.Button(frame,
text="Always ON",
command=display_on)
label_idle.pack(side=tk.BOTTOM,padx=15, pady=13)
label.pack(side=tk.BOTTOM,padx=15, pady=5)
slogan.pack(side=tk.LEFT,padx=15, pady=5)
button.pack(side=tk.LEFT,padx=15, pady=5)
root.mainloop()
ctypes.windll.kernel32.SetThreadExecutionState(0x80000000)
To add on @luc's answer, following is how the code would be more verbose about the handle it selected when multiple windows exist:
After pip install pywin32
, run
import win32gui
import re
class WindowMgr:
"""Encapsulates some calls to the winapi for window management"""
def __init__ (self):
"""Constructor"""
self._handle = None
self._handles = []
def find_window(self, class_name, window_name=None):
"""find a window by its class_name"""
self._handle = win32gui.FindWindow(class_name, window_name)
def _window_enum_callback(self, hwnd, wildcard):
"""Pass to win32gui.EnumWindows() to check all the opened windows"""
if re.match(wildcard, str(win32gui.GetWindowText(hwnd))) is not None:
self._handles.append(hwnd)
self._handle = hwnd
def find_window_wildcard(self, wildcard):
"""find a window whose title matches the wildcard regex"""
self._handle = None
self._handles = []
win32gui.EnumWindows(self._window_enum_callback, wildcard)
self.set_handle()
def set_foreground(self):
"""put the window in the foreground"""
if self._handle != None:
win32gui.SetForegroundWindow(self._handle)
else:
print("No handle is selected, couldn't set focus")
def set_handle(self):
"""get one handle to operate on from all the matched handles"""
if len(self._handles) < 1:
print("Matched no window")
return False
if len(self._handles) > 1:
print("Selecting the first handle of multiple windows:")
else: # len(self._handles) == 1:
print("Matched a single window:")
self.print_matches()
self._handle = self._handles[0]
return True
def print_matches(self):
"""print the title of each matched handle"""
for hwnd in self._handles:
print("- " + str(win32gui.GetWindowText(hwnd)))
w = WindowMgr()
w.find_window_wildcard(".*Hello.*")
w.set_foreground()
Note: I couldn't make the addition by editing @luc's answer as its suggested edit queue is full.
精彩评论