Python Window Activation
Asked Answered
C

8

31

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 used 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)?

Claus answered 19/1, 2010 at 1:25 Comment(2)
You really should tell us what GUI toolkit you are using, because it is possible that this capability is in the toolkit.Grandson
Maybe he is trying to activate just any one of the open Window?Amidase
P
61

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()
Peridium answered 19/1, 2010 at 6:21 Comment(8)
This doesn't work when you have several instances of the same application opened at the same time, as you you wouldn't be able to distinguish between them (since it's all based on the window title). It's a real shame but I suppose this is down the Win32API rather than this particular python module?Contradance
what does hwnd stand for? (boo abbreviations!)Trantrance
@Trantrance hand is a handle on a window. This abbreviation is usually used in windows win32 apiPeridium
2018-11-26. I get ModuleNotFoundError: No module named 'win32.distutils.command' during the installation of win32gui with pip 18.0.1, on Windows 10.Inbeing
On certain versions of Windows SetForegroundWindow can cause an unexpected error. To fix you need send an alt key first, more infoRuvalcaba
@Peridium Are you able to set foreground when the computer is locked? The script works perfectly when the screen isn't locked. However, when the script is run while the computer is locked, it will not set the foreground.Postaxial
@myidealab Don't know how to set foreground when computer is locked. You may have to check the WIndows API to know if this is possible.Peridium
@Peridium Thanks for the feedback. The short answer is you can't. The long answer is that you would need a second pc environment (virtual or remote). I ended up taking a different approach: Python outputs 3 text files (recipients, subject, body). Outlook VBA uses the text to send the daily email. References: 1. What happens 'behind' the windows lock screen? 2. pywinauto is not working while lock screenPostaxial
B
8

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()
Brass answered 17/5, 2014 at 18:34 Comment(3)
This example keeps giving me an error that the list index is out of range on line 6 (the one initializing the handle). I tried to look up info of pywinauto.findwindows.find_windows()-method, but there was none in the documentation.Greenstein
Note: any python shell running before the installation must be restarted before using pywinautogui or it will miss a dependency at load time; Pywinauto cannot be hotplugged.Inbeing
Thanks for the minimize maximize trick. It also solved my problem using win32guiVesiculate
F
2
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.

Freedafreedman answered 28/9, 2019 at 16:12 Comment(0)
A
1

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:

  1. Sidebar
  2. Windows key overlay
  3. Task manager which is always on top
Anthony answered 18/3, 2021 at 22:46 Comment(1)
I develop in Windows environment since... a lot :) and I should be used to face absurd issues. Despite this, I'm still amazed when one happens. Thanks for this suggestion, it looks it saved my day!Theogony
M
0

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]
Meza answered 3/6, 2022 at 14:37 Comment(0)
C
0

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)
Clo answered 31/10, 2022 at 9:42 Comment(0)
M
0

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.

Meddlesome answered 3/11, 2022 at 18:17 Comment(0)
C
0

A late entry, but this worked for me. The key to making this work was to take focus from the current window using the SendKeys() function in the script. This was tested on Windows 11 using 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://mcmap.net/q/460757/-python-window-activation
         - https://mcmap.net/q/470706/-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)

A simple use of this function, which can make use of chaining, as follows:

WindowMgr().find_window_wildcard("My Window Title").set_foreground()
Carmelacarmelia answered 31/5, 2023 at 14:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.