Closed Helcity closed 1 year ago
Hi! Thank you for your interest!
Right now, I'm not sure. Let me check if I am able to find anything about it (I guess you mean on MS-Windows, right?)
On Linux this might be inferred from _NET_WM_STATE_DEMANDS_ATTENTION
By the way... I'm having a hard time getting a window to get from Minimized/whatever to Focused / Active, using Ubuntu's Unity DE (Compiz). All related methods I try simply set this property, but it does not bring the window to front
Yeah, MS-Windows, Win10 would be enough
Hi again!
I've been googling around and I found that it's not possible to do that using Python: https://stackoverflow.com/questions/67048076/wh-shell-using-python-winapi-setwindowshookexa https://stackoverflow.com/questions/964564/how-to-add-a-system-windows-hook-so-as-to-be-notified-of-windows-being-created/964643#964643
The solution seems to be to hook to specific general events (WH_SHELL), but this can only be done on C++ from a separate (and unmanaged) DLL. Unfortunately, I'm totally unskilled in C...
Nevertheless I am thinking on a workaround, based on analyzing the color of the icons in the taskbar. This looks feasible, but I need to find the way to identify which icon belongs to which app. pywinauto looks promissing, but still not sure if it will work or not. I will keep on investigating and will let you know any progress.
Best regards!
Hi again!
Though still very experimental, hackish and tricky, I think I managed to get it working, at least on my system, that's the reason why I'd need you to try this on your own system before keeping investigating. Could you please try this? (you may need to install some modules on your system, please let me know if you need help)
import pywinctl as pwc
import pywinauto
from pywinauto import Application
import win32api
import win32gui
import win32con
import win32process
def isFlashing(name) -> bool:
def _getFileDescription(handle):
# https://stackoverflow.com/questions/31118877/get-application-name-from-exe-file-in-python
_, pid = win32process.GetWindowThreadProcessId(handle)
hProc = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION | win32con.PROCESS_VM_READ, 0, pid)
exeName = win32process.GetModuleFileNameEx(hProc, 0)
try:
language, codepage = win32api.GetFileVersionInfo(exeName, '\\VarFileInfo\\Translation')[0]
stringFileInfo = u'\\StringFileInfo\\%04X%04X\\%s' % (language, codepage, "FileDescription")
description = win32api.GetFileVersionInfo(exeName, stringFileInfo)
except:
description = "unknown"
return description
def _find_taskbar_icon(hWnd):
exStyle = win32api.GetWindowLong(hWnd, win32con.GWL_EXSTYLE)
owner = win32gui.GetWindow(hWnd, win32con.GW_OWNER)
if exStyle & win32con.WS_EX_APPWINDOW != 0 or owner != 0:
return None
name = _getFileDescription(hWnd)
try:
app: pywinauto.Application = Application(backend="uia").connect(path="explorer.exe")
sysTray: pywinauto.WindowSpecification = app.window(class_name="Shell_TrayWnd")
w: pywinauto.WindowSpecification = sysTray.child_window(title_re=name, found_index=0)
rect = w.rectangle()
except:
rect = None
return rect
w = pwc.getWindowsWithTitle(name, condition=pwc.Re.CONTAINS)
hWnd = w[0].getHandle()
iconRect = _find_taskbar_icon(hWnd)
if iconRect:
xPos = iconRect.left + int((iconRect.right - iconRect.left) / 2)
color = 0
desktop = win32gui.GetDesktopWindow()
dc = win32gui.GetWindowDC(desktop)
for i in range(50, 54):
color += win32gui.GetPixel(dc, xPos, iconRect.top + i)
win32gui.ReleaseDC(desktop, dc)
flashColor = 10787327 # This value is totally empirical. Find a way to retrieve it!!!!
if color / 4 == flashColor:
return True
else:
return False
else:
return False
print(isFlashing("Spotify")) # change this name for the one you'd like to target
Don't forget to change "Spotify" for whatever application you want to check!!!
I believe the end goal of this will be a Window(...).isFlashing()
instance method, and the name
argument is just a convenience for your tests/research and not an actual argument of the end method, correct?
A few comments:
True
if the window is in any stage, not just the initial flashing/animating, correct @Helcity? If so, .isFlashing()
is a bit of a misnomer. Maybe .isAlert()
or isHighlighed()
are better choices?alert()
(and there's no associated status).I believe the end goal of this will be a
Window(...).isFlashing()
instance method, and thename
argument is just a convenience for your tests/research and not an actual argument of the end method, correct?
Correct!
- In both Windows and most Linux DEs, "flashing" is a 2-stage status: a brief one where the taskbar icon actually "flashes" (in Windows) or "wiggle"/"vibrate" (Unity) for a few seconds, and then a more permanent status where it remains "highlighted" in some way (orange background in Windows, blue marker in Unity, etc), usually not flashing or animating, until the user intervenes and make the window active.
Just for information. In Windows, it's not possible without a C++ DLL, which is far beyond my knowledge. This is why I am trying this "ugly" workaround. In Linux I have not investigated yet, I think that the state you were pointing (_NET_WM_STATE_DEMANDS_ATTENTION) is very promissing! Finally, it's quite probable that this will not be feasible in macOS, for which I use AppleScript that has its... limitations (we will see).
- About the naming: in Windows world, it is called flashing (although it only actually flashes for a brief time, and then remain statically "colored"). In macOS, it bounces. In X, it is demanding attention. While looking for cross-platform references, Qt calls the action to set this
alert()
(and there's no associated status).
Didn't think much about a cross-platform name, to be honest. isAlerting
may be a good candidate, or requiresUserAction
, or maybe isFlashing
, isBouncing
and isDemandingAttention
(all three as alias).
In Linux I have not investigated yet, I think that the state you were pointing (_NET_WM_STATE_DEMANDS_ATTENTION) is very promissing!
Not only promising, but trivial:
def isAlerting(self):
return STATE_ATTENTION in EWMH.getWmState(self._hWnd, str=True)
Didn't think much about a cross-platform name, to be honest.
isAlerting
may be a good candidate, orrequiresUserAction
, or maybeisFlashing
,isBouncing
andisDemandingAttention
(all three as alias).
I like your suggestion of isAlerting
, looks quite platform neutral, is similar to what Qt chose (always good to back up a decision with a reference), and most importantly it does refer to both the initial, transient animation and the permanent state
Hi @Helcity! sorry to bother you, I'd just like to know if you had the time to test the code I was attaching in my previous comment. Please, let me know any progress you make or if you need help or similar.
Thank you!
It would be very nice if there was a function or method with the following functionality:
IsWindowFlashing(WinName): If (WinIsFlashing): return True Else: Return False
My objective is to check if a particular window is flashing on the taskbar, asking for the user attention and focus. Any similar method that scans all windows with this functionality and returns the names of windows that satisfy such criteria would be welcomed too.
Is it possible to create such a thing?