Python win32api get "stack" of windows - python

I'm looking for a way to find out what order windows are open on my desktop in order to tell what parts of what windows are visible to the user.
Say, in order, I open up a maximized chrome window, a maximized notepad++ window, and then a command prompt that only covers a small portion of the screen. Is there a way using the win32api (or possibly other library) that can tell me the stack of windows open so I can take the window dimensions and find out what is visible? I already know how to get which window has focus and the top-level window, but I'm looking for more info than that.
In the example I mentioned above, I'd return that the full command prompt is visible but in the places it isn't, the notepad++ window is visible for example. No part of the chrome window would be visible.

import win32gui
import win32con
def get_windows():
def sort_windows(windows):
sorted_windows = []
# Find the first entry
for window in windows:
if window["hwnd_above"] == 0:
sorted_windows.append(window)
break
else:
raise(IndexError("Could not find first entry"))
# Follow the trail
while True:
for window in windows:
if sorted_windows[-1]["hwnd"] == window["hwnd_above"]:
sorted_windows.append(window)
break
else:
break
# Remove hwnd_above
for window in windows:
del(window["hwnd_above"])
return sorted_windows
def enum_handler(hwnd, results):
window_placement = win32gui.GetWindowPlacement(hwnd)
results.append({
"hwnd":hwnd,
"hwnd_above":win32gui.GetWindow(hwnd, win32con.GW_HWNDPREV), # Window handle to above window
"title":win32gui.GetWindowText(hwnd),
"visible":win32gui.IsWindowVisible(hwnd) == 1,
"minimized":window_placement[1] == win32con.SW_SHOWMINIMIZED,
"maximized":window_placement[1] == win32con.SW_SHOWMAXIMIZED,
"rectangle":win32gui.GetWindowRect(hwnd) #(left, top, right, bottom)
})
enumerated_windows = []
win32gui.EnumWindows(enum_handler, enumerated_windows)
return sort_windows(enumerated_windows)
if __name__ == "__main__":
windows = get_windows()
for window in windows:
print(window)
print()
# Pretty print
for window in windows:
if window["title"] == "" or not window["visible"]:
continue
print(window)
Microsoft MSDN has good artice on zorder info with GetWindow() and GW_HWNDNEXT
https://msdn.microsoft.com/en-us/library/windows/desktop/ms633515(v=vs.85).aspx

Related

Python: Can bring window to front but cannot set focus (win32gui.SetForegroundWindow)

