Windows 7: ¿cómo traer una ventana al frente sin importar qué otra ventana tenga el foco?

Estoy implementando un progtwig de sustitución de la barra de tareas, similar al de una aplicación tipo docker. Está haciendo algunas cosas únicas con OpenGL y con los métodos abreviados de teclado, por lo que la configuración de la ventana no siempre tiene enfoque. Me gustaría implementarlo de tal manera que pueda traer una ventana arbitraria al primer plano, como lo haría una barra de tareas o un progtwig ALT-TAB.

Sin embargo, mi código simplemente hace que el icono de la aplicación parpadee en la barra de tareas. La documentación de la API de Windows dice que esto es lo que se supone que suceda, pero estoy buscando una forma de solucionar esto.

He adaptado mi código de los siguientes ejemplos, que dicen que adjuntar al hilo de primer plano debería permitirle establecer la ventana de primer plano. Aquí están los sitios:

http://www.voidnish.com/Articles/ShowArticle.aspx?code=dlgboxtricks

http://invers2008.blogspot.com/2008/10/mfc-how-to-steal-focus-on-2kxp.html

Mi código se ve así. Tenga en cuenta que está usando los envoltorios de win32 para python (self.hwnd es el controlador de la ventana que quiero traer al frente):

fgwin = win32gui.GetForegroundWindow() fg = win32process.GetWindowThreadProcessId(fgwin)[0] current = win32api.GetCurrentThreadId() if current != fg: win32process.AttachThreadInput(fg, current, True) win32gui.SetForegroundWindow(self.hwnd) win32process.AttachThreadInput(fg, win32api.GetCurrentThreadId(), False) 

Sin embargo, a menos que mi ventana sea la ventana de primer plano (que generalmente no lo es), esto solo hace que el icono del progtwig parpadee.

¿Estoy haciendo el hilo adjuntando mal? ¿Hay otra manera de evitar esto? Me imagino que debe haberlo, ya que hay muchos conmutadores de aplicaciones que parecen ser capaces de hacer esto muy bien.

Estoy escribiendo esto en Python, pero si hay una solución en otro idioma, usaré envoltorios o haré lo que sea necesario para poner esto en funcionamiento.

¡Gracias por adelantado!

EDITAR: Estaré abierto a una forma de hacer que funcione solo en mi computadora en particular, es decir, una manera de habilitar, en mi máquina, una forma de enfocar cualquier aplicación.

SOLUCIONADO: Lo que tienes que hacer es deshabilitar el locking de primer plano. Resulta que fue tan fácil como esto:

 win32gui.SystemParametersInfo(win32con.SPI_SETFOREGROUNDLOCKTIMEOUT, 0, win32con.SPIF_SENDWININICHANGE | win32con.SPIF_UPDATEINIFILE) 

He tenido algún código que se ha estado ejecutando durante años, yendo hasta Windows 95. Al hacer doble clic en el icono de la bandeja del sistema de aplicaciones, siempre usé las funciones de la API de Win32, como BringWindowToTop y SetForegroundWindow, para poner en primer plano las ventanas de mi aplicación. Todo esto dejó de funcionar como estaba previsto en Windows 7, donde mi ventana de entrada terminaría detrás de otras ventanas y el icono de la ventana parpadearía en la barra de estado. El “trabajo alrededor” que se me ocurrió fue este; y parece funcionar en todas las versiones de Windows.

 //-- show the window as you normally would, and bring window to foreground. // for example; ::ShowWindow(hWnd,SW_SHOW); ::BringWindowToTop(hWnd); ::SetForegroundWindow(hWnd); //-- on Windows 7, this workaround brings window to top ::SetWindowPos(hWnd,HWND_NOTOPMOST,0,0,0,0, SWP_NOMOVE | SWP_NOSIZE); ::SetWindowPos(hWnd,HWND_TOPMOST,0,0,0,0,SWP_NOMOVE | SWP_NOSIZE); ::SetWindowPos(hWnd,HWND_NOTOPMOST,0,0,0,0,SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE); 

No me gustan estas sugerencias de usar win32gui porque no puedes instalarlas fácilmente a través de pip . Así que aquí está mi solución:

Primero, instale pywinauto través de pip . Si estás en Python 2.7.9 o una versión más nueva en la twig 2, o Python 3.4.0 o una versión más nueva de la twig 3, pip ya está instalado. Para todos los demás, actualice Python para obtenerlo (o puede descargarlo e instalarlo manualmente ejecutando este script , si debe ejecutar una versión anterior de Python).

Simplemente ejecute esto desde la línea de comandos (no desde Python):

 pip install pywinauto 

A continuación, importa lo que necesites desde pywinauto :

 from pywinauto.findwindows import find_window from pywinauto.win32functions import SetForegroundWindow 

Finalmente, es solo una línea real:

 SetForegroundWindow(find_window(title='taskeng.exe')) 

Según nspire, he probado su solución con Python 2.7 y W8, y funciona como un encanto, incluso si la ventana está minimizada *.

 win32gui.ShowWindow(HWND, win32con.SW_RESTORE) win32gui.SetWindowPos(HWND,win32con.HWND_NOTOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE + win32con.SWP_NOSIZE) win32gui.SetWindowPos(HWND,win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE + win32con.SWP_NOSIZE) win32gui.SetWindowPos(HWND,win32con.HWND_NOTOPMOST, 0, 0, 0, 0, win32con.SWP_SHOWWINDOW + win32con.SWP_NOMOVE + win32con.SWP_NOSIZE) 
  • Originalmente era si la ventana no está minimizada , pero gracias al comentario de win32gui.ShowWindow(HWND, win32con.SW_RESTORE) , ahora funciona en todas las situaciones.

Si está implementando teclas de acceso rápido, use RegisterHotKey . Como lo señala Raymond Chen (artículo del blog complementario al ya vinculado por Chris), “Al presionar una tecla de acceso rápido registrada, se obtiene el amor de activación de primer plano” .

La documentación para la función SetForegroundWindow explica que este es realmente el comportamiento deseado; Los procesos no deberían ser capaces de “robar” el foco. Sin embargo, es posible ajustar su código para que funcione de todos modos.

Eche un vistazo a la sección de comentarios de LockSetForegroundWindow : explica

El sistema habilita automáticamente las llamadas a SetForegroundWindow si el usuario presiona la tecla ALT [..]

Puede explotar este comportamiento haciendo que su progtwig simule presionar la tecla Alt usando la función SendInput antes de llamar a SetForegroundWindow .

Así es como conseguí el mío trabajando:

 import win32gui from win32con import (SW_SHOW, SW_RESTORE) def get_windows_placement(window_id): return win32gui.GetWindowPlacement(window_id)[1] def set_active_window(window_id): if get_windows_placement(window_id) == 2: win32gui.ShowWindow(window_id, SW_RESTORE) else: win32gui.ShowWindow(window_id, SW_SHOW) win32gui.SetForegroundWindow(window_id) win32gui.SetActiveWindow(window_id)