멀티스레딩이 필요 없는 경우
stdout, stderr을 TextBrowser로 중계하고 싶으나 QThread를 사용하지 않는 경우(메인 스레드만 사용하는 경우) 아래와 같이 간단하게 stdout, stderr을 redirect할 수 있다.
class TextEditDemo(QWidget):
def __init__(self):
# TextBrowser 선언 등 잡다한 설정(생략)
sys.stdout.write = self._append_text
sys.stderr.write = lambda x: self._append_text(x, color='red')
def _append_text(self, text, color=None):
if color:
self.textBrowser.append(f'<span style=" color: {color};">{text}</span>')
else:
self.textBrowser.append(text)
sys.stdout
, sys.stderr
을 TextBrowser을 수정하는 함수로 덮어쓰도록 만들면 된다.
GUI 조작이 thread-safe하지 않기에 만약 여러 스레드를 사용한다면 위 소스코드를 사용해서는 안된다. 여러 스레드에서 GUI를 조작하면 아래와 같은 오류메시지를 뱉고, 가끔은 GUI가 죽어버리는 것을 확인했다.
QPainter::begin: Paint device returned engine == 0, type: 3
QPainter::setCompositionMode: Painter not active
QPainter::end: Painter not active, aborted
멀티스레딩이 필요한 경우
class StdoutWrapper(QThread):
proc = Signal(str, str)
def run(self):
sys.stdout.write = self.write
sys.stderr.write = lambda x: self.write(x, color='red')
# def stop(self):
# sys.stdout.write = sys.__stdout__.write
# sys.stderr.write = sys.__stderr__.write
def write(self, msg, color=None):
sys.stdout.flush()
self.proc.emit(msg, color)
class TextEditDemo(QWidget):
def __init__(self):
self.stdout = StdoutWrapper(self._append_text)
self.stdout.proc.connect(self._append_text)
self.stdout.start()
위와 같이 메인스레드(GUI 스레드)에서 TextBrowser에 출력하도록 만들면 된다.
stop 메서드는 참고한 아래 링크에 있길래 따라 썼는데 스레드가 멈출 때 실행되는 것 같지 않아 보인다..
원래는 sys.__stdout__.write
를 이용해 stdout, stderr을 복구하는 구문이다. 어차피 나에게 필요한 게 아니라 주석처리했다.
아래의 참고삼은 블로그에서는 QApplication.processEvents(QEventLoop.ExcludeUserInputEvents)
을 _append_text
에 넣으라는데 그러면 5개 스레드에서 시험삼아 100번 출력을 시켰을 때 메인 스레드가 의문사했다. 해당 구문을 넣지 않으면 의문사하지 않았다. 이유를 정확히 모르겠으나 일단 난 빼고 쓴다..
참고
https://4uwingnet.tistory.com/9 (PyQt에서 출력 리다이렉트)
'프로그래밍 언어 > Python' 카테고리의 다른 글
ThreadPool을 이용한 병렬 다운로드 예시 (0) | 2022.08.03 |
---|