Подтвердить что ты не робот

Как реализовать функции паузы (и более)?

Мои извинения заранее за длину вопроса, я не хотел ничего оставлять.

Некоторая справочная информация

Я пытаюсь автоматизировать процесс ввода данных, написав приложение 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, сейчас я использую его только для поиска а затем я напрямую использую SendKeysCtypes.SendKeys для имитации ввода с клавиатуры и win32api для имитации ввода мыши.

То, что я обнаружил до сих пор

Вот несколько методов, с которыми я сталкивался до сих пор в поиске ответа:

  • Я мог бы разделить функциональные возможности автоматизации и прослушиватель интерфейса + hotkey в двух отдельных процессах. Позвольте ссылаться на первое как "автомат", а второе - на "менеджера". Затем менеджер может приостановить выполнение автомата, отправив процессу сигнал SIGSTOP и отключив его с помощью сигнала SIGCONT (или эквивалентов Windows через SuspendThread/ResumeThread).

    Чтобы иметь возможность обновлять пользовательский интерфейс, автору необходимо будет сообщить менеджеру о его развитии через какой-то механизм IPC.

    Минусы:

    • Не будет ли использование SIGSTOP немного суровым? Будет ли он работать нормально? Многие люди, кажется, советуют против этого и даже называют это "опасным".

    • Я обеспокоен тем, что внедрение механизма МПК будет немного сложнее. С другой стороны, я работал с DBus, который не был бы слишком сложным для реализации.

  • Второй метод и тот, который, по-видимому, предлагает много людей, включает использование потоков и, по существу, сводится к следующему (упрощенному):

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

    Однако, делая это таким образом, кажется, что он остановится только после того, как больше не будет работы. Единственный способ, которым я вижу этот метод, будет заключаться в том, чтобы разделить работу (весь процесс автоматизации) на более мелкие рабочие сегменты (т.е. Задачи). Перед тем как приступить к новой задаче, рабочий поток проверит, следует ли приостановить и ждать.

    Минусы:

    • Кажется, что реализация для разделения работы на более мелкие сегменты, например, выше, будет очень уродливым кодом (эстетически).

      Как я себе это представляю, все утверждения будут преобразованы, чтобы выглядеть примерно так: queue.put((function, args)) (например, queue.put((app.Notepad.TypeKeys, "abcdef"))), и у вас будет поток процессов автоматизации, выполняющийся через задачи, и постоянная проверка состояния паузы перед запуском задача. Это просто не может быть прав...

    • Программа фактически не останавливается на своих треках, но сначала закончит задачу (пусть и небольшую), прежде чем на самом деле приостанавливается.

Достигнутый прогресс

UPDATE [23/01]: Я реализовал версию своего приложения, используя первый метод с помощью упомянутой функциональности SuspendThread/ResumeThread. Пока это работает очень хорошо, а также позволяет мне писать средства автоматизации так же, как вы пишете любые другие script. Единственный причуд, с которым я столкнулся, это то, что модификаторы клавиатуры (CTRL, ALT, SHIFT) становятся "застрявшими" при паузе. Возможно, что-то, что я могу легко обойти.

Я также написал тест, используя второй метод (потоки и сигналы/передачу сообщений), и реализовал функцию паузы. Тем не менее, это выглядит очень уродливо (оба проверяют флаг паузы и все, что связано с "выполнением работы" ). Поэтому, если кто-нибудь может показать мне пример того, что похоже на второй метод, я был бы признателен.

Связанные вопросы

4b9b3361

Ответ 1

Я не знаю pywinauto. Но я предполагаю, что у вас есть что-то вроде класса приложения, который вы получаете, и для этого есть методы, такие как SendKeys/SendMouseEvent/etc.

Создайте свой собственный класс MyApplication, который содержит ссылку на класс приложения pywinauto. Выполните те же методы, но перед каждым методом проверьте, произошло ли событие паузы. Если он есть, вы можете перейти в код, который обрабатывает событие паузы. Таким образом, вы проверяете паузу каждый раз, когда вы вызываете событие, но все это обрабатывается одним классом, не помещая паузу по всему вашему коду.

Как только вы обнаружите паузу, вы можете обращаться с ней так, как вам нравится. Например, вы можете выбросить исключение, чтобы отказаться от текущей задачи.

Ответ 2

Имейте в виду, что хотя в вашем уровне абстракции "выполнение нажатия клавиши" является одной атомной операцией, она реализована на машине как довольно сложная последовательность машинных команд. Таким образом, приостановка потока в произвольных точках может привести к тому, что вещи находятся в неопределенном состоянии. Отправка SIGSTOP - это тот же уровень опасности, что и приостановка потока в произвольной точке. В зависимости от того, где вы находитесь на определенном этапе, ваша автоматизация потенциально может быть нарушена. Например, если вы остановитесь в середине этапа, зависящего от времени.

Мне кажется, что эту проблему лучше всего решить на уровне библиотеки автоматизации. Я не очень хорошо знаком с библиотекой автоматизации, которую вы используете. Возможно, стоит связаться с разработчиками библиотеки, чтобы узнать, есть ли у них какие-либо предложения по приостановке выполнения шагов автоматизации на безопасных уровнях подэтапов.

Ответ 3

Разделение функциональности и потока/процесса интерфейса определенно является лучшим вариантом imho, второе решение выполняется быстрее и проще, но определенно не лучше.

Возможно, использование нескольких потоков и исключение было бы лучше, чем использование нескольких процессов. Но если вы используете несколько процессов, чем SIGSTOP может быть вашим единственным способом заставить его работать.

Есть ли что-нибудь против использования 2 потоков для этого?

  • 1 поток для фактического выполнения
  • 1 поток для чтения пользовательского ввода

Ответ 4

Я использую Python, но не pywinauto; для таких задач я использую AutoHotKey. Одним из способов реализации простой паузы в AutoHotkey script может быть использование клавиши "переключить", например ScrollLock, и проверки состояния ключа в script. Кроме того, script может восстановить состояние ключа после включения/выключения внутренней паузы.