Активация окна Python
Как мне программно активировать окно в Windows, используя Python? Я посылаю на него нажатия клавиш, и в данный момент я просто проверяю, что это последнее использовавшееся приложение, а затем отправляю нажатия клавиш Alt+Tab для переключения на него с консоли DOS. Есть ли лучший способ (так как я на собственном опыте узнал, что этот путь ни в коем случае не является надежным)?
8 ответов
Вы можете использовать модуль win32gui для этого. Для начала вам нужно получить действительный дескриптор вашего окна. Вы можете использовать win32gui.FindWindow
если вы знаете имя класса окна или точное название. Если нет, вы можете перечислить окна с win32gui.EnumWindows
и попытаться найти правильный.
Когда у вас есть ручка, вы можете позвонить win32gui.SetForegroundWindow
с ручкой. Он активирует окно и будет готов к нажатию клавиш.
Смотрите пример ниже. Я надеюсь, что это помогает
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 и SWAPY, вероятно, потребуют минимальных усилий, чтобы установить фокус окна.
Используйте SWAPY для автоматической генерации кода Python, необходимого для извлечения объекта окна, например:
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()
Если случайно другие окна находятся перед интересующим окном, не проблема. Этот дополнительный код или это убедитесь, что он отображается перед запуском вышеуказанного кода:
# 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)
Вот так. вам просто нужно сохранить значение активного окна.
Пип установить клавиатуру. Перед тем, как установить окно переднего плана, имитируйте клавиатуру для esc, которая называется keyboard.send('esc'). Вы можете сделать это три раза для любого из следующих действий:
- Боковая панель
- Наложение клавиш Windows
- Диспетчер задач, который всегда на высоте
Поздняя запись, но это сработало для меня. Ключом к выполнению этой работы было переключение фокуса с текущего окна с помощьюSendKeys()
функция в скрипте. Это было протестировано в Windows 11 с использованием Python 3.10.6:
import re
import win32gui
import win32com.client
class WindowMgr:
""" Encapsulates calls to the winapi for window management
Forces context window to take focus
Based on:
- https://stackoverflow.com/questions/2090464/python-window-activation
- https://stackoverflow.com/questions/14295337/win32gui-setactivewindow-error-the-specified-procedure-could-not-be-found
"""
def __init__ (self):
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)
return self
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)
return self
def set_foreground(self):
"""put the window in the foreground"""
shell = win32com.client.Dispatch("WScript.Shell")
shell.SendKeys('%') # left shift key sent, this shifts focus from current window
win32gui.SetForegroundWindow(self._handle)
Простое использование этой функции, которая может использовать цепочку, выглядит следующим образом:
WindowMgr().find_window_wildcard("My Window Title").set_foreground()
Чтобы добавить ответ @luc, ниже показано, как код будет более подробным о выбранном дескрипторе, когда существует несколько окон:
После
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()
Примечание. Я не смог внести дополнение, отредактировав ответ @luc, так как его предложенная очередь редактирования заполнена.
С использованием
SetWindowPos
или же
SetForegroundWindow
может НЕ хватить, если окно было минимизировано , т.е.
IsIconic
! Мы можем использовать
ShowWindow
с SW_RESTORE
(9):
import ctypes
def activate_window(hwnd):
user32 = ctypes.windll.user32
user32.SetForegroundWindow(hwnd)
if user32.IsIconic(hwnd):
user32.ShowWindow(hwnd, 9)
В зависимости от того, как вы идентифицируете нужное окно, есть несколько способов получить
hwnd
она же оконная ручка.
Вы можете просмотреть все окна , чтобы найти правильный дескриптор в соответствии с
pid
с помощью
user32.GetWindowThreadProcessId
или по имени окна с
user32.GetWindowTextW
Чтобы получить ProcessId , вы можете использовать встроенный в Windows
wmic
. Есть много других отличных вещей, которые вы можете сделать с ним.(все атрибуты:
wmic process get /?
)(
get handle
сломан, хотя) Например:
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]
Приложение с графическим интерфейсом для поддержания активности Windows
Python3
установить библиотеку
pip install pywin32
сохраните код ниже как
alive.pyw
файл
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)