2011-06-10 63 views
14

Estoy implementando un reemplazo de la barra de tareas, un programa de estilo de cambio de aplicación similar a un dock. Está haciendo algunas cosas únicas con OpenGL, y con atajos de teclado, por lo que la forma en que está configurado, la ventana no siempre tiene foco. Me gustaría implementarlo de modo que pueda traer una ventana arbitraria al primer plano, al igual que lo haría una barra de tareas o un programa ALT-TAB.Windows 7: ¿cómo llevar una ventana al frente sin importar qué otra ventana tenga foco?

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 debe suceder, pero estoy buscando una forma de evitar esto.

He adaptado mi código de los siguientes ejemplos, que dicen que la vinculación al subproceso en primer plano debería permitirle establecer la ventana en primer plano. Estos son 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 es el siguiente. Tenga en cuenta que se trata de utilizar las envolturas de Win32 para el pitón (self.hwnd es el identificador de la ventana 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 es la ventana en primer plano (que no lo es por lo general), esto solo causa que el ícono del programa parpadee.

¿Estoy haciendo el engarce de hilo equivocado? ¿Hay alguna otra forma de evitar esto? Me imagino que debe haber, ya que hay muchos mezcladores de aplicaciones que parecen ser capaces de hacer esto bien.

Escribo esto en python, pero si hay una solución en otro idioma, utilizaré wrappers o haré lo que sea necesario para ponerlo en funcionamiento.

¡Gracias de antemano!

EDIT: Estaría abierto a una forma de hacerlo funcionar solo en mi computadora en particular, es decir, una forma de habilitar, en mi máquina, una forma para que cualquier aplicación tome el enfoque.

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

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

Bien, pero hay muchas aplicaciones que pueden hacerlo. Es claramente posible, y he encontrado tutoriales diciéndome cómo. Mi pregunta es, ¿qué estoy haciendo que hace que el código del tutorial no funcione? – jmite

+0

¿por qué no toma un código de uno de estos tutoriales y ejecuta ese –

+1

? Eso es lo que hice. El código que publiqué es casi el código exacto del tutorial, en un contenedor de Python (compruébalo en los dos enlaces que publiqué). Pregunto si alguien puede decir por qué mi código no está haciendo lo que las páginas dicen que hará, o si hay una mejor manera de hacerlo. – jmite

Respuesta

6

He tenido algún código que se ha estado ejecutando durante años, desde Windows 95. Al hacer doble clic en el ícono de la bandeja del sistema de aplicaciones, siempre utilicé funciones de Win32 API como BringWindowToTop y SetForegroundWindow para llevar las ventanas de mi aplicación a el primer plano. 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 esto; 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); 
+0

Lo lleva a la cima, pero el foco del teclado no está en nuestra ventana superior. – Noitidart

1

La documentación de la función SetForegroundWindow explica, que este es en realidad el comportamiento previsto; 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.

Tener un vistazo a la sección de observación LockSetForegroundWindow: explica

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

Puede explotar esta comportamiento al hacer que su programa simule presionar la tecla Alt usando la función SendInput antes de llamar al SetForegroundWindow.

5

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 se si la ventana no está minimizado, pero gracias a whome 's comentario win32gui.ShowWindow(HWND, win32con.SW_RESTORE), que ahora funciona en todas las situaciones.
+2

Debe restaurar la ventana antes de llamar a las funciones de SetWindowPos. Las aplicaciones de Delphi pueden usar "Application.Restore"; funcionar para desmintimar Busque la llamada de pitido ShowWindow (handle, SW_RESTORE). Solo llámalo cada vez que no dañe aunque la ventana ya se haya mostrado. – Whome

9

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

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

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

pip install pywinauto 

a continuación, importar lo que necesita de pywinauto:

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

Por último, es sólo una línea real:

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