Приношу свои извинения заранее за объем вопроса, я не хотел ничего упускать.
Я пытаюсь автоматизировать процесс ввода данных, написав приложение Python, которое использует Windows API для имитации нажатия клавиш, движения мыши и манипуляций с окном / элементами управления. Мне приходится прибегать к этому методу, потому что у меня (пока) нет уровня допуска, необходимого для доступа к хранилищу данных / базе данных напрямую (например, с использованием SQL) или косвенно через более подходящий API. Бюрократия, это боль; -)
Процесс ввода данных включает в себя корректировку заказов на продажу из-за изменений в наличии товара. Недоступные статьи либо удаляются из заказа, либо заменяются другой подходящей статьей.
Изначально я хочу, чтобы человек мог отслеживать процесс автоматического ввода данных, чтобы убедиться, что все идет правильно. Для этого я, с одной стороны, замедляю действия, но также информирую пользователя о том, что в данный момент происходит, через закрепленное окно.
Чтобы позволить пользователю останавливать процесс автоматизации, я регистрирую клавишу Pause / Break как горячую клавишу, а в обработчике я хочу приостановить функциональность автоматизации. Однако в настоящее время я изо всех сил пытаюсь найти способ правильно приостановить выполнение функций автоматизации. Когда вызывается функция паузы, я хочу, чтобы процесс автоматизации останавливался на месте, независимо от того, что он делает. Я не хочу, чтобы он даже выполнял еще одно нажатие клавиши.
ОБНОВЛЕНИЕ [23/01]: Я действительно хочу сделать больше, чем просто приостановить, я хочу иметь возможность общаться с процессом автоматизации во время его работы и попросить его приостановить, пропустить текущий заказ на продажу, полностью отказаться и, возможно, даже больше.
Кто-нибудь может показать мне правильный путь (TM), чтобы достичь того, чего я хочу?
Вот пример того, как работает автоматизация (я использую библиотеку pywinauto ):
from pywinauto import application
app = application.Application()
app.start_("notepad")
app.Notepad.TypeKeys("abcdef")
ОБНОВЛЕНИЕ [25/01]: После нескольких дней работы над моим приложением я заметил, что не 'Я действительно не использую pywinauto так часто, прямо сейчас я' m, используя его только для поиска окна, а затем я напрямую использую SendKeysCtypes.SendKeys
для имитации ввода с клавиатуры и функции win32api
для имитации ввода с помощью мыши.
Вот несколько методов, с которыми я столкнулся до сих пор в поисках ответа:
Я мог бы разделить функции автоматизации и интерфейс + прослушиватель горячих клавиш в двух отдельных процессах. Назовем первого «автоматом», а второго «менеджером». Затем менеджер может приостановить выполнение автомата, отправив процессу сигнал SIGSTOP, и возобновить его с помощью сигнала SIGCONT (или его эквивалентов в Windows через SuspendThread / ResumeThread).
Чтобы иметь возможность обновлять пользовательский интерфейс, автоматизатору необходимо будет информировать менеджера о своем продвижении через какой-то механизм IPC.
Минусы:
Будет ли использование SIGSTOP немного резким? Будет ли он работать нормально? Многие люди, кажется, отговаривают это и даже называют это «опасным».
Я обеспокоен тем, что реализация механизма IPC будет немного сложной. С другой стороны, я работал с DBus, что было бы несложно реализовать.
Второй метод, который, кажется, предлагают многие, включает использование потоков и, по сути, сводится к следующему (упрощенно):
пока True:
Не будет ли использование SIGSTOP немного жестким? Будет ли он работать нормально? Многие люди, кажется, отговаривают это и даже называют это «опасным».
Я обеспокоен тем, что реализация механизма IPC будет немного сложной. С другой стороны, я работал с DBus, что было бы несложно реализовать.
Второй метод, который, кажется, предлагают многие, включает использование потоков и, по сути, сводится к следующему (упрощенно):
пока True:
Не будет ли использование SIGSTOP немного жестким? Будет ли он работать нормально? Многие люди, кажется, отговаривают это и даже называют это «опасным».
Я обеспокоен тем, что реализация механизма IPC будет немного сложной. С другой стороны, я работал с DBus, что было бы несложно реализовать.
Второй метод, который, кажется, предлагают многие, включает использование потоков и, по сути, сводится к следующему (упрощенно):
пока True:
если self.pause: # пауза
# Выполнять работу...
Однако, делая это таким образом, кажется, что он будет приостановлен только после того, как больше не будет работать. Единственный способ, которым, на мой взгляд, будет работать, - это разделить работу (весь процесс автоматизации) на более мелкие рабочие сегменты (т.е. задачи). Перед запуском новой задачи рабочий поток проверит, следует ли ему остановиться и ждать.
Минусы:
Похоже, реализация для разделения работы на более мелкие сегменты, такие как приведенный выше, была бы очень некрасивой с точки зрения кода (эстетически).
Как я себе это представляю, все операторы будут преобразованы так, чтобы выглядеть примерно так: queue.put ((function, args))
(например, queue.put ((app.Notepad.TypeKeys , "abcdef"))
), и у вас будет поток процесса автоматизации, выполняющий задачи и непрерывно проверяющий состояние паузы перед запуском задачи. Это просто не может быть правдой ...
Программа не остановится как вкопанная, а сначала завершит задачу (пусть даже небольшую) перед тем, как фактически приостановиться.
ОБНОВЛЕНИЕ [23 / 01]: Я реализовал версию своего приложения, используя первый метод с помощью упомянутой функции SuspendThread / ResumeThread
. Пока что это работает очень хорошо, а также позволяет мне писать элементы автоматизации, как если бы вы писали любой другой скрипт. Единственная странность, с которой я столкнулся, заключается в том, что модификаторы клавиатуры (CTRL, ALT, SHIFT) «застревают» во время паузы. Что-то, что я, вероятно, легко смогу обойти.
Я также написал тест, используя второй метод (потоки и сигналы / передача сообщений), и реализовал функцию паузы. Однако, это выглядит действительно некрасиво (как проверка флажка паузы, так и всего, что связано с «выполнением работы»). Так что, если кто-нибудь может показать мне подходящий пример чего-то похожего на второй метод, я был бы признателен.
Приостановка потока с использованием класса потоковой передачи
Алекс Мартелли отправил ответ :
Для других потоков не существует метода принудительной приостановки потока (как и для других потоков, чтобы убить этот поток) - целевой поток должен взаимодействовать, периодически проверяя соответствующие «флаги» ( условие потоковой передачи может быть подходящим для случая паузы / возобновления).
Затем он обратился к модулю многопроцессорности и SIGSTOP / SIGCONT.
Приостановка процесса в Windows
Ответ на этот вопрос цитирует документацию MSDN, касающуюся SuspendThread:
Эта функция в первую очередь предназначена для использования отладчики. Он не предназначен для синхронизации потоков. Вызов SuspendThread в потоке, которому принадлежит объект синхронизации, например мьютекс или критическая секция, может привести к тупиковой ситуации, если вызывающий поток пытается получить объект синхронизации, принадлежащий приостановленному потоку. Чтобы избежать этой ситуации, поток в приложении, не являющийся отладчиком, должен сигнализировать другому потоку о приостановке. Целевой поток должен быть спроектирован так, чтобы отслеживать этот сигнал и соответствующим образом реагировать.