My program pops up a window every time the user presses F2 (in any application).
I'm using pynput to capture the F2 button (works ok)
I'm using tkinter to create the popup window (works ok)
I'm using win32gui.SetForegroundWindow(windowHandel) to bring the tkinter window to the front and set the focus. And there is the problem.
If the python windows is selected when I press F2, everything works ok, and the tkinter window both moves to front and gets focus.
BUT - if any other window is selected when I press F2, the tkinter window does moves to the front, but it is not selected (i.e. focused).
Here is the relevant section from the code (find full code below):
while not windowFound and counter < MAX_TRIES_TO_FIND_THE_FLIPPER_WINDOW:
try:
windowHandel = win32gui.FindWindow(None, windowName)
win32gui.SetForegroundWindow(windowHandel)
except:
windowFound = False
else:
print("Success, Window found on the " + str(counter + 1) + " tries")
windowFound = True
After looking for an answer for a while, I found someone saying that this can be solved by using win32process. So I tried adding:
windowHandelID, _ = win32process.GetWindowThreadProcessId(windowHandel)
win32process.AttachThreadInput(win32api.GetCurrentThreadId(), windowHandelID, True)
win32gui.SetFocus(windowHandel)
Yet, it resulted in the same behavior.
Here below is the full (simplified, without exit conditions) code.
Try pressing F2 while pythong is focused.
And then try pressing F2 while any other window (e.g. notepad) is focused.
You'll see that in one case you can just start writing and the tkinter window will receive the input while in the other case, you'll still have to click the window.
I'd appreciate any help or suggestions.
import pyautogui # For keyboard shortcuts and moving the cursor and selecting the window
import time # For the delay function
from pynput import keyboard # For catching keyboard strokes
import tkinter # GUI
import threading # For Threading
import win32gui # For Setting Focus on the Flipper Window
import win32process
import win32api
# Resetting Variables / Settings
start_flipping_text_sequence = False
ContinueThreads = True
SearchForFlipperWindow = False
window_name = "tk"
MAX_TRIES_TO_FIND_THE_FLIPPER_WINDOW = 10
# This function runs in a separate thread
def selectFlipperWindow(windowName):
# Since the thread runs constantly, it will only start looking for the flipper window when this variable is True
global SearchForFlipperWindow
# How many loops should the program go through before it gives up on finding the window
global MAX_TRIES_TO_FIND_THE_FLIPPER_WINDOW
# While program was not ended
while True:
# This is False, unless F2 is pressed
if SearchForFlipperWindow:
# Did the program find the flipper window
windowFound = False
counter = 0
while not windowFound and counter < MAX_TRIES_TO_FIND_THE_FLIPPER_WINDOW:
try:
windowHandel = win32gui.FindWindow(None, windowName)
win32gui.SetForegroundWindow(windowHandel)
except:
windowFound = False
else:
print("Success, Window found on the " + str(counter + 1) + " tries")
windowHandelID, _ = win32process.GetWindowThreadProcessId(windowHandel)
win32process.AttachThreadInput(win32api.GetCurrentThreadId(), windowHandelID, True)
win32gui.SetFocus(windowHandel)
windowFound = True
counter += 1
time.sleep(0.1)
SearchForFlipperWindow = False
time.sleep(0.1)
# Execute functions based on the clicked key
def on_press(key):
global start_flipping_text_sequence
# If the user pressed the F2 key
if key == keyboard.Key.f2:
start_flipping_text_sequence = True
def okButton():
root.destroy()
def enter(event):
okButton()
# Assigning event to function
listener = keyboard.Listener(on_press=on_press)
# initiating listener
listener.start()
# Start a thread for searching for the flipper window
selectWindowThread = threading.Thread(target=selectFlipperWindow, args=(window_name,))
selectWindowThread.start()
while 1 == 1:
time.sleep(.05)
if start_flipping_text_sequence:
SearchForFlipperWindow = True
root = tkinter.Tk()
tk_window_input = tkinter.Entry(root, width=100)
tk_window_input.pack(padx=20)
tk_window_input.focus()
# Binds the OK button to the okButton function above
tk_window_ok = tkinter.Button(root, width=20, text="OK", command=okButton)
tk_window_ok.pack(pady=20)
# Binds the "Enter" keyboard key to the "enter" event above
tk_window_input.bind('<Return>', enter)
# the main looper of the tkinter window
# runs until root.destroy() to executed above
root.mainloop()
start_flipping_text_sequence = False
```
What you see is an intentional restriction in Windows. The restriction is described by Raymond Chen in article Foreground activation permission is like love: You can’t steal it, it has to be given to you. Remarks section of the SetForegroundWindow documentation gives more technical details about the restriction.
There are ways to be exempt from the restriction. One good way to do so is described by Raymond Chen in article Pressing a registered hotkey gives you the foreground activation love.
The following code shows one more, strange way to bypass the restriction:
kbd.press(keyboard.Key.alt)
try:
win32gui.SetForegroundWindow(windowHandel)
finally:
kbd.release(keyboard.Key.alt)
where kbd was created like this:
from pynput.keyboard import Controller
kbd = Controller()
Here is an explanation why this workaround works: link.
A good way to get rid of this workaround may be to require a user to press Alt-F2 in order to switch to your application.
Good luck with coding ;-)

How to toggle the console with a button in a GUI program

When I run my program (.exe file), the console automatically turns on.
I don't want to get rid of it, but I want to have the ability to hide it.
I know that I can completely get rid of the console while coverting .py to .exe with the auto-py-to-exe module.
Is there a way to turn the console on and off with a button, without closing the program or anything like that?
I am using the PySimpleGui Library for the gui if that changes anything.
The Button:
Try to use pywin32 library to hide/show the console, and it work only for WINDOWS.
import win32gui, win32con
import PySimpleGUI as sg
console = win32gui.GetForegroundWindow()
sg.theme("DarkBlue3")
sg.set_options(font=("Courier New", 12))
layout = [[sg.Button( "Console ON/OFF", key="-CONSOLE-")]]
window = sg.Window('Title', layout, finalize=True)
view_console = True
while True:
event, values = window.read()
if event == sg.WINDOW_CLOSED:
break
elif event == "-CONSOLE-":
view_console = not view_console
option = win32con.SW_SHOW if view_console else win32con.SW_HIDE
win32gui.ShowWindow(console, option)
window.close()

Bring window to focus with python periodically

import win32gui
import time
def windowEnumerationHandler(hwnd, top_windows):
top_windows.append((hwnd, win32gui.GetWindowText(hwnd)))
if __name__ == "__main__":
top_windows = []
win32gui.EnumWindows(windowEnumerationHandler, top_windows)
for i in top_windows:
print(i)
if "zoom" in i[1].lower():
print(i, 'is found')
while True:
win32gui.ShowWindow(i[0],5)
win32gui.SetForegroundWindow(i[0])
time.sleep(1)
I've heard that zoom monitors whether the window is not in focus for more than 30 seconds, so I've been working on a way to repetitively throw it to the front while I work on other projects. The problem is the code raises an exception
0, 'SetForegroundWindow', 'No error message is available'
and the window just flashes yellow. Same problem with chrome as well. Would appreciate some help here :)
I had the same problem while I was trying to SetForegroundWindow(hwnd). The icon on the taskbar was just flashing, but the program stayed in the background. As you can read here:
https://learn.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-setforegroundwindow?redirectedfrom=MSDN
"An application cannot force a window to the foreground while the user is working with another window. Instead, Windows flashes the taskbar button of the window to notify the user."
For me helped:
import win32gui, win32com.client
shell = win32com.client.Dispatch("WScript.Shell")
shell.SendKeys('%')
win32gui.SetForegroundWindow(hwnd)

How to close two windows of the same application using python?

I'm working on an automation program in python, I've two windows of cmd open on my screen. I want my code to only close the first window, and leave the second window as it is. How will I make my code determine which window to close?
To close the window I'm using the keyboard package in python, the code writes exit in the cmd window and generates an enter key press to exit the window.
keyboard.write("exit") # closes main cmd window
keyboard.press_and_release("enter") # window closes
If you use Windows OS, you can use the pywin32 and win32gui libraries which contain the modules win32gui and win32con which will help search through windows matching a certain name and getting the window handle number (hwnd). Given a hwnd, you can then close the first window that was opened.
You will first need to install pywin32 and wind32gui:
pip install win32gui
pip install pywin32
Here's the code to close the first command prompt window:
import win32gui
import win32con
def windowEnumerationHandler(hwnd, top_windows):
top_windows.append((hwnd, win32gui.GetWindowText(hwnd)))
def return_window_hwnd(window_name):
'''Return list window handles of that match a given window name'''
windows = []
win32gui.EnumWindows(windowEnumerationHandler, windows)
print(windows) #[(67084, 'Command Prompt'), (65868, ''), (722426, 'Command Prompt'), ...]
hwnds = []
for wind in windows:
if window_name in wind[1] or window_name == wind[1]:
hwnds.append(wind[0])
return hwnds
# search for Command Prompt windows and return the list of hwnd
command_hwnd_arr = return_window_hwnd('Command Prompt')
# if more than 1 is window open, close the first opened window
if len(command_hwnd_arr) > 1:
window_handle_to_close = command_hwnd_arr[1]
win32gui.PostMessage(window_handle_to_close, win32con.WM_CLOSE, 0, 0)

How to get the text of a widget/window using python-xlib?

I'm trying to find the whole text that is currently being edited in gedit window. Firstly i tried to find out the current gedit tab that is focused, by using Xlib.display. Now i got an Xlib.display.window object . Now i want to find out the text that is in that particular window/widget using this window object
And my code is like this
import gtk, gobject, Xlib.display
currentFocus=''
def current_focused:
global currentFocus
display = Xlib.display.Display()
window = display.get_input_focus().focus
wmname = window.get_wm_name()
wmclass = window.get_wm_class()
if wmclass is None and wmname is None:
window = window.query_tree().parent
wmname = window.get_wm_name()
if currentFocused!=wmname:
if window.get_wm_class()[0]=='gedit':
print "\nNow you are in : < %s >" % (wmname,)
# Here i have to find the text of the gedit's widget
currentFocused=wmname
return True
gobject.timeout_add(1000, current_focused)
gtk.main()
is there any API to get the text of a specific widget using Xlib.display.window
Please help me. I'm completely new in this area
Thank you
WM_CLASS / WM_NAME properties are only used to provide information to a window manager ( hence WM prefix ) and not usually set on child windows. Check GTK source code if edit widget sets similar properties, but in general it is not possible for external process to read edit control text

Categories

Resources