2013-04-03 2 views
8

Я пытаюсь реализовать простую, легкую систему для записи событий Qt GUI и воспроизведения их из сценария. Я думал, что это будет довольно просто, используя магию системы событий Qt, но я столкнулся с проблемой, которую я не понимаю.Запись и воспроизведение событий Qt GUI

Вот краткий обзор того, что я делаю:

ЗАПИСЬ:

Я использую QApplication.instance().eventFilter(), чтобы захватить все события GUI Я заинтересован в * и сохранить их в скрипт Python, в котором каждый шаг выглядит примерно так:

obj = get_named_object('MainWindow.my_menubar') 
recorded_event = QMouseEvent(2, PyQt4.QtCore.QPoint(45, 8), 1, Qt.MouseButtons(0x1), Qt.KeyboardModifiers(0x0)) 
post_event(obj, recorded_event) 

ВОСПРОИЗВЕДЕНИЯ:

Я просто выполнить сценарий выше, работник (не-GUI) нить. (Я не могу использовать поток графического интерфейса, потому что я хочу продолжать отправлять сценарии в приложение, даже если «главный» eventloop заблокирован, пока работает модальный диалог eventloop.)

Важный материал происходит в моем post_event() функция, которая должна сделать две вещи:

  • Сначала вызовите QApplication.postEvent(obj, recorded_event)
  • Ожидать все события завершения обработки: **
    • Post специальное событие к тому же eventloop, что obj запущен.
    • Когда специальное событие обрабатывается:
      • Вызов QApplication.processEvents()
      • Установите флажок, который говорит воспроизведение нить это нормально продолжать

После второй части завершена, я ожидаю, что все эффекты первой части (записанного события) завершили, так как специальное событие было поставлено в очередь af ter записанное событие.

Вся система в основном, кажется, работает просто отлично для событий мыши, ключевые события и т.д. Но у меня проблема с QAction обработчиков при попытке воспроизвести события для моего основного QMenuBar.

Независимо от того, что я стараюсь, она не кажется, что я не могу заставить мое воспроизведение нити заблокировать для завершения всех QAction.triggered обработчиков, что в результате нажатия на моих QMenu пунктах. Насколько я могу судить, QApplication.processEvents() возвращает доQAction обработчик завершен.

Есть ли что-то особенное QMenu виджетов или QAction сигналов, что нарушает нормальные правила QApplication.postEvent() и/или QApplication.processEvents()? Мне нужен способ блокировки для завершения моих обработчиков QMenuQAction.

[*] Не все события зарегистрированы. Я регистрирую только spontaneous() событий, а также отфильтровываю несколько других типов (например, Paint событий и обычных движений мыши).

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

ответ

1

Я думаю, что ваша проблема лучше всего обслуживать с помощью QFuture и QFutureWatcher (то есть, если вы используете пространство имен QtConcurrent для потоков, а не QThreads). В принципе, система обработки событий Qt НЕ обязательно обрабатывает события в том порядке, в котором они размещены. Если вам нужно заблокировать до тех пор, пока определенное действие не будет завершено, и вы выполните это действие в отдельном потоке, вы можете использовать объект QFuture, возвращаемый QtConcurrent :: run(), с QFutureWatcher для блокировки, пока этот конкретный поток не завершит свою обработку ,

Что-то еще, чтобы рассмотреть способ обработки событий. Когда вы используете QApplication.postEvent(), созданное вами событие добавляется в очередь событий получателя, которую нужно обработать позже. За кулисами Qt может изменить порядок и сжать эти события, чтобы сэкономить время процессора. Я подозреваю, что это больше ваша проблема.

В вашей функции, которая обрабатывает воспроизведение, рассмотрите возможность использования QCoreApplication :: processEvents(), которая не вернется, пока все события не завершили обработку. Документация для QCoreApplication: here.

+0

Я уже вызываю 'QApplication.processEvents()' во время воспроизведения (это не было ясно в моем вопросе, поэтому я отредактировал его). –

+0

Кроме того, спасибо за отзыв о Qt Concurrent - я раньше не видел эту библиотеку. Тем не менее, я не думаю, что это помогает мне в этом случае. Если я что-то не упустил, Qt Concurrent не позволяет мне * выбрать *, какой поток выполнить мою работу. Мне нужно, чтобы мое «специальное» действие обрабатывалось в основном потоке gui. В настоящее время я достигаю этого с помощью сигнала, который находится в потоке GUI, который я «подключаю()» к использованию «Qt.QueuedConnection». И все же ... он не ведет себя так, как ожидаешь, когда участвует «QMenu». –

+0

Ну, если вы делали эту обработку в отдельном потоке, QtConcurrent позволил бы вам выбрать «поток», чтобы запустить его. Так как вы делаете это в основном потоке, это не работает. Учитывая, что вы испытываете нестандартное поведение от QApplication, я подозреваю, что вы столкнулись с какой-то проблемой синхронизации с вашими потоками. Возможно, что действия QMenu не помещаются в очередь событий, когда вы думаете, что они есть. Мой лучший совет - попробовать отладить это, переместив работу post_event() в основной поток. Если это сработает, тогда вы узнаете, что у вас есть проблема синхронизации потоков. – gankoji

1

Виджеты QMenu и сигналы QAction являются особым случаем. QMenu имеет функцию exec(), обычно используемую для всплывающих окон. Я подозреваю (но я точно не знаю), что QMenuBar будет использовать этот механизм, когда он откроет обычное раскрывающееся меню. Документы не совсем понятны, но меню действуют так же, как диалоговые окна, поскольку они блокируют все другие действия пользователя - как бы Qt это делал, за исключением того, что предоставлял меню свой собственный цикл событий? Я не могу заполнить все пробелы из информации в вашем сообщении, но я не вижу, как ваш поток воспроизведения справится с новым циклом событий.