Сигналы Qt не обрабатываются в порядке их испускания

Краткая версия:

Отправка сигналов в GUI внезапным образом приводит к тому, что данные, которые они несут, вставляются повсюду в текстовое поле, в которое они должны входить.

Длинная версия:

У меня есть графический интерфейс PyQt, который подключен к скрипту Python. Скрипт запускается в отдельном потоке и пишет в stdout и stderr. Скрипт может выполняться независимо, но я создал графический интерфейс, чтобы обеспечить более удобный интерфейс.

Чтобы захватить stdout/stderr и записать их вывод в графическом интерфейсе, я перезаписал sys.stdout и sys.stderr в потоке объектом, имеющим метод записи.

sys.stdout = StreamWrapper(sys.stdout, self, self.mutex)
sys.stderr = StreamWrapper(sys.stderr, self, self.mutex)

StreamWrapper:

class StreamWrapper:
    def __init__(self, stream, thread, mutex):
        self.stream = stream # reference to original stream (stdout or stderr)
        self.thread = thread # thread in which this object is initialised.
        self.mutex  = mutex  # mutex ensuring write is only called once at a time

    def write(self, msg):
        with QtCore.QMutexLocker(self.mutex):
            self.thread.emit(QtCore.SIGNAL('update_console(QString)'), msg)
            self.stream.write(msg)
            time.sleep(0.5)

Этот метод записи выдает сигнал и сообщение графическому интерфейсу. Графический интерфейс (в основном потоке) подбирает его и соответственно обновляет обычное текстовое поле (называемое consoleOutput):

self.connect(self.thread, QtCore.SIGNAL('update_console(QString)'), self.ui.consoleOutput.insertPlainText)

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

Чтобы исправить это, я попытался ввести мьютекс, но безрезультатно. Затем я ввел задержку (путем вызова time.sleep(0.5)), и это решило проблему, хотя и неуклюжим образом.

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

Любые идеи?

ИЗМЕНИТЬ:

Я создал компилируемый пример, состоящий из трех файлов, которые должны находиться в одном каталоге:

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


person Nobilis    schedule 28.05.2014    source источник
comment
Не могли бы вы опубликовать минималистичный рабочий пример?   -  person three_pineapples    schedule 29.05.2014
comment
@three_pineapples Привет и большое спасибо за ваш интерес. Я добавил ссылки на компилируемый рабочий пример и скриншот, воспроизводящий проблему в посте.   -  person Nobilis    schedule 29.05.2014
comment
Я не могу быть уверен на 100%, потому что у меня нет времени запускать ваш код прямо сейчас, но я думаю, что слоты, вероятно, выполняются в потоке, а не в потоке графического интерфейса. В противном случае цикл событий Qt будет их сериализовать. Взгляните на этот вопрос, на который я ответил в прошлом, который в основном делает то, что вы хотите. stackoverflow.com/questions/21071448/   -  person three_pineapples    schedule 29.05.2014
comment
@three_pineapples Решение оказалось намного проще, чем я думал. Я опубликовал ответ с подробным описанием. Проверьте это, если вам интересно.   -  person Nobilis    schedule 29.05.2014


Ответы (1)


Оказывается, решение намного проще и тривиальнее, чем я думал.

По сути, проблема заключалась в том, что мне приходилось вручную перемещать курсор в конце до и после вставки текста.

Решение, которому я следовал, подробно описано здесь:

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

self.connect(self.fpga_thread, QtCore.SIGNAL('update_console(QString)'), self.updateConsole)

Что выглядит так:

def updateConsole(self, msg):
    self.ui.consoleOutput.moveCursor(QtGui.QTextCursor.End)
    self.ui.consoleOutput.insertPlainText(msg)
    self.ui.consoleOutput.moveCursor(QtGui.QTextCursor.End)

И теперь это работает, похоже, проблема связана с прокруткой. Если я прокручиваю текстовое поле по мере его обновления (без перемещения курсора вручную), он помещает текст в нижнюю часть отображаемого в данный момент текста (а не в нижнюю часть текстового поля).

В конце концов, мне не пришлось вводить задержку, и я удалил sleep, но на всякий случай оставил мьютекс.

person Nobilis    schedule 29.05.2014