Активация окна 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'). Вы можете сделать это три раза для любого из следующих действий:

  1. Боковая панель
  2. Наложение клавиш Windows
  3. Диспетчер задач, который всегда на высоте

Поздняя запись, но это сработало для меня. Ключом к выполнению этой работы было переключение фокуса с текущего окна с помощью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)
Другие вопросы по тегам