Вывести вывод терминала с помощью tqdm в QPlainTextEdit - PullRequest
0 голосов
/ 19 ноября 2018

Я пытаюсь найти способ получения наряду с другими отпечатками результата / эволюции индикатора выполнения в приложении pyqt, например, в виджете QPlainTextEdit.

Проблема, с которой я сталкиваюсь, заключается в том, что индикаторы выполнения могут использовать более продвинутый возврат каретки или даже более продвинутое позиционирование курсора, которые в основном не поддерживаются триамами. Я пробовал io.StringIO, но \r остается буквальным.

import io
from tqdm import tqdm
s = io.StringIO()
for i in tqdm(range(3), file=s):    
    sleep(.1)

выход:

s.getvalue()

Out[24]: '\n\r  0%|          | 0/3 [00:00<?, ?it/s]\x1b[A\n\r 33%|###3      | 1/3 [00:00<00:00,  9.99it/s]\x1b[A\n\r 67%|######6   | 2/3 [00:00<00:00,  9.98it/s]\x1b[A\n\r100%|##########| 3/3 [00:00<00:00,  9.98it/s]\x1b[A\n\x1b[A'

, что переводится как:

print(s.getvalue())
  0%|          | 0/3 [00:00<?, ?it/s]
 33%|###3      | 1/3 [00:00<00:00,  9.99it/s]
 67%|######6   | 2/3 [00:00<00:00,  9.98it/s]
100%|##########| 3/3 [00:00<00:00,  9.98it/s]

Для ясности, в моих выходных данных я не хочу, чтобы одна строка обновлялась, а только текущее состояние, как это будет напечатано в командной строке.

Есть идеи, как это сделать? Спасибо!

1 Ответ

0 голосов
/ 20 ноября 2018

Идея состоит в том, чтобы удалить предыдущую строку, если добавлен новый текст, но вы также должны удалить \r и убедиться, что это не пустой текст. Кроме того, чтобы объект получал текст tqdm, он должен иметь только метод write(), поэтому реализуйте пользовательский QPlainTextEdit. Используйте QMetaObject::invokeMethod(), чтобы сделать потокобезопасным

import time
import threading
from tqdm import tqdm
from PyQt5 import QtCore, QtGui, QtWidgets
import lorem

class LogTextEdit(QtWidgets.QPlainTextEdit):
    def write(self, message):
        if not hasattr(self, "flag"):
            self.flag = False
        message = message.replace('\r', '').rstrip()
        if message:
            method = "replace_last_line" if self.flag else "appendPlainText"
            QtCore.QMetaObject.invokeMethod(self,
                method,
                QtCore.Qt.QueuedConnection, 
                QtCore.Q_ARG(str, message))
            self.flag = True
        else:
            self.flag = False

    @QtCore.pyqtSlot(str)
    def replace_last_line(self, text):
        cursor = self.textCursor()
        cursor.movePosition(QtGui.QTextCursor.End)
        cursor.select(QtGui.QTextCursor.BlockUnderCursor)
        cursor.removeSelectedText()
        cursor.insertBlock()
        self.setTextCursor(cursor)
        self.insertPlainText(text)

def foo(w):
    for i in tqdm(range(100), file=w):
        time.sleep(0.1)

if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    w = LogTextEdit(readOnly=True)
    w.appendPlainText(lorem.paragraph())
    w.appendHtml("Welcome to Stack Overflow")
    w.show()
    threading.Thread(target=foo, args=(w,), daemon=True).start()
    sys.exit(app.exec_())
...