Получить активное окно с помощью Python

Я хотел бы вывести активное окно на экран с помощью python.

Например, интерфейс управления маршрутизатором, в котором вы вводите имя пользователя и пароль как admin.

Этот интерфейс администратора - это то, что я хочу записать с помощью Python, чтобы автоматизировать ввод имени пользователя и пароля.

Какой импорт мне потребуется для этого?


person Vinod K    schedule 22.04.2012    source источник
comment
Какая операционная система? Вы спрашиваете об активных окнах рабочего стола или окнах браузера? Вам нужно какое-либо активное окно, или вы просто пытаетесь автоматизировать интерфейс управления вашего роутера?   -  person Bryan Oakley    schedule 22.04.2012


Ответы (9)


В Windows вы можете использовать python для расширений Windows (http://sourceforge.net/projects/pywin32/):

from win32gui import GetWindowText, GetForegroundWindow
print GetWindowText(GetForegroundWindow())

Ниже приведен код для Python 3:

from win32gui import GetWindowText, GetForegroundWindow
print(GetWindowText(GetForegroundWindow()))

(Найдено на http://scott.sherrillmix.com/blog/programmer/active-window-logger/)

person William Saunders    schedule 21.06.2012
comment
Я запускаю этот скрипт с ярлыком своего скрипта (чтобы вызвать его с помощью сочетания клавиш). Таким образом, это ярлык для печати. Активные окна - это PDF-файл, открытый в acrobat, но когда я запускаю скрипт, активные окна - это ярлык (я хочу получить имя файла открытого PDF-файла). Есть идеи, как это решить? - person JinSnow; 23.11.2016
comment
Просто чтобы все знали, что pywin32 теперь имеет pip install pip install pywin32 - person Yusof Bandar; 28.01.2019

Следующий сценарий должен работать в Linux, Windows и Mac. В настоящее время он протестирован только в Linux (Ubuntu Mate Ubuntu 15.10).

Предпосылки

Для Linux:

Установите wnck (sudo apt-get install python-wnck в Ubuntu, см. libwnck.)

Для Windows:

Убедитесь, что win32gui доступен

Для Mac:

Убедитесь, что AppKit доступен

Сценарий

#!/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: http://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']:
        # http://stackoverflow.com/a/608814/562769
        import win32gui
        window = win32gui.GetForegroundWindow()
        active_window_name = win32gui.GetWindowText(window)
    elif sys.platform in ['Mac', 'darwin', 'os2', 'os2emx']:
        # http://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()))
person Martin Thoma    schedule 05.04.2016
comment
Я тестировал его только для Linux (Ubuntu Mate). Пожалуйста, добавьте комментарий, если у вас другая система, и дайте мне знать о результатах. - person Martin Thoma; 05.04.2016
comment
В Lubuntu, 14.04 LTS, я получаю 7 предупреждений Wnck-WARNING **: Unhandled action type _OB_WM_ACTION_UNDECORATE, по-видимому, в строке screen.force_update (), но в итоге получаю правильный результат. Кроме того, этот блок, кажется, возвращает имя окна (например, x-terminal-emulator), в отличие от памяти, я ожидал, что Win32 вернет заголовок окна, который обычно является динамически изменяющимся текстом. - person brezniczky; 18.06.2016
comment
Для wnck в Python3 см .: stackoverflow.com/a/43349245 - person jl005; 29.01.2021

Спасибо за ответ Нуно Андре, который показал, как использовать ctypes для взаимодействия с Windows API. Я написал пример реализации, используя его подсказки.

Библиотека ctypes включена в Python, начиная с версии 2.5, а это значит, что она есть почти у каждого пользователя. И это намного более чистый интерфейс, чем старые и мертвые библиотеки, такие как win32gui (последнее обновление в 2017 году на момент написания этой статьи). ((Обновление в конце 2020 года: мертвая библиотека win32gui ожила с переименованием в pywin32, поэтому, если вам нужна поддерживаемая библиотека, теперь это снова допустимый вариант. Но эта библиотека на 6% медленнее, чем мой код.))

Документация находится здесь: https://docs.python.org/3/library/ctypes.html (вы должны прочитать справку по его использованию, если хотите написать свой собственный код, иначе вы можете вызвать сбои из-за ошибки сегментации, хе-хе.)

По сути, ctypes включает привязки для наиболее распространенных библиотек DLL Windows. Вот как вы можете получить заголовок окна переднего плана на чистом Python без каких-либо внешних библиотек! Только встроенные типы! :-)

Самое крутое в ctypes - это то, что вы можете использовать любой Windows API в Google для всего, что вам нужно, и если вы хотите его использовать, вы можете сделать это через ctypes!

Код Python 3:

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

Чрезвычайно хорошая производительность: 0.01 МИЛЛИСЕКУНД на моем компьютере (0.00001 секунд).

Также будет работать на Python 2 с очень небольшими изменениями. Если вы используете Python 2, я думаю, вам нужно только удалить аннотации типов (from typing import Optional и -> Optional[str]). :-)

