2011-01-22 21 views
12

Mis disculpas de antemano por la duración de la pregunta, no quería dejar nada.¿Cómo implementar pausa (y más) funcionalidad?

Algunos antecedentes de información

Estoy tratando de automatizar un proceso de entrada de datos al escribir una aplicación Python que utiliza la API de Windows para simular las pulsaciones del teclado, el movimiento del ratón y la ventana/controlar la manipulación. Tengo que recurrir a este método porque aún no tengo la autorización de seguridad necesaria para acceder al almacén de datos/base de datos directamente (por ejemplo, usando SQL) o indirectamente a través de una API más adecuada. Burocracia, es un dolor ;-)

El proceso de entrada de datos implica la corrección de las órdenes de venta debido a cambios en la disponibilidad del artículo. Los artículos no disponibles se eliminan del pedido o se reemplazan por otro artículo adecuado.

Inicialmente quiero que un ser humano pueda monitorear el proceso automático de entrada de datos para asegurarse de que todo vaya bien. Para lograr esto, reduzco la velocidad de las acciones, por un lado, pero también informo al usuario de lo que está sucediendo actualmente a través de una ventana anclada.

La pregunta real

para permitir al usuario para detener el proceso de automatización que estoy registrando la tecla Pausa/Inter como una tecla de acceso directo y en el controlador Quiero hacer una pausa en la funcionalidad de automatización. Sin embargo, actualmente estoy luchando por encontrar una forma de pausar correctamente la ejecución de la funcionalidad de automatización. Cuando se invoca la función de pausa, quiero que el proceso de automatización se detenga en seco, sin importar lo que esté haciendo. No quiero que incluso ejecute otra pulsación de tecla.

ACTUALIZACIÓN [23/01]: realmente quiero hacer algo más que hacer una pausa, quiero ser capaz de comunicarse con el proceso de automatización mientras que está funcionando y lo soliciten para hacer una pausa, omitir el orden actual de ventas, renunciar por completo y tal vez incluso más.

¿Alguien puede mostrarme The Right Way (TM) para lograr lo que quiero?

Algunos más información

Aquí hay un ejemplo de cómo funciona la automatización (estoy usando the pywinauto library):

from pywinauto import application 
app = application.Application() 
app.start_("notepad") 
app.Notepad.TypeKeys("abcdef") 

ACTUALIZACIÓN [25/01]: Después de unos días de trabajo en En mi aplicación me he dado cuenta de que realmente no uso pywinauto, ahora mismo solo lo estoy usando para buscar ventanas y luego uso directamente SendKeysCtypes.SendKeys para simular la entrada del teclado y las funciones win32api para simular la entrada del mouse.

Lo que he encontrado hasta ahora

Éstos son algunos métodos que he encontrado hasta ahora en mi búsqueda de una respuesta:

  1. pude separar la funcionalidad de automatización y de la interfaz + escucha de teclas rápidas en dos procesos separados. Vamos a referirnos al primero como "automator" y al último como "gerente". El administrador puede detener la ejecución del automatizador enviando al proceso una señal SIGSTOP y ponerla en pausa usando la señal SIGCONT (o los equivalentes de Windows a través de SuspendThread/ResumeThread).

    Para poder actualizar la interfaz de usuario, el automatizador deberá informar al gerente de su progresión a través de algún tipo de mecanismo de IPC.

    Contras:

    • haría con SIGSTOP no puede ser un poco duro? ¿Funcionaría incluso correctamente? Mucha gente parece estar aconsejándolo en contra y hasta llamándolo "peligroso".

    • Me preocupa que la implementación del mecanismo de IPC sea un poco complicada. Por otro lado, he trabajado con DBus, que no sería demasiado difícil de implementar.

  2. El segundo método y una que mucha gente parece estar sugiriendo implica el uso de hilos y esencialmente se reduce a lo siguiente (simplificado):

    while True: 
        if self.pause: # pause 
        # Do the work... 
    

    Sin embargo, hacerlo de esta manera parece solo se detendrá una vez que ya no haya más trabajo por hacer. La única forma en que veo que este método funcionaría sería dividir el trabajo (todo el proceso de automatización) en segmentos de trabajo más pequeños (es decir, tareas). Antes de comenzar con una nueva tarea, el hilo del trabajador verificará si debe detenerse y esperar.

    Contras:

    • parece una aplicación para dividir el trabajo en segmentos más pequeños, como la de arriba, sería muy feo código sabia (estéticamente).

      La forma en que me imagino que, todas las declaraciones se transformaría a ser algo como: queue.put((function, args)) (por ejemplo queue.put((app.Notepad.TypeKeys, "abcdef"))) y usted tendría el hilo proceso de automatización se ejecuta a través de las tareas y continuamente comprobando el estado de pausa antes de comenzar una tarea. Eso simplemente no puede ser correcto ...

    • El programa no se detendría en seco, pero primero terminaría una tarea (por pequeña que fuera) antes de hacer una pausa.

Progresos realizados

ACTUALIZACIÓN [23/01]: he implementado una versión de mi aplicación que utiliza el primer método a través de la funcionalidad SuspendThread/ResumeThread mencionado. Hasta ahora, esto parece funcionar muy bien y también me permite escribir las cosas de automatización del mismo modo que escribirías cualquier otro script. La única peculiaridad con la que me he encontrado es que los modificadores de teclado (CTRL, ALT, SHIFT) se "bloquean" durante la pausa. Algo con lo que probablemente pueda trabajar fácilmente.

