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:
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.
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 ejemploqueue.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
- Pausing a process?
Pausing a thread using threading class
Alex Martelli posted an answer diciendo:
no existe un método para otros hilos para hacer una pausa por la fuerza de un hilo (como tampoco lo hay para otros hilos para matar ese hilo) - el hilo objetivo debe cooperar por occa inspeccionando las "banderas" apropiadas (un subprocesamiento. La condición puede ser apropiada para el caso de pausa/no pausa).
Luego se refirió al módulo de multiprocesamiento y SIGSTOP/SIGCONT.
- Is there a way to indefinitely pause a thread?
-
An answer to this question cita la documentación de MSDN respecto SuspendThread:
Esta función está diseñada principalmente para su uso por los depuradores. No está destinado a ser utilizado para la sincronización de hilos. Llamar a SuspendThread en un subproceso que posee un objeto de sincronización, como un mutex o sección crítica, puede llevar a un interbloqueo si el subproceso que llama intenta obtener un objeto de sincronización propiedad de un subproceso suspendido. Para evitar esta situación, un subproceso dentro de una aplicación que no es un depurador debe indicar al otro subproceso que se suspenda. El hilo de destino debe estar diseñado para observar esta señal y responder adecuadamente.
- Is there any way to kill a Thread in Python?
- How do I pass an exception between threads in python
+1 para la pregunta detallada – Wolph
¿Qué conjunto de herramientas está utilizando para la GUI? Usted mencionó una ventana anclada. –
@Cristian: decidí darle una oportunidad a Qt, pero estoy pensando en volver a GTK. –