Наслаждаться!

Технические пояснения к Win32:

Переменная length - это длина фактического текста в СИМВОЛАХ UTF-16 (Windows Wide Unicode). (Это НЕ количество БАЙТОВ.) Мы должны добавить + 1 чтобы добавить место для нулевого терминатора в конце строк в стиле C. Если мы этого не сделаем, у нас не будет достаточно места в буфере, чтобы уместить последний реальный символ фактического текста, и Windows усечет возвращаемую строку (она делает это, чтобы гарантировать, что она соответствует очень важной конечной строке Null -терминатор).

Функция create_unicode_buffer выделяет место для такого количества СИМВОЛОВ UTF-16.

Большинство (или все? Всегда читайте документы Microsoft MSDN!) Windows API, относящиеся к тексту Unicode, принимают длину буфера как СИМВОЛЫ, НЕ как байты.

Также внимательно посмотрите на вызовы функций. Некоторые заканчиваются на W (например, GetWindowTextLengthW). Это означает широкую строку, которая является именем Windows для строк Unicode. Очень важно, чтобы вы выполняли эти W вызовы для получения правильных строк Unicode (с поддержкой международных символов).

PS: Windows уже давно использует Unicode. Я точно знаю, что Windows 10 является полностью Unicode и требует только W вызовов функций. Я не знаю точной даты окончания, когда старые версии Windows использовали другие форматы многобайтовых строк, но я думаю, что это было до Windows Vista, и кого это волнует? Старые версии Windows (даже 7 и 8.1) мертвы и не поддерживаются Microsoft.

Снова ... наслаждайтесь! :-)

ОБНОВЛЕНИЕ в конце 2020 г., сравнительный анализ и библиотека pywin32:

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.")

Типичный результат после нескольких запусков на AMD Ryzen 3900x:

Моя функция: 4.5769994258880615 секунд

Библиотека Win32UI: 4.8619983196258545 секунд

Библиотека Win32UI на 6,226762715455125 процентов медленнее.

Однако разница небольшая, поэтому вы можете использовать библиотеку сейчас, когда она ожила (ранее она была мертва с 2017 года). Но вам придется иметь дело с этой странной библиотекой: нет окна в исключении переднего плана, от которого мой код не страдает (см. Комментарии к коду в тестовом коде).

В любом случае ... наслаждайтесь!

person Mitch McMabers    schedule 12.10.2019
comment
с использованием perf_counter (без предварительной компиляции) эта функция постоянно возвращала на 30-50% медленнее, чем при использовании библиотеки win32gui - person David; 16.06.2020
comment
@ Дэвид Это неправда. Мой код на 6% быстрее, чем win32gui (который ожил и переименован в pywin32). Я обновил свой пост тестом для сравнения, чтобы вы могли убедиться в этом сами. Возможно, вы забыли, что мой код выполняет две функции (получает активное окно и затем получает заголовок этого окна), тогда как эта библиотека выполняет только одно действие за раз, и вам нужно вызвать в ней две функции, чтобы добиться того, что делает мой код. - person Mitch McMabers; 16.11.2020

На самом деле нет необходимости импортировать какие-либо внешние зависимости для таких задач. Python поставляется с довольно изящным интерфейсом внешних функций - ctypes, который позволяет вызывать C общие библиотеки изначально. Он даже включает специальные привязки для наиболее распространенных библиотек DLL Win32.