También escribí una prueba utilizando el segundo método (hilos y señales/mensaje que pasa) e implementé la funcionalidad de pausa. Sin embargo, se ve realmente feo (ambos verificando el indicador de pausa y todo lo relacionado con "hacer el trabajo"). Entonces, si alguien puede mostrarme un ejemplo apropiado de algo similar al segundo método, lo agradecería.

preguntas relacionadas

+4

+1 para la pregunta detallada – Wolph

+0

¿Qué conjunto de herramientas está utilizando para la GUI? Usted mencionó una ventana anclada. –

+0

@Cristian: decidí darle una oportunidad a Qt, pero estoy pensando en volver a GTK. –

Respuesta

2

No sé pywinauto. Pero supongo que tiene algo así como una clase de aplicación que obtiene y tiene métodos como SendKeys/SendMouseEvent/etc para hacer cosas.

Crea tu propia clase MyApplication que contiene una referencia a la clase de aplicación de pywinauto. Proporcione los mismos métodos, pero antes de cada método verifique si se ha producido un evento de pausa. Si lo tiene, puede saltar al código que maneja el evento de pausa. De esta forma, está comprobando una pausa cada vez que causa un evento, pero todo esto es manejado por una clase sin poner pausa en todo su código.

Una vez que haya detectado la pausa, podrá manejarla como desee. Por ejemplo, puede lanzar una excepción para forzar el abandono de la tarea actual.

+0

+1 Es posible que haya algo allí ... Las cosas de automatización en las que estoy trabajando son un poco más complicadas que solo simular la entrada de teclado (hay selección de texto , análisis de texto, comparación de texto, etc.) pero envolver un conjunto de funciones/clases como esta podría funcionar muy bien. Voy a investigarlo y me pondré en contacto contigo. –

1

La separación de la funcionalidad y el hilo de interfaz/proceso es sin duda la mejor en mi humilde opinión opción, la segunda solución es más rápido y más fácil, pero sin duda no mejor.

Tal vez el uso de varios hilos y una excepción sería una mejor idea que el uso de múltiples procesos. Pero si usa múltiples procesos distintos de SIGSTOP, podría ser la única forma de que funcione.

¿Hay algo en contra de usar 2 hilos para esto?

  • 1 hilo para ejecutar realmente
  • 1 hilo para leer la entrada del usuario
+0

El segundo método en realidad implica el uso de subprocesos (he actualizado la respuesta para que quede más claro). La gente dice que no debes forzar a los hilos a pausar desde afuera y, en su lugar, recomendar la señalización/mensajes entre hilos. Entiendo cómo implementar esto último, pero no cómo hacer realmente el "trabajo" de una manera estéticamente agradable. –

+0

@Bruce van der Kooij: estoy al tanto de eso. De hecho, no es recomendable, pero yo soy la mejor manera de hacerlo. Hay 3 formas de hacer esto, la opción 'while true' (que realmente no funciona tan bien porque tienes que verificar la condición por ti mismo). La opción de hilo (de hecho, no es bonita). Y las señales externas que generalmente son más molestas que la opción con rosca. – Wolph

+0

Agregué otra actualización porque estoy bastante atrapado en este momento. ¿Estaría dispuesto a dar más detalles sobre lo que quería decir? Tal vez incluso proporcionar una muestra de código? Seguramente lo apreciaría. –

4

Tenga en cuenta que, aunque en su nivel de abstracción, "la ejecución de un golpe de teclado" es una sola operación atómica, es implementado en la máquina como una secuencia bastante complicada de instrucciones de la máquina. Por lo tanto, pausar un hilo en puntos arbitrarios podría llevar a que las cosas estén en un estado indeterminado. Enviar SIGSTOP es el mismo nivel peligroso que detener un hilo en un punto arbitrario. Sin embargo, dependiendo de dónde se encuentre en un paso en particular, su automatización podría romperse. Por ejemplo, si hace una pausa en medio de un paso dependiente del tiempo.

Me parece que este problema se resolvería mejor en el nivel de la biblioteca de automatización. No estoy muy familiarizado con la biblioteca de automatización que está utilizando. Puede valer la pena ponerse en contacto con los desarrolladores de la biblioteca para ver si tienen alguna sugerencia para detener la ejecución de los pasos de automatización a niveles de subpasos seguros.

+1

Buenos puntos. La única peculiaridad que he encontrado hasta ahora con la suspensión del proceso es que los modificadores del teclado (CTRL, ALT, SHIFT) se "bloquean" durante la pausa. Sin embargo, puedo evitarlo. Afortunadamente, otras teclas no se atascan en estado inactivo :-) –

0

Uso Python pero no pywinauto; para este tipo de tareas utilizo AutoHotKey. Una forma de implementar una pausa simple en un script de AutoHotkey puede ser usar una tecla "alternar" como ScrollLock y probar el estado de la tecla en el script. Además, la secuencia de comandos puede restaurar el estado de la tecla después de activar/desactivar la configuración de pausa interna.

+0

Gracias por su respuesta. He trabajado con AutoHotKey anteriormente (algunos guiones simples) pero esta vez decidí irme con Python porque tenía mis dudas sobre si podría hacer algunas de las cosas complicadas que estoy buscando hacer con la misma facilidad que puedo en Python. . –

+0

@Bruce: también prefiero Python sobre AutoHotKey para tareas complicadas.¿Pero el método de la tecla "alternar" no está disponible en pywinauto? Tal vez pueda usar sys.setprofile o sys.settrace para verificar el estado de la tecla alternar y hacer la pausa/reanudar sin modificar el código real. (solo mis dos centavos) – PabloG

Cuestiones relacionadas