Например. чтобы получить PID окна foregorund:

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)
person Nuno André    schedule 13.06.2019
comment
Большое спасибо, это намного лучше, чем мертвая библиотека win32gui (последняя активна в 2017 году). С вашим кодом я получаю те же функции напрямую через ctypes! Я собираюсь опубликовать отдельный ответ со своим собственным кодом для получения заголовка окна переднего плана. Но заслуга в том, что вы поделились этим общим методом! - person Mitch McMabers; 12.10.2019
comment
@MitchMcMabers Вы можете проверить эту реализацию некоторых методов WinAPI. Кроме того, если вы хотите вызывать некоторые COM-объекты с помощью ctypes, стоит эта библиотека. - person Nuno André; 13.10.2019
comment
Эта pywin32-ctypes библиотека очень хороша и крошечна и ориентирована только на загрузку библиотек DLL и файлов ресурсов, а это значит, что она не раздута. И comtypes известен и жизненно важен для работы с COM. Хорошие советы людям! :-) - person Mitch McMabers; 13.10.2019
comment
В Ubuntu 16.04 ctypes импортирует ОК, но затем при использовании: from ctypes import wintypes генерируются строки ошибок: File "/usr/lib/python2.7/ctypes/wintypes.py", line 19, in <module>, за которыми следует: class VARIANT_BOOL(_SimpleCData): и, наконец,: ValueError: _type_ 'v' not supported. Я воспользуюсь ответом, за который я только что проголосовал, от 17 до 18 голосов. - person WinEunuuchs2Unix; 01.05.2021
comment
можно ли это использовать, чтобы преобразовать его в лучший формат (не номер pid, а настоящее имя, например Outlook-mail)? - person Augurkenplukker12; 23.05.2021

Для пользователей Linux: все предоставленные ответы требовали дополнительных модулей, таких как «wx», которые имели множество ошибок при установке («pip» не удался при сборке), но я смог довольно легко изменить это решение -> исходный источник. В оригинале были ошибки (Python TypeError в регулярном выражении)

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())

Плюс в том, что он работает без дополнительных модулей. Если вы хотите, чтобы он работал на нескольких платформах, достаточно изменить строки команд и регулярных выражений, чтобы получить нужные данные в зависимости от платформы (со стандартным определением платформы if / else, показанным выше sys.platform ).

Замечание: import wnck работает только с python2.x при установке с помощью «sudo apt-get install python-wnck», поскольку я использовал python3.x, единственным вариантом был pypie, который я не тестировал. Надеюсь, это поможет кому-то другому.

person James Nelson    schedule 22.02.2017

У меня была такая же проблема с интерфейсом Linux (Lubuntu 20). Что я делаю, так это использую wmctrl и выполняю его с помощью команды оболочки из python.

Сначала установите wmctrl sudo apt install wmctrl

Затем добавьте этот код:

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.

person Leo Armanda Al Fadhillah    schedule 10.09.2020

В Linux под X11:

xdo_window_id = os.popen('xdotool getactivewindow').read()
print('xdo_window_id:', xdo_window_id)

выведет идентификатор активного окна в десятичном формате:

xdo_window_id: 67113707

Примечание xdotool необходимо установить сначала:

sudo apt install xdotool

Примечание wmctrl использует шестнадцатеричный формат для идентификатора окна.

person WinEunuuchs2Unix    schedule 19.06.2021

Просто хотел добавить на случай, если это поможет, у меня есть функция для моей программы (это программное обеспечение для освещения моего ПК, у меня есть эта простая функция из нескольких строк:

def isRunning(process_name):
   foregroundWindow = GetWindowText(GetForegroundWindow())
   return process_name in foregroundWindow
person Amy Gamble    schedule 08.01.2016

Попробуйте использовать wxPython:

import wx
wx.GetActiveWindow()
person 絢瀬絵里    schedule 22.04.2012
comment
Я не думаю, что ОП просит об этом. Согласно документации wxpython, GetActiveWindow получает текущее активное окно этого приложения. Таким образом, он вернет только другие окна wxPython. - person Bryan Oakley; 22.04.2012